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 }