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