From b6b4b370f97430266ec301fd5a33012d3cb11ad4 Mon Sep 17 00:00:00 2001 From: eeichinger Date: Mon, 13 Oct 2008 23:18:44 +0000 Subject: [PATCH] added SPRNET-711, SPRNET-713 - MappingHandlerFactory for configuring custom IHttpHandler and IHttpHandlerFactory instances --- .../App_Code/MyCustomHttpHandler.cs | 20 ++ .../DI/CustomHandlers/Default.aspx | 49 ++++ .../DI/CustomHandlers/Default.aspx.bak | 17 ++ .../DI/CustomHandlers/DemoHandler.ashx | 30 +++ .../DI/CustomHandlers/web.config | 54 ++++ .../Spring.WebQuickStart.2005/DI/Default.aspx | 2 + .../Script/Services/ScriptHandlerFactory.cs | 5 +- .../Context/Support/WebSupportModule.cs | 125 +++++---- src/Spring/Spring.Web/Spring.Web.2005.csproj | 6 + src/Spring/Spring.Web/Spring.Web.2008.csproj | 5 + .../Web/Support/AbstractHandlerFactory.cs | 59 ++++- .../Web/Support/DefaultHandlerFactory.cs | 104 ++++++++ .../Spring.Web/Web/Support/HandlerMap.cs | 238 ++++++++++++++++++ .../Spring.Web/Web/Support/HandlerMapEntry.cs | 92 +++++++ .../Web/Support/MappingHandlerFactory.cs | 175 +++++++++++++ .../MappingHandlerFactoryConfigurer.cs | 67 +++++ .../Web/Support/PageHandlerFactory.cs | 54 +--- .../Support/AbstractHandlerFactoryTests.cs | 14 +- 18 files changed, 1014 insertions(+), 102 deletions(-) create mode 100644 examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/App_Code/MyCustomHttpHandler.cs create mode 100644 examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/CustomHandlers/Default.aspx create mode 100644 examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/CustomHandlers/Default.aspx.bak create mode 100644 examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/CustomHandlers/DemoHandler.ashx create mode 100644 examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/CustomHandlers/web.config create mode 100644 src/Spring/Spring.Web/Web/Support/DefaultHandlerFactory.cs create mode 100644 src/Spring/Spring.Web/Web/Support/HandlerMap.cs create mode 100644 src/Spring/Spring.Web/Web/Support/HandlerMapEntry.cs create mode 100644 src/Spring/Spring.Web/Web/Support/MappingHandlerFactory.cs create mode 100644 src/Spring/Spring.Web/Web/Support/MappingHandlerFactoryConfigurer.cs diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/App_Code/MyCustomHttpHandler.cs b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/App_Code/MyCustomHttpHandler.cs new file mode 100644 index 00000000..93ef4dae --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/App_Code/MyCustomHttpHandler.cs @@ -0,0 +1,20 @@ +using System.Web; + +/// +/// Summary description for NoOpHandler +/// +public class MyCustomHttpHandler : IHttpHandler +{ + public string MessageText = ""; + + public void ProcessRequest(HttpContext context) + { + context.Response.ContentType = "text/plain"; + context.Response.Write(@"Response from MyCustomHttpHandler:" + MessageText); + } + + public bool IsReusable + { + get { return true; } + } +} diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/CustomHandlers/Default.aspx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/CustomHandlers/Default.aspx new file mode 100644 index 00000000..eaceb280 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/CustomHandlers/Default.aspx @@ -0,0 +1,49 @@ +<%@ Page Language="C#" %> + + + + + <%=Title%> + + +

Welcome to Spring.NET Web Framework Quick Start Guide

+

Dependency Injection

+
+
+

+ The links below demonstrate dependency injection on custom handlers using MappingHandlerFactory. + The example code shows, how to map all url requests to spring and let MappingHandlerFactory resolve urls to + container-managed IHttpHandlerFactory or IHttpHandler objects. +

