Package org.apache.hivemind.impl

Source Code of org.apache.hivemind.impl.RegistryBuilder

//  Copyright 2004 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.apache.hivemind.impl;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hivemind.ApplicationRuntimeException;
import org.apache.hivemind.ClassResolver;
import org.apache.hivemind.ErrorHandler;
import org.apache.hivemind.HiveMind;
import org.apache.hivemind.Occurances;
import org.apache.hivemind.Registry;
import org.apache.hivemind.Resource;
import org.apache.hivemind.ShutdownCoordinator;
import org.apache.hivemind.internal.Module;
import org.apache.hivemind.parse.ConfigurationPointDescriptor;
import org.apache.hivemind.parse.ContributionDescriptor;
import org.apache.hivemind.parse.DescriptorParser;
import org.apache.hivemind.parse.ImplementationDescriptor;
import org.apache.hivemind.parse.InstanceBuilder;
import org.apache.hivemind.parse.InterceptorDescriptor;
import org.apache.hivemind.parse.ModuleDescriptor;
import org.apache.hivemind.parse.ServicePointDescriptor;
import org.apache.hivemind.util.IdUtils;
import org.apache.hivemind.util.URLResource;

/**
* Class used to build a {@link org.apache.hivemind.Registry} from individual
* {@link org.apache.hivemind.parse.ModuleDescriptor}.  The descriptors
* are processed one at a time and the registry is constructed using a single call
* to {@link #constructRegistry(Locale)} at the end.
*
* <p>
* A note about threadsafety: The assumption is that a single thread will access the RegistryBuilder
* at one time (typically, a startup class within some form of server or application).  Code
* here and in many of the related classes is divided into construction-time logic
* and runtime logic.  Runtime logic is synchronized and threadsafe.  Construction-time logic
* is not threadsafe.  Methods such as {@link org.apache.hivemind.impl.RegistryImpl#addModule(Module)},
* {@link org.apache.hivemind.impl.ModuleImpl#addConfigurationPoint(ConfigurationPoint)},
* {@link org.apache.hivemind.impl.ConfigurationPointImpl#addContribution(Contribution)}
* and the like are construction-time.  Once the registry is fully constructed, it is not
* allowed to invoke those methods (though, at this time, no checks occur).
*
* <p>Runtime methods, such as {@link org.apache.hivemind.impl.ModuleImpl#getService(String, Class)}
* are fully threadsafe.
*
* @author Howard Lewis Ship
*/
public final class RegistryBuilder
{
    private static final Log LOG = LogFactory.getLog(RegistryBuilder.class);

    static {
        if (!LOG.isErrorEnabled())
        {
            System.err.println(
                "********************************************************************************");
            System.err.println(
                "* L O G G I N G   C O N F I G U R A T I O N   E R R O R                        *");
            System.err.println(
                "* ---------------------------------------------------------------------------- *");
            System.err.println(
                "* Logging is not enabled for org.apache.hivemind.impl.RegistryBuilder.         *");
            System.err.println(
                "* Errors during HiveMind module descriptor parsing and validation may not be   *");
            System.err.println(
                "* logged. This may result in difficult-to-trace runtime exceptions, if there   *");
            System.err.println(
                "* are errors in any of your module descriptors. You should enable error        *");
            System.err.println(
                "* logging for the org.apache.hivemind and hivemind loggers.                    *");
            System.err.println(
                "********************************************************************************");
        }
    }

    /**
     * The path, within a JAR or the classpath, to the XML HiveMind module
     * deployment descriptor: <code>META-INF/hivemodule.xml</code>.
     */
    public static final String HIVE_MODULE_XML = "META-INF/hivemodule.xml";

    /**
     * List of {@link ModuleDescriptor}.
     */

    private List _moduleDescriptors = new ArrayList();

    /**
     * Map of {@link ModuleImpl} keyed on fully qualified module id.
     */
    private Map _modules = new HashMap();

