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
.Providers
18 using System
.Collections
;
19 using System
.Reflection
;
20 using System
.Threading
;
21 using Castle
.Core
.Logging
;
22 using Castle
.MonoRail
.Framework
.Services
.Utils
;
27 /// Constructs and caches all collected information
28 /// about a <see cref="IController"/> and its actions.
29 /// <seealso cref="ControllerMetaDescriptor"/>
31 public class DefaultControllerDescriptorProvider
: IControllerDescriptorProvider
34 /// The logger instance
36 private ILogger logger
= NullLogger
.Instance
;
39 /// Used to lock the cache
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
;
53 /// Initializes a new instance of the <see cref="DefaultControllerDescriptorProvider"/> class.
55 public DefaultControllerDescriptorProvider()
60 /// Initializes a new instance of the <see cref="DefaultControllerDescriptorProvider"/> class.
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
89 /// Services the specified service provider.
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
>();
113 /// Occurs when the providers needs to create a <see cref="ControllerMetaDescriptor"/>.
115 public event MetaCreatorHandler Create
;
118 /// Occurs when the meta descriptor is about to the returned to the caller.
120 public event ControllerMetaDescriptorHandler AfterProcess
;
123 /// Occurs when the providers needs to create a <see cref="ActionMetaDescriptor"/>.
125 public event ActionMetaCreatorHandler ActionCreate
;
128 /// Occurs when the meta descriptor is about to be included on the <see cref="ControllerMetaDescriptor"/>.
130 public event ActionMetaDescriptorHandler AfterActionProcess
;
133 /// Constructs and populates a <see cref="ControllerMetaDescriptor"/>.
136 /// This implementation is also responsible for caching
137 /// constructed meta descriptors.
139 public ControllerMetaDescriptor
BuildDescriptor(IController controller
)
141 Type controllerType
= controller
.GetType();
143 return BuildDescriptor(controllerType
);
147 /// Constructs and populates a <see cref="ControllerMetaDescriptor"/>.
150 /// This implementation is also responsible for caching
151 /// constructed meta descriptors.
153 public ControllerMetaDescriptor
BuildDescriptor(Type controllerType
)
155 ControllerMetaDescriptor desc
;
157 locker
.AcquireReaderLock(-1);
159 desc
= (ControllerMetaDescriptor
) descriptorRepository
[controllerType
];
163 locker
.ReleaseReaderLock();
169 locker
.UpgradeToWriterLock(-1);
171 // We need to recheck after getting the writer lock
172 desc
= (ControllerMetaDescriptor
) descriptorRepository
[controllerType
];
179 desc
= InternalBuildDescriptor(controllerType
);
181 descriptorRepository
[controllerType
] = desc
;
185 locker
.ReleaseWriterLock();
192 /// Builds the <see cref="ControllerMetaDescriptor"/> for the specified controller type
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
);
224 /// Collects the actions.
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
)
245 if (declaringType
== typeof(Object
) ||
246 declaringType
== typeof(IController
) || declaringType
== typeof(Controller
))
247 // || declaringType == typeof(SmartDispatcherController))
252 if (desc
.Actions
.Contains(method
.Name
))
254 ArrayList list
= desc
.Actions
[method
.Name
] as ArrayList
;
258 list
= new ArrayList();
259 list
.Add(desc
.Actions
[method
.Name
]);
261 desc
.Actions
[method
.Name
] = list
;
268 desc
.Actions
[method
.Name
] = method
;
274 /// Collects the action level attributes.
276 /// <param name="descriptor">The descriptor.</param>
277 private void CollectActionLevelAttributes(ControllerMetaDescriptor descriptor
)
279 foreach(object action
in descriptor
.Actions
.Values
)
283 foreach(MethodInfo overloadedAction
in (action
as IList
))
285 CollectActionAttributes(overloadedAction
, descriptor
);
291 CollectActionAttributes(action
as MethodInfo
, descriptor
);
296 /// Collects the action attributes.
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
);
349 /// Collects the skip rescue.
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];
364 /// Collects the accessible through.
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];
379 /// Collects the skip filter.
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
);
394 /// Collects the resources.
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
);
404 /// Collects the transform filter.
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
);
415 /// Collects the return type binder.
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
);
425 /// Gets the real controller type, instead of the proxy type.
428 /// Workaround for DYNPROXY-14 bug. See: http://support.castleproject.org/browse/DYNPROXY-14
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
;
449 return controllerType
;
454 #region Controller data
457 /// Collects the class level attributes.
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
);
475 /// Collects the default action.
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];
490 /// Collects the scaffolding.
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
);
508 /// Collects the dynamic action.
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
);
526 /// Collects the helpers.
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
);
536 /// Collects the filters.
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
);
548 /// Collects the layout.
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
);
558 /// Collects the rescues.
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
);
568 /// Collects the rescues.
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
);
578 /// Collects the cache configures.
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];
594 private ControllerMetaDescriptor
CreateMetaDescriptor()
602 return new ControllerMetaDescriptor();
606 private ActionMetaDescriptor
CreateActionDescriptor()
608 if (ActionCreate
!= null)
610 return ActionCreate();
614 return new ActionMetaDescriptor();
619 /// This <see cref="IComparer"/> implementation
620 /// is used to sort the filters based on their Execution Order.
622 private class FilterDescriptorComparer
: IComparer
624 private static readonly FilterDescriptorComparer instance
= new FilterDescriptorComparer();
627 /// Initializes a new instance of the <see cref="FilterDescriptorComparer"/> class.
629 private FilterDescriptorComparer()
634 /// Gets the instance.
636 /// <value>The instance.</value>
637 public static FilterDescriptorComparer Instance
639 get { return instance; }
643 /// Compares the specified left.
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
;
655 /// This <see cref="IComparer"/> implementation
656 /// is used to sort the transformfilters based on their Execution Order.
658 private class TransformFilterDescriptorComparer
: IComparer
660 private static readonly TransformFilterDescriptorComparer instance
= new TransformFilterDescriptorComparer();
663 /// Initializes a new instance of the <see cref="TransformFilterDescriptorComparer"/> class.
665 private TransformFilterDescriptorComparer()
670 /// Gets the instance.
672 /// <value>The instance.</value>
673 public static TransformFilterDescriptorComparer Instance
675 get { return instance; }
679 /// Compares the specified left.
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
;