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();
48 static Assembly CurrentDomain_AssemblyResolve (object sender, ResolveEventArgs args)
50 Assembly resolvedAssembly = null;
53 resolvedAssembly = GetCachedAssembly (HttpContext.Current, args.Name);
58 Console.WriteLine (ex.ToString ());
60 resolvedAssembly = null;
63 return resolvedAssembly;
68 public static string GetFromMapPathCache(string key)
70 Hashtable answer = null;
71 lock(LOCK_GETFROMMAPPATHCACHE)
73 answer = (Hashtable) AppDomain.CurrentDomain.GetData(J2EEConsts.MAP_PATH_CACHE);
76 answer = new Hashtable();
77 CachedDocumentTypeStorage storage = (CachedDocumentTypeStorage)GetAssembliesCachedDocument();
78 IDictionaryEnumerator e = storage.GetEnumerator();
82 string currentFile = (string)((DictionaryEntry)e.Current).Key;
83 answer[currentFile]= IAppDomainConfig.WAR_ROOT_SYMBOL + currentFile;
85 AppDomain.CurrentDomain.SetData(J2EEConsts.MAP_PATH_CACHE,answer);
88 return (string)answer[key];
92 //The method was used by runtime to force file names casesensitivity
93 // problem. The filelist.xml file should contain correct file names,
94 // but currently it is unused
95 public static void LoadFileList()
97 Hashtable hashTable = (Hashtable) AppDomain.CurrentDomain.GetData(J2EEConsts.FILE_LIST_FILE);
98 if (hashTable == null)
103 Stream fs = (Stream)IOUtils.getStream(_fileListName);
106 AppDomain.CurrentDomain.SetData(J2EEConsts.FILE_LIST_FILE, new Hashtable());
110 doc = new XmlDocument();
115 // Console.WriteLine("filelist.xml was not found!!!");
116 AppDomain.CurrentDomain.SetData(J2EEConsts.FILE_LIST_FILE, new Hashtable());
119 // Console.WriteLine("filelist.xml was found!!!");
120 if (doc != null && doc.DocumentElement.HasChildNodes)
122 hashTable = CollectionsUtil.CreateCaseInsensitiveHashtable();
123 XmlNodeList nodeList = doc.DocumentElement.ChildNodes;
124 for (int i = 0;i < nodeList.Count ; i++)
126 string fileName = nodeList.Item(i).InnerText;
127 hashTable.Add(fileName,fileName);
129 AppDomain.CurrentDomain.SetData(J2EEConsts.FILE_LIST_FILE, hashTable);
135 private static ICachedXmlDoc GetAssembliesCachedDocument(HttpContext context)
137 ICachedXmlDoc doc = (ICachedXmlDoc) AppDomain.CurrentDomain.GetData (J2EEConsts.ASSEMBLIES_FILE);
140 lock (LOCK_GETASSEMBLIESCACHEDDOCUMENT) {
141 doc = (ICachedXmlDoc) AppDomain.CurrentDomain.GetData (J2EEConsts.ASSEMBLIES_FILE);
143 doc = CreateDocument ();
145 AppDomain.CurrentDomain.SetData (J2EEConsts.ASSEMBLIES_FILE, doc);
147 AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler (CurrentDomain_AssemblyResolve);
149 //Try to load the global resources
150 HttpContext.AppGlobalResourcesAssembly = GetCachedAssembly (context, context.Request.ApplicationPath + "/app_globalresources");
152 catch (Exception ex) {
154 Console.WriteLine (ex.ToString ());
165 private static String NormalizeName(string url)
168 url = System.Web.Util.UrlUtils.RemoveDoubleSlashes(url);
170 if (url.StartsWith(IAppDomainConfig.WAR_ROOT_SYMBOL))
171 url = url.Substring(IAppDomainConfig.WAR_ROOT_SYMBOL.Length);
174 private static ICachedXmlDoc CreateDocument()
176 return new CachedDocumentTypeStorage();
179 public static Type GetObjectType (HttpContext context, string url)
181 return GetCachedType(context, NormalizeName(url), true);
184 public static Type GetObjectType (HttpContext context, string url, bool throwException) {
185 return GetCachedType (context, NormalizeName (url), throwException);
188 public static Assembly GetObjectAssembly (HttpContext context, string url)
190 return GetCachedAssembly (context, NormalizeName (url));
192 public static string GetAssemblyResource (HttpContext context, string url)
194 return GetCachedResource (context, NormalizeName (url));
196 private static string GetCachedResource (HttpContext context, string url)
198 ICachedXmlDoc doc = PageMapper.GetAssembliesCachedDocument(context);
199 return doc.GetAssemblyResourceName (context, url);
201 private static Assembly GetCachedAssembly (HttpContext context, string url)
203 ICachedXmlDoc doc = PageMapper.GetAssembliesCachedDocument(context);
204 return doc.GetAssembly (context, url);
206 private static Type GetCachedType (HttpContext context, string url) {
207 return GetCachedType (context, url, true);
209 private static Type GetCachedType (HttpContext context, string url, bool throwException)
211 ICachedXmlDoc doc = PageMapper.GetAssembliesCachedDocument(context);
212 Type t = doc.GetType(context, url);
213 if (t == null && throwException)
214 throw new HttpException(404,"The requested resource (" + url + ") is not available.");
219 #region ICachedXmlDoc interface
220 interface ICachedXmlDoc
222 Type GetType (HttpContext context, string key);
223 Assembly GetAssembly (HttpContext context, string key);
224 string GetAssemblyResourceName (HttpContext context, string key);
228 #region CachedDocumentTypeStorage class
229 class CachedDocumentTypeStorage : ICachedXmlDoc
231 private static readonly object _fuse = new object();
232 public static readonly ICachedXmlDoc DEFAULT_DOC =
233 new CachedDocumentTypeStorage(0);
235 private static readonly int DEFAULT_PAGES_NUMBER = 25;
237 private Hashtable _table;
239 private CachedDocumentTypeStorage(int initTableSize)
241 _table = Hashtable.Synchronized(new Hashtable(initTableSize));
244 public CachedDocumentTypeStorage() :
245 this(DEFAULT_PAGES_NUMBER)
248 string ICachedXmlDoc.GetAssemblyResourceName (HttpContext context, string o)
250 return GetMetaByURL(context, o).Resource;
252 Type ICachedXmlDoc.GetType (HttpContext context, string o)
254 return GetMetaByURL(context, o).Type;
256 Assembly ICachedXmlDoc.GetAssembly (HttpContext context, string o)
258 return GetMetaByURL(context, o).Assembly;
261 internal IDictionaryEnumerator GetEnumerator()
263 return _table.GetEnumerator();
266 //rewamped for perfomance reasons
267 //It looks like issue is not important in development mode,
268 //but only will became important in production mode when dynamyc compilation will be enabled
269 //Anyway, locking whole table and compiling under lock looks odd
270 //spivak.December 07 2006
271 public MetaProvider GetMetaByURL(HttpContext context, string url)
274 #if !NO_GLOBAL_LOCK_ON_COMPILE
275 string lwUrl = url.ToLowerInvariant();
278 object retVal = _table[lwUrl];
281 retVal = PageCompiler.GetCompiler(context, url);
282 _table[lwUrl] = retVal;
285 return (MetaProvider)retVal;
289 string lwUrl = url.ToLower();
290 if (!_table.ContainsKey(lwUrl))
292 lock (_table.SyncRoot)
294 if (_table.ContainsKey(lwUrl))
296 _table[lwUrl] = _fuse;
300 MetaProvider meta = PageCompiler.GetCompiler(url);
301 lock (_table.SyncRoot)
303 return (MetaProvider)(_table[lwUrl] = meta);
308 _table.Remove(lwUrl);
313 while (_table[lwUrl] == _fuse)
316 return !_table.ContainsKey(lwUrl)? PageCompiler.Error: (MetaProvider)_table[lwUrl];
325 public interface MetaProvider
328 Assembly Assembly {get;}
329 string Resource { get;}
331 public class PageCompiler : MetaProvider
333 private static readonly string PAGE_XPATH = "preserve";
334 private static readonly string ASSEM_ATTRIB_NAME = "assem";
335 private static readonly string TYPE_ATTRIB_NAME = "type";
336 private static string _parser = null;
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 readonly private HttpContext _context;
347 PageCompiler(HttpContext context, string url)
351 _xmlDescriptor = GetDescFromUrl();
352 _session = DateTime.Now.Ticks.ToString();
356 public static PageCompiler GetCompiler(HttpContext context, string url)
358 return new PageCompiler(context, url);
361 Type MetaProvider.Type
367 Assembly MetaProvider.Assembly
373 string MetaProvider.Resource
377 return _origAssemblyName != null ? _origAssemblyName + ".ghres" : "dll.ghres";
380 private void LoadTypeAndAssem()
382 if (_assembly == null)
384 string typeName = GetCachedTypeName();
385 if (typeName != null)
387 if ((_type = Type.GetType(typeName)) != null)
388 _assembly = _type.Assembly;
390 _assembly = Assembly.Load(_origAssemblyName);
394 private bool InternalCompile()
396 string fileName = VirtualPathUtility.GetFileName (_url);
398 string fullFileName = (fileName.ToLower () == "global.asax") ? _url : _context.Request.MapPath (_url);
400 Console.WriteLine("fullFileName=" + fullFileName);
402 //type not found - run aspxparser
403 if (File.Exists(fullFileName) || Directory.Exists(fullFileName))
405 string[] command = GetParserCmd(fileName.ToLower() == "global.asax");
406 if (J2EEUtils.RunProc(command) != 0)
407 throw GetCompilerError();
414 //string message = "The requested resource (" + _url + ") is not available.";
415 //throw new HttpException(404, message);
418 private string GetDescriptorPath()
420 return String.Join("/", new string[] { "assemblies", _xmlDescriptor });
422 private string GetTypeNameFromAppFolder()
426 using (StreamReader sr = new StreamReader(_context.Request.MapPath("~/" + GetDescriptorPath())))
428 return GetTypeFromDescStream(sr.BaseStream);
433 Console.WriteLine(ex);
437 internal string GetTypeFromResources()
439 string typeName = null;
441 //if the desciptor exists in the war - get the type
442 string descPath = GetDescriptorPath();
447 Console.WriteLine(descPath);
449 using (Stream fs = (Stream)IOUtils.getStreamRecursive("/" + descPath))
453 return GetTypeFromDescStream(fs);
460 Console.WriteLine(ex);
465 internal string GetCachedTypeName()
467 string typeName = GetTypeFromResources();
468 if (typeName == null)
470 //spawn dynamic compilation and lookup typename from created folder
471 if (InternalCompile())
472 typeName = GetTypeNameFromAppFolder();
476 private string GetTypeName()
478 return String.Format("{0}, {1}", _typeName, _origAssemblyName);
480 private bool LoadMetaFromXmlStream(Stream fs)
486 XmlDocument descXml = new XmlDocument();
488 _origAssemblyName = descXml.SelectSingleNode(PAGE_XPATH).Attributes[ASSEM_ATTRIB_NAME].Value;
489 _typeName = descXml.SelectSingleNode(PAGE_XPATH).Attributes[TYPE_ATTRIB_NAME].Value;
495 Console.WriteLine("Failed to load typename from stream");
502 private string GetTypeFromDescStream(Stream fs)
504 if (LoadMetaFromXmlStream(fs))
505 return GetTypeName();
509 private string[] GetParserCmd(bool globalAsax)
515 cmd[3] = "/buildglobalasax";
520 cmd[3] = "/aspxFiles:" + _url;
521 cmd[4] = "/compilepages";
523 cmd[0] = GetParser();
524 cmd[1] = "/session:" + _session;
525 cmd[2] = "/appDir:" + (string)AppDomain.CurrentDomain.GetData(IAppDomainConfig.APP_PHYS_DIR);
529 private string GetParser()
534 File.OpenText (_context.Request.MapPath ("~/AspxParser.params"));
535 _parser = sr.ReadLine();
542 private string GetDescFromUrl()
544 string fileName = VirtualPathUtility.GetFileName (_url);
546 if (fileName.ToLower() == "global.asax")
547 return "global.asax.xml";
549 string id = GetIdFromUrl(_url);
550 string[] descName = new string[3] {fileName, id, ".xml"} ;
551 return string.Concat(descName).ToLower();
554 private string GetIdFromUrl(string path)
556 string fileName = VirtualPathUtility.GetFileName(path);
557 string id = string.Empty;
559 if (VirtualPathUtility.IsAbsolute (path))
560 path = path.Substring (_context.Request.ApplicationPath.Length + 1);
562 if (path.Length > fileName.Length)
563 id = "." + path.Substring(0,path.Length - fileName.Length).Replace('/','_');
567 private Exception GetCompilerError()
569 string _errFile = _context.Request.MapPath ("~/" + _session + ".vmwerr");
571 if (!File.Exists(_errFile))
572 throw new FileNotFoundException("Internal Error",_errFile);
574 StreamReader sr = new StreamReader(_errFile);
575 string message = string.Empty, line = null, file = null, lineInFile = "0";
577 while ((line = sr.ReadLine()) != null)
579 if (line.StartsWith("Message: "))
580 message = line.Substring("Message: ".Length);
581 else if (line.StartsWith("File: "))
582 file = line.Substring("File: ".Length);
583 else if (line.StartsWith("Line: "))
584 lineInFile = line.Substring("Line: ".Length);
591 Location loc = new Location(null);
593 loc.BeginLine = int.Parse(lineInFile);
594 return new ParseException(loc,message);
597 if (message.IndexOf(typeof(FileNotFoundException).Name) != -1 &&
598 message.IndexOf(_url.Trim('\\','/').Replace('/','\\')) != -1)
599 message = "The requested resource (" + _url + ") is not available.";
600 return new HttpException(404,(message != null ? message : string.Empty));