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
;
52 /// Initializes a new instance of the <see cref="DefaultControllerDescriptorProvider"/> class.
54 public DefaultControllerDescriptorProvider()
59 /// Initializes a new instance of the <see cref="DefaultControllerDescriptorProvider"/> class.
61 /// <param name="helperDescriptorProvider">The helper descriptor provider.</param>
62 /// <param name="filterDescriptorProvider">The filter descriptor provider.</param>
63 /// <param name="layoutDescriptorProvider">The layout descriptor provider.</param>
64 /// <param name="rescueDescriptorProvider">The rescue descriptor provider.</param>
65 /// <param name="resourceDescriptorProvider">The resource descriptor provider.</param>
66 /// <param name="transformFilterDescriptorProvider">The transform filter descriptor provider.</param>
67 public DefaultControllerDescriptorProvider(IHelperDescriptorProvider helperDescriptorProvider
,
68 IFilterDescriptorProvider filterDescriptorProvider
,
69 ILayoutDescriptorProvider layoutDescriptorProvider
,
70 IRescueDescriptorProvider rescueDescriptorProvider
,
71 IResourceDescriptorProvider resourceDescriptorProvider
,
72 ITransformFilterDescriptorProvider transformFilterDescriptorProvider
)
74 this.helperDescriptorProvider
= helperDescriptorProvider
;
75 this.filterDescriptorProvider
= filterDescriptorProvider
;
76 this.layoutDescriptorProvider
= layoutDescriptorProvider
;
77 this.rescueDescriptorProvider
= rescueDescriptorProvider
;
78 this.resourceDescriptorProvider
= resourceDescriptorProvider
;
79 this.transformFilterDescriptorProvider
= transformFilterDescriptorProvider
;
82 #region IMRServiceEnabled implementation
85 /// Services the specified service provider.
87 /// <param name="serviceProvider">The service provider.</param>
88 public void Service(IMonoRailServices serviceProvider
)
90 ILoggerFactory loggerFactory
= (ILoggerFactory
) serviceProvider
.GetService(typeof(ILoggerFactory
));
92 if (loggerFactory
!= null)
94 logger
= loggerFactory
.Create(typeof(DefaultControllerDescriptorProvider
));
97 helperDescriptorProvider
= serviceProvider
.GetService
<IHelperDescriptorProvider
>();
98 filterDescriptorProvider
= serviceProvider
.GetService
<IFilterDescriptorProvider
>();
99 layoutDescriptorProvider
= serviceProvider
.GetService
<ILayoutDescriptorProvider
>();
100 rescueDescriptorProvider
= serviceProvider
.GetService
<IRescueDescriptorProvider
>();
101 resourceDescriptorProvider
= serviceProvider
.GetService
<IResourceDescriptorProvider
>();
102 transformFilterDescriptorProvider
= serviceProvider
.GetService
<ITransformFilterDescriptorProvider
>();
108 /// Occurs when the providers needs to create a <see cref="ControllerMetaDescriptor"/>.
110 public event MetaCreatorHandler Create
;
113 /// Occurs when the meta descriptor is about to the returned to the caller.
115 public event ControllerMetaDescriptorHandler AfterProcess
;
118 /// Occurs when the providers needs to create a <see cref="ActionMetaDescriptor"/>.
120 public event ActionMetaCreatorHandler ActionCreate
;
123 /// Occurs when the meta descriptor is about to be included on the <see cref="ControllerMetaDescriptor"/>.
125 public event ActionMetaDescriptorHandler AfterActionProcess
;
128 /// Constructs and populates a <see cref="ControllerMetaDescriptor"/>.
131 /// This implementation is also responsible for caching
132 /// constructed meta descriptors.
134 public ControllerMetaDescriptor
BuildDescriptor(IController controller
)
136 Type controllerType
= controller
.GetType();
138 return BuildDescriptor(controllerType
);
142 /// Constructs and populates a <see cref="ControllerMetaDescriptor"/>.
145 /// This implementation is also responsible for caching
146 /// constructed meta descriptors.
148 public ControllerMetaDescriptor
BuildDescriptor(Type controllerType
)
150 ControllerMetaDescriptor desc
;
152 locker
.AcquireReaderLock(-1);
154 desc
= (ControllerMetaDescriptor
) descriptorRepository
[controllerType
];
158 locker
.ReleaseReaderLock();
164 locker
.UpgradeToWriterLock(-1);
166 // We need to recheck after getting the writer lock
167 desc
= (ControllerMetaDescriptor
) descriptorRepository
[controllerType
];
174 desc
= InternalBuildDescriptor(controllerType
);
176 descriptorRepository
[controllerType
] = desc
;
180 locker
.ReleaseWriterLock();
187 /// Builds the <see cref="ControllerMetaDescriptor"/> for the specified controller type
189 /// <param name="controllerType">Type of the controller.</param>
190 /// <returns></returns>
191 private ControllerMetaDescriptor
InternalBuildDescriptor(Type controllerType
)
193 if (logger
.IsDebugEnabled
)
195 logger
.DebugFormat("Building controller descriptor for {0}", controllerType
);
198 ControllerMetaDescriptor descriptor
= CreateMetaDescriptor();
200 descriptor
.ControllerDescriptor
= ControllerInspectionUtil
.Inspect(controllerType
);
202 CollectClassLevelAttributes(controllerType
, descriptor
);
204 CollectActions(controllerType
, descriptor
);
206 CollectActionLevelAttributes(descriptor
);
208 if (AfterProcess
!= null)
210 AfterProcess(descriptor
);
219 /// Collects the actions.
221 /// <param name="controllerType">Type of the controller.</param>
222 /// <param name="desc">The desc.</param>
223 private void CollectActions(Type controllerType
, ControllerMetaDescriptor desc
)
225 // HACK: GetRealControllerType is a workaround for DYNPROXY-14 bug
226 // see: http://support.castleproject.org/browse/DYNPROXY-14
227 controllerType
= GetRealControllerType(controllerType
);
229 MethodInfo
[] methods
= controllerType
.GetMethods(BindingFlags
.Public
| BindingFlags
.Instance
);
231 foreach(MethodInfo method
in methods
)
233 Type declaringType
= method
.DeclaringType
;
235 if (method
.IsSpecialName
) continue;
237 if (declaringType
== typeof(Object
) ||
238 declaringType
== typeof(IController
) || declaringType
== typeof(Controller
))
239 // || declaringType == typeof(SmartDispatcherController))
244 if (desc
.Actions
.Contains(method
.Name
))
246 ArrayList list
= desc
.Actions
[method
.Name
] as ArrayList
;
250 list
= new ArrayList();
251 list
.Add(desc
.Actions
[method
.Name
]);
253 desc
.Actions
[method
.Name
] = list
;
260 desc
.Actions
[method
.Name
] = method
;
266 /// Collects the action level attributes.
268 /// <param name="descriptor">The descriptor.</param>
269 private void CollectActionLevelAttributes(ControllerMetaDescriptor descriptor
)
271 foreach(object action
in descriptor
.Actions
.Values
)
275 foreach(MethodInfo overloadedAction
in (action
as IList
))
277 CollectActionAttributes(overloadedAction
, descriptor
);
283 CollectActionAttributes(action
as MethodInfo
, descriptor
);
288 /// Collects the action attributes.
290 /// <param name="method">The method.</param>
291 /// <param name="descriptor">The descriptor.</param>
292 private void CollectActionAttributes(MethodInfo method
, ControllerMetaDescriptor descriptor
)
294 if (logger
.IsDebugEnabled
)
296 logger
.DebugFormat("Collection attributes for action {0}", method
.Name
);
299 ActionMetaDescriptor actionDescriptor
= descriptor
.GetAction(method
);
301 if (actionDescriptor
== null)
303 actionDescriptor
= CreateActionDescriptor();
305 descriptor
.AddAction(method
, actionDescriptor
);
308 CollectResources(actionDescriptor
, method
);
309 CollectSkipFilter(actionDescriptor
, method
);
310 CollectRescues(actionDescriptor
, method
);
311 CollectAccessibleThrough(actionDescriptor
, method
);
312 CollectSkipRescue(actionDescriptor
, method
);
313 CollectLayout(actionDescriptor
, method
);
314 CollectCacheConfigure(actionDescriptor
, method
);
315 CollectTransformFilter(actionDescriptor
, method
);
317 if (method
.IsDefined(typeof(AjaxActionAttribute
), true))
319 descriptor
.AjaxActions
.Add(method
);
322 if (method
.IsDefined(typeof(DefaultActionAttribute
), true))
324 if (descriptor
.DefaultAction
!= null)
326 throw new MonoRailException(
327 "Cannot resolve a default action for {0}, DefaultActionAttribute was declared more than once.",
328 method
.DeclaringType
.FullName
);
330 descriptor
.DefaultAction
= new DefaultActionAttribute(method
.Name
);
333 if (AfterActionProcess
!= null)
335 AfterActionProcess(actionDescriptor
);
340 /// Collects the skip rescue.
342 /// <param name="actionDescriptor">The action descriptor.</param>
343 /// <param name="method">The method.</param>
344 private void CollectSkipRescue(ActionMetaDescriptor actionDescriptor
, MethodInfo method
)
346 object[] attributes
= method
.GetCustomAttributes(typeof(SkipRescueAttribute
), true);
348 if (attributes
.Length
!= 0)
350 actionDescriptor
.SkipRescue
= (SkipRescueAttribute
) attributes
[0];
355 /// Collects the accessible through.
357 /// <param name="actionDescriptor">The action descriptor.</param>
358 /// <param name="method">The method.</param>
359 private void CollectAccessibleThrough(ActionMetaDescriptor actionDescriptor
, MethodInfo method
)
361 object[] attributes
= method
.GetCustomAttributes(typeof(AccessibleThroughAttribute
), true);
363 if (attributes
.Length
!= 0)
365 actionDescriptor
.AccessibleThrough
= (AccessibleThroughAttribute
) attributes
[0];
370 /// Collects the skip filter.
372 /// <param name="actionDescriptor">The action descriptor.</param>
373 /// <param name="method">The method.</param>
374 private void CollectSkipFilter(ActionMetaDescriptor actionDescriptor
, MethodInfo method
)
376 object[] attributes
= method
.GetCustomAttributes(typeof(SkipFilterAttribute
), true);
378 foreach(SkipFilterAttribute attr
in attributes
)
380 actionDescriptor
.SkipFilters
.Add(attr
);
385 /// Collects the resources.
387 /// <param name="desc">The desc.</param>
388 /// <param name="memberInfo">The member info.</param>
389 private void CollectResources(BaseMetaDescriptor desc
, MemberInfo memberInfo
)
391 desc
.Resources
= resourceDescriptorProvider
.CollectResources(memberInfo
);
395 /// Collects the transform filter.
397 /// <param name="actionDescriptor">The action descriptor.</param>
398 /// <param name="method">The method.</param>
399 private void CollectTransformFilter(ActionMetaDescriptor actionDescriptor
, MethodInfo method
)
401 actionDescriptor
.TransformFilters
= transformFilterDescriptorProvider
.CollectFilters((method
));
402 Array
.Sort(actionDescriptor
.TransformFilters
, TransformFilterDescriptorComparer
.Instance
);
406 /// Gets the real controller type, instead of the proxy type.
409 /// Workaround for DYNPROXY-14 bug. See: http://support.castleproject.org/browse/DYNPROXY-14
411 private Type
GetRealControllerType(Type controllerType
)
413 Type prev
= controllerType
;
415 // try to get the first non-proxy type
416 while(controllerType
.Assembly
.FullName
.StartsWith("DynamicProxyGenAssembly2") ||
417 controllerType
.Assembly
.FullName
.StartsWith("DynamicAssemblyProxyGen"))
419 controllerType
= controllerType
.BaseType
;
421 if ( // controllerType == typeof(SmartDispatcherController) ||
422 controllerType
== typeof(IController
))
424 // oops, it's a pure-proxy controller. just let it go.
425 controllerType
= prev
;
430 return controllerType
;
435 #region Controller data
438 /// Collects the class level attributes.
440 /// <param name="controllerType">Type of the controller.</param>
441 /// <param name="descriptor">The descriptor.</param>
442 private void CollectClassLevelAttributes(Type controllerType
, ControllerMetaDescriptor descriptor
)
444 CollectHelpers(descriptor
, controllerType
);
445 CollectResources(descriptor
, controllerType
);
446 CollectFilters(descriptor
, controllerType
);
447 CollectLayout(descriptor
, controllerType
);
448 CollectRescues(descriptor
, controllerType
);
449 CollectDefaultAction(descriptor
, controllerType
);
450 CollectScaffolding(descriptor
, controllerType
);
451 CollectDynamicAction(descriptor
, controllerType
);
452 CollectCacheConfigure(descriptor
, controllerType
);
456 /// Collects the default action.
458 /// <param name="descriptor">The descriptor.</param>
459 /// <param name="controllerType">Type of the controller.</param>
460 private void CollectDefaultAction(ControllerMetaDescriptor descriptor
, Type controllerType
)
462 object[] attributes
= controllerType
.GetCustomAttributes(typeof(DefaultActionAttribute
), true);
464 if (attributes
.Length
!= 0)
466 descriptor
.DefaultAction
= (DefaultActionAttribute
) attributes
[0];
471 /// Collects the scaffolding.
473 /// <param name="descriptor">The descriptor.</param>
474 /// <param name="controllerType">Type of the controller.</param>
475 private void CollectScaffolding(ControllerMetaDescriptor descriptor
, Type controllerType
)
477 object[] attributes
= controllerType
.GetCustomAttributes(typeof(ScaffoldingAttribute
), false);
479 if (attributes
.Length
!= 0)
481 foreach(ScaffoldingAttribute scaffolding
in attributes
)
483 descriptor
.Scaffoldings
.Add(scaffolding
);
489 /// Collects the dynamic action.
491 /// <param name="descriptor">The descriptor.</param>
492 /// <param name="controllerType">Type of the controller.</param>
493 private void CollectDynamicAction(ControllerMetaDescriptor descriptor
, Type controllerType
)
495 object[] attributes
= controllerType
.GetCustomAttributes(typeof(DynamicActionProviderAttribute
), true);
497 if (attributes
.Length
!= 0)
499 foreach(DynamicActionProviderAttribute attr
in attributes
)
501 descriptor
.ActionProviders
.Add(attr
.ProviderType
);
507 /// Collects the helpers.
509 /// <param name="descriptor">The descriptor.</param>
510 /// <param name="controllerType">Type of the controller.</param>
511 private void CollectHelpers(ControllerMetaDescriptor descriptor
, Type controllerType
)
513 descriptor
.Helpers
= helperDescriptorProvider
.CollectHelpers(controllerType
);
517 /// Collects the filters.
519 /// <param name="descriptor">The descriptor.</param>
520 /// <param name="controllerType">Type of the controller.</param>
521 private void CollectFilters(ControllerMetaDescriptor descriptor
, Type controllerType
)
523 descriptor
.Filters
= filterDescriptorProvider
.CollectFilters(controllerType
);
525 Array
.Sort(descriptor
.Filters
, FilterDescriptorComparer
.Instance
);
529 /// Collects the layout.
531 /// <param name="descriptor">The descriptor.</param>
532 /// <param name="memberInfo">The member info.</param>
533 private void CollectLayout(BaseMetaDescriptor descriptor
, MemberInfo memberInfo
)
535 descriptor
.Layout
= layoutDescriptorProvider
.CollectLayout(memberInfo
);
539 /// Collects the rescues.
541 /// <param name="descriptor">The descriptor.</param>
542 /// <param name="memberInfo">The member info.</param>
543 private void CollectRescues(BaseMetaDescriptor descriptor
, MethodInfo memberInfo
)
545 descriptor
.Rescues
= rescueDescriptorProvider
.CollectRescues(memberInfo
);
549 /// Collects the rescues.
551 /// <param name="descriptor">The descriptor.</param>
552 /// <param name="type">The type.</param>
553 private void CollectRescues(BaseMetaDescriptor descriptor
, Type type
)
555 descriptor
.Rescues
= rescueDescriptorProvider
.CollectRescues(type
);
559 /// Collects the cache configures.
561 /// <param name="descriptor">The descriptor.</param>
562 /// <param name="memberInfo">The member info.</param>
563 private void CollectCacheConfigure(BaseMetaDescriptor descriptor
, MemberInfo memberInfo
)
565 object[] configurers
= memberInfo
.GetCustomAttributes(typeof(ICachePolicyConfigurer
), true);
567 if (configurers
.Length
!= 0)
569 descriptor
.CacheConfigurer
= (ICachePolicyConfigurer
) configurers
[0];
575 private ControllerMetaDescriptor
CreateMetaDescriptor()
583 return new ControllerMetaDescriptor();
587 private ActionMetaDescriptor
CreateActionDescriptor()
589 if (ActionCreate
!= null)
591 return ActionCreate();
595 return new ActionMetaDescriptor();
600 /// This <see cref="IComparer"/> implementation
601 /// is used to sort the filters based on their Execution Order.
603 private class FilterDescriptorComparer
: IComparer
605 private static readonly FilterDescriptorComparer instance
= new FilterDescriptorComparer();
608 /// Initializes a new instance of the <see cref="FilterDescriptorComparer"/> class.
610 private FilterDescriptorComparer()
615 /// Gets the instance.
617 /// <value>The instance.</value>
618 public static FilterDescriptorComparer Instance
620 get { return instance; }
624 /// Compares the specified left.
626 /// <param name="left">The left.</param>
627 /// <param name="right">The right.</param>
628 /// <returns></returns>
629 public int Compare(object left
, object right
)
631 return ((FilterDescriptor
) left
).ExecutionOrder
- ((FilterDescriptor
) right
).ExecutionOrder
;
636 /// This <see cref="IComparer"/> implementation
637 /// is used to sort the transformfilters based on their Execution Order.
639 private class TransformFilterDescriptorComparer
: IComparer
641 private static readonly TransformFilterDescriptorComparer instance
= new TransformFilterDescriptorComparer();
644 /// Initializes a new instance of the <see cref="TransformFilterDescriptorComparer"/> class.
646 private TransformFilterDescriptorComparer()
651 /// Gets the instance.
653 /// <value>The instance.</value>
654 public static TransformFilterDescriptorComparer Instance
656 get { return instance; }
660 /// Compares the specified left.
662 /// <param name="left">The left.</param>
663 /// <param name="right">The right.</param>
664 /// <returns></returns>
665 public int Compare(object left
, object right
)
667 return ((TransformFilterDescriptor
) right
).ExecutionOrder
- ((TransformFilterDescriptor
) left
).ExecutionOrder
;