be.idamediafoundry.sofa.livecycle.dsc.util.AnnotationDrivenQDoxComponentInfoExtractor.java Source code

Java tutorial

Introduction

Here is the source code for be.idamediafoundry.sofa.livecycle.dsc.util.AnnotationDrivenQDoxComponentInfoExtractor.java

Source

/*
 * Copyright 2012-2013 iDA MediaFoundry (www.ida-mediafoundry.be)
 *
 * 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 be.idamediafoundry.sofa.livecycle.dsc.util;

import be.idamediafoundry.sofa.livecycle.dsc.annotations.ConfigParam;
import be.idamediafoundry.sofa.livecycle.dsc.annotations.FactoryMethod;
import be.idamediafoundry.sofa.livecycle.dsc.annotations.Operation;
import be.idamediafoundry.sofa.livecycle.dsc.annotations.Version;
import be.idamediafoundry.sofa.livecycle.maven.component.configuration.Component;
import be.idamediafoundry.sofa.livecycle.maven.component.configuration.ConfigParameterType;
import be.idamediafoundry.sofa.livecycle.maven.component.configuration.FaultType;
import be.idamediafoundry.sofa.livecycle.maven.component.configuration.InputParameterType;
import be.idamediafoundry.sofa.livecycle.maven.component.configuration.OperationType;
import be.idamediafoundry.sofa.livecycle.maven.component.configuration.OutputParameterType;
import be.idamediafoundry.sofa.livecycle.maven.component.configuration.Service;
import be.idamediafoundry.sofa.livecycle.maven.component.configuration.Service.AutoDeploy;
import com.thoughtworks.qdox.model.AbstractJavaEntity;
import com.thoughtworks.qdox.model.Annotation;
import com.thoughtworks.qdox.model.DocletTag;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaMethod;
import com.thoughtworks.qdox.model.JavaParameter;
import com.thoughtworks.qdox.model.Type;
import com.thoughtworks.qdox.model.annotation.AnnotationConstant;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.plugin.logging.Log;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.Map;

public class AnnotationDrivenQDoxComponentInfoExtractor extends AbstractQDoxComponentInfoExtractor {

    public AnnotationDrivenQDoxComponentInfoExtractor(String sourcePath, Log log) {
        super(sourcePath, log);
    }

    public void populateComponent(Component component) {
        //TODO we might look for bootstrap and livecycle classes...
    }

    public boolean populateServices(Service service, JavaClass javaClass) {
        service.setName(javaClass.getName());
        service.setImplementationClass(javaClass.getFullyQualifiedName());

        be.idamediafoundry.sofa.livecycle.dsc.annotations.Service serviceAnnotation = findAnnotation(javaClass,
                be.idamediafoundry.sofa.livecycle.dsc.annotations.Service.class);
        if (StringUtils.isNotBlank(serviceAnnotation.smallIcon())) {
            service.setSmallIcon(serviceAnnotation.smallIcon());
        }
        if (StringUtils.isNotBlank(serviceAnnotation.largeIcon())) {
            service.setLargeIcon(serviceAnnotation.largeIcon());
        }

        String comment = javaClass.getComment();
        service.setHint(getFirstSentence(comment));
        service.setDescription(comment);

        // Find factory method.
        JavaMethod factoryMethod = findAnnotationOnMethods(javaClass, FactoryMethod.class);
        if (factoryMethod != null) {
            if (factoryMethod.isAbstract() || factoryMethod.isConstructor() || !factoryMethod.isPublic()
                    || factoryMethod.isPropertyAccessor() || factoryMethod.isPropertyMutator()
                    || !factoryMethod.isStatic()) {
                throw new IllegalStateException("You should not annotate " + factoryMethod.getName()
                        + " as FactoryMethod, it is not a valid factory method!");
            }

            service.setFactoryMethod(factoryMethod.getName());
        }

        if (serviceAnnotation
                .requestProcessingStrategy() != be.idamediafoundry.sofa.livecycle.dsc.annotations.Service.RequestProcessingStrategy.NONE) {
            service.setRequestProcessingStrategy(serviceAnnotation.requestProcessingStrategy().name());
        }
        return true;
    }

    public boolean populateAutoDeploy(Component component, AutoDeploy autoDeploy, JavaClass javaClass) {
        be.idamediafoundry.sofa.livecycle.dsc.annotations.Service serviceAnnotation = findAnnotation(javaClass,
                be.idamediafoundry.sofa.livecycle.dsc.annotations.Service.class);
        if (serviceAnnotation.autoDeploy()) {
            autoDeploy.setServiceId(javaClass.getName());
            autoDeploy
                    .setCategoryId(StringUtils.isBlank(serviceAnnotation.categoryId()) ? component.getComponentId()
                            : serviceAnnotation.categoryId());

            Version versionAnnotation = serviceAnnotation.version();

            if (versionAnnotation != null) {
                if (versionAnnotation.major() > -1) {
                    autoDeploy.setMajorVersion(versionAnnotation.major());
                }
                if (versionAnnotation.minor() > -1) {
                    autoDeploy.setMinorVersion(versionAnnotation.minor());
                }
            }
            return true;
        } else {
            return false;
        }
    }

    public boolean populateOperation(OperationType operation, JavaMethod javaMethod,
            List<String> existinOperationNames) {
        Operation operationAnnotation = findAnnotation(javaMethod, Operation.class);
        String suggestedName = operationAnnotation == null ? null : operationAnnotation.name();
        if (StringUtils.isBlank(suggestedName)) {
            suggestedName = null;
        }
        generateOperationNameMethodTitle(existinOperationNames, javaMethod, operation, suggestedName);

        String comment = javaMethod.getComment();
        operation.setHint(getFirstSentence(comment));
        operation.setDescription(comment);

        return true;
    }

    public boolean populateConfigParameter(ConfigParameterType configParameter, JavaMethod javaMethod) {
        String comment = javaMethod.getComment();
        String propertyName = javaMethod.getPropertyName();
        if (propertyName.length() > 100) {
            // Following spec: name must be no larger then 100 characters
            configParameter.setProperty(propertyName);
            propertyName = propertyName.substring(0, 100);
        }

        configParameter.setName(propertyName);
        configParameter.setType(getFullyQualifiedJavaType(javaMethod.getPropertyType()));
        configParameter.setHint(getFirstSentence(comment));
        configParameter.setDescription(comment);
        configParameter.setTitle(generateTitle(propertyName));

        ConfigParam configParam = findAnnotation(javaMethod, ConfigParam.class);
        if (configParam != null) {
            configParameter.setRequired(configParam.required());
            if (StringUtils.isNotBlank(configParam.defaultValue())) {
                configParameter.setDefaultValue(configParam.defaultValue());
            }
        }

        return true;
    }

    public boolean populateInputParameter(InputParameterType inputParameter, JavaMethod javaMethod,
            JavaParameter javaParameter) {
        Map<String, String> paramTagMap = getCommentMapForTag(javaMethod, PARAM_TAG);

        inputParameter.setName(javaParameter.getName());
        inputParameter.setType(getFullyQualifiedJavaType(javaParameter.getType()));

        String comment = paramTagMap.get(javaParameter.getName());
        inputParameter.setHint(getFirstSentence(comment));
        inputParameter.setDescription(comment);
        inputParameter.setTitle(generateTitle(javaParameter.getName()));
        return true;
    }

    public boolean populateOutputParameter(OutputParameterType outputParameter, JavaMethod javaMethod) {
        boolean result = false;
        Type methodResultType = javaMethod.getReturnType();
        if (!methodResultType.equals(Type.VOID)) {
            String outputParameterName = DEFAULT_OUT_PARAM_NAME;
            Operation operationAnnotation = findAnnotation(javaMethod, Operation.class);
            if (operationAnnotation != null) {
                if (StringUtils.isNotBlank(operationAnnotation.outputName())) {
                    outputParameterName = operationAnnotation.outputName();
                }
            }
            outputParameter.setName(outputParameterName);
            outputParameter.setTitle(outputParameterName);
            outputParameter.setType(getFullyQualifiedJavaType(methodResultType));

            DocletTag returnDocletTag = javaMethod.getTagByName(RETURN_TAG);
            if (returnDocletTag != null) {
                String comment = returnDocletTag.getValue();
                outputParameter.setHint(getFirstSentence(comment));
                outputParameter.setDescription(comment);
            }
            result = true;
        }
        return result;
    }

    public boolean populateFault(FaultType fault, JavaMethod javaMethod, Type exceptionType) {
        Map<String, String> exceptionTagMap = getCommentMapForTag(javaMethod, "throws");

        String name = exceptionType.getJavaClass().getName();
        fault.setName(name);
        fault.setType(exceptionType.getFullyQualifiedName());
        fault.setTitle(generateTitle(name));
        String comment = exceptionTagMap.get(name);
        fault.setHint(getFirstSentence(comment));
        fault.setDescription(comment);

        return true;
    }

    @Override
    public boolean acceptAsService(JavaClass javaClass) {
        boolean accept = false;

        be.idamediafoundry.sofa.livecycle.dsc.annotations.Service serviceAnnotation = findAnnotation(javaClass,
                be.idamediafoundry.sofa.livecycle.dsc.annotations.Service.class);
        if (serviceAnnotation != null) {
            if (!javaClass.isAbstract() && !javaClass.isInterface() && javaClass.isPublic()
                    && !javaClass.isA("java.lang.Throwable")) {
                accept = true;
            } else {
                throw new RuntimeException(
                        "You should not annotate this class with @Service. Only public non-abstract classes are supported (exceptions excluded).");
            }
        }

        return accept;
    }

    @Override
    public boolean acceptAsOperation(JavaMethod javaMethod) {
        Type methodResultType = javaMethod.getReturnType();
        FactoryMethod factoryMethod = findAnnotation(javaMethod, FactoryMethod.class);

        return javaMethod.isPublic() && methodResultType != null && !javaMethod.isPropertyAccessor()
                && !javaMethod.isPropertyMutator() && factoryMethod == null;
    }

    @Override
    public boolean acceptAsConfigParameter(JavaMethod javaMethod) {
        return javaMethod.isPropertyMutator();
    }

    /**
     * Find an annotation of a given type on the given entity.
     *
     * @param entity The entity on which we are looking for the annotation
     * @param type   the type of the annotation
     * @return the annotation of the given type on the given entity, or null if
     *         none is found.
     */
    private <T extends java.lang.annotation.Annotation> T findAnnotation(AbstractJavaEntity entity, Class<T> type) {
        Annotation[] annotations = entity.getAnnotations();
        T result = null;
        if (annotations != null) {
            for (Annotation annotation : annotations) {
                if (annotation.getType().getFullyQualifiedName().equals(type.getName())) {
                    result = convertToJavaLang(annotation, type);
                    break;
                }
            }
        }
        return result;
    }

    private JavaMethod findAnnotationOnMethods(JavaClass javaClass,
            Class<? extends java.lang.annotation.Annotation> annotationClass) {
        JavaMethod result = null;
        JavaMethod[] methods = javaClass.getMethods();
        for (JavaMethod javaMethod : methods) {
            java.lang.annotation.Annotation factoryMethod = findAnnotation(javaMethod, annotationClass);
            if (factoryMethod != null) {
                result = javaMethod;
                break;
            }
        }
        return result;
    }

    private <T extends java.lang.annotation.Annotation> T convertToJavaLang(final Annotation annotation,
            final Class<T> expectedType) {
        try {
            final Class<?> annotationClass = Class.forName(annotation.getType().getFullyQualifiedName());
            @SuppressWarnings("unchecked")
            T proxy = (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] { annotationClass },
                    new InvocationHandler() {

                        public Object invoke(Object instance, Method method, Object[] args) throws Throwable {
                            if (method.getName().equals("toString")) {
                                return "Proxied annotation of type " + annotationClass;
                            } else if (method.getName().equals("getClass")) {
                                return annotationClass;
                            }

                            Object value = annotation.getProperty(method.getName());
                            if (value == null) {
                                return method.getDefaultValue();
                            }
                            if (value instanceof Annotation) {
                                java.lang.annotation.Annotation sub = convertToJavaLang((Annotation) value,
                                        java.lang.annotation.Annotation.class);
                                return sub;
                            } else {
                                AnnotationConstant constant = (AnnotationConstant) value;
                                value = constant.getValue();
                                return value;
                            }
                        }
                    });

            return proxy;

        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException(
                    "The source code is annotated with a class that could not be found on your project's classpath, please fix this!",
                    e);
        }
    }

}