Minor changes to improve testability of helpers
[castle.git] / MonoRail / Castle.MonoRail.Framework / Extensions / ExceptionChaining / ExceptionChainingExtension.cs
blob691dc0b859de3d9693e9cbc8088e5f0148fcd3f3
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.Extensions.ExceptionChaining
17 using System;
18 using System.Configuration;
19 using System.Xml;
21 using Castle.MonoRail.Framework.Configuration;
23 /// <summary>
24 /// This extension allow one to perform one or more steps
25 /// in response to an exception threw by an action.
26 ///
27 /// <seealso cref="IExceptionHandler"/>
28 ///
29 /// </summary>
30 ///
31 /// <remarks>
32 /// To successfully install this extension you must register
33 /// it on the <c>extensions</c> node and the handlers within the <c>exception</c> node:
34 /// <code>
35 /// &lt;monorail&gt;
36 /// &lt;extensions&gt;
37 /// &lt;extension type="Castle.MonoRail.Framework.Extensions.ExceptionChaining.ExceptionChainingExtension, Castle.MonoRail.Framework" /&gt;
38 /// &lt;/extensions&gt;
39 ///
40 /// &lt;exception&gt;
41 /// &lt;exceptionHandler type="Type name that implements IExceptionHandler" /&gt;
42 /// &lt;exceptionHandler type="Type name that implements IExceptionHandler" /&gt;
43 /// &lt;/exception&gt;
44 /// &lt;/monorail&gt;
45 /// </code>
46 /// <para>
47 /// Controllers can request IExceptionProcessor through IServiceProvider
48 /// and invoke the handlers to process an exception
49 /// </para>
50 /// <code>
51 /// <![CDATA[
52 /// public void BuyMercedes()
53 /// {
54 /// try
55 /// {
56 /// ...
57 /// }
58 /// catch(Exception ex)
59 /// {
60 /// IExceptionProcessor exProcessor = ServiceProvider.GetService<IExceptionProcessor>();
61 /// exProcessor.ProcessException(ex);
62 ///
63 /// RenderView("CouldNotBuyMercedes");
64 /// }
65 /// }
66 /// ]]>
67 /// </code>
68 /// </remarks>
69 public class ExceptionChainingExtension : IMonoRailExtension, IExceptionProcessor
71 private IExceptionHandler firstHandler;
73 #region IMonoRailExtension implementation
75 /// <summary>
76 /// Gives to the extension implementor a chance to read
77 /// attributes and child nodes of the extension node
78 /// </summary>
79 /// <param name="node">The node that defines the MonoRail extension</param>
80 public void SetExtensionConfigNode(XmlNode node)
82 // Ignored
85 #endregion
87 #region IServiceEnabledComponent implementation
89 /// <summary>
90 /// Services the specified provider.
91 /// </summary>
92 /// <param name="provider">The provider.</param>
93 public void Service(IServiceProvider provider)
95 ExtensionManager manager = (ExtensionManager)
96 provider.GetService(typeof(ExtensionManager));
98 MonoRailConfiguration config = (MonoRailConfiguration)
99 provider.GetService(typeof(MonoRailConfiguration));
101 manager.ActionException += new ExtensionHandler(OnException);
102 manager.UnhandledException += new ExtensionHandler(OnException);
104 XmlNodeList handlers = config.ConfigurationSection.SelectNodes("exception/exceptionHandler");
106 foreach(XmlNode node in handlers)
108 XmlAttribute typeAtt = node.Attributes["type"];
110 if (typeAtt == null)
112 // TODO: Throw configuration exception
115 InstallExceptionHandler(node, typeAtt.Value);
118 manager.ServiceContainer.AddService(typeof(IExceptionProcessor), this);
121 #endregion
123 #region IExceptionProcessor implementation
125 /// <summary>
126 /// Initiates the ExceptionChainingExtension manualy
127 /// </summary>
128 /// <param name="exception">The exception to process</param>
129 public void ProcessException(Exception exception)
131 if (exception == null) return;
133 IRailsEngineContext context = MonoRailHttpHandler.CurrentContext;
134 context.LastException = exception;
136 OnException(context);
139 #endregion
141 /// <summary>
142 /// Called when an exception happens.
143 /// </summary>
144 /// <param name="context">The context.</param>
145 private void OnException(IRailsEngineContext context)
147 const String mrExceptionKey = "MonoRail.ExceptionHandled";
149 if (context.Items.Contains(mrExceptionKey))
151 return;
154 if (firstHandler != null)
156 context.Items.Add(mrExceptionKey, true);
158 firstHandler.Process(context);
162 /// <summary>
163 /// Installs the exception handler.
164 /// </summary>
165 /// <param name="node">The node.</param>
166 /// <param name="typeName">Name of the type.</param>
167 private void InstallExceptionHandler(XmlNode node, String typeName)
169 IExceptionHandler handler;
171 Type handlerType = TypeLoadUtil.GetType(typeName);
173 if (handlerType == null)
175 String message = "The Type for the custom session could not be loaded. " +
176 typeName;
177 throw new ConfigurationErrorsException(message);
182 handler = (IExceptionHandler) Activator.CreateInstance(handlerType);
184 catch(InvalidCastException)
186 String message = "The Type for the custom session must " +
187 "implement ICustomSessionFactory. " + typeName;
188 throw new ConfigurationErrorsException(message);
191 IConfigurableHandler configurableHandler = handler as IConfigurableHandler;
193 if (configurableHandler != null)
195 configurableHandler.Configure(node);
198 handler.Initialize();
200 if (firstHandler == null)
202 firstHandler = handler;
204 else
206 IExceptionHandler navHandler = firstHandler;
208 while(navHandler != null)
210 if (navHandler.Next == null)
212 navHandler.Next = handler;
213 break;
216 navHandler = navHandler.Next;