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