correctly remove the ApplicationPath from url
[mono.git] / mcs / class / System.Web / System.Web.J2EE / PageMapper.cs
index c6198a289c1491376edb807fc8b48168066dd26c..2e7a8a824b8e90d2a403e14abe5058c53012bafc 100644 (file)
@@ -31,6 +31,7 @@ using System.Web.Compilation;
 using System.Collections.Specialized;
 using System.Threading;
 using vmw.common;
+using System.Reflection;
 
 namespace System.Web.J2EE
 {
@@ -39,9 +40,30 @@ namespace System.Web.J2EE
        /// </summary>
        public class PageMapper
        {
-               private static readonly string _fileListName = "/filelist.xml";
+               //private static readonly string _fileListName = "/filelist.xml";
                private static readonly object LOCK_GETASSEMBLIESCACHEDDOCUMENT = new object();
-               private static readonly object LOCK_GETFROMMAPPATHCACHE = new object();
+               //private static readonly object LOCK_GETFROMMAPPATHCACHE = new object();
+
+
+               static Assembly CurrentDomain_AssemblyResolve (object sender, ResolveEventArgs args)
+               {
+                       Assembly resolvedAssembly = null;
+                       try
+                       {
+                               resolvedAssembly = GetCachedAssembly (HttpContext.Current, args.Name);
+                       }
+                       catch (Exception ex)
+                       {
+#if DEBUG
+                               Console.WriteLine (ex.ToString ());
+#endif
+                               resolvedAssembly = null;
+                       }
+
+                       return resolvedAssembly;
+               }
+
+#if UNUSED
 
                public static string GetFromMapPathCache(string key)
                {
@@ -61,7 +83,6 @@ namespace System.Web.J2EE
                                                answer[currentFile]= IAppDomainConfig.WAR_ROOT_SYMBOL + currentFile;
                                        }
                                        AppDomain.CurrentDomain.SetData(J2EEConsts.MAP_PATH_CACHE,answer);
-
                                }
                        }
                        return (string)answer[key];
@@ -110,47 +131,86 @@ namespace System.Web.J2EE
                        }
 
                }
