1 // Copyright 2004-2007 Castle Project - http://www.castleproject.org/
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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
19 using Castle
.Core
.Logging
;
20 using Castle
.MonoRail
.Framework
.Adapters
;
21 using Castle
.MonoRail
.Framework
.Extensions
.ExceptionChaining
;
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.
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
;
39 /// Configures the framework, starts the services
40 /// and application hooks.
42 /// <param name="context"></param>
43 public void Init(HttpApplication context
)
45 if (context
.Context
.Error
!= null)
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
);
55 CreateAndStartContainer(context
);
58 context
.BeginRequest
+= OnStartMonoRailRequest
;
59 context
.AuthorizeRequest
+= CreateControllerAndRunStartRequestFilters
;
61 SubscribeToApplicationHooks(context
);
65 /// Disposes of the resources (other than memory) used by the
66 /// module that implements <see langword="IHttpModule."/>
73 /// Creates and starts MonoRail's service container.
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
);
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
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.
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
))
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
);
134 /// Creates the controller, selects the target action
135 /// and run start request filters.
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
);
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();
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
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();
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");
216 /// Uses the url information and the controller factory
217 /// to instantiate the proper controller.
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
);
239 /// Creates the and initialize executor.
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
;
257 /// Registers to <c>HttpApplication</c> events
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
);
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
;
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
];