AbstractController.java
001 /*
002  *  Copyright (c) 1995-2012, The University of Sheffield. See the file
003  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
004  *
005  *  This file is part of GATE (see http://gate.ac.uk/), and is free
006  *  software, licenced under the GNU Library General Public License,
007  *  Version 2, June 1991 (in the distribution as file licence.html,
008  *  and also available at http://gate.ac.uk/gate/licence.html).
009  *
010  *  Valentin Tablan 27 Sep 2001
011  *
012  *  $I$
013  */
014 package gate.creole;
015 
016 import gate.Controller;
017 import gate.Gate;
018 import gate.ProcessingResource;
019 import gate.Resource;
020 import gate.creole.metadata.CreoleResource;
021 import gate.event.ControllerEvent;
022 import gate.event.ControllerListener;
023 import gate.event.ProgressListener;
024 import gate.event.StatusListener;
025 import gate.util.Benchmark;
026 import gate.util.Benchmarkable;
027 
028 import java.lang.reflect.UndeclaredThrowableException;
029 import java.util.ArrayList;
030 import java.util.Collection;
031 import java.util.Collections;
032 import java.util.HashMap;
033 import java.util.HashSet;
034 import java.util.Iterator;
035 import java.util.List;
036 import java.util.Map;
037 import java.util.Set;
038 import java.util.Vector;
039 
040 @CreoleResource(icon = "application")
041 public abstract class AbstractController extends AbstractResource 
042        implements Controller, ProcessingResource, Benchmarkable {
043 
044   private static final long serialVersionUID = 6466829205468662382L;
045 
046   /**
047    * Benchmark ID of this resource.
048    */
049   protected String benchmarkID;
050 
051   /**
052    * Shared featureMap
053    */
054   protected Map<Object,Object> benchmarkFeatures = new HashMap<Object,Object>();
055 
056   // executable code
057   /**
058    * Execute this controller. This implementation takes care of informing any
059    {@link ControllerAwarePR}s of the start and end of execution, and
060    * delegates to the {@link #executeImpl()} method to do the real work.
061    * Subclasses should override {@link #executeImpl()} rather than this method.
062    */
063   @Override
064   public void execute() throws ExecutionException {
065 
066     // inform ControllerAware PRs that execution has started, if automatic callbacks are enabled
067     if(controllerCallbacksEnabled) { 
068       invokeControllerExecutionStarted();
069     }
070     Throwable thrown = null;
071     try {
072       if(Benchmark.isBenchmarkingEnabled()) {
073         // write a start marker to the benchmark log for this
074         // controller as a whole
075         Benchmark.startPoint(getBenchmarkId());
076       }
077       // do the real work
078       this.executeImpl();
079     }
080     catch(Throwable t) {
081       thrown = t;
082     }
083     finally {
084       if(thrown == null) {
085         // successfully completed
086         if(controllerCallbacksEnabled) {
087           invokeControllerExecutionFinished();
088         }
089       }
090       else {
091         // aborted
092         if(controllerCallbacksEnabled) {
093           invokeControllerExecutionAborted(thrown);
094         }
095 
096         // rethrow the aborting exception or error
097         if(thrown instanceof Error) {
098           throw (Error)thrown;
099         }
100         else if(thrown instanceof RuntimeException) {
101           throw (RuntimeException)thrown;
102         }
103         else if(thrown instanceof ExecutionException) {
104           throw (ExecutionException)thrown;
105         }
106         else {
107           // we have a checked exception that isn't one executeImpl can
108           // throw. This shouldn't be possible, but just in case...
109           throw new UndeclaredThrowableException(thrown);
110         }
111       }
112     }
113 
114   }
115 
116   /**
117    * Get the set of PRs from this controller that implement
118    {@link ControllerAwarePR}. If there are no such PRs in this controller, an
119    * empty set is returned. This implementation simply filters the collection
120    * returned by {@link Controller#getPRs()}, override this method if your
121    * subclass admits a more efficient implementation.
122    */
123   protected Set<ControllerAwarePR> getControllerAwarePRs() {
124     Set<ControllerAwarePR> returnSet = null;
125     for(Object pr : getPRs()) {
126       if(pr instanceof ControllerAwarePR) {
127         if(returnSet == null) {
128           returnSet = new HashSet<ControllerAwarePR>();
129         }
130         returnSet.add((ControllerAwarePR)pr);
131       }
132     }
133 
134     if(returnSet == null) {
135       // optimization - don't waste time creating a new set in the most
136       // common case where there are no Controller aware PRs
137       return Collections.emptySet();
138     }
139     else {
140       return returnSet;
141     }
142   }
143 
144   /**
145    * Executes the PRs in this controller, according to the execution strategy of
146    * the particular controller type (simple pipeline, parallel execution,
147    * once-per-document in a corpus, etc.). Subclasses should override this
148    * method, allowing the default {@link #execute()} method to handle sending
149    * notifications to controller aware PRs.
150    */
151   protected void executeImpl() throws ExecutionException {
152     throw new ExecutionException("Controller " + getClass()
153       " hasn't overriden the executeImpl() method");
154   }
155 
156   /** Initialise this resource, and return it. */
157   @Override
158   public Resource init() throws ResourceInstantiationException {
159     return this;
160   }
161 
162   /* (non-Javadoc)
163    * @see gate.ProcessingResource#reInit()
164    */
165   @Override
166   public void reInit() throws ResourceInstantiationException {
167     init();
168   }
169 
170   /** Clears the internal data of the resource, when it gets released * */
171   @Override
172   public void cleanup() {
173   }
174 
175   /**
176    * Populates this controller from a collection of {@link ProcessingResource}s
177    * (optional operation).
178    
179    * Controllers that are serializable must implement this method needed by GATE
180    * to restore their contents.
181    
182    @throws UnsupportedOperationException
183    *           if the <tt>setPRs</tt> method is not supported by this
184    *           controller.
185    */
186   @Override
187   public void setPRs(Collection<? extends ProcessingResource> PRs) {
188   }
189 
190   /**
191    * Notifies all the PRs in this controller that they should stop their
192    * execution as soon as possible.
193    */
194   @Override
195   public synchronized void interrupt() {
196     interrupted = true;
197     Iterator<ProcessingResource> prIter = getPRs().iterator();
198     while(prIter.hasNext()) {
199       prIter.next().interrupt();
200     }
201   }
202 
203   @Override
204   public synchronized boolean isInterrupted() {
205     return interrupted;
206   }
207 
208   // events code
209   /**
210    * Removes a {@link gate.event.StatusListener} from the list of listeners for
211    * this processing resource
212    */
213   public synchronized void removeStatusListener(StatusListener l) {
214     if(statusListeners != null && statusListeners.contains(l)) {
215       @SuppressWarnings("unchecked")
216       Vector<StatusListener> v = (Vector<StatusListener>)statusListeners.clone();
217       v.removeElement(l);
218       statusListeners = v;
219     }
220   }
221 
222   /**
223    * Adds a {@link gate.event.StatusListener} to the list of listeners for this
224    * processing resource
225    */
226   public synchronized void addStatusListener(StatusListener l) {
227     @SuppressWarnings("unchecked")
228     Vector<StatusListener> v =
229       statusListeners == null new Vector<StatusListener>(2(Vector<StatusListener>)statusListeners.clone();
230     if(!v.contains(l)) {
231       v.addElement(l);
232       statusListeners = v;
233     }
234   }
235 
236   /**
237    * Notifies all the {@link gate.event.StatusListener}s of a change of status.
238    
239    @param e
240    *          the message describing the status change
241    */
242   protected void fireStatusChanged(String e) {
243     if(statusListeners != null) {
244       Vector<StatusListener> listeners = statusListeners;
245       int count = listeners.size();
246       for(int i = 0; i < count; i++) {
247         listeners.elementAt(i).statusChanged(e);
248       }
249     }
250   }
251 
252   /**
253    * Adds a {@link gate.event.ProgressListener} to the list of listeners for
254    * this processing resource.
255    */
256   public synchronized void addProgressListener(ProgressListener l) {
257     @SuppressWarnings("unchecked")
258     Vector<ProgressListener> v =
259       progressListeners == null new Vector<ProgressListener>(2(Vector<ProgressListener>)progressListeners
260         .clone();
261     if(!v.contains(l)) {
262       v.addElement(l);
263       progressListeners = v;
264     }
265   }
266 
267   /**
268    * Removes a {@link gate.event.ProgressListener} from the list of listeners
269    * for this processing resource.
270    */
271   public synchronized void removeProgressListener(ProgressListener l) {
272     if(progressListeners != null && progressListeners.contains(l)) {
273       @SuppressWarnings("unchecked")
274       Vector<ProgressListener> v = (Vector<ProgressListener>)progressListeners.clone();
275       v.removeElement(l);
276       progressListeners = v;
277     }
278   }
279 
280   /**
281    * Notifies all the {@link gate.event.ProgressListener}s of a progress change
282    * event.
283    
284    @param e
285    *          the new value of execution completion
286    */
287   protected void fireProgressChanged(int e) {
288     if(progressListeners != null) {
289       Vector<ProgressListener> listeners = progressListeners;
290       int count = listeners.size();
291       for(int i = 0; i < count; i++) {
292         listeners.elementAt(i).progressChanged(e);
293       }
294     }
295   }
296 
297   /**
298    * Notifies all the {@link gate.event.ProgressListener}s of a progress
299    * finished.
300    */
301   protected void fireProcessFinished() {
302     if(progressListeners != null) {
303       Vector<ProgressListener> listeners = progressListeners;
304       int count = listeners.size();
305       for(int i = 0; i < count; i++) {
306         listeners.elementAt(i).processFinished();
307       }
308     }
309   }
310 
311   /**
312    * A progress listener used to convert a 0..100 interval into a smaller one
313    */
314   protected class IntervalProgressListener implements ProgressListener {
315     public IntervalProgressListener(int start, int end) {
316       this.start = start;
317       this.end = end;
318     }
319 
320     @Override
321     public void progressChanged(int i) {
322       fireProgressChanged(start + (end - start* i / 100);
323     }
324 
325     @Override
326     public void processFinished() {
327       fireProgressChanged(end);
328     }
329 
330     int start;
331     int end;
332   }// CustomProgressListener
333 
334   /**
335    * A simple status listener used to forward the events upstream.
336    */
337   protected class InternalStatusListener implements StatusListener {
338     @Override
339     public void statusChanged(String message) {
340       fireStatusChanged(message);
341     }
342   }
343 
344   /**
345    * Checks whether all the contained PRs have all the required runtime
346    * parameters set.
347    
348    @return {@link List} of {@link ProcessingResource}s that have required
349    *         parameters with null values if they exist <tt>null</tt>
350    *         otherwise.
351    @throws {@link ResourceInstantiationException}
352    *           if problems occur while inspecting the parameters for one of the
353    *           resources. These will normally be introspection problems and are
354    *           usually caused by the lack of a parameter or of the read accessor
355    *           for a parameter.
356    */
357   public List<ProcessingResource> getOffendingPocessingResources()
358     throws ResourceInstantiationException {
359     // take all the contained PRs
360     List<ProcessingResource> badPRs = new ArrayList<ProcessingResource>(getPRs());
361     // remove the ones that no parameters problems
362     Iterator<ProcessingResource> prIter = getPRs().iterator();
363     while(prIter.hasNext()) {
364       ProcessingResource pr = prIter.next();
365       ResourceData rData =
366         Gate.getCreoleRegister().get(pr.getClass().getName());
367       if(AbstractResource.checkParameterValues(pr, rData.getParameterList()
368         .getRuntimeParameters())) {
369         badPRs.remove(pr);
370       }
371     }
372     return badPRs.isEmpty() null : badPRs;
373   }
374 
375   public synchronized void removeControllerListener(ControllerListener l) {
376     if(controllerListeners != null && controllerListeners.contains(l)) {
377       @SuppressWarnings("unchecked")
378       Vector<ControllerListener> v = (Vector<ControllerListener>)controllerListeners.clone();
379       v.removeElement(l);
380       controllerListeners = v;
381     }
382   }
383 
384   public synchronized void addControllerListener(ControllerListener l) {
385     @SuppressWarnings("unchecked")
386     Vector<ControllerListener> v =
387       controllerListeners == null new Vector<ControllerListener>(2(Vector<ControllerListener>)controllerListeners
388         .clone();
389     if(!v.contains(l)) {
390       v.addElement(l);
391       controllerListeners = v;
392     }
393   }
394 
395   /**
396    * The list of {@link gate.event.StatusListener}s registered with this
397    * resource
398    */
399   private transient Vector<StatusListener> statusListeners;
400 
401   /**
402    * The list of {@link gate.event.ProgressListener}s registered with this
403    * resource
404    */
405   private transient Vector<ProgressListener> progressListeners;
406 
407   /**
408    * The list of {@link gate.event.ControllerListener}s registered with this
409    * resource
410    */
411   private transient Vector<ControllerListener> controllerListeners;
412 
413   protected boolean interrupted = false;
414 
415   protected void fireResourceAdded(ControllerEvent e) {
416     if(controllerListeners != null) {
417       Vector<ControllerListener> listeners = controllerListeners;
418       int count = listeners.size();
419       for(int i = 0; i < count; i++) {
420         listeners.elementAt(i).resourceAdded(e);
421       }
422     }
423   }
424 
425   protected void fireResourceRemoved(ControllerEvent e) {
426     if(controllerListeners != null) {
427       Vector<ControllerListener> listeners = controllerListeners;
428       int count = listeners.size();
429       for(int i = 0; i < count; i++) {
430         listeners.elementAt(i).resourceRemoved(e);
431       }
432     }
433   }
434   
435   /**
436    * Sets the benchmark ID of this controller.
437    */
438   @Override
439   public void setBenchmarkId(String benchmarkID) {
440     this.benchmarkID = benchmarkID;
441   }
442 
443   /**
444    * Returns the benchmark ID of this controller.
445    */
446   @Override
447   public String getBenchmarkId() {
448     if(benchmarkID == null) {
449       benchmarkID = getName().replaceAll("[ ]+""_");
450     }
451     return benchmarkID;
452   }
453   
454    /**
455    * Invoke the controllerExecutionStarted method on this controller and all nested PRs and controllers. 
456    * This method is intended to be used after if the automatic invocation of the controller
457    * callback methods has been disabled with a call setControllerCallbackEnabled(false).  Normally
458    * the callback methods are automatically invoked at the start and end of execute().  
459    @throws ExecutionException 
460    */
461   public void invokeControllerExecutionStarted() throws ExecutionException {
462     for (ControllerAwarePR pr : getControllerAwarePRs()) {
463       pr.controllerExecutionStarted(this);
464     }
465   }
466 
467    /**
468    * Invoke the controllerExecutionFinished method on this controller and all nested PRs and controllers. 
469    * This method is intended to be used after if the automatic invocation of the controller
470    * callback methods has been disabled with a call setControllerCallbackEnabled(false).  Normally
471    * the callback methods are automatically invoked at the start and end of execute().  
472    @throws ExecutionException 
473    */
474   public void invokeControllerExecutionFinished() throws ExecutionException {
475     for (ControllerAwarePR pr : getControllerAwarePRs()) {
476       pr.controllerExecutionFinished(this);
477     }
478   }
479   
480    /**
481    * Invoke the controllerExecutionAborted method on this controller and all nested PRs and controllers. 
482    * This method is intended to be used after if the automatic invocation of the controller
483    * callback methods has been disabled with a call setControllerCallbackEnabled(false).  Normally
484    * the callback methods are automatically invoked at the start and end of execute().  
485    @throws ExecutionException 
486    */
487   public void invokeControllerExecutionAborted(Throwable thrownthrows ExecutionException {
488     for (ControllerAwarePR pr : getControllerAwarePRs()) {
489       pr.controllerExecutionAborted(this, thrown);
490     }
491   }
492     
493   protected boolean controllerCallbacksEnabled = true;  
494   /**
495    * Enable or disable the automatic invocation of the controller callbacks. 
496    * By default, the controller calls the controllerExecutionStarted method of each controllerAwarePR
497    * at the start of execute(), the controllerExecutionFinished method of each controllerAwarePR
498    * at the end of execute() or the controllerExecutionAborted method of each controllerAwarePR if
499    * there was an exception during execute(). If this method is called with the parameter false
500    * before execute() is called, then those controllerAwarePR methods will not get called automatically.
501    * In that case they can invoked deliberately using the invokeControllerExecutionStarted(), 
502    * invokeControllerExecutionFinished() and controllerExecutionAborted() methods.
503    
504    @param flag a boolean indicating if the callbacks should be enabled (true) or disabled (false)
505    */
506   public void setControllerCallbacksEnabled(boolean flag) {
507     controllerCallbacksEnabled = flag;
508   }
509     
510     
511   
512   
513   
514   
515   
516 }