Fixed bug with ComponentRegistration.Instance in which the instance type was not...
[castle.git] / MonoRail / Castle.MonoRail.Framework / MonoRailHttpHandlerFactory.cs
blobd2eb3a5fe5bc96a993dd84efd7664b4b2e2c515d
1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle.MonoRail.Framework
17 using System;
18 using System.Collections.Generic;
19 using System.Threading;
20 using System.Web;
21 using Adapters;
22 using Castle.Core;
23 using Castle.MonoRail.Framework.Container;
24 using Castle.MonoRail.Framework.Configuration;
25 using Castle.MonoRail.Framework.Descriptors;
26 using Routing;
27 using Services;
29 /// <summary>
30 /// Coordinates the creation of new <see cref="MonoRailHttpHandler"/>
31 /// and uses the configuration to obtain the correct factories
32 /// instances.
33 /// </summary>
34 public class MonoRailHttpHandlerFactory : IHttpHandlerFactory
36 private readonly static string CurrentEngineContextKey = "currentmrengineinstance";
37 private readonly static string CurrentControllerKey = "currentmrcontroller";
38 private readonly static string CurrentControllerContextKey = "currentmrcontrollercontext";
39 private readonly ReaderWriterLock locker = new ReaderWriterLock();
41 private static IMonoRailConfiguration configuration;
42 private static IMonoRailContainer mrContainer;
43 private static IUrlTokenizer urlTokenizer;
44 private static IEngineContextFactory engineContextFactory;
45 private static IServiceProviderLocator serviceProviderLocator;
46 private static IControllerFactory controllerFactory;
47 private static IControllerContextFactory controllerContextFactory;
48 private static IStaticResourceRegistry staticResourceRegistry;
50 /// <summary>
51 /// Initializes a new instance of the <see cref="MonoRailHttpHandlerFactory"/> class.
52 /// </summary>
53 public MonoRailHttpHandlerFactory() : this(ServiceProviderLocator.Instance)
57 /// <summary>
58 /// Initializes a new instance of the <see cref="MonoRailHttpHandlerFactory"/> class.
59 /// </summary>
60 /// <param name="serviceLocator">The service locator.</param>
61 public MonoRailHttpHandlerFactory(IServiceProviderLocator serviceLocator)
63 serviceProviderLocator = serviceLocator;
66 /// <summary>
67 /// Returns an instance of a class that implements
68 /// the <see cref="T:System.Web.IHttpHandler"></see> interface.
69 /// </summary>
70 /// <param name="context">An instance of the <see cref="T:System.Web.HttpContext"></see> class that provides references to intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param>
71 /// <param name="requestType">The HTTP data transfer method (GET or POST) that the client uses.</param>
72 /// <param name="url">The <see cref="P:System.Web.HttpRequest.RawUrl"></see> of the requested resource.</param>
73 /// <param name="pathTranslated">The <see cref="P:System.Web.HttpRequest.PhysicalApplicationPath"></see> to the requested resource.</param>
74 /// <returns>
75 /// A new <see cref="T:System.Web.IHttpHandler"></see> object that processes the request.
76 /// </returns>
77 public virtual IHttpHandler GetHandler(HttpContext context,
78 String requestType,
79 String url, String pathTranslated)
81 PerformOneTimeInitializationIfNecessary(context);
83 EnsureServices();
85 HttpRequest req = context.Request;
87 RouteMatch routeMatch = (RouteMatch) context.Items[RouteMatch.RouteMatchKey] ?? new RouteMatch();
89 UrlInfo urlInfo = urlTokenizer.TokenizeUrl(req.FilePath, req.PathInfo, req.Url, req.IsLocal, req.ApplicationPath);
91 if (urlInfo.Area == "MonoRail" && urlInfo.Controller == "Files")
93 return new ResourceFileHandler(urlInfo, new DefaultStaticResourceRegistry());
96 // TODO: Identify requests for files (js files) and serve them directly bypassing the flow
98 IEngineContext engineContext = engineContextFactory.Create(mrContainer, urlInfo, context, routeMatch);
99 engineContext.AddService(typeof(IEngineContext), engineContext);
100 context.Items[CurrentEngineContextKey] = engineContext;
102 IController controller;
106 controller = controllerFactory.CreateController(urlInfo.Area, urlInfo.Controller);
108 catch(ControllerNotFoundException)
110 return new NotFoundHandler(urlInfo.Area, urlInfo.Controller, engineContext);
112 catch(Exception ex)
114 HttpResponse response = context.Response;
116 if (response.StatusCode == 200)
118 response.StatusCode = 500;
121 engineContext.LastException = ex;
123 engineContext.Services.ExtensionManager.RaiseUnhandledError(engineContext);
125 throw new MonoRailException("Error creating controller " + urlInfo.Controller, ex);
128 ControllerMetaDescriptor controllerDesc =
129 mrContainer.ControllerDescriptorProvider.BuildDescriptor(controller);
131 IControllerContext controllerContext =
132 controllerContextFactory.Create(urlInfo.Area, urlInfo.Controller, urlInfo.Action, controllerDesc, routeMatch);
134 engineContext.CurrentController = controller;
135 engineContext.CurrentControllerContext = controllerContext;
137 context.Items[CurrentControllerKey] = controller;
138 context.Items[CurrentControllerContextKey] = controllerContext;
140 return CreateHandler(controllerDesc, engineContext, controller, controllerContext);
143 /// <summary>
144 /// Creates the handler.
145 /// </summary>
146 /// <param name="controllerDesc">The controller descriptor.</param>
147 /// <param name="engineContext">The engine context.</param>
148 /// <param name="controller">The controller.</param>
149 /// <param name="controllerContext">The controller context.</param>
150 /// <returns>
151 /// A new <see cref="T:System.Web.IHttpHandler"></see> object that processes the request.
152 /// </returns>
153 protected virtual IHttpHandler CreateHandler(ControllerMetaDescriptor controllerDesc, IEngineContext engineContext, IController controller, IControllerContext controllerContext)
155 if (IgnoresSession(controllerDesc.ControllerDescriptor))
157 return new SessionlessMonoRailHttpHandler(engineContext, controller, controllerContext);
159 return new MonoRailHttpHandler(engineContext, controller, controllerContext);
162 /// <summary>
163 /// Enables a factory to reuse an existing handler instance.
164 /// </summary>
165 /// <param name="handler">The <see cref="T:System.Web.IHttpHandler"></see> object to reuse.</param>
166 public virtual void ReleaseHandler(IHttpHandler handler)
170 /// <summary>
171 /// Resets the state (only used from test cases)
172 /// </summary>
173 public void ResetState()
175 configuration = null;
176 mrContainer = null;
177 urlTokenizer = null;
178 engineContextFactory = null;
179 serviceProviderLocator = null;
180 controllerFactory = null;
181 controllerContextFactory = null;
182 staticResourceRegistry = null;
185 /// <summary>
186 /// Gets or sets the configuration.
187 /// </summary>
188 /// <value>The configuration.</value>
189 public IMonoRailConfiguration Configuration
191 get { return configuration; }
192 set { configuration = value; }
195 /// <summary>
196 /// Gets or sets the container.
197 /// </summary>
198 /// <value>The container.</value>
199 public IMonoRailContainer Container
201 get { return mrContainer; }
202 set { mrContainer = value; }
205 /// <summary>
206 /// Gets or sets the service provider locator.
207 /// </summary>
208 /// <value>The service provider locator.</value>
209 public IServiceProviderLocator ProviderLocator
211 get { return serviceProviderLocator; }
212 set { serviceProviderLocator = value; }
215 /// <summary>
216 /// Gets or sets the URL tokenizer.
217 /// </summary>
218 /// <value>The URL tokenizer.</value>
219 public IUrlTokenizer UrlTokenizer
221 get { return urlTokenizer; }
222 set { urlTokenizer = value; }
225 /// <summary>
226 /// Gets or sets the engine context factory.
227 /// </summary>
228 /// <value>The engine context factory.</value>
229 public IEngineContextFactory EngineContextFactory
231 get { return engineContextFactory; }
232 set { engineContextFactory = value; }
235 /// <summary>
236 /// Gets or sets the controller factory.
237 /// </summary>
238 /// <value>The controller factory.</value>
239 public IControllerFactory ControllerFactory
241 get { return controllerFactory; }
242 set { controllerFactory = value; }
245 /// <summary>
246 /// Gets or sets the controller context factory.
247 /// </summary>
248 /// <value>The controller context factory.</value>
249 public IControllerContextFactory ControllerContextFactory
251 get { return controllerContextFactory; }
252 set { controllerContextFactory = value; }
255 /// <summary>
256 /// Checks whether we should ignore session for the specified controller.
257 /// </summary>
258 /// <param name="controllerDesc">The controller desc.</param>
259 /// <returns></returns>
260 protected virtual bool IgnoresSession(ControllerDescriptor controllerDesc)
262 return controllerDesc.Sessionless;
265 /// <summary>
266 /// Creates the default service container.
267 /// </summary>
268 /// <param name="userServiceProvider">The user service provider.</param>
269 /// <param name="appInstance">The app instance.</param>
270 /// <returns></returns>
271 protected virtual IMonoRailContainer CreateDefaultMonoRailContainer(IServiceProviderEx userServiceProvider, HttpApplication appInstance)
273 DefaultMonoRailContainer container = new DefaultMonoRailContainer(userServiceProvider);
275 container.UseServicesFromParent();
276 container.InstallPrimordialServices();
277 container.Configure(Configuration);
279 FireContainerCreated(appInstance, container);
281 // Too dependent on Http and MR surroundings services to be moved to Container class
282 if (!container.HasService<IServerUtility>())
284 container.AddService<IServerUtility>(new ServerUtilityAdapter(appInstance.Context.Server));
286 if (!container.HasService<IRoutingEngine>())
288 container.AddService<IRoutingEngine>(RoutingModuleEx.Engine);
291 container.InstallMissingServices();
292 container.StartExtensionManager();
294 FireContainerInitialized(appInstance, container);
296 return container;
299 #region Static accessors
301 /// <summary>
302 /// Gets the current engine context.
303 /// </summary>
304 /// <value>The current engine context.</value>
305 public static IEngineContext CurrentEngineContext
307 get { return HttpContext.Current.Items[CurrentEngineContextKey] as IEngineContext; }
310 /// <summary>
311 /// Gets the current controller.
312 /// </summary>
313 /// <value>The current controller.</value>
314 public static IController CurrentController
316 get { return HttpContext.Current.Items[CurrentControllerKey] as IController; }
319 /// <summary>
320 /// Gets the current controller context.
321 /// </summary>
322 /// <value>The current controller context.</value>
323 public static IControllerContext CurrentControllerContext
325 get { return HttpContext.Current.Items[CurrentControllerContextKey] as IControllerContext; }
328 #endregion
330 private void PerformOneTimeInitializationIfNecessary(HttpContext context)
332 locker.AcquireReaderLock(Timeout.Infinite);
334 if (mrContainer != null)
336 locker.ReleaseReaderLock();
337 return;
340 locker.UpgradeToWriterLock(Timeout.Infinite);
342 if (mrContainer != null) // remember remember the race condition
344 locker.ReleaseWriterLock();
345 return;
350 if (configuration == null)
352 configuration = ObtainConfiguration(context.ApplicationInstance);
355 IServiceProviderEx userServiceProvider = serviceProviderLocator.LocateProvider();
357 mrContainer = CreateDefaultMonoRailContainer(userServiceProvider, context.ApplicationInstance);
359 finally
361 locker.ReleaseWriterLock();
365 private void FireContainerCreated(HttpApplication instance, DefaultMonoRailContainer container)
367 IMonoRailContainerEvents events = instance as IMonoRailContainerEvents;
369 if (events != null)
371 events.Created(container);
375 private void FireContainerInitialized(HttpApplication instance, DefaultMonoRailContainer container)
377 IMonoRailContainerEvents events = instance as IMonoRailContainerEvents;
379 if (events != null)
381 events.Initialized(container);
385 private IMonoRailConfiguration ObtainConfiguration(HttpApplication appInstance)
387 IMonoRailConfiguration config = MonoRailConfiguration.GetConfig();
389 IMonoRailConfigurationEvents events = appInstance as IMonoRailConfigurationEvents;
391 if (events != null)
393 config = config ?? new MonoRailConfiguration();
395 events.Configure(config);
398 if (config == null)
400 throw new ApplicationException("You have to provide a small configuration to use " +
401 "MonoRail. This can be done using the web.config or " +
402 "your global asax (your class that extends HttpApplication) " +
403 "through the method MonoRail_Configure(IMonoRailConfiguration config). " +
404 "Check the samples or the documentation.");
407 return config;
410 private void EnsureServices()
412 if (urlTokenizer == null)
414 urlTokenizer = mrContainer.UrlTokenizer;
416 if (engineContextFactory == null)
418 engineContextFactory = mrContainer.EngineContextFactory;
420 if (controllerFactory == null)
422 controllerFactory = mrContainer.ControllerFactory;
424 if (controllerContextFactory == null)
426 controllerContextFactory = mrContainer.ControllerContextFactory;
428 if (staticResourceRegistry == null)
430 staticResourceRegistry = mrContainer.StaticResourceRegistry;
434 /// <summary>
435 /// Handles the controller not found situation
436 /// </summary>
437 public class NotFoundHandler : IHttpHandler
439 private readonly string area;
440 private readonly string controller;
441 private readonly IEngineContext engineContext;
443 /// <summary>
444 /// Initializes a new instance of the <see cref="NotFoundHandler"/> class.
445 /// </summary>
446 /// <param name="area">The area.</param>
447 /// <param name="controller">The controller.</param>
448 /// <param name="engineContext">The engine context.</param>
449 public NotFoundHandler(string area, string controller, IEngineContext engineContext)
451 this.area = area;
452 this.controller = controller;
453 this.engineContext = engineContext;
456 /// <summary>
457 /// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler"/> interface.
458 /// </summary>
459 /// <param name="context">An <see cref="T:System.Web.HttpContext"/> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param>
460 public void ProcessRequest(HttpContext context)
462 engineContext.Response.StatusCode = 404;
463 engineContext.Response.StatusDescription = "Not found";
465 if (engineContext.Services.ViewEngineManager.HasTemplate("rescues/404"))
467 Dictionary<string, object> parameters = new Dictionary<string, object>();
469 engineContext.Services.ViewEngineManager.Process("rescues/404", null, engineContext.Response.Output, parameters);
471 return; // gracefully handled
474 throw new ControllerNotFoundException(area, controller);
477 /// <summary>
478 /// Gets a value indicating whether another request can use the <see cref="T:System.Web.IHttpHandler"/> instance.
479 /// </summary>
480 /// <value></value>
481 /// <returns>true if the <see cref="T:System.Web.IHttpHandler"/> instance is reusable; otherwise, false.</returns>
482 public bool IsReusable
484 get { return false; }