All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.hammelion.processors.ResourceProcessor Maven / Gradle / Ivy

Go to download

Jraml helps you build your java implementation of REST API, based on specification, described by raml file.

There is a newer version: 0.3
Show newest version
package com.github.hammelion.processors;

import java.beans.Introspector;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ConstPool;
import javassist.bytecode.ParameterAnnotationsAttribute;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.StringMemberValue;

import javax.inject.Inject;
import javax.inject.Named;
import javax.ws.rs.FormParam;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;

import com.github.hammelion.RAMLConfig;
import com.github.hammelion.exceptions.CorrespondingMethodNotFoundException;
import com.github.hammelion.matchers.method.MethodMatcher;
import com.github.hammelion.parsers.RAMLParserFacade;
import org.raml.model.Action;
import org.raml.model.Resource;
import org.raml.model.parameter.UriParameter;

import com.google.common.collect.Sets;

@Named
public class ResourceProcessor {
    private final ClassPool POOL = ClassPool.getDefault();

    private final MethodMatcher methodMatcher;

    @Inject
    public ResourceProcessor(MethodMatcher methodMatcher) {
        this.methodMatcher = methodMatcher;
    }

    public void process(RAMLParserFacade ramlParserFacade, Class originalClass, ClassLoader classLoader)
            throws NotFoundException, CorrespondingMethodNotFoundException, CannotCompileException {
        final String ramlFilePath = originalClass.getAnnotation(RAMLConfig.class).value();
        final String resourceKey = determineResourceKey(originalClass);

        final Resource resource = ramlParserFacade.findResource(ramlFilePath, resourceKey);
        process(resource, originalClass, classLoader);
    }

    private void process(Resource resource, Class originalClass, ClassLoader classLoader) throws NotFoundException,
            CorrespondingMethodNotFoundException, CannotCompileException {
        if (resource != null) {
            final CtClass clazz = POOL.getCtClass(originalClass.getName());
            final ConstPool constPool = clazz.getClassFile().getConstPool();
            Set missingUriParameters = determineMissingUriParameters(originalClass, resource.getUriParameters());
            actionLoop: for (Action action : resource.getActions().values()) {
                processAction(clazz, constPool, originalClass, missingUriParameters, action);
            }
            resourceLoop: for (Resource subResource : resource.getResources().values()) {
                processSubResource(classLoader, clazz, constPool, originalClass, subResource);
            }
            clazz.toClass(classLoader);
        }
    }

    private Set determineMissingUriParameters(Class originalClass, Map uriParameters) {
        Set missingUriParameters = new HashSet<>();
        Set fields = new HashSet<>();
        for (Field field : originalClass.getDeclaredFields()) {
            fields.add(Introspector.decapitalize(field.getType().getSimpleName()));
        }
        if (uriParameters != null) {
            Sets.difference(uriParameters.keySet(), fields).copyInto(missingUriParameters);

        }
        return missingUriParameters;
    }

    private void processSubResource(ClassLoader classLoader, CtClass clazz, ConstPool constPool, Class originalClass,
            Resource subResource) throws NotFoundException, CorrespondingMethodNotFoundException, CannotCompileException {
        for (Method originalMethod : originalClass.getMethods()) {
            if (this.methodMatcher.matches(originalMethod, subResource)) {
                final Path path = originalMethod.getAnnotation(Path.class);
                if (path == null) {
                    final CtMethod method = clazz.getDeclaredMethod(originalMethod.getName());
                    annotateMethod(constPool, method, originalMethod, Path.class.getName());
                }
                process(subResource, originalMethod.getReturnType(), classLoader);
                return;
            }
        }
        throw new CorrespondingMethodNotFoundException(MessageFormat.format(
                "Method''s name should start with ''{0}'' and have no parameters",
                subResource.getRelativeUri().replaceAll("[\\{\\}/]", "")));
    }

    private void processAction(CtClass clazz, ConstPool constPool, Class originalClass, Set missingUriParameters,
            Action action) throws NotFoundException, CorrespondingMethodNotFoundException {
        for (Method originalMethod : originalClass.getMethods()) {
            if (this.methodMatcher.matches(originalMethod, missingUriParameters, action)) {
                final CtMethod method = clazz.getDeclaredMethod(originalMethod.getName());
                annotateMethod(constPool, method, originalMethod, "javax.ws.rs." + action.getType().name());
                annotateMethodParameters(constPool, method, originalMethod, action, missingUriParameters);
                return;
            }
        }
        throw new CorrespondingMethodNotFoundException(MessageFormat.format(
                "Method''s name should start with ''{0}'' and have following parameters:\n{1} {2}", action.getType().toString()
                        .toLowerCase(), action.getBaseUriParameters(), action.getQueryParameters()));
    }

