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