Package org.apache.hivemind.annotations.internal

Source Code of org.apache.hivemind.annotations.internal.AnnotatedModuleProcessor

// Copyright 2007 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.annotations.internal;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

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.Location;
import org.apache.hivemind.Resource;
import org.apache.hivemind.annotations.AnnotationsMessages;
import org.apache.hivemind.annotations.definition.Configuration;
import org.apache.hivemind.annotations.definition.Module;
import org.apache.hivemind.annotations.definition.Service;
import org.apache.hivemind.annotations.definition.Submodule;
import org.apache.hivemind.definition.Contribution;
import org.apache.hivemind.definition.ImplementationConstructor;
import org.apache.hivemind.definition.ImplementationDefinition;
import org.apache.hivemind.definition.Occurances;
import org.apache.hivemind.definition.RegistryDefinition;
import org.apache.hivemind.definition.Visibility;
import org.apache.hivemind.definition.impl.ConfigurationPointDefinitionImpl;
import org.apache.hivemind.definition.impl.ContributionDefinitionImpl;
import org.apache.hivemind.definition.impl.ModuleDefinitionImpl;
import org.apache.hivemind.definition.impl.ImplementationDefinitionImpl;
import org.apache.hivemind.definition.impl.ServicePointDefinitionImpl;
import org.apache.hivemind.util.ClasspathResource;
import org.apache.hivemind.util.IdUtils;

/**
* Does the work for {@link org.apache.hivemind.annotations.AnnotatedModuleReader}. Processes an
* annotated class and registers the defined extension and extension points in a registry
* definition.
* The construction of extension points and extensions bases on reflective method calls
* to an instance of the module class. The module instance is created by a
* {@link ModuleInstanceProvider} during registry construction.
*
* @author Achim Huegen
*/
public class AnnotatedModuleProcessor
{
    private static final Log _log = LogFactory.getLog(AnnotatedModuleProcessor.class);

    private ClassResolver _classResolver;

    private ErrorHandler _errorHandler;

    private RegistryDefinition _registryDefinition;

    public AnnotatedModuleProcessor(RegistryDefinition registryDefinition,
            ClassResolver classResolver, ErrorHandler errorHandler)
    {
        _registryDefinition = registryDefinition;
        _classResolver = classResolver;
        _errorHandler = errorHandler;
    }
   
    public void processModule(Class moduleClass)
    {
        String moduleId = determineModuleId(moduleClass);
        processModule(moduleClass, moduleId);
    }

    /**
     * Processes a module. Inspects the class.
     *
     * @param moduleClass
     */
    public void processModule(Class moduleClass, String moduleId)
    {
        checkModuleClassPrerequisites(moduleClass);
       
        ModuleDefinitionImpl module = new ModuleDefinitionImpl(moduleId,
                createModuleLocation(moduleClass), _classResolver, moduleClass.getPackage().getName());

        // processServices(moduleClass);

        ModuleInstanceProvider instanceProvider = new ModuleInstanceProviderImpl(moduleClass,
                module.getId());
        // Register provider as initialization provider so it can acquire a reference to the
        // registry
        _registryDefinition.addRegistryInitializationListener(instanceProvider);

        processModuleMethods(moduleClass, module, instanceProvider);
        _registryDefinition.addModule(module);

    }
   
    /**
     * Ensures that a module class fulfills all prerequisites.
     *
     * @param moduleClass
     */
    protected void checkModuleClassPrerequisites(Class moduleClass)
    {
        // These modifiers are allowed
        final int validModifiers = Modifier.PUBLIC;
       
        int invalidModifiers = moduleClass.getModifiers() & ~validModifiers;
        if (invalidModifiers > 0) {
            throw new ApplicationRuntimeException(AnnotationsMessages.moduleClassHasInvalidModifiers(moduleClass, invalidModifiers));
        }
       
        // Check for package-private access
        if ((moduleClass.getModifiers() & (Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE)) == 0) {
            throw new ApplicationRuntimeException(AnnotationsMessages.moduleClassIsPackagePrivate(moduleClass));
        }
    }

