PooledProxyBeanDefinitionDecorator.java
001 /*
002  *  PooledProxyBeanDefinitionDecorator.java
003  *
004  *  Copyright (c) 1995-2010, The University of Sheffield. See the file
005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
006  *
007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
008  *  software, licenced under the GNU Library General Public License,
009  *  Version 2, June 1991 (in the distribution as file licence.html,
010  *  and also available at http://gate.ac.uk/gate/licence.html).
011  *
012  *  Ian Roberts, 10/Apr/2010
013  *
014  *  $Id: PooledProxyBeanDefinitionDecorator.java 12496 2010-04-15 16:20:57Z ian_roberts $
015  */
016 package gate.util.spring.xml;
017 
018 import org.springframework.beans.factory.config.BeanDefinition;
019 import org.springframework.beans.factory.config.BeanDefinitionHolder;
020 import org.springframework.beans.factory.config.RuntimeBeanReference;
021 import org.springframework.beans.factory.support.AbstractBeanDefinition;
022 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
023 import org.springframework.beans.factory.support.RootBeanDefinition;
024 import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
025 import org.springframework.beans.factory.xml.ParserContext;
026 import org.springframework.core.Conventions;
027 import org.w3c.dom.Attr;
028 import org.w3c.dom.Element;
029 import org.w3c.dom.NamedNodeMap;
030 import org.w3c.dom.Node;
031 
032 /**
033  * Bean decorator to easily create a pool of target beans.
034  
035  <pre>
036  * &lt;bean id=&quot;myBean&quot; class=&quot;some.pkg.MyBean&quot;&gt;
037  *   &lt;property name=&quot;gateApplication&quot; ref=&quot;app&quot; /&gt;
038  *   &lt;gate:pooled-proxy max-size=&quot;3&quot; /&gt;
039  * &lt;/bean&gt;
040  </pre>
041  
042  <p>
043  * This replaces the <code>myBean</code> bean with a proxy that
044  * delegates to a pool of target objects. The targets are obtained by
045  * converting the original <code>myBean</code> definition to a
046  * prototype-scoped bean definition and setting that as the
047  * targetBeanName of a CommonsPoolTargetSource. The
048  * CommonsPoolTargetSource is then used as the target source for a
049  * standard ProxyFactoryBean, which is created with the scope specified
050  * in the original <code>myBean</code> definition (usually singleton).
051  </p>
052  
053  <p>
054  * If the pooled-proxy element has an attribute proxy-target-class, this
055  * value is passed through to the generated ProxyFactoryBean. All other
056  * attributes are passed through to the CommonsPoolTargetSource. The
057  * element also supports an initial-size attribute. If specified it will
058  * pre-populate the pool with this number of instances, which is useful
059  * if you want to load several copies of a saved application up-front
060  * (the normal behaviour of a Spring pool is to only instantiate the
061  * pooled objects as and when they are required).
062  </p>
063  
064  <p>
065  <b>NOTE</b> In addition to the <code>spring-aop</code> JAR, you
066  * also need the Apache <code>commons-pool</code> JAR and its
067  * dependencies available to your application in order to use this
068  * class.
069  </p>
070  */
071 public class PooledProxyBeanDefinitionDecorator implements
072                                                BeanDefinitionDecorator {
073 
074   public static final String TARGET_PREFIX = "gate.util.spring.pool-target.";
075 
076   public static final String TARGET_SOURCE_PREFIX = "gate.util.spring.pool-target-source.";
077 
078   public static final String POOL_FILLER_PREFIX = "gate.util.spring.pool-filler.";
079 
080   private static final String PROXY_TARGET_CLASS = "proxy-target-class";
081 
082   private static final String INITIAL_SIZE = "initial-size";
083 
084   public BeanDefinitionHolder decorate(Node node,
085           BeanDefinitionHolder definition, ParserContext parserContext) {
086     String originalBeanName = definition.getBeanName();
087     String[] originalAliases = definition.getAliases();
088     String targetBeanName = TARGET_PREFIX + originalBeanName;
089 
090     BeanDefinition targetDefinition = definition.getBeanDefinition();
091     BeanDefinitionRegistry reg = parserContext.getRegistry();
092 
093     // remember the scope of the original definition,
094     // change the target bean to be prototype.
095     String originalScope = targetDefinition.getScope();
096     targetDefinition.setScope("prototype");
097 
098     // create a bean definition for the target source, pointing at the
099     // target bean name
100     RootBeanDefinition targetSourceDefinition = new RootBeanDefinition();
101     targetSourceDefinition.setScope(originalScope);
102     targetSourceDefinition
103             .setBeanClassName("org.springframework.aop.target.CommonsPoolTargetSource");
104     targetSourceDefinition.getPropertyValues().addPropertyValue(
105             "targetBeanName", targetBeanName);
106     String targetSourceBeanName = TARGET_SOURCE_PREFIX + originalBeanName;
107 
108     // apply any attributes of the pooled-proxy element except
109     // proxy-target-class and initial-size as properties of the
110     // target source
111     if(node instanceof Element) {
112       Element ele = (Element)node;
113       NamedNodeMap attrs = ele.getAttributes();
114       for(int i = 0; i < attrs.getLength(); i++) {
115         Attr att = (Attr)attrs.item(i);
116         if(!PROXY_TARGET_CLASS.equals(att.getLocalName())
117                 && !INITIAL_SIZE.equals(att.getLocalName())) {
118           String propName = Conventions.attributeNameToPropertyName(att
119                   .getLocalName());
120           targetSourceDefinition.getPropertyValues().addPropertyValue(propName,
121                   att.getValue());
122         }
123       }
124     }
125 
126     // create a bean definition for the proxy, pointing to the target
127     // source
128     RootBeanDefinition proxyDefinition = new RootBeanDefinition();
129     proxyDefinition.setScope(originalScope);
130     proxyDefinition
131             .setBeanClassName("org.springframework.aop.framework.ProxyFactoryBean");
132     proxyDefinition.getPropertyValues().addPropertyValue("targetSource",
133             new RuntimeBeanReference(targetSourceBeanName));
134 
135     Boolean proxyTargetClass = Boolean.TRUE;
136     if(node instanceof Element) {
137       Element ele = (Element)node;
138       if(ele.hasAttribute(PROXY_TARGET_CLASS)) {
139         proxyTargetClass = Boolean
140                 .valueOf(ele.getAttribute(PROXY_TARGET_CLASS));
141       }
142     }
143     proxyDefinition.getPropertyValues().addPropertyValue("proxyTargetClass",
144             proxyTargetClass);
145 
146     if(targetDefinition instanceof AbstractBeanDefinition) {
147       AbstractBeanDefinition abd = (AbstractBeanDefinition)targetDefinition;
148       proxyDefinition.setDependsOn(abd.getDependsOn());
149       proxyDefinition.setAutowireCandidate(abd.isAutowireCandidate());
150       // The target bean should be ignored in favor of the scoped proxy.
151       abd.setAutowireCandidate(false);
152     }
153 
154     // if we have an initial-size attribute, create a pool filler bean
155     // to pre-fill the pool to the stated initial size
156     if(node instanceof Element && ((Element)node).hasAttribute(INITIAL_SIZE)) {
157       RootBeanDefinition poolFillerDefinition = new RootBeanDefinition(
158               PoolFiller.class);
159       String poolFillerBeanName = POOL_FILLER_PREFIX + originalBeanName;
160       poolFillerDefinition.getPropertyValues().addPropertyValue("targetSource",
161               new RuntimeBeanReference(targetSourceBeanName));
162       poolFillerDefinition.getPropertyValues().addPropertyValue("numInstances",
163               ((Element)node).getAttribute(INITIAL_SIZE));
164       poolFillerDefinition.setScope(targetSourceDefinition.getScope());
165       // make the proxy depend on the pool filler
166       String[] proxyDepends = proxyDefinition.getDependsOn();
167       if(proxyDepends == null) {
168         proxyDepends = new String[1];
169       }
170       else {
171         String[] newDepends = new String[proxyDepends.length + 1];
172         System.arraycopy(proxyDepends, 0, newDepends, 0, proxyDepends.length);
173         proxyDepends = newDepends;
174       }
175       proxyDepends[proxyDepends.length - 1= poolFillerBeanName;
176       proxyDefinition.setDependsOn(proxyDepends);
177 
178       reg.registerBeanDefinition(poolFillerBeanName, poolFillerDefinition);
179     }
180 
181     // register the (prototype) target bean under its new name
182     reg.registerBeanDefinition(targetBeanName, targetDefinition);
183     // register the target source
184     reg.registerBeanDefinition(targetSourceBeanName, targetSourceDefinition);
185 
186     // return the bean definition for the proxy
187     return new BeanDefinitionHolder(proxyDefinition, originalBeanName,
188             originalAliases);
189   }
190 
191 }