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   @Override
100   public Object getObject() throws Exception {
101     ensureGateInit();
102     Resource toReturn = null;
103     if(returnTemplate) {
104       synchronized(resourcesReturned) {
105         if(resourcesReturned.isEmpty()) {
106           resourcesReturned.add(new WeakReference<Resource>(templateResource));
107           toReturn = templateResource;
108         }
109       }
110     }
111 
112     if(toReturn == null) {
113       toReturn = Factory.duplicate(templateResource);
114     }
115 
116     if(customisers != null) {
117       for(ResourceCustomiser c : customisers) {
118         try {
119           c.customiseResource(toReturn);
120         }
121         catch(GateException gx) {
122           throw gx;
123         }
124         catch(IOException ix) {
125           throw ix;
126         }
127         catch(RuntimeException rx) {
128           throw rx;
129         }
130         catch(Exception e) {
131           throw new ResourceInstantiationException(
132                   "Exception in resource customiser", e);
133         }
134       }
135     }
136 
137     if(toReturn != templateResource) {
138       resourcesReturned.add(new WeakReference<Resource>(toReturn));
139     }
140 
141     return toReturn;
142   }
143 
144   /**
145    * Returns a proxy class that implements the same set of GATE
146    * interfaces as the template resource. See
147    {@link Factory#duplicate(Resource)} for the list of interfaces that
148    * will be considered.
149    */
150   @Override
151   public Class<?> getObjectType() {
152     if(templateResource != null && typeClass == null) {
153       List<Class<?>> interfaces = new ArrayList<Class<?>>();
154       interfaces.add(Resource.class);
155       if(templateResource instanceof ProcessingResource) {
156         interfaces.add(ProcessingResource.class);
157       }
158       if(templateResource instanceof LanguageAnalyser) {
159         interfaces.add(LanguageAnalyser.class);
160       }
161       if(templateResource instanceof Gazetteer) {
162         interfaces.add(Gazetteer.class);
163       }
164       if(templateResource instanceof Controller) {
165         interfaces.add(Controller.class);
166       }
167       if(templateResource instanceof CorpusController) {
168         interfaces.add(CorpusController.class);
169       }
170       if(templateResource instanceof ConditionalController) {
171         interfaces.add(ConditionalController.class);
172       }
173       if(templateResource instanceof LanguageResource) {
174         interfaces.add(LanguageResource.class);
175       }
176       if(templateResource instanceof Ontology) {
177         interfaces.add(Ontology.class);
178       }
179       if(templateResource instanceof Document) {
180         interfaces.add(Document.class);
181       }
182       if(templateResource instanceof Corpus) {
183         interfaces.add(Corpus.class);
184       }
185       typeClass = Proxy.getProxyClass(Gate.getClassLoader(), interfaces
186               .toArray(new Class<?>[interfaces.size()]));
187     }
188     return typeClass;
189   }
190 
191   /**
192    * This factory is not a singleton - it produces a new object each
193    * time {@link #getObject()} is called.
194    */
195   @Override
196   public boolean isSingleton() {
197     return false;
198   }
199 
200   /**
201    * Delete the duplicates we have returned, unless they have already
202    * been freed.
203    */
204   @Override
205   public void destroy() {
206     for(WeakReference<Resource> res : resourcesReturned) {
207       Resource r = res.get();
208       if(r != null) {
209         Factory.deleteResource(r);
210       }
211     }
212   }
213 }