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    }