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