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