    /**
     * Map of {@link PrimitiveServiceModel} (or subclass) keyed on fully qualified id.
     */

    private Map _servicePoints = new HashMap();

    /**
     * Map of {@link ConfigurationPointImpl} keyed on fully qualified id.
     */

    private Map _configurationPoints = new HashMap();

    /**
     * Delegate used for handling errors.
     */

    private ErrorHandler _errorHandler;

    /**
     * Parser instance used by all parsing for this builder.
     */
    private DescriptorParser _parser;

    /**
     * Shutdown coordinator shared by all objects.
     */

    private ShutdownCoordinator _shutdownCoordinator = new ShutdownCoordinatorImpl();

    /**
     * RegistryAssembly used by the module descriptor parser(s).
     */

    private RegistryAssemblyImpl _registryAssembly;

    public RegistryBuilder()
    {
        this(new DefaultErrorHandler());
    }

    public RegistryBuilder(ErrorHandler handler)
    {
        _errorHandler = handler;

        _registryAssembly = new RegistryAssemblyImpl(handler);
    }

    /**
     * Processes all modules that can be found using the resolver.
     */
    public void processModules(ClassResolver resolver)
    {
        if (LOG.isDebugEnabled())
            LOG.debug("Processing modules visible to " + resolver);

        processModulesResources(resolver, HIVE_MODULE_XML);
    }

    /**
     * Locates module deployment descriptors in the "standard" location (META-INF).
     */
    private void processModulesResources(ClassResolver resolver, String resourcePath)
    {
        ClassLoader loader = resolver.getClassLoader();
        Enumeration e = null;

        try
        {
            e = loader.getResources(resourcePath);
        }
        catch (IOException ex)
        {
            throw new ApplicationRuntimeException(
                ImplMessages.unableToFindModules(resolver, ex),
                ex);
        }

        while (e.hasMoreElements())
        {
            URL descriptorURL = (URL) e.nextElement();
            Resource descriptorResource = new URLResource(descriptorURL);

            processModule(resolver, descriptorResource);
        }
    }

    /**
     * Parses a module and processes its contents.  This is often used
     * in conjunction with {@link #processModules(ClassResolver)} to
     * parse additional modules that are not in the standard location
     * (for whatever reason).
     */
    public void processModule(ClassResolver resolver, Resource moduleDescriptorResource)
    {
        if (_parser == null)
            _parser = new DescriptorParser(_errorHandler, _registryAssembly);

        try
        {
            ModuleDescriptor md = _parser.parse(moduleDescriptorResource, resolver);

            processModule(md);

            // After parsing a module, parse any additional modules identified
            // within the module (using the <sub-module> element.

            while (_registryAssembly.moreQueuedModules())
            {
                md = _registryAssembly.parseNextQueued(_parser);

                processModule(md);
            }
        }
        catch (RuntimeException ex)
        {
            // An exception may leave the parser in an unknown state, so
            // give up on that instance and start with a fresh one.

            _parser = null;

            _errorHandler.error(LOG, ex.getMessage(), HiveMind.getLocation(ex), ex);
        }
    }

    /**
     * Processes a parsed HiveMind module descriptor.  This may be called
     * repeatedly before invoking {@link #constructRegistry(Locale)}.
     *
     * @param md the parsed module descriptor
     */
    public void processModule(ModuleDescriptor md)
    {
        String id = md.getModuleId();

        if (LOG.isDebugEnabled())
            LOG.debug("Processing module " + id);

        if (_modules.containsKey(id))
        {
            Module existing = (Module) _modules.get(id);

            _errorHandler.error(LOG, ImplMessages.duplicateModuleId(existing, md), null, null);

            // Ignore the duplicate module descriptor.
            return;
        }

        ModuleImpl module = new ModuleImpl();

        module.setLocation(md.getLocation());
        module.setModuleId(id);
        module.setClassResolver(md.getClassResolver());

        _modules.put(id, module);

        _moduleDescriptors.add(md);
    }

