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