SerialController.java
001 /*
002  *  SerialController.java
003  *
004  *  Copyright (c) 1995-2010, The University of Sheffield. See the file
005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
006  *
007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
008  *  software, licenced under the GNU Library General Public License,
009  *  Version 2, June 1991 (in the distribution as file licence.html,
010  *  and also available at http://gate.ac.uk/gate/licence.html).
011  *
012  *  Hamish Cunningham, 9/Nov/2000
013  *
014  *  $Id: SerialController.java 12936 2010-08-06 15:08:54Z thomas_heitz $
015  */
016 
017 package gate.creole;
018 
019 import java.util.*;
020 
021 import org.apache.log4j.Logger;
022 
023 import gate.*;
024 import gate.creole.metadata.*;
025 import gate.event.*;
026 import gate.util.*;
027 import gate.util.profile.Profiler;
028 import gate.util.Out;
029 
030 /**
031  * Execute a list of PRs serially.
032  */
033 @CreoleResource(name = "Pipeline",
034     comment = "A simple serial controller for PR pipelines.",
035     helpURL = "http://gate.ac.uk/userguide/sec:developer:apps")
036 public class SerialController extends AbstractController implements
037                                                         CreoleListener,
038                                                         CustomDuplication {
039 
040   protected static final Logger log = Logger.getLogger(SerialController.class);
041 
042   /** Profiler to track PR execute time */
043   protected Profiler prof;
044   protected HashMap timeMap;
045   protected HashMap<String, Long> prTimeMap;
046 
047   public SerialController() {
048     prList = Collections.synchronizedList(new ArrayList());
049     sListener = new InternalStatusListener();
050     prTimeMap = new HashMap<String, Long>();
051 
052     if(log.isDebugEnabled()) {
053       prof = new Profiler();
054       prof.enableGCCalling(false);
055       prof.printToSystemOut(true);
056       timeMap = new HashMap();
057     }
058     Gate.getCreoleRegister().addCreoleListener(this);
059   }
060 
061   /**
062    * Returns all the {@link gate.ProcessingResource}s contained by thisFeature
063    * controller as an unmodifiable list.
064    */
065   public Collection getPRs() {
066     return Collections.unmodifiableList(prList);
067   }
068 
069   /**
070    * Populates this controller from a collection of {@link ProcessingResource}s
071    * (optional operation).
072    *
073    * Controllers that are serializable must implement this method needed by GATE
074    * to restore the contents of the controllers.
075    *
076    @throws UnsupportedOperationException
077    *           if the <tt>setPRs</tt> method is not supported by this
078    *           controller.
079    */
080   public void setPRs(Collection prs) {
081     prList.clear();
082     Iterator prIter = prs.iterator();
083     while(prIter.hasNext())
084       add((ProcessingResource)prIter.next());
085   }
086 
087   public void add(int index, ProcessingResource pr) {
088     prList.add(index, pr);
089     fireResourceAdded(new ControllerEvent(this, ControllerEvent.RESOURCE_ADDED,
090       pr));
091   }
092 
093   public void add(ProcessingResource pr) {
094     prList.add(pr);
095     fireResourceAdded(new ControllerEvent(this, ControllerEvent.RESOURCE_ADDED,
096       pr));
097   }
098 
099   public ProcessingResource remove(int index) {
100     ProcessingResource aPr = (ProcessingResource)prList.remove(index);
101     fireResourceRemoved(new ControllerEvent(this,
102       ControllerEvent.RESOURCE_REMOVED, aPr));
103     return aPr;
104   }
105 
106   public boolean remove(ProcessingResource pr) {
107     boolean ret = prList.remove(pr);
108     if(ret)
109       fireResourceRemoved(new ControllerEvent(this,
110         ControllerEvent.RESOURCE_REMOVED, pr));
111     return ret;
112   }
113 
114   public ProcessingResource set(int index, ProcessingResource pr) {
115     return (ProcessingResource)prList.set(index, pr);
116   }
117 
118   /**
119    * Verifies that all PRs have all their required rutime parameters set.
120    */
121   protected void checkParameters() throws ExecutionException {
122     List badPRs;
123     try {
124       badPRs = getOffendingPocessingResources();
125     }
126     catch(ResourceInstantiationException rie) {
127       throw new ExecutionException(
128         "Could not check runtime parameters for the processing resources:\n"
129           + rie.toString());
130     }
131     if(badPRs != null && !badPRs.isEmpty()) { throw new ExecutionException(
132       "Some of the processing resources in this controller have unset "
133         "runtime parameters:\n" + badPRs.toString())}
134   }
135 
136   /** Run the Processing Resources in sequence. */
137   protected void executeImpl() throws ExecutionException {
138     // check all the PRs have the right parameters
139     checkParameters();
140 
141     if(log.isDebugEnabled()) {
142       prof.initRun("Execute controller [" + getName() "]");
143     }
144 
145     // execute all PRs in sequence
146     interrupted = false;
147     for(int i = 0; i < prList.size(); i++) {
148       if(isInterrupted()) {
149 
150       throw new ExecutionInterruptedException("The execution of the "
151         + getName() " application has been abruptly interrupted!")}
152 
153       runComponent(i);
154       if(log.isDebugEnabled()) {
155         prof.checkPoint("~Execute PR ["
156           ((ProcessingResource)prList.get(i)).getName() "]");
157         Long timeOfPR =
158           (Long)timeMap.get(((ProcessingResource)prList.get(i)).getName());
159         if(timeOfPR == null)
160           timeMap.put(((ProcessingResource)prList.get(i)).getName()new Long(
161             prof.getLastDuration()));
162         else timeMap.put(((ProcessingResource)prList.get(i)).getName(),
163           new Long(timeOfPR.longValue() + prof.getLastDuration()));
164         log.debug("Time taken so far by "
165           ((ProcessingResource)prList.get(i)).getName() ": "
166           + timeMap.get(((ProcessingResource)prList.get(i)).getName()));
167 
168       }
169     }
170 
171     if(log.isDebugEnabled()) {
172       prof.checkPoint("Execute controller [" + getName() "] finished");
173     }
174     fireStatusChanged("Finished running " + getName());
175 
176   // executeImpl()
177 
178   /**
179    * Resets the Time taken by various PRs
180    */
181   public void resetPrTimeMap() {
182     prTimeMap.clear();
183   }
184 
185   /**
186    * Returns the HashMap that lists the total time taken by each PR
187    @return
188    */
189   public HashMap<String, Long> getPrTimeMap() {
190     return this.prTimeMap;
191   }
192 
193   /**
194    * Executes a {@link ProcessingResource}.
195    */
196   protected void runComponent(int componentIndexthrows ExecutionException {
197     ProcessingResource currentPR =
198       (ProcessingResource)prList.get(componentIndex);
199 
200     // create the listeners
201     FeatureMap listeners = Factory.newFeatureMap();
202     listeners.put("gate.event.StatusListener", sListener);
203     int componentProgress = 100 / prList.size();
204     listeners.put("gate.event.ProgressListener"new IntervalProgressListener(
205       componentIndex * componentProgress, (componentIndex + 1)
206         * componentProgress));
207 
208     // add the listeners
209     try {
210       AbstractResource.setResourceListeners(currentPR, listeners);
211     }
212     catch(Exception e) {
213       // the listeners setting failed; nothing important
214       log.error("Could not set listeners for " + currentPR.getClass().getName()
215         "\n" + e.toString() "\n...nothing to lose any sleep over.");
216     }
217 
218     benchmarkFeatures.put(Benchmark.PR_NAME_FEATURE, currentPR.getName());
219 
220     long startTime = System.currentTimeMillis();
221     // run the thing
222     Benchmark.executeWithBenchmarking(currentPR,
223             Benchmark.createBenchmarkId(Benchmark.PR_PREFIX + currentPR.getName(),
224                     getBenchmarkId()), this, benchmarkFeatures);
225 
226     benchmarkFeatures.remove(Benchmark.PR_NAME_FEATURE);
227 
228     // calculate the time taken by the PR
229     long timeTakenByThePR = System.currentTimeMillis() - startTime;
230     Long time = prTimeMap.get(currentPR.getName());
231     if(time == null) {
232       time = new Long(0);
233     }
234     time = new Long(time.longValue() + timeTakenByThePR);
235     prTimeMap.put(currentPR.getName(), time);
236 
237 
238     // remove the listeners
239     try {
240       AbstractResource.removeResourceListeners(currentPR, listeners);
241     }
242     catch(Exception e) {
243       // the listeners removing failed; nothing important
244       log.error("Could not clear listeners for "
245         + currentPR.getClass().getName() "\n" + e.toString()
246         "\n...nothing to lose any sleep over.");
247     }
248   }// protected void runComponent(int componentIndex)
249 
250   /**
251    * Cleans the internal data and prepares this object to be collected
252    */
253   public void cleanup() {
254     //stop listening to Creole events.
255     Gate.getCreoleRegister().removeCreoleListener(this);
256     // close all PRs in this controller, if not members of other apps.
257     if(prList != null && !prList.isEmpty()) {
258       try {
259         //get all the other controllers
260         List<Resource> allOtherControllers  = 
261           Gate.getCreoleRegister().getAllInstances("gate.Controller");
262         allOtherControllers.remove(this);
263         //get all the PRs in all the other controllers
264         List<Resource> prsInOtherControllers = new ArrayList<Resource>();
265         for(Resource aController : allOtherControllers){
266           prsInOtherControllers.addAll(((Controller)aController).getPRs());
267         }
268         //remove all PRs in this controller, that are not also in other 
269         //controllers
270 //        for(Iterator prIter = getPRs().iterator(); prIter.hasNext();){
271 //          ProcessingResource aPr = (ProcessingResource)prIter.next();
272 //          if(!prsInOtherControllers.contains(aPr)){
273 //            prIter.remove();
274 //            Factory.deleteResource((Resource)aPr);
275 //          }
276 //        }
277         for(Object aPr : new ArrayList(getPRs())){
278           if(!prsInOtherControllers.contains(aPr)){
279             Factory.deleteResource((Resource)aPr);
280           }
281         }
282       }
283       catch(GateException e) {
284         //ignore
285       }
286     }
287   }
288   
289   /**
290    * Duplicate this controller.  We perform a default duplication of
291    * the controller itself, then recursively duplicate its contained
292    * PRs and add these duplicates to the copy.
293    */
294   public Resource duplicate(Factory.DuplicationContext ctx)
295           throws ResourceInstantiationException {
296     // duplicate this controller in the default way - this handles
297     // subclasses nicely
298     Controller c = (Controller)Factory.defaultDuplicate(this, ctx);
299     
300     // duplicate each of our PRs
301     List<ProcessingResource> newPRs = new ArrayList<ProcessingResource>();
302     for(Object pr : prList) {
303       newPRs.add((ProcessingResource)Factory.duplicate((Resource)pr, ctx));
304     }
305     // and set this duplicated list as the PRs of the copy
306     c.setPRs(newPRs);
307     
308     return c;
309   }
310 
311   /** The list of contained PRs */
312   protected List prList;
313 
314   /** A proxy for status events */
315   protected StatusListener sListener;
316 
317   public void resourceLoaded(CreoleEvent e) {
318   }
319 
320   public void resourceUnloaded(CreoleEvent e) {
321     // remove all occurences of the resource from this controller
322     if(e.getResource() instanceof ProcessingResource)
323     // while(prList.remove(e.getResource()));
324       // remove all occurrences of this PR from the controller
325       // Use the controller's remove method (rather than prList's so that
326       // subclasses of this controller type can add their own functionality
327       while(remove((ProcessingResource)e.getResource()))
328         ;
329     // remove links in parameters
330     for(int i = 0; i < prList.size(); i++) {
331       ProcessingResource aPr = (ProcessingResource)prList.get(i);
332       ResourceData rData =
333         (ResourceData)Gate.getCreoleRegister().get(aPr.getClass().getName());
334       if(rData != null) {
335         Iterator rtParamDisjIter =
336           rData.getParameterList().getRuntimeParameters().iterator();
337         while(rtParamDisjIter.hasNext()) {
338           List aDisjunction = (List)rtParamDisjIter.next();
339           Iterator rtParamIter = aDisjunction.iterator();
340           while(rtParamIter.hasNext()) {
341             Parameter aParam = (Parameter)rtParamIter.next();
342             String paramName = aParam.getName();
343             try {
344               if(aPr.getParameterValue(paramName== e.getResource()) {
345                 aPr.setParameterValue(paramName, null);
346               }
347             }
348             catch(ResourceInstantiationException rie) {
349               // nothing to do - this should never happen
350               throw new GateRuntimeException(rie);
351             }
352           }
353         }
354       }
355     }
356   }
357 
358   public void resourceRenamed(Resource resource, String oldName, String newName) {
359   }
360 
361   public void datastoreOpened(CreoleEvent e) {
362   }
363 
364   public void datastoreCreated(CreoleEvent e) {
365   }
366 
367   public void datastoreClosed(CreoleEvent e) {
368   }
369 
370 // class SerialController