DuplicateResourceFactoryBean.java
001 package gate.util.spring;
002 
003 import java.io.IOException;
004 import java.lang.ref.WeakReference;
005 import java.lang.reflect.Proxy;
006 import java.util.ArrayList;
007 import java.util.Collections;
008 import java.util.List;
009 
010 import gate.Controller;
011 import gate.Corpus;
012 import gate.CorpusController;
013 import gate.Document;
014 import gate.Factory;
015 import gate.Gate;
016 import gate.LanguageAnalyser;
017 import gate.LanguageResource;
018 import gate.ProcessingResource;
019 import gate.Resource;
020 import gate.creole.ConditionalController;
021 import gate.creole.ResourceInstantiationException;
022 import gate.creole.gazetteer.Gazetteer;
023 import gate.creole.ontology.Ontology;
024 import gate.util.GateException;
025 
026 import org.springframework.beans.factory.DisposableBean;
027 import org.springframework.beans.factory.FactoryBean;
028 
029 /**
030  * Spring factory bean to create duplicate copies of a GATE resource.
031  * This bean would typically be declared with singleton scope, but the
032  * factory produces a new duplicate of its template resource each time
033  * getBean is called (or each time it is injected as a dependency).
034  */
035 public class DuplicateResourceFactoryBean extends GateAwareObject implements
036                                                                  FactoryBean,
037                                                                  DisposableBean {
038 
039   /**
040    * The template resource which we will duplicate.
041    */
042   private Resource templateResource;
043 
044   /**
045    * Should we return the template itself the first time
046    {@link #getObject()} is called, or should we keep it for use only
047    * as a template and just return duplicates?
048    */
049   private boolean returnTemplate = false;
050 
051   /**
052    * Customisers that are applied to the duplicated resource before it
053    * is returned.
054    */
055   private List<ResourceCustomiser> customisers;
056 
057   /**
058    * Set the template resource that this factory bean will duplicate.
059    */
060   public void setTemplate(Resource template) {
061     this.templateResource = template;
062   }
063 
064   /**
065    * Should this factory bean return the template resource itself the
066    * first time {@link #getObject()} is called, or should it always
067    * return a duplicate, keeping the template in pristine condition.
068    * Generally, if the duplicates will all be created up-front, and
069    * there are no configured customisers, then it is safe to set this
070    * option to true. In cases where the duplicates may be created
071    * asynchronously (possibly creating one duplicate while another one
072    * is in use in a different thread) or may be customised after
073    * creation it is safer to set this option to false (the default) to
074    * keep the template pristine and always return duplicates.
075    */
076   public void setReturnTemplate(boolean returnTemplate) {
077     this.returnTemplate = returnTemplate;
078   }
079 
080   /**
081    * Optional customisers that will be applied to the duplicate resource
082    * before it is returned. If customisers are specified then it is
083    * strongly recommended to leave the returnTemplate option set to
084    <code>false</code>.
085    */
086   public void setCustomisers(List<ResourceCustomiser> customisers) {
087     this.customisers = customisers;
088   }
089 
090   /**
091    * A list of weak references to the duplicates that have been
092    * returned, so they can be freed when this factory bean is disposed.
093    */
094   private List<WeakReference<Resource>> resourcesReturned = Collections
095           .synchronizedList(new ArrayList<WeakReference<Resource>>());
096 
097   private Class<?> typeClass = null;
098 
099   public Object getObject() throws Exception {
100     ensureGateInit();
101     Resource toReturn = null;
102     if(returnTemplate) {
103       synchronized(resourcesReturned) {
104         if(resourcesReturned.isEmpty()) {
105           resourcesReturned.add(new WeakReference<Resource>(templateResource));
106           toReturn = templateResource;
107         }
108       }
109     }
110 
111     if(toReturn == null) {
112       toReturn = Factory.duplicate(templateResource);
113     }
114 
115     if(customisers != null) {
116       for(ResourceCustomiser c : customisers) {
117         try {
118           c.customiseResource(toReturn);
119         }
120         catch(GateException gx) {
121           throw gx;
122         }
123         catch(IOException ix) {
124           throw ix;
125         }
126         catch(RuntimeException rx) {
127           throw rx;
128         }
129         catch(Exception e) {
130           throw new ResourceInstantiationException(
131                   "Exception in resource customiser", e);
132         }
133       }
134     }
135 
136     if(toReturn != templateResource) {
137       resourcesReturned.add(new WeakReference<Resource>(toReturn));
138     }
139 
140     return toReturn;
141   }
142 
143   /**
144    * Returns a proxy class that implements the same set of GATE
145    * interfaces as the template resource. See
146    {@link Factory#duplicate(Resource)} for the list of interfaces that
147    * will be considered.
148    */
149   public Class<?> getObjectType() {
150     if(templateResource != null && typeClass == null) {
151       List<Class<?>> interfaces = new ArrayList<Class<?>>();
152       interfaces.add(Resource.class);
153       if(templateResource instanceof ProcessingResource) {
154         interfaces.add(ProcessingResource.class);
155       }
156       if(templateResource instanceof LanguageAnalyser) {
157         interfaces.add(LanguageAnalyser.class);
158       }
159       if(templateResource instanceof Gazetteer) {
160         interfaces.add(Gazetteer.class);
161       }
162       if(templateResource instanceof Controller) {
163         interfaces.add(Controller.class);
164       }
165       if(templateResource instanceof CorpusController) {
166         interfaces.add(CorpusController.class);
167       }
168       if(templateResource instanceof ConditionalController) {
169         interfaces.add(ConditionalController.class);
170       }
171       if(templateResource instanceof LanguageResource) {
172         interfaces.add(LanguageResource.class);
173       }
174       if(templateResource instanceof Ontology) {
175         interfaces.add(Ontology.class);
176       }
177       if(templateResource instanceof Document) {
178         interfaces.add(Document.class);
179       }
180       if(templateResource instanceof Corpus) {
181         interfaces.add(Corpus.class);
182       }
183       typeClass = Proxy.getProxyClass(Gate.getClassLoader(), interfaces
184               .toArray(new Class<?>[interfaces.size()]));
185     }
186     return typeClass;
187   }
188 
189   /**
190    * This factory is not a singleton - it produces a new object each
191    * time {@link #getObject()} is called.
192    */
193   public boolean isSingleton() {
194     return false;
195   }
196 
197   /**
198    * Delete the duplicates we have returned, unless they have already
199    * been freed.
200    */
201   public void destroy() {
202     for(WeakReference<Resource> res : resourcesReturned) {
203       Resource r = res.get();
204       if(r != null) {
205         Factory.deleteResource(r);
206       }
207     }
208   }
209 }