001    /*******************************************************************************
002     * Copyright (C) PicoContainer Organization. All rights reserved. 
003     * ---------------------------------------------------------------------------
004     * The software in this package is published under the terms of the BSD style
005     * license a copy of which has been included with this distribution in the
006     * LICENSE.txt file. 
007     ******************************************************************************/
008    package org.picocontainer.web.chain;
009    
010    import java.io.InputStream;
011    import java.io.InputStreamReader;
012    import java.io.Reader;
013    import java.io.StringReader;
014    
015    import javax.servlet.ServletContext;
016    
017    import org.picocontainer.ComponentAdapter;
018    import org.picocontainer.MutablePicoContainer;
019    import org.picocontainer.Parameter;
020    import org.picocontainer.PicoContainer;
021    import org.picocontainer.parameters.ConstantParameter;
022    import org.picocontainer.classname.ClassName;
023    import org.picocontainer.script.ContainerBuilder;
024    import org.picocontainer.classname.DefaultClassLoadingPicoContainer;
025    import org.picocontainer.classname.ClassLoadingPicoContainer;
026    
027    /**
028     * <p>
029     * ServletChainBuilder builds ContainerChains from servlet path and caches
030     * container recorders for later use.
031     * </p>
032     * 
033     * @author Kontantin Pribluda
034     * @author Mauro Talevi
035     */
036    public final class ServletChainBuilder {
037    
038        private final ServletContext context;
039        private final String containerBuilderClassName;
040        private final String containerScriptName;
041        private final String emptyContainerScript;
042    
043        /**
044         * Constructor for the ServletChainBuilder object
045         * 
046         * @param context the ServletContext
047         * @param containerBuilderClassName the class name of the ContainerBuilder
048         * @param containerScriptName the name of the container script resource
049         * @param emptyContainerScript the script for empty container if the
050         *            container config is not found
051         */
052        public ServletChainBuilder(ServletContext context, String containerBuilderClassName, String containerScriptName,
053                String emptyContainerScript) {
054            this.context = context;
055            this.containerBuilderClassName = containerBuilderClassName;
056            this.containerScriptName = containerScriptName;
057            this.emptyContainerScript = emptyContainerScript;
058        }
059    
060        /**
061         * populate container for given path. cache result in container recorders
062         * 
063         * @param container the MutablePicoContainer used by the recorder
064         * @param path the String representing the servlet path used as key for the
065         *            recorder cache
066         */
067    
068    
069        private void populateContainer(String resourcePath, MutablePicoContainer container) {
070            PicoContainer buildContainer = buildContainer(resourcePath, container.getParent());
071            for (ComponentAdapter<?> adapter : buildContainer.getComponentAdapters()) {
072                container.addAdapter(adapter);
073            }
074        }
075    
076        private PicoContainer buildContainer(String resourcePath, PicoContainer parent) {
077            ContainerBuilder builder = createContainerBuilder(obtainReader(resourcePath));
078            return builder.buildContainer(parent, null, false);
079        }
080    
081        private ContainerBuilder createContainerBuilder(Reader reader) {
082            ClassLoadingPicoContainer scripted = new DefaultClassLoadingPicoContainer(getClassLoader());
083            Parameter[] parameters = new Parameter[] { new ConstantParameter(reader),
084                    new ConstantParameter(getClassLoader()) };
085            scripted.addComponent(containerBuilderClassName, new ClassName(containerBuilderClassName), parameters);
086            return scripted.getComponent(ContainerBuilder.class);
087        }
088    
089    
090    
091    
092        /**
093         * Obtain reader from servlet context path, by appending container script
094         * name to path. If not found, returns reader for empty container instead.
095         * 
096         * @param path the String representing the path in servlet context
097         * @return A Reader for corresponding script or for empty container if
098         *         script not found
099         */
100        private Reader obtainReader(String path) {
101            InputStream is = context.getResourceAsStream(path + containerScriptName);
102            if (is != null) {
103                return new InputStreamReader(is);
104            } else {
105                return new StringReader(emptyContainerScript);
106            }
107        }
108        
109        private ClassLoader getClassLoader() {
110            return Thread.currentThread().getContextClassLoader();
111        }
112    
113    }