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.ioc; 014 015import org.apache.tapestry5.beanmodel.services.PlasticProxyFactoryImpl; 016import org.apache.tapestry5.commons.internal.*; 017import org.apache.tapestry5.commons.services.PlasticProxyFactory; 018import org.apache.tapestry5.commons.util.CollectionFactory; 019import org.apache.tapestry5.commons.util.ExceptionUtils; 020import org.apache.tapestry5.ioc.annotations.ImportModule; 021import org.apache.tapestry5.ioc.annotations.SubModule; 022import org.apache.tapestry5.ioc.def.ModuleDef; 023import org.apache.tapestry5.ioc.def.ModuleDef2; 024import org.apache.tapestry5.ioc.internal.DefaultModuleDefImpl; 025import org.apache.tapestry5.ioc.internal.LoggerSourceImpl; 026import org.apache.tapestry5.ioc.internal.PerThreadOperationTracker; 027import org.apache.tapestry5.ioc.internal.RegistryImpl; 028import org.apache.tapestry5.ioc.internal.RegistryWrapper; 029import org.apache.tapestry5.ioc.internal.util.InternalUtils; 030import org.apache.tapestry5.ioc.internal.util.OneShotLock; 031import org.apache.tapestry5.ioc.modules.TapestryIOCModule; 032import org.slf4j.Logger; 033 034import java.lang.reflect.AnnotatedElement; 035import java.util.Arrays; 036import java.util.List; 037import java.util.Set; 038 039/** 040 * Used to construct the IoC {@link org.apache.tapestry5.ioc.Registry}. This class is <em>not</em> thread-safe. The 041 * Registry, once created, <em>is</em> thread-safe. 042 */ 043public final class RegistryBuilder 044{ 045 private final OneShotLock lock = new OneShotLock(); 046 047 /** 048 * Module defs, keyed on module id. 049 */ 050 final List<ModuleDef2> modules = CollectionFactory.newList(); 051 052 private final ClassLoader classLoader; 053 054 private final Logger logger; 055 056 private final LoggerSource loggerSource; 057 058 private final PlasticProxyFactory proxyFactory; 059 060 private final Set<Class> addedModuleClasses = CollectionFactory.newSet(); 061 062 public RegistryBuilder() 063 { 064 this(Thread.currentThread().getContextClassLoader()); 065 } 066 067 public RegistryBuilder(ClassLoader classLoader) 068 { 069 this(classLoader, new LoggerSourceImpl()); 070 } 071 072 public RegistryBuilder(ClassLoader classLoader, LoggerSource loggerSource) 073 { 074 this.classLoader = classLoader; 075 this.loggerSource = loggerSource; 076 logger = loggerSource.getLogger(RegistryBuilder.class); 077 078 // Make the Proxy Factory appear to be a service inside TapestryIOCModule, even before that 079 // module exists. 080 081 Logger proxyFactoryLogger = loggerSource.getLogger(TapestryIOCModule.class.getName() + ".PlasticProxyFactory"); 082 083 proxyFactory = new PlasticProxyFactoryImpl(this.classLoader, proxyFactoryLogger); 084 085 add(TapestryIOCModule.class); 086 } 087 088 /** 089 * Adds a {@link ModuleDef} to the registry, returning the builder for further configuration. 090 */ 091 public RegistryBuilder add(ModuleDef moduleDef) 092 { 093 lock.check(); 094 095 // TODO: Some way to ensure that duplicate modules are not being added. 096 // Part of TAPESTRY-2117 is in add(Class...) and that may be as much as we can 097 // do as there is no concept of ModuleDef identity. 098 099 modules.add(InternalUtils.toModuleDef2(moduleDef)); 100 101 return this; 102 } 103 104 /** 105 * Adds a number of modules (as module classes) to the registry, returning the builder for further configuration. 106 * 107 * @see org.apache.tapestry5.ioc.annotations.ImportModule 108 */ 109 public RegistryBuilder add(Class... moduleClasses) 110 { 111 lock.check(); 112 113 List<Class> queue = CollectionFactory.newList(Arrays.asList(moduleClasses)); 114 115 while (!queue.isEmpty()) 116 { 117 Class c = queue.remove(0); 118 119 // Quietly ignore previously added classes. 120 121 if (addedModuleClasses.contains(c)) 122 continue; 123 124 addedModuleClasses.add(c); 125 126 logger.info("Adding module definition for " + c); 127 128 ModuleDef def = new DefaultModuleDefImpl(c, logger, proxyFactory); 129 add(def); 130 131 132 @SuppressWarnings("RedundantCast") 133 AnnotatedElement element = (AnnotatedElement) c; 134 135 SubModule subModule = element.getAnnotation(SubModule.class); 136 137 if (subModule != null) 138 { 139 queue.addAll(Arrays.asList(subModule.value())); 140 } 141 ImportModule importModule = element.getAnnotation(ImportModule.class); 142 143 if (importModule != null) 144 { 145 queue.addAll(Arrays.asList(importModule.value())); 146 } 147 } 148 149 return this; 150 } 151 152 /** 153 * Adds a modle class (specified by fully qualified class name) to the registry, returning the builder 154 * for further configuration. 155 * 156 * @see org.apache.tapestry5.ioc.annotations.ImportModule 157 */ 158 public RegistryBuilder add(String classname) 159 { 160 lock.check(); 161 162 try 163 { 164 Class builderClass = Class.forName(classname, true, classLoader); 165 166 add(builderClass); 167 } catch (Exception ex) 168 { 169 throw new RuntimeException(String.format("Failure loading Tapestry IoC module class %s: %s", classname, 170 ExceptionUtils.toMessage(ex)), ex); 171 } 172 173 return this; 174 } 175 176 /** 177 * Constructs and returns the registry; this may only be done once. The caller is responsible for invoking 178 * {@link org.apache.tapestry5.ioc.Registry#performRegistryStartup()}. 179 */ 180 public Registry build() 181 { 182 lock.lock(); 183 184 PerThreadOperationTracker tracker = new PerThreadOperationTracker(loggerSource.getLogger(Registry.class)); 185 186 RegistryImpl registry = new RegistryImpl(modules, proxyFactory, loggerSource, tracker); 187 188 return new RegistryWrapper(registry); 189 } 190 191 public ClassLoader getClassLoader() 192 { 193 return classLoader; 194 } 195 196 public Logger getLogger() 197 { 198 return logger; 199 } 200 201 /** 202 * Constructs the registry, adds a {@link ModuleDef} and a number of modules (as module classes) to the registry and 203 * performs registry startup. The returned registry is ready to use. The caller is must not invoke 204 * {@link org.apache.tapestry5.ioc.Registry#performRegistryStartup()}. 205 * 206 * @param moduleDef 207 * {@link ModuleDef} to add 208 * @param moduleClasses 209 * modules (as module classes) to add 210 * @return {@link Registry} 211 * @since 5.2.0 212 */ 213 public static Registry buildAndStartupRegistry(ModuleDef moduleDef, Class... moduleClasses) 214 { 215 RegistryBuilder builder = new RegistryBuilder(); 216 217 if (moduleDef != null) 218 builder.add(moduleDef); 219 220 builder.add(moduleClasses); 221 222 Registry registry = builder.build(); 223 224 registry.performRegistryStartup(); 225 226 return registry; 227 } 228 229 /** 230 * Constructs the registry, adds a number of modules (as module classes) to the registry and 231 * performs registry startup. The returned registry is ready to use. The caller is must not invoke 232 * {@link org.apache.tapestry5.ioc.Registry#performRegistryStartup()}. 233 * 234 * @param moduleClasses 235 * modules (as module classes) to add 236 * @return {@link Registry} 237 * @since 5.2.0 238 */ 239 public static Registry buildAndStartupRegistry(Class... moduleClasses) 240 { 241 return buildAndStartupRegistry(null, moduleClasses); 242 } 243}