    private void addServiceAndConfigurationPoints(RegistryImpl registry)
    {
        int count = _moduleDescriptors.size();

        for (int i = 0; i < count; i++)
        {
            ModuleDescriptor md = (ModuleDescriptor) _moduleDescriptors.get(i);

            String id = md.getModuleId();

            ModuleImpl module = (ModuleImpl) _modules.get(id);

            addServicePoints(registry, module, md);

            addConfigurationPoints(registry, module, md);
        }
    }

    private void addServicePoints(RegistryImpl registry, ModuleImpl module, ModuleDescriptor md)
    {
        String moduleId = md.getModuleId();
        List services = md.getServicePoints();
        int count = size(services);

        for (int i = 0; i < count; i++)
        {
            ServicePointDescriptor sd = (ServicePointDescriptor) services.get(i);

            String pointId = moduleId + "." + sd.getId();

            if (LOG.isDebugEnabled())
                LOG.debug("Creating service extension point: " + pointId);

            // Choose which class to instantiate based on
            // whether the service is create-on-first-reference
            // or create-on-first-use (deferred).

            ServicePointImpl point = new ServicePointImpl();

            point.setExtensionPointId(pointId);
            point.setLocation(sd.getLocation());
            point.setModule(module);

            point.setServiceInterfaceName(sd.getInterfaceClassName());
            point.setParametersSchema(sd.getParametersSchema());
            point.setParametersCount(sd.getParametersCount());

            point.setShutdownCoordinator(_shutdownCoordinator);

            registry.addServicePoint(point);

            // Save this for the second phase, where contributions
            // from other modules are applied.

            _servicePoints.put(pointId, point);

            addInternalImplementations(module, pointId, sd);
        }
    }

    private void addConfigurationPoints(
        RegistryImpl registry,
        ModuleImpl module,
        ModuleDescriptor md)
    {
        String moduleId = md.getModuleId();
        List points = md.getConfigurationPoints();
        int count = size(points);

        for (int i = 0; i < count; i++)
        {
            ConfigurationPointDescriptor cpd = (ConfigurationPointDescriptor) points.get(i);

            String pointId = moduleId + "." + cpd.getId();

            if (LOG.isDebugEnabled())
                LOG.debug("Creating extension point " + pointId);

            ConfigurationPointImpl point = new ConfigurationPointImpl();

            point.setExtensionPointId(pointId);
            point.setLocation(cpd.getLocation());
            point.setModule(module);
            point.setExpectedCount(cpd.getCount());
            point.setContributionsSchema(cpd.getContributionsSchema());

            point.setShutdownCoordinator(_shutdownCoordinator);

            registry.addConfigurationPoint(point);

            // Needed later when we reconcile the rest
            // of the configuration contributions.

            _configurationPoints.put(pointId, point);
        }
    }

    private void addContributionElements(
        Module sourceModule,
        ConfigurationPointImpl point,
        List elements)
    {
        if (size(elements) == 0)
            return;

        if (LOG.isDebugEnabled())
            LOG.debug("Adding extensions to configuration point " + point.getExtensionPointId());

        ContributionImpl c = new ContributionImpl();
        c.setContributingModule(sourceModule);
        c.addElements(elements);

        point.addContribution(c);
    }

    /**
     * Invoked after all modules have been added with
     * {@link #processModule(ClassResolver, Resource)}.
     * This first resolves all the contributions, then constructs and returns
     * the registry.
     */
    public Registry constructRegistry(Locale locale)
    {

        // Process any deferred operations

        _registryAssembly.performPostProcessing();

        RegistryImpl result = new RegistryImpl(_errorHandler, locale);

        addServiceAndConfigurationPoints(result);

        addImplementationsAndContributions();

        checkForMissingServices();

        checkContributionCounts();

        result.setShutdownCoordinator(_shutdownCoordinator);

        addModulesToRegistry(result);

        result.startup();

        return result;
    }

