@SuppressWarnings("unchecked") @Test public void testGetMethodAnnotations() throws Exception { Method method = ExampleService.class.getMethod("testJsonBody", DummyAccountInfo.class); testForAnns(method, Arrays.<Class<? extends Annotation>>asList(POST.class, Consumes.class)); testForAnns(method, Arrays.<Class<? extends Annotation>>asList(Path.class)); testForAnns(method, Arrays.<Class<? extends Annotation>>asList()); List<Class<? extends Annotation>> classes = Arrays.asList(POST.class, GET.class, DELETE.class, PUT.class, HEAD.class); Map<Class<? extends Annotation>, Annotation> map = AnnotationUtils.getMethodAnnotationMap(method, classes); assertThat( map.keySet(), equalTo(sorted(Arrays.<Class<? extends Annotation>>asList(POST.class)))); assertThat(map.get(POST.class), IsInstanceOf.instanceOf(POST.class)); }
private void writeMethodImpl(JMethod method) throws UnableToCompleteException { boolean returnRequest = false; if (method.getReturnType() != JPrimitiveType.VOID) { if (!method.getReturnType().getQualifiedSourceName().equals(Request.class.getName()) && !method .getReturnType() .getQualifiedSourceName() .equals(JsonpRequest.class.getName())) { getLogger() .log( ERROR, "Invalid rest method. Method must have void, Request or JsonpRequest return types: " + method.getReadableDeclaration()); throw new UnableToCompleteException(); } else { returnRequest = true; } } Json jsonAnnotation = source.getAnnotation(Json.class); final Style classStyle = jsonAnnotation != null ? jsonAnnotation.style() : Style.DEFAULT; Options classOptions = source.getAnnotation(Options.class); Options options = method.getAnnotation(Options.class); p(method.getReadableDeclaration(false, false, false, false, true) + " {").i(1); { String restMethod = getRestMethod(method); LinkedList<JParameter> args = new LinkedList<JParameter>(Arrays.asList(method.getParameters())); // the last arg should be the callback. if (args.isEmpty()) { getLogger() .log( ERROR, "Invalid rest method. Method must declare at least a callback argument: " + method.getReadableDeclaration()); throw new UnableToCompleteException(); } JParameter callbackArg = args.removeLast(); JClassType callbackType = callbackArg.getType().isClassOrInterface(); JClassType methodCallbackType = METHOD_CALLBACK_TYPE; if (callbackType == null || !callbackType.isAssignableTo(methodCallbackType)) { getLogger() .log( ERROR, "Invalid rest method. Last argument must be a " + methodCallbackType.getName() + " type: " + method.getReadableDeclaration()); throw new UnableToCompleteException(); } JClassType resultType = getCallbackTypeGenericClass(callbackType); String pathExpression = null; Path pathAnnotation = method.getAnnotation(Path.class); if (pathAnnotation != null) { pathExpression = wrap(pathAnnotation.value()); } JParameter contentArg = null; HashMap<String, JParameter> queryParams = new HashMap<String, JParameter>(); HashMap<String, JParameter> formParams = new HashMap<String, JParameter>(); HashMap<String, JParameter> headerParams = new HashMap<String, JParameter>(); for (JParameter arg : args) { PathParam paramPath = arg.getAnnotation(PathParam.class); if (paramPath != null) { if (pathExpression == null) { getLogger() .log( ERROR, "Invalid rest method. Invalid @PathParam annotation. Method is missing the @Path annotation: " + method.getReadableDeclaration()); throw new UnableToCompleteException(); } pathExpression = pathExpression(pathExpression, arg, paramPath); // .replaceAll(Pattern.quote("{" + paramPath.value() + "}"), // "\"+com.google.gwt.http.client.URL.encodePathSegment(" + toStringExpression(arg) + // ")+\""); if (arg.getAnnotation(Attribute.class) != null) { // allow part of the arg-object participate in as PathParam and the object goes over the // wire contentArg = arg; } continue; } QueryParam queryParam = arg.getAnnotation(QueryParam.class); if (queryParam != null) { queryParams.put(queryParam.value(), arg); continue; } FormParam formParam = arg.getAnnotation(FormParam.class); if (formParam != null) { formParams.put(formParam.value(), arg); continue; } HeaderParam headerParam = arg.getAnnotation(HeaderParam.class); if (headerParam != null) { headerParams.put(headerParam.value(), arg); continue; } if (!formParams.isEmpty()) { getLogger() .log( ERROR, "You can not have both @FormParam parameters and a content parameter: " + method.getReadableDeclaration()); throw new UnableToCompleteException(); } if (contentArg != null) { getLogger() .log( ERROR, "Invalid rest method. Only one content parameter is supported: " + method.getReadableDeclaration()); throw new UnableToCompleteException(); } contentArg = arg; } String acceptTypeBuiltIn = null; if (callbackType.equals(TEXT_CALLBACK_TYPE)) { acceptTypeBuiltIn = "CONTENT_TYPE_TEXT"; } else if (callbackType.equals(JSON_CALLBACK_TYPE)) { acceptTypeBuiltIn = "CONTENT_TYPE_JSON"; } else if (callbackType.isAssignableTo(OVERLAY_CALLBACK_TYPE)) { acceptTypeBuiltIn = "CONTENT_TYPE_JSON"; } else if (callbackType.equals(XML_CALLBACK_TYPE)) { acceptTypeBuiltIn = "CONTENT_TYPE_XML"; } p("final " + METHOD_CLASS + " __method ="); p("getResource()"); if (pathExpression != null) { p(".resolve(" + pathExpression + ")"); } for (Map.Entry<String, JParameter> entry : queryParams.entrySet()) { String expr = entry.getValue().getName(); JClassType type = entry.getValue().getType().isClassOrInterface(); if (type != null && isQueryParamListType(type)) { p( ".addQueryParams(" + wrap(entry.getKey()) + ", " + toIteratedStringExpression(entry.getValue()) + ")"); } else { p( ".addQueryParam(" + wrap(entry.getKey()) + ", " + toStringExpression(entry.getValue().getType(), expr) + ")"); } } // example: .get() p("." + restMethod + "();"); // Handle JSONP specific configuration... JSONP jsonpAnnotation = method.getAnnotation(JSONP.class); final boolean isJsonp = restMethod.equals(METHOD_JSONP) && jsonpAnnotation != null; if (isJsonp) { if (returnRequest && !method .getReturnType() .getQualifiedSourceName() .equals(JsonpRequest.class.getName())) { getLogger() .log( ERROR, "Invalid rest method. JSONP method must have void or JsonpRequest return types: " + method.getReadableDeclaration()); throw new UnableToCompleteException(); } if (jsonpAnnotation.callbackParam().length() > 0) { p( "((" + JSONP_METHOD_CLASS + ")__method).callbackParam(" + wrap(jsonpAnnotation.callbackParam()) + ");"); } if (jsonpAnnotation.failureCallbackParam().length() > 0) { p( "((" + JSONP_METHOD_CLASS + ")__method).failureCallbackParam(" + wrap(jsonpAnnotation.failureCallbackParam()) + ");"); } } else { if (returnRequest && !method.getReturnType().getQualifiedSourceName().equals(Request.class.getName())) { getLogger() .log( ERROR, "Invalid rest method. Non JSONP method must have void or Request return types: " + method.getReadableDeclaration()); throw new UnableToCompleteException(); } } // configure the dispatcher if (options != null && options.dispatcher() != Dispatcher.class) { // use the dispatcher configured for the method. p("__method.setDispatcher(" + options.dispatcher().getName() + ".INSTANCE);"); } else { // use the default dispatcher configured for the service.. p("__method.setDispatcher(this.dispatcher);"); } // configure the expected statuses.. if (options != null && options.expect().length != 0) { // Using method level defined expected status p("__method.expect(" + join(options.expect(), ", ") + ");"); } else if (classOptions != null && classOptions.expect().length != 0) { // Using class level defined expected status p("__method.expect(" + join(classOptions.expect(), ", ") + ");"); } // configure the timeout if (options != null && options.timeout() >= 0) { // Using method level defined value p("__method.timeout(" + options.timeout() + ");"); } else if (classOptions != null && classOptions.timeout() >= 0) { // Using class level defined value p("__method.timeout(" + classOptions.timeout() + ");"); } if (jsonpAnnotation == null) { Produces producesAnnotation = findAnnotationOnMethodOrEnclosingType(method, Produces.class); if (producesAnnotation != null) { p( "__method.header(" + RESOURCE_CLASS + ".HEADER_ACCEPT, " + wrap(producesAnnotation.value()[0]) + ");"); } else { // set the default accept header.... if (acceptTypeBuiltIn != null) { p( "__method.header(" + RESOURCE_CLASS + ".HEADER_ACCEPT, " + RESOURCE_CLASS + "." + acceptTypeBuiltIn + ");"); } else { p( "__method.header(" + RESOURCE_CLASS + ".HEADER_ACCEPT, " + RESOURCE_CLASS + ".CONTENT_TYPE_JSON);"); } } Consumes consumesAnnotation = findAnnotationOnMethodOrEnclosingType(method, Consumes.class); if (consumesAnnotation != null) { p( "__method.header(" + RESOURCE_CLASS + ".HEADER_CONTENT_TYPE, " + wrap(consumesAnnotation.value()[0]) + ");"); } // and set the explicit headers now (could override the accept header) for (Map.Entry<String, JParameter> entry : headerParams.entrySet()) { String expr = entry.getValue().getName(); p( "__method.header(" + wrap(entry.getKey()) + ", " + toStringExpression(entry.getValue().getType(), expr) + ");"); } } if (!formParams.isEmpty()) { p(FORM_POST_CONTENT_CLASS + " __formPostContent = new " + FORM_POST_CONTENT_CLASS + "();"); for (Map.Entry<String, JParameter> entry : formParams.entrySet()) { p( "__formPostContent.addParameter(" + wrap(entry.getKey()) + ", " + toFormStringExpression(entry.getValue(), classStyle) + ");"); } p("__method.form(__formPostContent.getTextContent());"); } if (contentArg != null) { if (contentArg.getType() == STRING_TYPE) { p("__method.text(" + contentArg.getName() + ");"); } else if (contentArg.getType() == JSON_VALUE_TYPE) { p("__method.json(" + contentArg.getName() + ");"); } else if (contentArg.getType().isClass() != null && isOverlayArrayType(contentArg.getType().isClass())) { p("__method.json(new " + JSON_ARRAY_CLASS + "(" + contentArg.getName() + "));"); } else if (contentArg.getType().isClass() != null && contentArg.getType().isClass().isAssignableTo(OVERLAY_VALUE_TYPE)) { p("__method.json(new " + JSON_OBJECT_CLASS + "(" + contentArg.getName() + "));"); } else if (contentArg.getType() == DOCUMENT_TYPE) { p("__method.xml(" + contentArg.getName() + ");"); } else { JClassType contentClass = contentArg.getType().isClass(); if (contentClass == null) { contentClass = contentArg.getType().isClassOrInterface(); if (!locator.isCollectionType(contentClass)) { getLogger().log(ERROR, "Content argument must be a class."); throw new UnableToCompleteException(); } } jsonAnnotation = contentArg.getAnnotation(Json.class); Style style = jsonAnnotation != null ? jsonAnnotation.style() : classStyle; // example: // .json(Listings$_Generated_JsonEncoder_$.INSTANCE.encode(arg0) // ) p( "__method.json(" + locator.encodeExpression(contentClass, contentArg.getName(), style) + ");"); } } List<AnnotationResolver> annotationResolvers = getAnnotationResolvers(context, getLogger()); getLogger() .log( TreeLogger.DEBUG, "found " + annotationResolvers.size() + " additional AnnotationResolvers"); for (AnnotationResolver a : annotationResolvers) { getLogger() .log( TreeLogger.DEBUG, "(" + a.getClass().getName() + ") resolve `" + source.getName() + "#" + method.getName() + "´ ..."); final Map<String, String[]> addDataParams = a.resolveAnnotation(getLogger(), source, method, restMethod); if (addDataParams != null) { for (String s : addDataParams.keySet()) { final StringBuilder sb = new StringBuilder(); final List<String> classList = Arrays.asList(addDataParams.get(s)); sb.append("["); for (int i = 0; i < classList.size(); ++i) { sb.append("\\\"").append(classList.get(i)).append("\\\""); if ((i + 1) < classList.size()) { sb.append(","); } } sb.append("]"); getLogger() .log(TreeLogger.DEBUG, "add call with (\"" + s + "\", \"" + sb.toString() + "\")"); p("__method.addData(\"" + s + "\", \"" + sb.toString() + "\");"); } } } if (acceptTypeBuiltIn != null) { // TODO: shouldn't we also have a cach in here? p(returnRequest(returnRequest, isJsonp) + "__method.send(" + callbackArg.getName() + ");"); } else if (isJsonp) { p(returnRequest(returnRequest, isJsonp) + "((" + JSONP_METHOD_CLASS + ")__method).send(new " + ABSTRACT_ASYNC_CALLBACK_CLASS + "<" + resultType.getParameterizedQualifiedSourceName() + ">((" + JSONP_METHOD_CLASS + ")__method, " + callbackArg.getName() + ") {") .i(1); { p("protected " + resultType.getParameterizedQualifiedSourceName() + " parseResult(" + JSON_VALUE_CLASS + " result) throws Exception {") .i(1); { if (resultType.getParameterizedQualifiedSourceName().equals("java.lang.Void")) { p("return (java.lang.Void) null;"); } else { p("try {").i(1); { if (resultType.isAssignableTo(locator.LIST_TYPE)) { p("result = new " + JSON_ARRAY_CLASS + "(result.getJavaScriptObject());"); } jsonAnnotation = method.getAnnotation(Json.class); Style style = jsonAnnotation != null ? jsonAnnotation.style() : classStyle; p("return " + locator.decodeExpression(resultType, "result", style) + ";"); } i(-1).p("} catch (Throwable __e) {").i(1); { p( "throw new " + RESPONSE_FORMAT_EXCEPTION_CLASS + "(\"Response was NOT a valid JSON document\", __e);"); } i(-1).p("}"); } } i(-1).p("}"); } i(-1).p("});"); } else { p("try {").i(1); { p(returnRequest(returnRequest, isJsonp) + "__method.send(new " + ABSTRACT_REQUEST_CALLBACK_CLASS + "<" + resultType.getParameterizedQualifiedSourceName() + ">(__method, " + callbackArg.getName() + ") {") .i(1); { p("protected " + resultType.getParameterizedQualifiedSourceName() + " parseResult() throws Exception {") .i(1); { if (resultType.getParameterizedQualifiedSourceName().equals("java.lang.Void")) { p("return (java.lang.Void) null;"); } else { p("try {").i(1); { jsonAnnotation = method.getAnnotation(Json.class); Style style = jsonAnnotation != null ? jsonAnnotation.style() : classStyle; p( "return " + locator.decodeExpression( resultType, JSON_PARSER_CLASS + ".parse(__method.getResponse().getText())", style) + ";"); } i(-1).p("} catch (Throwable __e) {").i(1); { p( "throw new " + RESPONSE_FORMAT_EXCEPTION_CLASS + "(\"Response was NOT a valid JSON document\", __e);"); } i(-1).p("}"); } } i(-1).p("}"); } i(-1).p("});"); } i(-1).p("} catch (" + REQUEST_EXCEPTION_CLASS + " __e) {").i(1); { p(callbackArg.getName() + ".onFailure(__method,__e);"); if (returnRequest) { p("return null;"); } } i(-1).p("}"); } } i(-1).p("}"); }
public ResourceMethod(MethodDeclaration delegate, Resource parent) { super(delegate); Set<String> httpMethods = new TreeSet<String>(); Collection<AnnotationMirror> mirrors = delegate.getAnnotationMirrors(); for (AnnotationMirror mirror : mirrors) { AnnotationTypeDeclaration annotationDeclaration = mirror.getAnnotationType().getDeclaration(); HttpMethod httpMethodInfo = annotationDeclaration.getAnnotation(HttpMethod.class); if (httpMethodInfo != null) { // request method designator found. httpMethods.add(httpMethodInfo.value()); } } if (httpMethods.isEmpty()) { throw new IllegalStateException( "A resource method must specify an HTTP method by using a request method designator annotation."); } this.httpMethods = httpMethods; Set<String> consumes; Consumes consumesInfo = delegate.getAnnotation(Consumes.class); if (consumesInfo != null) { consumes = new TreeSet<String>(Arrays.asList(JAXRSUtils.value(consumesInfo))); } else { consumes = new TreeSet<String>(parent.getConsumesMime()); } this.consumesMime = consumes; Set<String> produces; Produces producesInfo = delegate.getAnnotation(Produces.class); if (producesInfo != null) { produces = new TreeSet<String>(Arrays.asList(JAXRSUtils.value(producesInfo))); } else { produces = new TreeSet<String>(parent.getProducesMime()); } this.producesMime = produces; String subpath = null; Path pathInfo = delegate.getAnnotation(Path.class); if (pathInfo != null) { subpath = pathInfo.value(); } ResourceEntityParameter entityParameter; List<ResourceEntityParameter> declaredEntityParameters = new ArrayList<ResourceEntityParameter>(); List<ResourceParameter> resourceParameters; ResourceRepresentationMetadata outputPayload; ResourceMethodSignature signatureOverride = delegate.getAnnotation(ResourceMethodSignature.class); if (signatureOverride == null) { entityParameter = null; resourceParameters = new ArrayList<ResourceParameter>(); // if we're not overriding the signature, assume we use the real method signature. for (ParameterDeclaration parameterDeclaration : getParameters()) { if (ResourceParameter.isResourceParameter(parameterDeclaration)) { resourceParameters.add(new ResourceParameter(parameterDeclaration)); } else if (ResourceParameter.isFormBeanParameter(parameterDeclaration)) { resourceParameters.addAll(ResourceParameter.getFormBeanParameters(parameterDeclaration)); } else if (parameterDeclaration.getAnnotation(Context.class) == null) { entityParameter = new ResourceEntityParameter(this, parameterDeclaration); declaredEntityParameters.add(entityParameter); } } DecoratedTypeMirror returnTypeMirror; TypeHint hintInfo = getAnnotation(TypeHint.class); if (hintInfo != null) { try { Class hint = hintInfo.value(); AnnotationProcessorEnvironment env = net.sf.jelly.apt.Context.getCurrentEnvironment(); if (TypeHint.NO_CONTENT.class.equals(hint)) { returnTypeMirror = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(env.getTypeUtils().getVoidType()); } else { String hintName = hint.getName(); if (TypeHint.NONE.class.equals(hint)) { hintName = hintInfo.qualifiedName(); } if (!"##NONE".equals(hintName)) { TypeDeclaration type = env.getTypeDeclaration(hintName); returnTypeMirror = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(env.getTypeUtils().getDeclaredType(type)); } else { returnTypeMirror = (DecoratedTypeMirror) getReturnType(); } } } catch (MirroredTypeException e) { returnTypeMirror = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(e.getTypeMirror()); } returnTypeMirror.setDocComment(((DecoratedTypeMirror) getReturnType()).getDocComment()); } else { returnTypeMirror = (DecoratedTypeMirror) getReturnType(); if (getJavaDoc().get("returnWrapped") != null) { // support jax-doclets. see http://jira.codehaus.org/browse/ENUNCIATE-690 String fqn = getJavaDoc().get("returnWrapped").get(0); AnnotationProcessorEnvironment env = net.sf.jelly.apt.Context.getCurrentEnvironment(); TypeDeclaration type = env.getTypeDeclaration(fqn); if (type != null) { returnTypeMirror = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(env.getTypeUtils().getDeclaredType(type)); } } // in the case where the return type is com.sun.jersey.api.JResponse, // we can use the type argument to get the entity type if (returnTypeMirror.isClass() && returnTypeMirror.isInstanceOf("com.sun.jersey.api.JResponse")) { DecoratedClassType jresponse = (DecoratedClassType) returnTypeMirror; if (!jresponse.getActualTypeArguments().isEmpty()) { DecoratedTypeMirror responseType = (DecoratedTypeMirror) TypeMirrorDecorator.decorate( jresponse.getActualTypeArguments().iterator().next()); if (responseType.isDeclared()) { responseType.setDocComment(returnTypeMirror.getDocComment()); returnTypeMirror = responseType; } } } } outputPayload = returnTypeMirror.isVoid() ? null : new ResourceRepresentationMetadata(returnTypeMirror); } else { entityParameter = loadEntityParameter(signatureOverride); declaredEntityParameters.add(entityParameter); resourceParameters = loadResourceParameters(signatureOverride); outputPayload = loadOutputPayload(signatureOverride); } JavaDoc.JavaDocTagList doclets = getJavaDoc() .get( "RequestHeader"); // support jax-doclets. see // http://jira.codehaus.org/browse/ENUNCIATE-690 if (doclets != null) { for (String doclet : doclets) { int firstspace = doclet.indexOf(' '); String header = firstspace > 0 ? doclet.substring(0, firstspace) : doclet; String doc = ((firstspace > 0) && (firstspace + 1 < doclet.length())) ? doclet.substring(firstspace + 1) : ""; resourceParameters.add( new ExplicitResourceParameter(this, doc, header, ResourceParameterType.HEADER)); } } ArrayList<ResponseCode> statusCodes = new ArrayList<ResponseCode>(); ArrayList<ResponseCode> warnings = new ArrayList<ResponseCode>(); StatusCodes codes = getAnnotation(StatusCodes.class); if (codes != null) { for (org.codehaus.enunciate.jaxrs.ResponseCode code : codes.value()) { ResponseCode rc = new ResponseCode(); rc.setCode(code.code()); rc.setCondition(code.condition()); statusCodes.add(rc); } } doclets = getJavaDoc() .get("HTTP"); // support jax-doclets. see http://jira.codehaus.org/browse/ENUNCIATE-690 if (doclets != null) { for (String doclet : doclets) { int firstspace = doclet.indexOf(' '); String code = firstspace > 0 ? doclet.substring(0, firstspace) : doclet; String doc = ((firstspace > 0) && (firstspace + 1 < doclet.length())) ? doclet.substring(firstspace + 1) : ""; try { ResponseCode rc = new ResponseCode(); rc.setCode(Integer.parseInt(code)); rc.setCondition(doc); statusCodes.add(rc); } catch (NumberFormatException e) { // fall through... } } } Warnings warningInfo = getAnnotation(Warnings.class); if (warningInfo != null) { for (org.codehaus.enunciate.jaxrs.ResponseCode code : warningInfo.value()) { ResponseCode rc = new ResponseCode(); rc.setCode(code.code()); rc.setCondition(code.condition()); warnings.add(rc); } } codes = parent.getAnnotation(StatusCodes.class); if (codes != null) { for (org.codehaus.enunciate.jaxrs.ResponseCode code : codes.value()) { ResponseCode rc = new ResponseCode(); rc.setCode(code.code()); rc.setCondition(code.condition()); statusCodes.add(rc); } } warningInfo = parent.getAnnotation(Warnings.class); if (warningInfo != null) { for (org.codehaus.enunciate.jaxrs.ResponseCode code : warningInfo.value()) { ResponseCode rc = new ResponseCode(); rc.setCode(code.code()); rc.setCondition(code.condition()); warnings.add(rc); } } ResponseHeaders responseHeaders = parent.getAnnotation(ResponseHeaders.class); if (responseHeaders != null) { for (ResponseHeader header : responseHeaders.value()) { this.responseHeaders.put(header.name(), header.description()); } } responseHeaders = getAnnotation(ResponseHeaders.class); if (responseHeaders != null) { for (ResponseHeader header : responseHeaders.value()) { this.responseHeaders.put(header.name(), header.description()); } } doclets = getJavaDoc() .get( "ResponseHeader"); // support jax-doclets. see // http://jira.codehaus.org/browse/ENUNCIATE-690 if (doclets != null) { for (String doclet : doclets) { int firstspace = doclet.indexOf(' '); String header = firstspace > 0 ? doclet.substring(0, firstspace) : doclet; String doc = ((firstspace > 0) && (firstspace + 1 < doclet.length())) ? doclet.substring(firstspace + 1) : ""; this.responseHeaders.put(header, doc); } } this.entityParameter = entityParameter; this.resourceParameters = resourceParameters; this.subpath = subpath; this.parent = parent; this.statusCodes = statusCodes; this.warnings = warnings; this.representationMetadata = outputPayload; this.declaredEntityParameters = declaredEntityParameters; }