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