ConditionalSerialAnalyserController.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 08/10/2001
011  *
012  *  $Id: ConditionalSerialAnalyserController.java 19158 2016-03-30 18:29:41Z johann_p $
013  *
014  */
015 
016 package gate.creole;
017 
018 import gate.Controller;
019 import gate.CorpusController;
020 import gate.Document;
021 import gate.Factory;
022 import gate.Gate;
023 import gate.LanguageAnalyser;
024 import gate.ProcessingResource;
025 import gate.creole.metadata.CreoleParameter;
026 import gate.creole.metadata.CreoleResource;
027 import gate.creole.metadata.Optional;
028 import gate.creole.metadata.RunTime;
029 import gate.event.CreoleEvent;
030 import gate.util.Benchmark;
031 import gate.util.GateRuntimeException;
032 import gate.util.Out;
033 
034 import java.lang.reflect.UndeclaredThrowableException;
035 import java.util.ArrayList;
036 import java.util.Iterator;
037 import java.util.List;
038 
039 /**
040  * This class implements a SerialController that only contains
041  {@link gate.LanguageAnalyser}s.
042  * It has a {@link gate.Corpus} and its execute method runs all the analysers in
043  * turn over each of the documents in the corpus.
044  * This is a copy of the {@link SerialAnalyserController}, the only difference
045  * being that it inherits from {@link ConditionalSerialController} rather than
046  * from {@link SerialController} which makes it a <b>conditional</b> serial
047  * analyser controller.
048  <p>
049  * NOTE: if at the time when execute() is invoked, the document is not null,
050  * it is assumed that this controller is invoked from another controller and
051  * only this document is processed while the corpus (which must still be
052  * non-null) is ignored. Also, if the document is not null, the CorpusAwarePRs
053  * are not notified at the beginning, end, or abnormal termination of the pipeline. 
054  <p>
055  * If the document is null, all documents in the corpus
056  * are processed in sequence and CorpusAwarePRs are notified
057  * before the processing of the documents and after all documents
058  * have been processed or an abnormal termination occurred.
059  
060  */
061 @CreoleResource(name = "Conditional Corpus Pipeline",
062     comment = "A serial controller for conditionally run PR pipelines "
063         "over corpora.",
064     helpURL = "http://gate.ac.uk/userguide/sec:developer:cond")
065 public class ConditionalSerialAnalyserController
066        extends ConditionalSerialController
067        implements CorpusController, LanguageAnalyser, ControllerAwarePR {
068 
069   private static final long serialVersionUID = -2328353583769147103L;
070 
071 
072   /** Debug flag */
073   private static final boolean DEBUG = false;
074 
075   
076   /**
077    @return the document
078    */
079   @Override
080   public Document getDocument() {
081     return document;
082   }
083 
084   /**
085    @param document the document to set
086    */
087   @Override
088   @Optional
089   @RunTime
090   @CreoleParameter
091   public void setDocument(Document document) {
092     this.document = document;
093   }
094   
095   @Override
096   public gate.Corpus getCorpus() {
097     return corpus;
098   }
099 
100   @Override
101   public void setCorpus(gate.Corpus corpus) {
102     this.corpus = corpus;
103   }
104 
105   protected boolean runningAsSubPipeline = false;
106   
107   @Override
108   public void execute() throws ExecutionException {
109 
110     // Our assumption of if we run as a subpipeline of another corpus pipeline or
111     // not is based on whether or not the document is null or not:
112     if(document != null) {
113       runningAsSubPipeline = true;
114     else {
115       runningAsSubPipeline = false;
116     }
117     // inform ControllerAware PRs that execution has started, but only if we are not
118     // running as a subpipeline of another corpus pipeline.
119     if(!runningAsSubPipeline) {
120       if(controllerCallbacksEnabled) {
121         invokeControllerExecutionStarted();
122       }
123     }
124     Throwable thrown = null;
125     try {
126       if(Benchmark.isBenchmarkingEnabled()) {
127         // write a start marker to the benchmark log for this
128         // controller as a whole
129         Benchmark.startPoint(getBenchmarkId());
130       }
131       // do the real work
132       this.executeImpl();
133     }
134     catch(Throwable t) {
135       thrown = t;
136     }
137     finally {
138       if(thrown == null) {
139         // successfully completed
140         if(!runningAsSubPipeline) {
141           if(controllerCallbacksEnabled) {
142             invokeControllerExecutionFinished();
143           }
144         }
145       }
146       else {
147         // aborted
148         if(!runningAsSubPipeline) {
149           if(controllerCallbacksEnabled) {
150             invokeControllerExecutionAborted(thrown);
151           }
152         
153         // rethrow the aborting exception or error
154         if(thrown instanceof Error) {
155           throw (Error)thrown;
156         }
157         else if(thrown instanceof RuntimeException) {
158           throw (RuntimeException)thrown;
159         }
160         else if(thrown instanceof ExecutionException) {
161           throw (ExecutionException)thrown;
162         }
163         else {
164           // we have a checked exception that isn't one executeImpl can
165           // throw. This shouldn't be possible, but just in case...
166           throw new UndeclaredThrowableException(thrown);
167         }
168       }
169     }
170   }
171   
172   
173 
174   /** Run the Processing Resources in sequence. */
175   @Override
176   protected void executeImpl() throws ExecutionException{
177     interrupted = false;
178     if(corpus == nullthrow new ExecutionException(
179       "(ConditionalSerialAnalyserController) \"" + getName() "\":\n" +
180       "The corpus supplied for execution was null!");
181     
182     benchmarkFeatures.put(Benchmark.CORPUS_NAME_FEATURE, corpus.getName());
183     
184     
185     if(document == null){
186       //running as a top-level controller -> execute over all documents in 
187       //sequence
188       // iterate through the documents in the corpus
189       for(int i = 0; i < corpus.size(); i++) {
190         String savedBenchmarkId = getBenchmarkId();
191         try {
192           if(isInterrupted()) {
193             throw new ExecutionInterruptedException("The execution of the "
194               + getName() " application has been abruptly interrupted!");
195           }
196   
197           boolean docWasLoaded = corpus.isDocumentLoaded(i);
198   
199           // record the time before loading the document
200           long documentLoadingStartTime = Benchmark.startPoint();
201   
202           Document doc = corpus.get(i);
203   
204           // include the document name in the benchmark ID for sub-events
205           setBenchmarkId(Benchmark.createBenchmarkId("doc_" + doc.getName(),
206                   getBenchmarkId()));
207           // report the document loading
208           benchmarkFeatures.put(Benchmark.DOCUMENT_NAME_FEATURE, doc.getName());
209           Benchmark.checkPoint(documentLoadingStartTime,
210                   Benchmark.createBenchmarkId(Benchmark.DOCUMENT_LOADED,
211                           getBenchmarkId()), this, benchmarkFeatures);
212   
213           // run the system over this document
214           // set the doc and corpus
215           for(int j = 0; j < prList.size(); j++) {
216             ((LanguageAnalyser)prList.get(j)).setDocument(doc);
217             ((LanguageAnalyser)prList.get(j)).setCorpus(corpus);
218           }
219   
220           try {
221             if(DEBUG)
222               Out.pr("SerialAnalyserController processing doc=" + doc.getName()
223                 "...");
224   
225             super.executeImpl();
226             if(DEBUGOut.prln("done.");
227           }
228           finally {
229             // make sure we unset the doc and corpus even if we got an exception
230             for(int j = 0; j < prList.size(); j++) {
231               ((LanguageAnalyser)prList.get(j)).setDocument(null);
232               ((LanguageAnalyser)prList.get(j)).setCorpus(null);
233             }
234           }
235   
236           if(!docWasLoaded) {
237             long documentSavingStartTime = Benchmark.startPoint();
238             // trigger saving
239             corpus.unloadDocument(doc);
240             Benchmark.checkPoint(documentSavingStartTime,
241                     Benchmark.createBenchmarkId(Benchmark.DOCUMENT_SAVED,
242                             getBenchmarkId()), this, benchmarkFeatures);
243             
244             // close the previously unloaded Doc
245             Factory.deleteResource(doc);
246           }
247         }
248         finally {
249           setBenchmarkId(savedBenchmarkId);
250         }
251       }      
252     }else{
253       //document is set, so we run as a contained controller (i.e. as a compound
254       //Language Analyser
255       // run the system over this document
256       // set the doc and corpus
257       for(int j = 0; j < prList.size(); j++) {
258         ((LanguageAnalyser)prList.get(j)).setDocument(document);
259         ((LanguageAnalyser)prList.get(j)).setCorpus(corpus);
260       }
261 
262       try {
263         if(DEBUG)
264           Out.pr("SerialAnalyserController processing doc=" + document.getName()
265             "...");
266 
267         super.executeImpl();
268         if(DEBUGOut.prln("done.");
269       }
270       finally {
271         // make sure we unset the doc and corpus even if we got an exception
272         for(int j = 0; j < prList.size(); j++) {
273           ((LanguageAnalyser)prList.get(j)).setDocument(null);
274           ((LanguageAnalyser)prList.get(j)).setCorpus(null);
275         }
276       }
277     }//document was not null
278 
279     
280     
281 //    //iterate through the documents in the corpus
282 //    for(int i = 0; i < corpus.size(); i++){
283 //      if(isInterrupted()) throw new ExecutionInterruptedException(
284 //        "The execution of the " + getName() +
285 //        " application has been abruptly interrupted!");
286 //      
287 //      boolean docWasLoaded = corpus.isDocumentLoaded(i);
288 //      
289 //      // record the time before loading the document
290 //      long documentLoadingStartTime = Benchmark.startPoint();
291 //
292 //      Document doc = (Document)corpus.get(i);
293 //
294 //      // report the document loading
295 //      benchmarkFeatures.put(Benchmark.DOCUMENT_NAME_FEATURE, doc.getName());
296 //      Benchmark.checkPoint(documentLoadingStartTime,
297 //              Benchmark.createBenchmarkId(Benchmark.DOCUMENT_LOADED,
298 //                      getBenchmarkId()), this, benchmarkFeatures);
299 //      //run the system over this document
300 //      //set the doc and corpus
301 //      for(int j = 0; j < prList.size(); j++){
302 //        ((LanguageAnalyser)prList.get(j)).setDocument(doc);
303 //        ((LanguageAnalyser)prList.get(j)).setCorpus(corpus);
304 //      }
305 //
306 //      try{
307 //        if (DEBUG) 
308 //          Out.pr("ConditionalSerialAnalyserController processing doc=" + doc.getName()+ "...");      
309 //        super.executeImpl();
310 //        if (DEBUG) 
311 //          Out.prln("done.");      
312 //      }
313 //      finally {
314 //        // make sure we unset the doc and corpus even if we got an exception
315 //        for(int j = 0; j < prList.size(); j++){
316 //          ((LanguageAnalyser)prList.get(j)).setDocument(null);
317 //          ((LanguageAnalyser)prList.get(j)).setCorpus(null);
318 //        }
319 //      }
320 //
321 //      if(!docWasLoaded){
322 //        long documentSavingStartTime = Benchmark.startPoint();
323 //        // trigger saving
324 //        corpus.unloadDocument(doc);
325 //        Benchmark.checkPoint(documentSavingStartTime,
326 //                Benchmark.createBenchmarkId(Benchmark.DOCUMENT_SAVED,
327 //                        getBenchmarkId()), this, benchmarkFeatures);
328 //        //close the previoulsy unloaded Doc
329 //        Factory.deleteResource(doc);
330 //      }
331 //    }
332   }
333 
334   /**
335    * Overidden from {@link SerialController} to only allow
336    {@link LanguageAnalyser}s as components.
337    */
338   @Override
339   public void add(ProcessingResource pr){
340     checkLanguageAnalyser(pr);
341     super.add(pr);
342   }
343   
344   /**
345    * Overidden from {@link SerialController} to only allow
346    {@link LanguageAnalyser}s as components.
347    */
348   @Override
349   public void add(int index, ProcessingResource pr) {
350     checkLanguageAnalyser(pr);
351     super.add(index, pr);
352   }
353 
354   /**
355    * Throw an exception if the given processing resource is not
356    * a LanguageAnalyser.
357    */
358   protected void checkLanguageAnalyser(ProcessingResource pr) {
359     if(!(pr instanceof LanguageAnalyser)) {
360       throw new GateRuntimeException(getClass().getName() +
361                                      " only accepts " +
362                                      LanguageAnalyser.class.getName() +
363                                      "s as components\n" +
364                                      pr.getClass().getName() +
365                                      " is not!");
366     }
367   }
368   
369   /**
370    * Sets the current document to the memeber PRs
371    */
372   protected void setDocToPrs(Document doc){
373     Iterator<ProcessingResource> prIter = getPRs().iterator();
374     while(prIter.hasNext()){
375       ProcessingResource pr = prIter.next();
376       
377       // This is a bug fix, found by playing with the generics, as the
378       // old version cast everything to be a LanguageAnalyser even
379       // though not every ProcessingResource is one
380       if (pr instanceof LanguageAnalyser)
381         ((LanguageAnalyser)pr).setDocument(doc);
382     }
383   }
384 
385 
386   /**
387    * Checks whether all the contained PRs have all the required runtime
388    * parameters set. Ignores the corpus and document parameters as these will
389    * be set at run time.
390    *
391    @return {@link List} of {@link ProcessingResource}s that have required
392    * parameters with null values if they exist <tt>null</tt> otherwise.
393    @throws {@link ResourceInstantiationException} if problems occur while
394    * inspecting the parameters for one of the resources. These will normally be
395    * introspection problems and are usually caused by the lack of a parameter
396    * or of the read accessor for a parameter.
397    */
398   @Override
399   public List<ProcessingResource> getOffendingPocessingResources()
400          throws ResourceInstantiationException{
401     //take all the contained PRs
402     List<ProcessingResource> badPRs = new ArrayList<ProcessingResource>(getPRs());
403     //remove the ones that no parameters problems
404     Iterator<ProcessingResource> prIter = getPRs().iterator();
405     while(prIter.hasNext()){
406       ProcessingResource pr = prIter.next();
407       ResourceData rData = Gate.getCreoleRegister().
408                                               get(pr.getClass().getName());
409       //this is a list of lists
410       List<List<Parameter>> parameters = rData.getParameterList().getRuntimeParameters();
411       //remove corpus and document
412       List<List<Parameter>> newParameters = new ArrayList<List<Parameter>>();
413       Iterator<List<Parameter>> pDisjIter = parameters.iterator();
414       while(pDisjIter.hasNext()){
415         List<Parameter> aDisjunction = pDisjIter.next();
416         List<Parameter> newDisjunction = new ArrayList<Parameter>(aDisjunction);
417         Iterator<Parameter> internalParIter = newDisjunction.iterator();
418         while(internalParIter.hasNext()){
419           Parameter parameter = internalParIter.next();
420           if(parameter.getName().equals("corpus"||
421              parameter.getName().equals("document")) internalParIter.remove();
422         }
423         if(!newDisjunction.isEmpty()) newParameters.add(newDisjunction);
424       }
425 
426       if(AbstractResource.checkParameterValues(pr, newParameters)){
427         badPRs.remove(pr);
428       }
429     }
430     return badPRs.isEmpty() null : badPRs;
431   }
432 
433 
434   protected gate.Corpus corpus;
435 
436   
437   /**
438    * The document being processed. This is part of the {@link LanguageAnalyser} 
439    * interface, so this value is only used when the controller is used as a 
440    * member of another controller.
441    */
442   protected Document document;
443   
444   
445   /**
446    * Overridden to also clean up the corpus value.
447    */
448   @Override
449   public void resourceUnloaded(CreoleEvent e) {
450     super.resourceUnloaded(e);    
451     if(e.getResource() == corpus){
452       setCorpus(null);
453     }
454   }
455 
456   @Override
457   public void controllerExecutionStarted(Controller c)
458       throws ExecutionException {
459     
460     for(int i=0; i<getPRs().size(); i++) {
461       ProcessingResource pr = getPRs().get(i);
462       if(pr instanceof ControllerAwarePR) {
463         if(getRunningStrategies().get(i).getRunMode() != RunningStrategy.RUN_NEVER) {
464           if(pr instanceof LanguageAnalyser) {
465             ((LanguageAnalyser)pr).setCorpus(corpus);
466           }
467           ((ControllerAwarePR)pr).controllerExecutionStarted(c);
468         }
469       }
470     }
471     
472   }
473 
474   @Override
475   public void controllerExecutionFinished(Controller c)
476       throws ExecutionException {
477     
478     for(int i=0; i<getPRs().size(); i++) {
479       ProcessingResource pr = getPRs().get(i);
480       if(pr instanceof ControllerAwarePR) {
481         if(getRunningStrategies().get(i).getRunMode() != RunningStrategy.RUN_NEVER) {
482           if(pr instanceof LanguageAnalyser) {
483             ((LanguageAnalyser)pr).setCorpus(corpus);
484           }
485           ((ControllerAwarePR)pr).controllerExecutionFinished(c);
486           if(pr instanceof LanguageAnalyser) {
487             ((LanguageAnalyser)pr).setCorpus(null);
488           }
489         }
490       }
491     }       
492   }
493 
494   @Override
495   public void controllerExecutionAborted(Controller c, Throwable t)
496       throws ExecutionException {
497     
498     for(int i=0; i<getPRs().size(); i++) {
499       ProcessingResource pr = getPRs().get(i);
500       if(pr instanceof ControllerAwarePR) {
501         if(getRunningStrategies().get(i).getRunMode() != RunningStrategy.RUN_NEVER) {
502           if(pr instanceof LanguageAnalyser) {
503             ((LanguageAnalyser)pr).setCorpus(corpus);
504           }
505           ((ControllerAwarePR)pr).controllerExecutionAborted(c,t);
506           if(pr instanceof LanguageAnalyser) {
507             ((LanguageAnalyser)pr).setCorpus(null);
508           }
509         }
510       }
511     }       
512   }
513   
514    /**
515    * Invoke the controllerExecutionStarted method on this controller and all nested PRs and controllers. 
516    * This method is intended to be used after if the automatic invocation of the controller
517    * callback methods has been disabled with a call setControllerCallbackEnabled(false).  Normally
518    * the callback methods are automatically invoked at the start and end of execute().  
519    @throws ExecutionException 
520    */
521   @Override
522   public void invokeControllerExecutionStarted() throws ExecutionException {
523     
524     for(int i=0; i<getPRs().size(); i++) {
525       ProcessingResource pr = getPRs().get(i);
526       if(pr instanceof ControllerAwarePR) {
527         if(getRunningStrategies().get(i).getRunMode() != RunningStrategy.RUN_NEVER) {
528           if(pr instanceof LanguageAnalyser) {
529             ((LanguageAnalyser)pr).setCorpus(corpus);
530           }
531           ((ControllerAwarePR)pr).controllerExecutionStarted(this);
532         }
533       }
534     }
535   }
536 
537    /**
538    * Invoke the controllerExecutionFinished method on this controller and all nested PRs and controllers. 
539    * This method is intended to be used after if the automatic invocation of the controller
540    * callback methods has been disabled with a call setControllerCallbackEnabled(false).  Normally
541    * the callback methods are automatically invoked at the start and end of execute().  
542    @throws ExecutionException 
543    */
544   @Override
545   public void invokeControllerExecutionFinished() throws ExecutionException {
546     
547     for(int i=0; i<getPRs().size(); i++) {
548       ProcessingResource pr = getPRs().get(i);
549       if(pr instanceof ControllerAwarePR) {
550         if(getRunningStrategies().get(i).getRunMode() != RunningStrategy.RUN_NEVER) {
551           if(pr instanceof LanguageAnalyser) {
552             ((LanguageAnalyser)pr).setCorpus(corpus);
553           }
554           ((ControllerAwarePR)pr).controllerExecutionFinished(this);
555           if(pr instanceof LanguageAnalyser) {
556             ((LanguageAnalyser)pr).setCorpus(null);
557           }
558         }
559       }
560     }       
561     
562   }
563   
564    /**
565    * Invoke the controllerExecutionAborted method on this controller and all nested PRs and controllers. 
566    * This method is intended to be used after if the automatic invocation of the controller
567    * callback methods has been disabled with a call setControllerCallbackEnabled(false).  Normally
568    * the callback methods are automatically invoked at the start and end of execute().  
569    @param thrown The Throwable to pass on to the controller callback method.
570    @throws ExecutionException 
571    */
572   @Override
573   public void invokeControllerExecutionAborted(Throwable thrownthrows ExecutionException {
574     
575     for(int i=0; i<getPRs().size(); i++) {
576       ProcessingResource pr = getPRs().get(i);
577       if(pr instanceof ControllerAwarePR) {
578         if(getRunningStrategies().get(i).getRunMode() != RunningStrategy.RUN_NEVER) {
579           if(pr instanceof LanguageAnalyser) {
580             ((LanguageAnalyser)pr).setCorpus(corpus);
581           }
582           ((ControllerAwarePR)pr).controllerExecutionAborted(this,thrown);
583           if(pr instanceof LanguageAnalyser) {
584             ((LanguageAnalyser)pr).setCorpus(null);
585           }
586         }
587       }
588     }       
589   }
590   
591   
592   
593 }