Mark tests as not working under TARGET_JVM
[mono.git] / mcs / class / System.Web / System.Web.J2EE / PageMapper.cs
1 //
2 // (C) 2005 Mainsoft Corporation (http://www.mainsoft.com)
3 //
4
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the
8 // "Software"), to deal in the Software without restriction, including
9 // without limitation the rights to use, copy, modify, merge, publish,
10 // distribute, sublicense, and/or sell copies of the Software, and to
11 // permit persons to whom the Software is furnished to do so, subject to
12 // the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be
15 // included in all copies or substantial portions of the Software.
16 //
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 //
25
26 using System;
27 using System.Xml;
28 using System.IO;
29 using System.Collections;
30 using System.Web.Compilation;
31 using System.Collections.Specialized;
32 using System.Threading;
33 using vmw.common;
34 using System.Reflection;
35
36 namespace System.Web.J2EE
37 {
38         /// <summary>
39         /// Class that allows reading assemblies.xml file for getting information about different types.
40         /// </summary>
41         public class PageMapper
42         {
43                 private static readonly string _fileListName = "/filelist.xml";
44                 private static readonly object LOCK_GETASSEMBLIESCACHEDDOCUMENT = new object();
45                 private static readonly object LOCK_GETFROMMAPPATHCACHE = new object();
46
47                 static PageMapper ()
48                 {
49                         AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler (CurrentDomain_AssemblyResolve);
50                         try
51                         {
52                                 //Try to load the  global resources
53                                 HttpContext.AppGlobalResourcesAssembly = GetCachedAssembly ("app_globalresources");
54                         }
55                         catch
56                         {
57                         }
58                 }
59
60
61                 static Assembly CurrentDomain_AssemblyResolve (object sender, ResolveEventArgs args)
62                 {
63                         Assembly resolvedAssembly = null;
64                         try
65                         {
66                                 resolvedAssembly = GetCachedAssembly (args.Name);
67                         }
68                         catch
69                         {
70                                 resolvedAssembly = null;
71                         }
72
73                         return resolvedAssembly;
74                 }
75                 public static string GetFromMapPathCache(string key)
76                 {
77                         Hashtable answer = null;
78                         lock(LOCK_GETFROMMAPPATHCACHE)
79                         {
80                                 answer = (Hashtable) AppDomain.CurrentDomain.GetData(J2EEConsts.MAP_PATH_CACHE);
81                                 if (answer == null)
82                                 {
83                                         answer = new Hashtable();
84                                         CachedDocumentTypeStorage storage = (CachedDocumentTypeStorage)GetAssembliesCachedDocument();
85                                         IDictionaryEnumerator e = storage.GetEnumerator();
86                                         e.Reset();
87                                         while (e.MoveNext())
88                                         {                                       
89                                                 string currentFile = (string)((DictionaryEntry)e.Current).Key;
90                                                 answer[currentFile]= IAppDomainConfig.WAR_ROOT_SYMBOL + currentFile;
91                                         }
92                                         AppDomain.CurrentDomain.SetData(J2EEConsts.MAP_PATH_CACHE,answer);
93
94                                 }
95                         }
96                         return (string)answer[key];
97                 }
98
99 #if UNUSED
100                 // UNUSED METHOD
101                 //The method was used by runtime to force file names casesensitivity
102                 // problem. The filelist.xml file should contain correct file names,
103                 // but currently it is unused
104                 public static void LoadFileList()
105                 {
106                         Hashtable hashTable = (Hashtable) AppDomain.CurrentDomain.GetData(J2EEConsts.FILE_LIST_FILE);
107                         if (hashTable == null)
108                         {
109                                 XmlDocument doc;
110                                 try
111                                 {
112                                         Stream fs = (Stream)IOUtils.getStream(_fileListName);
113                                         if (fs == null)
114                                         {
115                                                 AppDomain.CurrentDomain.SetData(J2EEConsts.FILE_LIST_FILE, new Hashtable());
116                                                 return;
117                                         }
118
119                                         doc = new XmlDocument();
120                                         doc.Load(fs);
121                                 }
122                                 catch (Exception)
123                                 {
124 //                                      Console.WriteLine("filelist.xml was not found!!!");
125                                         AppDomain.CurrentDomain.SetData(J2EEConsts.FILE_LIST_FILE, new Hashtable());
126                                         return;
127                                 }
128 //                              Console.WriteLine("filelist.xml was found!!!");
129                                 if (doc != null && doc.DocumentElement.HasChildNodes)
130                                 {
131                                         hashTable = CollectionsUtil.CreateCaseInsensitiveHashtable();
132                                         XmlNodeList nodeList = doc.DocumentElement.ChildNodes;
133                                         for (int i = 0;i < nodeList.Count ; i++)
134                                         {
135                                                 string fileName = nodeList.Item(i).InnerText;
136                                                 hashTable.Add(fileName,fileName);
137                                         }
138                                         AppDomain.CurrentDomain.SetData(J2EEConsts.FILE_LIST_FILE, hashTable);
139                                 }
140                         }
141
142                 }
143 #endif
144                 private static ICachedXmlDoc GetAssembliesCachedDocument()
145                 {
146                         lock(LOCK_GETASSEMBLIESCACHEDDOCUMENT)
147                         {
148                                 ICachedXmlDoc doc = (ICachedXmlDoc) AppDomain.CurrentDomain.GetData(J2EEConsts.ASSEMBLIES_FILE);
149                                 if (doc == null)
150                                 {
151                                         doc = CreateDocument();
152                                         if (doc != null)
153                                                 AppDomain.CurrentDomain.SetData(J2EEConsts.ASSEMBLIES_FILE, doc);
154                                 }
155
156                                 return doc;
157                         }
158                 }
159
160                 private static String NormalizeName(string url)
161                 {
162 #if NET_2_0
163                         url = System.Web.Util.UrlUtils.RemoveDoubleSlashes(url);
164 #endif 
165                         if (url.StartsWith(IAppDomainConfig.WAR_ROOT_SYMBOL))
166                                 url = url.Substring(IAppDomainConfig.WAR_ROOT_SYMBOL.Length);
167                         return url;
168                 }
169                 private static ICachedXmlDoc CreateDocument()
170                 {
171                         return new CachedDocumentTypeStorage();
172                 }
173
174                 public static Type GetObjectType(string url)
175                 {
176                         return GetCachedType(NormalizeName(url), true);
177                 }
178
179                 public static Type GetObjectType (string url, bool throwException) {
180                         return GetCachedType (NormalizeName (url), throwException);
181                 }
182
183                 public static Assembly GetObjectAssembly(string url)
184                 {
185                         return GetCachedAssembly(NormalizeName(url));
186                 }
187                 public static string GetAssemblyResource(string url)
188                 {
189                         return GetCachedResource(NormalizeName(url));
190                 }
191                 private static string GetCachedResource(string url)
192                 {
193                         ICachedXmlDoc doc = PageMapper.GetAssembliesCachedDocument();
194                         return doc.GetAssemblyResourceName(url);
195                 }
196                 private static Assembly GetCachedAssembly(string url)
197                 {
198                         ICachedXmlDoc doc = PageMapper.GetAssembliesCachedDocument();
199                         Assembly t = doc.GetAssembly(url);
200                         if (t == null)
201                                 throw new HttpException(404, "The requested resource (" + url + ") is not available.");
202
203                         return t;
204                 }
205                 private static Type GetCachedType (string url) {
206                         return GetCachedType (url, true);
207                 }
208                 private static Type GetCachedType (string url, bool throwException)
209                 {
210                         ICachedXmlDoc doc = PageMapper.GetAssembliesCachedDocument();                                           
211                         Type t = doc.GetType(url);
212                         if (t == null && throwException)
213                                 throw new HttpException(404,"The requested resource (" + url + ") is not available.");
214
215                         return t;
216                 }
217
218                 #region ICachedXmlDoc interface
219                 interface ICachedXmlDoc
220                 {
221                         Type GetType(string key);
222                         Assembly GetAssembly(string key);
223                         string GetAssemblyResourceName(string key);
224                 }
225                 #endregion
226
227                 #region CachedDocumentTypeStorage class
228                 class CachedDocumentTypeStorage : ICachedXmlDoc
229                 {
230                         private static readonly object _fuse = new object();
231                         public static readonly ICachedXmlDoc DEFAULT_DOC =
232                                 new CachedDocumentTypeStorage(0);
233
234                         private static readonly int DEFAULT_PAGES_NUMBER = 25;
235
236                         private Hashtable _table;
237
238                         private CachedDocumentTypeStorage(int initTableSize)
239                         {
240                                 _table = Hashtable.Synchronized(new Hashtable(initTableSize));
241                         }
242
243                         public CachedDocumentTypeStorage() :
244                                 this(DEFAULT_PAGES_NUMBER)
245                         {}
246
247                         string ICachedXmlDoc.GetAssemblyResourceName(string o)
248                         {
249                                 return GetMetaByURL(o).Resource;
250                         }
251                         Type ICachedXmlDoc.GetType(string o)
252                         {
253                                 return GetMetaByURL(o).Type;
254                         }
255                         Assembly ICachedXmlDoc.GetAssembly(string o)
256                         {
257                                 return GetMetaByURL(o).Assembly;
258                         }
259
260                         internal IDictionaryEnumerator GetEnumerator()
261                         {
262                                 return _table.GetEnumerator();                          
263                         }       
264
265                         //rewamped for perfomance reasons
266                         //It looks like issue is not important in development mode,
267                         //but only will became important in production mode when dynamyc compilation will be enabled
268                         //Anyway, locking whole table and compiling under lock looks odd
269                         //spivak.December 07 2006
270                         public MetaProvider GetMetaByURL(string url)
271                         {
272
273 #if !NO_GLOBAL_LOCK_ON_COMPILE
274                                 string lwUrl = url.ToLower();
275                                 lock (_table)
276                                 {
277                                         object retVal = _table[lwUrl];
278                                         if (retVal == null)
279                                         {
280                                                 retVal = PageCompiler.GetCompiler(url);
281                                                 _table[lwUrl] = retVal;
282                                         }
283                                 
284                                         return (MetaProvider)retVal;
285                                 }
286
287 #else
288                                 string lwUrl = url.ToLower();
289                                 if (!_table.ContainsKey(lwUrl))
290                                 {
291                                         lock (_table.SyncRoot)
292                                         {
293                                                 if (_table.ContainsKey(lwUrl))
294                                                         goto Fused;
295                                                 _table[lwUrl] = _fuse;
296                                         }
297                                         try
298                                         {
299                                                 MetaProvider meta = PageCompiler.GetCompiler(url);
300                                                 lock (_table.SyncRoot)
301                                                 {
302                                                         return (MetaProvider)(_table[lwUrl] = meta);
303                                                 }
304                                         }
305                                         catch(Exception e)
306                                         {
307                                                 _table.Remove(lwUrl);
308                                         }
309                                 }                               
310                         Fused:
311                                 
312                                 while (_table[lwUrl] == _fuse) 
313                                         Thread.Sleep(10);
314
315                                 return !_table.ContainsKey(lwUrl)? PageCompiler.Error: (MetaProvider)_table[lwUrl];
316 #endif
317                         }
318                 }
319                 
320
321                 #endregion
322         }
323
324         public interface  MetaProvider
325         {
326                 Type Type { get;}
327                 Assembly Assembly {get;}
328                 string Resource { get;}
329         }
330         public class PageCompiler : MetaProvider
331         {
332                 private static readonly string PAGE_XPATH = "preserve";
333                 private static readonly string ASSEM_ATTRIB_NAME = "assem";
334                 private static readonly string TYPE_ATTRIB_NAME = "type";
335                 private static string _parser = null;
336                 private static PageCompiler _errorProvider = new PageCompiler();
337
338                 private Type _type = null;
339                 private string _typeName = null;
340                 private Assembly _assembly = null;
341                 private string _origAssemblyName = null;
342                 private string _xmlDescriptor = null;
343                 private string _url = null;
344                 private string _session = null;
345
346                 PageCompiler(string url)
347                 {
348                         _url = url;
349                         _xmlDescriptor = GetDescFromUrl();
350                         _session = DateTime.Now.Ticks.ToString();
351                         LoadTypeAndAssem();
352                 }
353                 PageCompiler()
354                 {
355                 }
356
357                 public static PageCompiler Error
358                 {
359                         get 
360                         {
361                                 return _errorProvider; 
362                         }
363                 }
364                 public static PageCompiler GetCompiler(string url)
365                 {
366                         try{
367                                 return new PageCompiler(url);
368                         }
369                         catch(Exception e)
370                         {
371                                 return Error;
372                         }
373                 }
374
375                 Type MetaProvider.Type
376                 {
377                         get{
378                                 return _type;
379                         }
380                 }
381                 Assembly MetaProvider.Assembly
382                 {
383                         get{
384                                 return _assembly;
385                         }
386                 }
387                 string MetaProvider.Resource
388                 {
389                         get
390                         {
391                                 return _origAssemblyName != null ? _origAssemblyName + ".ghres" : "dll.ghres";
392                         }
393                 }
394                 private void LoadTypeAndAssem()
395                 {
396                         if (_assembly == null)
397                         {
398                                 string typeName = GetCachedTypeName();
399                                 if (typeName != null)
400                                 {
401                                         if ((_type = Type.GetType(typeName)) != null)
402                                                 _assembly = _type.Assembly;
403                                         else
404                                                 _assembly = Assembly.Load(_origAssemblyName);
405                                 }
406                         }
407                 }
408                 private void InternalCompile()
409                 {
410                         string fileName = Path.GetFileName(_url);
411
412                         string fullFileName = (fileName.ToLower() == "global.asax") ? _url : HttpContext.Current.Request.MapPath(_url);
413 #if DEBUG
414                         Console.WriteLine("fullFileName=" + fullFileName);
415 #endif
416                         //type not found - run aspxparser
417                         if (File.Exists(fullFileName) || Directory.Exists(fullFileName))
418                         {
419                                 string[] command = GetParserCmd(fileName.ToLower() == "global.asax");
420                                 if (J2EEUtils.RunProc(command) != 0)
421                                         throw GetCompilerError();
422                         }
423                         else
424                         {
425                                 string message = "The requested resource (" + _url + ") is not available.";
426                                 throw new HttpException(404, message);
427                         }
428                 }
429                 private string GetDescriptorPath()
430                 {
431                         return String.Join("/", new string[] { "assemblies", _xmlDescriptor });
432                 }
433                 private string GetTypeNameFromAppFolder()
434                 {
435                         try
436                         {
437                                 using (StreamReader sr = new StreamReader(HttpContext.Current.Request.MapPath("/" + GetDescriptorPath())))
438                                 {
439                                         return GetTypeFromDescStream(sr.BaseStream);
440                                 }
441                         }
442                         catch (Exception ex)
443                         {
444                                 Console.WriteLine(ex);
445                                 throw ex;
446                         }
447                 }
448                 internal string GetTypeFromResources()
449                 {
450                         string typeName = null;
451
452                         //if the desciptor exists in the war - get the type
453                         string descPath = GetDescriptorPath();
454
455                         try
456                         {
457 #if DEBUG
458                                 Console.WriteLine(descPath);
459 #endif
460                                 using (Stream fs = (Stream)IOUtils.getStreamRecursive("/" + descPath))
461                                 {
462                                         if (fs != null)
463                                         {
464                                                 return GetTypeFromDescStream(fs);
465                                         }
466                                 }
467                         }
468                         catch (Exception ex)
469                         {
470 #if DEBUG
471                                 Console.WriteLine(ex);
472 #endif
473                         }
474                         return null;
475                 }
476                 internal string GetCachedTypeName()
477                 {                       
478                         string typeName = GetTypeFromResources();
479                         if (typeName == null)
480                         {
481                                 //spawn dynamic compilation and lookup typename from created folder
482                                 InternalCompile();
483                                 typeName = GetTypeNameFromAppFolder();
484                         }
485                         return typeName;
486                 }
487                 private string GetTypeName()
488                 {
489                         return String.Format("{0}, {1}", _typeName, _origAssemblyName); 
490                 }
491                 private bool LoadMetaFromXmlStream(Stream fs)
492                 {
493                         if (fs != null)
494                         {
495                                 try
496                                 {
497                                         XmlDocument descXml = new XmlDocument();
498                                         descXml.Load(fs);
499                                         _origAssemblyName = descXml.SelectSingleNode(PAGE_XPATH).Attributes[ASSEM_ATTRIB_NAME].Value;
500                                         _typeName = descXml.SelectSingleNode(PAGE_XPATH).Attributes[TYPE_ATTRIB_NAME].Value;
501                                         return true;
502                                 }
503                                 catch
504                                 {
505 #if DEBUG
506                                         Console.WriteLine("Failed to load typename from stream");
507 #endif
508                                 }
509                         }
510                         return false;
511                 }
512
513                 private string GetTypeFromDescStream(Stream fs)
514                 {
515                         if (LoadMetaFromXmlStream(fs))
516                                 return GetTypeName();
517                         return null;
518                 }
519
520                 private string[] GetParserCmd(bool globalAsax)
521                 {
522             string[] cmd = null;                        
523             if (globalAsax)
524             {
525                 cmd = new string[4];
526                 cmd[3] = "/buildglobalasax";
527             }
528             else
529             {
530                 cmd = new string[5];
531                 cmd[3] = "/aspxFiles:" + _url;
532                 cmd[4] = "/compilepages";
533             }
534             cmd[0] = GetParser();
535             cmd[1] = "/session:" + _session;
536             cmd[2] = "/appDir:" + (string)AppDomain.CurrentDomain.GetData(IAppDomainConfig.APP_PHYS_DIR);
537                         return cmd;
538                 }
539
540                 private string GetParser()
541                 {
542                         if (_parser == null)
543                         {
544                                 StreamReader sr =
545                                         File.OpenText(HttpContext.Current.Request.MapPath("/AspxParser.params"));
546                                 _parser = sr.ReadLine();
547                                 sr.Close();
548                         }
549
550                         return _parser;
551                 }
552
553                 private string GetDescFromUrl()
554                 {
555                         string fileName = Path.GetFileName(_url);
556                         
557                         if (fileName.ToLower() == "global.asax")
558                                 return "global.asax.xml";
559
560                         string id = GetIdFromUrl(_url);
561                         string[] descName = new string[3] {fileName, id, ".xml"} ;
562                         return string.Concat(descName).ToLower();
563                 }
564
565                 private string GetIdFromUrl(string path)
566                 {
567                         path = path.Trim('/');
568                         string fileName = Path.GetFileName(path);
569                         string id = string.Empty;
570
571                         path = path.Substring (path.IndexOf ("/") + 1);
572
573                         if (path.Length > fileName.Length)
574                                 id = "." + path.Substring(0,path.Length - fileName.Length).Replace('/','_');
575                         return id;      
576                 }
577
578                 private Exception GetCompilerError()
579                 {
580                         string _errFile = HttpContext.Current.Request.MapPath("/" + _session + ".vmwerr");
581                         
582                         if (!File.Exists(_errFile))
583                                 throw new FileNotFoundException("Internal Error",_errFile);
584
585                         StreamReader sr = new StreamReader(_errFile);
586                         string message = string.Empty, line = null, file = null, lineInFile = "0";
587
588                         while ((line = sr.ReadLine()) != null)
589                         {
590                                 if (line.StartsWith("Message: "))
591                                         message = line.Substring("Message: ".Length);
592                                 else if (line.StartsWith("File: "))
593                                         file = line.Substring("File: ".Length);
594                                 else if (line.StartsWith("Line: "))
595                                         lineInFile = line.Substring("Line: ".Length);
596                         }
597
598                         sr.Close();
599
600                         if (file != null)
601                         {
602                                 Location loc = new Location(null);
603                                 loc.Filename = file;
604                                 loc.BeginLine = int.Parse(lineInFile);
605                                 return new ParseException(loc,message);
606                         }
607
608                         if (message.IndexOf(typeof(FileNotFoundException).Name) != -1 &&
609                                 message.IndexOf(_url.Trim('\\','/').Replace('/','\\')) != -1)
610                                 message = "The requested resource (" + _url + ") is not available.";
611                         return new HttpException(404,(message !=  null ? message : string.Empty));
612                 }
613         }
614 }