Fix the build.
[castle.git] / MonoRail / Castle.MonoRail.Framework / EngineContextModule.cs
blob1038a42337b1579a9de3f6d5ce0b8f8323fd454a
1 // Copyright 2004-2007 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.Web;
19 using Castle.Core.Logging;
20 using Castle.MonoRail.Framework.Adapters;
21 using Castle.MonoRail.Framework.Extensions.ExceptionChaining;
22 using Routing;
24 /// <summary>
25 /// Provides the services used and shared by the framework. Also
26 /// is in charge of creating an implementation of <see cref="IRailsEngineContext"/>
27 /// upon the start of a new request.
28 /// </summary>
29 public class EngineContextModule : IHttpModule
31 internal static readonly String RailsContextKey = "rails.context";
32 private static readonly Object initLock = new Object();
34 private static MonoRailServiceContainer container;
36 private ILogger logger = NullLogger.Instance;
38 /// <summary>
39 /// Configures the framework, starts the services
40 /// and application hooks.
41 /// </summary>
42 /// <param name="context"></param>
43 public void Init(HttpApplication context)
45 if (context.Context.Error != null)
47 throw new Exception(
48 "An exception happened on Global application or on a module that run before MonoRail's module. " +
49 "MonoRail will not be initialized and further requests are going to fail. " +
50 "Fix the cause of the error reported below.", context.Context.Error);
53 lock(initLock)
55 CreateAndStartContainer(context);
58 context.BeginRequest += OnStartMonoRailRequest;
59 context.AuthorizeRequest += CreateControllerAndRunStartRequestFilters;
61 SubscribeToApplicationHooks(context);
64 /// <summary>
65 /// Disposes of the resources (other than memory) used by the
66 /// module that implements <see langword="IHttpModule."/>
67 /// </summary>
68 public void Dispose()
72 /// <summary>
73 /// Creates and starts MonoRail's service container.
74 /// </summary>
75 /// <param name="context"></param>
76 public void CreateAndStartContainer(HttpApplication context)
78 if (container == null)
80 container = new MonoRailServiceContainer();
81 container.RegisterBaseService(typeof(IServerUtility), new ServerUtilityAdapter(context.Server));
82 container.RegisterBaseService(typeof(IRoutingEngine), RoutingModuleEx.Engine);
83 container.Start();
85 ILoggerFactory loggerFactory = (ILoggerFactory) container.GetService(typeof(ILoggerFactory));
87 if (loggerFactory != null)
89 logger = loggerFactory.Create(typeof(EngineContextModule));
93 ServiceContainerAccessor.ServiceContainer = container;
96 #region MonoRail Request Lifecycle
98 /// <summary>
99 /// This method is invoked in response to BeginRequest event.
100 /// It checks if the request should be treat by MonoRail (by reading the file extension)
101 /// and if so, creates the <see cref="IRailsEngineContext"/> instance.
102 /// </summary>
103 /// <param name="sender">The HttpApplication instance</param>
104 /// <param name="e">Event information</param>
105 private void OnStartMonoRailRequest(object sender, EventArgs e)
107 HttpApplication app = (HttpApplication) sender;
109 // Is this request a MonoRail request?
110 if (!container.IsMonoRailRequest(app.Context.Request.FilePath))
112 return;
115 // Mark it so we dont have to check the file extension again
116 MarkRequestAsMonoRailRequest(app.Context);
118 // Creates the our context
119 IRailsEngineContext context = CreateRailsEngineContext(app.Context);
121 #region TestSupport related
123 String isTest = context.Request.Headers["IsTestWorkerRequest"];
125 if ("true" == isTest)
127 Castle.MonoRail.Framework.Internal.Test.TestContextHolder.SetContext(app.Context);
130 #endregion
133 /// <summary>
134 /// Creates the controller, selects the target action
135 /// and run start request filters.
136 /// </summary>
137 /// <param name="sender">The HttpApplication instance</param>
138 /// <param name="e">Event information</param>
139 private void CreateControllerAndRunStartRequestFilters(object sender, EventArgs e)
141 HttpApplication app = (HttpApplication) sender;
142 if (!IsMonoRailRequest(app.Context)) return;
144 IRailsEngineContext context = ObtainRailsEngineContext(app.Context);
146 Controller controller;
150 controller = CreateController(context);
152 catch(Exception ex)
154 IViewEngineManager viewEngineManager = context.GetService<IViewEngineManager>();
156 if (viewEngineManager.HasTemplate("rescues/404"))
158 viewEngineManager.Process(context, new Controller.EmptyController(context), "rescues/404");
160 IExceptionProcessor exProcessor = context.GetService<IExceptionProcessor>();
162 if (exProcessor != null)
164 exProcessor.ProcessException(ex);
167 app.Context.Response.End();
169 else
171 throw;
174 return;
177 IControllerLifecycleExecutor executor = CreateControllerExecutor(controller, context);
179 UrlInfo info = context.UrlInfo;
181 executor.InitializeController(info.Area, info.Controller, info.Action);
183 if (!executor.SelectAction(info.Action, info.Controller))
185 // Could not even select the action, stop here
187 return;
190 if (!executor.RunStartRequestFilters())
192 // Cancel request execution as
193 // one of the filters returned false
194 // We assume that the filter that stopped the request, also sent a redirect
195 // or something similar
197 context.UnderlyingContext.Response.End();
199 executor.Dispose();
203 #endregion
205 private static void MarkRequestAsMonoRailRequest(HttpContext context)
207 context.Items["is.mr.request"] = true;
210 private static bool IsMonoRailRequest(HttpContext context)
212 return context.Items.Contains("is.mr.request");
215 /// <summary>
216 /// Uses the url information and the controller factory
217 /// to instantiate the proper controller.
218 /// </summary>
219 /// <param name="context">MonoRail's request context</param>
220 /// <returns>A controller instance</returns>
221 private Controller CreateController(IRailsEngineContext context)
223 UrlInfo info = context.UrlInfo;
225 if (logger.IsDebugEnabled)
227 logger.DebugFormat("Starting request process for '{0}'/'{1}.{2}' Extension '{3}' with url '{4}'",
228 info.Area, info.Controller, info.Action, info.Extension, info.UrlRaw);
231 IControllerFactory controllerFactory = (IControllerFactory) context.GetService(typeof(IControllerFactory));
233 Controller controller = controllerFactory.CreateController(info);
235 return controller;
238 /// <summary>
239 /// Creates the and initialize executor.
240 /// </summary>
241 /// <param name="controller">The controller.</param>
242 /// <param name="context">The context.</param>
243 /// <returns></returns>
244 private IControllerLifecycleExecutor CreateControllerExecutor(Controller controller, IRailsEngineContext context)
246 IControllerLifecycleExecutorFactory factory =
247 (IControllerLifecycleExecutorFactory) context.GetService(typeof(IControllerLifecycleExecutorFactory));
249 IControllerLifecycleExecutor executor = factory.CreateExecutor(controller, context);
251 context.Items[ControllerLifecycleExecutor.ExecutorEntry] = executor;
253 return executor;
256 /// <summary>
257 /// Registers to <c>HttpApplication</c> events
258 /// </summary>
259 /// <param name="context">The application instance</param>
260 private void SubscribeToApplicationHooks(HttpApplication context)
262 context.BeginRequest += OnBeginRequest;
263 context.EndRequest += OnEndRequest;
264 context.AcquireRequestState += OnAcquireRequestState;
265 context.ReleaseRequestState += OnReleaseRequestState;
266 context.PreRequestHandlerExecute += OnPreRequestHandlerExecute;
267 context.PostRequestHandlerExecute += OnPostRequestHandlerExecute;
268 context.AuthenticateRequest += OnAuthenticateRequest;
269 context.AuthorizeRequest += OnAuthorizeRequest;
270 context.Error += OnError;
271 context.ResolveRequestCache += OnResolveRequestCache;
272 context.UpdateRequestCache += OnUpdateRequestCache;
275 #region Hooks dispatched to extensions
277 private void OnBeginRequest(object sender, EventArgs e)
279 HttpApplication app = (HttpApplication) sender;
280 if (!IsMonoRailRequest(app.Context)) return;
281 IRailsEngineContext mrContext = ObtainContextFromApplication(sender);
283 if (mrContext == null) throw new NullReferenceException("mrContext");
285 if (container == null) throw new NullReferenceException("container");
286 if (container.extensionManager == null) throw new NullReferenceException("container.extensionManager");
288 container.extensionManager.RaiseContextCreated(mrContext);
291 private void OnAuthenticateRequest(object sender, EventArgs e)
293 HttpApplication app = (HttpApplication)sender;
294 if (!IsMonoRailRequest(app.Context)) return;
295 IRailsEngineContext mrContext = ObtainContextFromApplication(sender);
297 container.extensionManager.RaiseAuthenticateRequest(mrContext);
300 private void OnAuthorizeRequest(object sender, EventArgs e)
302 HttpApplication app = (HttpApplication)sender;
303 if (!IsMonoRailRequest(app.Context)) return;
304 IRailsEngineContext mrContext = ObtainContextFromApplication(sender);
306 container.extensionManager.RaiseAuthorizeRequest(mrContext);
309 private void OnEndRequest(object sender, EventArgs e)
311 HttpApplication app = (HttpApplication)sender;
312 if (!IsMonoRailRequest(app.Context)) return;
313 IRailsEngineContext mrContext = ObtainContextFromApplication(sender);
315 container.extensionManager.RaiseContextDisposed(mrContext);
318 private void OnAcquireRequestState(object sender, EventArgs e)
320 HttpApplication app = (HttpApplication)sender;
321 if (!IsMonoRailRequest(app.Context)) return;
322 IRailsEngineContext mrContext = ObtainContextFromApplication(sender);
324 container.extensionManager.RaiseAcquireRequestState(mrContext);
327 private void OnReleaseRequestState(object sender, EventArgs e)
329 HttpApplication app = (HttpApplication)sender;
330 if (!IsMonoRailRequest(app.Context)) return;
331 IRailsEngineContext mrContext = ObtainContextFromApplication(sender);
333 container.extensionManager.RaiseReleaseRequestState(mrContext);
336 private void OnPreRequestHandlerExecute(object sender, EventArgs e)
338 HttpApplication app = (HttpApplication)sender;
339 if (!IsMonoRailRequest(app.Context)) return;
340 IRailsEngineContext mrContext = ObtainContextFromApplication(sender);
342 container.extensionManager.RaisePreProcess(mrContext);
345 private void OnPostRequestHandlerExecute(object sender, EventArgs e)
347 HttpApplication app = (HttpApplication)sender;
348 if (!IsMonoRailRequest(app.Context)) return;
349 IRailsEngineContext mrContext = ObtainContextFromApplication(sender);
351 container.extensionManager.RaisePostProcess(mrContext);
354 private void OnError(object sender, EventArgs e)
356 HttpApplication app = (HttpApplication)sender;
357 if (!IsMonoRailRequest(app.Context)) return;
358 IRailsEngineContext mrContext = ObtainContextFromApplication(sender);
360 if (mrContext != null)
362 mrContext.LastException = mrContext.UnderlyingContext.Server.GetLastError();
364 container.extensionManager.RaiseUnhandledError(mrContext);
368 private void OnResolveRequestCache(object sender, EventArgs e)
370 HttpApplication app = (HttpApplication)sender;
371 if (!IsMonoRailRequest(app.Context)) return;
372 IRailsEngineContext mrContext = ObtainContextFromApplication(sender);
374 container.extensionManager.RaiseResolveRequestCache(mrContext);
377 private void OnUpdateRequestCache(object sender, EventArgs e)
379 HttpApplication app = (HttpApplication)sender;
380 if (!IsMonoRailRequest(app.Context)) return;
381 IRailsEngineContext mrContext = ObtainContextFromApplication(sender);
383 container.extensionManager.RaiseUpdateRequestCache(mrContext);
386 #endregion
388 private IRailsEngineContext CreateRailsEngineContext(HttpContext context)
390 IRailsEngineContext mrContext = ObtainRailsEngineContext(context);
392 if (mrContext == null)
394 IUrlTokenizer urlTokenizer = (IUrlTokenizer) container.GetService(typeof(IUrlTokenizer));
396 HttpRequest req = context.Request;
398 UrlInfo urlInfo = urlTokenizer.TokenizeUrl(req.FilePath, req.PathInfo, req.Url, req.IsLocal, req.ApplicationPath);
400 IServiceProvider userServiceProvider = ServiceProviderLocator.Instance.LocateProvider();
402 DefaultRailsEngineContext newContext = new DefaultRailsEngineContext(container, urlInfo, context, userServiceProvider);
404 context.Items[RailsContextKey] = newContext;
406 newContext.AddService(typeof(IRailsEngineContext), newContext);
408 mrContext = newContext;
411 return mrContext;
414 private static IRailsEngineContext ObtainContextFromApplication(object sender)
416 HttpApplication app = (HttpApplication) sender;
417 HttpContext context = app.Context;
419 return ObtainRailsEngineContext(context);
422 internal static bool Initialized
424 get { return container != null; }
427 internal static IRailsEngineContext ObtainRailsEngineContext(HttpContext context)
429 return (IRailsEngineContext) context.Items[RailsContextKey];