    private void addModulesToRegistry(RegistryImpl registry)
    {
        // Add each module to the registry.

        Iterator i = _modules.values().iterator();
        while (i.hasNext())
        {
            ModuleImpl module = (ModuleImpl) i.next();

            if (LOG.isDebugEnabled())
                LOG.debug("Adding module " + module.getModuleId() + " to registry");

            module.setRegistry(registry);
        }
    }

    private void addImplementationsAndContributions()
    {
        int count = _moduleDescriptors.size();

        for (int i = 0; i < count; i++)
        {
            ModuleDescriptor md = (ModuleDescriptor) _moduleDescriptors.get(i);

            if (LOG.isDebugEnabled())
                LOG.debug("Adding contributions from module " + md.getModuleId());

            addImplementations(md);
            addContributions(md);
        }
    }

    private void addImplementations(ModuleDescriptor md)
    {
        String moduleId = md.getModuleId();
        ModuleImpl sourceModule = (ModuleImpl) _modules.get(moduleId);

        List implementations = md.getImplementations();
        int count = size(implementations);

        for (int i = 0; i < count; i++)
        {
            ImplementationDescriptor impl = (ImplementationDescriptor) implementations.get(i);

            String pointId = impl.getServiceId();
            String qualifiedId = qualify(moduleId, pointId);

            addImplementations(sourceModule, qualifiedId, impl);
        }

    }

    private void addContributions(ModuleDescriptor md)
    {
        String moduleId = md.getModuleId();
        ModuleImpl sourceModule = (ModuleImpl) _modules.get(moduleId);

        List contributions = md.getContributions();
        int count = size(contributions);

        for (int i = 0; i < count; i++)
        {
            ContributionDescriptor cd = (ContributionDescriptor) contributions.get(i);

            String pointId = cd.getConfigurationId();
            String qualifiedId = qualify(moduleId, pointId);

            ConfigurationPointImpl point =
                (ConfigurationPointImpl) _configurationPoints.get(qualifiedId);

            if (point == null)
            {
                _errorHandler.error(
                    LOG,
                    ImplMessages.unknownConfigurationPoint(moduleId, cd),
                    cd.getLocation(),
                    null);

                continue;
            }

            addContributionElements(sourceModule, point, cd.getElements());
        }
    }

    /**
     * Qualifies the artifact id with the module id, if necessary.
     */
    private static String qualify(String moduleId, String artifactId)
    {
        if (artifactId.indexOf('.') >= 0)
            return artifactId;

        return moduleId + "." + artifactId;
    }

    /**
     * Adds internal service contributions; the contributions provided inplace
     * with the service definition.
     */
    private void addInternalImplementations(
        ModuleImpl sourceModule,
        String pointId,
        ServicePointDescriptor spd)
    {
        InstanceBuilder builder = spd.getInstanceBuilder();
        List interceptors = spd.getInterceptors();

        if (builder == null && interceptors == null)
            return;

        if (builder != null)
            addServiceInstanceBuilder(sourceModule, pointId, builder);

        if (interceptors == null)
            return;

        int count = size(interceptors);

        for (int i = 0; i < count; i++)
        {
            InterceptorDescriptor id = (InterceptorDescriptor) interceptors.get(i);
            addInterceptor(sourceModule, pointId, id);
        }
    }

    /**
     * Adds ordinary service contributions.
     */

    private void addImplementations(
        ModuleImpl sourceModule,
        String pointId,
        ImplementationDescriptor id)
    {
        InstanceBuilder builder = id.getInstanceBuilder();
        List interceptors = id.getInterceptors();

        if (builder != null)
            addServiceInstanceBuilder(sourceModule, pointId, builder);

        int count = size(interceptors);
        for (int i = 0; i < count; i++)
        {
            InterceptorDescriptor ind = (InterceptorDescriptor) interceptors.get(i);
            addInterceptor(sourceModule, pointId, ind);
        }
    }

