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