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
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.services.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.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")
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     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}