KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > event > EventBridge


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20 package org.apache.cayenne.event;
21
22 import java.util.ArrayList JavaDoc;
23 import java.util.Collection JavaDoc;
24 import java.util.Collections JavaDoc;
25 import java.util.EventListener JavaDoc;
26 import java.util.HashSet JavaDoc;
27 import java.util.Iterator JavaDoc;
28
29 import org.apache.cayenne.util.Util;
30
31 /**
32  * An object that passes events between a local EventManager and some other event dispatch
33  * mechanism. The most common example is sending local events to remote JVMs and receiving
34  * remote events dispatched by those VMs. EventBridge makes possible to connect a network
35  * of regular EventManagers in a single "virtual" distributed EventManager.
36  * </p>
37  * <p>
38  * EventBridge encapsulates a transport agreed upon by all paries (such as JMS) and
39  * maintains an array of "local" subjects to communicate with local EventManager, and a
40  * single "remote" subject - to use for "external" communications that are
41  * transport-specific.
42  * <p>
43  * Subclasses that require special setup to listen for external events should implement
44  * <code>startupExternal()</code> method accordingly.
45  * </p>
46  * <p>
47  * This class is an example of <a
48  * HREF="http://en.wikipedia.org/wiki/Bridge_pattern">"bridge" design pattern</a>, hence
49  * the name.
50  * </p>
51  *
52  * @author Andrus Adamchik
53  * @since 1.1
54  */