    /**
     * Adds an {@link InstanceBuilder}
     * to a service extension point.
     *
      *
     */

    private void addServiceInstanceBuilder(
        ModuleImpl sourceModule,
        String pointId,
        InstanceBuilder builder)
    {
        if (LOG.isDebugEnabled())
            LOG.debug("Adding " + builder + " to service extension point " + pointId);

        ServicePointImpl sep = (ServicePointImpl) _servicePoints.get(pointId);

        if (sep == null)
        {
            _errorHandler.error(
                LOG,
                ImplMessages.unknownServicePoint(sourceModule, pointId),
                builder.getLocation(),
                null);
            return;
        }

        if (sep.getServiceConstructor() != null)
        {
            LOG.error(ImplMessages.duplicateFactory(sourceModule, pointId, sep));

            return;
        }

        sep.setServiceModel(builder.getServiceModel());
        sep.setServiceConstructor(builder.createConstructor(sep, sourceModule));
    }

    private void addInterceptor(ModuleImpl sourceModule, String pointId, InterceptorDescriptor id)
    {
        if (LOG.isDebugEnabled())
            LOG.debug("Adding " + id + " to service extension point " + pointId);

        ServicePointImpl sep = (ServicePointImpl) _servicePoints.get(pointId);

        String sourceModuleId = sourceModule.getModuleId();

        if (sep == null)
        {
            _errorHandler.error(
                LOG,
                ImplMessages.unknownServicePoint(sourceModule, pointId),
                id.getLocation(),
                null);

            return;
        }

        ServiceInterceptorContributionImpl sic = new ServiceInterceptorContributionImpl();

        // Allow the factory id to be unqualified, to refer to an interceptor factory
        // service from within the same module.

        sic.setFactoryServiceId(qualify(sourceModuleId, id.getFactoryServiceId()));
        sic.setLocation(id.getLocation());

        sic.setFollowingInterceptorIds(IdUtils.qualifyList(sourceModuleId, id.getBefore()));
        sic.setPrecedingInterceptorIds(IdUtils.qualifyList(sourceModuleId, id.getAfter()));

        sic.setContributingModule(sourceModule);
        sic.setParameters(id.getParameters());

        sep.addInterceptorContribution(sic);
    }

    /**
     * Checks that each service has at service constructor.
     */
    private void checkForMissingServices()
    {
        Iterator i = _servicePoints.values().iterator();
        while (i.hasNext())
        {
            ServicePointImpl point = (ServicePointImpl) i.next();

            if (point.getServiceConstructor() != null)
                continue;

            _errorHandler.error(LOG, ImplMessages.missingService(point), null, null);
        }
    }

    /**
     * Checks that each configuration extension point has the right number of contributions.
     */

    private void checkContributionCounts()
    {
        Iterator i = _configurationPoints.values().iterator();

        while (i.hasNext())
        {
            ConfigurationPointImpl point = (ConfigurationPointImpl) i.next();

            Occurances expected = point.getExpectedCount();

            int actual = point.getContributionCount();

            if (expected.inRange(actual))
                continue;

            _errorHandler.error(
                LOG,
                ImplMessages.wrongNumberOfContributions(point, actual, expected),
                point.getLocation(),
                null);
        }

    }

    private static int size(Collection c)
    {
        return c == null ? 0 : c.size();
    }

    /**
     * Constructs a default registry based on just the modules
     * visible to the thread context class loader (this is sufficient
     * is the majority of cases), and using the default locale. If you have different
     * error handling needs, or wish to pick up HiveMind module deployment
     * descriptors for non-standard locations, you must create
     * a RegistryBuilder instance yourself.
     */
    public static Registry constructDefaultRegistry()
    {
        ClassResolver resolver = new DefaultClassResolver();
        RegistryBuilder builder = new RegistryBuilder();

        builder.processModules(resolver);

        return builder.constructRegistry(Locale.getDefault());
    }

}
TOP

Related Classes of org.apache.hivemind.impl.RegistryBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.