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 a {@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 }
|