-
-               private static ICachedXmlDoc GetAssembliesCachedDocument()
+#endif
+               private static ICachedXmlDoc GetAssembliesCachedDocument(HttpContext context)
                {
-                       lock(LOCK_GETASSEMBLIESCACHEDDOCUMENT)
-                       {
-                               ICachedXmlDoc doc = (ICachedXmlDoc) AppDomain.CurrentDomain.GetData(J2EEConsts.ASSEMBLIES_FILE);
-                               if (doc == null)
-                               {
-                                       doc = CreateDocument();
-                                       if (doc != null)
-                                               AppDomain.CurrentDomain.SetData(J2EEConsts.ASSEMBLIES_FILE, doc);
+                       ICachedXmlDoc doc = (ICachedXmlDoc) AppDomain.CurrentDomain.GetData (J2EEConsts.ASSEMBLIES_FILE);
+
+                       if (doc == null) {
+                               lock (LOCK_GETASSEMBLIESCACHEDDOCUMENT) {
+                                       doc = (ICachedXmlDoc) AppDomain.CurrentDomain.GetData (J2EEConsts.ASSEMBLIES_FILE);
+                                       if (doc == null) {
+                                               doc = CreateDocument ();
+                                               if (doc != null) {
+                                                       AppDomain.CurrentDomain.SetData (J2EEConsts.ASSEMBLIES_FILE, doc);
+
+                                                       AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler (CurrentDomain_AssemblyResolve);
+                                                       try {
+                                                               //Try to load the  global resources
+                                                               HttpContext.AppGlobalResourcesAssembly = GetCachedAssembly (context,  context.Request.ApplicationPath + "/app_globalresources");
+                                                       }
+                                                       catch (Exception ex) {
+#if DEBUG
+                                                               Console.WriteLine (ex.ToString ());
+#endif
+                                                       }
+                                               }
+                                       }
                                }
-
-                               return doc;
                        }
+
+                       return doc;
                }
 
+               private static String NormalizeName(string url)
+               {
+#if NET_2_0
+                       url = System.Web.Util.UrlUtils.RemoveDoubleSlashes(url);
+#endif 
+                       if (url.StartsWith(IAppDomainConfig.WAR_ROOT_SYMBOL))
+                               url = url.Substring(IAppDomainConfig.WAR_ROOT_SYMBOL.Length);
+                       return url;
+               }
                private static ICachedXmlDoc CreateDocument()
                {
                        return new CachedDocumentTypeStorage();
                }
 
-               public static Type GetObjectType(string url)
+               public static Type GetObjectType (HttpContext context, string url)
                {
-#if NET_2_0
-                       return GetCachedType(System.Web.Util.UrlUtils.RemoveDoubleSlashes(url));
-#else
-                       return GetCachedType(url);
-#endif
+                       return GetCachedType(context, NormalizeName(url), true);
                }
 
-               private static Type GetCachedType(string url)
-               {
-                       ICachedXmlDoc doc = PageMapper.GetAssembliesCachedDocument();
-                       
-                       if (url.StartsWith(IAppDomainConfig.WAR_ROOT_SYMBOL))
-                               url = url.Substring(IAppDomainConfig.WAR_ROOT_SYMBOL.Length);
-                       
-                       Type t = doc.Get(url);
+               public static Type GetObjectType (HttpContext context, string url, bool throwException) {
+                       return GetCachedType (context, NormalizeName (url), throwException);
+               }
 
-                       if (t == null)
+               public static Assembly GetObjectAssembly (HttpContext context, string url)
+               {
+                       return GetCachedAssembly (context, NormalizeName (url));
+               }
+               public static string GetAssemblyResource (HttpContext context, string url)
+               {
+                       return GetCachedResource (context, NormalizeName (url));
+               }
+               private static string GetCachedResource (HttpContext context, string url)
+               {
+                       ICachedXmlDoc doc = PageMapper.GetAssembliesCachedDocument(context);
+                       return doc.GetAssemblyResourceName (context, url);
+               }
+               private static Assembly GetCachedAssembly (HttpContext context, string url)
+               {
+                       ICachedXmlDoc doc = PageMapper.GetAssembliesCachedDocument(context);
+                       return doc.GetAssembly (context, url);
+               }
+               private static Type GetCachedType (HttpContext context, string url) {
+                       return GetCachedType (context, url, true);
+               }
+               private static Type GetCachedType (HttpContext context, string url, bool throwException)
+               {
+                       ICachedXmlDoc doc = PageMapper.GetAssembliesCachedDocument(context);                                            
+                       Type t = doc.GetType(context, url);
+                       if (t == null && throwException)
                                throw new HttpException(404,"The requested resource (" + url + ") is not available.");
 
                        return t;
@@ -159,14 +219,16 @@ namespace System.Web.J2EE
                #region ICachedXmlDoc interface
                interface ICachedXmlDoc
                {
-                       Type Get(string key);
-                       //bool ContainsKey(object key);
+                       Type GetType (HttpContext context, string key);
+                       Assembly GetAssembly (HttpContext context, string key);
+                       string GetAssemblyResourceName (HttpContext context, string key);
                }
                #endregion
 
                #region CachedDocumentTypeStorage class
                class CachedDocumentTypeStorage : ICachedXmlDoc
                {
+                       private static readonly object _fuse = new object();
                        public static readonly ICachedXmlDoc DEFAULT_DOC =
                                new CachedDocumentTypeStorage(0);
 
@@ -183,9 +245,17 @@ namespace System.Web.J2EE
                                this(DEFAULT_PAGES_NUMBER)
                        {}
 
-                       Type ICachedXmlDoc.Get(string o)
+                       string ICachedXmlDoc.GetAssemblyResourceName (HttpContext context, string o)
                        {
-                               return GetTypeByURL(o);
+                               return GetMetaByURL(context, o).Resource;
+                       }
+                       Type ICachedXmlDoc.GetType (HttpContext context, string o)
+                       {
+                               return GetMetaByURL(context, o).Type;
+                       }
+                       Assembly ICachedXmlDoc.GetAssembly (HttpContext context, string o)
+                       {
+                               return GetMetaByURL(context, o).Assembly;
                        }
 
                        internal IDictionaryEnumerator GetEnumerator()
@@ -193,28 +263,72 @@ namespace System.Web.J2EE
                                return _table.GetEnumerator();                          
                        }       
 
-                       public Type GetTypeByURL(string url)
+                       //rewamped for perfomance reasons
+                       //It looks like issue is not important in development mode,
+                       //but only will became important in production mode when dynamyc compilation will be enabled
+                       //Anyway, locking whole table and compiling under lock looks odd
+                       //spivak.December 07 2006
+                       public MetaProvider GetMetaByURL(HttpContext context, string url)
                        {
-                               string lwUrl = url.ToLower();
+
+#if !NO_GLOBAL_LOCK_ON_COMPILE
+                               string lwUrl = url.ToLowerInvariant();
                                lock (_table)
                                {
                                        object retVal = _table[lwUrl];
                                        if (retVal == null)
                                        {
-                                               PageCompiler compiler = new PageCompiler(url);
-                                               retVal = compiler.GetCachedType();
+                                               retVal = PageCompiler.GetCompiler(context, url);
                                                _table[lwUrl] = retVal;
                                        }
                                
-                                       return (Type)retVal;
+                                       return (MetaProvider)retVal;
                                }
+
+#else
+                               string lwUrl = url.ToLower();
+                               if (!_table.ContainsKey(lwUrl))
+                               {
+                                       lock (_table.SyncRoot)
+                                       {
+                                               if (_table.ContainsKey(lwUrl))
+                                                       goto Fused;
+                                               _table[lwUrl] = _fuse;
+                                       }
+                                       try
+                                       {
+                                               MetaProvider meta = PageCompiler.GetCompiler(url);
+                                               lock (_table.SyncRoot)
+                                               {
+                                                       return (MetaProvider)(_table[lwUrl] = meta);
+                                               }
+                                       }
+                                       catch(Exception e)
+                                       {
+                                               _table.Remove(lwUrl);
+                                       }
+                               }                               
+                       Fused:
+                               
+                               while (_table[lwUrl] == _fuse) 
+                                       Thread.Sleep(10);
+
+                               return !_table.ContainsKey(lwUrl)? PageCompiler.Error: (MetaProvider)_table[lwUrl];
+#endif
                        }
                }
-               #endregion
+               
 
+               #endregion
        }
 
-       public class PageCompiler
+       public interface  MetaProvider
+       {
+               Type Type { get;}
+               Assembly Assembly {get;}
+               string Resource { get;}
+       }
+       public class PageCompiler : MetaProvider
        {
                private static readonly string PAGE_XPATH = "preserve";
                private static readonly string ASSEM_ATTRIB_NAME = "assem";
@@ -222,36 +336,122 @@ namespace System.Web.J2EE
                private static string _parser = null;
 
                private Type _type = null;
+               private string _typeName = null;
+               private Assembly _assembly = null;
+               private string _origAssemblyName = null;
                private string _xmlDescriptor = null;
                private string _url = null;
                private string _session = null;
+               readonly private HttpContext _context;
 
-               public PageCompiler(string url)
+               PageCompiler(HttpContext context, string url)
                {
                        _url = url;
+                       _context = context;
                        _xmlDescriptor = GetDescFromUrl();
                        _session = DateTime.Now.Ticks.ToString();
+                       LoadTypeAndAssem();
+               }
+
+               public static PageCompiler GetCompiler(HttpContext context, string url)
+               {
+                       return new PageCompiler(context, url);
                }
 
-               public Type GetCachedType()
+               Type MetaProvider.Type
                {
-                       if (_type != null)
+                       get{
                                return _type;
-                       
+                       }
+               }
+               Assembly MetaProvider.Assembly
+               {
+                       get{
+                               return _assembly;
+                       }
+               }
+               string MetaProvider.Resource
+               {
+                       get
+                       {
+                               return _origAssemblyName != null ? _origAssemblyName + ".ghres" : "dll.ghres";
+                       }
+               }
+               private void LoadTypeAndAssem()
+               {
+                       if (_assembly == null)
+                       {
+                               string typeName = GetCachedTypeName();
+                               if (typeName != null)
+                               {
+                                       if ((_type = Type.GetType(typeName)) != null)
+                                               _assembly = _type.Assembly;
+                                       else
+                                               _assembly = Assembly.Load(_origAssemblyName);
+                               }
+                       }
+               }
+               private bool InternalCompile()
+               {
+                       string fileName = VirtualPathUtility.GetFileName (_url);
+
+                       string fullFileName = (fileName.ToLower () == "global.asax") ? _url : _context.Request.MapPath (_url);
+#if DEBUG
+                       Console.WriteLine("fullFileName=" + fullFileName);
+#endif
+                       //type not found - run aspxparser
+                       if (File.Exists(fullFileName) || Directory.Exists(fullFileName))
+                       {
+                               string[] command = GetParserCmd(fileName.ToLower() == "global.asax");
+                               if (J2EEUtils.RunProc(command) != 0)
+                                       throw GetCompilerError();
+
+                               return true;
+                       }
+                       else
+                       {
+                               return false;
+                               //string message = "The requested resource (" + _url + ") is not available.";
+                               //throw new HttpException(404, message);
+                       }
+               }
+               private string GetDescriptorPath()
+               {
+                       return String.Join("/", new string[] { "assemblies", _xmlDescriptor });
+               }
+               private string GetTypeNameFromAppFolder()
+               {
+                       try
+                       {
+                               using (StreamReader sr = new StreamReader(_context.Request.MapPath("~/" + GetDescriptorPath())))
+                               {
+                                       return GetTypeFromDescStream(sr.BaseStream);
+                               }
+                       }
+                       catch (Exception ex)
+                       {
+                               Console.WriteLine(ex);
+                               throw ex;
+                       }
+               }
+               internal string GetTypeFromResources()
+               {
                        string typeName = null;
-               
+
                        //if the desciptor exists in the war - get the type
-                       string descPath = String.Join("/", new string[]{"assemblies", _xmlDescriptor});
+                       string descPath = GetDescriptorPath();
 
                        try
                        {
 #if DEBUG
                                Console.WriteLine(descPath);
 #endif
-                               Stream fs = (Stream)IOUtils.getStream("/" + descPath);
-                               if (fs != null)
+                               using (Stream fs = (Stream)IOUtils.getStreamRecursive("/" + descPath))
                                {
-                                       typeName = GetTypeFromDescStream(fs);
+                                       if (fs != null)
+                                       {
+                                               return GetTypeFromDescStream(fs);
+                                       }
                                }
                        }
                        catch (Exception ex)
@@ -259,71 +459,50 @@ namespace System.Web.J2EE
 #if DEBUG
                                Console.WriteLine(ex);
 #endif
-                               //desc not in the war
-                               typeName = null;
                        }
-
-                       if (typeName != null)
+                       return null;
+               }
+               internal string GetCachedTypeName()
+               {                       
+                       string typeName = GetTypeFromResources();
+                       if (typeName == null)
                        {
-                               _type = Type.GetType(typeName);
-                               return _type;
+                               //spawn dynamic compilation and lookup typename from created folder
+                               if (InternalCompile())
+                                       typeName = GetTypeNameFromAppFolder();
                        }
-                       
-                       string fileName = Path.GetFileName(_url);
-            
-                       if (fileName.ToLower() != "defaultwsdlhelpgenerator.aspx")
+                       return typeName;
+               }
+               private string GetTypeName()
+               {
+                       return String.Format("{0}, {1}", _typeName, _origAssemblyName); 
+               }
+               private bool LoadMetaFromXmlStream(Stream fs)
+               {
+                       if (fs != null)
                        {
-                string fullFileName = (fileName.ToLower() == "global.asax") ? _url : HttpContext.Current.Request.MapPath(_url);
-#if DEBUG
-                Console.WriteLine("fullFileName=" + fullFileName);
-#endif                
-                               if ( File.Exists(fullFileName) || Directory.Exists(fullFileName)) {
-                                       //type not found - run aspxparser
-                    string[] command = GetParserCmd(fileName.ToLower() == "global.asax");
-                                       if (J2EEUtils.RunProc(command) != 0)
-                                               throw GetCompilerError();
-                               }
-                               else {
-                                       string message = "The requested resource (" + _url + ") is not available.";
-                                       throw new HttpException(404, message);
-                               }
-
-                               //if the desciptor exists in the real app dir - get the type
-                               try {
-                                       StreamReader sr = new StreamReader (HttpContext.Current.Request.MapPath ("/" + descPath));
-                                       typeName = GetTypeFromDescStream (sr.BaseStream);
-                                       sr.Close ();
+                               try
+                               {
+                                       XmlDocument descXml = new XmlDocument();
+                                       descXml.Load(fs);
+                                       _origAssemblyName = descXml.SelectSingleNode(PAGE_XPATH).Attributes[ASSEM_ATTRIB_NAME].Value;
+                                       _typeName = descXml.SelectSingleNode(PAGE_XPATH).Attributes[TYPE_ATTRIB_NAME].Value;
+                                       return true;
                                }
-                               catch (Exception ex) {
-                                       Console.WriteLine (ex);
-                                       throw ex;
+                               catch
+                               {
+#if DEBUG
+                                       Console.WriteLine("Failed to load typename from stream");
+#endif
                                }
                        }
-                       else
-                               typeName = "ASP.defaultwsdlhelpgenerator_jvm_aspx";
-
-                       if (typeName != null)
-                       {
-                               _type = Type.GetType(typeName);
-                               return _type;
-                       }
-
-                       return null;
+                       return false;
                }
 
                private string GetTypeFromDescStream(Stream fs)
                {
-                       if (fs != null)
-                       {
-                               XmlDocument descXml = new XmlDocument();
-                               descXml.Load(fs);
-                               string assem = descXml.SelectSingleNode(PAGE_XPATH).Attributes[ASSEM_ATTRIB_NAME].Value;
-                               string shortType = descXml.SelectSingleNode(PAGE_XPATH).Attributes[TYPE_ATTRIB_NAME].Value;
-                               string typeName = String.Format("{0}, {1}",shortType,assem);
-                               fs.Close();
-                               return typeName;
-                       }
-
+                       if (LoadMetaFromXmlStream(fs))
+                               return GetTypeName();
                        return null;
                }
 
@@ -352,7 +531,7 @@ namespace System.Web.J2EE
                        if (_parser == null)
                        {
                                StreamReader sr =
-                                       File.OpenText(HttpContext.Current.Request.MapPath("/AspxParser.params"));
+                                       File.OpenText (_context.Request.MapPath ("~/AspxParser.params"));
                                _parser = sr.ReadLine();
                                sr.Close();
                        }
@@ -362,7 +541,7 @@ namespace System.Web.J2EE
 
                private string GetDescFromUrl()
                {
-                       string fileName = Path.GetFileName(_url);
+                       string fileName = VirtualPathUtility.GetFileName (_url);
                        
                        if (fileName.ToLower() == "global.asax")
                                return "global.asax.xml";
@@ -374,11 +553,11 @@ namespace System.Web.J2EE
 
                private string GetIdFromUrl(string path)
                {
-                       path = path.Trim('/');
-                       string fileName = Path.GetFileName(path);
+                       string fileName = VirtualPathUtility.GetFileName(path);
                        string id = string.Empty;
 
-                       path = path.Substring (path.IndexOf ("/") + 1);
+                       if (VirtualPathUtility.IsAbsolute (path))
+                               path = path.Substring (_context.Request.ApplicationPath.Length + 1);
 
                        if (path.Length > fileName.Length)
                                id = "." + path.Substring(0,path.Length - fileName.Length).Replace('/','_');
@@ -387,7 +566,7 @@ namespace System.Web.J2EE
 
                private Exception GetCompilerError()
                {
-                       string _errFile = HttpContext.Current.Request.MapPath("/" + _session + ".vmwerr");
+                       string _errFile = _context.Request.MapPath ("~/" + _session + ".vmwerr");
                        
                        if (!File.Exists(_errFile))
                                throw new FileNotFoundException("Internal Error",_errFile);