    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    }