PageRenderTime 15ms CodeModel.GetById 2ms app.highlight 9ms RepoModel.GetById 2ms app.codeStats 0ms

/src/LinFu.IoC/Interceptors/InterceptorAttributeLoader.cs

http://github.com/philiplaureano/LinFu
C# | 268 lines | 146 code | 44 blank | 78 comment | 32 complexity | 987957a0602564b612ea5abcc858efc1 MD5 | raw file
  1using System;
  2using System.Collections.Generic;
  3using System.IO;
  4using System.Linq;
  5using System.Reflection;
  6using LinFu.AOP.Interfaces;
  7using LinFu.IoC.Configuration;
  8using LinFu.IoC.Configuration.Interfaces;
  9using LinFu.IoC.Interfaces;
 10using LinFu.Proxy.Interfaces;
 11using LinFu.Reflection;
 12
 13namespace LinFu.IoC.Interceptors
 14{
 15    /// <summary>
 16    ///     The class responsible for loading interceptors marked with the
 17    ///     <see cref="InterceptsAttribute" /> class.
 18    /// </summary>
 19    internal class InterceptorAttributeLoader : ITypeLoader
 20    {
 21        private readonly ILoader<IServiceContainer> _loaderHost;
 22
 23        /// <summary>
 24        ///     Initializes the class with the given <paramref name="loaderHost" />.
 25        /// </summary>
 26        /// <param name="loaderHost">
 27        ///     The <see cref="ILoader{TTarget}" /> instance that will be responsible for loading the
 28        ///     <see cref="IServiceContainer" /> instance itself.
 29        /// </param>
 30        internal InterceptorAttributeLoader(ILoader<IServiceContainer> loaderHost)
 31        {
 32            _loaderHost = loaderHost;
 33        }
 34
 35
 36        /// <summary>
 37        ///     Loads an <see cref="IInterceptor" /> derived class into a particular <see cref="IServiceContainer" /> instance
 38        ///     so that the current interceptor type can intercept calls made to services created from the given
 39        ///     target container.
 40        /// </summary>
 41        /// <param name="input">The interceptor type.</param>
 42        /// <returns>
 43        ///     By default, this will always return an empty set of container actions. The actual interceptor itself will be
 44        ///     injected at the end of the postprocessor chain.
 45        /// </returns>
 46        public IEnumerable<Action<IServiceContainer>> Load(Type input)
 47        {
 48            var typeInstance = Activator.CreateInstance(input);
 49            var interceptor = typeInstance as IInterceptor;
 50
 51            Func<IServiceRequestResult, IInterceptor> getInterceptor = null;
 52
 53            // Return the interceptor by default
 54            if (interceptor != null)
 55                getInterceptor = result =>
 56                {
 57                    var target = result.ActualResult;
 58                    var container = result.Container;
 59                    var methodInvoke = container.GetService<IMethodInvoke<MethodInfo>>();
 60                    var factory = container.GetService<IProxyFactory>();
 61
 62                    // Manually initialize the interceptor
 63                    var initialize = interceptor as IInitialize;
 64                    if (initialize != null)
 65                        initialize.Initialize(container);
 66
 67                    return new Redirector(() => target, interceptor, factory, methodInvoke);
 68                };
 69
 70            if (typeInstance != null && typeInstance is IAroundInvoke)
 71            {
 72                // Convert the IAroundInvoke instance into 
 73                // a running interceptor
 74                var aroundInvoke = typeInstance as IAroundInvoke;
 75                getInterceptor = result =>
 76                {
 77                    var container = result.Container;
 78                    var methodInvoke = container.GetService<IMethodInvoke<MethodInfo>>();
 79                    var factory = container.GetService<IProxyFactory>();
 80
 81                    // Manually initialize the interceptor
 82                    var initialize = aroundInvoke as IInitialize;
 83                    if (initialize != null)
 84                        initialize.Initialize(container);
 85
 86                    // HACK: The adapter can't be created until runtime since
 87                    // the aroundInvoke instance needs an actual target
 88                    var target = result.ActualResult;
 89                    var adapter = new AroundInvokeAdapter(() => target,
 90                        methodInvoke, aroundInvoke);
 91
 92                    var redirector = new Redirector(() => target, adapter, factory, methodInvoke);
 93
 94                    return redirector;
 95                };
 96            }
 97
 98            // The type must implement either the IInterceptor interface
 99            // or the IAroundInvoke interface
100            if (getInterceptor == null)
101                return new Action<IServiceContainer>[0];
102
103            // Determine which service types should be intercepted
104            var attributes =
105                from attribute in input.GetCustomAttributes(typeof(InterceptsAttribute), false)
106                let currentAttribute = attribute as InterceptsAttribute
107                where currentAttribute != null
108                select currentAttribute;
109
110            var interceptedTypes = new Dictionary<Type, HashSet<string>>();
111            foreach (var attribute in attributes)
112            {
113                var serviceName = attribute.ServiceName;
114                var serviceType = attribute.TargetType;
115
116                // Keep track of the service name and service type
117                // and mark the current type for interception
118                // using the current interceptor
119                if (!interceptedTypes.ContainsKey(serviceType))
120                    interceptedTypes[serviceType] = new HashSet<string>();
121
122                if (!interceptedTypes[serviceType].Contains(serviceName))
123                    interceptedTypes[serviceType].Add(serviceName);
124            }
125
126            // There must be at least one InterceptsAttribute defined on
127            // the input type
128            if (interceptedTypes.Count == 0)
129                return new Action<IServiceContainer>[0];
130
131            // Match the service type with the current type
132            Func<IServiceRequestResult, bool> filter = request =>
133            {
134                var container = request.Container;
135
136                // There must be a valid proxy factory
137                if (container == null ||
138                    !container.Contains(typeof(IProxyFactory)))
139                    return false;
140
141                var serviceType = request.ServiceType;
142                // Ignore requests to intercept IMethodInvoke<MethodInfo>
143                if (serviceType == typeof(IMethodInvoke<MethodInfo>))
144                    return false;
145
146                // Sealed types cannot be proxied by default
147                if (serviceType.IsSealed)
148                    return false;
149
150                // Match any service name if the service name is blank
151                if (request.ServiceName == null &&
152                    interceptedTypes.ContainsKey(serviceType))
153                    return true;
154
155                // Match the service name and type
156                if (interceptedTypes.ContainsKey(serviceType) &&
157                    interceptedTypes[serviceType].Contains(
158                        request.ServiceName))
159                    return true;
160
161                if (!serviceType.IsGenericType)
162                    return false;
163
164                // Determine if an interceptor can intercept the
165                // entire family of generic types
166                var baseDefinition =
167                    serviceType.GetGenericTypeDefinition();
168
169                // The list of intercepted types should contain
170                // the generic type definition and its matching 
171                // service name
172                var serviceName = request.ServiceName;
173
174                return interceptedTypes.ContainsKey(baseDefinition) &&
175                       interceptedTypes[baseDefinition].Contains(
176                           serviceName);
177            };
178
179            // Create the proxy using the service request
180            Func<IServiceRequestResult, object> createProxy = request => CreateProxyFrom(request, getInterceptor);
181
182            // Place the interceptor at the end of the 
183            // postprocessor chain
184            var injector = new ProxyInjector(filter, createProxy);
185            _loaderHost.Plugins.Add(new ProxyContainerPlugin(injector));
186
187            return new Action<IServiceContainer>[0];
188        }
189
190        /// <summary>
191        ///     Determines whether or not a target type is an interceptor.
192        /// </summary>
193        /// <param name="inputType">The target type currently being tested.</param>
194        /// <returns>
195        ///     Returns <c>true</c> if the <paramref name="inputType" /> is an interceptor; otherwise, it will return
196        ///     <c>false</c>.
197        /// </returns>
198        public bool CanLoad(Type inputType)
199        {
200            try
201            {
202                var attributes = inputType.GetCustomAttributes(typeof(InterceptsAttribute), false);
203
204                if (attributes == null)
205                    attributes = new object[0];
206
207                // The target type must have at least one InterceptsAttribute defined
208                var matches = from attribute in attributes
209                    let currentAttribute = attribute as InterceptsAttribute
210                    where currentAttribute != null
211                    select currentAttribute;
212
213                return matches.Count() > 0;
214            }
215            catch (TypeInitializationException)
216            {
217                // Ignore the error
218                return false;
219            }
220            catch (FileNotFoundException)
221            {
222                // Ignore the error
223                return false;
224            }
225        }
226
227
228        /// <summary>
229        ///     Generates a proxy instance from an existing <see cref="IServiceRequestResult" /> instance.
230        /// </summary>
231        /// <param name="request">
232        ///     The <see cref="IServiceRequestResult" /> instance that describes the proxy type that must be
233        ///     generated.
234        /// </param>
235        /// <param name="getInterceptor">
236        ///     The <see cref="IInterceptor" /> functor that will create the interceptor which will handle
237        ///     all calls made to the proxy instance.
238        /// </param>
239        /// <returns>A service proxy.</returns>
240        private static object CreateProxyFrom(IServiceRequestResult request,
241            Func<IServiceRequestResult, IInterceptor> getInterceptor)
242        {
243            var interceptor = getInterceptor(request);
244
245            var container = request.Container;
246            var proxyFactory =
247                container.GetService<IProxyFactory>();
248
249            // The proxy factory must exist
250            if (proxyFactory == null)
251                return null;
252
253            // Generate the proxy type
254            var proxyType = proxyFactory.CreateProxyType(request.ServiceType,
255                new Type[0]);
256
257            // The generated proxy instance
258            // must implement IProxy
259            var proxy = Activator.CreateInstance(proxyType) as IProxy;
260
261            // Assign the interceptor
262            if (proxy != null)
263                proxy.Interceptor = interceptor;
264
265            return proxy;
266        }
267    }
268}