    private void processModuleMethods(Class moduleClass, ModuleDefinitionImpl module,
            ModuleInstanceProvider instanceProvider)
    {
        // We need access to protected methods via getDeclaredMethods
        // That means we must visit all superclasses manually
        Method[] methods = moduleClass.getDeclaredMethods();
        for (int i = 0; i < methods.length; i++)
        {
            Method method = methods[i];
            processMethod(method, module, instanceProvider);
            // Process superclass
            Class superClass = moduleClass.getSuperclass();
            if (!superClass.equals(Object.class)) {
                processModuleMethods(superClass, module, instanceProvider);
            }
        }
    }

    private void processMethod(Method method, ModuleDefinitionImpl module,
            ModuleInstanceProvider instanceProvider)
    {
        if (_log.isDebugEnabled())
        {
            _log.debug("Checking method " + method.getName() + " for annotations");
        }

        Annotation[] annotations = method.getAnnotations();
        for (int j = 0; j < annotations.length; j++)
        {
            Annotation annotation = annotations[j];

            if (Service.class.equals(annotation.annotationType()))
            {
                processAnnotatedServiceMethod(
                        method,
                        (Service) annotation,
                        module,
                        instanceProvider);
            }
            else if (Configuration.class.equals(annotation.annotationType()))
            {
                processAnnotatedConfigurationMethod(
                        method,
                        (Configuration) annotation,
                        module,
                        instanceProvider);
            }
            else if (org.apache.hivemind.annotations.definition.Contribution.class.equals(annotation.annotationType()))
            {
                processAnnotatedContributionMethod(
                        method,
                        (org.apache.hivemind.annotations.definition.Contribution) annotation,
                        module,
                        instanceProvider);
            }
            else if (Submodule.class.equals(annotation.annotationType()))
            {
                processAnnotatedSubmoduleMethod(
                        method,
                        (Submodule) annotation,
                        module,
                        instanceProvider);
            }
        }

    }
   
    /**
     * Ensures that an annotated method has only allowed modifiers.
     * By default Modifier.PUBLIC and Modifier.PROTECTED are allowed.
     * @param method  the method
     * @param allowedModifiers  allowed {@link Modifier modifiers}.
     * @param methodType  used in error messages to describe what the method is used for
     */
    protected void checkMethodModifiers(Method method, int allowedModifiers, String methodType)
    {
        // These modifiers are allowed
        final int validModifiers = Modifier.PUBLIC | Modifier.PROTECTED | allowedModifiers;
       
        int invalidModifiers = method.getModifiers() & ~validModifiers;
        if (invalidModifiers > 0) {
            throw new ApplicationRuntimeException(AnnotationsMessages.annotatedMethodHasInvalidModifiers(method, methodType, invalidModifiers));
        }

        // TODO: Check for package access
       
        // Check for setAccessible-Errors when Modifier.PROTECTED is used
        if (Modifier.isProtected(method.getModifiers())) {
            // Try to set method accessible
            try
            {
                method.setAccessible(true);
            }
            catch (SecurityException e)
            {
                throw new ApplicationRuntimeException(AnnotationsMessages.annotatedMethodIsProtectedAndNotAccessible(method, methodType));
            }
        }
    }

    private void processAnnotatedServiceMethod(Method method, Service service,
            ModuleDefinitionImpl module, ModuleInstanceProvider instanceProvider)
    {
        checkMethodModifiers(method, 0, "service point");
       
        if (_log.isDebugEnabled())
        {
            _log.debug("Method " + method.getName() + "classified as service point.");
        }
       
        Location location = new AnnotatedModuleLocation(module.getLocation().getResource(),
                method.getDeclaringClass(), method);

        Visibility visibility = Visibility.PUBLIC;
        if (Modifier.isProtected(method.getModifiers())) {
            visibility = Visibility.PRIVATE;
        }
        ServicePointDefinitionImpl spd = new ServicePointDefinitionImpl(module, service.id(), location,
                visibility, method.getReturnType().getName());
        module.addServicePoint(spd);

        ImplementationConstructor constructor = new MethodCallImplementationConstructor(location,
                method, instanceProvider);

        ImplementationDefinition sid = new ImplementationDefinitionImpl(module, location,
                constructor, service.serviceModel(), true);

        spd.addImplementation(sid);

    }

