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