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