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.74 2001/11/13 17:09:34 marin 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 
284     Map listeners = new HashMap(gate.gui.MainFrame.getListeners());
285     // set the listeners if any
286     if(listeners != null && !listeners.isEmpty()) {
287       try {
288         if(DEBUG) Out.prln("Setting the listeners for  " + res.toString());
289         AbstractResource.setResourceListeners(res, listeners);
290       } catch(Exception e) {
291         if(DEBUG) Out.prln("Failed to set listeners for " + res.toString());
292         throw new
293           ResourceInstantiationException("Parameterisation failure" + e);
294       }
295     }
296 
297     // if the features of the resource have not been explicitly set,
298     // set them to the features of the resource data
299     if(res.getFeatures() == null || res.getFeatures().isEmpty()){
300       FeatureMap fm = newFeatureMap();
301       fm.putAll(resData.getFeatures());
302       res.setFeatures(fm);
303     }
304 
305     // initialise the resource
306     if(DEBUG) Out.prln("Initialising resource " + res.toString());
307     res = res.init();
308 
309     // remove the listeners if any
310     if(listeners != null && !listeners.isEmpty()) {
311       try {
312         if(DEBUG) Out.prln("Removing the listeners for  " + res.toString());
313         AbstractResource.removeResourceListeners(res, listeners);
314       } catch(Exception e) {
315         if (DEBUG) Out.prln(
316           "Failed to remove the listeners for " + res.toString()
317         );
318         throw new
319           ResourceInstantiationException("Parameterisation failure" + e);
320       }
321     }
322 
323     // record the instantiation on the resource data's stack
324     resData.addInstantiation(res);
325 
326     // add the features specified by the user
327     if(features != null) res.getFeatures().putAll(features);
328 
329     // fire the event
330     creoleProxy.fireResourceLoaded(
331       new CreoleEvent(res, CreoleEvent.RESOURCE_LOADED)
332     );
333 
334     return res;
335   } // create(resourceClassName, parameterValues, features, listeners)
336 
337   /** Delete an instance of a resource. This involves removing it from
338     * the stack of instantiations maintained by this resource type's
339     * resource data. Deletion does not guarantee that the resource will
340     * become a candidate for garbage collection, just that the GATE framework
341     * is no longer holding references to the resource.
342     *
343     * @param resource the resource to be deleted.
344     */
345   public static void deleteResource(Resource resource) {
346     ResourceData rd =
347       (ResourceData) reg.get(resource.getClass().getName());
348     if(rd!= null)
349       rd.removeInstantiation(resource);
350     resource.cleanup();
351     creoleProxy.fireResourceUnloaded(
352       new CreoleEvent(resource, CreoleEvent.RESOURCE_UNLOADED)
353     );
354   } // deleteResource
355 
356   /** Create a new transient Corpus. */
357   public static Corpus newCorpus(String name)
358                                           throws ResourceInstantiationException
359   {
360     FeatureMap parameterValues = newFeatureMap();
361     parameterValues.put("name", name);
362 //    parameterValues.put("features", Factory.newFeatureMap());
363     return (Corpus) createResource("gate.corpora.CorpusImpl", parameterValues);
364   } // newCorpus
365 
366   /** Create a new transient Document from a URL. */
367   public static Document newDocument(URL sourceUrl)
368                                           throws ResourceInstantiationException
369   {
370     FeatureMap parameterValues = newFeatureMap();
371     parameterValues.put("sourceUrl", sourceUrl);
372     return
373       (Document) createResource("gate.corpora.DocumentImpl", parameterValues);
374   } // newDocument(URL)
375 
376   /** Create a new transient Document from a URL and an encoding. */
377   public static Document newDocument(URL sourceUrl, String encoding)
378                                           throws ResourceInstantiationException
379   {
380     FeatureMap parameterValues = newFeatureMap();
381     parameterValues.put("sourceUrl", sourceUrl);
382     parameterValues.put("encoding", encoding);
383     return
384       (Document) createResource("gate.corpora.DocumentImpl", parameterValues);
385   } // newDocument(URL)
386 
387   /** Create a new transient textual Document from a string. */
388   public static Document newDocument(String content)
389                                           throws ResourceInstantiationException
390   {
391     FeatureMap params = newFeatureMap();
392     params.put("stringContent", content);
393     Document doc =
394       (Document) createResource("gate.corpora.DocumentImpl", params);
395 /*
396     // laziness: should fit this into createResource by adding a new
397     // document parameter, but haven't time right now...
398     doc.setContent(new DocumentContentImpl(content));
399 */
400     // various classes are in the habit of assuming that a document
401     // inevitably has a source URL...  so give it a dummy one
402 /*    try {
403       doc.setSourceUrl(new URL("http://localhost/"));
404     } catch(MalformedURLException e) {
405       throw new ResourceInstantiationException(
406         "Couldn't create dummy URL in newDocument(String): " + e
407       );
408     }
409 */
410     doc.setSourceUrl(null);
411     return doc;
412   } // newDocument(String)
413 
414   /** Create a new FeatureMap. */
415   public static FeatureMap newFeatureMap() {
416     return new SimpleFeatureMapImpl();
417   } // newFeatureMap
418 
419   /** Open an existing DataStore. */
420   public static DataStore openDataStore(
421     String dataStoreClassName, String storageUrl
422   ) throws PersistenceException {
423     DataStore ds = instantiateDataStore(dataStoreClassName, storageUrl);
424     ds.open();
425     if(dsReg.add(ds))
426       creoleProxy.fireDatastoreOpened(
427         new CreoleEvent(ds, CreoleEvent.DATASTORE_OPENED)
428       );
429 
430     return ds;
431   } // openDataStore()
432 
433   /** Create a new DataStore and open it. <B>NOTE:</B> for some data stores
434     * creation is an system administrator task; in such cases this
435     * method will throw an UnsupportedOperationException.
436     */
437   public static DataStore createDataStore(
438     String dataStoreClassName, String storageUrl
439   ) throws PersistenceException, UnsupportedOperationException {
440     DataStore ds = instantiateDataStore(dataStoreClassName, storageUrl);
441     ds.create();
442     ds.open();
443     if(dsReg.add(ds))
444       creoleProxy.fireDatastoreCreated(
445         new CreoleEvent(ds, CreoleEvent.DATASTORE_CREATED)
446       );
447 
448     return ds;
449   } // createDataStore()
450 
451   /** Instantiate a DataStore (not open or created). */
452   protected static DataStore instantiateDataStore(
453     String dataStoreClassName, String storageUrl
454   ) throws PersistenceException {
455     DataStore godfreyTheDataStore = null;
456     try {
457       godfreyTheDataStore =
458         (DataStore) Gate.getClassLoader().
459                     loadClass(dataStoreClassName).newInstance();
460     } catch(Exception e) {
461       throw new PersistenceException("Couldn't create DS class: " + e);
462     }
463 
464     if(dsReg == null) // static init ran before Gate.init....
465       dsReg = Gate.getDataStoreRegister();
466     godfreyTheDataStore.setStorageUrl(storageUrl.toString());
467 
468     return godfreyTheDataStore;
469   } // instantiateDS(dataStoreClassName, storageURL)
470 
471   /** Add a listener */
472   public static synchronized void addCreoleListener(CreoleListener l){
473     creoleProxy.addCreoleListener(l);
474   } // addCreoleListener(CreoleListener)
475 
476   /** Static initialiser to set up the CreoleProxy event source object */
477   static {
478     creoleProxy = new CreoleProxy();
479     accessControllerPool = new HashMap();
480   } // static initialiser
481 
482 
483   /**
484    * Creates and opens a new AccessController (if not available in the pool).
485   */
486   public static synchronized AccessController createAccessController(String jdbcURL)
487     throws PersistenceException {
488 
489     if (false == accessControllerPool.containsKey(jdbcURL)) {
490       AccessController ac = new AccessControllerImpl(jdbcURL);
491       ac.open();
492       accessControllerPool.put(jdbcURL,ac);
493     }
494 
495     return (AccessController)accessControllerPool.get(jdbcURL);
496   } // createAccessController()
497 
498 } // abstract Factory
499 
500 
501 /**
502  * Factory is basically a collection of static methods but events need to
503  * have as source an object and not a class. The CreolProxy class addresses
504  * this issue acting as source for all events fired by the Factory class.
505  */
506 class CreoleProxy {
507 
508   public synchronized void removeCreoleListener(CreoleListener l) {
509     if (creoleListeners != null && creoleListeners.contains(l)) {
510       Vector v = (Vector) creoleListeners.clone();
511       v.removeElement(l);
512       creoleListeners = v;
513     }// if
514   }// removeCreoleListener(CreoleListener l)
515 
516   public synchronized void addCreoleListener(CreoleListener l) {
517     Vector v =
518       creoleListeners == null ? new Vector(2) : (Vector) creoleListeners.clone();
519     if (!v.contains(l)) {
520       v.addElement(l);
521       creoleListeners = v;
522     }// if
523   }// addCreoleListener(CreoleListener l)
524 
525   protected void fireResourceLoaded(CreoleEvent e) {
526     if (creoleListeners != null) {
527       Vector listeners = creoleListeners;
528       int count = listeners.size();
529       for (int i = 0; i < count; i++) {
530         ((CreoleListener) listeners.elementAt(i)).resourceLoaded(e);
531       }// for
532     }// if
533   }// fireResourceLoaded(CreoleEvent e)
534 
535   protected void fireResourceUnloaded(CreoleEvent e) {
536     if (creoleListeners != null) {
537       Vector listeners = creoleListeners;
538       int count = listeners.size();
539       for (int i = 0; i < count; i++) {
540         ((CreoleListener) listeners.elementAt(i)).resourceUnloaded(e);
541       }// for
542     }// if
543   }// fireResourceUnloaded(CreoleEvent e)
544 
545   protected void fireDatastoreOpened(CreoleEvent e) {
546     if (creoleListeners != null) {
547       Vector listeners = creoleListeners;
548       int count = listeners.size();
549       for (int i = 0; i < count; i++) {
550         ((CreoleListener) listeners.elementAt(i)).datastoreOpened(e);
551       }// for
552     }// if
553   }// fireDatastoreOpened(CreoleEvent e)
554 
555   protected void fireDatastoreCreated(CreoleEvent e) {
556     if (creoleListeners != null) {
557       Vector listeners = creoleListeners;
558       int count = listeners.size();
559       for (int i = 0; i < count; i++) {
560         ((CreoleListener) listeners.elementAt(i)).datastoreCreated(e);
561       }// for
562     }// if
563   }// fireDatastoreCreated(CreoleEvent e)
564 
565   protected void fireDatastoreClosed(CreoleEvent e) {
566     if (creoleListeners != null) {
567       Vector listeners = creoleListeners;
568       int count = listeners.size();
569       for (int i = 0; i < count; i++) {
570         ((CreoleListener) listeners.elementAt(i)).datastoreClosed(e);
571       }// for
572     }// if
573   }// fireDatastoreClosed(CreoleEvent e)
574 
575 
576   private transient Vector creoleListeners;
577 }//class CreoleProxy
578