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
.Extensions
.ExceptionChaining
18 using System
.Configuration
;
21 using Castle
.MonoRail
.Framework
.Configuration
;
24 /// This extension allow one to perform one or more steps
25 /// in response to an exception threw by an action.
27 /// <seealso cref="IExceptionHandler"/>
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:
36 /// <extensions>
37 /// <extension type="Castle.MonoRail.Framework.Extensions.ExceptionChaining.ExceptionChainingExtension, Castle.MonoRail.Framework" />
38 /// </extensions>
41 /// <exceptionHandler type="Type name that implements IExceptionHandler" />
42 /// <exceptionHandler type="Type name that implements IExceptionHandler" />
43 /// </exception>
47 /// Controllers can request IExceptionProcessor through IServiceProvider
48 /// and invoke the handlers to process an exception
52 /// public void BuyMercedes()
58 /// catch(Exception ex)
60 /// IExceptionProcessor exProcessor = ServiceProvider.GetService<IExceptionProcessor>();
61 /// exProcessor.ProcessException(ex);
63 /// RenderView("CouldNotBuyMercedes");
69 public class ExceptionChainingExtension
: IMonoRailExtension
, IExceptionProcessor
71 private IExceptionHandler firstHandler
;
73 #region IMonoRailExtension implementation
76 /// Gives to the extension implementor a chance to read
77 /// attributes and child nodes of the extension node
79 /// <param name="node">The node that defines the MonoRail extension</param>
80 public void SetExtensionConfigNode(XmlNode node
)
87 #region IServiceEnabledComponent implementation
90 /// Services the specified provider.
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"];
112 // TODO: Throw configuration exception
115 InstallExceptionHandler(node
, typeAtt
.Value
);
118 manager
.ServiceContainer
.AddService(typeof(IExceptionProcessor
), this);
123 #region IExceptionProcessor implementation
126 /// Initiates the ExceptionChainingExtension manualy
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
);
142 /// Called when an exception happens.
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
))
154 if (firstHandler
!= null)
156 context
.Items
.Add(mrExceptionKey
, true);
158 firstHandler
.Process(context
);
163 /// Installs the exception handler.
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. " +
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
;
206 IExceptionHandler navHandler
= firstHandler
;
208 while(navHandler
!= null)
210 if (navHandler
.Next
== null)
212 navHandler
.Next
= handler
;
216 navHandler
= navHandler
.Next
;