2 // (C) 2005 Mainsoft Corporation (http://www.mainsoft.com)
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:
14 // The above copyright notice and this permission notice shall be
15 // included in all copies or substantial portions of the Software.
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.
29 using System.Collections;
30 using System.Web.Compilation;
31 using System.Collections.Specialized;
32 using System.Threading;
34 using System.Reflection;
36 namespace System.Web.J2EE
39 /// Class that allows reading assemblies.xml file for getting information about different types.
41 public class PageMapper
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();
49 AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler (CurrentDomain_AssemblyResolve);
52 //Try to load the global resources
53 HttpContext.AppGlobalResourcesAssembly = GetCachedAssembly ("app_globalresources");
61 static Assembly CurrentDomain_AssemblyResolve (object sender, ResolveEventArgs args)
63 Assembly resolvedAssembly = null;
66 resolvedAssembly = GetCachedAssembly (args.Name);
70 resolvedAssembly = null;
73 return resolvedAssembly;
75 public static string GetFromMapPathCache(string key)
77 Hashtable answer = null;
78 lock(LOCK_GETFROMMAPPATHCACHE)
80 answer = (Hashtable) AppDomain.CurrentDomain.GetData(J2EEConsts.MAP_PATH_CACHE);
83 answer = new Hashtable();
84 CachedDocumentTypeStorage storage = (CachedDocumentTypeStorage)GetAssembliesCachedDocument();
85 IDictionaryEnumerator e = storage.GetEnumerator();
89 string currentFile = (string)((DictionaryEntry)e.Current).Key;
90 answer[currentFile]= IAppDomainConfig.WAR_ROOT_SYMBOL + currentFile;
92 AppDomain.CurrentDomain.SetData(J2EEConsts.MAP_PATH_CACHE,answer);
96 return (string)answer[key];
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()
106 Hashtable hashTable = (Hashtable) AppDomain.CurrentDomain.GetData(J2EEConsts.FILE_LIST_FILE);
107 if (hashTable == null)
112 Stream fs = (Stream)IOUtils.getStream(_fileListName);
115 AppDomain.CurrentDomain.SetData(J2EEConsts.FILE_LIST_FILE, new Hashtable());
119 doc = new XmlDocument();
124 // Console.WriteLine("filelist.xml was not found!!!");
125 AppDomain.CurrentDomain.SetData(J2EEConsts.FILE_LIST_FILE, new Hashtable());
128 // Console.WriteLine("filelist.xml was found!!!");
129 if (doc != null && doc.DocumentElement.HasChildNodes)
131 hashTable = CollectionsUtil.CreateCaseInsensitiveHashtable();
132 XmlNodeList nodeList = doc.DocumentElement.ChildNodes;
133 for (int i = 0;i < nodeList.Count ; i++)
135 string fileName = nodeList.Item(i).InnerText;
136 hashTable.Add(fileName,fileName);
138 AppDomain.CurrentDomain.SetData(J2EEConsts.FILE_LIST_FILE, hashTable);
144 private static ICachedXmlDoc GetAssembliesCachedDocument()
146 lock(LOCK_GETASSEMBLIESCACHEDDOCUMENT)
148 ICachedXmlDoc doc = (ICachedXmlDoc) AppDomain.CurrentDomain.GetData(J2EEConsts.ASSEMBLIES_FILE);
151 doc = CreateDocument();
153 AppDomain.CurrentDomain.SetData(J2EEConsts.ASSEMBLIES_FILE, doc);
160 private static String NormalizeName(string url)
163 url = System.Web.Util.UrlUtils.RemoveDoubleSlashes(url);
165 if (url.StartsWith(IAppDomainConfig.WAR_ROOT_SYMBOL))
166 url = url.Substring(IAppDomainConfig.WAR_ROOT_SYMBOL.Length);
169 private static ICachedXmlDoc CreateDocument()
171 return new CachedDocumentTypeStorage();
174 public static Type GetObjectType(string url)
176 return GetCachedType(NormalizeName(url), true);
179 public static Type GetObjectType (string url, bool throwException) {
180 return GetCachedType (NormalizeName (url), throwException);
183 public static Assembly GetObjectAssembly(string url)
185 return GetCachedAssembly(NormalizeName(url));
187 public static string GetAssemblyResource(string url)
189 return GetCachedResource(NormalizeName(url));
191 private static string GetCachedResource(string url)
193 ICachedXmlDoc doc = PageMapper.GetAssembliesCachedDocument();
194 return doc.GetAssemblyResourceName(url);
196 private static Assembly GetCachedAssembly(string url)
198 ICachedXmlDoc doc = PageMapper.GetAssembliesCachedDocument();
199 Assembly t = doc.GetAssembly(url);
201 throw new HttpException(404, "The requested resource (" + url + ") is not available.");
205 private static Type GetCachedType (string url) {
206 return GetCachedType (url, true);
208 private static Type GetCachedType (string url, bool throwException)
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.");
218 #region ICachedXmlDoc interface
219 interface ICachedXmlDoc
221 Type GetType(string key);
222 Assembly GetAssembly(string key);
223 string GetAssemblyResourceName(string key);
227 #region CachedDocumentTypeStorage class
228 class CachedDocumentTypeStorage : ICachedXmlDoc
230 private static readonly object _fuse = new object();
231 public static readonly ICachedXmlDoc DEFAULT_DOC =
232 new CachedDocumentTypeStorage(0);
234 private static readonly int DEFAULT_PAGES_NUMBER = 25;
236 private Hashtable _table;
238 private CachedDocumentTypeStorage(int initTableSize)
240 _table = Hashtable.Synchronized(new Hashtable(initTableSize));
243 public CachedDocumentTypeStorage() :
244 this(DEFAULT_PAGES_NUMBER)
247 string ICachedXmlDoc.GetAssemblyResourceName(string o)
249 return GetMetaByURL(o).Resource;
251 Type ICachedXmlDoc.GetType(string o)
253 return GetMetaByURL(o).Type;
255 Assembly ICachedXmlDoc.GetAssembly(string o)
257 return GetMetaByURL(o).Assembly;
260 internal IDictionaryEnumerator GetEnumerator()
262 return _table.GetEnumerator();
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)
273 #if !NO_GLOBAL_LOCK_ON_COMPILE
274 string lwUrl = url.ToLower();
277 object retVal = _table[lwUrl];
280 retVal = PageCompiler.GetCompiler(url);
281 _table[lwUrl] = retVal;
284 return (MetaProvider)retVal;
288 string lwUrl = url.ToLower();
289 if (!_table.ContainsKey(lwUrl))
291 lock (_table.SyncRoot)
293 if (_table.ContainsKey(lwUrl))
295 _table[lwUrl] = _fuse;
299 MetaProvider meta = PageCompiler.GetCompiler(url);
300 lock (_table.SyncRoot)
302 return (MetaProvider)(_table[lwUrl] = meta);
307 _table.Remove(lwUrl);
312 while (_table[lwUrl] == _fuse)
315 return !_table.ContainsKey(lwUrl)? PageCompiler.Error: (MetaProvider)_table[lwUrl];
324 public interface MetaProvider
327 Assembly Assembly {get;}
328 string Resource { get;}
330 public class PageCompiler : MetaProvider
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();
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;
346 PageCompiler(string url)
349 _xmlDescriptor = GetDescFromUrl();
350 _session = DateTime.Now.Ticks.ToString();
357 public static PageCompiler Error
361 return _errorProvider;
364 public static PageCompiler GetCompiler(string url)
367 return new PageCompiler(url);
375 Type MetaProvider.Type
381 Assembly MetaProvider.Assembly
387 string MetaProvider.Resource
391 return _origAssemblyName != null ? _origAssemblyName + ".ghres" : "dll.ghres";
394 private void LoadTypeAndAssem()
396 if (_assembly == null)
398 string typeName = GetCachedTypeName();
399 if (typeName != null)
401 if ((_type = Type.GetType(typeName)) != null)
402 _assembly = _type.Assembly;
404 _assembly = Assembly.Load(_origAssemblyName);
408 private void InternalCompile()
410 string fileName = Path.GetFileName(_url);
412 string fullFileName = (fileName.ToLower() == "global.asax") ? _url : HttpContext.Current.Request.MapPath(_url);
414 Console.WriteLine("fullFileName=" + fullFileName);
416 //type not found - run aspxparser
417 if (File.Exists(fullFileName) || Directory.Exists(fullFileName))
419 string[] command = GetParserCmd(fileName.ToLower() == "global.asax");
420 if (J2EEUtils.RunProc(command) != 0)
421 throw GetCompilerError();
425 string message = "The requested resource (" + _url + ") is not available.";
426 throw new HttpException(404, message);
429 private string GetDescriptorPath()
431 return String.Join("/", new string[] { "assemblies", _xmlDescriptor });
433 private string GetTypeNameFromAppFolder()
437 using (StreamReader sr = new StreamReader(HttpContext.Current.Request.MapPath("/" + GetDescriptorPath())))
439 return GetTypeFromDescStream(sr.BaseStream);
444 Console.WriteLine(ex);
448 internal string GetTypeFromResources()
450 string typeName = null;
452 //if the desciptor exists in the war - get the type
453 string descPath = GetDescriptorPath();
458 Console.WriteLine(descPath);
460 using (Stream fs = (Stream)IOUtils.getStreamRecursive("/" + descPath))
464 return GetTypeFromDescStream(fs);
471 Console.WriteLine(ex);
476 internal string GetCachedTypeName()
478 string typeName = GetTypeFromResources();
479 if (typeName == null)
481 //spawn dynamic compilation and lookup typename from created folder
483 typeName = GetTypeNameFromAppFolder();
487 private string GetTypeName()
489 return String.Format("{0}, {1}", _typeName, _origAssemblyName);
491 private bool LoadMetaFromXmlStream(Stream fs)
497 XmlDocument descXml = new XmlDocument();
499 _origAssemblyName = descXml.SelectSingleNode(PAGE_XPATH).Attributes[ASSEM_ATTRIB_NAME].Value;
500 _typeName = descXml.SelectSingleNode(PAGE_XPATH).Attributes[TYPE_ATTRIB_NAME].Value;
506 Console.WriteLine("Failed to load typename from stream");
513 private string GetTypeFromDescStream(Stream fs)
515 if (LoadMetaFromXmlStream(fs))
516 return GetTypeName();
520 private string[] GetParserCmd(bool globalAsax)
526 cmd[3] = "/buildglobalasax";
531 cmd[3] = "/aspxFiles:" + _url;
532 cmd[4] = "/compilepages";
534 cmd[0] = GetParser();
535 cmd[1] = "/session:" + _session;
536 cmd[2] = "/appDir:" + (string)AppDomain.CurrentDomain.GetData(IAppDomainConfig.APP_PHYS_DIR);
540 private string GetParser()
545 File.OpenText(HttpContext.Current.Request.MapPath("/AspxParser.params"));
546 _parser = sr.ReadLine();
553 private string GetDescFromUrl()
555 string fileName = Path.GetFileName(_url);
557 if (fileName.ToLower() == "global.asax")
558 return "global.asax.xml";
560 string id = GetIdFromUrl(_url);
561 string[] descName = new string[3] {fileName, id, ".xml"} ;
562 return string.Concat(descName).ToLower();
565 private string GetIdFromUrl(string path)
567 path = path.Trim('/');
568 string fileName = Path.GetFileName(path);
569 string id = string.Empty;
571 path = path.Substring (path.IndexOf ("/") + 1);
573 if (path.Length > fileName.Length)
574 id = "." + path.Substring(0,path.Length - fileName.Length).Replace('/','_');
578 private Exception GetCompilerError()
580 string _errFile = HttpContext.Current.Request.MapPath("/" + _session + ".vmwerr");
582 if (!File.Exists(_errFile))
583 throw new FileNotFoundException("Internal Error",_errFile);
585 StreamReader sr = new StreamReader(_errFile);
586 string message = string.Empty, line = null, file = null, lineInFile = "0";
588 while ((line = sr.ReadLine()) != null)
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);
602 Location loc = new Location(null);
604 loc.BeginLine = int.Parse(lineInFile);
605 return new ParseException(loc,message);
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));