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;
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 * Bootstraps the Beanlet container by obtaining a {@code BeanletApplicationContext} instance.
151 *
152 * @param args no arguments required.
153 */
154 public static void main(String... args) {
155 BeanletApplicationContext.instance();
156 }
157
158 /**
159 * <p>Returns a {@code BeanletApplicationContext} instance. If the container
160 * isn't already running, the first call to this method automatically starts
161 * and initializes the container.</p>
162 *
163 * <p>It is not specified whether multiple calls to this method results in
164 * a single or multiple instances of the {@code BeanletApplicationContext}.
165 * However, as the container only starts once, invoking this method multiple
166 * times is valid and does not consume any additional resources.</p>
167 *
168 * @return a {@code BeanletApplicationContext} instance.
169 * @throws BeanletApplicationException indicates an error during container
170 * initialization.
171 */
172 public static BeanletApplicationContext instance() throws
173 BeanletApplicationException {
174 try {
175 try {
176 return LazyHolder.ctx.resolveInstance();
177 } catch (ExceptionInInitializerError e) {
178 try {
179 throw e.getException();
180 } catch (BeanletApplicationException e2) {
181 throw e2;
182 } catch (Error e2) {
183 throw e2;
184 } catch (Throwable t) {
185 throw new BeanletApplicationException(t);
186 }
187 }
188 } catch (BeanletApplicationException e) { // Don't catch Errors and RuntimeExceptions.
189 throw e;
190 }
191 }
192
193 /**
194 * Subclasses of this class can implement this method to control which
195 * instance is returned to the caller of the static {@code instance} method.
196 * The default implementation of this method simply returns {@code this}.
197 *
198 * @return a beanlet application context reference.
199 * @throws BeanletApplicationException indicates an error during container
200 * initialization.
201 */
202 protected BeanletApplicationContext resolveInstance() throws
203 BeanletApplicationException {
204 return this;
205 }
206
207 /**
208 * Undeploys all components and stops all internal container threads.
209 * @throws BeanletApplicationContext indicates an error during container
210 * shutdown.
211 */
212 public abstract void shutdown() throws BeanletApplicationException;
213
214 /**
215 * Factory method for the specified {@code eventType}.
216 *
217 * @return a concrete implementation of the specified {@code eventType}.
218 */
219 public abstract <T extends Event> T getEvent(Class<T> eventType);
220
221 /**
222 * <p>Returns a beanlet instance for the specified {@code beanletName}.</p>
223 *
224 * <p>If his beanlet instance implements the {@code FactoryBeanlet}
225 * interface, the result of {@link FactoryBeanlet#getObject} is returned.
226 * Prefix the {@code beanletName} with {@code "&"} to obtain an instance to
227 * the {@code FactoryBeanlet} itself.</p>
228 *
229 * @param beanletName name of the beanlet.
230 * @return a beanlet.
231 * @throws BeanletNotFoundException if beanlet does not exist.
232 * @throws BeanletCreationException if beanlet could not be created for any
233 * reason.
234 */
235 public abstract Object getBeanlet(String beanletName) throws
236 BeanletNotFoundException, BeanletCreationException;
237
238 /**
239 * <p>Returns a beanlet instance for the specified {@code beanletName}. If
240 * no beanlet exists for the specied {@code beanletName} a
241 * {@code BeanletNotFoundException} is thrown. A
242 * {@code BeanletNotOfRequiredTypeException} is thrown if the beanlet
243 * instance cannot be assigned to the {@code requiredType}.</p>
244 *
245 * <p>If his beanlet instance implements the {@code FactoryBeanlet}
246 * interface, the result of {@link FactoryBeanlet#getObject} is returned.
247 * Prefix the {@code beanletName} with {@code "&"} to obtain an instance to
248 * the {@code FactoryBeanlet} itself.</p>
249 *
250 * <p>The entries of the {@code info} argument can be used to wire members
251 * of the beanlet instance. This does not apply to singleton and stateless
252 * beanlets.</p>
253 *
254 * @param beanletName name of the beanlet.
255 * @param requiredType type to mached the beanlet type.
256 * @return a beanlet.
257 * @throws BeanletNotFoundException if beanlet does not exist.
258 * @throws BeanletCreationException if beanlet could not be created for
259 * any reason.
260 * @throws BeanletNotOfRequiredTypeException if beanlet cannot be assigned
261 * to the {@code requiredType}.
262 * @see Inject
263 * @see Wiring
264 */
265 public abstract <T> T getBeanlet(String beanletName, Class<T> requiredType)
266 throws BeanletNotFoundException, BeanletCreationException,
267 BeanletNotOfRequiredTypeException;
268
269 /**
270 * <p>Returns a beanlet for the specified {@code beanletName}.</p>
271 *
272 * <p>If this beanlet implements the {@code FactoryBeanlet}
273 * interface, the result of {@link FactoryBeanlet#getObject} is returned.
274 * Prefix the {@code beanletName} with {@code "&"} to obtain an instance to
275 * the {@code FactoryBeanlet} itself.</p>
276 *
277 * <p>The entries of the {@code info} argument can be used to wire members
278 * of the beanlet instance. This does not apply to static beanlets.</p>
279 *
280 * @param beanletName name of the beanlet.
281 * @param info map that contains parameters that can be injected into
282 * the beanlet instance.
283 * @return a beanlet.
284 * @throws BeanletNotFoundException if beanlet does not exist.
285 * @throws BeanletCreationException if beanlet could not be
286 * created for any reason.
287 * @see Inject
288 * @see Wiring
289 */
290 public abstract Object getBeanlet(String beanletName,
291 Map<String, ?> info) throws BeanletNotFoundException,
292 BeanletCreationException;
293
294 /**
295 * <p>Returns a {@code BeanletFactory} for the specified {@code beanletName}.
296 * If no beanlet exists for the specified {@code beanletName} a
297 * {@code BeanletNotFoundException} is thrown. If the beanlet definition
298 * type is either the same as, or a subclass of the specified
299 * {@code requiredType}, a generified {@code BeanletFactory} is returned
300 * with the {@code requiredType} as upper bound.</p>
301 *
302 * <p>This method ignores the {@code "&"} prefix.</p>
303 *
304 * <p>The entries of the {@code info} argument can be used to wire members
305 * of the beanlet instance. This does not apply to static beanlets.</p>
306 *
307 * @param beanletName name of the beanlet.
308 * @param requiredType type to mached the beanlet type.
309 * @param info map that contains parameters that can be injected into
310 * the beanlet instance.
311 * @return a {@code BeanletFactory} for the specified {@code beanletName}.
312 * @throws BeanletNotFoundException if beanlet does not exist.
313 * @throws BeanletCreationException if beanlet could not be created for any
314 * reason.
315 * @throws BeanletNotOfRequiredTypeException if beanlet cannot be assigned
316 * to the {@code requiredType}.
317 * @see Inject
318 * @see Wiring
319 */
320 public abstract <T> T getBeanlet(String beanletName, Class<T> requiredType,
321 Map<String, ?> info) throws BeanletNotFoundException,
322 BeanletCreationException, BeanletNotOfRequiredTypeException;
323
324 /**
325 * Returns an immutable set of beanlet names of all registered beanlets.
326 */
327 public abstract Set<String> getBeanletNames();
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}.
333 *
334 * @param type type to mached the beanlet type.
335 */
336 public abstract Set<String> getBeanletNamesForType(Class<?> type);
337
338 /**
339 * Returns an immutable set of beanlet names of all registered beanlets,
340 * which the beanlet instance type is the same as, or a subclass of the
341 * specified {@code type}. Additionally, beanlet factories, which return
342 * type match the specified {@code type} can be added as well, if
343 * {@code factoryAware} is set to {@code true}. Set {@code usePrefix} to
344 * {@code true} if these beanlet names must be prepended with "&".
345 *
346 * @param type type to mached the beanlet type, or factory
347 * beanlet return type (optional).
348 * @param factoryAware specify {@code true} to include factory beanlets,
349 * which return type match the given type.
350 * @param usePrefix specify {@code true} to prefix factory beanlet names.
351 */
352 public abstract Set<String> getBeanletNamesForType(Class<?> type,
353 boolean factoryAware, boolean usePrefix);
354
355 /**
356 * <p>Returns a {@code BeanletFactory} for the specified {@code beanletName},
357 * or throws a {@code BeanletNotFoundException} if beanlet does not exist.</p>
358 *
359 * <p>This method ignores the {@code "&"} prefix.</p>
360 *
361 * @param beanletName name of the beanlet.
362 * @return a {@code BeanletFactory} for the specified {@code beanletName}.
363 * @throws BeanletNotFoundException if beanlet does not exist.
364 */
365 public abstract BeanletFactory<?> getBeanletFactory(String beanletName)
366 throws BeanletNotFoundException;
367
368 /**
369 * <p>Returns a {@code BeanletFactory} for the specified {@code beanletName}.
370 * If no beanlet exists for the specified {@code beanletName} a
371 * {@code BeanletNotFoundException} is thrown. If the beanlet definition
372 * type is either the same as, or a subclass of the specified
373 * {@code requiredType}, a generified {@code BeanletFactory} is returned
374 * with the {@code requiredType} as upper bound.</p>
375 *
376 * <p>This method ignores the {@code "&"} prefix.</p>
377 *
378 * @param beanletName name of the beanlet.
379 * @param requiredType type to mached the beanlet type.
380 * @return a {@code BeanletFactory} for the specified {@code beanletName}.
381 * @throws BeanletNotFoundException if beanlet does not exist.
382 * @throws BeanletNotOfRequiredTypeException if beanlet's type
383 * is not the same as, or a subtype of {@code requiredType}.
384 */
385 public abstract <T> BeanletFactory<? extends T> getBeanletFactory(
386 String beanletName, Class<T> requiredType) throws
387 BeanletNotFoundException, BeanletNotOfRequiredTypeException;
388
389 /**
390 * <p>Returns {@code true} if a beanlet exists for the specified
391 * {@code beanletName}.</p>
392 *
393 * <p>This method ignores the {@code "&"} prefix.</p>
394 *
395 * @param beanletName name of the beanlet.
396 * @return {@code true} if beanlet exists, or {@code false} otherwise.
397 */
398 public abstract boolean exists(String beanletName);
399 }
400