Added asynchronous result support to SynchronizeFacility.
[castle.git] / Facilities / Synchronize / Castle.Facilities.Synchronize / SynchronizeInterceptor.cs
blobd3e6fb0d6b30cabaf14e1cc2deb485c74098b446
1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
2 //
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
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
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
17 using System;
18 using System.ComponentModel;
19 using System.Reflection;
20 using System.Threading;
21 using System.Runtime.Serialization;
22 using Castle.Core;
23 using Castle.Core.Interceptor;
24 using Castle.MicroKernel;
26 /// <summary>
27 /// Intercepts calls to synchronized components and ensures
28 /// that they execute in the proper synchronization context.
29 /// </summary>
30 [Transient]
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);
41 /// <summary>
42 /// Initializes a new instance of the <see cref="SynchronizeInterceptor"/> class.
43 /// </summary>
44 /// <param name="kernel">The kernel.</param>
45 /// <param name="metaStore">The meta store.</param>
46 public SynchronizeInterceptor(IKernel kernel, SynchronizeMetaInfoStore metaStore)
48 this.kernel = kernel;
49 this.metaStore = metaStore;
52 #region IOnBehalfAware
54 /// <summary>
55 /// Sets the intercepted ComponentModel.
56 /// </summary>
57 /// <param name="target">The targets ComponentModel.</param>
58 public void SetInterceptedComponentModel(ComponentModel target)
60 metaInfo = metaStore.GetMetaFor(target.Implementation);
63 #endregion
65 /// <summary>
66 /// Intercepts the invocation and applies any necessary
67 /// synchronization.
68 /// </summary>
69 /// <param name="invocation">The invocation.</param>
70 public void Intercept(IInvocation invocation)
72 if (!InvokeInSynchronizationContext(invocation) &&
73 !InvokeUsingSynchronizationTarget(invocation))
75 InvokeSynchronously(invocation);
79 /// <summary>
80 /// Continues the invocation in a synchronization context
81 /// if necessary.
82 /// </summary>
83 /// <param name="invocation">The invocation.</param>
84 /// <returns>
85 /// <c>true</c> if continued; otherwise, <c>false</c>.
86 /// </returns>
87 private bool InvokeInSynchronizationContext(IInvocation invocation)
89 if (metaInfo != null)
91 IHandler handler = null;
92 MethodInfo methodInfo = invocation.MethodInvocationTarget;
93 SynchronizeContextReference syncContextRef = metaInfo.GetSynchronizedContextFor(methodInfo);
95 if (syncContextRef == null)
97 InvokeSynchronously(invocation);
98 return true;
101 switch (syncContextRef.ReferenceType)
103 case SynchronizeContextReferenceType.Key:
104 handler = kernel.GetHandler(syncContextRef.ComponentKey);
105 break;
107 case SynchronizeContextReferenceType.Interface:
108 handler = kernel.GetHandler(syncContextRef.ServiceType);
109 break;
112 if (handler == null)
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);
142 finally
144 activeSyncContext = null;
146 }, null);
148 finally
150 SynchronizationContext.SetSynchronizationContext(prevSyncContext);
153 else
155 InvokeSynchronously(invocation);
158 return true;
161 return false;
164 /// <summary>
165 /// Continues the invocation using the targets implicit
166 /// synchronization if necessary.
167 /// </summary>
168 /// <param name="invocation">The invocation.</param>
169 /// <returns>
170 /// <c>true</c> if continued; otherwise, <c>false</c>.
171 /// </returns>
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 });
184 else
186 InvokeSynchronously(invocation);
189 return true;
192 return false;
195 /// <summary>
196 /// Continues the invocation synchronously.
197 /// </summary>
198 /// <param name="invocation">The invocation.</param>
199 private static void InvokeSynchronously(IInvocation invocation)
201 invocation.Proceed();
203 Result result = CreateResult(invocation);
204 if (result != null)
206 result.SetValue(true, invocation.ReturnValue);
210 /// <summary>
211 /// Used by the safe synchronization delegate.
212 /// </summary>
213 /// <param name="invocation">The invocation.</param>
214 /// <param name="result">The result holder.</param>
215 private static void InvokeSafely(IInvocation invocation, Result result)
217 if (result == null)
219 invocation.Proceed();
221 else
225 invocation.Proceed();
226 result.SetValue(false, invocation.ReturnValue);
228 catch (Exception exception)
230 result.SetException(false, exception);
235 /// <summary>
236 /// Creates the result of the invocation.
237 /// </summary>
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;
250 return result;
253 /// <summary>
254 /// Gets the default value for a type.
255 /// </summary>
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);
265 return defaultValue;