55 // TODO Andrus, 10/15/2005 - potentially big inefficiency of concrete implementations of
56
// EventBridgeFactory is that all the expensive resources are managed by the bridge
57
// itself. Scaling to a big number of bridge instances would require resource pooling to
58
// be done by the factory singleton.
59
public abstract class EventBridge implements EventListener JavaDoc {
60
61     public static final int RECEIVE_LOCAL = 1;
62     public static final int RECEIVE_EXTERNAL = 2;
63     public static final int RECEIVE_LOCAL_EXTERNAL = 3;
64
65     protected String JavaDoc externalSubject;
66     protected Collection JavaDoc localSubjects;
67     protected EventManager eventManager;
68     protected int mode;
69
70     protected Object JavaDoc externalEventSource;
71
72     // keeps all listeners so that they are not deallocated
73
Collection JavaDoc listeners;
74
75     /**
76      * A utility method that performs consistent translation from an EventSubject to a
77      * String that can be used by external transport as subject for distributed
78      * communications. Substitutes all chars that can be incorrectly interpreted by
79      * whoever (JNDI, ...?).
80      */

81     public static String JavaDoc convertToExternalSubject(EventSubject localSubject) {
82         char[] chars = localSubject.getSubjectName().toCharArray();
83         for (int i = 0; i < chars.length; i++) {
84             if (chars[i] == '/' || chars[i] == '.') {
85                 chars[i] = '_';
86             }
87         }
88
89         return new String JavaDoc(chars);
90     }
91
92     /**
93      * Creates an EventBridge with a single local subject.
94      */

95     public EventBridge(EventSubject localSubject, String JavaDoc externalSubject) {
96         this(Collections.singleton(localSubject), externalSubject);
97     }
98
99     /**
100      * Creates an EventBridge with multiple local subjects and a single external subject.
101      *
102      * @since 1.2
103      */

104     public EventBridge(Collection JavaDoc localSubjects, String JavaDoc externalSubject) {
105         this.localSubjects = new HashSet JavaDoc(localSubjects);
106         this.externalSubject = externalSubject;
107     }
108
109     /**
110      * Returns a String subject used to post distributed events.
111      */

112     public String JavaDoc getExternalSubject() {
113         return externalSubject;
114     }
115
116     /**
117      * Returns true if this bridge is active.
118      *
119      * @since 1.2
120      */

121     public boolean isRunning() {
122         return eventManager != null;
123     }
124
125     /**
126      * Returns a Collection of local EventSubjects.
127      *
128      * @since 1.2
129      */

130     public Collection JavaDoc getLocalSubjects() {
131         return localSubjects;
132     }
133
134     /**
135      * Returns local EventManager used by the bridge. Returned value will be null before
136      * the bridge is started and after it is shutdown.
137      *
138      * @since 1.2
139      */

140     public EventManager getEventManager() {
141         return eventManager;
142     }
143
144     /**
145      * Returns an object used as a source of local events posted in response to remote
146      * events. If externalEventSource wasn't setup during bridge startup (or if the bridge
147      * is not started), returns this object.
148      *
149      * @since 1.2
150      */

151     public Object JavaDoc getExternalEventSource() {
152         return externalEventSource != null ? externalEventSource : this;
153     }
154
155     /**
156      * Returns true if the bridge is configured to receive local events from its internal
157      * EventManager.
158      */

159     public boolean receivesLocalEvents() {
160         return mode == RECEIVE_LOCAL_EXTERNAL || mode == RECEIVE_LOCAL;
161     }
162
163     /**
164      * Returns true if the bridge is configured to receive external events.
165      */

166     public boolean receivesExternalEvents() {
167         return mode == RECEIVE_LOCAL_EXTERNAL || mode == RECEIVE_EXTERNAL;
168     }
169
170     /**
171      * Starts EventBridge in the specified mode and locally listening to all event sources
172      * that post on a preconfigured subject. Remote events reposted locally will have this
173      * EventBridge as their source.
174      *
175      * @param eventManager EventManager used to send and receive local events.
176      * @param mode One of the possible modes of operation - RECEIVE_EXTERNAL,
177      * RECEIVE_LOCAL, RECEIVE_LOCAL_EXTERNAL.
178      */

179     public void startup(EventManager eventManager, int mode) throws Exception JavaDoc {
180         this.startup(eventManager, mode, null);
181     }
182
183     /**
184      * Starts EventBridge in the specified mode and locally listening to a specified event
185      * source. Remote events reposted locally will have this EventBridge as their source.
186      *
187      * @param eventManager EventManager used to send and receive local events.
188      * @param mode One of the possible modes of operation - RECEIVE_EXTERNAL,
189      * RECEIVE_LOCAL, RECEIVE_LOCAL_EXTERNAL.
190      * @param localEventSource If not null, only events originating from localEventSource
191      * object will be processed by this bridge.
192      */

193     public void startup(EventManager eventManager, int mode, Object JavaDoc localEventSource)
194             throws Exception JavaDoc {
195         startup(eventManager, mode, localEventSource, null);
196     }
197
198     /**
199      * Starts EventBridge in the specified mode.
200      *
201      * @param eventManager EventManager used to send and receive local events.
202      * @param mode One of the possible modes of operation - RECEIVE_EXTERNAL,
203      * RECEIVE_LOCAL, RECEIVE_LOCAL_EXTERNAL.
204      * @param localEventSource If not null, only events originating from localEventSource
205      * object will be processed by this bridge.
206      * @param remoteEventSource If not null, remoteEventSource object will be used as
207      * standby source of local events posted by this EventBridge in response to
208      * remote events.
209      * @since 1.2
210      */

211     public void startup(
212             EventManager eventManager,
213             int mode,
214             Object JavaDoc localEventSource,
215             Object JavaDoc remoteEventSource) throws Exception JavaDoc {
216
217         if (eventManager == null) {
218             throw new IllegalArgumentException JavaDoc("'eventManager' can't be null.");
219         }
220
221         // uninstall old event manager
222
if (this.eventManager != null) {
223             shutdown();
224         }
225
226         this.externalEventSource = remoteEventSource;
227         this.eventManager = eventManager;
228         this.mode = mode;
229
230         if (receivesLocalEvents() && !localSubjects.isEmpty()) {
231
232             listeners = new ArrayList JavaDoc(localSubjects.size());
233
234             Iterator JavaDoc it = localSubjects.iterator();
235             while (it.hasNext()) {
236
237                 EventSubject subject = (EventSubject) it.next();
238                 SubjectListener listener = new SubjectListener(subject);
239
240                 listeners.add(listener);
241                 eventManager.addNonBlockingListener(
242                         listener,
243                         "onLocalEvent",
244                         CayenneEvent.class,
245                         subject,
246                         localEventSource);
247             }
248         }
249
250         startupExternal();
251     }
252
253     /**
254      * Starts an external interface of the EventBridge.
255      */

256     protected abstract void startupExternal() throws Exception JavaDoc;
257
258     /**
259      * Stops listening for events on both local and external interfaces.
260      */

261     public void shutdown() throws Exception JavaDoc {
262
263         this.externalEventSource = null;
264
265         if (listeners != null && eventManager != null) {
266
267             Iterator JavaDoc it = listeners.iterator();
268             while (it.hasNext()) {
269                 SubjectListener listener = (SubjectListener) it.next();
270                 eventManager.removeListener(listener, listener.subject);
271             }
272
273             eventManager = null;
274             listeners = null;
275         }
276
277         shutdownExternal();
278     }
279
280     /**
281      * Shuts down the external interface of the EventBridge, cleaning up and releasing any
282      * resources used to communicate external events.
283      */

284     protected abstract void shutdownExternal() throws Exception JavaDoc;
285
286     /**
287      * Helper method intended to be called explicitly by subclasses to asynchronously post
288      * an event obtained from a remote source. Subclasses do not have to use this method,
289      * but they probably should for consistency.
290      */

291     protected void onExternalEvent(CayenneEvent event) {
292         if (eventManager != null) {
293
294             EventSubject localSubject = event.getSubject();
295
296             // check for valid subject
297
if (localSubject == null || !localSubjects.contains(localSubject)) {
298                 return;
299             }
300
301             event.setSource(getExternalEventSource());
302             event.setPostedBy(this);
303
304             // inject external eveny to the event manager queue.. leave it up to the
305
// listeners to figure out correct synchronization.
306
eventManager.postEvent(event, localSubject);
307         }
308         else {
309             throw new IllegalStateException JavaDoc(
310                     "Can't post events. EventBridge was not started properly. "
311                             + "EventManager is null.");
312         }
313     }
314
315     /**
316      * Sends a Cayenne event over the transport supported by this bridge.
317      */

318     protected abstract void sendExternalEvent(CayenneEvent localEvent) throws Exception JavaDoc;
319
320     final class SubjectListener {
321
322         EventSubject subject;
323
324         SubjectListener(EventSubject subject) {
325             this.subject = subject;
326         }
327
328         void onLocalEvent(CayenneEvent event) throws Exception JavaDoc {
329
330             // ignore events posted by this Bridge...
331
if (event.getSource() != getExternalEventSource()
332                     && event.getPostedBy() != EventBridge.this) {
333
334                 // make sure external event has the right subject, if not make a clone
335
// with the right one...
336
if (!subject.equals(event.getSubject())) {
337                     CayenneEvent clone = (CayenneEvent) Util.cloneViaSerialization(event);
338                     clone.setSubject(subject);
339                     clone.setPostedBy(event.getPostedBy());
340                     clone.setSource(event.getSource());
341
342                     event = clone;
343                 }
344
345                 sendExternalEvent(event);
346             }
347         }
348     }
349 }
350
Popular Tags