    private void processAnnotatedConfigurationMethod(Method method, Configuration configuration, ModuleDefinitionImpl module, ModuleInstanceProvider instanceProvider)
    {
        checkMethodModifiers(method, 0, "configuration point");

        if (_log.isDebugEnabled())
        {
            _log.debug("Method " + method.getName() + "classified as configuration point.");
        }
       
        Location location = new AnnotatedModuleLocation(module.getLocation().getResource(),
                method.getDeclaringClass(), method);
       
        Visibility visibility = Visibility.PUBLIC;
        if (Modifier.isProtected(method.getModifiers())) {
            visibility = Visibility.PRIVATE;
        }
        ConfigurationPointDefinitionImpl cpd = new ConfigurationPointDefinitionImpl(module, configuration.id(),
                location, visibility, method.getReturnType().getName(), Occurances.UNBOUNDED);
        module.addConfigurationPoint(cpd);
       
        // Add method implementation as initial contribution
        Contribution contribution = new MethodCallContributionConstructor(
                location, method, instanceProvider);
        ContributionDefinitionImpl cd = new ContributionDefinitionImpl(module, location, contribution, true);
        cpd.addContribution(cd);
    }

    private void processAnnotatedContributionMethod(Method method, org.apache.hivemind.annotations.definition.Contribution contribution, ModuleDefinitionImpl module, ModuleInstanceProvider instanceProvider)
    {
        checkMethodModifiers(method, 0, "contribution");

        if (_log.isDebugEnabled())
        {
            _log.debug("Method " + method.getName() + "classified as contribution.");
        }
       
        Location location = new AnnotatedModuleLocation(module.getLocation().getResource(),
                method.getDeclaringClass(), method);
       
        Contribution constructor = new MethodCallContributionConstructor(
                location, method, instanceProvider);

        ContributionDefinitionImpl cd = new ContributionDefinitionImpl(module, location, constructor, false);
        String qualifiedConfigurationId = IdUtils.qualify(
                module.getId(),
                contribution.configurationId());
        module.addContribution(qualifiedConfigurationId, cd);

    }
   
    /**
     * Processes a method that is marked as submodule definition.
     */
    private void processAnnotatedSubmoduleMethod(Method method, Submodule submodule, ModuleDefinitionImpl module, ModuleInstanceProvider instanceProvider)
    {
        checkMethodModifiers(method, 0, "submodule");

        if (_log.isDebugEnabled())
        {
            _log.debug("Method " + method.getName() + "classified as submodule.");
        }
       
        String fullModuleId = IdUtils.qualify(
                module.getId(),
                submodule.id());
        // TODO: Check if return type is defined
        AnnotatedModuleProcessor submoduleProcessor = new AnnotatedModuleProcessor(_registryDefinition,
                _classResolver, _errorHandler);
        submoduleProcessor.processModule(method.getReturnType(), fullModuleId);
    }
   
    /**
     * Creates a location pointing at the module class.
     */
    protected Location createModuleLocation(Class moduleClass)
    {
        String path = "/" + moduleClass.getName().replace('.', '/');

        Resource r = new ClasspathResource(_classResolver, path);

        return new AnnotatedModuleLocation(r, moduleClass);
    }

    /**
     * Determines the module id of the module defined by the annotated class.
     * First priority has a {@link Module} annotation. If none is defined the
     * id is determined from class and package name.
     *
     * @param moduleClass
     *            the module class
     * @return the id
     */
    private String determineModuleId(Class moduleClass)
    {
        Module moduleAnnotation = (Module) moduleClass.getAnnotation(Module.class);
        if (moduleAnnotation != null) {
            return moduleAnnotation.id();
        } else {
            return getDefaultModuleId(moduleClass);
        }
    }

    private String getDefaultModuleId(Class moduleClass)
    {
        return moduleClass.getName();
    }
}
TOP

Related Classes of org.apache.hivemind.annotations.internal.AnnotatedModuleProcessor

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.