    private void annotateSubResourceMethod(CtClass clazz, Method originalMethod, Resource subResource) throws NotFoundException {

    }

    private String determineResourceKey(Class originalClass) {
        final Path path = originalClass.getAnnotation(Path.class);
        if (path != null) {
            return path.value().toLowerCase();
        } else {
            return "/" + originalClass.getSimpleName().toLowerCase().replace("resource", "");
        }
    }

    private void annotateMethod(ConstPool constPool, CtMethod method, Method originalMethod, String annotationName)
            throws NotFoundException {
        Annotation annotation = new Annotation(constPool, POOL.getCtClass(annotationName));
        AnnotationsAttribute attribute = (AnnotationsAttribute) method.getMethodInfo().getAttribute(
                AnnotationsAttribute.visibleTag);
        if (attribute == null) {
            attribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
            method.getMethodInfo().addAttribute(attribute);
        }
        attribute.addAnnotation(annotation);
    }

    private void annotateMethodParameters(ConstPool constPool, CtMethod method, Method originalMethod, Action action,
            Set missingUriParameters) throws NotFoundException {
        if (action != null) {
            ParameterAnnotationsAttribute attribute = ((ParameterAnnotationsAttribute) method.getMethodInfo().getAttribute(
                    ParameterAnnotationsAttribute.visibleTag));
            if (attribute == null) {
                attribute = new ParameterAnnotationsAttribute(constPool, ParameterAnnotationsAttribute.visibleTag);
                method.getMethodInfo().addAttribute(attribute);
            }
            Annotation[][] allAnnotations = attribute.getAnnotations();
            if (allAnnotations.length == 0) {
                allAnnotations = new Annotation[originalMethod.getParameters().length][];
            }
            annotateMethodParameters(constPool, attribute, allAnnotations, originalMethod, action.getQueryParameters().keySet(),
                    QueryParam.class);
            annotateMethodParameters(constPool, attribute, allAnnotations, originalMethod, missingUriParameters, PathParam.class);
            if (action.getBody() != null) {
                annotateMethodParameters(constPool, attribute, allAnnotations, originalMethod, action.getBody().keySet(),
                        FormParam.class);
            }
            annotateMethodParameters(constPool, attribute, allAnnotations, originalMethod, action.getHeaders().keySet(),
                    HeaderParam.class);
            attribute.setAnnotations(allAnnotations);
        }
    }

    private void annotateMethodParameters(final ConstPool constPool, final ParameterAnnotationsAttribute attribute,
            final Annotation[][] allAnnotations, final Method originalMethod, final Set requiredParameters,
            final Class annotationType) throws NotFoundException {

        for (int parameterPosition = 0; parameterPosition < originalMethod.getParameters().length; parameterPosition++) {
            Parameter parameter = originalMethod.getParameters()[parameterPosition];
            final String restParameter = findCorrespondingRestParameter(parameter, requiredParameters);
            if (!parameterIsAnnotated(parameter, annotationType) && restParameter != null) {
                Annotation annotation = new Annotation(constPool, POOL.getCtClass(annotationType.getName()));
                annotation.addMemberValue("value", new StringMemberValue(restParameter, attribute.getConstPool()));
                final Annotation[] annotations = allAnnotations[parameterPosition];
                final List annotationList;
                if (annotations == null) {
                    annotationList = new ArrayList<>();
                } else {
                    annotationList = Arrays.asList(annotations);
                }
                annotationList.add(annotation);
                allAnnotations[parameterPosition] = annotationList.toArray(new Annotation[annotationList.size()]);
            }

        }
        // TODO DefaultValue
    }

    private String findCorrespondingRestParameter(Parameter parameter, Set requiredParameters) {
        final String parameterName = parameter.getType().getSimpleName();
        for (String requiredParameter : requiredParameters) {
            if (parameterName.equalsIgnoreCase(requiredParameter)) {
                return requiredParameter;
            }
        }
        return null;
    }

    private boolean parameterIsAnnotated(Parameter parameter, Class requiredType) {
        for (java.lang.annotation.Annotation annotation : parameter.getAnnotations()) {
            if (annotation.annotationType().equals(requiredType)) {
                return true;
            }
        }
        return false;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy