001 /*
002 * ============================================================================
003 * GNU Lesser General Public License
004 * ============================================================================
005 *
006 * Beanlet - JSE Application Container.
007 * Copyright (C) 2006 Leon van Zantvoort
008 *
009 * This library is free software; you can redistribute it and/or
010 * modify it under the terms of the GNU Lesser General Public
011 * License as published by the Free Software Foundation; either
012 * version 2.1 of the License, or (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017 * Lesser General Public License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
022 *
023 * Leon van Zantvoort
024 * 243 Acalanes Drive #11
025 * Sunnyvale, CA 94086
026 * USA
027 *
028 * zantvoort@users.sourceforge.net
029 * http://beanlet.org
030 */
031 package org.beanlet;
032
033 import java.io.BufferedReader;
034 import java.io.InputStreamReader;
035 import java.lang.reflect.Constructor;
036 import java.lang.reflect.InvocationTargetException;
037 import java.lang.reflect.Modifier;
038 import java.net.URL;
039 import java.security.AccessController;
040 import java.security.PrivilegedActionException;
041 import java.security.PrivilegedExceptionAction;
042 import java.util.Enumeration;
043 import java.util.Map;
044 import java.util.Set;
045
046 /**
047 * <p>Bootstrap class for the application container. An instance of the
048 * {@code BeanletApplicationContext} can be obtained through the static
049 * {@code instance} method. If the container isn't already running, the
050 * first call to this method automatically starts and initializes the container.
051 * </p>
052 *
053 * <p>During initialization, the container registers the beanlets listed in all
054 * the {@code beanlet.xml} and {@code META-INF/beanlet.xml} files that are
055 * available from the container's classpath.</p>
056 *
057 * <p>The beanlet application context providess access to all registered
058 * beanlets. All methods of this class are thread-safe. They can be called at
059 * all time. That includes during initialization of the application container.
060 * </p>
061 *
062 * @author Leon van Zantvoort
063 */
064 public abstract class BeanletApplicationContext {
065
066 /**
067 * The LazyHolder class is responsible for looking up the Beanlet container
068 * implementation using Jar service discovery. Furthermore, this static
069 * class provides a solution for the flawed double-checked locking idiom,
070 */
071 private static class LazyHolder {
072 static final BeanletApplicationContext ctx;
073 static {
074 try {
075 try {
076 Constructor constructor = AccessController.doPrivileged(
077 new PrivilegedExceptionAction<Constructor>() {
078 public Constructor run() throws Exception {
079 String path = "META-INF/services/" +
080 BeanletApplicationContext.class.getName();
081
082 // PERMISSION: java.lang.RuntimePermission getClassLoader
083 ClassLoader loader = Thread.currentThread().
084 getContextClassLoader();
085 final Enumeration<URL> urls;
086 if (loader == null) {
087 urls = BeanletApplicationContext.class.
088 getClassLoader().getResources(path);
089 } else {
090 urls = loader.getResources(path);
091 }
092 while (urls.hasMoreElements()) {
093 URL url = urls.nextElement();
094 BufferedReader reader = new BufferedReader(new InputStreamReader(
095 url.openStream()));
096 try {
097 String className = null;
098 while ((className = reader.readLine()) != null) {
099 final String name = className.trim();
100 if (!name.startsWith("#") && !name.startsWith(";") &&
101 !name.startsWith("//")) {
102 final Class<?> cls;
103 if (loader == null) {
104 cls = Class.forName(name);
105 } else {
106 cls = Class.forName(name, true, loader);
107 }
108 int m = cls.getModifiers();
109 if (BeanletApplicationContext.class.isAssignableFrom(cls) &&
110 !Modifier.isAbstract(m) &&
111 !Modifier.isInterface(m)) {
112 // PERMISSION: java.lang.RuntimePermission accessDeclaredMembers
113 Constructor constructor = cls.getDeclaredConstructor();
114 // PERMISSION: java.lang.reflect.ReflectPermission suppressAccessChecks
115 if (!Modifier.isPublic(constructor.getModifiers())) {
116 constructor.setAccessible(true);
117 }
118 return constructor;
119 } else {
120 throw new ClassCastException(cls.getName());
121 }
122 }
123 }
124 } finally {
125 reader.close();
126 }
127 }
128 throw new BeanletApplicationException("No " +
129 "BeanletApplicationContext implementation " +
130 "found.");
131 }
132 });
133 ctx = (BeanletApplicationContext) constructor.newInstance();
134 } catch (PrivilegedActionException e) {
135 throw e.getException();
136 } catch (InvocationTargetException e) {
137 throw e.getTargetException();
138 }
139 } catch (BeanletApplicationException e) {
140 throw e;
141 } catch (Error e) {
142 throw e;
143 } catch (Throwable t) {
144 throw new BeanletApplicationException(t);
145 }
146 }
147 }
148
149 /**
150 * <p>Returns a {@code BeanletApplicationContext} instance. If the container
151 * isn't already running, the first call to this method automatically starts
152 * and initializes the container.</p>
153 *
154 * <p>It is not specified whether multiple calls to this method results in
155 * a single or multiple instances of the {@code BeanletApplicationContext}.
156 * However, as the container only starts once, invoking this method multiple
157 * times is valid and does not consume any additional resources.</p>
158 *
159 * @return a {@code BeanletApplicationContext} instance.
160 * @throws BeanletApplicationException indicates an error during container
161 * initialization.
162 */
163 public static BeanletApplicationContext instance() throws
164 BeanletApplicationException {
165 try {
166 try {
167 return LazyHolder.ctx.resolveInstance();
168 } catch (ExceptionInInitializerError e) {
169 try {
170 throw e.getException();
171 } catch (BeanletApplicationException e2) {
172 throw e2;
173 } catch (Error e2) {
174 throw e2;
175 } catch (Throwable t) {
176 throw new BeanletApplicationException(t);
177 }
178 }
179 } catch (BeanletApplicationException e) { // Don't catch Errors and RuntimeExceptions.
180 throw e;
181 }
182 }
183
184 /**
185 * Subclasses of this class can implement this method to control which
186 * instance is returned to the caller of the static {@code instance} method.
187 * The default implementation of this method simply returns {@code this}.
188 *
189 * @return a beanlet application context reference.
190 * @throws BeanletApplicationContext indicates an error during container
191 * initialization.
192 */
193 protected BeanletApplicationContext resolveInstance() throws
194 BeanletApplicationException {
195 return this;
196 }
197
198 /**
199 * Undeploys all components and stops all internal container threads.
200 * @throws BeanletApplicationContext indicates an error during container
201 * shutdown.
202 */
203 public abstract void shutdown() throws BeanletApplicationException;
204
205 /**
206 * Factory method for the specified {@code eventType}.
207 *
208 * @return a concrete implementation of the specified {@code eventType}.
209 */
210 public abstract <T extends Event> T getEvent(Class<T> eventType);
211
212 /**
213 * <p>Returns a beanlet instance for the specified {@code beanletName}.</p>
214 *
215 * <p>If his beanlet instance implements the {@code FactoryBeanlet}
216 * interface, the result of {@link FactoryBeanlet#getObject} is returned.
217 * Prefix the {@code beanletName} with {@code "&"} to obtain an instance to
218 * the {@code FactoryBeanlet} itself.</p>
219 *
220 * @param beanletName name of the beanlet.
221 * @return a beanlet.
222 * @throws BeanletNotFoundException if beanlet does not exist.
223 * @throws BeanletCreationException if beanlet could not be created for any
224 * reason.
225 */
226 public abstract Object getBeanlet(String beanletName) throws
227 BeanletNotFoundException, BeanletCreationException;
228
229 /**
230 * <p>Returns a beanlet instance for the specified {@code beanletName}. If
231 * no beanlet exists for the specied {@code beanletName} a
232 * {@code BeanletNotFoundException} is thrown. A
233 * {@code BeanletNotOfRequiredTypeException} is thrown if the beanlet
234 * instance cannot be assigned to the {@code requiredType}.</p>
235 *
236 * <p>If his beanlet instance implements the {@code FactoryBeanlet}
237 * interface, the result of {@link FactoryBeanlet#getObject} is returned.
238 * Prefix the {@code beanletName} with {@code "&"} to obtain an instance to
239 * the {@code FactoryBeanlet} itself.</p>
240 *
241 * <p>The entries of the {@code info} argument can be used to wire members
242 * of the beanlet instance. This does not apply to singleton and stateless
243 * beanlets.</p>
244 *
245 * @param beanletName name of the beanlet.
246 * @param requiredType type to mached the beanlet type.
247 * @return a beanlet.
248 * @throws BeanletNotFoundException if beanlet does not exist.
249 * @throws BeanletCreationException if beanlet could not be created for
250 * any reason.
251 * @throws BeanletNotOfRequiredTypeException if beanlet cannot be assigned
252 * to the {@code requiredType}.
253 * @see Inject
254 * @see Wiring
255 */
256 public abstract <T> T getBeanlet(String beanletName, Class<T> requiredType)
257 throws BeanletNotFoundException, BeanletCreationException,
258 BeanletNotOfRequiredTypeException;
259
260 /**
261 * <p>Returns a beanlet for the specified {@code beanletName}.</p>
262 *
263 * <p>If this beanlet implements the {@code FactoryBeanlet}
264 * interface, the result of {@link FactoryBeanlet#getObject} is returned.
265 * Prefix the {@code beanletName} with {@code "&"} to obtain an instance to
266 * the {@code FactoryBeanlet} itself.</p>
267 *
268 * <p>The entries of the {@code info} argument can be used to wire members
269 * of the beanlet instance. This does not apply to static beanlets.</p>
270 *
271 * @param beanletName name of the beanlet.
272 * @param info map that contains parameters that can be injected into
273 * the beanlet instance.
274 * @return a beanlet.
275 * @throws BeanletNotFoundException if beanlet does not exist.
276 * @throws BeanletCreationException if beanlet could not be
277 * created for any reason.
278 * @see Inject
279 * @see Wiring
280 */
281 public abstract Object getBeanlet(String beanletName,
282 Map<String, ?> info) throws BeanletNotFoundException,
283 BeanletCreationException;
284
285 /**
286 * <p>Returns a {@code BeanletFactory} for the specified {@code beanletName}.
287 * If no beanlet exists for the specified {@code beanletName} a
288 * {@code BeanletNotFoundException} is thrown. If the beanlet definition
289 * type is either the same as, or a subclass of the specified
290 * {@code requiredType}, a generified {@code BeanletFactory} is returned
291 * with the {@code requiredType} as upper bound.</p>
292 *
293 * <p>This method ignores the {@code "&"} prefix.</p>
294 *
295 * <p>The entries of the {@code info} argument can be used to wire members
296 * of the beanlet instance. This does not apply to static beanlets.</p>
297 *
298 * @param beanletName name of the beanlet.
299 * @param requiredType type to mached the beanlet type.
300 * @param info map that contains parameters that can be injected into
301 * the beanlet instance.
302 * @return a {@code BeanletFactory} for the specified {@code beanletName}.
303 * @throws BeanletNotFoundException if beanlet does not exist.
304 * @throws BeanletCreationException if beanlet could not be created for any
305 * reason.
306 * @throws BeanletNotOfRequiredTypeException if beanlet cannot be assigned
307 * to the {@code requiredType}.
308 * @see Inject
309 * @see Wiring
310 */
311 public abstract <T> T getBeanlet(String beanletName, Class<T> requiredType,
312 Map<String, ?> info) throws BeanletNotFoundException,
313 BeanletCreationException, BeanletNotOfRequiredTypeException;
314
315 /**
316 * Returns an immutable set of beanlet names of all registered beanlets.
317 */
318 public abstract Set<String> getBeanletNames();
319
320 /**
321 * Returns an immutable set of beanlet names of all registered beanlets,
322 * which the beanlet instance type is the same as, or a subclass of the
323 * specified {@code type}.
324 *
325 * @param type type to mached the beanlet type.
326 */
327 public abstract Set<String> getBeanletNamesForType(Class<?> type);
328
329 /**
330 * Returns an immutable set of beanlet names of all registered beanlets,
331 * which the beanlet instance type is the same as, or a subclass of the
332 * specified {@code type}. Additionally, beanlet factories, which return
333 * type match the specified {@code type} can be added as well, if
334 * {@code factoryAware} is set to {@code true}. Set {@code usePrefix} to
335 * {@code true} if these beanlet names must be prepended with "&".
336 *
337 * @param type type to mached the beanlet type, or factory
338 * beanlet return type (optional).
339 * @param factoryAware specify {@code true} to include factory beanlets,
340 * which return type match the given type.
341 * @param usePrefix specify {@code true} to prefix factory beanlet names.
342 */
343 public abstract Set<String> getBeanletNamesForType(Class<?> type,
344 boolean factoryAware, boolean usePrefix);
345
346 /**
347 * <p>Returns a {@code BeanletFactory} for the specified {@code beanletName},
348 * or throws a {@code BeanletNotFoundException} if beanlet does not exist.</p>
349 *
350 * <p>This method ignores the {@code "&"} prefix.</p>
351 *
352 * @param beanletName name of the beanlet.
353 * @return a {@code BeanletFactory} for the specified {@code beanletName}.
354 * @throws BeanletNotFoundException if beanlet does not exist.
355 */
356 public abstract BeanletFactory<?> getBeanletFactory(String beanletName)
357 throws BeanletNotFoundException;
358
359 /**
360 * <p>Returns a {@code BeanletFactory} for the specified {@code beanletName}.
361 * If no beanlet exists for the specified {@code beanletName} a
362 * {@code BeanletNotFoundException} is thrown. If the beanlet definition
363 * type is either the same as, or a subclass of the specified
364 * {@code requiredType}, a generified {@code BeanletFactory} is returned
365 * with the {@code requiredType} as upper bound.</p>
366 *
367 * <p>This method ignores the {@code "&"} prefix.</p>
368 *
369 * @param beanletName name of the beanlet.
370 * @param requiredType type to mached the beanlet type.
371 * @return a {@code BeanletFactory} for the specified {@code beanletName}.
372 * @throws BeanletNotFoundException if beanlet does not exist.
373 * @throws BeanletNotOfRequiredTypeException if beanlet's type
374 * is not the same as, or a subtype of {@code requiredType}.
375 */
376 public abstract <T> BeanletFactory<? extends T> getBeanletFactory(
377 String beanletName, Class<T> requiredType) throws
378 BeanletNotFoundException, BeanletNotOfRequiredTypeException;
379
380 /**
381 * <p>Returns {@code true} if a beanlet exists for the specified
382 * {@code beanletName}.</p>
383 *
384 * <p>This method ignores the {@code "&"} prefix.</p>
385 *
386 * @param beanletName name of the beanlet.
387 * @return {@code true} if beanlet exists, or {@code false} otherwise.
388 */
389 public abstract boolean exists(String beanletName);
390 }
391