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