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    }