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    }