Removed untyped contructor from ComponentRegistration and add a protected setter.
[castle.git] / MonoRail / Castle.MonoRail.Framework / Providers / DefaultControllerDescriptorProvider.cs
blob0aa3bc313a5791a6f7deeafca5b1ca70ff6a5da2
1 // Copyright 2004-2008 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.Providers
17 using System;
18 using System.Collections;
19 using System.Reflection;
20 using System.Threading;
21 using Castle.Core.Logging;
22 using Castle.MonoRail.Framework.Services.Utils;
23 using Descriptors;
24 using Providers;
26 /// <summary>
27 /// Constructs and caches all collected information
28 /// about a <see cref="IController"/> and its actions.
29 /// <seealso cref="ControllerMetaDescriptor"/>
30 /// </summary>
31 public class DefaultControllerDescriptorProvider : IControllerDescriptorProvider
33 /// <summary>
34 /// The logger instance
35 /// </summary>
36 private ILogger logger = NullLogger.Instance;
38 /// <summary>
39 /// Used to lock the cache
40 /// </summary>
41 private ReaderWriterLock locker = new ReaderWriterLock();
43 private Hashtable descriptorRepository = new Hashtable();
44 private IHelperDescriptorProvider helperDescriptorProvider;
45 private IFilterDescriptorProvider filterDescriptorProvider;
46 private ILayoutDescriptorProvider layoutDescriptorProvider;
47 private IRescueDescriptorProvider rescueDescriptorProvider;
48 private IResourceDescriptorProvider resourceDescriptorProvider;
49 private ITransformFilterDescriptorProvider transformFilterDescriptorProvider;
50 private IReturnBinderDescriptorProvider returnBinderDescriptorProvider;
52 /// <summary>
53 /// Initializes a new instance of the <see cref="DefaultControllerDescriptorProvider"/> class.
54 /// </summary>
55 public DefaultControllerDescriptorProvider()
59 /// <summary>
60 /// Initializes a new instance of the <see cref="DefaultControllerDescriptorProvider"/> class.
61 /// </summary>
62 /// <param name="helperDescriptorProvider">The helper descriptor provider.</param>
63 /// <param name="filterDescriptorProvider">The filter descriptor provider.</param>
64 /// <param name="layoutDescriptorProvider">The layout descriptor provider.</param>
65 /// <param name="rescueDescriptorProvider">The rescue descriptor provider.</param>
66 /// <param name="resourceDescriptorProvider">The resource descriptor provider.</param>
67 /// <param name="transformFilterDescriptorProvider">The transform filter descriptor provider.</param>
68 /// <param name="returnBinderDescriptorProvider">The return binder descriptor provider.</param>
69 public DefaultControllerDescriptorProvider(IHelperDescriptorProvider helperDescriptorProvider,
70 IFilterDescriptorProvider filterDescriptorProvider,
71 ILayoutDescriptorProvider layoutDescriptorProvider,
72 IRescueDescriptorProvider rescueDescriptorProvider,
73 IResourceDescriptorProvider resourceDescriptorProvider,
74 ITransformFilterDescriptorProvider transformFilterDescriptorProvider,
75 IReturnBinderDescriptorProvider returnBinderDescriptorProvider)
77 this.helperDescriptorProvider = helperDescriptorProvider;
78 this.filterDescriptorProvider = filterDescriptorProvider;
79 this.layoutDescriptorProvider = layoutDescriptorProvider;
80 this.rescueDescriptorProvider = rescueDescriptorProvider;
81 this.resourceDescriptorProvider = resourceDescriptorProvider;
82 this.transformFilterDescriptorProvider = transformFilterDescriptorProvider;
83 this.returnBinderDescriptorProvider = returnBinderDescriptorProvider;
86 #region IMRServiceEnabled implementation
88 /// <summary>
89 /// Services the specified service provider.
90 /// </summary>
91 /// <param name="serviceProvider">The service provider.</param>
92 public void Service(IMonoRailServices serviceProvider)
94 ILoggerFactory loggerFactory = (ILoggerFactory) serviceProvider.GetService(typeof(ILoggerFactory));
96 if (loggerFactory != null)
98 logger = loggerFactory.Create(typeof(DefaultControllerDescriptorProvider));
101 helperDescriptorProvider = serviceProvider.GetService<IHelperDescriptorProvider>();
102 filterDescriptorProvider = serviceProvider.GetService<IFilterDescriptorProvider>();
103 layoutDescriptorProvider = serviceProvider.GetService<ILayoutDescriptorProvider>();
104 rescueDescriptorProvider = serviceProvider.GetService<IRescueDescriptorProvider>();
105 resourceDescriptorProvider = serviceProvider.GetService<IResourceDescriptorProvider>();
106 transformFilterDescriptorProvider = serviceProvider.GetService<ITransformFilterDescriptorProvider>();
107 returnBinderDescriptorProvider = serviceProvider.GetService<IReturnBinderDescriptorProvider>();
110 #endregion
112 /// <summary>
113 /// Occurs when the providers needs to create a <see cref="ControllerMetaDescriptor"/>.
114 /// </summary>
115 public event MetaCreatorHandler Create;
117 /// <summary>
118 /// Occurs when the meta descriptor is about to the returned to the caller.
119 /// </summary>
120 public event ControllerMetaDescriptorHandler AfterProcess;
122 /// <summary>
123 /// Occurs when the providers needs to create a <see cref="ActionMetaDescriptor"/>.
124 /// </summary>
125 public event ActionMetaCreatorHandler ActionCreate;
127 /// <summary>
128 /// Occurs when the meta descriptor is about to be included on the <see cref="ControllerMetaDescriptor"/>.
129 /// </summary>
130 public event ActionMetaDescriptorHandler AfterActionProcess;
132 /// <summary>
133 /// Constructs and populates a <see cref="ControllerMetaDescriptor"/>.
134 /// </summary>
135 /// <remarks>
136 /// This implementation is also responsible for caching
137 /// constructed meta descriptors.
138 /// </remarks>
139 public ControllerMetaDescriptor BuildDescriptor(IController controller)
141 Type controllerType = controller.GetType();
143 return BuildDescriptor(controllerType);
146 /// <summary>
147 /// Constructs and populates a <see cref="ControllerMetaDescriptor"/>.
148 /// </summary>
149 /// <remarks>
150 /// This implementation is also responsible for caching
151 /// constructed meta descriptors.
152 /// </remarks>
153 public ControllerMetaDescriptor BuildDescriptor(Type controllerType)
155 ControllerMetaDescriptor desc;
157 locker.AcquireReaderLock(-1);
159 desc = (ControllerMetaDescriptor) descriptorRepository[controllerType];
161 if (desc != null)
163 locker.ReleaseReaderLock();
164 return desc;
169 locker.UpgradeToWriterLock(-1);
171 // We need to recheck after getting the writer lock
172 desc = (ControllerMetaDescriptor) descriptorRepository[controllerType];
174 if (desc != null)
176 return desc;
179 desc = InternalBuildDescriptor(controllerType);
181 descriptorRepository[controllerType] = desc;
183 finally
185 locker.ReleaseWriterLock();
188 return desc;
191 /// <summary>
192 /// Builds the <see cref="ControllerMetaDescriptor"/> for the specified controller type
193 /// </summary>
194 /// <param name="controllerType">Type of the controller.</param>
195 /// <returns></returns>
196 private ControllerMetaDescriptor InternalBuildDescriptor(Type controllerType)
198 if (logger.IsDebugEnabled)
200 logger.DebugFormat("Building controller descriptor for {0}", controllerType);
203 ControllerMetaDescriptor descriptor = CreateMetaDescriptor();
205 descriptor.ControllerDescriptor = ControllerInspectionUtil.Inspect(controllerType);
207 CollectClassLevelAttributes(controllerType, descriptor);
209 CollectActions(controllerType, descriptor);
211 CollectActionLevelAttributes(descriptor);
213 if (AfterProcess != null)
215 AfterProcess(descriptor);
218 return descriptor;
221 #region Action data
223 /// <summary>
224 /// Collects the actions.
225 /// </summary>
226 /// <param name="controllerType">Type of the controller.</param>
227 /// <param name="desc">The desc.</param>
228 private void CollectActions(Type controllerType, ControllerMetaDescriptor desc)
230 // HACK: GetRealControllerType is a workaround for DYNPROXY-14 bug
231 // see: http://support.castleproject.org/browse/DYNPROXY-14
232 controllerType = GetRealControllerType(controllerType);
234 MethodInfo[] methods = controllerType.GetMethods(BindingFlags.Public | BindingFlags.Instance);
236 foreach(MethodInfo method in methods)
238 Type declaringType = method.DeclaringType;
240 if (method.IsSpecialName)
242 continue;
245 if (declaringType == typeof(Object) ||
246 declaringType == typeof(IController) || declaringType == typeof(Controller))
247 // || declaringType == typeof(SmartDispatcherController))
249 continue;
252 if (desc.Actions.Contains(method.Name))
254 ArrayList list = desc.Actions[method.Name] as ArrayList;
256 if (list == null)
258 list = new ArrayList();
259 list.Add(desc.Actions[method.Name]);
261 desc.Actions[method.Name] = list;
264 list.Add(method);
266 else
268 desc.Actions[method.Name] = method;
273 /// <summary>
274 /// Collects the action level attributes.
275 /// </summary>
276 /// <param name="descriptor">The descriptor.</param>
277 private void CollectActionLevelAttributes(ControllerMetaDescriptor descriptor)
279 foreach(object action in descriptor.Actions.Values)
281 if (action is IList)
283 foreach(MethodInfo overloadedAction in (action as IList))
285 CollectActionAttributes(overloadedAction, descriptor);
288 continue;
291 CollectActionAttributes(action as MethodInfo, descriptor);
295 /// <summary>
296 /// Collects the action attributes.
297 /// </summary>
298 /// <param name="method">The method.</param>
299 /// <param name="descriptor">The descriptor.</param>
300 private void CollectActionAttributes(MethodInfo method, ControllerMetaDescriptor descriptor)
302 if (logger.IsDebugEnabled)
304 logger.DebugFormat("Collection attributes for action {0}", method.Name);
307 ActionMetaDescriptor actionDescriptor = descriptor.GetAction(method);
309 if (actionDescriptor == null)
311 actionDescriptor = CreateActionDescriptor();
313 descriptor.AddAction(method, actionDescriptor);
316 CollectResources(actionDescriptor, method);
317 CollectSkipFilter(actionDescriptor, method);
318 CollectRescues(actionDescriptor, method);
319 CollectAccessibleThrough(actionDescriptor, method);
320 CollectSkipRescue(actionDescriptor, method);
321 CollectLayout(actionDescriptor, method);
322 CollectCacheConfigure(actionDescriptor, method);
323 CollectTransformFilter(actionDescriptor, method);
324 CollectReturnTypeBinder(actionDescriptor, method);
326 if (method.IsDefined(typeof(AjaxActionAttribute), true))
328 descriptor.AjaxActions.Add(method);
331 if (method.IsDefined(typeof(DefaultActionAttribute), true))
333 if (descriptor.DefaultAction != null)
335 throw new MonoRailException(
336 "Cannot resolve a default action for {0}, DefaultActionAttribute was declared more than once.",
337 method.DeclaringType.FullName);
339 descriptor.DefaultAction = new DefaultActionAttribute(method.Name);
342 if (AfterActionProcess != null)
344 AfterActionProcess(actionDescriptor);
348 /// <summary>
349 /// Collects the skip rescue.
350 /// </summary>
351 /// <param name="actionDescriptor">The action descriptor.</param>
352 /// <param name="method">The method.</param>
353 private void CollectSkipRescue(ActionMetaDescriptor actionDescriptor, MethodInfo method)
355 object[] attributes = method.GetCustomAttributes(typeof(SkipRescueAttribute), true);
357 if (attributes.Length != 0)
359 actionDescriptor.SkipRescue = (SkipRescueAttribute) attributes[0];
363 /// <summary>
364 /// Collects the accessible through.
365 /// </summary>
366 /// <param name="actionDescriptor">The action descriptor.</param>
367 /// <param name="method">The method.</param>
368 private void CollectAccessibleThrough(ActionMetaDescriptor actionDescriptor, MethodInfo method)
370 object[] attributes = method.GetCustomAttributes(typeof(AccessibleThroughAttribute), true);
372 if (attributes.Length != 0)
374 actionDescriptor.AccessibleThrough = (AccessibleThroughAttribute) attributes[0];
378 /// <summary>
379 /// Collects the skip filter.
380 /// </summary>
381 /// <param name="actionDescriptor">The action descriptor.</param>
382 /// <param name="method">The method.</param>
383 private void CollectSkipFilter(ActionMetaDescriptor actionDescriptor, MethodInfo method)
385 object[] attributes = method.GetCustomAttributes(typeof(SkipFilterAttribute), true);
387 foreach(SkipFilterAttribute attr in attributes)
389 actionDescriptor.SkipFilters.Add(attr);
393 /// <summary>
394 /// Collects the resources.
395 /// </summary>
396 /// <param name="desc">The desc.</param>
397 /// <param name="memberInfo">The member info.</param>
398 private void CollectResources(BaseMetaDescriptor desc, MemberInfo memberInfo)
400 desc.Resources = resourceDescriptorProvider.CollectResources(memberInfo);
403 /// <summary>
404 /// Collects the transform filter.
405 /// </summary>
406 /// <param name="actionDescriptor">The action descriptor.</param>
407 /// <param name="method">The method.</param>
408 private void CollectTransformFilter(ActionMetaDescriptor actionDescriptor, MethodInfo method)
410 actionDescriptor.TransformFilters = transformFilterDescriptorProvider.CollectFilters((method));
411 Array.Sort(actionDescriptor.TransformFilters, TransformFilterDescriptorComparer.Instance);
414 /// <summary>
415 /// Collects the return type binder.
416 /// </summary>
417 /// <param name="actionDescriptor">The action descriptor.</param>
418 /// <param name="method">The method.</param>
419 private void CollectReturnTypeBinder(ActionMetaDescriptor actionDescriptor, MethodInfo method)
421 actionDescriptor.ReturnDescriptor = returnBinderDescriptorProvider.Collect(method);
424 /// <summary>
425 /// Gets the real controller type, instead of the proxy type.
426 /// </summary>
427 /// <remarks>
428 /// Workaround for DYNPROXY-14 bug. See: http://support.castleproject.org/browse/DYNPROXY-14
429 /// </remarks>
430 private Type GetRealControllerType(Type controllerType)
432 Type prev = controllerType;
434 // try to get the first non-proxy type
435 while(controllerType.Assembly.FullName.StartsWith("DynamicProxyGenAssembly2") ||
436 controllerType.Assembly.FullName.StartsWith("DynamicAssemblyProxyGen"))
438 controllerType = controllerType.BaseType;
440 if ( // controllerType == typeof(SmartDispatcherController) ||
441 controllerType == typeof(IController))
443 // oops, it's a pure-proxy controller. just let it go.
444 controllerType = prev;
445 break;
449 return controllerType;
452 #endregion
454 #region Controller data
456 /// <summary>
457 /// Collects the class level attributes.
458 /// </summary>
459 /// <param name="controllerType">Type of the controller.</param>
460 /// <param name="descriptor">The descriptor.</param>
461 private void CollectClassLevelAttributes(Type controllerType, ControllerMetaDescriptor descriptor)
463 CollectHelpers(descriptor, controllerType);
464 CollectResources(descriptor, controllerType);
465 CollectFilters(descriptor, controllerType);
466 CollectLayout(descriptor, controllerType);
467 CollectRescues(descriptor, controllerType);
468 CollectDefaultAction(descriptor, controllerType);
469 CollectScaffolding(descriptor, controllerType);
470 CollectDynamicAction(descriptor, controllerType);
471 CollectCacheConfigure(descriptor, controllerType);
474 /// <summary>
475 /// Collects the default action.
476 /// </summary>
477 /// <param name="descriptor">The descriptor.</param>
478 /// <param name="controllerType">Type of the controller.</param>
479 private void CollectDefaultAction(ControllerMetaDescriptor descriptor, Type controllerType)
481 object[] attributes = controllerType.GetCustomAttributes(typeof(DefaultActionAttribute), true);
483 if (attributes.Length != 0)
485 descriptor.DefaultAction = (DefaultActionAttribute) attributes[0];
489 /// <summary>
490 /// Collects the scaffolding.
491 /// </summary>
492 /// <param name="descriptor">The descriptor.</param>
493 /// <param name="controllerType">Type of the controller.</param>
494 private void CollectScaffolding(ControllerMetaDescriptor descriptor, Type controllerType)
496 object[] attributes = controllerType.GetCustomAttributes(typeof(ScaffoldingAttribute), false);
498 if (attributes.Length != 0)
500 foreach(ScaffoldingAttribute scaffolding in attributes)
502 descriptor.Scaffoldings.Add(scaffolding);
507 /// <summary>
508 /// Collects the dynamic action.
509 /// </summary>
510 /// <param name="descriptor">The descriptor.</param>
511 /// <param name="controllerType">Type of the controller.</param>
512 private void CollectDynamicAction(ControllerMetaDescriptor descriptor, Type controllerType)
514 object[] attributes = controllerType.GetCustomAttributes(typeof(DynamicActionProviderAttribute), true);
516 if (attributes.Length != 0)
518 foreach(DynamicActionProviderAttribute attr in attributes)
520 descriptor.ActionProviders.Add(attr.ProviderType);
525 /// <summary>
526 /// Collects the helpers.
527 /// </summary>
528 /// <param name="descriptor">The descriptor.</param>
529 /// <param name="controllerType">Type of the controller.</param>
530 private void CollectHelpers(ControllerMetaDescriptor descriptor, Type controllerType)
532 descriptor.Helpers = helperDescriptorProvider.CollectHelpers(controllerType);
535 /// <summary>
536 /// Collects the filters.
537 /// </summary>
538 /// <param name="descriptor">The descriptor.</param>
539 /// <param name="controllerType">Type of the controller.</param>
540 private void CollectFilters(ControllerMetaDescriptor descriptor, Type controllerType)
542 descriptor.Filters = filterDescriptorProvider.CollectFilters(controllerType);
544 Array.Sort(descriptor.Filters, FilterDescriptorComparer.Instance);
547 /// <summary>
548 /// Collects the layout.
549 /// </summary>
550 /// <param name="descriptor">The descriptor.</param>
551 /// <param name="memberInfo">The member info.</param>
552 private void CollectLayout(BaseMetaDescriptor descriptor, MemberInfo memberInfo)
554 descriptor.Layout = layoutDescriptorProvider.CollectLayout(memberInfo);
557 /// <summary>
558 /// Collects the rescues.
559 /// </summary>
560 /// <param name="descriptor">The descriptor.</param>
561 /// <param name="memberInfo">The member info.</param>
562 private void CollectRescues(BaseMetaDescriptor descriptor, MethodInfo memberInfo)
564 descriptor.Rescues = rescueDescriptorProvider.CollectRescues(memberInfo);
567 /// <summary>
568 /// Collects the rescues.
569 /// </summary>
570 /// <param name="descriptor">The descriptor.</param>
571 /// <param name="type">The type.</param>
572 private void CollectRescues(BaseMetaDescriptor descriptor, Type type)
574 descriptor.Rescues = rescueDescriptorProvider.CollectRescues(type);
577 /// <summary>
578 /// Collects the cache configures.
579 /// </summary>
580 /// <param name="descriptor">The descriptor.</param>
581 /// <param name="memberInfo">The member info.</param>
582 private void CollectCacheConfigure(BaseMetaDescriptor descriptor, MemberInfo memberInfo)
584 object[] configurers = memberInfo.GetCustomAttributes(typeof(ICachePolicyConfigurer), true);
586 if (configurers.Length != 0)
588 descriptor.CacheConfigurer = (ICachePolicyConfigurer) configurers[0];
592 #endregion
594 private ControllerMetaDescriptor CreateMetaDescriptor()
596 if (Create != null)
598 return Create();
600 else
602 return new ControllerMetaDescriptor();
606 private ActionMetaDescriptor CreateActionDescriptor()
608 if (ActionCreate != null)
610 return ActionCreate();
612 else
614 return new ActionMetaDescriptor();
618 /// <summary>
619 /// This <see cref="IComparer"/> implementation
620 /// is used to sort the filters based on their Execution Order.
621 /// </summary>
622 private class FilterDescriptorComparer : IComparer
624 private static readonly FilterDescriptorComparer instance = new FilterDescriptorComparer();
626 /// <summary>
627 /// Initializes a new instance of the <see cref="FilterDescriptorComparer"/> class.
628 /// </summary>
629 private FilterDescriptorComparer()
633 /// <summary>
634 /// Gets the instance.
635 /// </summary>
636 /// <value>The instance.</value>
637 public static FilterDescriptorComparer Instance
639 get { return instance; }
642 /// <summary>
643 /// Compares the specified left.
644 /// </summary>
645 /// <param name="left">The left.</param>
646 /// <param name="right">The right.</param>
647 /// <returns></returns>
648 public int Compare(object left, object right)
650 return ((FilterDescriptor) left).ExecutionOrder - ((FilterDescriptor) right).ExecutionOrder;
654 /// <summary>
655 /// This <see cref="IComparer"/> implementation
656 /// is used to sort the transformfilters based on their Execution Order.
657 /// </summary>
658 private class TransformFilterDescriptorComparer : IComparer
660 private static readonly TransformFilterDescriptorComparer instance = new TransformFilterDescriptorComparer();
662 /// <summary>
663 /// Initializes a new instance of the <see cref="TransformFilterDescriptorComparer"/> class.
664 /// </summary>
665 private TransformFilterDescriptorComparer()
669 /// <summary>
670 /// Gets the instance.
671 /// </summary>
672 /// <value>The instance.</value>
673 public static TransformFilterDescriptorComparer Instance
675 get { return instance; }
678 /// <summary>
679 /// Compares the specified left.
680 /// </summary>
681 /// <param name="left">The left.</param>
682 /// <param name="right">The right.</param>
683 /// <returns></returns>
684 public int Compare(object left, object right)
686 return ((TransformFilterDescriptor) right).ExecutionOrder - ((TransformFilterDescriptor) left).ExecutionOrder;