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 * <bean id="myBean" class="some.pkg.MyBean">
037 * <property name="gateApplication" ref="app" />
038 * <gate:pooled-proxy max-size="3" />
039 * </bean>
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 }
|