1   /*
2    *  Factory.java
3    *
4    *  Copyright (c) 1998-2004, The University of Sheffield.
5    *
6    *  This file is part of GATE (see http://gate.ac.uk/), and is free
7    *  software, licenced under the GNU Library General Public License,
8    *  Version 2, June 1991 (in the distribution as file licence.html,
9    *  and also available at http://gate.ac.uk/gate/licence.html).
10   *
11   *  Hamish Cunningham, 25/May/2000
12   *
13   *  $Id: Factory.java,v 1.80 2004/07/21 17:10:02 akshay Exp $
14   */
15  
16  package gate;
17  
18  import java.io.Serializable;
19  import java.net.URL;
20  import java.util.*;
21  
22  import gate.creole.*;
23  import gate.event.CreoleEvent;
24  import gate.event.CreoleListener;
25  import gate.persist.PersistenceException;
26  import gate.persist.SerialDataStore;
27  import gate.security.*;
28  import gate.security.SecurityException;
29  import gate.util.*;
30  
31  /** Provides static methods for the creation of Resources.
32    */
33  public abstract class Factory {
34    /** Debug flag */
35    private static final boolean DEBUG = false;
36  
37    /** The CREOLE register */
38    private static CreoleRegister reg = Gate.getCreoleRegister();
39  
40    /** The DataStore register */
41    private static DataStoreRegister dsReg = Gate.getDataStoreRegister();
42  
43    /** An object to source events from. */
44    private static CreoleProxy creoleProxy;
45  
46    /** An object to source events from. */
47    private static HashMap accessControllerPool;
48  
49    /** Create an instance of a resource using default parameter values.
50      * @see #createResource(String,FeatureMap)
51      */
52    public static Resource createResource(String resourceClassName)
53    throws ResourceInstantiationException
54    {
55      // get the resource metadata
56      ResourceData resData = (ResourceData) reg.get(resourceClassName);
57      if(resData == null)
58        throw new ResourceInstantiationException(
59          "Couldn't get resource data for " + resourceClassName
60        );
61  
62      // get the parameter list and default values
63      ParameterList paramList = resData.getParameterList();
64      FeatureMap parameterValues = null;
65      try {
66        parameterValues = paramList.getInitimeDefaults();
67      } catch(ParameterException e) {
68        throw new ResourceInstantiationException(
69          "Couldn't get default parameters for " + resourceClassName + ": " + e
70        );
71      }
72  
73      return createResource(resourceClassName, parameterValues);
74    } // createResource(resClassName)
75  
76    /** Create an instance of a resource, and return it.
77      * Callers of this method are responsible for
78      * querying the resource's parameter lists, putting together a set that
79      * is complete apart from runtime parameters, and passing a feature map
80      * containing these parameter settings.
81      *
82      * @param resourceClassName the name of the class implementing the resource.
83      * @param parameterValues the feature map containing intialisation time
84      *   parameterValues for the resource.
85      * @return an instantiated resource.
86      */
87    public static Resource createResource(
88      String resourceClassName, FeatureMap parameterValues
89    ) throws ResourceInstantiationException
90    {
91      return createResource(resourceClassName, parameterValues, null, null);
92    } // createResource(resClassName, paramVals, listeners)
93  
94    /** Create an instance of a resource, and return it.
95      * Callers of this method are responsible for
96      * querying the resource's parameter lists, putting together a set that
97      * is complete apart from runtime parameters, and passing a feature map
98      * containing these parameter settings.
99      *
100     * @param resourceClassName the name of the class implementing the resource.
101     * @param parameterValues the feature map containing intialisation time
102     *   parameterValues for the resource.
103     * @param features the features for the new resource
104     * @return an instantiated resource.
105     */
106   public static Resource createResource(
107     String resourceClassName, FeatureMap parameterValues,
108     FeatureMap features
109     ) throws ResourceInstantiationException
110    {
111       return createResource(resourceClassName, parameterValues,
112                             features, null);
113    }
114 
115   /** Create an instance of a resource, and return it.
116     * Callers of this method are responsible for
117     * querying the resource's parameter lists, putting together a set that
118     * is complete apart from runtime parameters, and passing a feature map
119     * containing these parameter settings.
120     *
121     * In the case of ProcessingResources they will have their runtime parameters
122     * initialised to their default values.
123     *
124     * @param resourceClassName the name of the class implementing the resource.
125     * @param parameterValues the feature map containing intialisation time
126     *   parameterValues for the resource.
127     * @param features the features for the new resource
128     * @return an instantiated resource.
129     */
130   public static Resource createResource(
131     String resourceClassName, FeatureMap parameterValues,
132     FeatureMap features, String resourceName
133   ) throws ResourceInstantiationException
134    {
135     // get the resource metadata
136     ResourceData resData = (ResourceData) reg.get(resourceClassName);
137     if(resData == null)
138       throw new ResourceInstantiationException(
139         "Couldn't get resource data for " + resourceClassName
140       );
141     // get the default implementation class
142     Class resClass = null;
143     try {
144       resClass = resData.getResourceClass();
145     } catch(ClassNotFoundException e) {
146       throw new ResourceInstantiationException(
147         "Couldn't get resource class from the resource data:"+Strings.getNl()+e
148       );
149     }
150 
151     //create a pointer for the resource
152     Resource res = null;
153 
154     //if the object is an LR and it should come from a DS then create that way
155     DataStore dataStore;
156     if(LanguageResource.class.isAssignableFrom(resClass) &&
157        ((dataStore = (DataStore)parameterValues.
158                      get(DataStore.DATASTORE_FEATURE_NAME)) != null)
159       ){
160       //ask the datastore to create our object
161       if(dataStore instanceof SerialDataStore) {
162         // SDS doesn't need a wrapper class; just check for serialisability
163         if(! (resClass instanceof Serializable))
164           throw new ResourceInstantiationException(
165             "Resource cannot be (de-)serialized: " + resClass.getName()
166           );
167       }
168 
169       // get the datastore instance id and retrieve the resource
170       Object instanceId = parameterValues.get(DataStore.LR_ID_FEATURE_NAME);
171       if(instanceId == null)
172         throw new
173           ResourceInstantiationException("No instance id for " + resClass);
174       try {
175         res = dataStore.getLr(resClass.getName(), instanceId);
176       } catch(PersistenceException pe) {
177         throw new ResourceInstantiationException("Bad read from DB: " + pe);
178       } catch(SecurityException se) {
179         throw new ResourceInstantiationException("Insufficient permissions: " + se);
180       }
181       resData.addInstantiation(res);
182       if(features != null){
183         if(res.getFeatures() == null){
184           res.setFeatures(newFeatureMap());
185         }
186         res.getFeatures().putAll(features);
187       }
188 
189       //set the name
190       if(res.getName() == null){
191         res.setName(resourceName == null ?
192                     resData.getName() + "_" + Gate.genSym() :
193                     resourceName);
194       }
195 
196       // fire the event
197       creoleProxy.fireResourceLoaded(
198         new CreoleEvent(res, CreoleEvent.RESOURCE_LOADED)
199       );
200 
201       return res;
202     }
203 
204     //The resource is not a persistent LR; use a constructor
205 
206     // create an object using the resource's default constructor
207     try {
208       if(DEBUG) Out.prln("Creating resource " + resClass.getName());
209       res = (Resource) resClass.newInstance();
210     } catch(IllegalAccessException e) {
211       throw new ResourceInstantiationException(
212         "Couldn't create resource instance, access denied: " + e
213       );
214     } catch(InstantiationException e) {
215       throw new ResourceInstantiationException(
216         "Couldn't create resource instance due to newInstance() failure: " + e
217       );
218     }
219 
220     //set the name
221     if(resourceName == null){
222       resourceName = resData.getName() + "_" + Gate.genSym();
223     }
224     res.setName(resourceName);
225 
226     if(LanguageResource.class.isAssignableFrom(resClass)) {
227       // type-specific stuff for LRs
228       if(DEBUG) Out.prln(resClass.getName() + " is a LR");
229     } else if(ProcessingResource.class.isAssignableFrom(resClass)) {
230       // type-specific stuff for PRs
231       if(DEBUG) Out.prln(resClass.getName() + " is a PR");
232       //set the runtime parameters to their defaults
233       try{
234         FeatureMap parameters = newFeatureMap();
235         parameters.putAll(resData.getParameterList().getRuntimeDefaults());
236         res.setParameterValues(parameters);
237       }catch(ParameterException pe){
238         throw new ResourceInstantiationException(
239                   "Could not set the runtime parameters " +
240                   "to their default values for: " + res.getClass().getName() +
241                   " :\n" + pe.toString()
242                   );
243       }
244     // type-specific stuff for VRs
245     } else if(VisualResource.class.isAssignableFrom(resClass)) {
246       if(DEBUG) Out.prln(resClass.getName() + " is a VR");
247 
248     // we have a resource which is not an LR, PR or VR
249     } else if(Controller.class.isAssignableFrom(resClass)){
250       //type specific stuff for Controllers
251     } else {
252       Err.prln("WARNING: instantiating resource which is not a PR, LR or VR:");
253       Err.prln(resData + "END OF WARNING" + Strings.getNl());
254     }
255 
256 
257 
258     //set the parameterValues of the resource
259     try{
260       FeatureMap parameters = newFeatureMap();
261       //put the defaults
262       parameters.putAll(resData.getParameterList().getInitimeDefaults());
263       //overwrite the defaults with the user provided values
264       parameters.putAll(parameterValues);
265       res.setParameterValues(parameters);
266     }catch(ParameterException pe){
267         throw new ResourceInstantiationException(
268                     "Could not set the init parameters for: " +
269                     res.getClass().getName() + " :\n" + pe.toString()
270                   );
271     }
272 
273     Map listeners = new HashMap(gate.gui.MainFrame.getListeners());
274     // set the listeners if any
275     if(listeners != null && !listeners.isEmpty()) {
276       try {
277         if(DEBUG) Out.prln("Setting the listeners for  " + res.toString());
278         AbstractResource.setResourceListeners(res, listeners);
279       } catch(Exception e) {
280         if(DEBUG) Out.prln("Failed to set listeners for " + res.toString());
281         throw new
282           ResourceInstantiationException("Parameterisation failure" + e);
283       }
284     }
285 
286     // if the features of the resource have not been explicitly set,
287     // set them to the features of the resource data
288     if(res.getFeatures() == null || res.getFeatures().isEmpty()){
289       FeatureMap fm = newFeatureMap();
290       fm.putAll(resData.getFeatures());
291       res.setFeatures(fm);
292     }
293 
294     // initialise the resource
295     if(DEBUG) Out.prln("Initialising resource " + res.toString());
296     res = res.init();
297 
298     // remove the listeners if any
299     if(listeners != null && !listeners.isEmpty()) {
300       try {
301         if(DEBUG) Out.prln("Removing the listeners for  " + res.toString());
302         AbstractResource.removeResourceListeners(res, listeners);
303       } catch(Exception e) {
304         if (DEBUG) Out.prln(
305           "Failed to remove the listeners for " + res.toString()
306         );
307         throw new
308           ResourceInstantiationException("Parameterisation failure" + e);
309       }
310     }
311     // record the instantiation on the resource data's stack
312     resData.addInstantiation(res);
313     // add the features specified by the user
314     if(features != null) res.getFeatures().putAll(features);
315     // fire the event
316     creoleProxy.fireResourceLoaded(
317       new CreoleEvent(res, CreoleEvent.RESOURCE_LOADED)
318     );
319     return res;
320   } // create(resourceClassName, parameterValues, features, listeners)
321 
322   /** Delete an instance of a resource. This involves removing it from
323     * the stack of instantiations maintained by this resource type's
324     * resource data. Deletion does not guarantee that the resource will
325     * become a candidate for garbage collection, just that the GATE framework
326     * is no longer holding references to the resource.
327     *
328     * @param resource the resource to be deleted.
329     */
330   public static void deleteResource(Resource resource) {
331     ResourceData rd =
332       (ResourceData) reg.get(resource.getClass().getName());
333     if(rd!= null)
334       rd.removeInstantiation(resource);
335     creoleProxy.fireResourceUnloaded(
336       new CreoleEvent(resource, CreoleEvent.RESOURCE_UNLOADED)
337     );
338     resource.cleanup();
339   } // deleteResource
340 
341   /** Create a new transient Corpus. */
342   public static Corpus newCorpus(String name)
343                                           throws ResourceInstantiationException
344   {
345     FeatureMap parameterValues = newFeatureMap();
346     parameterValues.put(Corpus.CORPUS_NAME_PARAMETER_NAME, name);
347 //    parameterValues.put("features", Factory.newFeatureMap());
348     return (Corpus) createResource("gate.corpora.CorpusImpl", parameterValues);
349   } // newCorpus
350 
351   /** Create a new transient Document from a URL. */
352   public static Document newDocument(URL sourceUrl)
353                                           throws ResourceInstantiationException
354   {
355     FeatureMap parameterValues = newFeatureMap();
356     parameterValues.put(Document.DOCUMENT_URL_PARAMETER_NAME, sourceUrl);
357     return
358       (Document) createResource("gate.corpora.DocumentImpl", parameterValues);
359   } // newDocument(URL)
360 
361   /** Create a new transient Document from a URL and an encoding. */
362   public static Document newDocument(URL sourceUrl, String encoding)
363                                           throws ResourceInstantiationException
364   {
365     FeatureMap parameterValues = newFeatureMap();
366     parameterValues.put(Document.DOCUMENT_URL_PARAMETER_NAME, sourceUrl);
367     parameterValues.put(Document.DOCUMENT_ENCODING_PARAMETER_NAME, encoding);
368     return
369       (Document) createResource("gate.corpora.DocumentImpl", parameterValues);
370   } // newDocument(URL)
371 
372   /** Create a new transient textual Document from a string. */
373   public static Document newDocument(String content)
374                                           throws ResourceInstantiationException
375   {
376     FeatureMap params = newFeatureMap();
377     params.put(Document.DOCUMENT_STRING_CONTENT_PARAMETER_NAME, content);
378     Document doc =
379       (Document) createResource("gate.corpora.DocumentImpl", params);
380 /*
381     // laziness: should fit this into createResource by adding a new
382     // document parameter, but haven't time right now...
383     doc.setContent(new DocumentContentImpl(content));
384 */
385     // various classes are in the habit of assuming that a document
386     // inevitably has a source URL...  so give it a dummy one
387 /*    try {
388       doc.setSourceUrl(new URL("http://localhost/"));
389     } catch(MalformedURLException e) {
390       throw new ResourceInstantiationException(
391         "Couldn't create dummy URL in newDocument(String): " + e
392       );
393     }
394 */
395     doc.setSourceUrl(null);
396     return doc;
397   } // newDocument(String)
398 
399   /** Create a new FeatureMap. */
400   public static FeatureMap newFeatureMap() {
401     return new SimpleFeatureMapImpl();
402   } // newFeatureMap
403 
404   /** Open an existing DataStore. */
405   public static DataStore openDataStore(
406     String dataStoreClassName, String storageUrl
407   ) throws PersistenceException {
408     DataStore ds = instantiateDataStore(dataStoreClassName, storageUrl);
409     ds.open();
410     if(dsReg.add(ds))
411       creoleProxy.fireDatastoreOpened(
412         new CreoleEvent(ds, CreoleEvent.DATASTORE_OPENED)
413       );
414 
415     return ds;
416   } // openDataStore()
417 
418   /** Create a new DataStore and open it. <B>NOTE:</B> for some data stores
419     * creation is an system administrator task; in such cases this
420     * method will throw an UnsupportedOperationException.
421     */
422   public static DataStore createDataStore(
423     String dataStoreClassName, String storageUrl
424   ) throws PersistenceException, UnsupportedOperationException {
425     DataStore ds = instantiateDataStore(dataStoreClassName, storageUrl);
426     ds.create();
427     ds.open();
428     if(dsReg.add(ds))
429       creoleProxy.fireDatastoreCreated(
430         new CreoleEvent(ds, CreoleEvent.DATASTORE_CREATED)
431       );
432 
433     return ds;
434   } // createDataStore()
435 
436   /** Instantiate a DataStore (not open or created). */
437   protected static DataStore instantiateDataStore(
438     String dataStoreClassName, String storageUrl
439   ) throws PersistenceException {
440     DataStore godfreyTheDataStore = null;
441     try {
442       godfreyTheDataStore =
443         (DataStore) Gate.getClassLoader().
444                     loadClass(dataStoreClassName).newInstance();
445     } catch(Exception e) {
446       throw new PersistenceException("Couldn't create DS class: " + e);
447     }
448 
449     if(dsReg == null) // static init ran before Gate.init....
450       dsReg = Gate.getDataStoreRegister();
451     godfreyTheDataStore.setStorageUrl(storageUrl.toString());
452 
453     return godfreyTheDataStore;
454   } // instantiateDS(dataStoreClassName, storageURL)
455 
456   /** Add a listener */
457   public static synchronized void addCreoleListener(CreoleListener l){
458     creoleProxy.addCreoleListener(l);
459   } // addCreoleListener(CreoleListener)
460 
461   /** Static initialiser to set up the CreoleProxy event source object */
462   static {
463     creoleProxy = new CreoleProxy();
464     accessControllerPool = new HashMap();
465   } // static initialiser
466 
467 
468   /**
469    * Creates and opens a new AccessController (if not available in the pool).
470   */
471   public static synchronized AccessController createAccessController(String jdbcURL)
472     throws PersistenceException {
473 
474     if (false == accessControllerPool.containsKey(jdbcURL)) {
475       AccessController ac = new AccessControllerImpl(jdbcURL);
476       ac.open();
477       accessControllerPool.put(jdbcURL,ac);
478     }
479 
480     return (AccessController)accessControllerPool.get(jdbcURL);
481   } // createAccessController()
482 
483 } // abstract Factory
484 
485 
486 /**
487  * Factory is basically a collection of static methods but events need to
488  * have as source an object and not a class. The CreolProxy class addresses
489  * this issue acting as source for all events fired by the Factory class.
490  */
491 class CreoleProxy {
492 
493   public synchronized void removeCreoleListener(CreoleListener l) {
494     if (creoleListeners != null && creoleListeners.contains(l)) {
495       Vector v = (Vector) creoleListeners.clone();
496       v.removeElement(l);
497       creoleListeners = v;
498     }// if
499   }// removeCreoleListener(CreoleListener l)
500 
501   public synchronized void addCreoleListener(CreoleListener l) {
502     Vector v =
503       creoleListeners == null ? new Vector(2) : (Vector) creoleListeners.clone();
504     if (!v.contains(l)) {
505       v.addElement(l);
506       creoleListeners = v;
507     }// if
508   }// addCreoleListener(CreoleListener l)
509 
510   protected void fireResourceLoaded(CreoleEvent e) {
511     if (creoleListeners != null) {
512       Vector listeners = creoleListeners;
513       int count = listeners.size();
514       for (int i = 0; i < count; i++) {
515         ((CreoleListener) listeners.elementAt(i)).resourceLoaded(e);
516       }// for
517     }// if
518   }// fireResourceLoaded(CreoleEvent e)
519 
520   protected void fireResourceUnloaded(CreoleEvent e) {
521     if (creoleListeners != null) {
522       Vector listeners = creoleListeners;
523       int count = listeners.size();
524       for (int i = 0; i < count; i++) {
525         ((CreoleListener) listeners.elementAt(i)).resourceUnloaded(e);
526       }// for
527     }// if
528   }// fireResourceUnloaded(CreoleEvent e)
529 
530   protected void fireDatastoreOpened(CreoleEvent e) {
531     if (creoleListeners != null) {
532       Vector listeners = creoleListeners;
533       int count = listeners.size();
534       for (int i = 0; i < count; i++) {
535         ((CreoleListener) listeners.elementAt(i)).datastoreOpened(e);
536       }// for
537     }// if
538   }// fireDatastoreOpened(CreoleEvent e)
539 
540   protected void fireDatastoreCreated(CreoleEvent e) {
541     if (creoleListeners != null) {
542       Vector listeners = creoleListeners;
543       int count = listeners.size();
544       for (int i = 0; i < count; i++) {
545         ((CreoleListener) listeners.elementAt(i)).datastoreCreated(e);
546       }// for
547     }// if
548   }// fireDatastoreCreated(CreoleEvent e)
549 
550   protected void fireDatastoreClosed(CreoleEvent e) {
551     if (creoleListeners != null) {
552       Vector listeners = creoleListeners;
553       int count = listeners.size();
554       for (int i = 0; i < count; i++) {
555         ((CreoleListener) listeners.elementAt(i)).datastoreClosed(e);
556       }// for
557     }// if
558   }// fireDatastoreClosed(CreoleEvent e)
559 
560   private transient Vector creoleListeners;
561 }//class CreoleProxy
562