+
+        // web.config
+
+        <httpHandlers>
+          <!-- map all requests to spring (just for demo - don't do this at home!) -->
+          <add verb="*" path="*.*" type="Spring.Web.Support.MappingHandlerFactory, Spring.Web" />
+        </httpHandlers>
+
+        // spring-objects.config
+
+        <object type="Spring.Web.Support.MappingHandlerFactoryConfigurer, Spring.Web">
+          <property name="HandlerMap">
+	          <dictionary>
+	              <entry key="\.ashx$" value="standardHandlerFactory" />
+	              <!-- map any request ending with *.whatever to standardHandlerFactory -->
+	              <entry key="\.whatever$" value="specialHandlerFactory" />
+	          </dictionary>
+          </property>
+        </object>
+
+        <object name="standardHandlerFactory" type="Spring.Web.Support.DefaultHandlerFactory, Spring.Web" />
+
+        <object name="specialHandlerFactory" type="MySpecialHandlerFactoryImpl" />
+    
+ +
+
+ + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/CustomHandlers/Default.aspx.bak b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/CustomHandlers/Default.aspx.bak new file mode 100644 index 00000000..891c9843 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/CustomHandlers/Default.aspx.bak @@ -0,0 +1,17 @@ +<%@ Page Language="C#" %> + + + + + + Untitled Page + + +
+
+ DemoHandler.ashx
+ AnotherCustomHandler.whatever
+
+
+ + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/CustomHandlers/DemoHandler.ashx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/CustomHandlers/DemoHandler.ashx new file mode 100644 index 00000000..f18cf579 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/CustomHandlers/DemoHandler.ashx @@ -0,0 +1,30 @@ +<%@ WebHandler Language="C#" Class="DemoHandler" %> + +using System; +using System.Web; + +public class DemoHandler : IHttpHandler +{ + private string _outputText; + + public string OutputText + { + get { return _outputText; } + set { _outputText = value; } + } + + public void ProcessRequest(HttpContext context) + { + context.Response.ContentType = "text/plain"; + context.Response.Write("injected text:" + _outputText); + } + + public bool IsReusable + { + get + { + return false; + } + } + +} \ No newline at end of file diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/CustomHandlers/web.config b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/CustomHandlers/web.config new file mode 100644 index 00000000..77791d90 --- /dev/null +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/CustomHandlers/web.config @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This text is injected via Spring + + + + + diff --git a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/Default.aspx b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/Default.aspx index 16361140..e152b358 100644 --- a/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/Default.aspx +++ b/examples/Spring/Spring.WebQuickStart/src/Spring.WebQuickStart.2005/DI/Default.aspx @@ -17,6 +17,8 @@

The inevitable "Hello World!" example demonstrates DI for pages, usercontrols and webcontrols

Nested Contexts

This sample shows, how contexts are nested in webapplications

+

Custom HTTP Handlers

+

This sample shows, how to configure your custom IHttpHandler and IHttpHandlerFactory implementations

diff --git a/src/Spring/Spring.Web.Extensions/Web/Script/Services/ScriptHandlerFactory.cs b/src/Spring/Spring.Web.Extensions/Web/Script/Services/ScriptHandlerFactory.cs index 4173b511..4a74fb85 100644 --- a/src/Spring/Spring.Web.Extensions/Web/Script/Services/ScriptHandlerFactory.cs +++ b/src/Spring/Spring.Web.Extensions/Web/Script/Services/ScriptHandlerFactory.cs @@ -120,12 +120,13 @@ namespace Spring.Web.Script.Services /// /// Create a handler instance for the given URL. /// + /// the application context corresponding to the current request /// The instance for this request. /// The HTTP data transfer method (GET, POST, ...) - /// The requested . + /// The requested . /// The physical path of the requested resource. /// A handler instance for the current request. - protected override IHttpHandler CreateHandlerInstance( HttpContext context, string requestType, string url, string physicalPath ) + protected override IHttpHandler CreateHandlerInstance( IConfigurableApplicationContext appContext, HttpContext context, string requestType, string rawUrl, string physicalPath ) { throw new NotSupportedException(); } diff --git a/src/Spring/Spring.Web/Context/Support/WebSupportModule.cs b/src/Spring/Spring.Web/Context/Support/WebSupportModule.cs index f6fac471..ff1defc0 100644 --- a/src/Spring/Spring.Web/Context/Support/WebSupportModule.cs +++ b/src/Spring/Spring.Web/Context/Support/WebSupportModule.cs @@ -88,28 +88,28 @@ namespace Spring.Context.Support /// static WebSupportModule() { - s_log = LogManager.GetLogger( typeof( WebSupportModule ) ); + s_log = LogManager.GetLogger(typeof(WebSupportModule)); // register additional resource handler - ResourceHandlerRegistry.RegisterResourceHandler( WebUtils.DEFAULT_RESOURCE_PROTOCOL, typeof( WebResource ) ); + ResourceHandlerRegistry.RegisterResourceHandler(WebUtils.DEFAULT_RESOURCE_PROTOCOL, typeof(WebResource)); // replace default IResource converter - TypeConverterRegistry.RegisterConverter( typeof( IResource ), + TypeConverterRegistry.RegisterConverter(typeof(IResource), new ResourceConverter( - new ConfigurableResourceLoader( WebUtils.DEFAULT_RESOURCE_PROTOCOL ) ) ); + new ConfigurableResourceLoader(WebUtils.DEFAULT_RESOURCE_PROTOCOL))); // default to hybrid thread storage implementation - LogicalThreadContext.SetStorage( new HybridContextStorage() ); + LogicalThreadContext.SetStorage(new HybridContextStorage()); - s_log.Debug( "Set default resource protocol to 'web' and installed HttpContext-aware HybridContextStorage" ); + s_log.Debug("Set default resource protocol to 'web' and installed HttpContext-aware HybridContextStorage"); } /// /// Registers this module for all events required by the Spring.Web framework /// - public virtual void Init( HttpApplication app ) + public virtual void Init(HttpApplication app) { - lock (typeof( WebSupportModule )) + lock (typeof(WebSupportModule)) { - s_log.Debug( "Initializing Application instance" ); + s_log.Debug("Initializing Application instance"); if (!s_isInitialized) { HttpModuleCollection modules = app.Modules; @@ -118,7 +118,7 @@ namespace Spring.Context.Support if (modules[moduleKey] is SessionStateModule) { #if !NET_1_1 - HookSessionEvent( (SessionStateModule)modules[moduleKey] ); + HookSessionEvent((SessionStateModule)modules[moduleKey]); #else HookSessionEvent11(); #endif @@ -132,57 +132,92 @@ namespace Spring.Context.Support VirtualEnvironment.SetInitialized(); } - app.PreRequestHandlerExecute += new EventHandler( OnPreRequestHandlerExecute ); - app.EndRequest += new EventHandler( VirtualEnvironment.RaiseEndRequest ); + 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" ); + throw new InvalidOperationException("Implementations of IApplicationContext must also implement IConfigurableApplicationContext"); } - HttpApplicationConfigurer.Configure( appContext, app ); + HttpApplicationConfigurer.Configure(appContext, app); } + #region IHttpHandler configuration + /// /// Configures the current IHttpHandler as specified by . /// - private void OnPreRequestHandlerExecute( object sender, EventArgs e ) + private void OnConfigureHandler(object sender, EventArgs e) { - HandlerConfigurationMetaData hCfg = (HandlerConfigurationMetaData)LogicalThreadContext.GetData( CURRENTHANDLER_OBJECTDEFINITION ); + HandlerConfigurationMetaData hCfg = (HandlerConfigurationMetaData)LogicalThreadContext.GetData(CURRENTHANDLER_OBJECTDEFINITION); if (hCfg != null) { HttpApplication app = (HttpApplication)sender; - //app.Context.Handler = - ConfigureHandler( app.Context.Handler, hCfg.ApplicationContext, hCfg.ObjectDefinitionName, hCfg.IsContainerManaged ); + // app.Context.Handler = // TODO: check, if this makes sense (EE) + ConfigureHandlerNow(app.Context.Handler, hCfg.ApplicationContext, hCfg.ObjectDefinitionName, hCfg.IsContainerManaged); + } + } + + /// + /// Configures the specified handler instance using the object definition . + /// + /// + /// TODO + /// + /// + /// + /// + /// + /// + /// + 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; } } /// + /// TODO /// /// /// /// - public static void SetCurrentHandlerConfiguration( IConfigurableApplicationContext applicationContext, string name, bool isContainerManaged ) + private static void SetCurrentHandlerConfiguration(IConfigurableApplicationContext applicationContext, string name, bool isContainerManaged) { - LogicalThreadContext.SetData( CURRENTHANDLER_OBJECTDEFINITION, new HandlerConfigurationMetaData(applicationContext, name, isContainerManaged) ); + LogicalThreadContext.SetData(CURRENTHANDLER_OBJECTDEFINITION, new HandlerConfigurationMetaData(applicationContext, name, isContainerManaged)); } /// + /// TODO /// /// /// /// /// - public static IHttpHandler ConfigureHandler( IHttpHandler handler, IConfigurableApplicationContext applicationContext, string name, bool isContainerManaged) + private static IHttpHandler ConfigureHandlerNow(IHttpHandler handler, IConfigurableApplicationContext applicationContext, string name, bool isContainerManaged) { if (isContainerManaged) { - handler = (IHttpHandler)applicationContext.ObjectFactory.ConfigureObject( handler, name ); + 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); @@ -191,6 +226,8 @@ namespace Spring.Context.Support return handler; } + #endregion + /// /// Disposes this instance /// @@ -201,15 +238,15 @@ namespace Spring.Context.Support #region Session Handling Stuff - private static void OnCacheItemRemoved( string key, object value, CacheItemRemovedReason reason ) + private static void OnCacheItemRemoved(string key, object value, CacheItemRemovedReason reason) { - s_log.Debug( "end session " + key + " because of " + reason ); + s_log.Debug("end session " + key + " because of " + reason); try { - HttpSessionState ss = CreateSessionState( key, value ); + HttpSessionState ss = CreateSessionState(key, value); - VirtualEnvironment.RaiseEndSession( ss, reason ); + VirtualEnvironment.RaiseEndSession(ss, reason); } catch (Exception ex) { @@ -217,51 +254,51 @@ namespace Spring.Context.Support // are we on a current request? if (HttpContext.Current != null) { - s_log.Error( msg, ex ); + 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 ); + s_log.Fatal(msg, ex); } } finally { if (s_originalCallback != null) { - s_originalCallback( key, value, reason ); + s_originalCallback(key, value, reason); } } } #if !NET_1_1 - private static void HookSessionEvent( SessionStateModule sessionStateModule ) + private static void HookSessionEvent(SessionStateModule sessionStateModule) { // Hook only into InProcState - all others ignore SessionEnd anyway - object store = ExpressionEvaluator.GetValue( sessionStateModule, "_store" ); + 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 ) ); + 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" ); + CACHEKEYPREFIXLENGTH = (int)ExpressionEvaluator.GetValue(store, "CACHEKEYPREFIXLENGTH"); } } - private static HttpSessionState CreateSessionState( string key, object state ) + private static HttpSessionState CreateSessionState(string key, object state) { - string id = key.Substring( CACHEKEYPREFIXLENGTH ); + string id = key.Substring(CACHEKEYPREFIXLENGTH); ISessionStateItemCollection sessionItems = - (ISessionStateItemCollection)ExpressionEvaluator.GetValue( state, "_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 ) ); + (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" ); + (HttpCookieMode)ExpressionEvaluator.GetValue(null, "SessionStateModule.s_configCookieless"); SessionStateMode stateMode = - (SessionStateMode)ExpressionEvaluator.GetValue( null, "SessionStateModule.s_configMode" ); + (SessionStateMode)ExpressionEvaluator.GetValue(null, "SessionStateModule.s_configMode"); HttpSessionStateContainer container = new HttpSessionStateContainer( id , sessionItems @@ -274,7 +311,7 @@ namespace Spring.Context.Support ); return (HttpSessionState)Activator.CreateInstance( - typeof( HttpSessionState ) + typeof(HttpSessionState) , BindingFlags.Instance | BindingFlags.NonPublic , null , new object[] { container } diff --git a/src/Spring/Spring.Web/Spring.Web.2005.csproj b/src/Spring/Spring.Web/Spring.Web.2005.csproj index a5ed06e5..f4994d15 100644 --- a/src/Spring/Spring.Web/Spring.Web.2005.csproj +++ b/src/Spring/Spring.Web/Spring.Web.2005.csproj @@ -125,6 +125,10 @@ + + + + @@ -132,7 +136,9 @@ + + diff --git a/src/Spring/Spring.Web/Spring.Web.2008.csproj b/src/Spring/Spring.Web/Spring.Web.2008.csproj index 7d22f15a..95da6b69 100644 --- a/src/Spring/Spring.Web/Spring.Web.2008.csproj +++ b/src/Spring/Spring.Web/Spring.Web.2008.csproj @@ -124,9 +124,14 @@ Code + + + + + diff --git a/src/Spring/Spring.Web/Web/Support/AbstractHandlerFactory.cs b/src/Spring/Spring.Web/Web/Support/AbstractHandlerFactory.cs index b5c9161b..9457e207 100644 --- a/src/Spring/Spring.Web/Web/Support/AbstractHandlerFactory.cs +++ b/src/Spring/Spring.Web/Web/Support/AbstractHandlerFactory.cs @@ -24,6 +24,7 @@ using System; using System.Collections; using System.IO; using System.Web; +using System.Web.UI; using Common.Logging; using Spring.Collections; using Spring.Context; @@ -90,6 +91,32 @@ namespace Spring.Web.Support /// private readonly IDictionary _reusableHandlerCache = new CaseInsensitiveHashtable(); + /// + /// Holds an instance of the instrinsic System.Web.UI.SimpleHandlerFactory + /// + private static IHttpHandlerFactory s_simpleHandlerFactory; + + /// + /// Get the global instance of System.Web.UI.SimpleHandlerFactory + /// + /// + /// This factory is a plaform version agnostic way to instantiate + /// arbitrary handlers without the need for additional reflection. + /// + public static IHttpHandlerFactory SimpleHandlerFactory + { + get + { + // instantiate lazy to avoid security exceptions in restricted reflection environments + if (s_simpleHandlerFactory == null) + { + Type simpleHandlerFactoryType = typeof(IHttpHandler).Assembly.GetType("System.Web.UI.SimpleHandlerFactory"); + s_simpleHandlerFactory = (IHttpHandlerFactory)Activator.CreateInstance(simpleHandlerFactoryType, true); + } + return s_simpleHandlerFactory; + } + } + /// /// Holds the shared logger for all factories. /// @@ -158,7 +185,11 @@ namespace Spring.Web.Support handler = (IHttpHandler)_reusableHandlerCache[url]; if (handler == null) { - handler = CreateHandlerInstance( context, requestType, url, physicalPath ); + IConfigurableApplicationContext appContext = GetCheckedApplicationContext(url); + + handler = CreateHandlerInstance( appContext, context, requestType, url, physicalPath ); + + ApplyDependencyInjectionInfrastructure(handler, appContext); if (handler.IsReusable) { @@ -182,12 +213,13 @@ namespace Spring.Web.Support /// /// Create a handler instance for the given URL. /// + /// the application context corresponding to the current request /// The instance for this request. /// The HTTP data transfer method (GET, POST, ...) - /// The requested . + /// The requested . /// The physical path of the requested resource. /// A handler instance for processing the current request. - protected abstract IHttpHandler CreateHandlerInstance( HttpContext context, string requestType, string url, string physicalPath ); + protected abstract IHttpHandler CreateHandlerInstance( IConfigurableApplicationContext appContext, HttpContext context, string requestType, string rawUrl, string physicalPath ); /// /// Get the application context instance corresponding to the given absolute url and checks @@ -292,5 +324,26 @@ namespace Spring.Web.Support return (pageDefinition == null) ? (NamedObjectDefinition)null : new NamedObjectDefinition( objectDefinitionName, pageDefinition ); } + + /// + /// Apply dependency injection stuff on the handler. + /// + /// the handler to be intercepted + /// the context responsible for configuring this handler + private static void ApplyDependencyInjectionInfrastructure(IHttpHandler handler, IApplicationContext applicationContext) + { + if (handler is Control) + { + ControlInterceptor.EnsureControlIntercepted(applicationContext, (Control)handler); + } + else + { + if ( (handler is ISupportsWebDependencyInjection) + && (((ISupportsWebDependencyInjection)handler).DefaultApplicationContext == null) ) + { + ((ISupportsWebDependencyInjection)handler).DefaultApplicationContext = applicationContext; + } + } + } } } \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Support/DefaultHandlerFactory.cs b/src/Spring/Spring.Web/Web/Support/DefaultHandlerFactory.cs new file mode 100644 index 00000000..c18e44ad --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/DefaultHandlerFactory.cs @@ -0,0 +1,104 @@ +#region License + +/* + * Copyright © 2002-2008 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.Web; +using Spring.Context; +using Spring.Context.Support; +using Spring.Util; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// SimpleHandlerFactory is used to wrap any arbitrary to make it "Spring-aware". + /// + /// + /// By default, an instance of is used as underlying factory. + /// + /// Erich Eichinger + public class DefaultHandlerFactory : AbstractHandlerFactory + { + private readonly IHttpHandlerFactory _innerFactory; + + /// + /// Creates a new instance, using a as underlying factory. + /// + public DefaultHandlerFactory() + : this(SimpleHandlerFactory) + { } + + /// + /// Create a new instance, using an instance of as underlying factory. + /// + /// a type that implements + public DefaultHandlerFactory(Type innerFactoryType) + : this((IHttpHandlerFactory)Activator.CreateInstance(innerFactoryType, true)) + { + } + + /// + /// Create a new instance, using as underlying factory. + /// + /// the factory to be wrapped. + public DefaultHandlerFactory(IHttpHandlerFactory innerFactory) + { + AssertUtils.ArgumentNotNull(innerFactory, "innerFactory"); + _innerFactory = innerFactory; + } + + /// + /// Create a handler instance for the given URL. + /// + /// the application context corresponding to the current request + /// The instance for this request. + /// The HTTP data transfer method (GET, POST, ...) + /// The requested . + /// The physical path of the requested resource. + /// A handler instance for processing the current request. + protected override IHttpHandler CreateHandlerInstance(IConfigurableApplicationContext appContext, HttpContext context, string requestType, string rawUrl, string physicalPath) + { + IHttpHandler handler = _innerFactory.GetHandler(context, requestType, rawUrl, physicalPath); + + // find a matching object definition + string appRelativeVirtualPath = WebUtils.GetAppRelativePath(rawUrl); + NamedObjectDefinition nod = FindWebObjectDefinition(appRelativeVirtualPath, appContext.ObjectFactory); + string objectDefinitionName = (nod != null) ? nod.Name : rawUrl; + + handler = WebSupportModule.ConfigureHandler(context, handler, appContext, objectDefinitionName, (nod != null)); + return handler; + } + + /// + /// Enables a factory to release an existing + /// instance. + /// + /// + /// The object to release. + /// + public override void ReleaseHandler(IHttpHandler handler) + { + _innerFactory.ReleaseHandler(handler); + } + } +} diff --git a/src/Spring/Spring.Web/Web/Support/HandlerMap.cs b/src/Spring/Spring.Web/Web/Support/HandlerMap.cs new file mode 100644 index 00000000..99ba41b5 --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/HandlerMap.cs @@ -0,0 +1,238 @@ +#region License + +/* + * Copyright © 2002-2008 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.Text.RegularExpressions; +using System.Web; +using Common.Logging; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// Holds a list of url expressions and their corresponding names of responsible + /// or objects managed by the container. + /// + /// Erich Eichinger + public class HandlerMap : IDictionary + { + private readonly ILog Log = LogManager.GetLogger(typeof(HandlerMap)); + + private ArrayList _internalTable = new ArrayList(); + + /// + /// Maps the specified url pattern expression to an object name denoting a or object. + /// + /// the string pattern (see conform to syntax) + /// an object name denoting the spring-managed handler definition + public void Add(string urlPattern, string handlerObjectName) + { + this._internalTable.Add( new HandlerMapEntry(urlPattern, handlerObjectName) ); + } + + /// + /// Maps the specified url pattern expression to an object name denoting a or object. + /// + /// the url pattern + /// an object name denoting the spring-managed handler definition + public void Add(Regex urlPattern, string handlerObjectName) + { + this._internalTable.Add( new HandlerMapEntry(urlPattern, handlerObjectName) ); + } + + /// + /// Maps the to a handler object name by matching against all registered patterns. + /// + /// the virtual path + /// the object name + public HandlerMapEntry MapPath( string virtualPath ) + { + if(Log.IsDebugEnabled) Log.Debug( string.Format( "looking up mapping for url '{0}'", virtualPath ) ); + for(int i=0;i + /// Add a new mapping + /// + /// an url pattern string + /// a handler object name string + void IDictionary.Add(object key, object value) + { + this.Add((string) key, (string) value); + } + + /// + /// Add or replace a mapping + /// + /// + /// Getter will throw a ! + /// + /// an url pattern string + object IDictionary.this[object key] + { + get + { + throw new NotSupportedException(); + } + set + { + this.Add( (string)key,(string)value ); + } + } + + /// + /// Clear the mapping table. + /// + public void Clear() + { + this._internalTable.Clear(); + } + + /// + /// Always returns false. + /// + public bool IsReadOnly + { + get + { + return false; + // return this._internalTable.IsReadOnly; + } + } + + /// + ///Gets a value indicating whether the object has a fixed size. + /// + /// + /// + ///true if the object has a fixed size; otherwise, false. + /// + ///2 + public bool IsFixedSize + { + get { return false; /*return this._internalTable.IsFixedSize;*/ } + } + + /// + ///Copies all entries to the specified array. + /// + public void CopyTo(Array array, int index) + { + this._internalTable.CopyTo(array, index); + } + + /// + /// Get the number of registered mappings. + /// + public int Count + { + get { return this._internalTable.Count; } + } + + /// + ///Gets an object that can be used to synchronize access. + /// + public object SyncRoot + { + get { return this._internalTable.SyncRoot; } + } + + /// + /// Always returns false. + /// + public bool IsSynchronized + { + get { return false; /*return this._internalTable.IsSynchronized;*/ } + } + + /// + /// Get an enumerator for iterating over the list of registered mappings. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable) this._internalTable).GetEnumerator(); + } + + #region Unsupported methods + + /// + /// Not supported by this implementation. + /// + bool IDictionary.Contains(object key) + { + throw new NotSupportedException(); + } + + /// + /// Not supported by this implementation. + /// + IDictionaryEnumerator IDictionary.GetEnumerator() + { + throw new NotSupportedException(); + } + + /// + /// Not supported by this implementation. + /// + void IDictionary.Remove(object key) + { + throw new NotSupportedException(); + } + + /// + /// Not supported by this implementation. + /// + ICollection IDictionary.Keys + { + get + { + throw new NotSupportedException(); + } + } + + /// + /// Not supported by this implementation. + /// + ICollection IDictionary.Values + { + get + { + throw new NotSupportedException(); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Support/HandlerMapEntry.cs b/src/Spring/Spring.Web/Web/Support/HandlerMapEntry.cs new file mode 100644 index 00000000..285ebcca --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/HandlerMapEntry.cs @@ -0,0 +1,92 @@ +#region License + +/* + * Copyright © 2002-2008 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.Text.RegularExpressions; +using Spring.Util; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// Holds pairs of (url pattern, handler object name). + /// + /// + /// + /// + /// Erich Eichinger + public class HandlerMapEntry + { + private Regex _urlPattern; + private string _handlerObjectName; + + /// + /// Create a new instance. + /// + /// + /// + public HandlerMapEntry(string urlPattern, string handlerObjectName) + { + AssertUtils.ArgumentNotNull(urlPattern, "urlPattern"); + AssertUtils.ArgumentNotNull(handlerObjectName, "handlerObjectName"); + this._urlPattern = new Regex(urlPattern, RegexOptions.Compiled|RegexOptions.ECMAScript|RegexOptions.CultureInvariant|RegexOptions.IgnoreCase); + this._handlerObjectName = handlerObjectName; + } + + /// + /// Create a new instance + /// + /// + /// + public HandlerMapEntry(Regex urlPattern, string handlerObjectName) + { + AssertUtils.ArgumentNotNull(urlPattern, "urlPattern"); + AssertUtils.ArgumentNotNull(handlerObjectName, "handlerObjectName"); + this._urlPattern = urlPattern; + this._handlerObjectName = handlerObjectName; + } + + /// + /// Get the url pattern + /// + public Regex UrlPattern + { + get { return this._urlPattern; } + } + + /// + /// Get the handler object name + /// + public string HandlerObjectName + { + get { return this._handlerObjectName; } + } + + /// + /// Return a string representation of this entry. + /// + public override string ToString() + { + return string.Format("HandlerMapEntry['{0}','{1}']", _urlPattern, _handlerObjectName); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Support/MappingHandlerFactory.cs b/src/Spring/Spring.Web/Web/Support/MappingHandlerFactory.cs new file mode 100644 index 00000000..b1bb54f2 --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/MappingHandlerFactory.cs @@ -0,0 +1,175 @@ +#region License + +/* + * Copyright © 2002-2008 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.Collections; +using System.Net; +using System.Web; +using Spring.Context; +using Spring.Context.Support; + +#endregion + +namespace Spring.Web.Support +{ + /// + /// MappingHandleryFactory allows for full Spring-managed <httpHandlers> configuration. + /// It uses regular expressions for url matching. + /// + /// + /// + /// The example below shows, how to map all url requests to spring and let + /// resolve urls to container-managed or objects. + /// + /// // web.config + /// + /// <httpHandlers> + /// <!-- map all requests to spring (just for demo - don't do this at home!) --> + /// <add verb="*" path="*.*" type="Spring.Web.Support.MappingHandlerFactory, Spring.Web" /> + /// </httpHandlers> + /// + /// // spring-objects.config + /// + /// <object type="Spring.Web.Support.MappingHandlerFactoryConfigurer, Spring.Web"> + /// <property name="HandlerMap"> + /// <dictionary> + /// <entry key="\.ashx$" value="standardHandlerFactory" /> + /// <!-- map any request ending with *.whatever to standardHandlerFactory --> + /// <entry key="\.whatever$" value="specialHandlerFactory" /> + /// </dictionary> + /// </property> + /// </object> + /// + /// <object name="standardHandlerFactory" type="Spring.Web.Support.DefaultHandlerFactory, Spring.Web" /> + /// + /// <object name="specialHandlerFactory" type="MySpecialHandlerFactoryImpl" /> + /// + /// + /// + /// + /// + /// + /// + /// Erich Eichinger + public class MappingHandlerFactory : AbstractHandlerFactory + { + private static readonly HandlerMap s_handlerMap = new HandlerMap(); + + /// + /// Holds the global list of mappings from url patterns to handler names. + /// + public static HandlerMap HandlerMap + { + get { return s_handlerMap; } + } + + /// + /// Holds the cache of handler/factory pairs handed out by this factory. This is required + /// for proper handling of . + /// + private readonly Hashtable _handlerWithFactoryTable = new Hashtable(); + + /// + /// Create a handler instance for the given URL. Will try to find a match of onto patterns in . + /// If a match is found, delegates the call to the matching method. + /// + /// the application context corresponding to the current request + /// The instance for this request. + /// The HTTP data transfer method (GET, POST, ...) + /// The requested . + /// The physical path of the requested resource. + /// A handler instance for processing the current request. + protected override IHttpHandler CreateHandlerInstance(IConfigurableApplicationContext appContext, HttpContext context, string requestType, string rawUrl, string physicalPath) + { + return MapHandlerInstance(appContext, context, requestType, rawUrl, physicalPath, s_handlerMap, _handlerWithFactoryTable); + } + + /// + /// Obtains a handler by mapping to the list of patterns in . + /// + /// the application context corresponding to the current request + /// The instance for this request. + /// The HTTP data transfer method (GET, POST, ...) + /// The requested . + /// The physical path of the requested resource. + /// + /// + /// A handler instance for processing the current request. + protected IHttpHandler MapHandlerInstance(IConfigurableApplicationContext appContext, HttpContext context, string requestType, string rawUrl, string physicalPath, HandlerMap handlerMappings, IDictionary handlerWithFactoryTable) + { + // resolve handler instance by mapping the url to the list of patterns + HandlerMapEntry handlerMapEntry = handlerMappings.MapPath(rawUrl); + if (handlerMapEntry == null) + { + throw new HttpException(404, HttpStatusCode.NotFound.ToString()); + } + object handlerObject = appContext.GetObject(handlerMapEntry.HandlerObjectName); + + if (handlerObject is IHttpHandler) + { + return (IHttpHandler) handlerObject; + } + else if (handlerObject is IHttpHandlerFactory) + { + // keep a reference to the issuing factory for later ReleaseHandler call + IHttpHandlerFactory factory = (IHttpHandlerFactory) handlerObject; + IHttpHandler handler = factory.GetHandler(context, requestType, rawUrl, physicalPath); + lock(handlerWithFactoryTable.SyncRoot) + { + handlerWithFactoryTable.Add(handler, factory); + } + return handler; + } + + throw new HttpException((int)HttpStatusCode.NotFound, HttpStatusCode.NotFound.ToString()); + } + + /// + /// Enables a factory to release an existing + /// instance. + /// + /// + /// The object to release. + /// + public override void ReleaseHandler(IHttpHandler handler) + { + ReleaseHandler(handler, this._handlerWithFactoryTable); + } + + /// + /// Removes the handler from the handler/factory dictionary and releases the handler. + /// + /// the handler to be released + /// a dictionary containing (, ) entries. + protected void ReleaseHandler( IHttpHandler handler, IDictionary _handlerWithFactoryTable ) + { + lock (_handlerWithFactoryTable.SyncRoot) + { + IHttpHandlerFactory factory = _handlerWithFactoryTable[handler] as IHttpHandlerFactory; + if (factory != null) + { + _handlerWithFactoryTable.Remove(handler); + factory.ReleaseHandler(handler); + } + } + } + } +} diff --git a/src/Spring/Spring.Web/Web/Support/MappingHandlerFactoryConfigurer.cs b/src/Spring/Spring.Web/Web/Support/MappingHandlerFactoryConfigurer.cs new file mode 100644 index 00000000..5ba4a85b --- /dev/null +++ b/src/Spring/Spring.Web/Web/Support/MappingHandlerFactoryConfigurer.cs @@ -0,0 +1,67 @@ +#region License + +/* + * Copyright © 2002-2008 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 + +#endregion + +namespace Spring.Web.Support +{ + /// + /// Configures . + /// + /// Erich Eichinger + public class MappingHandlerFactoryConfigurer +// : Spring.Objects.Factory.Config.IObjectPostProcessor // just a trick to have + { + /// + /// Contains mappings of url patterns to handler objects. + /// + public HandlerMap HandlerMap + { + get + { + return MappingHandlerFactory.HandlerMap; + } +// set +// { +// AssertUtils.ArgumentNotNull(value, "HandlerMap"); +// foreach(HandlerMapEntry mapEntry in value) +// { +// GenericHandlerFactory.HandlerMap[mapEntry.UrlPattern] = mapEntry; +// } +// } + } + + #region IObjectPostProcessor implementation + +// object IObjectPostProcessor.PostProcessBeforeInitialization(object instance, string name) +// { +// return instance; +// } +// +// object IObjectPostProcessor.PostProcessAfterInitialization(object instance, string objectName) +// { +// return instance; +// } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Web/Web/Support/PageHandlerFactory.cs b/src/Spring/Spring.Web/Web/Support/PageHandlerFactory.cs index 466d116a..25ae6176 100644 --- a/src/Spring/Spring.Web/Web/Support/PageHandlerFactory.cs +++ b/src/Spring/Spring.Web/Web/Support/PageHandlerFactory.cs @@ -82,22 +82,17 @@ namespace Spring.Web.Support /// /// Create a handler instance for the given URL. /// + /// the application context corresponding to the current request /// The instance for this request. /// The HTTP data transfer method (GET, POST, ...) - /// The requested . + /// The requested . /// The physical path of the requested resource. /// A handler instance for the current request. - protected override IHttpHandler CreateHandlerInstance(HttpContext context, string requestType, string url, string physicalPath) + protected override IHttpHandler CreateHandlerInstance(IConfigurableApplicationContext appContext, HttpContext context, string requestType, string rawUrl, string physicalPath) { IHttpHandler handler; - IConfigurableApplicationContext appContext = GetCheckedApplicationContext(url); - if (appContext == null) - { - throw new InvalidOperationException("PageHandlerFactory requires an IConfigurableApplicationContext"); - } - - string appRelativeVirtualPath = WebUtils.GetAppRelativePath(url); + string appRelativeVirtualPath = WebUtils.GetAppRelativePath(rawUrl); NamedObjectDefinition namedPageDefinition = FindWebObjectDefinition(appRelativeVirtualPath, appContext.ObjectFactory); if (namedPageDefinition != null) @@ -113,50 +108,17 @@ namespace Spring.Web.Support // execution pipeline "entry-point" - create page instance only // and defer configuration to PreRequestHandlerExecute step handler = (IHttpHandler)appContext.CreateObject(namedPageDefinition.Name, typeof(IHttpHandler), null); + WebSupportModule.ConfigureHandler(context, handler, appContext, namedPageDefinition.Name, true); } - WebSupportModule.SetCurrentHandlerConfiguration(appContext, namedPageDefinition.Name, true); } else { - handler = WebObjectUtils.CreateHandler(context, url); - - // is this a nested call (HttpServerUtility.Transfer() or HttpServerUtility.Execute())? - if (context.Handler != null) - { - // apply ObjectPostProcessors now - handler = WebSupportModule.ConfigureHandler(handler, appContext, url, false); - } - else - { - // execution pipeline "entry-point" - create page instance only - // and defer configuration to PreRequestHandlerExecute step - WebSupportModule.SetCurrentHandlerConfiguration(appContext, url, false); - } + handler = WebObjectUtils.CreateHandler(context, rawUrl); + // let WebSupportModule handle configuration + handler = WebSupportModule.ConfigureHandler(context, handler, appContext, rawUrl, false); } - ApplyDependencyInjectionInfrastructure(handler, appContext); - return handler; } - - /// - /// Apply dependency injection stuff on the handler. - /// - /// the handler to be intercepted - /// the context responsible for configuring this handler - private static void ApplyDependencyInjectionInfrastructure(IHttpHandler handler, IApplicationContext applicationContext) - { - if (handler is Control) - { - ControlInterceptor.EnsureControlIntercepted(applicationContext, (Control)handler); - } - else - { - if (handler is ISupportsWebDependencyInjection) - { - ((ISupportsWebDependencyInjection)handler).DefaultApplicationContext = applicationContext; - } - } - } } } diff --git a/test/Spring/Spring.Web.Tests/Web/Support/AbstractHandlerFactoryTests.cs b/test/Spring/Spring.Web.Tests/Web/Support/AbstractHandlerFactoryTests.cs index 67c77379..4ed2736c 100644 --- a/test/Spring/Spring.Web.Tests/Web/Support/AbstractHandlerFactoryTests.cs +++ b/test/Spring/Spring.Web.Tests/Web/Support/AbstractHandlerFactoryTests.cs @@ -79,7 +79,7 @@ namespace Spring.Web.Support #region AbstractHandlerFactory implementations - protected override IHttpHandler CreateHandlerInstance( HttpContext context, string requestType, string url, string physicalPath ) + protected override IHttpHandler CreateHandlerInstance( IConfigurableApplicationContext appContext, HttpContext context, string requestType, string url, string physicalPath ) { throw new NotImplementedException(); } @@ -105,12 +105,12 @@ namespace Spring.Web.Support return base.GetCheckedApplicationContext(virtualPath); } - protected override IHttpHandler CreateHandlerInstance(HttpContext context, string requestType, string url, string physicalPath ) + protected override IHttpHandler CreateHandlerInstance(IConfigurableApplicationContext appContext, HttpContext context, string requestType, string url, string physicalPath ) { - return CreateHandlerInstanceStub(context, requestType, url, physicalPath); + return CreateHandlerInstanceStub(appContext, context, requestType, url, physicalPath); } - public virtual IHttpHandler CreateHandlerInstanceStub(HttpContext context, string requestType, string url, string physicalPath) + public virtual IHttpHandler CreateHandlerInstanceStub(IConfigurableApplicationContext appContext, HttpContext context, string requestType, string url, string physicalPath) { throw new NotImplementedException(); } @@ -178,7 +178,7 @@ namespace Spring.Web.Support using(Record(mocks)) { Expect.Call(reusableHandler.IsReusable).Return(true); - Expect.Call(f.CreateHandlerInstanceStub(null, null, "reusable", null)).Return(reusableHandler); + Expect.Call(f.CreateHandlerInstanceStub(null, null, null, "reusable", null)).Return(reusableHandler); } using (Playback(mocks)) { @@ -201,8 +201,8 @@ namespace Spring.Web.Support // - CreateHandlerInstance() is called for each request using(Record(mocks)) { - Expect.Call(f.CreateHandlerInstanceStub(null, null, "notreusable", null)).Return(nonReusableHandler); - Expect.Call(f.CreateHandlerInstanceStub(null, null, "notreusable", null)).Return(nonReusableHandler2); + Expect.Call(f.CreateHandlerInstanceStub(null, null, null, "notreusable", null)).Return(nonReusableHandler); + Expect.Call(f.CreateHandlerInstanceStub(null, null, null, "notreusable", null)).Return(nonReusableHandler2); } using (Playback(mocks)) {