Add Newtonsoft.Json to the references of TestSiteBrail
[castle.git] / MonoRail / Castle.MonoRail.Framework / MonoRailHttpHandlerFactory.cs
blob55c7a4da6818cc66f6495177a0ee713ab259fb81
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 Providers;
27 using Routing;
28 using Services;
30 /// <summary>
31 /// Coordinates the creation of new <see cref="MonoRailHttpHandler"/>
32 /// and uses the configuration to obtain the correct factories
33 /// instances.
34 /// </summary>
35 public class MonoRailHttpHandlerFactory : IHttpHandlerFactory
37 private static readonly string CurrentEngineContextKey = "currentmrengineinstance";
38 private static readonly string CurrentControllerKey = "currentmrcontroller";
39 private static readonly string CurrentControllerContextKey = "currentmrcontrollercontext";
40 private readonly ReaderWriterLock locker = new ReaderWriterLock();
42 private static IMonoRailConfiguration configuration;
43 private static IMonoRailContainer mrContainer;
44 private static IUrlTokenizer urlTokenizer;
45 private static IEngineContextFactory engineContextFactory;
46 private static IServiceProviderLocator serviceProviderLocator;
47 private static IControllerFactory controllerFactory;
48 private static IControllerContextFactory controllerContextFactory;
49 private static IStaticResourceRegistry staticResourceRegistry;
51 /// <summary>
52 /// Initializes a new instance of the <see cref="MonoRailHttpHandlerFactory"/> class.
53 /// </summary>
54 public MonoRailHttpHandlerFactory()
55 : this(ServiceProviderLocator.Instance)
59 /// <summary>
60 /// Initializes a new instance of the <see cref="MonoRailHttpHandlerFactory"/> class.
61 /// </summary>
62 /// <param name="serviceLocator">The service locator.</param>
63 public MonoRailHttpHandlerFactory(IServiceProviderLocator serviceLocator)
65 serviceProviderLocator = serviceLocator;
68 /// <summary>
69 /// Returns an instance of a class that implements
70 /// the <see cref="T:System.Web.IHttpHandler"></see> interface.
71 /// </summary>
72 /// <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>
73 /// <param name="requestType">The HTTP data transfer method (GET or POST) that the client uses.</param>
74 /// <param name="url">The <see cref="P:System.Web.HttpRequest.RawUrl"></see> of the requested resource.</param>
75 /// <param name="pathTranslated">The <see cref="P:System.Web.HttpRequest.PhysicalApplicationPath"></see> to the requested resource.</param>
76 /// <returns>
77 /// A new <see cref="T:System.Web.IHttpHandler"></see> object that processes the request.
78 /// </returns>
79 public virtual IHttpHandler GetHandler(HttpContext context,
80 String requestType,
81 String url, String pathTranslated)
83 PerformOneTimeInitializationIfNecessary(context);
85 EnsureServices();
87 HttpRequest req = context.Request;
89 RouteMatch routeMatch = (RouteMatch)context.Items[RouteMatch.RouteMatchKey] ?? new RouteMatch();
91 UrlInfo urlInfo = urlTokenizer.TokenizeUrl(req.FilePath, req.PathInfo, req.Url, req.IsLocal, req.ApplicationPath);
93 if (urlInfo.Area == "MonoRail" && urlInfo.Controller == "Files")
95 return new ResourceFileHandler(urlInfo, new DefaultStaticResourceRegistry());
98 // TODO: Identify requests for files (js files) and serve them directly bypassing the flow
100 IEngineContext engineContext = engineContextFactory.Create(mrContainer, urlInfo, context, routeMatch);
101 engineContext.AddService(typeof(IEngineContext), engineContext);
102 context.Items[CurrentEngineContextKey] = engineContext;
104 IController controller;
108 controller = controllerFactory.CreateController(urlInfo.Area, urlInfo.Controller);
110 catch(ControllerNotFoundException)
112 return new NotFoundHandler(urlInfo.Area, urlInfo.Controller, engineContext);
114 catch(Exception ex)
116 HttpResponse response = context.Response;
118 if (response.StatusCode == 200)
120 response.StatusCode = 500;
123 engineContext.LastException = ex;
125 engineContext.Services.ExtensionManager.RaiseUnhandledError(engineContext);
127 throw new MonoRailException("Error creating controller " + urlInfo.Controller, ex);
130 ControllerMetaDescriptor controllerDesc =
131 mrContainer.ControllerDescriptorProvider.BuildDescriptor(controller);
133 IControllerContext controllerContext =
134 controllerContextFactory.Create(urlInfo.Area, urlInfo.Controller, urlInfo.Action, controllerDesc, routeMatch);
136 engineContext.CurrentController = controller;
137 engineContext.CurrentControllerContext = controllerContext;
139 context.Items[CurrentControllerKey] = controller;
140 context.Items[CurrentControllerContextKey] = controllerContext;
142 if (IsAsyncAction(controllerContext) == false)
144 return CreateHandler(controllerDesc, engineContext, controller, controllerContext);
146 else
148 return CreateAsyncHandler(controllerDesc, engineContext, (IAsyncController)controller, controllerContext);
152 /// <summary>
153 /// Creates the handler.
154 /// </summary>
155 /// <param name="controllerDesc">The controller descriptor.</param>
156 /// <param name="engineContext">The engine context.</param>
157 /// <param name="controller">The controller.</param>
158 /// <param name="controllerContext">The controller context.</param>
159 /// <returns>
160 /// A new <see cref="T:System.Web.IHttpHandler"></see> object that processes the request.
161 /// </returns>
162 protected virtual IHttpHandler CreateHandler(ControllerMetaDescriptor controllerDesc, IEngineContext engineContext,
163 IController controller, IControllerContext controllerContext)
165 if (IgnoresSession(controllerDesc.ControllerDescriptor))
167 return new SessionlessMonoRailHttpHandler(engineContext, controller, controllerContext);
169 return new MonoRailHttpHandler(engineContext, controller, controllerContext);
173 /// <summary>
174 /// Creates the handler.
175 /// </summary>
176 /// <param name="controllerDesc">The controller descriptor.</param>
177 /// <param name="engineContext">The engine context.</param>
178 /// <param name="controller">The controller.</param>
179 /// <param name="controllerContext">The controller context.</param>
180 /// <returns>
181 /// A new <see cref="T:System.Web.IHttpHandler"></see> object that processes the request.
182 /// </returns>
183 protected virtual IHttpAsyncHandler CreateAsyncHandler(ControllerMetaDescriptor controllerDesc,
184 IEngineContext engineContext, IAsyncController controller,
185 IControllerContext controllerContext)
187 if (IgnoresSession(controllerDesc.ControllerDescriptor))
189 return new AsyncSessionlessMonoRailHttpHandler(engineContext, controller, controllerContext);
191 return new AsyncMonoRailHttpHandler(engineContext, controller, controllerContext);
195 /// <summary>
196 /// Enables a factory to reuse an existing handler instance.
197 /// </summary>
198 /// <param name="handler">The <see cref="T:System.Web.IHttpHandler"></see> object to reuse.</param>
199 public virtual void ReleaseHandler(IHttpHandler handler)
203 /// <summary>
204 /// Resets the state (only used from test cases)
205 /// </summary>
206 public void ResetState()
208 configuration = null;
209 mrContainer = null;
210 urlTokenizer = null;
211 engineContextFactory = null;
212 serviceProviderLocator = null;
213 controllerFactory = null;
214 controllerContextFactory = null;
215 staticResourceRegistry = null;
218 /// <summary>
219 /// Gets or sets the configuration.
220 /// </summary>
221 /// <value>The configuration.</value>
222 public IMonoRailConfiguration Configuration
224 get { return configuration; }
225 set { configuration = value; }
228 /// <summary>
229 /// Gets or sets the container.
230 /// </summary>
231 /// <value>The container.</value>
232 public IMonoRailContainer Container
234 get { return mrContainer; }
235 set { mrContainer = value; }
238 /// <summary>
239 /// Gets or sets the service provider locator.
240 /// </summary>
241 /// <value>The service provider locator.</value>
242 public IServiceProviderLocator ProviderLocator
244 get { return serviceProviderLocator; }
245 set { serviceProviderLocator = value; }
248 /// <summary>
249 /// Gets or sets the URL tokenizer.
250 /// </summary>
251 /// <value>The URL tokenizer.</value>
252 public IUrlTokenizer UrlTokenizer
254 get { return urlTokenizer; }
255 set { urlTokenizer = value; }
258 /// <summary>
259 /// Gets or sets the engine context factory.
260 /// </summary>
261 /// <value>The engine context factory.</value>
262 public IEngineContextFactory EngineContextFactory
264 get { return engineContextFactory; }
265 set { engineContextFactory = value; }
268 /// <summary>
269 /// Gets or sets the controller factory.
270 /// </summary>
271 /// <value>The controller factory.</value>
272 public IControllerFactory ControllerFactory
274 get { return controllerFactory; }
275 set { controllerFactory = value; }
278 /// <summary>
279 /// Gets or sets the controller context factory.
280 /// </summary>
281 /// <value>The controller context factory.</value>
282 public IControllerContextFactory ControllerContextFactory
284 get { return controllerContextFactory; }
285 set { controllerContextFactory = value; }
288 /// <summary>
289 /// Checks whether we should ignore session for the specified controller.
290 /// </summary>
291 /// <param name="controllerDesc">The controller desc.</param>
292 /// <returns></returns>
293 protected virtual bool IgnoresSession(ControllerDescriptor controllerDesc)
295 return controllerDesc.Sessionless;
298 /// <summary>
299 /// Checks whether the target action is an async method.
300 /// </summary>
301 /// <param name="controllerContext">The controller context.</param>
302 /// <returns></returns>
303 protected virtual bool IsAsyncAction(IControllerContext controllerContext)
305 if (controllerContext.ControllerDescriptor == null || controllerContext.Action == null)
307 return false;
309 return controllerContext.ControllerDescriptor.Actions[controllerContext.Action] is AsyncActionPair;
312 /// <summary>
313 /// Creates the default service container.
314 /// </summary>
315 /// <param name="userServiceProvider">The user service provider.</param>
316 /// <param name="appInstance">The app instance.</param>
317 /// <returns></returns>
318 protected virtual IMonoRailContainer CreateDefaultMonoRailContainer(IServiceProviderEx userServiceProvider,
319 HttpApplication appInstance)
321 DefaultMonoRailContainer container = new DefaultMonoRailContainer(userServiceProvider);
323 container.UseServicesFromParent();
324 container.InstallPrimordialServices();
325 container.Configure(Configuration);
327 FireContainerCreated(appInstance, container);
329 // Too dependent on Http and MR surroundings services to be moved to Container class
330 if (!container.HasService<IServerUtility>())
332 container.AddService<IServerUtility>(new ServerUtilityAdapter(appInstance.Context.Server));
334 if (!container.HasService<IRoutingEngine>())
336 container.AddService<IRoutingEngine>(RoutingModuleEx.Engine);
339 container.InstallMissingServices();
340 container.StartExtensionManager();
342 FireContainerInitialized(appInstance, container);
344 return container;
347 #region Static accessors
349 /// <summary>
350 /// Gets the current engine context.
351 /// </summary>
352 /// <value>The current engine context.</value>
353 public static IEngineContext CurrentEngineContext
357 if (HttpContext.Current == null)//for tests
358 return null;
359 return HttpContext.Current.Items[CurrentEngineContextKey] as IEngineContext;
363 /// <summary>
364 /// Gets the current controller.
365 /// </summary>
366 /// <value>The current controller.</value>
367 public static IController CurrentController
369 get { return HttpContext.Current.Items[CurrentControllerKey] as IController; }
372 /// <summary>
373 /// Gets the current controller context.
374 /// </summary>
375 /// <value>The current controller context.</value>
376 public static IControllerContext CurrentControllerContext
378 get { return HttpContext.Current.Items[CurrentControllerContextKey] as IControllerContext; }
381 #endregion
383 private void PerformOneTimeInitializationIfNecessary(HttpContext context)
385 locker.AcquireReaderLock(Timeout.Infinite);
387 if (mrContainer != null)
389 locker.ReleaseReaderLock();
390 return;
393 locker.UpgradeToWriterLock(Timeout.Infinite);
395 if (mrContainer != null) // remember remember the race condition
397 locker.ReleaseWriterLock();
398 return;
403 if (configuration == null)
405 configuration = ObtainConfiguration(context.ApplicationInstance);
408 IServiceProviderEx userServiceProvider = serviceProviderLocator.LocateProvider();
410 mrContainer = CreateDefaultMonoRailContainer(userServiceProvider, context.ApplicationInstance);
412 finally
414 locker.ReleaseWriterLock();
418 private void FireContainerCreated(HttpApplication instance, DefaultMonoRailContainer container)
420 IMonoRailContainerEvents events = instance as IMonoRailContainerEvents;
422 if (events != null)
424 events.Created(container);
428 private void FireContainerInitialized(HttpApplication instance, DefaultMonoRailContainer container)
430 IMonoRailContainerEvents events = instance as IMonoRailContainerEvents;
432 if (events != null)
434 events.Initialized(container);
438 private IMonoRailConfiguration ObtainConfiguration(HttpApplication appInstance)
440 IMonoRailConfiguration config = MonoRailConfiguration.GetConfig();
442 IMonoRailConfigurationEvents events = appInstance as IMonoRailConfigurationEvents;
444 if (events != null)
446 config = config ?? new MonoRailConfiguration();
448 events.Configure(config);
451 if (config == null)
453 throw new ApplicationException("You have to provide a small configuration to use " +
454 "MonoRail. This can be done using the web.config or " +
455 "your global asax (your class that extends HttpApplication) " +
456 "through the method MonoRail_Configure(IMonoRailConfiguration config). " +
457 "Check the samples or the documentation.");
460 return config;
463 private void EnsureServices()
465 if (urlTokenizer == null)
467 urlTokenizer = mrContainer.UrlTokenizer;
469 if (engineContextFactory == null)
471 engineContextFactory = mrContainer.EngineContextFactory;
473 if (controllerFactory == null)
475 controllerFactory = mrContainer.ControllerFactory;
477 if (controllerContextFactory == null)
479 controllerContextFactory = mrContainer.ControllerContextFactory;
481 if (staticResourceRegistry == null)
483 staticResourceRegistry = mrContainer.StaticResourceRegistry;
487 /// <summary>
488 /// Handles the controller not found situation
489 /// </summary>
490 public class NotFoundHandler : IHttpHandler
492 private readonly string area;
493 private readonly string controller;
494 private readonly IEngineContext engineContext;
496 /// <summary>
497 /// Initializes a new instance of the <see cref="NotFoundHandler"/> class.
498 /// </summary>
499 /// <param name="area">The area.</param>
500 /// <param name="controller">The controller.</param>
501 /// <param name="engineContext">The engine context.</param>
502 public NotFoundHandler(string area, string controller, IEngineContext engineContext)
504 this.area = area;
505 this.controller = controller;
506 this.engineContext = engineContext;
509 /// <summary>
510 /// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler"/> interface.
511 /// </summary>
512 /// <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>
513 public void ProcessRequest(HttpContext context)
515 engineContext.Response.StatusCode = 404;
516 engineContext.Response.StatusDescription = "Not found";
518 if (engineContext.Services.ViewEngineManager.HasTemplate("rescues/404"))
520 Dictionary<string, object> parameters = new Dictionary<string, object>();
522 engineContext.Services.ViewEngineManager.Process("rescues/404", null, engineContext.Response.Output, parameters);
524 return; // gracefully handled
527 throw new ControllerNotFoundException(area, controller);
530 /// <summary>
531 /// Gets a value indicating whether another request can use the <see cref="T:System.Web.IHttpHandler"/> instance.
532 /// </summary>
533 /// <value></value>
534 /// <returns>true if the <see cref="T:System.Web.IHttpHandler"/> instance is reusable; otherwise, false.</returns>
535 public bool IsReusable
537 get { return false; }