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
.EventWiring
18 using System
.Collections
;
19 using System
.Collections
.Specialized
;
20 using System
.Reflection
;
23 using Castle
.Core
.Configuration
;
25 using Castle
.MicroKernel
;
26 using Castle
.MicroKernel
.Facilities
;
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.
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.
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>
43 /// public class SimplePublisher
45 /// public event PublishEventHandler Event;
47 /// public void Trigger()
49 /// if (Event != null)
51 /// Event(this, new EventArgs());
56 /// <para>The Subscriber class:</para>
58 /// public class SimpleListener
60 /// private bool _listened;
61 /// private object _sender;
63 /// public void OnPublish(object sender, EventArgs e)
66 /// _listened = sender != null;
69 /// public bool Listened
71 /// get { return _listened; }
74 /// public object Sender
76 /// get { return _sender; }
80 /// <para>The configuration file:</para>
83 /// <?xml version="1.0" encoding="utf-8" ?>
88 /// type="Castle.Facilities.EventWiring.EventWiringFacility, Castle.MicroKernel" />
93 /// id="SimpleListener"
94 /// type="Castle.Facilities.EventWiring.Tests.Model.SimpleListener, Castle.Facilities.EventWiring.Tests" />
97 /// id="SimplePublisher"
98 /// type="Castle.Facilities.EventWiring.Tests.Model.SimplePublisher, Castle.Facilities.EventWiring.Tests" >
100 /// <subscriber id="SimpleListener" event="Event" handler="OnPublish"/>
108 public class EventWiringFacility
: AbstractFacility
110 private const string SubscriberList
= "evts.subscriber.list";
113 /// Overriden. Initializes the facility, subscribing to the <see cref="IKernelEvents.ComponentModelCreated"/>,
114 /// <see cref="IKernelEvents.ComponentCreated"/>, <see cref="IKernelEvents.ComponentDestroyed"/> Kernel events.
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
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.
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);
221 #region OnComponentCreated
224 /// Checks if the component we're dealing is a publisher. If it is,
225 /// iterates the subscribers starting them and wiring the events.
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
231 /// The handler method isn't found
233 /// The event isn't found
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
];
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
)
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");
330 private void OnComponentDestroyed(ComponentModel model
, object instance
)
332 // TODO: Remove Listener
337 /// Represents the information about an event.
339 internal class WireInfo
341 private String eventName
;
343 private String handler
;
346 /// Initializes a new instance of the <see cref="WireInfo"/> class.
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
;
357 /// Gets the name of the event.
359 /// <value>The name of the event.</value>
360 public string EventName
362 get { return eventName; }
366 /// Gets the handler method name.
368 /// <value>The handler.</value>
369 public string Handler
371 get { return handler; }
375 /// Serves as a hash function for a particular type.
378 /// A hash code for the current <see cref="T:System.Object"></see>.
380 public override int GetHashCode()
382 return eventName
.GetHashCode() + 29 * handler
.GetHashCode();
386 /// Determines whether the specified <see cref="T:System.Object"></see> is equal to the current <see cref="T:System.Object"></see>.
388 /// <param name="obj">The <see cref="T:System.Object"></see> to compare with the current <see cref="T:System.Object"></see>.</param>
390 /// true if the specified <see cref="T:System.Object"></see> is equal to the current <see cref="T:System.Object"></see>; otherwise, false.
392 public override bool Equals(object obj
)
399 WireInfo wireInfo
= obj
as WireInfo
;
400 if (wireInfo
== null)
405 if (!Equals(eventName
, wireInfo
.eventName
))
410 if (!Equals(handler
, wireInfo
.handler
))