- If the controller cannot be found, MR searches for a special rescue "rescues/404...
[castle.git] / MonoRail / Castle.MonoRail.Framework / MonoRailServiceContainer.cs
blob15c15edf0c6fbdd66399829a655e41eac65381d7
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.Collections;
19 using System.Collections.Specialized;
20 using System.ComponentModel;
21 using System.Configuration;
22 using System.IO;
23 using System.Xml;
24 using Castle.Components.Validator;
25 using Castle.Core;
26 using Castle.MonoRail.Framework.Configuration;
27 using Castle.MonoRail.Framework.Internal;
28 using Castle.MonoRail.Framework.Services;
29 using Castle.MonoRail.Framework.Services.AjaxProxyGenerator;
31 /// <summary>
32 /// Parent Service container for the MonoRail framework
33 /// </summary>
34 public class MonoRailServiceContainer : AbstractServiceContainer
36 /// <summary>The only one Extension Manager</summary>
37 protected internal ExtensionManager extensionManager;
39 /// <summary>Prevents GC from collecting the extensions</summary>
40 private IList extensions = new ArrayList();
42 /// <summary>Keeps only one copy of the config</summary>
43 private MonoRailConfiguration config;
45 /// <summary></summary>
46 private IDictionary extension2handler;
48 /// <summary>
49 /// Initializes a new instance of the <see cref="MonoRailServiceContainer"/> class.
50 /// </summary>
51 public MonoRailServiceContainer()
53 extension2handler = new HybridDictionary(true);
56 /// <summary>
57 /// Initializes a new instance of the <see cref="MonoRailServiceContainer"/> class.
58 /// </summary>
59 /// <param name="config">The config.</param>
60 public MonoRailServiceContainer(MonoRailConfiguration config) : this()
62 this.config = config;
65 /// <summary>
66 /// Allows registration without the configuration
67 /// </summary>
68 public void RegisterBaseService(Type service, object instance)
70 InvokeServiceEnabled(instance);
71 InvokeInitialize(instance);
73 AddService(service, instance);
76 /// <summary>
77 /// Initializes the container state and its services
78 /// </summary>
79 public void Start()
81 DiscoverHttpHandlerExtensions();
83 InitConfiguration();
85 MonoRailConfiguration config = ObtainConfiguration();
87 InitExtensions(config);
88 InitServices(config);
91 /// <summary>
92 /// Checks whether the specified URL is to be handled by MonoRail
93 /// </summary>
94 /// <param name="url">The URL.</param>
95 /// <returns>
96 /// <see langword="true"/> if it is a MonoRail request; otherwise, <see langword="false"/>.
97 /// </returns>
98 public bool IsMonoRailRequest(String url)
100 String extension = Path.GetExtension(url);
102 return extension2handler.Contains(extension);
105 private void DiscoverHttpHandlerExtensions()
107 XmlDocument webConfig = new XmlDocument();
108 webConfig.Load(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "web.config"));
110 XmlNodeList addNodes = webConfig.DocumentElement.SelectNodes(
111 "/configuration/system.web/httpHandlers/add");
113 if (addNodes.Count == 0)
115 // Possibly the web.config configuration node is using a namespace declaration
117 XmlElement systemWebElem = webConfig.DocumentElement["system.web"];
118 XmlElement handlers = systemWebElem["httpHandlers"];
119 addNodes = handlers.ChildNodes;
122 bool found = false;
124 foreach(XmlNode addNode in addNodes)
126 if (addNode.NodeType != XmlNodeType.Element)
128 continue;
131 XmlElement addElem = (XmlElement) addNode;
133 String type = addElem.GetAttribute("type");
135 if (type.StartsWith("Castle.MonoRail.Framework.MonoRailHttpHandlerFactory"))
137 String extension = addElem.GetAttribute("path").Substring(1);
138 extension2handler.Add(extension, String.Empty);
139 found = true;
143 if (!found)
145 throw new RailsException("We inspected the web.config httpHandlers section and " +
146 "couldn't find an extension that mapped to MonoRailHttpHandlerFactory. " +
147 "Is your configuration right to use MonoRail?");
151 /// <summary>
152 /// Reads the configuration and initializes
153 /// registered extensions.
154 /// </summary>
155 /// <param name="config">The configuration object</param>
156 private void InitExtensions(MonoRailConfiguration config)
158 extensionManager = new ExtensionManager(this);
160 foreach(ExtensionEntry entry in config.ExtensionEntries)
162 AssertImplementsService(typeof(IMonoRailExtension), entry.ExtensionType);
164 IMonoRailExtension extension = (IMonoRailExtension) ActivateService(entry.ExtensionType);
166 extension.SetExtensionConfigNode(entry.ExtensionNode);
168 extensions.Add(extension);
172 /// <summary>
173 /// Coordinates the instantiation, registering and initialization (lifecycle-wise)
174 /// of the services used by MonoRail.
175 /// </summary>
176 /// <param name="config">The configuration object</param>
177 private void InitServices(MonoRailConfiguration config)
179 AddService(typeof(ExtensionManager), extensionManager);
180 AddService(typeof(MonoRailConfiguration), config);
182 IList services = InstantiateAndRegisterServices(config.ServiceEntries);
184 LifecycleService(services);
185 LifecycleService(extensions);
187 LifecycleInitialize(services);
188 LifecycleInitialize(extensions);
191 /// <summary>
192 /// Checks for services that implements <see cref="IInitializable"/>
193 /// or <see cref="ISupportInitialize"/> and initialize them through the interface
194 /// </summary>
195 /// <param name="services">List of MonoRail's services</param>
196 private void LifecycleInitialize(IList services)
198 foreach(object instance in services)
200 InvokeInitialize(instance);
204 /// <summary>
205 /// Checks for services that implements <see cref="IServiceEnabledComponent"/>
206 /// and invoke <see cref="IServiceEnabledComponent.Service"/> on them
207 /// </summary>
208 /// <param name="services">List of MonoRail's services</param>
209 private void LifecycleService(IList services)
211 foreach(object instance in services)
213 InvokeServiceEnabled(instance);
217 /// <summary>
218 /// Instantiates and registers the services used by MonoRail.
219 /// </summary>
220 /// <param name="services">The service's registry</param>
221 /// <returns>List of service's instances</returns>
222 private IList InstantiateAndRegisterServices(ServiceEntryCollection services)
224 IList instances = new ArrayList();
226 // Builtin services
228 foreach(DictionaryEntry entry in services.ServiceImplMap)
230 Type service = (Type) entry.Key;
231 Type impl = (Type) entry.Value;
233 AssertImplementsService(service, impl);
235 object instance = ActivateService(impl);
237 AddService(service, instance);
239 instances.Add(instance);
242 // Custom services
244 foreach(Type type in services.CustomServices)
246 object instance = ActivateService(type);
248 AddService(type, instance);
250 instances.Add(instance);
253 return instances;
256 /// <summary>
257 /// Registers the default implementation of services, if
258 /// they are not registered
259 /// </summary>
260 private void InitConfiguration()
262 config = ObtainConfiguration();
264 RegisterMissingServices(config);
267 /// <summary>
268 /// Checks whether non-optional services were supplied
269 /// through the configuration, and if not, register the
270 /// default implementation.
271 /// </summary>
272 /// <param name="config">The configuration object</param>
273 private void RegisterMissingServices(MonoRailConfiguration config)
275 ServiceEntryCollection services = config.ServiceEntries;
277 services.RegisterService(ServiceIdentification.ViewEngineManager,
278 typeof(DefaultViewEngineManager));
281 if (!services.HasService(ServiceIdentification.ViewSourceLoader))
283 services.RegisterService(ServiceIdentification.ViewSourceLoader,
284 typeof(FileAssemblyViewSourceLoader));
286 if (!services.HasService(ServiceIdentification.ScaffoldingSupport))
288 Type defaultScaffoldingType =
289 TypeLoadUtil.GetType(
290 TypeLoadUtil.GetEffectiveTypeName(
291 "Castle.MonoRail.ActiveRecordScaffold.ScaffoldingSupport, Castle.MonoRail.ActiveRecordScaffold"), true);
293 if (defaultScaffoldingType != null)
295 services.RegisterService(ServiceIdentification.ScaffoldingSupport, defaultScaffoldingType);
298 if (!services.HasService(ServiceIdentification.ControllerFactory))
300 if (config.ControllersConfig.CustomControllerFactory != null)
302 services.RegisterService(ServiceIdentification.ControllerFactory,
303 config.ControllersConfig.CustomControllerFactory);
305 else
307 services.RegisterService(ServiceIdentification.ControllerFactory,
308 typeof(DefaultControllerFactory));
311 if (!services.HasService(ServiceIdentification.ViewComponentFactory))
313 if (config.ViewComponentsConfig.CustomViewComponentFactory != null)
315 services.RegisterService(ServiceIdentification.ViewComponentFactory,
316 config.ViewComponentsConfig.CustomViewComponentFactory);
318 else
320 services.RegisterService(ServiceIdentification.ViewComponentFactory,
321 typeof(DefaultViewComponentFactory));
324 if (!services.HasService(ServiceIdentification.FilterFactory))
326 if (config.CustomFilterFactory != null)
328 services.RegisterService(ServiceIdentification.FilterFactory,
329 config.CustomFilterFactory);
331 else
333 services.RegisterService(ServiceIdentification.FilterFactory,
334 typeof(DefaultFilterFactory));
337 if (!services.HasService(ServiceIdentification.ResourceFactory))
339 services.RegisterService(ServiceIdentification.ResourceFactory, typeof(DefaultResourceFactory));
341 if (!services.HasService(ServiceIdentification.EmailSender))
343 services.RegisterService(ServiceIdentification.EmailSender, typeof(MonoRailSmtpSender));
345 if (!services.HasService(ServiceIdentification.ControllerDescriptorProvider))
347 services.RegisterService(ServiceIdentification.ControllerDescriptorProvider,
348 typeof(DefaultControllerDescriptorProvider));
350 if (!services.HasService(ServiceIdentification.ResourceDescriptorProvider))
352 services.RegisterService(ServiceIdentification.ResourceDescriptorProvider, typeof(DefaultResourceDescriptorProvider));
354 if (!services.HasService(ServiceIdentification.RescueDescriptorProvider))
356 services.RegisterService(ServiceIdentification.RescueDescriptorProvider, typeof(DefaultRescueDescriptorProvider));
358 if (!services.HasService(ServiceIdentification.LayoutDescriptorProvider))
360 services.RegisterService(ServiceIdentification.LayoutDescriptorProvider, typeof(DefaultLayoutDescriptorProvider));
362 if (!services.HasService(ServiceIdentification.HelperDescriptorProvider))
364 services.RegisterService(ServiceIdentification.HelperDescriptorProvider, typeof(DefaultHelperDescriptorProvider));
366 if (!services.HasService(ServiceIdentification.FilterDescriptorProvider))
368 services.RegisterService(ServiceIdentification.FilterDescriptorProvider, typeof(DefaultFilterDescriptorProvider));
370 if (!services.HasService(ServiceIdentification.EmailTemplateService))
372 services.RegisterService(ServiceIdentification.EmailTemplateService, typeof(EmailTemplateService));
374 if (!services.HasService(ServiceIdentification.ControllerTree))
376 services.RegisterService(ServiceIdentification.ControllerTree, typeof(DefaultControllerTree));
378 if (!services.HasService(ServiceIdentification.CacheProvider))
380 services.RegisterService(ServiceIdentification.CacheProvider, typeof(DefaultCacheProvider));
382 if (!services.HasService(ServiceIdentification.ExecutorFactory))
384 services.RegisterService(ServiceIdentification.ExecutorFactory, typeof(DefaultControllerLifecycleExecutorFactory));
386 if (!services.HasService(ServiceIdentification.TransformationFilterFactory))
388 services.RegisterService(ServiceIdentification.TransformationFilterFactory, typeof(DefaultTransformFilterFactory));
390 if (!services.HasService(ServiceIdentification.TransformFilterDescriptorProvider))
392 services.RegisterService(ServiceIdentification.TransformFilterDescriptorProvider,
393 typeof(DefaultTransformFilterDescriptorProvider));
396 if (!services.HasService(ServiceIdentification.UrlBuilder))
398 services.RegisterService(ServiceIdentification.UrlBuilder, typeof(DefaultUrlBuilder));
400 if (!services.HasService(ServiceIdentification.UrlTokenizer))
402 services.RegisterService(ServiceIdentification.UrlTokenizer, typeof(DefaultUrlTokenizer));
404 if (!services.HasService(ServiceIdentification.ValidatorRegistry))
406 services.RegisterService(ServiceIdentification.ValidatorRegistry, typeof(CachedValidationRegistry));
409 if (!services.HasService(ServiceIdentification.AjaxProxyGenerator))
411 services.RegisterService(ServiceIdentification.AjaxProxyGenerator, typeof(PrototypeAjaxProxyGenerator));
415 private object ActivateService(Type type)
419 return Activator.CreateInstance(type);
421 catch(Exception ex)
423 String message = String.Format("Initialization Exception: " +
424 "Could not instantiate {0}", type.FullName);
425 throw new ConfigurationErrorsException(message, ex);
429 private void InvokeInitialize(object instance)
431 IInitializable initializable = instance as IInitializable;
433 if (initializable != null)
435 initializable.Initialize();
438 ISupportInitialize suppInitialize = instance as ISupportInitialize;
440 if (suppInitialize != null)
442 suppInitialize.BeginInit();
443 suppInitialize.EndInit();
447 private void InvokeServiceEnabled(object instance)
449 IServiceEnabledComponent serviceEnabled = instance as IServiceEnabledComponent;
451 if (serviceEnabled != null)
453 serviceEnabled.Service(this);
457 private MonoRailConfiguration ObtainConfiguration()
459 if (config == null)
461 config = MonoRailConfiguration.GetConfig();
464 return config;
467 private void AssertImplementsService(Type service, Type impl)
469 if (!service.IsAssignableFrom(impl))
471 String message = String.Format("Initialization Exception: " +
472 "Service {0} does not implement or extend {1}", impl.FullName, service.FullName);
473 throw new ConfigurationErrorsException(message);