1 /*
2 * EL4J, the Extension Library for the J2EE, adds incremental enhancements to
3 * the spring framework, http://el4j.sf.net
4 * Copyright (C) 2005 by ELCA Informatique SA, Av. de la Harpe 22-24,
5 * 1000 Lausanne, Switzerland, http://www.elca.ch
6 *
7 * EL4J is published under the GNU Lesser General Public License (LGPL)
8 * Version 2.1. See http://www.gnu.org/licenses/
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * For alternative licensing, please contact info@elca.ch
16 */
17 package ch.elca.el4j.core.beans;
18
19 import java.util.HashMap;
20 import java.util.Map;
21
22 import org.springframework.beans.factory.InitializingBean;
23 import org.springframework.beans.factory.support.DefaultListableBeanFactory;
24 import org.springframework.context.ApplicationContext;
25 import org.springframework.context.ApplicationContextAware;
26
27 /**
28 * Locates all beans in the application context that match the configured
29 * criteria.
30 * <ul>
31 * <li>In the <code>classes</code> property a comma-separated list of classes
32 * whose instances to include in the result can be specified.</li>
33 * <li>In the <code>excludeBeanNames</code> property a comma-separated list
34 * of bean names to exclude from the result can be specified.</li>
35 * <li>In the <code>includeBeanNames</code> property a comma-separated list
36 * of bean names to include in the result can be specified.</li>
37 * <li>In the <code>includeFactoryBeans</code> property it is specified if
38 * FactoryBeans should be included in the result. Default value = true.</li>
39 * <li>In the <code>includePrototypes</code> property it is specified if
40 * prototype beans should be included in the result. Default value = true.</li>
41 * </ul>
42 *
43 * For the <code>excludeBeanNames</code> and <code>includeBeanNames</code>
44 * properties wildcards <code>xxx*</code> and <code>*xxx</code> are allowed.
45 *
46 * If a bean is listed in <code>includeBeanNames</code> and
47 * <code>excludeBeanNames</code> it will be excluded.
48 *
49 * Beans which exist in application context and are listed in
50 * <code>includeBeanNames</code> but are not an instance of a listed class in
51 * <code>classes</code> will be also returned on invocation of method
52 * <code>getBeans</code>.
53 *
54 *
55 * @svnLink $Revision: 3873 $;$Date: 2009-08-04 13:59:45 +0200 (Di, 04. Aug 2009) $;$Author: swismer $;$URL: https://el4j.svn.sourceforge.net/svnroot/el4j/branches/el4j_3_1/el4j/framework/modules/core/src/main/java/ch/elca/el4j/core/beans/BeanLocator.java $
56 *
57 * @author Andreas Pfenninger (APR)
58 */
59 public class BeanLocator implements ApplicationContextAware, InitializingBean {
60
61 /**
62 * The delimiter (",") for the include- and exclude bean names properties.
63 */
64 private static final String DELIMITER = ",";
65
66 /** The application context. */
67 private ApplicationContext m_applicationContext;
68
69 /**
70 * A comma-delimited String of all the classes whose instances have to be
71 * located.
72 */
73 private String m_classes;
74
75 /**
76 * An array of the classes whose instances have to be located, converted
77 * from the <code>m_classes</code> String.
78 */
79 private Class<?>[] m_classesArray;
80
81 /**
82 * A comma-delimited String of all bean names that have to be excluded from
83 * the result. <b>Wildcards </b> *xxx and xxx* are allowed.
84 */
85 private String m_excludeBeanNames;
86
87 /**
88 * An array of the bean names to be excluded, converted from the
89 * <code>m_excludeBeanNames</code> String.
90 */
91 private String[] m_excludeBeanNamesArray;
92
93 /**
94 * A comma-delimited String of all bean names that have to be included in
95 * the result. <b>Wildcards </b> *xxx and xxx* are allowed.
96 */
97 private String m_includeBeanNames;
98
99 /**
100 * An array of the bean names to be included, converted from the
101 * <code>m_excludeBeanNames</code> String.
102 */
103 private String[] m_includeBeanNamesArray;
104
105 /**
106 * Whether to include FactoryBeans too or just conventional beans. Default
107 * value: true.
108 */
109 private boolean m_includeFactoryBeans = true;
110
111 /**
112 * Whether to include prototype beans too or just singletons (also applies
113 * to FactoryBeans). Default value: true.
114 */
115 private boolean m_includePrototypes = true;
116
117 /**
118 * @param classes
119 * The comma-delimited String of all the classes whose instances
120 * have to be located.
121 */
122 public void setClasses(String classes) {
123 m_classes = classes;
124 }
125
126 /**
127 * @param excludeBeanNames
128 * The comma-delimited String of all bean names that have to be
129 * excluded from the result.
130 */
131 public void setExcludeBeanNames(String excludeBeanNames) {
132 m_excludeBeanNames = excludeBeanNames;
133 }
134
135 /**
136 * @param includeBeanNames
137 * The comma-delimited String of all bean names that have to be
138 * included in the result.
139 */
140 public void setIncludeBeanNames(String includeBeanNames) {
141 m_includeBeanNames = includeBeanNames;
142 }
143
144 /**
145 * @param includeFactoryBeans
146 * Whether to include FactoryBeans too or just conventional
147 * beans.
148 */
149 public void setIncludeFactoryBeans(boolean includeFactoryBeans) {
150 m_includeFactoryBeans = includeFactoryBeans;
151 }
152
153 /**
154 * @param includePrototypes
155 * Whether to include prototype beans too or just singletons
156 * (also applies to FactoryBeans).
157 */
158 public void setIncludePrototypes(boolean includePrototypes) {
159 m_includePrototypes = includePrototypes;
160 }
161
162 /**
163 * {@inheritDoc}
164 */
165 public void setApplicationContext(ApplicationContext applicationContext) {
166 m_applicationContext = applicationContext;
167 }
168
169 /**
170 * {@inheritDoc}
171 */
172 public void afterPropertiesSet() throws Exception {
173 if (m_classes != null && m_classes.length() > 0
174 && m_classes.trim().length() > 0) {
175 m_classes = m_classes.replaceAll(" ", "");
176 String[] classesStringArray = m_classes.split(DELIMITER);
177 m_classesArray = new Class[classesStringArray.length];
178 for (int i = 0; i < classesStringArray.length; i++) {
179 m_classesArray[i] = Class.forName(classesStringArray[i]);
180 }
181 }
182 if (m_includeBeanNames != null
183 && m_includeBeanNames.trim().length() > 0) {
184 m_includeBeanNames = m_includeBeanNames.replaceAll(" ", "");
185 m_includeBeanNamesArray = m_includeBeanNames.split(DELIMITER);
186 }
187 if (m_excludeBeanNames != null
188 && m_excludeBeanNames.trim().length() > 0) {
189 m_excludeBeanNames = m_excludeBeanNames.replaceAll(" ", "");
190 m_excludeBeanNamesArray = m_excludeBeanNames.split(DELIMITER);
191 }
192 }
193
194 /**
195 * Returns all beans that match the specified search criteria.
196 *
197 * @return A map with the matching beans, containing the bean names as keys
198 * and the corresponding bean instances as values.
199 */
200 public Map<String,Object> getBeans() {
201 Map<String,Object> beans = new HashMap<String,Object>();
202 if (m_classesArray != null) {
203 for (int i = 0; i < m_classesArray.length; i++) {
204 Map addbeans = m_applicationContext.getBeansOfType(
205 m_classesArray[i], m_includePrototypes,
206 m_includeFactoryBeans);
207 beans.putAll(addbeans);
208 }
209 }
210
211 String[] beanNames = m_applicationContext.getBeanDefinitionNames();
212 for (int i = 0; i < beanNames.length; i++) {
213 handleIncludedBeanNames(beans, beanNames[i]);
214 handleExcludedBeanNames(beans, beanNames[i]);
215 }
216 return beans;
217 }
218
219 /**
220 * Method to include beans with a given name.
221 *
222 * @param beans
223 * Are the currently collected beans.
224 * @param beanName
225 * Is the name of the bean to include.
226 */
227 private void handleIncludedBeanNames(Map<String,Object> beans, String beanName) {
228 if (m_includeBeanNamesArray != null) {
229 for (int j = 0; j < m_includeBeanNamesArray.length; j++) {
230 if (isMatch(beanName, m_includeBeanNamesArray[j])) {
231 boolean prototypeConstraint = !m_includePrototypes
232 && !m_applicationContext.isSingleton(beanName);
233 DefaultListableBeanFactory beanFactory
234 = new DefaultListableBeanFactory(
235 m_applicationContext);
236 boolean factoryBeanConstraint = !m_includeFactoryBeans
237 && (beanFactory.isFactoryBean(beanName));
238 if (!prototypeConstraint && !factoryBeanConstraint) {
239 Object bean = m_applicationContext.getBean(beanName);
240 beans.put(beanName, bean);
241 }
242 }
243 }
244 }
245 }
246
247 /**
248 * Method to exclude beans with a given name.
249 *
250 * @param beans
251 * Are the currently collected beans.
252 * @param beanName
253 * Is the name of the bean to exclude.
254 */
255 private void handleExcludedBeanNames(Map beans, String beanName) {
256 if (m_excludeBeanNamesArray != null) {
257 for (int j = 0; j < m_excludeBeanNamesArray.length; j++) {
258 if (isMatch(beanName, m_excludeBeanNamesArray[j])) {
259 beans.remove(beanName);
260 }
261 }
262 }
263 }
264
265 /**
266 * Returns whether the given bean name matches the mapped name. The default
267 * implementation checks for "xxx*", "*xxx" and exact matches. Can be
268 * overridden in subclasses.
269 *
270 * @param beanName
271 * The bean name to check.
272 * @param mappedName
273 * The name in the configured list of names.
274 * @return True if the names match, false otherwise.
275 */
276 protected boolean isMatch(String beanName, String mappedName) {
277 boolean match = false;
278 if (beanName.equals(mappedName)) {
279 match = true;
280 } else {
281 boolean prefixMatch = (mappedName.endsWith("*") && beanName
282 .startsWith(mappedName
283 .substring(0, mappedName.length() - 1)));
284 boolean suffixMatch = (mappedName.startsWith("*") && beanName
285 .endsWith(mappedName.substring(1, mappedName.length())));
286 match = prefixMatch || suffixMatch;
287 }
288 return match;
289 }
290 }