/* * JBoss, Home of Professional Open Source. * Copyright 2013, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.capedwarf.bytecode.endpoints; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import javax.inject.Named; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import com.google.api.server.spi.config.Api; import com.google.api.server.spi.config.ApiMethod; import javassist.CtClass; import javassist.CtMethod; import javassist.NotFoundException; import javassist.bytecode.ParameterAnnotationsAttribute; import javassist.bytecode.annotation.Annotation; import javassist.bytecode.annotation.ArrayMemberValue; import javassist.bytecode.annotation.MemberValue; import javassist.bytecode.annotation.StringMemberValue; import org.jboss.capedwarf.bytecode.Annotator; /** * @author <a href="mailto:mluksa@redhat.com">Marko Luksa</a> */ public class ApiAnnotator extends Annotator { public static final String DEFAULT_NAME = "myapi"; public static final String DEFAULT_VERSION = "v1"; private static Map<String, Class<? extends java.lang.annotation.Annotation>> HTTP_METHODS = new HashMap<>(); static { HTTP_METHODS.put(ApiMethod.HttpMethod.GET, GET.class); HTTP_METHODS.put(ApiMethod.HttpMethod.POST, POST.class); HTTP_METHODS.put(ApiMethod.HttpMethod.PUT, PUT.class); HTTP_METHODS.put(ApiMethod.HttpMethod.DELETE, DELETE.class); } public ApiAnnotator(CtClass clazz) { super(clazz); } @Override public void addAnnotations() throws Exception { Api api = findApi(getClazz()); if (api == null) { return; } addAnnotationsToClass( createPathAnnotation("" // TODO: api.root() + "/" + (api.name().isEmpty() ? DEFAULT_NAME : api.name()) + "/" + (api.version().isEmpty() ? DEFAULT_VERSION : api.version()))); for (CtMethod method : getClazz().getDeclaredMethods()) { ApiMethod apiMethod = findApiMethod(method); if (apiMethod != null) { convertApiMethodAnnotation(method, apiMethod); } } } protected Api findApi(CtClass ctClass) throws Exception { if (ctClass == null) { return null; } Api api = (Api) ctClass.getAnnotation(Api.class); return (api == null) ? findApi(ctClass.getSuperclass()) : api; } protected ApiMethod findApiMethod(CtMethod ctMethod) throws Exception { if (ctMethod == null) { return null; } ApiMethod method = (ApiMethod) ctMethod.getAnnotation(ApiMethod.class); if (method != null) { return method; } CtClass declaringClass = ctMethod.getDeclaringClass(); CtClass superClass = declaringClass.getSuperclass(); if (superClass == null) { return null; } return findApiMethod(findCtMethod(superClass, ctMethod)); } protected CtMethod findCtMethod(CtClass clazz, CtMethod method) throws Exception { if (clazz == null) { return null; } try { return clazz.getDeclaredMethod(method.getName(), method.getParameterTypes()); } catch (NotFoundException ignored) { } return findCtMethod(clazz.getSuperclass(), method); } private Annotation createPathAnnotation(String urlPath) { return createAnnotation(Path.class, memberValueOf(urlPath)); } private void convertApiMethodAnnotation(CtMethod method, ApiMethod apiMethod) { Class<? extends java.lang.annotation.Annotation> httpMethod = HTTP_METHODS.get(apiMethod.httpMethod()); if (httpMethod == null) { return; } String path = apiMethod.path(); if (path == null || path.isEmpty()) { path = method.getName(); } addAnnotationsToMethod(method, Arrays.asList( createAnnotation(httpMethod), createProducesAnnotation(MediaType.APPLICATION_JSON), createPathAnnotation(path) )); convertAnnotationsOnParameters(method, path); } private void convertAnnotationsOnParameters(CtMethod method, String path) { ParameterAnnotationsAttribute attributeInfo = (ParameterAnnotationsAttribute) method.getMethodInfo().getAttribute(ParameterAnnotationsAttribute.visibleTag); if (attributeInfo == null) { return; } Annotation[][] paramArrays = attributeInfo.getAnnotations(); for (int i = 0; i < paramArrays.length; i++) { Annotation[] paramAnnotations = paramArrays[i]; for (Annotation paramAnnotation : paramAnnotations) { if (paramAnnotation.getTypeName().equals(Named.class.getName())) { MemberValue value = paramAnnotation.getMemberValue("value"); String paramName = ((StringMemberValue) value).getValue(); Annotation param = createAnnotation(isPathParam(path, paramName) ? PathParam.class : QueryParam.class, value); paramAnnotations = addToArray(paramAnnotations, param); paramArrays[i] = paramAnnotations; } } } attributeInfo.setAnnotations(paramArrays); } private boolean isPathParam(String path, String paramName) { return path.contains("{" + paramName + "}"); } private Annotation createProducesAnnotation(String value) { StringMemberValue element = memberValueOf(value); ArrayMemberValue array = createSingleElementArrayMemberValue(String.class, element); return createAnnotation(Produces.class, array); } private Annotation[] addToArray(Annotation[] paramAnnotations, Annotation queryParam) { Annotation newParamAnnotations[] = new Annotation[paramAnnotations.length + 1]; System.arraycopy(paramAnnotations, 0, newParamAnnotations, 0, paramAnnotations.length); newParamAnnotations[newParamAnnotations.length - 1] = queryParam; return newParamAnnotations; } }