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
.Facilities
.Synchronize
18 using System
.ComponentModel
;
19 using System
.Reflection
;
20 using System
.Threading
;
21 using System
.Runtime
.Serialization
;
23 using Castle
.Core
.Interceptor
;
24 using Castle
.MicroKernel
;
27 /// Intercepts calls to synchronized components and ensures
28 /// that they execute in the proper synchronization context.
31 internal class SynchronizeInterceptor
: IInterceptor
, IOnBehalfAware
33 private readonly IKernel kernel
;
34 private SynchronizeMetaInfo metaInfo
;
35 private readonly SynchronizeMetaInfoStore metaStore
;
36 private readonly InvocationDelegate safeInvokeDelegate
= InvokeSafely
;
37 [ThreadStatic
] private SynchronizationContext activeSyncContext
;
39 private delegate void InvocationDelegate(IInvocation invocation
, Result result
);
42 /// Initializes a new instance of the <see cref="SynchronizeInterceptor"/> class.
44 /// <param name="kernel">The kernel.</param>
45 /// <param name="metaStore">The meta store.</param>
46 public SynchronizeInterceptor(IKernel kernel
, SynchronizeMetaInfoStore metaStore
)
49 this.metaStore
= metaStore
;
52 #region IOnBehalfAware
55 /// Sets the intercepted ComponentModel.
57 /// <param name="target">The targets ComponentModel.</param>
58 public void SetInterceptedComponentModel(ComponentModel target
)
60 metaInfo
= metaStore
.GetMetaFor(target
.Implementation
);
66 /// Intercepts the invocation and applies any necessary
69 /// <param name="invocation">The invocation.</param>
70 public void Intercept(IInvocation invocation
)
72 if (!InvokeInSynchronizationContext(invocation
) &&
73 !InvokeUsingSynchronizationTarget(invocation
))
75 InvokeSynchronously(invocation
);
80 /// Continues the invocation in a synchronization context
83 /// <param name="invocation">The invocation.</param>
85 /// <c>true</c> if continued; otherwise, <c>false</c>.
87 private bool InvokeInSynchronizationContext(IInvocation invocation
)
91 IHandler handler
= null;
92 MethodInfo methodInfo
= invocation
.MethodInvocationTarget
;
93 SynchronizeContextReference syncContextRef
= metaInfo
.GetSynchronizedContextFor(methodInfo
);
95 if (syncContextRef
== null)
97 InvokeSynchronously(invocation
);
101 switch (syncContextRef
.ReferenceType
)
103 case SynchronizeContextReferenceType
.Key
:
104 handler
= kernel
.GetHandler(syncContextRef
.ComponentKey
);
107 case SynchronizeContextReferenceType
.Interface
:
108 handler
= kernel
.GetHandler(syncContextRef
.ServiceType
);
114 throw new ApplicationException("The synchronization context could not be resolved");
117 SynchronizationContext syncContext
= handler
.Resolve(CreationContext
.Empty
)
118 as SynchronizationContext
;
120 if (syncContext
== null)
122 throw new ApplicationException(string.Format("{0} does not implement {1}",
123 syncContextRef
, typeof(SynchronizationContext
).FullName
));
126 if (syncContext
!= activeSyncContext
)
128 SynchronizationContext prevSyncContext
= SynchronizationContext
.Current
;
132 Result result
= CreateResult(invocation
);
133 SynchronizationContext
.SetSynchronizationContext(syncContext
);
135 syncContext
.Send(delegate {
136 activeSyncContext
= syncContext
;
140 InvokeSafely(invocation
, result
);
144 activeSyncContext
= null;
150 SynchronizationContext
.SetSynchronizationContext(prevSyncContext
);
155 InvokeSynchronously(invocation
);
165 /// Continues the invocation using the targets implicit
166 /// synchronization if necessary.
168 /// <param name="invocation">The invocation.</param>
170 /// <c>true</c> if continued; otherwise, <c>false</c>.
172 private bool InvokeUsingSynchronizationTarget(IInvocation invocation
)
174 ISynchronizeInvoke syncTarget
= (ISynchronizeInvoke
)invocation
.InvocationTarget
;
176 if (syncTarget
!= null)
178 Result result
= CreateResult(invocation
);
180 if (syncTarget
.InvokeRequired
)
182 syncTarget
.Invoke(safeInvokeDelegate
, new object[] { invocation, result }
);
186 InvokeSynchronously(invocation
);
196 /// Continues the invocation synchronously.
198 /// <param name="invocation">The invocation.</param>
199 private static void InvokeSynchronously(IInvocation invocation
)
201 invocation
.Proceed();
203 Result result
= CreateResult(invocation
);
206 result
.SetValue(true, invocation
.ReturnValue
);
211 /// Used by the safe synchronization delegate.
213 /// <param name="invocation">The invocation.</param>
214 /// <param name="result">The result holder.</param>
215 private static void InvokeSafely(IInvocation invocation
, Result result
)
219 invocation
.Proceed();
225 invocation
.Proceed();
226 result
.SetValue(false, invocation
.ReturnValue
);
228 catch (Exception exception
)
230 result
.SetException(false, exception
);
236 /// Creates the result of the invocation.
238 /// <param name="invocation">The invocation.</param>
239 /// <returns>Holds the invocation result.</returns>
240 private static Result
CreateResult(IInvocation invocation
)
242 Result result
= null;
243 Type returnType
= invocation
.Method
.ReturnType
;
244 if (returnType
!= typeof(void))
246 invocation
.ReturnValue
= GetDefault(returnType
);
247 result
= new Result();
249 Result
.Last
= result
;
254 /// Gets the default value for a type.
256 /// <param name="type">The type.</param>
257 /// <returns>The default value for the type.</returns>
258 private static object GetDefault(Type type
)
260 object defaultValue
= null;
261 if (type
.IsValueType
)
263 defaultValue
= FormatterServices
.GetUninitializedObject(type
);