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
015package org.apache.tapestry5.ioc;
016
017import static org.apache.tapestry5.ioc.IOCConstants.MODULE_BUILDER_MANIFEST_ENTRY_NAME;
018import org.apache.tapestry5.ioc.annotations.SubModule;
019import org.apache.tapestry5.ioc.internal.util.InternalUtils;
020
021import java.io.Closeable;
022import java.io.IOException;
023import java.io.InputStream;
024import java.net.URL;
025import java.util.Enumeration;
026import 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 */
032public 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}