Fixing an issue with output parameters that are of type IntPtr
[castle.git] / InversionOfControl / Castle.MicroKernel / Facilities / EventWiring / EventWiringFacility.cs
blobac8fdbf47859a276faced49c89a2b3d4a59ded84
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.EventWiring
17 using System;
18 using System.Collections;
19 using System.Collections.Specialized;
20 using System.Reflection;
22 using Castle.Core;
23 using Castle.Core.Configuration;
25 using Castle.MicroKernel;
26 using Castle.MicroKernel.Facilities;
28 /// <summary>
29 /// Facility to allow components to dynamically subscribe to events offered by
30 /// other components. We call the component that offers events publishers and
31 /// the components that uses them, subscribers.
32 /// </summary>
33 /// <remarks>
34 /// A component that wish to subscribe to an event must use the external configuration
35 /// adding a node <c>subscribers</c> on the publisher. This node can have multiple entries using the
36 /// <c>subscriber</c> node.
37 /// </remarks>
38 /// <example>
39 /// <para>This example shows two simple components: one is the event publisher and the other is the
40 /// subscriber. The subscription will be done by the facility, using the publisher associated configuration.</para>
41 /// <para>The Publisher class:</para>
42 /// <code>
43 /// public class SimplePublisher
44 /// {
45 /// public event PublishEventHandler Event;
46 ///
47 /// public void Trigger()
48 /// {
49 /// if (Event != null)
50 /// {
51 /// Event(this, new EventArgs());
52 /// }
53 /// }
54 /// }
55 /// </code>
56 /// <para>The Subscriber class:</para>
57 /// <code>
58 /// public class SimpleListener
59 /// {
60 /// private bool _listened;
61 /// private object _sender;
62 ///
63 /// public void OnPublish(object sender, EventArgs e)
64 /// {
65 /// _sender = sender;
66 /// _listened = sender != null;
67 /// }
68 ///
69 /// public bool Listened
70 /// {
71 /// get { return _listened; }
72 /// }
73 ///
74 /// public object Sender
75 /// {
76 /// get { return _sender; }
77 /// }
78 /// }
79 /// </code>
80 /// <para>The configuration file:</para>
81 /// <code>
82 /// <![CDATA[
83 /// <?xml version="1.0" encoding="utf-8" ?>
84 /// <configuration>
85 /// <facilities>
86 /// <facility
87 /// id="event.wiring"
88 /// type="Castle.Facilities.EventWiring.EventWiringFacility, Castle.MicroKernel" />
89 /// </facilities>
90 ///
91 /// <components>
92 /// <component
93 /// id="SimpleListener"
94 /// type="Castle.Facilities.EventWiring.Tests.Model.SimpleListener, Castle.Facilities.EventWiring.Tests" />
95 ///
96 /// <component
97 /// id="SimplePublisher"
98 /// type="Castle.Facilities.EventWiring.Tests.Model.SimplePublisher, Castle.Facilities.EventWiring.Tests" >
99 /// <subscribers>
100 /// <subscriber id="SimpleListener" event="Event" handler="OnPublish"/>
101 /// </subscribers>
102 /// </component>
103 /// </components>
104 /// </configuration>
105 /// ]]>
106 /// </code>
107 /// </example>
108 public class EventWiringFacility : AbstractFacility
110 private const string SubscriberList = "evts.subscriber.list";
112 /// <summary>
113 /// Overriden. Initializes the facility, subscribing to the <see cref="IKernelEvents.ComponentModelCreated"/>,
114 /// <see cref="IKernelEvents.ComponentCreated"/>, <see cref="IKernelEvents.ComponentDestroyed"/> Kernel events.
115 /// </summary>
116 protected override void Init()
118 Kernel.ComponentModelCreated += new ComponentModelDelegate(OnComponentModelCreated);
119 Kernel.ComponentCreated += new ComponentInstanceDelegate(OnComponentCreated);
120 Kernel.ComponentDestroyed += new ComponentInstanceDelegate(OnComponentDestroyed);
123 #region OnComponentModelCreated
125 /// <summary>
126 /// Checks if the component we're dealing is a publisher. If it is,
127 /// parses the configuration (the subscribers node) getting the event wiring info.
128 /// </summary>
129 /// <param name="model">The component model.</param>
130 /// <exception cref="EventWiringException">Invalid and/or a error in the configuration</exception>
131 private void OnComponentModelCreated(ComponentModel model)
133 ExtractAndRegisterEventInformation(model);
136 private void ExtractAndRegisterEventInformation(ComponentModel model)
138 if (IsNotPublishingEvents(model)) return;
140 IConfiguration subscribersNode = model.Configuration.Children["subscribers"];
142 if (subscribersNode.Children.Count < 1)
144 throw new EventWiringException(
145 "The subscribers node must have at least an one subsciber child. Check node subscribers of the "
146 + model.Name + " component");
149 IDictionary subscribers2Evts = new HybridDictionary();
151 foreach (IConfiguration subscriber in subscribersNode.Children)
153 string subscriberKey = GetSubscriberKey(subscriber);
155 AddSubscriberDependecyToModel(subscriberKey, model);
157 ExtractAndAddEventInfo(subscribers2Evts, subscriberKey, subscriber, model);
160 model.ExtendedProperties[SubscriberList] = subscribers2Evts;
163 private void ExtractAndAddEventInfo(IDictionary subscribers2Evts, string subscriberKey, IConfiguration subscriber, ComponentModel model)
165 ArrayList wireInfoList = (ArrayList)subscribers2Evts[subscriberKey];
167 if (wireInfoList == null)
169 wireInfoList = new ArrayList();
170 subscribers2Evts[subscriberKey] = wireInfoList;
173 string eventName = subscriber.Attributes["event"];
174 if (eventName == null || eventName.Length == 0)
176 throw new EventWiringException("You must supply an 'event' " +
177 "attribute which is the event name on the publisher you want to subscribe." +
178 " Check node 'subscriber' for component " + model.Name + "and id = " + subscriberKey);
181 string handlerMethodName = subscriber.Attributes["handler"];
182 if (handlerMethodName == null || handlerMethodName.Length == 0)
184 throw new EventWiringException("You must supply an 'handler' attribute " +
185 "which is the method on the subscriber that will handle the event." +
186 " Check node 'subscriber' for component " + model.Name + "and id = " + subscriberKey);
189 wireInfoList.Add(new WireInfo(eventName, handlerMethodName));
192 private void AddSubscriberDependecyToModel(string subscriberKey, ComponentModel model)
194 DependencyModel dp = new DependencyModel(DependencyType.ServiceOverride, subscriberKey, null, false);
196 if (!model.Dependencies.Contains(dp))
198 model.Dependencies.Add(dp);
202 private static string GetSubscriberKey(IConfiguration subscriber)
204 string subscriberKey = subscriber.Attributes["id"];
206 if (subscriberKey == null || subscriberKey.Length == 0)
208 throw new EventWiringException("The subscriber node must have a valid Id assigned");
211 return subscriberKey;
214 private bool IsNotPublishingEvents(ComponentModel model)
216 return (model.Configuration == null) || (model.Configuration.Children["subscribers"] == null);
219 #endregion
221 #region OnComponentCreated
223 /// <summary>
224 /// Checks if the component we're dealing is a publisher. If it is,
225 /// iterates the subscribers starting them and wiring the events.
226 /// </summary>
227 /// <param name="model">The component model.</param>
228 /// <param name="instance">The instance representing the component.</param>
229 /// <exception cref="EventWiringException">When the subscriber is not found
230 /// <br /> or <br/>
231 /// The handler method isn't found
232 /// <br /> or <br/>
233 /// The event isn't found
234 /// </exception>
235 private void OnComponentCreated(ComponentModel model, object instance)
237 if (IsPublisher(model))
239 WirePublisher(model, instance);
243 private void WirePublisher(ComponentModel model, object publisher)
245 StartAndWirePublisherSubscribers(model, publisher);
248 private bool IsPublisher(ComponentModel model)
250 return model.ExtendedProperties[SubscriberList] != null;
253 private void StartAndWirePublisherSubscribers(ComponentModel model, object publisher)
255 IDictionary subscribers = (IDictionary)model.ExtendedProperties[SubscriberList];
257 if (subscribers == null) return;
259 foreach (DictionaryEntry subscriberInfo in subscribers)
261 string subscriberKey = (string) subscriberInfo.Key;
263 IList wireInfoList = (IList) subscriberInfo.Value;
265 IHandler handler = Kernel.GetHandler(subscriberKey);
267 AssertValidHandler(handler, subscriberKey);
269 object subscriberInstance;
273 //subscriberInstance = handler.Resolve(CreationContext.Empty);
274 subscriberInstance = Kernel[subscriberKey];
276 catch (Exception ex)
278 throw new EventWiringException("Failed to start subscriber " + subscriberKey, ex);
281 Type publisherType = model.Implementation;
283 foreach (WireInfo wireInfo in wireInfoList)
285 String eventName = wireInfo.EventName;
287 //TODO: Caching of EventInfos.
288 EventInfo eventInfo = publisherType.GetEvent(eventName,
289 BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
291 if (eventInfo == null)
293 throw new EventWiringException("Could not find event on publisher. Event " +
294 eventName + " Publisher " + publisherType.FullName);
297 MethodInfo handlerMethod = subscriberInstance.GetType().GetMethod(wireInfo.Handler,
298 BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
300 if (handlerMethod == null)
302 throw new EventWiringException("Could not find the method '" + wireInfo.Handler +
303 "' to handle the event " + eventName + ". Subscriber "
304 + subscriberInstance.GetType().FullName);
307 Delegate delegateHandler = Delegate.CreateDelegate(eventInfo.EventHandlerType,
308 subscriberInstance, wireInfo.Handler);
310 eventInfo.AddEventHandler(publisher, delegateHandler);
315 private static void AssertValidHandler(IHandler handler, string subscriberKey)
317 if (handler == null)
319 throw new EventWiringException("Publisher tried to start subscriber " + subscriberKey + " that was not found");
322 if (handler.CurrentState == HandlerState.WaitingDependency)
324 throw new EventWiringException("Publisher tried to start subscriber " + subscriberKey + " that is waiting for a dependency");
328 #endregion
330 private void OnComponentDestroyed(ComponentModel model, object instance)
332 // TODO: Remove Listener
336 /// <summary>
337 /// Represents the information about an event.
338 /// </summary>
339 internal class WireInfo
341 private String eventName;
343 private String handler;
345 /// <summary>
346 /// Initializes a new instance of the <see cref="WireInfo"/> class.
347 /// </summary>
348 /// <param name="eventName">Name of the event.</param>
349 /// <param name="handler">The name of the handler method.</param>
350 public WireInfo(string eventName, string handler)
352 this.eventName = eventName;
353 this.handler = handler;
356 /// <summary>
357 /// Gets the name of the event.
358 /// </summary>
359 /// <value>The name of the event.</value>
360 public string EventName
362 get { return eventName; }
365 /// <summary>
366 /// Gets the handler method name.
367 /// </summary>
368 /// <value>The handler.</value>
369 public string Handler
371 get { return handler; }
374 /// <summary>
375 /// Serves as a hash function for a particular type.
376 /// </summary>
377 /// <returns>
378 /// A hash code for the current <see cref="T:System.Object"></see>.
379 /// </returns>
380 public override int GetHashCode()
382 return eventName.GetHashCode() + 29 * handler.GetHashCode();
385 /// <summary>
386 /// Determines whether the specified <see cref="T:System.Object"></see> is equal to the current <see cref="T:System.Object"></see>.
387 /// </summary>
388 /// <param name="obj">The <see cref="T:System.Object"></see> to compare with the current <see cref="T:System.Object"></see>.</param>
389 /// <returns>
390 /// true if the specified <see cref="T:System.Object"></see> is equal to the current <see cref="T:System.Object"></see>; otherwise, false.
391 /// </returns>
392 public override bool Equals(object obj)
394 if (this == obj)
396 return true;
399 WireInfo wireInfo = obj as WireInfo;
400 if (wireInfo == null)
402 return false;
405 if (!Equals(eventName, wireInfo.eventName))
407 return false;
410 if (!Equals(handler, wireInfo.handler))
412 return false;
415 return true;