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