1 // Copyright 2004-2008 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
;
19 using Castle
.Core
.Configuration
;
20 using Castle
.MonoRail
.Framework
.Configuration
;
23 /// This extension allow one to perform one or more steps
24 /// in response to an exception threw by an action.
26 /// <seealso cref="IExceptionHandler"/>
31 /// To successfully install this extension you must register
32 /// it on the <c>extensions</c> node and the handlers within the <c>exception</c> node:
35 /// <extensions>
36 /// <extension type="Castle.MonoRail.Framework.Extensions.ExceptionChaining.ExceptionChainingExtension, Castle.MonoRail.Framework" />
37 /// </extensions>
40 /// <exceptionHandler type="Type name that implements IExceptionHandler" />
41 /// <exceptionHandler type="Type name that implements IExceptionHandler" />
42 /// </exception>
46 /// Controllers can request IExceptionProcessor through IServiceProvider
47 /// and invoke the handlers to process an exception
51 /// public void BuyMercedes()
57 /// catch(Exception ex)
59 /// IExceptionProcessor exProcessor = ServiceProvider.GetService<IExceptionProcessor>();
60 /// exProcessor.ProcessException(ex);
62 /// RenderView("CouldNotBuyMercedes");
68 public class ExceptionChainingExtension
: IMonoRailExtension
, IExceptionProcessor
70 private IExceptionHandler firstHandler
;
72 #region IMonoRailExtension implementation
75 /// Gives to the extension implementor a chance to read
76 /// attributes and child nodes of the extension node
78 /// <param name="node">The node that defines the MonoRail extension</param>
79 public void SetExtensionConfigNode(IConfiguration node
)
86 #region IServiceEnabledComponent implementation
89 /// Services the specified provider.
91 /// <param name="serviceProvider">The provider.</param>
92 public void Service(IMonoRailServices serviceProvider
)
94 ExtensionManager manager
= (ExtensionManager
)
95 serviceProvider
.GetService(typeof(ExtensionManager
));
97 IMonoRailConfiguration config
= (IMonoRailConfiguration
)
98 serviceProvider
.GetService(typeof(IMonoRailConfiguration
));
100 manager
.ActionException
+= OnException
;
101 manager
.UnhandledException
+= OnException
;
103 IConfiguration exceptionNode
= config
.ConfigurationSection
.Children
["exception"];
105 foreach(IConfiguration node
in exceptionNode
.Children
)
107 string typeAtt
= node
.Attributes
["type"];
111 throw new MonoRailException("Configuration error: missing type attribute on exception handler configuration.");
114 InstallExceptionHandler(node
, typeAtt
);
120 #region IExceptionProcessor implementation
123 /// Initiates the ExceptionChainingExtension manualy
125 /// <param name="exception">The exception to process</param>
126 /// <param name="engineContext">The engine context.</param>
127 public void ProcessException(Exception exception
, IEngineContext engineContext
)
129 if (exception
== null) return;
131 engineContext
.LastException
= exception
;
133 OnException(engineContext
);
139 /// Called when an exception happens.
141 /// <param name="context">The context.</param>
142 private void OnException(IEngineContext context
)
144 const String mrExceptionKey
= "MonoRail.ExceptionHandled";
146 if (context
.Items
.Contains(mrExceptionKey
))
151 if (firstHandler
!= null)
153 context
.Items
.Add(mrExceptionKey
, true);
155 firstHandler
.Process(context
);
160 /// Installs the exception handler.
162 /// <param name="node">The node.</param>
163 /// <param name="typeName">Name of the type.</param>
164 private void InstallExceptionHandler(IConfiguration node
, String typeName
)
166 IExceptionHandler handler
;
168 Type handlerType
= TypeLoadUtil
.GetType(typeName
);
170 if (handlerType
== null)
172 String message
= "The Type for the custom session could not be loaded. " +
174 throw new ConfigurationErrorsException(message
);
179 handler
= (IExceptionHandler
) Activator
.CreateInstance(handlerType
);
181 catch(InvalidCastException
)
183 String message
= "The Type for the custom session must " +
184 "implement ICustomSessionFactory. " + typeName
;
185 throw new ConfigurationErrorsException(message
);
188 IConfigurableHandler configurableHandler
= handler
as IConfigurableHandler
;
190 if (configurableHandler
!= null)
192 configurableHandler
.Configure(node
);
195 handler
.Initialize();
197 if (firstHandler
== null)
199 firstHandler
= handler
;
203 IExceptionHandler navHandler
= firstHandler
;
205 while(navHandler
!= null)
207 if (navHandler
.Next
== null)
209 navHandler
.Next
= handler
;
213 navHandler
= navHandler
.Next
;