1 // Copyright 2006, 2007, 2008 The Apache Software Foundation
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package org.apache.tapestry5.ioc;
16
17 import org.apache.tapestry5.ioc.annotations.SubModule;
18 import org.apache.tapestry5.ioc.def.ModuleDef;
19 import org.apache.tapestry5.ioc.internal.DefaultModuleDefImpl;
20 import org.apache.tapestry5.ioc.internal.LoggerSourceImpl;
21 import org.apache.tapestry5.ioc.internal.RegistryImpl;
22 import org.apache.tapestry5.ioc.internal.RegistryWrapper;
23 import org.apache.tapestry5.ioc.internal.services.ClassFactoryImpl;
24 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
25 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
26 import org.apache.tapestry5.ioc.internal.util.OneShotLock;
27 import org.apache.tapestry5.ioc.services.ClassFactory;
28 import org.apache.tapestry5.ioc.services.TapestryIOCModule;
29 import org.slf4j.Logger;
30
31 import java.lang.reflect.AnnotatedElement;
32 import java.util.Arrays;
33 import java.util.List;
34 import java.util.Set;
35
36 /**
37 * Used to construct the IoC {@link org.apache.tapestry5.ioc.Registry}. This class is <em>not</em> thread-safe. The
38 * Registry, once created, <em>is</em> thread-safe.
39 */
40 public final class RegistryBuilder
41 {
42 private final OneShotLock lock = new OneShotLock();
43
44 /**
45 * Module defs, keyed on module id.
46 */
47 final List<ModuleDef> modules = CollectionFactory.newList();
48
49 private final ClassLoader classLoader;
50
51 private final Logger logger;
52
53 private final LoggerSource loggerSource;
54
55 private final ClassFactory classFactory;
56
57 private final Set<Class> addedModuleClasses = CollectionFactory.newSet();
58
59 public RegistryBuilder()
60 {
61 this(Thread.currentThread().getContextClassLoader());
62 }
63
64 public RegistryBuilder(ClassLoader classLoader)
65 {
66 this(classLoader, new LoggerSourceImpl());
67 }
68
69 public RegistryBuilder(ClassLoader classLoader, LoggerSource loggerSource)
70 {
71 this.classLoader = classLoader;
72 this.loggerSource = loggerSource;
73 logger = loggerSource.getLogger(RegistryBuilder.class);
74
75 // Make the ClassFactory appear to be a service inside TapestryIOCModule, even before that
76 // module exists.
77
78 Logger classFactoryLogger = loggerSource.getLogger(TapestryIOCModule.class.getName() + ".ClassFactory");
79
80 classFactory = new ClassFactoryImpl(this.classLoader, classFactoryLogger);
81
82 add(TapestryIOCModule.class);
83 }
84
85 /**
86 * Adds a {@link ModuleDef} to the registry, returning the builder for further configuration.
87 */
88 public RegistryBuilder add(ModuleDef moduleDef)
89 {
90 lock.check();
91
92 // TODO: Some way to ensure that duplicate modules are not being added.
93 // Part of TAPESTRY-2117 is in add(Class...) and that may be as much as we can
94 // do as there is no concept of ModuleDef identity.
95
96 modules.add(moduleDef);
97
98 return this;
99 }
100
101 /**
102 * Adds a number of modules (as module classes) to the registry, returning the builder for further configuration.
103 *
104 * @see org.apache.tapestry5.ioc.annotations.SubModule
105 */
106 public RegistryBuilder add(Class... moduleBuilderClasses)
107 {
108 lock.check();
109
110 List<Class> queue = CollectionFactory.newList(Arrays.asList(moduleBuilderClasses));
111
112 while (!queue.isEmpty())
113 {
114 Class c = queue.remove(0);
115
116 // Quietly ignore previously added classes.
117
118 if (addedModuleClasses.contains(c)) continue;
119
120 addedModuleClasses.add(c);
121
122 ModuleDef def = new DefaultModuleDefImpl(c, logger, classFactory);
123 add(def);
124
125 SubModule annotation = ((AnnotatedElement) c).getAnnotation(SubModule.class);
126
127 if (annotation == null) continue;
128
129 queue.addAll(Arrays.asList(annotation.value()));
130 }
131
132 return this;
133 }
134
135 /**
136 * Adds a number of module classes (specified by fully qualified class name) to the registry, returning the builder
137 * for further configuration.
138 *
139 * @see org.apache.tapestry5.ioc.annotations.SubModule
140 */
141 public RegistryBuilder add(String classname)
142 {
143 lock.check();
144
145 try
146 {
147 Class builderClass = Class.forName(classname, true, classLoader);
148
149 add(builderClass);
150 }
151 catch (Exception ex)
152 {
153 throw new RuntimeException(String.format("Failure loading Tapestry IoC module class %s: %s",
154 classname, InternalUtils.toMessage(ex), ex));
155 }
156
157 return this;
158 }
159
160 /**
161 * Constructs and returns the registry; this may only be done once. The caller is responsible for invoking {@link
162 * org.apache.tapestry5.ioc.Registry#performRegistryStartup()}.
163 */
164 public Registry build()
165 {
166 lock.lock();
167
168 RegistryImpl registry = new RegistryImpl(modules, classFactory, loggerSource);
169
170 return new RegistryWrapper(registry);
171 }
172
173 public ClassLoader getClassLoader()
174 {
175 return classLoader;
176 }
177
178 public Logger getLogger()
179 {
180 return logger;
181 }
182 }