001    // Copyright 2006, 2007, 2008 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry5.ioc;
016    
017    import static org.apache.tapestry5.ioc.IOCConstants.MODULE_BUILDER_MANIFEST_ENTRY_NAME;
018    import org.apache.tapestry5.ioc.annotations.SubModule;
019    import org.apache.tapestry5.ioc.internal.util.InternalUtils;
020    
021    import java.io.Closeable;
022    import java.io.IOException;
023    import java.io.InputStream;
024    import java.net.URL;
025    import java.util.Enumeration;
026    import java.util.jar.Manifest;
027    
028    /**
029     * A collection of utility methods for a couple of different areas, including creating the initial {@link
030     * org.apache.tapestry5.ioc.Registry}.
031     */
032    public final class IOCUtilities
033    {
034        private IOCUtilities()
035        {
036        }
037    
038        /**
039         * Construct a default Registry, including modules identifed via the Tapestry-Module-Classes Manifest entry. The
040         * registry will have been {@linkplain Registry#performRegistryStartup() started up} before it is returned.
041         *
042         * @return constructed Registry, after startup
043         * @see #addDefaultModules(RegistryBuilder)
044         */
045        public static Registry buildDefaultRegistry()
046        {
047            RegistryBuilder builder = new RegistryBuilder();
048    
049            addDefaultModules(builder);
050    
051            Registry registry = builder.build();
052    
053            registry.performRegistryStartup();
054    
055            return registry;
056        }
057    
058        /**
059         * Scans the classpath for JAR Manifests that contain the Tapestry-Module-Classes attribute and adds each
060         * corresponding class to the RegistryBuilder. In addition, looks for a system property named "tapestry.modules" and
061         * adds all of those modules as well. The tapestry.modules approach is intended for development.
062         *
063         * @param builder the builder to which modules will be added
064         * @see SubModule
065         * @see RegistryBuilder#add(String)
066         */
067        public static void addDefaultModules(RegistryBuilder builder)
068        {
069            try
070            {
071                Enumeration<URL> urls = builder.getClassLoader().getResources("META-INF/MANIFEST.MF");
072    
073                while (urls.hasMoreElements())
074                {
075                    URL url = urls.nextElement();
076    
077                    addModulesInManifest(builder, url);
078                }
079    
080                addModulesInList(builder, System.getProperty("tapestry.modules"));
081    
082            }
083            catch (IOException ex)
084            {
085                throw new RuntimeException(ex.getMessage(), ex);
086            }
087        }
088    
089        private static void addModulesInManifest(RegistryBuilder builder, URL url)
090        {
091            InputStream in = null;
092    
093            Throwable fail = null;
094    
095            try
096            {
097                in = url.openStream();
098    
099                Manifest mf = new Manifest(in);
100    
101                in.close();
102    
103                in = null;
104    
105                String list = mf.getMainAttributes().getValue(MODULE_BUILDER_MANIFEST_ENTRY_NAME);
106    
107                addModulesInList(builder, list);
108            }
109            catch (RuntimeException ex)
110            {
111                fail = ex;
112            }
113            catch (IOException ex)
114            {
115                fail = ex;
116            }
117            finally
118            {
119                close(in);
120            }
121    
122            if (fail != null)
123                throw new RuntimeException(String.format("Exception loading module(s) from manifest %s: %s",
124                                                         url.toString(),
125                                                         InternalUtils.toMessage(fail)), fail);
126    
127        }
128    
129        static void addModulesInList(RegistryBuilder builder, String list)
130        {
131            if (list == null) return;
132    
133            String[] classnames = list.split(",");
134    
135            for (String classname : classnames)
136            {
137                builder.add(classname.trim());
138            }
139        }
140    
141        /**
142         * Closes an input stream (or other Closeable), ignoring any exception.
143         *
144         * @param closeable the thing to close, or null to close nothing
145         */
146        private static void close(Closeable closeable)
147        {
148            if (closeable != null)
149            {
150                try
151                {
152                    closeable.close();
153                }
154                catch (IOException ex)
155                {
156                    // Ignore.
157                }
158            }
159        }
160    }