    001// Licensed under the Apache License, Version 2.0 (the "License");
    002// you may not use this file except in compliance with the License.
    003// You may obtain a copy of the License at
    004//
    005// http://www.apache.org/licenses/LICENSE-2.0
    006//
    007// Unless required by applicable law or agreed to in writing, software
    008// distributed under the License is distributed on an "AS IS" BASIS,
    009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    010// See the License for the specific language governing permissions and
    011// limitations under the License.
    012
    013package org.apache.tapestry5.ioc;
    014
    015import org.apache.tapestry5.beanmodel.services.PlasticProxyFactoryImpl;
    016import org.apache.tapestry5.commons.internal.*;
    017import org.apache.tapestry5.commons.services.PlasticProxyFactory;
    018import org.apache.tapestry5.commons.util.CollectionFactory;
    019import org.apache.tapestry5.commons.util.ExceptionUtils;
    020import org.apache.tapestry5.ioc.annotations.ImportModule;
    021import org.apache.tapestry5.ioc.annotations.SubModule;
    022import org.apache.tapestry5.ioc.def.ModuleDef;
    023import org.apache.tapestry5.ioc.def.ModuleDef2;
    024import org.apache.tapestry5.ioc.internal.DefaultModuleDefImpl;
    025import org.apache.tapestry5.ioc.internal.LoggerSourceImpl;
    026import org.apache.tapestry5.ioc.internal.PerThreadOperationTracker;
    027import org.apache.tapestry5.ioc.internal.RegistryImpl;
    028import org.apache.tapestry5.ioc.internal.RegistryWrapper;
    029import org.apache.tapestry5.ioc.internal.util.InternalUtils;
    030import org.apache.tapestry5.ioc.internal.util.OneShotLock;
    031import org.apache.tapestry5.ioc.modules.TapestryIOCModule;
    032import org.slf4j.Logger;
    033
    034import java.lang.reflect.AnnotatedElement;
    035import java.util.Arrays;
    036import java.util.List;
    037import java.util.Set;
    038
    039/**
    040 * Used to construct the IoC {@link org.apache.tapestry5.ioc.Registry}. This class is <em>not</em> thread-safe. The
    041 * Registry, once created, <em>is</em> thread-safe.
    042 */
    043public final class RegistryBuilder
    044{
    045    private final OneShotLock lock = new OneShotLock();
    046
    047    /**
    048     * Module defs, keyed on module id.
    049     */
    050    final List<ModuleDef2> modules = CollectionFactory.newList();
    051
    052    private final ClassLoader classLoader;
    053
    054    private final Logger logger;
    055
    056    private final LoggerSource loggerSource;
    057
    058    private final PlasticProxyFactory proxyFactory;
    059
    060    private final Set<Class> addedModuleClasses = CollectionFactory.newSet();
    061
    062    public RegistryBuilder()
    063    {
    064        this(Thread.currentThread().getContextClassLoader());
    065    }
    066
    067    public RegistryBuilder(ClassLoader classLoader)
    068    {
    069        this(classLoader, new LoggerSourceImpl());
    070    }
    071
    072    public RegistryBuilder(ClassLoader classLoader, LoggerSource loggerSource)
    073    {
    074        this.classLoader = classLoader;
    075        this.loggerSource = loggerSource;
    076        logger = loggerSource.getLogger(RegistryBuilder.class);
    077
    078        // Make the Proxy Factory appear to be a service inside TapestryIOCModule, even before that
    079        // module exists.
    080
    081        Logger proxyFactoryLogger = loggerSource.getLogger(TapestryIOCModule.class.getName() + ".PlasticProxyFactory");
    082
    083        proxyFactory = new PlasticProxyFactoryImpl(this.classLoader, proxyFactoryLogger);
    084
    085        add(TapestryIOCModule.class);
    086    }
    087
    088    /**
    089     * Adds a {@link ModuleDef} to the registry, returning the builder for further configuration.
    090     */
    091    public RegistryBuilder add(ModuleDef moduleDef)
    092    {
    093        lock.check();
    094
    095        // TODO: Some way to ensure that duplicate modules are not being added.
    096        // Part of TAPESTRY-2117 is in add(Class...) and that may be as much as we can
    097        // do as there is no concept of ModuleDef identity.
    098
    099        modules.add(InternalUtils.toModuleDef2(moduleDef));
    100
    101        return this;
    102    }
    103
    104    /**
    105     * Adds a number of modules (as module classes) to the registry, returning the builder for further configuration.
    106     *
    107     * @see org.apache.tapestry5.ioc.annotations.ImportModule
    108     */
    109    public RegistryBuilder add(Class... moduleClasses)
    110    {
    111        lock.check();
    112
    113        List<Class> queue = CollectionFactory.newList(Arrays.asList(moduleClasses));
    114
    115        while (!queue.isEmpty())
    116        {
    117            Class c = queue.remove(0);
    118
    119            // Quietly ignore previously added classes.
    120
    121            if (addedModuleClasses.contains(c))
    122                continue;
    123
    124            addedModuleClasses.add(c);
    125
    126            logger.info("Adding module definition for " + c);
    127
    128            ModuleDef def = new DefaultModuleDefImpl(c, logger, proxyFactory);
    129            add(def);
    130
    131
    132            @SuppressWarnings("RedundantCast")
    133            AnnotatedElement element = (AnnotatedElement) c;
    134
    135            SubModule subModule = element.getAnnotation(SubModule.class);
    136
    137            if (subModule != null)
    138            {
    139                queue.addAll(Arrays.asList(subModule.value()));
    140            }
    141            ImportModule importModule = element.getAnnotation(ImportModule.class);
    142
    143            if (importModule != null)
    144            {
    145                queue.addAll(Arrays.asList(importModule.value()));
    146            }
    147        }
    148
    149        return this;
    150    }
    151
    152    /**
    153     * Adds a modle class (specified by fully qualified class name) to the registry, returning the builder
    154     * for further configuration.
    155     *
    156     * @see org.apache.tapestry5.ioc.annotations.ImportModule
    157     */
    158    public RegistryBuilder add(String classname)
    159    {
    160        lock.check();
    161
    162        try
    163        {
    164            Class builderClass = Class.forName(classname, true, classLoader);
    165
    166            add(builderClass);
    167        } catch (Exception ex)
    168        {
    169            throw new RuntimeException(String.format("Failure loading Tapestry IoC module class %s: %s", classname,
    170                    ExceptionUtils.toMessage(ex)), ex);
    171        }
    172
    173        return this;
    174    }
    175
    176    /**
    177     * Constructs and returns the registry; this may only be done once. The caller is responsible for invoking
    178     * {@link org.apache.tapestry5.ioc.Registry#performRegistryStartup()}.
    179     */
    180    public Registry build()
    181    {
    182        lock.lock();
    183
    184        PerThreadOperationTracker tracker = new PerThreadOperationTracker(loggerSource.getLogger(Registry.class));
    185
    186        RegistryImpl registry = new RegistryImpl(modules, proxyFactory, loggerSource, tracker);
    187
    188        return new RegistryWrapper(registry);
    189    }
    190
    191    public ClassLoader getClassLoader()
    192    {
    193        return classLoader;
    194    }
    195
    196    public Logger getLogger()
    197    {
    198        return logger;
    199    }
    200
    201    /**
    202     * Constructs the registry, adds a {@link ModuleDef} and a number of modules (as module classes) to the registry and
    203     * performs registry startup. The returned registry is ready to use. The caller is must not invoke
    204     * {@link org.apache.tapestry5.ioc.Registry#performRegistryStartup()}.
    205     *
    206     * @param moduleDef
    207     *         {@link ModuleDef} to add
    208     * @param moduleClasses
    209     *         modules (as module classes) to add
    210     * @return {@link Registry}
    211     * @since 5.2.0
    212     */
    213    public static Registry buildAndStartupRegistry(ModuleDef moduleDef, Class... moduleClasses)
    214    {
    215        RegistryBuilder builder = new RegistryBuilder();
    216
    217        if (moduleDef != null)
    218            builder.add(moduleDef);
    219
    220        builder.add(moduleClasses);
    221
    222        Registry registry = builder.build();
    223
    224        registry.performRegistryStartup();
    225
    226        return registry;
    227    }
    228
    229    /**
    230     * Constructs the registry, adds a number of modules (as module classes) to the registry and
    231     * performs registry startup. The returned registry is ready to use. The caller is must not invoke
    232     * {@link org.apache.tapestry5.ioc.Registry#performRegistryStartup()}.
    233     *
    234     * @param moduleClasses
    235     *         modules (as module classes) to add
    236     * @return {@link Registry}
    237     * @since 5.2.0
    238     */
    239    public static Registry buildAndStartupRegistry(Class... moduleClasses)
    240    {
    241        return buildAndStartupRegistry(null, moduleClasses);
    242    }
    243}