    001// Copyright 2006-2014 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.internal;
    016
    017import org.apache.tapestry5.SymbolConstants;
    018import org.apache.tapestry5.ioc.IOCUtilities;
    019import org.apache.tapestry5.ioc.Registry;
    020import org.apache.tapestry5.ioc.RegistryBuilder;
    021import org.apache.tapestry5.ioc.def.ContributionDef;
    022import org.apache.tapestry5.ioc.def.ModuleDef;
    023import org.apache.tapestry5.ioc.internal.util.InternalUtils;
    024import org.apache.tapestry5.ioc.services.*;
    025import org.apache.tapestry5.modules.TapestryModule;
    026import org.slf4j.Logger;
    027
    028import java.util.Formatter;
    029import java.util.List;
    030
    031/**
    032 * This class is used to build the {@link Registry}. The Registry contains
    033 * {@link org.apache.tapestry5.ioc.modules.TapestryIOCModule} and {@link TapestryModule}, any
    034 * modules identified by {@link #addModules(Class[])} )}, plus the application module.
    035 * <p/>
    036 * The application module is optional.
    037 * <p/>
    038 * The application module is identified as <em>package</em>.services.<em>appName</em>Module, where
    039 * <em>package</em> and the <em>appName</em> are specified by the caller.
    040 */
    041@SuppressWarnings("rawtypes")
    042public class TapestryAppInitializer
    043{
    044    private final Logger logger;
    045
    046    private final SymbolProvider appProvider;
    047
    048    private final String appName;
    049
    050    private final long startTime;
    051
    052    private final RegistryBuilder builder = new RegistryBuilder();
    053
    054    private long registryCreatedTime;
    055
    056    private Registry registry;
    057
    058    /**
    059     * @param logger
    060     *         logger for output confirmation
    061     * @param appPackage
    062     *         root package name to search for pages and components
    063     * @param appName
    064     *         the name of the application (i.e., the name of the application servlet)
    065     */
    066    public TapestryAppInitializer(Logger logger, String appPackage, String appName)
    067    {
    068        this(logger, new SingleKeySymbolProvider(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM, appPackage), appName,
    069                null);
    070    }
    071
    072    /**
    073     * @param logger
    074     *         logger for output confirmation
    075     * @param appProvider
    076     *         provides symbols for the application (normally, from the ServletContext init
    077     *         parameters), plus (as of 5.4) the value for symbol {@link SymbolConstants#CONTEXT_PATH}
    078     * @param appName
    079     *         the name of the application (i.e., the name of the application servlet)
    080     * @param executionModes
    081     *         an optional, comma-separated list of execution modes, each of which is used
    082     *         to find a list of additional module classes to load (key
    083     *         <code>tapestry.<em>name</em>-modules</code> in appProvider, i.e., the servlet
    084     *         context)
    085     */
    086    public TapestryAppInitializer(Logger logger, SymbolProvider appProvider, String appName, String executionModes)
    087    {
    088        this.logger = logger;
    089        this.appProvider = appProvider;
    090
    091        String appPackage = appProvider.valueForSymbol(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM);
    092
    093        this.appName = appName;
    094
    095        startTime = System.currentTimeMillis();
    096
    097        if (!Boolean.parseBoolean(appProvider.valueForSymbol(InternalConstants.DISABLE_DEFAULT_MODULES_PARAM)))
    098        {
    099            IOCUtilities.addDefaultModules(builder);
    100        }
    101
    102        // This gets added automatically.
    103
    104        addModules(TapestryModule.class);
    105
    106        String className = appPackage + ".services." + InternalUtils.capitalize(this.appName) + "Module";
    107
    108        try
    109        {
    110            // This class is possibly loaded by a parent class loader of the application class
    111            // loader. The context class loader should have the appropriate view to the module
    112            // class,
    113            // if any.
    114
    115            Class moduleClass = Thread.currentThread().getContextClassLoader().loadClass(className);
    116
    117            builder.add(moduleClass);
    118        } catch (ClassNotFoundException ex)
    119        {
    120            // That's OK, not all applications will have a module class, even though any
    121            // non-trivial application will.
    122            logger.warn("Application Module class {} not found", className);
    123        }
    124
    125        // Add a synthetic module that contributes symbol sources.
    126
    127        addSyntheticSymbolSourceModule(appPackage);
    128
    129        for (String mode : TapestryInternalUtils.splitAtCommas(executionModes))
    130        {
    131            String key = String.format("tapestry.%s-modules", mode);
    132            String moduleList = appProvider.valueForSymbol(key);
    133
    134            for (String moduleClassName : TapestryInternalUtils.splitAtCommas(moduleList))
    135            {
    136                builder.add(moduleClassName);
    137            }
    138        }
    139    }
    140
    141    /**
    142     * Adds additional modules.
    143     *
    144     * @param moduleDefs
    145     */
    146    public void addModules(ModuleDef... moduleDefs)
    147    {
    148        for (ModuleDef def : moduleDefs)
    149            builder.add(def);
    150    }
    151
    152    public void addModules(Class... moduleClasses)
    153    {
    154        builder.add(moduleClasses);
    155    }
    156
    157    private void addSyntheticSymbolSourceModule(String appPackage)
    158    {
    159        ContributionDef appPathContribution = new SyntheticSymbolSourceContributionDef("AppPath",
    160                new SingleKeySymbolProvider(InternalSymbols.APP_PACKAGE_PATH, appPackage.replace('.', '/')));
    161
    162        ContributionDef symbolSourceContribution = new SyntheticSymbolSourceContributionDef("ServletContext",
    163                appProvider, "before:ApplicationDefaults", "after:EnvironmentVariables");
    164
    165        ContributionDef appNameContribution = new SyntheticSymbolSourceContributionDef("AppName",
    166                new SingleKeySymbolProvider(InternalSymbols.APP_NAME, appName), "before:ServletContext");
    167
    168        builder.add(new SyntheticModuleDef(symbolSourceContribution, appNameContribution, appPathContribution));
    169    }
    170
    171    public Registry createRegistry()
    172    {
    173        registryCreatedTime = System.currentTimeMillis();
    174
    175        registry = builder.build();
    176
    177        return registry;
    178    }
    179
    180    /**
    181     * Announce application startup, by logging (at INFO level) the names of all pages,
    182     * components, mixins and services.
    183     */
    184    public void announceStartup()
    185    {
    186        if (!logger.isInfoEnabled()) // if info logging is off we can stop now
    187        {
    188            return;
    189        }
    190        long toFinish = System.currentTimeMillis();
    191
    192        SymbolSource source = registry.getService("SymbolSource", SymbolSource.class);
    193
    194        StringBuilder buffer = new StringBuilder("Startup status:\n\nServices:\n\n");
    195        Formatter f = new Formatter(buffer);
    196
    197
    198        int unrealized = 0;
    199
    200        ServiceActivityScoreboard scoreboard = registry.getService(ServiceActivityScoreboard.class);
    201
    202        List<ServiceActivity> serviceActivity = scoreboard.getServiceActivity();
    203
    204        int longest = 0;
    205
    206        // One pass to find the longest name, and to count the unrealized services.
    207
    208        for (ServiceActivity activity : serviceActivity)
    209        {
    210            Status status = activity.getStatus();
    211
    212            longest = Math.max(longest, activity.getServiceId().length());
    213
    214            if (status == Status.DEFINED || status == Status.VIRTUAL)
    215                unrealized++;
    216        }
    217
    218        String formatString = "%" + longest + "s: %s\n";
    219
    220        // A second pass to output all the services
    221
    222        for (ServiceActivity activity : serviceActivity)
    223        {
    224            f.format(formatString, activity.getServiceId(), activity.getStatus().name());
    225        }
    226
    227        f.format("\n%4.2f%% unrealized services (%d/%d)\n", 100. * unrealized / serviceActivity.size(), unrealized,
    228                serviceActivity.size());
    229
    230
    231        f.format("\nApplication '%s' (version %s) startup time: %,d ms to build IoC Registry, %,d ms overall.", appName,
    232                source.valueForSymbol(SymbolConstants.APPLICATION_VERSION),
    233                registryCreatedTime - startTime,
    234                toFinish - startTime);
    235
    236        String version = source.valueForSymbol(SymbolConstants.TAPESTRY_VERSION);
    237        boolean productionMode = Boolean.parseBoolean(source.valueForSymbol(SymbolConstants.PRODUCTION_MODE));
    238
    239
    240        buffer.append("\n\n");
    241        buffer.append(" ______                  __             ____\n");
    242        buffer.append("/_  __/__ ____  ___ ___ / /_______ __  / __/\n");
    243        buffer.append(" / / / _ `/ _ \\/ -_|_-</ __/ __/ // / /__ \\ \n");
    244        buffer.append("/_/  \\_,_/ .__/\\__/___/\\__/_/  \\_, / /____/\n");
    245        f.format     ("        /_/                   /___/  %s%s\n\n",
    246                version, productionMode ? "" : " (development mode)");
    247
    248        // log multi-line string with OS-specific line endings (TAP5-2294)
    249        logger.info(buffer.toString().replaceAll("\\n", System.getProperty("line.separator")));
    250    }
    251}