    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}