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