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
.Services
18 using System
.Collections
;
19 using System
.Reflection
;
20 using System
.Threading
;
22 using Castle
.Core
.Logging
;
23 using Castle
.MonoRail
.Framework
.Internal
;
26 /// Constructs and caches all collected information
27 /// about a <see cref="Controller"/> and its actions.
28 /// <seealso cref="ControllerMetaDescriptor"/>
30 public class DefaultControllerDescriptorProvider
: IControllerDescriptorProvider
33 /// The logger instance
35 private ILogger logger
= NullLogger
.Instance
;
38 /// Used to lock the cache
40 private ReaderWriterLock locker
= new ReaderWriterLock();
41 private Hashtable descriptorRepository
= new Hashtable();
42 private IHelperDescriptorProvider helperDescriptorProvider
;
43 private IFilterDescriptorProvider filterDescriptorProvider
;
44 private ILayoutDescriptorProvider layoutDescriptorProvider
;
45 private IRescueDescriptorProvider rescueDescriptorProvider
;
46 private IResourceDescriptorProvider resourceDescriptorProvider
;
47 private ITransformFilterDescriptorProvider transformFilterDescriptorProvider
;
49 #region IServiceEnabledComponent implementation
52 /// Services the specified service provider.
54 /// <param name="serviceProvider">The service provider.</param>
55 public void Service(IServiceProvider serviceProvider
)
57 ILoggerFactory loggerFactory
= (ILoggerFactory
) serviceProvider
.GetService(typeof(ILoggerFactory
));
59 if (loggerFactory
!= null)
61 logger
= loggerFactory
.Create(typeof(DefaultControllerDescriptorProvider
));
64 helperDescriptorProvider
= (IHelperDescriptorProvider
)
65 serviceProvider
.GetService(typeof(IHelperDescriptorProvider
));
67 filterDescriptorProvider
= (IFilterDescriptorProvider
)
68 serviceProvider
.GetService(typeof(IFilterDescriptorProvider
));
70 layoutDescriptorProvider
= (ILayoutDescriptorProvider
)
71 serviceProvider
.GetService(typeof(ILayoutDescriptorProvider
));
73 rescueDescriptorProvider
= (IRescueDescriptorProvider
)
74 serviceProvider
.GetService(typeof(IRescueDescriptorProvider
));
76 resourceDescriptorProvider
= (IResourceDescriptorProvider
)
77 serviceProvider
.GetService(typeof(IResourceDescriptorProvider
));
79 transformFilterDescriptorProvider
= (ITransformFilterDescriptorProvider
)
80 serviceProvider
.GetService(typeof(ITransformFilterDescriptorProvider
));
86 /// Constructs and populates a <see cref="ControllerMetaDescriptor"/>.
89 /// This implementation is also responsible for caching
90 /// constructed meta descriptors.
92 public ControllerMetaDescriptor
BuildDescriptor(Controller controller
)
94 Type controllerType
= controller
.GetType();
96 return BuildDescriptor(controllerType
);
100 /// Constructs and populates a <see cref="ControllerMetaDescriptor"/>.
103 /// This implementation is also responsible for caching
104 /// constructed meta descriptors.
106 public ControllerMetaDescriptor
BuildDescriptor(Type controllerType
)
108 ControllerMetaDescriptor desc
;
110 locker
.AcquireReaderLock(-1);
112 desc
= (ControllerMetaDescriptor
) descriptorRepository
[controllerType
];
116 locker
.ReleaseReaderLock();
122 locker
.UpgradeToWriterLock(-1);
124 // We need to recheck after getting the writer lock
125 desc
= (ControllerMetaDescriptor
) descriptorRepository
[controllerType
];
132 desc
= InternalBuildDescriptor(controllerType
);
134 descriptorRepository
[controllerType
] = desc
;
138 locker
.ReleaseWriterLock();
145 /// Builds the <see cref="ControllerMetaDescriptor"/> for the specified controller type
147 /// <param name="controllerType">Type of the controller.</param>
148 /// <returns></returns>
149 private ControllerMetaDescriptor
InternalBuildDescriptor(Type controllerType
)
151 if (logger
.IsDebugEnabled
)
153 logger
.DebugFormat("Building controller descriptor for {0}", controllerType
);
156 ControllerMetaDescriptor descriptor
= new ControllerMetaDescriptor();
158 CollectClassLevelAttributes(controllerType
, descriptor
);
160 CollectActions(controllerType
, descriptor
);
162 CollectActionLevelAttributes(descriptor
);
170 /// Collects the actions.
172 /// <param name="controllerType">Type of the controller.</param>
173 /// <param name="desc">The desc.</param>
174 private void CollectActions(Type controllerType
, ControllerMetaDescriptor desc
)
176 // HACK: GetRealControllerType is a workaround for DYNPROXY-14 bug
177 // see: http://support.castleproject.org/browse/DYNPROXY-14
178 controllerType
= GetRealControllerType(controllerType
);
180 MethodInfo
[] methods
= controllerType
.GetMethods(BindingFlags
.Public
| BindingFlags
.Instance
);
182 foreach(MethodInfo method
in methods
)
184 Type declaringType
= method
.DeclaringType
;
186 if (declaringType
== typeof(Object
) ||
187 declaringType
== typeof(Controller
) ||
188 declaringType
== typeof(SmartDispatcherController
))
193 if (desc
.Actions
.Contains(method
.Name
))
195 ArrayList list
= desc
.Actions
[method
.Name
] as ArrayList
;
199 list
= new ArrayList();
200 list
.Add(desc
.Actions
[method
.Name
]);
202 desc
.Actions
[method
.Name
] = list
;
209 desc
.Actions
[method
.Name
] = method
;
215 /// Collects the action level attributes.
217 /// <param name="descriptor">The descriptor.</param>
218 private void CollectActionLevelAttributes(ControllerMetaDescriptor descriptor
)
220 foreach(object action
in descriptor
.Actions
.Values
)
224 foreach(MethodInfo overloadedAction
in (action
as IList
))
226 CollectActionAttributes(overloadedAction
, descriptor
);
232 CollectActionAttributes(action
as MethodInfo
, descriptor
);
237 /// Collects the action attributes.
239 /// <param name="method">The method.</param>
240 /// <param name="descriptor">The descriptor.</param>
241 private void CollectActionAttributes(MethodInfo method
, ControllerMetaDescriptor descriptor
)
243 if (logger
.IsDebugEnabled
)
245 logger
.DebugFormat("Collection attributes for action {0}", method
.Name
);
248 ActionMetaDescriptor actionDescriptor
= descriptor
.GetAction(method
);
250 CollectResources(actionDescriptor
, method
);
251 CollectSkipFilter(actionDescriptor
, method
);
252 CollectRescues(actionDescriptor
, method
);
253 CollectAccessibleThrough(actionDescriptor
, method
);
254 CollectSkipRescue(actionDescriptor
, method
);
255 CollectLayout(actionDescriptor
, method
);
256 CollectCacheConfigures(actionDescriptor
, method
);
257 CollectTransformFilter(actionDescriptor
, method
);
259 if (method
.IsDefined(typeof(AjaxActionAttribute
), true))
261 descriptor
.AjaxActions
.Add(method
);
264 if (method
.IsDefined(typeof(DefaultActionAttribute
), true))
266 if (descriptor
.DefaultAction
!= null)
268 throw new MonoRailException("Cannot resolve a default action for {0}, DefaultActionAttribute was declared more than once.", method
.DeclaringType
.FullName
);
270 descriptor
.DefaultAction
= new DefaultActionAttribute(method
.Name
);
275 /// Collects the skip rescue.
277 /// <param name="actionDescriptor">The action descriptor.</param>
278 /// <param name="method">The method.</param>
279 private void CollectSkipRescue(ActionMetaDescriptor actionDescriptor
, MethodInfo method
)
281 object[] attributes
= method
.GetCustomAttributes(typeof(SkipRescueAttribute
), true);
283 if (attributes
.Length
!= 0)
285 actionDescriptor
.SkipRescue
= (SkipRescueAttribute
) attributes
[0];
290 /// Collects the accessible through.
292 /// <param name="actionDescriptor">The action descriptor.</param>
293 /// <param name="method">The method.</param>
294 private void CollectAccessibleThrough(ActionMetaDescriptor actionDescriptor
, MethodInfo method
)
296 object[] attributes
= method
.GetCustomAttributes(typeof(AccessibleThroughAttribute
), true);
298 if (attributes
.Length
!= 0)
300 actionDescriptor
.AccessibleThrough
= (AccessibleThroughAttribute
) attributes
[0];
305 /// Collects the skip filter.
307 /// <param name="actionDescriptor">The action descriptor.</param>
308 /// <param name="method">The method.</param>
309 private void CollectSkipFilter(ActionMetaDescriptor actionDescriptor
, MethodInfo method
)
311 object[] attributes
= method
.GetCustomAttributes(typeof(SkipFilterAttribute
), true);
313 foreach(SkipFilterAttribute attr
in attributes
)
315 actionDescriptor
.SkipFilters
.Add(attr
);
320 /// Collects the resources.
322 /// <param name="desc">The desc.</param>
323 /// <param name="memberInfo">The member info.</param>
324 private void CollectResources(BaseMetaDescriptor desc
, MemberInfo memberInfo
)
326 desc
.Resources
= resourceDescriptorProvider
.CollectResources(memberInfo
);
330 /// Collects the transform filter.
332 /// <param name="actionDescriptor">The action descriptor.</param>
333 /// <param name="method">The method.</param>
334 private void CollectTransformFilter(ActionMetaDescriptor actionDescriptor
, MethodInfo method
)
336 actionDescriptor
.TransformFilters
= transformFilterDescriptorProvider
.CollectFilters((method
));
337 Array
.Sort(actionDescriptor
.TransformFilters
, TransformFilterDescriptorComparer
.Instance
);
341 /// Gets the real controller type, instead of the proxy type.
344 /// Workaround for DYNPROXY-14 bug. See: http://support.castleproject.org/browse/DYNPROXY-14
346 private Type
GetRealControllerType(Type controllerType
)
348 Type prev
= controllerType
;
350 // try to get the first non-proxy type
351 while(controllerType
.Assembly
.FullName
.StartsWith("DynamicProxyGenAssembly2") ||
352 controllerType
.Assembly
.FullName
.StartsWith("DynamicAssemblyProxyGen"))
354 controllerType
= controllerType
.BaseType
;
356 if (controllerType
== typeof(SmartDispatcherController
) ||
357 controllerType
== typeof(Controller
))
359 // oops, it's a pure-proxy controller. just let it go.
360 controllerType
= prev
;
365 return controllerType
;
370 #region Controller data
373 /// Collects the class level attributes.
375 /// <param name="controllerType">Type of the controller.</param>
376 /// <param name="descriptor">The descriptor.</param>
377 private void CollectClassLevelAttributes(Type controllerType
, ControllerMetaDescriptor descriptor
)
379 CollectHelpers(descriptor
, controllerType
);
380 CollectResources(descriptor
, controllerType
);
381 CollectFilters(descriptor
, controllerType
);
382 CollectLayout(descriptor
, controllerType
);
383 CollectRescues(descriptor
, controllerType
);
384 CollectDefaultAction(descriptor
, controllerType
);
385 CollectScaffolding(descriptor
, controllerType
);
386 CollectDynamicAction(descriptor
, controllerType
);
390 /// Collects the default action.
392 /// <param name="descriptor">The descriptor.</param>
393 /// <param name="controllerType">Type of the controller.</param>
394 private void CollectDefaultAction(ControllerMetaDescriptor descriptor
, Type controllerType
)
396 object[] attributes
= controllerType
.GetCustomAttributes(typeof(DefaultActionAttribute
), true);
398 if (attributes
.Length
!= 0)
400 descriptor
.DefaultAction
= (DefaultActionAttribute
) attributes
[0];
405 /// Collects the scaffolding.
407 /// <param name="descriptor">The descriptor.</param>
408 /// <param name="controllerType">Type of the controller.</param>
409 private void CollectScaffolding(ControllerMetaDescriptor descriptor
, Type controllerType
)
411 object[] attributes
= controllerType
.GetCustomAttributes(typeof(ScaffoldingAttribute
), false);
413 if (attributes
.Length
!= 0)
415 foreach(ScaffoldingAttribute scaffolding
in attributes
)
417 descriptor
.Scaffoldings
.Add(scaffolding
);
423 /// Collects the dynamic action.
425 /// <param name="descriptor">The descriptor.</param>
426 /// <param name="controllerType">Type of the controller.</param>
427 private void CollectDynamicAction(ControllerMetaDescriptor descriptor
, Type controllerType
)
429 object[] attributes
= controllerType
.GetCustomAttributes(typeof(DynamicActionProviderAttribute
), true);
431 if (attributes
.Length
!= 0)
433 foreach(DynamicActionProviderAttribute attr
in attributes
)
435 descriptor
.ActionProviders
.Add(attr
.ProviderType
);
441 /// Collects the helpers.
443 /// <param name="descriptor">The descriptor.</param>
444 /// <param name="controllerType">Type of the controller.</param>
445 private void CollectHelpers(ControllerMetaDescriptor descriptor
, Type controllerType
)
447 descriptor
.Helpers
= helperDescriptorProvider
.CollectHelpers(controllerType
);
451 /// Collects the filters.
453 /// <param name="descriptor">The descriptor.</param>
454 /// <param name="controllerType">Type of the controller.</param>
455 private void CollectFilters(ControllerMetaDescriptor descriptor
, Type controllerType
)
457 descriptor
.Filters
= filterDescriptorProvider
.CollectFilters(controllerType
);
459 Array
.Sort(descriptor
.Filters
, FilterDescriptorComparer
.Instance
);
463 /// Collects the layout.
465 /// <param name="descriptor">The descriptor.</param>
466 /// <param name="memberInfo">The member info.</param>
467 private void CollectLayout(BaseMetaDescriptor descriptor
, MemberInfo memberInfo
)
469 descriptor
.Layout
= layoutDescriptorProvider
.CollectLayout(memberInfo
);
473 /// Collects the rescues.
475 /// <param name="descriptor">The descriptor.</param>
476 /// <param name="memberInfo">The member info.</param>
477 private void CollectRescues(BaseMetaDescriptor descriptor
, MethodInfo memberInfo
)
479 descriptor
.Rescues
= rescueDescriptorProvider
.CollectRescues(memberInfo
);
483 /// Collects the rescues.
485 /// <param name="descriptor">The descriptor.</param>
486 /// <param name="type">The type.</param>
487 private void CollectRescues(BaseMetaDescriptor descriptor
, Type type
)
489 descriptor
.Rescues
= rescueDescriptorProvider
.CollectRescues(type
);
493 /// Collects the cache configures.
495 /// <param name="descriptor">The descriptor.</param>
496 /// <param name="memberInfo">The member info.</param>
497 private void CollectCacheConfigures(ActionMetaDescriptor descriptor
, MemberInfo memberInfo
)
499 object[] configurers
= memberInfo
.GetCustomAttributes(typeof(ICachePolicyConfigurer
), true);
501 if (configurers
.Length
!= 0)
503 foreach(ICachePolicyConfigurer cacheConfigurer
in configurers
)
505 descriptor
.CacheConfigurers
.Add(cacheConfigurer
);
513 /// This <see cref="IComparer"/> implementation
514 /// is used to sort the filters based on their Execution Order.
516 class FilterDescriptorComparer
: IComparer
518 private static readonly FilterDescriptorComparer instance
= new FilterDescriptorComparer();
521 /// Initializes a new instance of the <see cref="FilterDescriptorComparer"/> class.
523 private FilterDescriptorComparer()
528 /// Gets the instance.
530 /// <value>The instance.</value>
531 public static FilterDescriptorComparer Instance
533 get { return instance; }
537 /// Compares the specified left.
539 /// <param name="left">The left.</param>
540 /// <param name="right">The right.</param>
541 /// <returns></returns>
542 public int Compare(object left
, object right
)
544 return ((FilterDescriptor
) left
).ExecutionOrder
- ((FilterDescriptor
) right
).ExecutionOrder
;
549 /// This <see cref="IComparer"/> implementation
550 /// is used to sort the transformfilters based on their Execution Order.
552 class TransformFilterDescriptorComparer
: IComparer
554 private static readonly TransformFilterDescriptorComparer instance
= new TransformFilterDescriptorComparer();
557 /// Initializes a new instance of the <see cref="TransformFilterDescriptorComparer"/> class.
559 private TransformFilterDescriptorComparer()
564 /// Gets the instance.
566 /// <value>The instance.</value>
567 public static TransformFilterDescriptorComparer Instance
569 get { return instance; }
573 /// Compares the specified left.
575 /// <param name="left">The left.</param>
576 /// <param name="right">The right.</param>
577 /// <returns></returns>
578 public int Compare(object left
, object right
)
580 return ((TransformFilterDescriptor
)right
).ExecutionOrder
- ((TransformFilterDescriptor
)left
).ExecutionOrder
;