View Javadoc
1   /*
2    Copyright (C) 2006 Grid Systems, S.A.
3   
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8   
9    This library is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   Lesser General Public License for more details.
13  
14   You should have received a copy of the GNU Lesser General Public
15   License along with this library; if not, write to the Free Software
16   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
17  */
18  package com.gridsystems.nextgrid.api.pom;
19  
20  import java.lang.reflect.Method;
21  import java.util.LinkedHashSet;
22  import java.util.Set;
23  import java.util.concurrent.locks.ReentrantReadWriteLock;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  
28  import nextgrid.api.pem.DiscoveryEvent;
29  import nextgrid.api.pem.DiscoveryListener;
30  import nextgrid.api.pem.POMListener;
31  import nextgrid.api.pem.ProcessEvent;
32  import nextgrid.api.pem.ProcessListener;
33  import nextgrid.api.pom.Process;
34  import nextgrid.api.pom.ProcessException;
35  
36  /**
37   * Base class for ProcessImpl.
38   * <p>
39   * Provides functionality for the management of Process Event Model listeners,
40   * and notification of events.
41   *
42   * @author Rodrigo Ruiz
43   */
44  public abstract class PemHelper implements Process {
45  
46    /**
47     * Class logger.
48     */
49    private static final Log LOG = LogFactory.getLog("ENACTOR");
50  
51    /**
52     * Listeners associated to this process.
53     */
54    private Set<POMListener> listeners = new LinkedHashSet<POMListener>();
55  
56    /**
57     * Lock used to control access to the listeners set.
58     */
59    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
60  
61    /**
62     * {@inheritDoc}
63     */
64    public Process copy() {
65      try {
66        PemHelper copy = (PemHelper)this.clone();
67        copy.listeners = new LinkedHashSet<POMListener>();
68        copy.lock = new ReentrantReadWriteLock(true);
69  
70        return copy;
71      } catch (CloneNotSupportedException e) {
72        throw new InternalError("Unexpected CloneNotSupported exception");
73      }
74    }
75  
76    /**
77     * Adds a listener to the list.
78     *
79     * @param listener The one to add
80     */
81    public final void addListener(POMListener listener) {
82      if (listener == null) {
83        return;
84      }
85  
86      lock.writeLock().lock();
87      try {
88        this.listeners.add(listener);
89      } finally {
90        lock.writeLock().unlock();
91      }
92    }
93  
94    /**
95     * Removes a listener from the list.
96     *
97     * @param listener The one to remove
98     */
99    public final void removeListener(POMListener listener) {
100     if (listener == null) {
101       return;
102     }
103 
104     lock.writeLock().lock();
105     try {
106       this.listeners.remove(listener);
107     } finally {
108       lock.writeLock().unlock();
109     }
110   }
111 
112   /**
113    * Notifies a discovererSelected event.
114    *
115    * @param event The event to notify
116    */
117   public final void fireDiscovererSelected(DiscoveryEvent event) {
118     fireEvent(DiscoveryListener.class, "discovererSelected", event);
119   }
120 
121   /**
122    * Notifies a discoveryStarting event.
123    *
124    * @param event The event to notify
125    */
126   public final void fireDiscoveryStarting(DiscoveryEvent event) {
127     fireEvent(DiscoveryListener.class, "discoveryStarting", event);
128   }
129 
130   /**
131    * Notifies a discoveryFinished event.
132    *
133    * @param event The event to notify
134    */
135   public final void fireDiscoveryFinished(DiscoveryEvent event) {
136     fireEvent(DiscoveryListener.class, "discoveryFinished", event);
137   }
138 
139   /**
140    * Notifies a discoveryFailed event.
141    *
142    * @param event The event to notify
143    */
144   public final void fireDiscoveryFailed(DiscoveryEvent event) {
145     fireEvent(DiscoveryListener.class, "discoveryFailed", event);
146   }
147 
148   /**
149    * Notifies a processSelected event.
150    *
151    * @param event The event to notify
152    */
153   public final void fireProcessSelected(ProcessEvent event) {
154     fireEvent(ProcessListener.class, "processSelected", event);
155   }
156 
157   /**
158    * Notifies a processFinished event.
159    *
160    * @param event The event to notify
161    */
162   public final void fireProcessFinished(ProcessEvent event) {
163     fireEvent(ProcessListener.class, "processFinished", event);
164   }
165 
166   /**
167    * Notifies a processFailed event.
168    *
169    * @param event The event to notify
170    */
171   public final void fireProcessFailed(ProcessEvent event) {
172     fireEvent(ProcessListener.class, "processFailed", event);
173   }
174 
175   /**
176    * Notifies a processFinished event.
177    */
178   public final void fireProcessFinished() {
179     fireProcessFinished(new ProcessEvent(this));
180   }
181 
182   /**
183    * Notifies a processEvaluated event.
184    */
185   public final void fireProcessEvaluated() {
186     fireEvent(ProcessListener.class, "processEvaluated", new ProcessEvent(this));
187   }
188 
189   /**
190    * Notifies a processStarted event.
191    */
192   public final void fireProcessStarted() {
193     fireEvent(ProcessListener.class, "processStarted", new ProcessEvent(this));
194   }
195 
196   /**
197    * Notifies a processFailed event.
198    *
199    * @param e The exception
200    */
201   public final void fireProcessFailed(ProcessException e) {
202     ProcessEvent event = new ProcessEvent(this);
203     event.setException(e);
204     fireProcessFailed(event);
205   }
206 
207   /**
208    * Fires an event on all listeners using reflection. This method notifies
209    * all listeners registered on this process, and its ancestors.
210    *
211    * @param iface     The listener interface to notify
212    * @param method    The name of the method to invoke
213    * @param event     The event to pass as parameter
214    */
215   protected final void fireEvent(Class<?> iface, String method, Object event) {
216     // Parameter validation
217     if (iface == null || method == null || event == null) {
218       return;
219     }
220     fireEvent(iface, method, event, true);
221   }
222 
223   /**
224    * Fires an event on all listeners using reflection. This method notifies
225    * all listeners registered on this process, and its ancestors.
226    * <p>
227    * This method is for internal use, and should never be called directly.
228    * Programmers should use {@link #fireEvent(Class, String, Object)} instead.
229    *
230    * @param iface     The listener interface to notify
231    * @param method    The name of the method to invoke
232    * @param event     The event to pass as parameter
233    * @param direct    Used internally to specify that the event
234    *                  is related to a child process.
235    */
236   @SuppressWarnings("unchecked")
237   private void fireEvent(Class iface, String method, Object event,
238     boolean direct) {
239 
240     // Get the method to invoke
241     Method m;
242     try {
243       m = iface.getMethod(method, event.getClass());
244     } catch (SecurityException e) {
245       LOG.fatal("Security policy prevents access to listener", e);
246       throw new RuntimeException(e);
247     } catch (NoSuchMethodException e) {
248       LOG.fatal("Method '" + method + "'not found in listener", e);
249       throw new RuntimeException("Method '" + method + "'not found in listener",
250         e);
251     }
252 
253     // Notify all listeners
254     lock.readLock().lock();
255     try {
256 
257       // Iterate over the "local" listeners
258       for (POMListener listener : listeners) {
259         if (direct || listener.isListeningToDescendants()) {
260           if (iface.isAssignableFrom(listener.getClass())) {
261             // The listener is of the correct type
262             m.invoke(listener, event);
263           }
264         }
265       }
266     } catch (Exception e) {
267       LOG.error("Unexpected exception", e);
268       throw new RuntimeException("Unexpected exception", e);
269     } finally {
270       lock.readLock().unlock();
271     }
272 
273     // Go on with the parent node listeners
274     Process parentProcess = this.getParent();
275     if (parentProcess instanceof ProcessImpl) {
276       ((PemHelper)parentProcess).fireEvent(iface, method, event, false);
277     }
278   }
279 
280 }