View Javadoc

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 }