#region License
/*
* Copyright 2002-2006 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.Globalization;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Web;
using System.Web.Caching;
using System.Web.SessionState;
using System.Web.UI;
using Common.Logging;
using Spring.Core.IO;
using Spring.Core.TypeConversion;
using Spring.Core.TypeResolution;
using Spring.Expressions;
using Spring.Objects.Factory.Config;
using Spring.Objects.Factory.Support;
using Spring.Reflection.Dynamic;
using Spring.Threading;
using Spring.Util;
using Spring.Web.Support;
#endregion
namespace Spring.Context.Support{
/// <summary>
/// Provides various support for proper handling requests.
/// </summary>
/// <author>Erich Eichinger</author>
public class WebSupportModule : IHttpModule
{
/// <summary>
/// Identifies the Objectdefinition used for the current IHttpHandler instance in TLS
/// </summary>
private static readonly string CURRENTHANDLER_OBJECTDEFINITION = "__spring.web" + new Guid().ToString();
/// <summary>
/// Holds the handler configuration information.
/// </summary>
private class HandlerConfigurationMetaData
{
public readonly IConfigurableApplicationContext ApplicationContext;
public readonly string ObjectDefinitionName;
public readonly bool IsContainerManaged;
public HandlerConfigurationMetaData(IConfigurableApplicationContext applicationContext, string objectDefinitionName, bool isContainerManaged)
{
ApplicationContext = applicationContext;
ObjectDefinitionName = objectDefinitionName;
IsContainerManaged = isContainerManaged;
}
}
private static readonly ILog s_log;
private static bool s_isInitialized = false;
// Required for Session End event handling
private static int CACHEKEYPREFIXLENGTH = 0;
private static CacheItemRemovedCallback s_originalCallback;
#if NET_2_0
// required to enable accessing HttpContext.Request during IHttpModule.Init() in integrated mode
private static readonly FieldInfo fiHideRequestResponse;
private static readonly SafeField ContextHideRequestResponse;
#endif
/// <summary>
/// For webapplications always
/// <ul>
/// <li>convert IResources using the current context.</li>
/// <li>use "web" as default resource protocol</li>
/// <li>use <see cref="HybridContextStorage"/> as default threading storage</li>
/// </ul>
/// </summary>
static WebSupportModule()
{
s_log = LogManager.GetLogger(typeof(WebSupportModule));
#if NET_2_0
// required to enable accessing HttpContext.Request during IHttpModule.Init() in integrated mode
ContextHideRequestResponse = null;
try
{
fiHideRequestResponse = typeof(HttpContext).GetField("HideRequestResponse", BindingFlags.Instance|BindingFlags.NonPublic);
// fiHideRequestResponse.SetValue(HttpContext.Current, false);
ContextHideRequestResponse = (fiHideRequestResponse!=null)?new SafeField(fiHideRequestResponse):null;
}
catch(SecurityException sec)
{
s_log.Warn(string.Format("failed reflecting field HttpContext.HideRequestResponse due to security restrictions {0}", sec));
}
#endif
// register additional resource handler
ResourceHandlerRegistry.RegisterResourceHandler(WebUtils.DEFAULT_RESOURCE_PROTOCOL, typeof(WebResource));
// replace default IResource converter
TypeConverterRegistry.RegisterConverter(typeof(IResource),
new ResourceConverter(
new ConfigurableResourceLoader(WebUtils.DEFAULT_RESOURCE_PROTOCOL)));
// default to hybrid thread storage implementation
LogicalThreadContext.SetStorage(new HybridContextStorage());
s_log.Debug("Set default resource protocol to 'web' and installed HttpContext-aware HybridContextStorage");
}
/// <summary>
/// Registers this module for all events required by the Spring.Web framework
/// </summary>
public virtual void Init(HttpApplication app)
{
lock (typeof(WebSupportModule))
{
s_log.Debug("Initializing Application instance");
if (!s_isInitialized)
{
HttpModuleCollection modules = app.Modules;
foreach (string moduleKey in modules.AllKeys)
{
if (modules[moduleKey] is SessionStateModule)
{
#if !NET_1_1
HookSessionEvent((SessionStateModule)modules[moduleKey]);
#else
HookSessionEvent11();
#endif
}
}
}
s_isInitialized = true;
// signal, that VirtualEnvironment is ready to accept
// handler registrations for EndRequest and EndSession events
VirtualEnvironment.SetInitialized();
}
app.PreRequestHandlerExecute += new EventHandler(OnConfigureHandler);
app.EndRequest += new EventHandler(VirtualEnvironment.RaiseEndRequest);
// ensure context is instantiated
IConfigurableApplicationContext appContext = WebApplicationContext.GetRootContext() as IConfigurableApplicationContext;
// configure this app + it's module instances
if (appContext == null)
{
throw new InvalidOperationException("Implementations of IApplicationContext must also implement IConfigurableApplicationContext");
}
#if NET_2_0
// TODO: this is only a workaround to get us up & running in IIS7/integrated mode
// We must review all code for relative virtual paths - they must be resolved to application-relative paths
// during parsing of the object definitions
bool hideRequestResponse = (ContextHideRequestResponse!=null)? (bool) ContextHideRequestResponse.GetValue(app.Context) : false;
if (ContextHideRequestResponse!=null) ContextHideRequestResponse.SetValue(app.Context, false);
try
{
HttpApplicationConfigurer.Configure(appContext, app);
}
finally
{
if (ContextHideRequestResponse!=null) ContextHideRequestResponse.SetValue(app.Context, hideRequestResponse);
}
#else
HttpApplicationConfigurer.Configure(appContext, app);
#endif
}
#region IHttpHandler configuration
///<summary>
/// Configures the current IHttpHandler as specified by <see cref="Spring.Web.Support.PageHandlerFactory"/>.
///</summary>
private void OnConfigureHandler(object sender, EventArgs e)
{
HandlerConfigurationMetaData hCfg = (HandlerConfigurationMetaData)LogicalThreadContext.GetData(CURRENTHANDLER_OBJECTDEFINITION);
if (hCfg != null)
{
HttpApplication app = (HttpApplication)sender;
// app.Context.Handler = // TODO: check, if this makes sense (EE)
ConfigureHandlerNow(app.Context.Handler, hCfg.ApplicationContext, hCfg.ObjectDefinitionName, hCfg.IsContainerManaged);
}
}
/// <summary>
/// Configures the specified handler instance using the object definition <paramref name="name"/>.
/// </summary>
/// <remarks>
/// TODO
/// </remarks>
/// <param name="context"></param>
/// <param name="handler"></param>
/// <param name="applicationContext"></param>
/// <param name="name"></param>
/// <param name="isContainerManaged"></param>
/// <returns></returns>
public static IHttpHandler ConfigureHandler(HttpContext context, IHttpHandler handler, IConfigurableApplicationContext applicationContext, string name, bool isContainerManaged)
{
if (context.Handler != null)
{
s_log.Debug(string.Format("previous handler is present - configuring handler now using application context '{0}' and name '{1}'", applicationContext, name));
// this is a Server.Execute() or Server.Transfer() request -> configure immediately
return ConfigureHandlerNow(handler, applicationContext, name, isContainerManaged);
}
else
{
// remember the resolved object definition name for applying it during PreRequestHandlerExecute
s_log.Debug(string.Format("no previous handler is present - defer handler configuration using application context '{0}' and name '{1}'", applicationContext, name));
SetCurrentHandlerConfiguration(applicationContext, name, isContainerManaged);
return handler;
}
}
///<summary>
/// TODO
///</summary>
///<param name="applicationContext"></param>
///<param name="name"></param>
///<param name="isContainerManaged"></param>
private static void SetCurrentHandlerConfiguration(IConfigurableApplicationContext applicationContext, string name, bool isContainerManaged)
{
LogicalThreadContext.SetData(CURRENTHANDLER_OBJECTDEFINITION, new HandlerConfigurationMetaData(applicationContext, name, isContainerManaged));
}
///<summary>
/// TODO
///</summary>
///<param name="handler"></param>
///<param name="applicationContext"></param>
///<param name="name"></param>
///<param name="isContainerManaged"></param>
private static IHttpHandler ConfigureHandlerNow(IHttpHandler handler, IConfigurableApplicationContext applicationContext, string name, bool isContainerManaged)
{
if (isContainerManaged)
{
s_log.Debug(string.Format("configuring managed handler using application context '{0}' and name '{1}'", applicationContext, name));
handler = (IHttpHandler)applicationContext.ObjectFactory.ConfigureObject(handler, name);
}
else
{
s_log.Debug(string.Format("configuring unmanaged handler using application context '{0}' and name '{1}'", applicationContext, name));
// at a minimum we'll apply ObjectPostProcessors
handler = (IHttpHandler)applicationContext.ObjectFactory.ApplyObjectPostProcessorsBeforeInitialization(handler, name);
handler = (IHttpHandler)applicationContext.ObjectFactory.ApplyObjectPostProcessorsAfterInitialization(handler, name);
}
return handler;
}
#endregion
/// <summary>
/// Disposes this instance
/// </summary>
public virtual void Dispose()
{
// noop
}
#region Session Handling Stuff
private static void OnCacheItemRemoved(string key, object value, CacheItemRemovedReason reason)
{
s_log.Debug("end session " + key + " because of " + reason);
try
{
HttpSessionState ss = CreateSessionState(key, value);
VirtualEnvironment.RaiseEndSession(ss, reason);
}
catch (Exception ex)
{
string msg = "Failure during EndSession event handling";
// are we on a current request?
if (HttpContext.Current != null)
{
s_log.Error(msg, ex);
}
else
{
// this is an async session timout - log as fatal since this is the thread's exit point!
s_log.Fatal(msg, ex);
}
}
finally
{
if (s_originalCallback != null)
{
s_originalCallback(key, value, reason);
}
}
}
#if !NET_1_1
private static void HookSessionEvent(SessionStateModule sessionStateModule)
{
// Hook only into InProcState - all others ignore SessionEnd anyway
object store = ExpressionEvaluator.GetValue(sessionStateModule, "_store");
if ((store != null) && store.GetType().Name == "InProcSessionStateStore")
{
s_log.Debug("attaching to InProcSessionStateStore");
s_originalCallback = (CacheItemRemovedCallback)ExpressionEvaluator.GetValue(store, "_callback");
ExpressionEvaluator.SetValue(store, "_callback", new CacheItemRemovedCallback(OnCacheItemRemoved));
CACHEKEYPREFIXLENGTH = (int)ExpressionEvaluator.GetValue(store, "CACHEKEYPREFIXLENGTH");
}
}
private static HttpSessionState CreateSessionState(string key, object state)
{
string id = key.Substring(CACHEKEYPREFIXLENGTH);
ISessionStateItemCollection sessionItems =
(ISessionStateItemCollection)ExpressionEvaluator.GetValue(state, "_sessionItems");
HttpStaticObjectsCollection staticObjects =
(HttpStaticObjectsCollection)ExpressionEvaluator.GetValue(state, "_staticObjects");
int timeout = (int)ExpressionEvaluator.GetValue(state, "_timeout");
TypeRegistry.RegisterType("SessionStateModule", typeof(SessionStateModule));
HttpCookieMode cookieMode =
(HttpCookieMode)ExpressionEvaluator.GetValue(null, "SessionStateModule.s_configCookieless");
SessionStateMode stateMode =
(SessionStateMode)ExpressionEvaluator.GetValue(null, "SessionStateModule.s_configMode");
HttpSessionStateContainer container = new HttpSessionStateContainer(
id
, sessionItems
, staticObjects
, timeout
, false
, cookieMode
, stateMode
, true
);
return (HttpSessionState)Activator.CreateInstance(
typeof(HttpSessionState)
, BindingFlags.Instance | BindingFlags.NonPublic
, null
, new object[] { container }
, CultureInfo.InvariantCulture
);
}
#else
private static Type t_SessionDictionary;
private static void HookSessionEvent11()
{
// Hook only into InProcState - all others ignore SessionEnd anyway
t_SessionDictionary = typeof(HttpSessionState).Assembly.GetType("System.Web.SessionState.SessionDictionary");
Type t_InProcStateClientManager =
typeof(HttpSessionState).Assembly.GetType("System.Web.SessionState.InProcStateClientManager");
TypeRegistry.RegisterType( "InProcStateClientManager", t_InProcStateClientManager );
CacheItemRemovedCallback circ = new CacheItemRemovedCallback( OnCacheItemRemoved );
CACHEKEYPREFIXLENGTH = (int)ExpressionEvaluator.GetValue(null, "InProcStateClientManager.CACHEKEYPREFIXLENGTH");
s_originalCallback = (CacheItemRemovedCallback)ExpressionEvaluator.GetValue(null, "InProcStateClientManager.s_callback");
ExpressionEvaluator.SetValue(null, "InProcStateClientManager.s_callback", circ);
}
private static HttpSessionState CreateSessionState(string key, object state1)
{
string id = key.Substring(CACHEKEYPREFIXLENGTH);
object dict = ExpressionEvaluator.GetValue(state1, "dict");
if (dict == null)
{
dict = Activator.CreateInstance(t_SessionDictionary, true);
}
object staticObjects = ExpressionEvaluator.GetValue(state1, "staticObjects");
int timeout = (int)ExpressionEvaluator.GetValue(state1, "timeout");
bool isCookieless = (bool)ExpressionEvaluator.GetValue(state1, "isCookieless");
HttpSessionState state2 = (HttpSessionState)Activator.CreateInstance(
typeof(HttpSessionState)
, BindingFlags.Instance|BindingFlags.NonPublic
, null
, new object[]
{
id,
dict,
staticObjects,
timeout,
false,
isCookieless,
SessionStateMode.InProc,
true
}
, CultureInfo.InvariantCulture
);
return state2;
}
#endif
#endregion Session Handling Stuff
}
}
|