1 /*
2 Copyright 2008 Ramon Servadei
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16 package fulmine.model.container.subscription;
17
18 import static fulmine.util.Utils.logException;
19
20 import java.util.Collection;
21 import java.util.Map;
22
23 import fulmine.IAddressable;
24 import fulmine.context.IFrameworkContext;
25 import fulmine.event.IEventSource;
26 import fulmine.event.ImageEvent;
27 import fulmine.event.listener.AbstractEventHandler;
28 import fulmine.event.listener.IEventListener;
29 import fulmine.event.listener.ILifeCycleEventListener;
30 import fulmine.event.listener.MultiSystemEventListener;
31 import fulmine.event.subscription.ISubscription;
32 import fulmine.event.subscription.ISubscriptionFactory;
33 import fulmine.event.subscription.ISubscriptionManager;
34 import fulmine.event.subscription.ISubscriptionParameters;
35 import fulmine.event.subscription.SubscriptionManager;
36 import fulmine.event.system.ISystemEventListener;
37 import fulmine.model.container.IContainer;
38 import fulmine.model.container.events.ContainerCreatedEvent;
39 import fulmine.model.container.events.ContainerDestroyedEvent;
40 import fulmine.protocol.specification.FrameReader;
41 import fulmine.util.log.AsyncLog;
42
43 /**
44 * Standard implementation of an {@link ISubscriptionManager} for
45 * {@link IContainer} subscriptions. This is thread safe.
46 * <p>
47 * Registers for the following system events:
48 * <ul>
49 * <li>{@link ContainerCreatedEvent}
50 * <li>{@link ContainerDestroyedEvent}
51 * </ul>
52 *
53 * @see ContainerSubscription
54 * @author Ramon Servadei
55 */
56 public class ContainerSubscriptionManager extends SubscriptionManager
57 {
58 /**
59 * Handles {@link ContainerCreatedEvent} events
60 *
61 * @author Ramon Servadei
62 */
63 private class ContainerCreatedEventHandler extends
64 AbstractEventHandler<ContainerCreatedEvent> implements
65 ISystemEventListener
66 {
67 @Override
68 public AsyncLog getLog()
69 {
70 return LOG;
71 }
72
73 @Override
74 public void handle(ContainerCreatedEvent event)
75 {
76 eventSourceCreated(event);
77 }
78 }
79
80 /**
81 * Handles {@link ContainerDestroyedEvent} events
82 *
83 * @author Ramon Servadei
84 */
85 private class ContainerDestroyedEventHandler extends
86 AbstractEventHandler<ContainerDestroyedEvent> implements
87 ISystemEventListener
88 {
89 @Override
90 public AsyncLog getLog()
91 {
92 return LOG;
93 }
94
95 @Override
96 public void handle(ContainerDestroyedEvent event)
97 {
98 eventSourceDestroyed(event);
99 }
100 }
101
102 private final static AsyncLog LOG =
103 new AsyncLog(ContainerSubscriptionManager.class);
104
105 protected ILifeCycleEventListener eventHandler;
106
107 /**
108 * Standard constructor
109 *
110 * @param context
111 * the context this manager is associated with
112 */
113 public ContainerSubscriptionManager(IFrameworkContext context)
114 {
115 this(context, null);
116 }
117
118 /**
119 * Standard constructor
120 *
121 * @param context
122 * the context this manager is associated with
123 * @param factory
124 * the container subscription factory to use
125 */
126 @SuppressWarnings("unchecked")
127 public ContainerSubscriptionManager(IFrameworkContext context,
128 ISubscriptionFactory factory)
129 {
130 super(context, (factory == null ? new ContainerSubscriptionFactory()
131 : factory));
132 this.eventHandler =
133 new MultiSystemEventListener(this.getClass().getSimpleName(),
134 context, AbstractEventHandler.getEventHandlerMappings(
135 new ContainerCreatedEventHandler(),
136 new ContainerDestroyedEventHandler()));
137 }
138
139 @Override
140 protected final void doEventSourceCreated(IAddressable identity)
141 {
142 // trigger an image to be generated for the newly created container...
143 final IContainer container = (IContainer) identity;
144 container.addEvent(new ImageEvent());
145 if (!container.isFrameActive())
146 {
147 container.flushFrame();
148 }
149 }
150
151 @Override
152 protected void doAddListener(ISubscriptionParameters parameters,
153 IEventListener listener)
154 {
155 final Collection<IEventSource> matches =
156 getSubscribedSources(parameters);
157 if (matches.size() > 0)
158 {
159 /*
160 * This is not pretty - we need to masquerade as being in a
161 * FrameReader context to ensure we can bypass the usual checks that
162 * prevent remote containers flushed - this happens when a duplicate
163 * subscription for a remote container occurs and we just want to
164 * flush the available remote one to re-trigger notifications.
165 */
166 try
167 {
168 FrameReader.inContext.set(Boolean.TRUE);
169 for (IEventSource source : matches)
170 {
171 try
172 {
173 IContainer container = (IContainer) source;
174 /*
175 * On adding, we need to trigger an image for an update,
176 * this affects all listeners unfortunately. If we don't
177 * do this and try to provide the image to the new
178 * listener in this thread, we break some assumptions
179 * about a source only being serviced by a fixed, single
180 * thread.
181 */
182 container.addEvent(new ImageEvent());
183 if (!container.isFrameActive())
184 {
185 container.flushFrame();
186 }
187 }
188 catch (Exception e)
189 {
190 logException(getLog(), source, e);
191 }
192 }
193 }
194 finally
195 {
196 FrameReader.inContext.remove();
197 }
198 }
199 }
200
201 /**
202 * Template method to get the {@link IEventSource} identified by the
203 * {@link IAddressable} argument.
204 *
205 * @param id
206 * identifies the event source
207 * @return the {@link IEventSource} implementation to use
208 */
209 @Override
210 protected IEventSource doGetEventSource(IAddressable id)
211 {
212 return getContext().getLocalContainer(id.getIdentity(), id.getType(),
213 id.getDomain());
214 }
215
216 /**
217 * Template method to identify if the {@link IEventSource} identified by the
218 * {@link IAddressable} argument exists.
219 *
220 * @param id
221 * identifies the event source
222 *
223 * @return <code>true</code> if the container exists in the current context
224 * scope
225 */
226 @Override
227 protected boolean doEventSourceExists(IAddressable id)
228 {
229 return getContext().containsLocalContainer(id.getIdentity(),
230 id.getType(), id.getDomain());
231 }
232
233 @Override
234 protected final void doStart()
235 {
236 this.eventHandler.start();
237 }
238
239 @Override
240 protected void doDestroy()
241 {
242 super.doDestroy();
243 this.eventHandler.destroy();
244 }
245
246 @Override
247 protected AsyncLog getLog()
248 {
249 return LOG;
250 }
251
252 @Override
253 protected Collection<? extends IEventSource> doGetEventSources()
254 {
255 return getContext().getLocalContainers();
256 }
257
258 /**
259 * Nasty package scoped access to the underlying subscriptions. Needed for
260 * unit tests to setup state. <b>Do not use this in application code, there
261 * are thread safety issues.</b>
262 *
263 * @return as per {@link #getSubscriptions()}
264 */
265 final Map<ISubscriptionParameters, ISubscription> _getSubscriptions()
266 {
267 return getSubscriptions();
268 }
269 }