View Javadoc

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 }