#region License
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
#region Imports
using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Reflection;
using System.Web;
using System.Web.Caching;
using System.Web.Compilation;
using System.Web.SessionState;
using System.Web.UI;
using Common.Logging;
#endregion
namespace Spring.Util{
/// <summary>
/// Provides platform independent access to HttpRuntime methods
/// </summary>
/// <remarks>
/// For e.g. testing purposes, the default environment implementation may be replaced using <see cref="SetInstance(IVirtualEnvironment)" />.
/// </remarks>
/// <author>Erich Eichinger</author>
public sealed class VirtualEnvironment
{
// default to standard HttpRuntime
private static IVirtualEnvironment instance = new HttpRuntimeEnvironment();
/// <summary>
/// Represents a method that handles Request related events
/// </summary>
public delegate void RequestEventHandler(HttpContext context);
/// <summary>
/// Represents a method that handles Session related events
/// </summary>
public delegate void SessionEventHandler(HttpSessionState session, CacheItemRemovedReason reason);
private static readonly object syncEndRequestEvent = new object();
private static RequestEventHandler s_requestEvent;
private static readonly object syncEndSessionEvent = new object();
private static SessionEventHandler s_sessionEvent;
private static volatile bool s_isInitialized = false;
/// <summary>
/// Replaces the current enviroment implementation.
/// </summary>
/// <param name="newEnvironment">the new environment implementation to be used</param>
/// <returns>the previously set environment instance</returns>
public static IVirtualEnvironment SetInstance(IVirtualEnvironment newEnvironment)
{
IVirtualEnvironment prevEnvironment = instance;
instance = newEnvironment;
return prevEnvironment;
}
#region default IVirtualEnvironment Adapter for HttpRuntime
/// <summary>
/// Implementation for running within HttpRuntime
/// </summary>
private class HttpRuntimeEnvironment : IVirtualEnvironment
{
#region HttpSessionState Adapter
private class SessionDictionaryAdapter : ISessionState
{
private readonly HttpSessionState _sessionState;
public SessionDictionaryAdapter(HttpSessionState sessionState)
{
_sessionState = sessionState;
}
public bool Contains(object key)
{
ICollection keys = _sessionState.Keys;
foreach (string sessionKey in keys)
{
if (object.Equals(sessionKey, (string)key)) return true;
}
return false;
}
public void Add(object key, object value)
{
_sessionState.Add((string)key, value);
}
public void Clear()
{
_sessionState.Clear();
}
public IDictionaryEnumerator GetEnumerator()
{
Hashtable tableCopy = new Hashtable();
ICollection keys = _sessionState.Keys;
foreach (string sessionKey in keys)
{
tableCopy.Add(sessionKey, _sessionState[sessionKey]);
}
return tableCopy.GetEnumerator();
}
public void Remove(object key)
{
_sessionState.Remove((string) key);
}
public object this[object key]
{
get { return _sessionState[(string)key]; }
set { _sessionState[(string)key] = value; }
}
public ICollection Keys
{
get { return _sessionState.Keys; }
}
public ICollection Values
{
get
{
object[] values = new object[_sessionState.Count];
_sessionState.CopyTo(values, 0);
return values;
}
}
public bool IsReadOnly
{
get { return _sessionState.IsReadOnly; }
}
public bool IsFixedSize
{
get { return false; }
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void CopyTo(Array array, int index)
{
_sessionState.CopyTo(array, index);
}
public int Count
{
get { return _sessionState.Count; }
}
public object SyncRoot
{
get { return _sessionState.SyncRoot; }
}
public bool IsSynchronized
{
get { return _sessionState.IsSynchronized; }
}
public void Abandon()
{
_sessionState.Abandon();
}
public bool IsCookieless
{
get { return _sessionState.IsCookieless; }
}
public bool IsNewSession
{
get { return _sessionState.IsNewSession; }
}
public int LCID
{
get { return _sessionState.LCID; }
set { _sessionState.LCID = value; }
}
public SessionStateMode Mode
{
get { return _sessionState.Mode; }
}
public string SessionID
{
get { return _sessionState.SessionID; }
}
public int CodePage
{
get { return _sessionState.CodePage; }
set { _sessionState.CodePage = value; }
}
#if NET_2_0 && !MONO
public HttpCookieMode CookieMode
{
get { return _sessionState.CookieMode; }
}
#endif
}
#endregion //HttpSessionState Adapter
private static readonly ILog log = LogManager.GetLogger(typeof (HttpRuntimeEnvironment));
#if NET_1_1
// Required method for resolving control types
private static MethodInfo miGetCompiledUserControlType = null;
static HttpRuntimeEnvironment()
{
Type tUserControlParser = typeof(System.Web.UI.UserControl).Assembly.GetType("System.Web.UI.UserControlParser");
miGetCompiledUserControlType = tUserControlParser.GetMethod("GetCompiledUserControlType", BindingFlags.Static | BindingFlags.NonPublic);
}
#endif
private class RewriteContext : IDisposable
{
private string originalPath;
private bool rebaseClientPath;
private HttpContext ctx;
public RewriteContext(string virtualDirectory, bool rebaseClientPath)
{
ctx = HttpContext.Current;
if (ctx == null)
{
return;
}
this.rebaseClientPath = rebaseClientPath;
string newVirtualPath = WebUtils.GetVirtualDirectory(virtualDirectory);
string currentFileDirectory = WebUtils.GetVirtualDirectory(ctx.Request.FilePath);
// only switch path if necessary
if (string.Compare(newVirtualPath, currentFileDirectory, true) != 0)
{
originalPath = ctx.Request.Url.PathAndQuery;
string newPath = newVirtualPath + "currentcontext.dummy";
#if NET_1_1
ctx.RewritePath(newPath);
#else
ctx.RewritePath(newPath, rebaseClientPath);
#endif
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug("rewriting path from " + currentFileDirectory + " to " + newPath + " results in " + ctx.Request.FilePath);
}
#endregion
}
}
public void Dispose()
{
if (originalPath != null)
{
if (log.IsDebugEnabled)
{
log.Debug("restoring path from " + ctx.Request.FilePath + " back to " + originalPath);
}
#if NET_1_1
ctx.RewritePath(originalPath);
#else
ctx.RewritePath(originalPath, rebaseClientPath);
#endif
}
}
}
public string ApplicationVirtualPath
{
get
{
string appPath = HttpRuntime.AppDomainAppVirtualPath;
if (!appPath.EndsWith("/")) appPath = appPath + "/";
return appPath;
}
}
public string CurrentVirtualPath
{
get { return HttpContext.Current.Request.Path; }
}
public string CurrentVirtualFilePath
{
get { return HttpContext.Current.Request.FilePath; }
}
public string CurrentExecutionFilePath
{
get { return HttpContext.Current.Request.CurrentExecutionFilePath; }
}
public NameValueCollection QueryString
{
get { return HttpContext.Current.Request.QueryString; }
}
public string MapPath(string virtualPath)
{
HttpContext ctx = HttpContext.Current;
if (ctx != null)
{
return ctx.Request.MapPath(virtualPath);
}
#if NET_1_1
throw new ArgumentException("can't map context relative path outside a context");
#else
if (VirtualPathUtility.IsAbsolute(virtualPath) && virtualPath.StartsWith(HttpRuntime.AppDomainAppVirtualPath))
{
virtualPath = VirtualPathUtility.ToAppRelative(virtualPath);
}
if (VirtualPathUtility.IsAppRelative(virtualPath))
{
virtualPath = virtualPath.Substring(2); // strip "~/"
string physicalPath = Path.Combine(HttpRuntime.AppDomainAppPath, virtualPath);
return physicalPath;
}
return virtualPath;
#endif
}
public IDisposable RewritePath(string virtualDirectory, bool rebaseClientPath)
{
return new RewriteContext(virtualDirectory, rebaseClientPath);
}
public ISessionState Session
{
get { return new SessionDictionaryAdapter(HttpContext.Current.Session); }
}
public IDictionary RequestVariables
{
get { return HttpContext.Current.Items; }
}
public NameValueCollection RequestParams
{
get { return HttpContext.Current.Request.Params; }
}
public Type GetCompiledType(string virtualPath)
{
string rootedVPath = WebUtils.CombineVirtualPaths(CurrentExecutionFilePath, virtualPath);
Type type = null;
#if NET_1_1
if (virtualPath.EndsWith(".aspx"))
{
type = CreateInstanceFromVirtualPath(virtualPath, typeof(Page)).GetType();
}
else if (virtualPath.EndsWith(".ascx"))
{
type = (Type)miGetCompiledUserControlType.Invoke(null, new object[] { rootedVPath, null, HttpContext.Current });
}
#else
type = BuildManager.GetCompiledType(rootedVPath); // requires rooted virtual path!
#endif
return type;
}
public object CreateInstanceFromVirtualPath(string virtualPath, Type requiredBaseType)
{
string rootedVPath = WebUtils.CombineVirtualPaths(CurrentExecutionFilePath, virtualPath);
object result;
#if NET_1_1
HttpContext ctx = HttpContext.Current;
string physicalPath = ctx.Server.MapPath(rootedVPath);
result = PageParser.GetCompiledPageInstance(virtualPath, physicalPath, ctx);
#else
result = BuildManager.CreateInstanceFromVirtualPath(rootedVPath, requiredBaseType);
#endif
if (!requiredBaseType.IsAssignableFrom(result.GetType()))
{
throw new HttpException(string.Format("Type '{0}' from virtual path '{1}' does not inherit from '{2}'", result.GetType(), rootedVPath, requiredBaseType));
}
return result;
}
}
#endregion
/// <summary>
/// The virtual (rooted) path of the current Application containing a leading '/' as well as a trailing '/'
/// </summary>
public static string ApplicationVirtualPath
{
get { return instance.ApplicationVirtualPath; }
}
/// <summary>
/// The virtual (rooted) path of the current Request including <see cref="HttpRequest.PathInfo"/>
/// </summary>
public static string CurrentVirtualPath
{
get { return instance.CurrentVirtualPath; }
}
/// <summary>
/// The virtual (rooted) path of the current Request including <see cref="HttpRequest.PathInfo"/>
/// </summary>
public static string CurrentVirtualPathAndQuery
{
get
{
string result = CurrentVirtualPath;
if (QueryString.Count > 0)
{
result = result + "?" + QueryString.ToString();
}
return result;
}
}
/// <summary>
/// The virtual (rooted) path of the current Request without trailing <see cref="HttpRequest.PathInfo"/>
/// </summary>
public static string CurrentVirtualFilePath
{
get { return instance.CurrentVirtualFilePath; }
}
/// <summary>
/// The virtual (rooted) path of the currently executing script
/// </summary>
public static string CurrentExecutionFilePath
{
get { return instance.CurrentExecutionFilePath; }
}
/// <summary>
/// The query parameters
/// </summary>
public static NameValueCollection QueryString
{
get { return instance.QueryString; }
}
/// <summary>
/// Returns the current Request's variable dictionary (<see cref="HttpContext.Items"/>)
/// </summary>
public static IDictionary RequestVariables
{
get { return instance.RequestVariables; }
}
/// <summary>
/// Returns the current Request's parameter dictionary (<see cref="HttpRequest.Params"/>)
/// </summary>
public static NameValueCollection RequestParams
{
get { return instance.RequestParams; }
}
/// <summary>
/// Maps a virtual path to it's physical location
/// </summary>
public static string MapPath(string virtualPath)
{
return instance.MapPath(virtualPath);
}
/// <summary>
/// Rewrites the <see cref="CurrentVirtualPath"/>, thus also affecting <see cref="MapPath"/>
/// </summary>
public static IDisposable RewritePath(string newVirtualPath, bool rebaseClientPath)
{
return instance.RewritePath(newVirtualPath, rebaseClientPath);
}
/// <summary>
/// Returns an instance of the specified file.
/// </summary>
public static object CreateInstanceFromVirtualPath(string virtualPath, Type requiredBaseType)
{
string rootedVPath = WebUtils.CombineVirtualPaths(instance.CurrentExecutionFilePath, virtualPath);
return instance.CreateInstanceFromVirtualPath(rootedVPath, requiredBaseType);
}
/// <summary>
/// Returns an the compiled type of the specified file.
/// </summary>
public static Type GetCompiledType(string virtualPath)
{
string rootedVPath = WebUtils.CombineVirtualPaths(instance.CurrentExecutionFilePath, virtualPath);
return instance.GetCompiledType(rootedVPath);
}
/// <summary>
/// Receives EndRequest-event from an <see cref="HttpApplication"/> instance
/// and dispatches it to all handlers registered with this module.
/// </summary>
/// <param name="sender">the HttpApplication instance sending this event</param>
/// <param name="e">always <see cref="EventArgs.Empty"/></param>
public static void RaiseEndRequest(object sender, EventArgs e)
{
// NOTE: don't sync here for performance reasons.
// It is assumed, that all handlers are registered during application startup
if (s_requestEvent != null)
{
s_requestEvent(((HttpApplication)sender).Context);
}
}
/// <summary>
/// Receives the EndSession-event and dispatches it to all handlers
/// registered with this module.
/// </summary>
public static void RaiseEndSession(HttpSessionState sessionState, CacheItemRemovedReason reason)
{
// NOTE: don't sync here for performance reasons.
// It is assumed, that all handlers are registered during application startup
if (s_sessionEvent != null)
{
s_sessionEvent(sessionState, reason);
}
}
/// <summary>
/// Register with this event to receive any EndRequest event occuring in the current AppDomain.
/// </summary>
public static event RequestEventHandler EndRequest
{
add
{
AssertInitialized();
lock (syncEndRequestEvent)
{
s_requestEvent += value;
}
}
remove
{
AssertInitialized();
lock (syncEndRequestEvent)
{
s_requestEvent -= value;
}
}
}
/// <summary>
/// Register with this event to receive any EndSession event occuring in the current AppDomain
/// </summary>
/// <remarks>
/// This event may be raised asynchronously on it's own thread.
/// Don't rely on e.g. <see cref="HttpContext.Current"/> being available.
/// </remarks>
public static event SessionEventHandler EndSession
{
add
{
AssertInitialized();
lock (syncEndSessionEvent)
{
s_sessionEvent += value;
}
}
remove
{
AssertInitialized();
lock (syncEndSessionEvent)
{
s_sessionEvent -= value;
}
}
}
/// <summary>
/// Signals, that VirtualEnvironment is ready to accept
/// handler registrations for EndRequest and EndSession events
/// </summary>
public static void SetInitialized()
{
s_isInitialized = true;
}
/// <summary>
/// Is this VirtualEnviroment ready to accept handler registrations
/// for EndRequest and EndSession events ?
/// </summary>
public static bool IsInitialized
{
get { return s_isInitialized; }
}
/// <summary>
/// Ensures, that WebSupportModule has been initialized. Otherwise an exception is thrown.
/// </summary>
private static void AssertInitialized()
{
if (!s_isInitialized)
{
string msg =
@"WebSupportModule not initialized. Did you forget to add " +
@"<add name=""Spring"" type=""Spring.Context.Support.WebSupportModule, Spring.Web""/> " +
@"to your web.config's <httpModules>-section?";
throw ConfigurationUtils.CreateConfigurationException(msg);
}
}
}
}
|