001 // Copyright 2006, 2007, 2008, 2009 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.util.CollectionFactory;
025 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
026 import org.apache.tapestry5.ioc.internal.util.OneShotLock;
027 import org.apache.tapestry5.ioc.services.ClassFactory;
028 import org.apache.tapestry5.ioc.services.TapestryIOCModule;
029 import org.slf4j.Logger;
030
031 import java.lang.reflect.AnnotatedElement;
032 import java.util.Arrays;
033 import java.util.List;
034 import java.util.Set;
035
036 /**
037 * Used to construct the IoC {@link org.apache.tapestry5.ioc.Registry}. This class is <em>not</em> thread-safe. The
038 * Registry, once created, <em>is</em> thread-safe.
039 */
040 public final class RegistryBuilder
041 {
042 private final OneShotLock lock = new OneShotLock();
043
044 /**
045 * Module defs, keyed on module id.
046 */
047 final List<ModuleDef> modules = CollectionFactory.newList();
048
049 private final ClassLoader classLoader;
050
051 private final Logger logger;
052
053 private final LoggerSource loggerSource;
054
055 private final ClassFactory classFactory;
056
057 private final Set<Class> addedModuleClasses = CollectionFactory.newSet();
058
059 public RegistryBuilder()
060 {
061 this(Thread.currentThread().getContextClassLoader());
062 }
063
064 public RegistryBuilder(ClassLoader classLoader)
065 {
066 this(classLoader, new LoggerSourceImpl());
067 }
068
069 public RegistryBuilder(ClassLoader classLoader, LoggerSource loggerSource)
070 {
071 this.classLoader = classLoader;
072 this.loggerSource = loggerSource;
073 logger = loggerSource.getLogger(RegistryBuilder.class);
074
075 // Make the ClassFactory appear to be a service inside TapestryIOCModule, even before that
076 // module exists.
077
078 Logger classFactoryLogger = loggerSource.getLogger(TapestryIOCModule.class.getName() + ".ClassFactory");
079
080 classFactory = new ClassFactoryImpl(this.classLoader, classFactoryLogger);
081
082 add(TapestryIOCModule.class);
083 }
084
085 /**
086 * Adds a {@link ModuleDef} to the registry, returning the builder for further configuration.
087 */
088 public RegistryBuilder add(ModuleDef moduleDef)
089 {
090 lock.check();
091
092 // TODO: Some way to ensure that duplicate modules are not being added.
093 // Part of TAPESTRY-2117 is in add(Class...) and that may be as much as we can
094 // do as there is no concept of ModuleDef identity.
095
096 modules.add(moduleDef);
097
098 return this;
099 }
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... moduleClasses)
107 {
108 lock.check();
109
110 List<Class> queue = CollectionFactory.newList(Arrays.asList(moduleClasses));
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 logger.info("Adding module definition for " + c);
123
124 ModuleDef def = new DefaultModuleDefImpl(c, logger, classFactory);
125 add(def);
126
127 SubModule annotation = ((AnnotatedElement) c).getAnnotation(SubModule.class);
128
129 if (annotation == null) continue;
130
131 queue.addAll(Arrays.asList(annotation.value()));
132 }
133
134 return this;
135 }
136
137 /**
138 * Adds a number of module classes (specified by fully qualified class name) to the registry, returning the builder
139 * for further configuration.
140 *
141 * @see org.apache.tapestry5.ioc.annotations.SubModule
142 */
143 public RegistryBuilder add(String classname)
144 {
145 lock.check();
146
147 try
148 {
149 Class builderClass = Class.forName(classname, true, classLoader);
150
151 add(builderClass);
152 }
153 catch (Exception ex)
154 {
155 throw new RuntimeException(String.format("Failure loading Tapestry IoC module class %s: %s",
156 classname, InternalUtils.toMessage(ex), ex));
157 }
158
159 return this;
160 }
161
162 /**
163 * Constructs and returns the registry; this may only be done once. The caller is responsible for invoking {@link
164 * org.apache.tapestry5.ioc.Registry#performRegistryStartup()}.
165 */
166 public Registry build()
167 {
168 lock.lock();
169
170 RegistryImpl registry = new RegistryImpl(modules, classFactory, loggerSource);
171
172 return new RegistryWrapper(registry);
173 }
174
175 public ClassLoader getClassLoader()
176 {
177 return classLoader;
178 }
179
180 public Logger getLogger()
181 {
182 return logger;
183 }
184 }