From d23a232ae9c16def3ad0205eb45717e92a02552d Mon Sep 17 00:00:00 2001 From: dhleong Date: Sat, 4 May 2013 15:38:50 -0400 Subject: [PATCH 01/15] Add new annotations --- .../rest/RequiresAuthentication.java | 27 +++++++++++++++++++ .../annotations/rest/RequiresCookie.java | 12 +++++++++ .../annotations/rest/RequiresCookieInUrl.java | 12 +++++++++ .../annotations/rest/RequiresHeader.java | 27 +++++++++++++++++++ .../annotations/rest/SetsCookie.java | 13 +++++++++ 5 files changed, 91 insertions(+) create mode 100644 AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresAuthentication.java create mode 100644 AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresCookie.java create mode 100644 AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresCookieInUrl.java create mode 100644 AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresHeader.java create mode 100644 AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/SetsCookie.java diff --git a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresAuthentication.java b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresAuthentication.java new file mode 100644 index 0000000000..127be1d929 --- /dev/null +++ b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresAuthentication.java @@ -0,0 +1,27 @@ +/** + * Copyright (C) 2010-2012 eBusiness Information, Excilys Group + * + * 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.androidannotations.annotations.rest; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target({ ElementType.METHOD, ElementType.TYPE }) +public @interface RequiresAuthentication { + +} diff --git a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresCookie.java b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresCookie.java new file mode 100644 index 0000000000..1b30f8c690 --- /dev/null +++ b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresCookie.java @@ -0,0 +1,12 @@ +package org.androidannotations.annotations.rest; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target({ ElementType.METHOD, ElementType.TYPE }) +public @interface RequiresCookie { + public String[] value(); +} diff --git a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresCookieInUrl.java b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresCookieInUrl.java new file mode 100644 index 0000000000..7c784ad74f --- /dev/null +++ b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresCookieInUrl.java @@ -0,0 +1,12 @@ +package org.androidannotations.annotations.rest; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target({ ElementType.METHOD, ElementType.TYPE }) +public @interface RequiresCookieInUrl { + public String[] value(); +} diff --git a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresHeader.java b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresHeader.java new file mode 100644 index 0000000000..5b45edc9b4 --- /dev/null +++ b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresHeader.java @@ -0,0 +1,27 @@ +/** + * Copyright (C) 2010-2012 eBusiness Information, Excilys Group + * + * 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.androidannotations.annotations.rest; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target({ ElementType.METHOD, ElementType.TYPE }) +public @interface RequiresHeader { + String[] value(); +} diff --git a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/SetsCookie.java b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/SetsCookie.java new file mode 100644 index 0000000000..a400bd41eb --- /dev/null +++ b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/SetsCookie.java @@ -0,0 +1,13 @@ +package org.androidannotations.annotations.rest; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target({ ElementType.METHOD, ElementType.TYPE }) +public @interface SetsCookie { + + public String[] value(); +} From 8122abf0e344c89db9359dfcd01d9d335cf5c3f8 Mon Sep 17 00:00:00 2001 From: dhleong Date: Sat, 4 May 2013 17:45:46 -0400 Subject: [PATCH 02/15] Init some internal structures, add an annotation to tests --- .../processing/rest/RestImplementationHolder.java | 6 ++++++ .../processing/rest/RestProcessor.java | 12 ++++++++++++ .../androidannotations/test15/rest/MyService.java | 2 ++ 3 files changed, 20 insertions(+) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestImplementationHolder.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestImplementationHolder.java index 7ced7511a8..58995390e8 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestImplementationHolder.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestImplementationHolder.java @@ -26,4 +26,10 @@ public class RestImplementationHolder { public JFieldVar rootUrlField; + public JFieldVar availableHeadersField; + + public JFieldVar availableCookiesField; + + public JFieldVar authenticationField; + } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestProcessor.java index 6f1d73b252..db211d8d54 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestProcessor.java @@ -88,6 +88,15 @@ public void process(Element element, JCodeModel codeModel, EBeansHolder eBeansHo JClass stringClass = eBeansHolder.refClass(STRING); holder.rootUrlField = holder.restImplementationClass.field(JMod.PRIVATE, stringClass, "rootUrl"); + // available headers/cookies + JClass mapClass = eBeansHolder.refClass("java.util.HashMap").narrow(stringClass, stringClass); + holder.availableHeadersField = holder.restImplementationClass.field(JMod.PRIVATE, mapClass, "availableHeaders"); + holder.availableCookiesField = holder.restImplementationClass.field(JMod.PRIVATE, mapClass, "availableCookies"); + + // any auth + JClass httpAuthClass = eBeansHolder.refClass("org.springframework.http.HttpAuthentication"); + holder.authenticationField = holder.restImplementationClass.field(JMod.PRIVATE, httpAuthClass, "authentication"); + { // Constructor JMethod constructor = holder.restImplementationClass.constructor(JMod.PUBLIC); @@ -118,6 +127,9 @@ public void process(Element element, JCodeModel codeModel, EBeansHolder eBeansHo } } constructorBody.assign(holder.rootUrlField, lit(typeElement.getAnnotation(Rest.class).rootUrl())); + + constructorBody.assign(holder.availableHeadersField, _new(mapClass)); + constructorBody.assign(holder.availableCookiesField, _new(mapClass)); } // Implement getRestTemplate method diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java index 7f921b060b..eac537361b 100644 --- a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java @@ -33,6 +33,7 @@ import org.androidannotations.annotations.rest.Options; import org.androidannotations.annotations.rest.Post; import org.androidannotations.annotations.rest.Put; +import org.androidannotations.annotations.rest.RequiresCookie; import org.androidannotations.annotations.rest.Rest; import org.androidannotations.api.rest.MediaType; @@ -43,6 +44,7 @@ public interface MyService { // *** GET *** // url variables are mapped to method parameter names. + @RequiresCookie("xt") @Get("/events/{year}/{location}") @Accept(MediaType.APPLICATION_JSON) EventList getEvents(String location, int year); From 37ea7321d41aabcd57efda5fcd72f3a7d0f0034b Mon Sep 17 00:00:00 2001 From: dhleong Date: Sat, 4 May 2013 17:51:01 -0400 Subject: [PATCH 03/15] Allow full urls in @Post, etc. to be specified and handled correctly --- .../processing/rest/MethodProcessor.java | 14 ++++++++++---- .../androidannotations/test15/rest/MyService.java | 6 ++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java index a357fe98be..e9cafce146 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java @@ -84,11 +84,17 @@ protected void generateRestTemplateCallBlock(MethodProcessorHolder methodHolder) // RestTemplate exchange() method call JInvocation restCall = JExpr.invoke(holder.restTemplateField, "exchange"); - // RestTemplate exchange() 1st arg : concat root url + suffix - JInvocation concatCall = JExpr.invoke(holder.rootUrlField, "concat"); + final String urlSuffix = methodHolder.getUrlSuffix(); + if (!(urlSuffix.startsWith("http://") || urlSuffix.startsWith("https://"))) { + // RestTemplate exchange() 1st arg : concat root url + suffix + JInvocation concatCall = JExpr.invoke(holder.rootUrlField, "concat"); - // RestTemplate exchange() 2nd arg : add url param - restCall.arg(concatCall.arg(JExpr.lit(methodHolder.getUrlSuffix()))); + // RestTemplate exchange() 2nd arg : add url param + restCall.arg(concatCall.arg(JExpr.lit(urlSuffix))); + } else { + // full url provided... don't prefix + restCall.arg(JExpr.lit(urlSuffix)); + } // RestTemplate exchange() 3rd arg : add HttpMethod type param JClass httpMethod = eBeanHolder.refClass(CanonicalNameConstants.HTTP_METHOD); diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java index eac537361b..a126fcba4a 100644 --- a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java @@ -33,8 +33,10 @@ import org.androidannotations.annotations.rest.Options; import org.androidannotations.annotations.rest.Post; import org.androidannotations.annotations.rest.Put; +import org.androidannotations.annotations.rest.RequiresAuthentication; import org.androidannotations.annotations.rest.RequiresCookie; import org.androidannotations.annotations.rest.Rest; +import org.androidannotations.annotations.rest.SetsCookie; import org.androidannotations.api.rest.MediaType; // if defined, the rootUrl will be added as a prefix to every request @@ -118,6 +120,10 @@ ResponseEntity getEventsArrayOfArrays2(String location, int year) void getEventsVoid(String location, int year) throws RestClientException; // *** POST *** + @RequiresAuthentication + @Post("http://company.com/oauth/token") + @SetsCookie({"xt", "sjsaid"}) + void authenticate(); // There should be max 1 parameter that is not mapped to an attribute. This // parameter will be used as the post entity. From 63d51ee29f0644a0e48208598e4b72649a880950 Mon Sep 17 00:00:00 2001 From: dhleong Date: Sat, 4 May 2013 18:41:38 -0400 Subject: [PATCH 04/15] Give entity a var so we can pull cookies from it if needed --- .../processing/rest/GetPostProcessor.java | 3 +- .../processing/rest/HeadProcessor.java | 4 +-- .../processing/rest/MethodProcessor.java | 28 +++++++++++++++---- .../processing/rest/OptionsProcessor.java | 4 +-- 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/GetPostProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/GetPostProcessor.java index 1afac17926..7b255d9637 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/GetPostProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/GetPostProcessor.java @@ -42,6 +42,7 @@ import com.sun.codemodel.JCodeModel; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JExpr; +import com.sun.codemodel.JExpression; import com.sun.codemodel.JInvocation; import com.sun.codemodel.JPackage; @@ -267,7 +268,7 @@ protected JInvocation addResponseEntityArg(JInvocation restCall, MethodProcessor } @Override - protected JInvocation addResultCallMethod(JInvocation restCall, MethodProcessorHolder methodHolder) { + protected JExpression addResultCallMethod(JExpression restCall, MethodProcessorHolder methodHolder) { JClass generatedReturnType = methodHolder.getMethodReturnClass(); if (generatedReturnType == null) { return restCall; diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/HeadProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/HeadProcessor.java index 5770b75838..203fb700a0 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/HeadProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/HeadProcessor.java @@ -26,7 +26,7 @@ import com.sun.codemodel.JClass; import com.sun.codemodel.JCodeModel; import com.sun.codemodel.JExpr; -import com.sun.codemodel.JInvocation; +import com.sun.codemodel.JExpression; public class HeadProcessor extends MethodProcessor { @@ -55,7 +55,7 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) t } @Override - protected JInvocation addResultCallMethod(JInvocation restCall, MethodProcessorHolder methodHolder) { + protected JExpression addResultCallMethod(JExpression restCall, MethodProcessorHolder methodHolder) { return JExpr.invoke(restCall, "getHeaders"); } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java index e9cafce146..579b6c3634 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java @@ -27,6 +27,7 @@ import javax.lang.model.element.VariableElement; import org.androidannotations.annotations.rest.Accept; +import org.androidannotations.annotations.rest.SetsCookie; import org.androidannotations.helper.APTCodeModelHelper; import org.androidannotations.helper.CanonicalNameConstants; import org.androidannotations.helper.RestAnnotationHelper; @@ -114,7 +115,12 @@ protected void generateRestTemplateCallBlock(MethodProcessorHolder methodHolder) restCall.arg(hashMapVar); } - insertRestCallInBody(body, restCall, methodHolder, methodReturnVoid); + JClass voidClass = eBeanHolder.refClass(Void.class); + JClass responseEntityClass = eBeanHolder.refClass(CanonicalNameConstants.RESPONSE_ENTITY).narrow(methodReturnVoid ? voidClass : expectedClass); + JVar responseEntity = body.decl(responseEntityClass, "response", restCall); + + JExpression result = JExpr.ref(responseEntity.name()); + insertRestCallInBody(body, result, methodHolder, methodReturnVoid); } /** @@ -138,13 +144,13 @@ protected JInvocation addResponseEntityArg(JInvocation restCall, MethodProcessor * Add an extra method calls on the result of restTemplate.exchange(). By * default, just return the result */ - protected JInvocation addResultCallMethod(JInvocation restCall, MethodProcessorHolder methodHolder) { - return restCall; + protected JExpression addResultCallMethod(JExpression i, MethodProcessorHolder methodHolder) { + return i; } - private void insertRestCallInBody(JBlock body, JInvocation restCall, MethodProcessorHolder methodHolder, boolean methodReturnVoid) { + private void insertRestCallInBody(JBlock body, JExpression restCall, MethodProcessorHolder methodHolder, boolean methodReturnVoid) { if (methodReturnVoid) { - body.add(restCall); + // body.add(restCall); } else { restCall = addResultCallMethod(restCall, methodHolder); body._return(restCall); @@ -254,6 +260,18 @@ private String retrieveAcceptAnnotationValue(ExecutableElement executableElement } } + private String[] retrieveSettingCookieNames(ExecutableElement executableElement) { + SetsCookie cookieAnnotation = executableElement.getAnnotation(SetsCookie.class); + if (cookieAnnotation == null) { + cookieAnnotation = executableElement.getEnclosingElement().getAnnotation(SetsCookie.class); + } + if (cookieAnnotation != null) { + return cookieAnnotation.value(); + } else { + return null; + } + } + private TreeMap extractMethodParamsVar(EBeanHolder eBeanHolder, JMethod method, ExecutableElement executableElement, RestImplementationHolder holder) { List params = executableElement.getParameters(); TreeMap methodParams = new TreeMap(); diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/OptionsProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/OptionsProcessor.java index e2f93a1dfe..3f46b72b07 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/OptionsProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/OptionsProcessor.java @@ -28,7 +28,7 @@ import com.sun.codemodel.JClass; import com.sun.codemodel.JCodeModel; import com.sun.codemodel.JExpr; -import com.sun.codemodel.JInvocation; +import com.sun.codemodel.JExpression; public class OptionsProcessor extends MethodProcessor { @@ -63,7 +63,7 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) t } @Override - protected JInvocation addResultCallMethod(JInvocation restCall, MethodProcessorHolder methodHolder) { + protected JExpression addResultCallMethod(JExpression restCall, MethodProcessorHolder methodHolder) { restCall = JExpr.invoke(restCall, "getHeaders"); restCall = JExpr.invoke(restCall, "getAllow"); return restCall; From dd1397b211199378f28cf1a61a5d4e909aa34d0e Mon Sep 17 00:00:00 2001 From: dhleong Date: Sat, 4 May 2013 20:05:09 -0400 Subject: [PATCH 05/15] Implement SetsCookie, only using an Entity instance if needed --- .../processing/rest/MethodProcessor.java | 61 ++++++++++++++++--- 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java index 579b6c3634..6b5787a498 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java @@ -34,11 +34,13 @@ import org.androidannotations.processing.DecoratingElementProcessor; import org.androidannotations.processing.EBeanHolder; +import com.sun.codemodel.JArray; import com.sun.codemodel.JBlock; import com.sun.codemodel.JClass; import com.sun.codemodel.JCodeModel; import com.sun.codemodel.JExpr; import com.sun.codemodel.JExpression; +import com.sun.codemodel.JForEach; import com.sun.codemodel.JInvocation; import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; @@ -115,12 +117,53 @@ protected void generateRestTemplateCallBlock(MethodProcessorHolder methodHolder) restCall.arg(hashMapVar); } - JClass voidClass = eBeanHolder.refClass(Void.class); - JClass responseEntityClass = eBeanHolder.refClass(CanonicalNameConstants.RESPONSE_ENTITY).narrow(methodReturnVoid ? voidClass : expectedClass); - JVar responseEntity = body.decl(responseEntityClass, "response", restCall); + final JExpression result; + final boolean usesInstance; // do we have an instance of the entity? + + // attempt to retrieve cookies from the response + String[] settingCookies = retrieveSettingCookieNames(executableElement); + boolean setsCookies = settingCookies != null; + if (setsCookies) { + + JClass voidClass = eBeanHolder.refClass(Void.class); + JClass responseEntityClass = eBeanHolder.refClass(CanonicalNameConstants.RESPONSE_ENTITY).narrow(methodReturnVoid ? voidClass : expectedClass); + JVar responseEntity = body.decl(responseEntityClass, "response", restCall); + + // set cookies + JClass listClass = eBeanHolder.refClass(List.class).narrow(String.class); + JClass stringClass = eBeanHolder.refClass(CanonicalNameConstants.STRING); + JClass stringArrayClass = stringClass.array(); + JArray cookiesArray = JExpr.newArray(stringClass); + for (String cookie : settingCookies) { + cookiesArray.add(JExpr.lit(cookie)); + } + JVar requestedCookiesVar = body.decl(stringArrayClass, "requestedCookies", cookiesArray); + + JInvocation setCookiesList = JExpr.invoke(responseEntity, "getHeaders").invoke("get").arg("Set-Cookie"); + JVar allCookiesList = body.decl(listClass, "allCookies", setCookiesList); + + // for loop over list... add if in string array + JForEach forEach = body.forEach(stringClass, "rawCookie", allCookiesList); + JVar rawCookieVar = forEach.var(); + + JBlock forLoopBody = forEach.body(); + + JForEach innerForEach = forLoopBody.forEach(stringClass, "thisCookieName", requestedCookiesVar); + JBlock innerBody = innerForEach.body(); + JBlock thenBlock = innerBody._if(JExpr.invoke(rawCookieVar, "startsWith").arg(innerForEach.var()))._then(); + JExpression indexOfValue = rawCookieVar.invoke("indexOf").arg("=").plus(JExpr.lit(1)); + JInvocation cookieValue = rawCookieVar.invoke("substring").arg(indexOfValue); + thenBlock.invoke(holder.availableCookiesField, "put").arg(innerForEach.var()).arg(cookieValue); + thenBlock._break(); - JExpression result = JExpr.ref(responseEntity.name()); - insertRestCallInBody(body, result, methodHolder, methodReturnVoid); + result = JExpr.ref(responseEntity.name()); + usesInstance = true; + } else { + result = restCall; + usesInstance = false; + } + + insertRestCallInBody(body, result, methodHolder, methodReturnVoid, usesInstance); } /** @@ -148,10 +191,10 @@ protected JExpression addResultCallMethod(JExpression i, MethodProcessorHolder m return i; } - private void insertRestCallInBody(JBlock body, JExpression restCall, MethodProcessorHolder methodHolder, boolean methodReturnVoid) { - if (methodReturnVoid) { - // body.add(restCall); - } else { + private void insertRestCallInBody(JBlock body, JExpression restCall, MethodProcessorHolder methodHolder, boolean methodReturnVoid, boolean usesInstance) { + if (methodReturnVoid && !usesInstance && restCall instanceof JInvocation) { + body.add((JInvocation) restCall); + } else if (!methodReturnVoid) { restCall = addResultCallMethod(restCall, methodHolder); body._return(restCall); } From c377bb536b1db24d7604f10ae90a6eb3a8ba3ccc Mon Sep 17 00:00:00 2001 From: dhleong Date: Sat, 4 May 2013 20:15:06 -0400 Subject: [PATCH 06/15] Implement RequiresCookieInUrl, and prepare for a few others --- .../processing/rest/MethodProcessor.java | 64 +++++++++++++++++-- .../test15/rest/MyService.java | 2 + 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java index 6b5787a498..96e2ebbe02 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java @@ -27,6 +27,9 @@ import javax.lang.model.element.VariableElement; import org.androidannotations.annotations.rest.Accept; +import org.androidannotations.annotations.rest.RequiresCookie; +import org.androidannotations.annotations.rest.RequiresCookieInUrl; +import org.androidannotations.annotations.rest.RequiresHeader; import org.androidannotations.annotations.rest.SetsCookie; import org.androidannotations.helper.APTCodeModelHelper; import org.androidannotations.helper.CanonicalNameConstants; @@ -108,7 +111,7 @@ protected void generateRestTemplateCallBlock(MethodProcessorHolder methodHolder) restCall.arg(httpMethod.staticRef(restMethodInCapitalLetters)); - JVar hashMapVar = generateHashMapVar(methodHolder); + JVar hashMapVar = generateHashMapVar(holder, methodHolder); restCall = addHttpEntityVar(restCall, methodHolder); restCall = addResponseEntityArg(restCall, methodHolder); @@ -200,7 +203,7 @@ private void insertRestCallInBody(JBlock body, JExpression restCall, MethodProce } } - private JVar generateHashMapVar(MethodProcessorHolder methodHolder) { + private JVar generateHashMapVar(RestImplementationHolder holder, MethodProcessorHolder methodHolder) { ExecutableElement element = (ExecutableElement) methodHolder.getElement(); JCodeModel codeModel = methodHolder.getCodeModel(); JBlock body = methodHolder.getBody(); @@ -208,14 +211,29 @@ private JVar generateHashMapVar(MethodProcessorHolder methodHolder) { JVar hashMapVar = null; Set urlVariables = restAnnotationHelper.extractUrlVariableNames(element); + + // cookies in url? + String[] cookiesToUrl = retrieveRequiredUrlCookieNames(element); + if (cookiesToUrl != null) { + for (String cookie : cookiesToUrl) { + urlVariables.add(cookie); + } + } + JClass hashMapClass = codeModel.ref(HashMap.class).narrow(String.class, Object.class); if (!urlVariables.isEmpty()) { hashMapVar = body.decl(hashMapClass, "urlVariables", JExpr._new(hashMapClass)); for (String urlVariable : urlVariables) { - JVar urlValue = methodParams.get(urlVariable); - body.invoke(hashMapVar, "put").arg(urlVariable).arg(urlValue); - methodParams.remove(urlVariable); + JVar methodParam = methodParams.get(urlVariable); + if (methodParam != null) { + body.invoke(hashMapVar, "put").arg(urlVariable).arg(methodParam); + methodParams.remove(urlVariable); + } else { + // cookie from url + JInvocation cookieValue = holder.availableCookiesField.invoke("get").arg(JExpr.lit(urlVariable)); + body.invoke(hashMapVar, "put").arg(urlVariable).arg(cookieValue); + } } } return hashMapVar; @@ -303,6 +321,42 @@ private String retrieveAcceptAnnotationValue(ExecutableElement executableElement } } + private String[] retrieveRequiredHeaderNames(ExecutableElement executableElement) { + RequiresHeader cookieAnnotation = executableElement.getAnnotation(RequiresHeader.class); + if (cookieAnnotation == null) { + cookieAnnotation = executableElement.getEnclosingElement().getAnnotation(RequiresHeader.class); + } + if (cookieAnnotation != null) { + return cookieAnnotation.value(); + } else { + return null; + } + } + + private String[] retrieveRequiredCookieNames(ExecutableElement executableElement) { + RequiresCookie cookieAnnotation = executableElement.getAnnotation(RequiresCookie.class); + if (cookieAnnotation == null) { + cookieAnnotation = executableElement.getEnclosingElement().getAnnotation(RequiresCookie.class); + } + if (cookieAnnotation != null) { + return cookieAnnotation.value(); + } else { + return null; + } + } + + private static String[] retrieveRequiredUrlCookieNames(ExecutableElement executableElement) { + RequiresCookieInUrl cookieAnnotation = executableElement.getAnnotation(RequiresCookieInUrl.class); + if (cookieAnnotation == null) { + cookieAnnotation = executableElement.getEnclosingElement().getAnnotation(RequiresCookieInUrl.class); + } + if (cookieAnnotation != null) { + return cookieAnnotation.value(); + } else { + return null; + } + } + private String[] retrieveSettingCookieNames(ExecutableElement executableElement) { SetsCookie cookieAnnotation = executableElement.getAnnotation(SetsCookie.class); if (cookieAnnotation == null) { diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java index a126fcba4a..98f1e16495 100644 --- a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java @@ -35,6 +35,7 @@ import org.androidannotations.annotations.rest.Put; import org.androidannotations.annotations.rest.RequiresAuthentication; import org.androidannotations.annotations.rest.RequiresCookie; +import org.androidannotations.annotations.rest.RequiresCookieInUrl; import org.androidannotations.annotations.rest.Rest; import org.androidannotations.annotations.rest.SetsCookie; import org.androidannotations.api.rest.MediaType; @@ -129,6 +130,7 @@ ResponseEntity getEventsArrayOfArrays2(String location, int year) // parameter will be used as the post entity. @Post("/events/") @Accept(MediaType.APPLICATION_JSON) + @RequiresCookieInUrl("xt") Event addEvent(Event event); @Post("/events/{year}/") From d73107eadae9da462bb8419cafc08f506413bf9d Mon Sep 17 00:00:00 2001 From: dhleong Date: Sat, 4 May 2013 20:28:21 -0400 Subject: [PATCH 07/15] Implement RequiresCookie and RequiresHeader --- .../processing/rest/GetProcessor.java | 2 +- .../processing/rest/MethodProcessor.java | 46 +++++++++++++++++-- .../test15/rest/MyService.java | 3 ++ 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/GetProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/GetProcessor.java index d1c0e766a3..9c88d284e3 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/GetProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/GetProcessor.java @@ -54,7 +54,7 @@ protected JExpression generateHttpEntityVar(MethodProcessorHolder methodHolder) JClass httpEntity = holder.refClass(CanonicalNameConstants.HTTP_ENTITY); JBlock body = methodHolder.getBody(); - JVar httpHeadersVar = generateHttpHeadersVar(holder, body, executableElement); + JVar httpHeadersVar = generateHttpHeadersVar(methodHolder, holder, body, executableElement); boolean hasHeaders = httpHeadersVar != null; diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java index 96e2ebbe02..e7908d3423 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java @@ -190,8 +190,8 @@ protected JInvocation addResponseEntityArg(JInvocation restCall, MethodProcessor * Add an extra method calls on the result of restTemplate.exchange(). By * default, just return the result */ - protected JExpression addResultCallMethod(JExpression i, MethodProcessorHolder methodHolder) { - return i; + protected JExpression addResultCallMethod(JExpression restCall, MethodProcessorHolder methodHolder) { + return restCall; } private void insertRestCallInBody(JBlock body, JExpression restCall, MethodProcessorHolder methodHolder, boolean methodReturnVoid, boolean usesInstance) { @@ -264,7 +264,7 @@ protected JExpression generateHttpEntityVar(MethodProcessorHolder methodHolder) } JBlock body = methodHolder.getBody(); - JVar httpHeadersVar = generateHttpHeadersVar(holder, body, executableElement); + JVar httpHeadersVar = generateHttpHeadersVar(methodHolder, holder, body, executableElement); boolean hasHeaders = httpHeadersVar != null; @@ -289,16 +289,26 @@ protected JExpression generateHttpEntityVar(MethodProcessorHolder methodHolder) return httpEntityVar; } - protected JVar generateHttpHeadersVar(EBeanHolder holder, JBlock body, ExecutableElement executableElement) { + protected JVar generateHttpHeadersVar(MethodProcessorHolder methodHolder, EBeanHolder holder, JBlock body, ExecutableElement executableElement) { JVar httpHeadersVar = null; JClass httpHeadersClass = holder.refClass(CanonicalNameConstants.HTTP_HEADERS); String mediaType = retrieveAcceptAnnotationValue(executableElement); boolean hasMediaTypeDefined = mediaType != null; - if (hasMediaTypeDefined) { + + String cookies[] = retrieveRequiredCookieNames(executableElement); + boolean requiresCookies = cookies != null && cookies.length > 0; + + String headers[] = retrieveRequiredHeaderNames(executableElement); + boolean requiresHeaders = headers != null && headers.length > 0; + + if (hasMediaTypeDefined || requiresCookies || requiresHeaders) { + // we need the headers httpHeadersVar = body.decl(httpHeadersClass, "httpHeaders", JExpr._new(httpHeadersClass)); + } + if (hasMediaTypeDefined) { JClass collectionsClass = holder.refClass(CanonicalNameConstants.COLLECTIONS); JClass mediaTypeClass = holder.refClass(CanonicalNameConstants.MEDIA_TYPE); @@ -306,6 +316,32 @@ protected JVar generateHttpHeadersVar(EBeanHolder holder, JBlock body, Executabl body.add(JExpr.invoke(httpHeadersVar, "setAccept").arg(mediaTypeListParam)); } + if (requiresCookies) { + RestImplementationHolder restHolder = restImplementationsHolder.getEnclosingHolder(methodHolder.getElement()); + + JClass stringClass = holder.refClass(CanonicalNameConstants.STRING); + JClass stringBuilderClass = holder.refClass("java.lang.StringBuilder"); + JVar cookiesValueVar = body.decl(stringBuilderClass, "cookiesValue", JExpr._new(stringBuilderClass)); + for (String cookie : cookies) { + JInvocation cookieValue = JExpr.invoke(restHolder.availableCookiesField, "get").arg(cookie); + JInvocation cookieFormatted = stringClass.staticInvoke("format").arg(String.format("%s=%%s;", cookie)).arg(cookieValue); + JInvocation appendCookie = JExpr.invoke(cookiesValueVar, "append").arg(cookieFormatted); + body.add(appendCookie); + } + + JInvocation cookiesToString = cookiesValueVar.invoke("toString"); + body.add(JExpr.invoke(httpHeadersVar, "set").arg("Cookie").arg(cookiesToString)); + } + + if (requiresHeaders) { + RestImplementationHolder restHolder = restImplementationsHolder.getEnclosingHolder(methodHolder.getElement()); + for (String header : headers) { + JInvocation headerValue = JExpr.invoke(restHolder.availableHeadersField, "get").arg(header); + body.add(JExpr.invoke(httpHeadersVar, "set").arg(header).arg(headerValue)); + } + + } + return httpHeadersVar; } diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java index 98f1e16495..08aa66fa13 100644 --- a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java @@ -36,6 +36,7 @@ import org.androidannotations.annotations.rest.RequiresAuthentication; import org.androidannotations.annotations.rest.RequiresCookie; import org.androidannotations.annotations.rest.RequiresCookieInUrl; +import org.androidannotations.annotations.rest.RequiresHeader; import org.androidannotations.annotations.rest.Rest; import org.androidannotations.annotations.rest.SetsCookie; import org.androidannotations.api.rest.MediaType; @@ -122,6 +123,7 @@ ResponseEntity getEventsArrayOfArrays2(String location, int year) // *** POST *** @RequiresAuthentication + @RequiresHeader("SomeFancyHeader") @Post("http://company.com/oauth/token") @SetsCookie({"xt", "sjsaid"}) void authenticate(); @@ -130,6 +132,7 @@ ResponseEntity getEventsArrayOfArrays2(String location, int year) // parameter will be used as the post entity. @Post("/events/") @Accept(MediaType.APPLICATION_JSON) + @RequiresCookie("sjsaid") @RequiresCookieInUrl("xt") Event addEvent(Event event); From 0cab24349c617146a84b2bc905584f614f65b383 Mon Sep 17 00:00:00 2001 From: dhleong Date: Sat, 4 May 2013 20:30:43 -0400 Subject: [PATCH 08/15] Implement RequiresAuthorization --- .../processing/rest/MethodProcessor.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java index e7908d3423..b54c7a598a 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java @@ -27,6 +27,7 @@ import javax.lang.model.element.VariableElement; import org.androidannotations.annotations.rest.Accept; +import org.androidannotations.annotations.rest.RequiresAuthentication; import org.androidannotations.annotations.rest.RequiresCookie; import org.androidannotations.annotations.rest.RequiresCookieInUrl; import org.androidannotations.annotations.rest.RequiresHeader; @@ -342,6 +343,13 @@ protected JVar generateHttpHeadersVar(MethodProcessorHolder methodHolder, EBeanH } + boolean requiresAuth = requiresAuth(executableElement); + if (requiresAuth) { + // attach auth + RestImplementationHolder restHolder = restImplementationsHolder.getEnclosingHolder(methodHolder.getElement()); + body.add(httpHeadersVar.invoke("setAuthorization").arg(restHolder.authenticationField)); + } + return httpHeadersVar; } @@ -405,6 +413,14 @@ private String[] retrieveSettingCookieNames(ExecutableElement executableElement) } } + private boolean requiresAuth(ExecutableElement executableElement) { + RequiresAuthentication basicAuthAnnotation = executableElement.getAnnotation(RequiresAuthentication.class); + if (basicAuthAnnotation == null) { + basicAuthAnnotation = executableElement.getEnclosingElement().getAnnotation(RequiresAuthentication.class); + } + return basicAuthAnnotation != null; + } + private TreeMap extractMethodParamsVar(EBeanHolder eBeanHolder, JMethod method, ExecutableElement executableElement, RestImplementationHolder holder) { List params = executableElement.getParameters(); TreeMap methodParams = new TreeMap(); From aae71df241c1de7ae9440a7232e2e75f6a20ecbb Mon Sep 17 00:00:00 2001 From: dhleong Date: Sat, 4 May 2013 20:42:28 -0400 Subject: [PATCH 09/15] Implement setters and getters for cookies and headers and validate them --- .../helper/ValidatorHelper.java | 33 +++++++++++++ .../processing/rest/RestProcessor.java | 48 +++++++++++++++++++ .../test15/rest/MyService.java | 8 ++++ 3 files changed, 89 insertions(+) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java index a0ec0f922a..18b2aefe6b 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java @@ -79,6 +79,8 @@ public class ValidatorHelper { private static final List ANDROID_FRAGMENT_QUALIFIED_NAMES = asList(CanonicalNameConstants.FRAGMENT, CanonicalNameConstants.SUPPORT_V4_FRAGMENT); private static final String METHOD_NAME_SET_ROOT_URL = "setRootUrl"; + private static final String METHOD_NAME_GET_COOKIE = "getCookie"; + private static final String METHOD_NAME_GET_HEADER = "getHeader"; private static final List VALID_PREF_RETURN_TYPES = Arrays.asList("int", "boolean", "float", "long", CanonicalNameConstants.STRING); @@ -763,6 +765,8 @@ public void unannotatedMethodReturnsRestTemplate(TypeElement typeElement, IsVali boolean foundGetRestTemplateMethod = false; boolean foundSetRestTemplateMethod = false; boolean foundSetRootUrlMethod = false; + boolean foundGetCookieMethod = false; + boolean foundGetHeaderMethod = false; for (Element enclosedElement : enclosedElements) { if (enclosedElement.getKind() != ElementKind.METHOD) { valid.invalidate(); @@ -811,10 +815,39 @@ public void unannotatedMethodReturnsRestTemplate(TypeElement typeElement, IsVali annotationHelper.printError(enclosedElement, "The method to set a RestTemplate should have only one RestTemplate parameter on a " + TargetAnnotationHelper.annotationName(Rest.class) + " annotated interface"); } + } else if (parameters.size() == 2) { + VariableElement firstParameter = parameters.get(0); + VariableElement secondParameter = parameters.get(1); + if (!(firstParameter.asType().toString().equals(CanonicalNameConstants.STRING) && secondParameter.asType().toString().equals(CanonicalNameConstants.STRING))) { + valid.invalidate(); + annotationHelper.printError(enclosedElement, "The method to set headers or cookies should have only String parameters on a " + TargetAnnotationHelper.annotationName(Rest.class) + " annotated interface"); + } } else { valid.invalidate(); annotationHelper.printError(enclosedElement, "The method to set a RestTemplate should have only one RestTemplate parameter on a " + TargetAnnotationHelper.annotationName(Rest.class) + " annotated interface"); } + } else if (returnType.toString().equals(CanonicalNameConstants.STRING)) { + List parameters = executableElement.getParameters(); + if (parameters.size() == 1) { + VariableElement firstParameter = parameters.get(0); + if (firstParameter.asType().toString().equals(CanonicalNameConstants.STRING)) { + if (executableElement.getSimpleName().toString().equals(METHOD_NAME_GET_COOKIE) && !foundGetCookieMethod) { + foundGetCookieMethod = true; + } else if (executableElement.getSimpleName().toString().equals(METHOD_NAME_GET_HEADER) && !foundGetHeaderMethod) { + foundGetHeaderMethod = true; + } else { + valid.invalidate(); + annotationHelper.printError(enclosedElement, "Only one getCookie(String) and one getHeader(String) method are allowed on a " + TargetAnnotationHelper.annotationName(Rest.class) + " annotated interface"); + } + } else { + valid.invalidate(); + annotationHelper.printError(enclosedElement, "Only getCookie(String) and getHeader(String) can return a String on a " + TargetAnnotationHelper.annotationName(Rest.class) + " annotated interface"); + } + + } else { + valid.invalidate(); + annotationHelper.printError(enclosedElement, "The only methods that can return a String on a " + TargetAnnotationHelper.annotationName(Rest.class) + " annotated interface are getCookie(String) and getHeader(String)"); + } } else { valid.invalidate(); annotationHelper.printError(enclosedElement, "All methods should be annotated in a " + TargetAnnotationHelper.annotationName(Rest.class) + " annotated interface, except the ones that returns or set a RestTemplate"); diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestProcessor.java index db211d8d54..138d6c5a2b 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestProcessor.java @@ -45,6 +45,9 @@ import com.sun.codemodel.JBlock; import com.sun.codemodel.JClass; import com.sun.codemodel.JCodeModel; +import com.sun.codemodel.JExpr; +import com.sun.codemodel.JFieldVar; +import com.sun.codemodel.JInvocation; import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; import com.sun.codemodel.JVar; @@ -179,5 +182,50 @@ public void process(Element element, JCodeModel codeModel, EBeansHolder eBeansHo } } + // Implement getCookie and getHeader methods + implementMapGetMethod(holder, stringClass, methods, holder.availableCookiesField, "getCookie"); + implementMapGetMethod(holder, stringClass, methods, holder.availableHeadersField, "getHeader"); + + // Implement putCookie and putHeader methods + implementMapPutMethod(holder, stringClass, codeModel, methods, holder.availableCookiesField, "setCookie"); + implementMapPutMethod(holder, stringClass, codeModel, methods, holder.availableHeadersField, "setHeader"); + } + + private void implementMapGetMethod(RestImplementationHolder holder, JClass stringClass, List methods, JFieldVar field, String methodName) { + for (ExecutableElement method : methods) { + List parameters = method.getParameters(); + if (parameters.size() == 1 && method.getReturnType().toString().equals(STRING)) { + VariableElement firstParameter = parameters.get(0); + if (firstParameter.asType().toString().equals(STRING) && method.getSimpleName().toString().equals(methodName)) { + JMethod getCookieMethod = holder.restImplementationClass.method(JMod.PUBLIC, stringClass, method.getSimpleName().toString()); + getCookieMethod.annotate(Override.class); + + JVar cookieNameParam = getCookieMethod.param(stringClass, firstParameter.getSimpleName().toString()); + JInvocation cookieValue = JExpr.invoke(field, "get").arg(cookieNameParam); + getCookieMethod.body()._return(cookieValue); + break; // Only one implementation + } + } + } + } + + private void implementMapPutMethod(RestImplementationHolder holder, JClass stringClass, JCodeModel codeModel, List methods, JFieldVar field, String methodName) { + for (ExecutableElement method : methods) { + List parameters = method.getParameters(); + if (parameters.size() == 2 && method.getReturnType().getKind() == TypeKind.VOID) { + VariableElement firstParameter = parameters.get(0); + VariableElement secondParameter = parameters.get(1); + if (firstParameter.asType().toString().equals(STRING) && secondParameter.asType().toString().equals(STRING) && method.getSimpleName().toString().equals(methodName)) { + JMethod putMapMethod = holder.restImplementationClass.method(JMod.PUBLIC, codeModel.VOID, method.getSimpleName().toString()); + putMapMethod.annotate(Override.class); + + JVar keyParam = putMapMethod.param(stringClass, firstParameter.getSimpleName().toString()); + JVar valParam = putMapMethod.param(stringClass, secondParameter.getSimpleName().toString()); + + putMapMethod.body().invoke(field, "put").arg(keyParam).arg(valParam); + break; // Only one implementation + } + } + } } } diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java index 08aa66fa13..bda4ba0207 100644 --- a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java @@ -219,4 +219,12 @@ ResponseEntity getEventsArrayOfArrays2(String location, int year) void setRestTemplate(RestTemplate restTemplate); void setRootUrl(String test); + + void setCookie(String cookieName, String value); + + String getCookie(String cookieName); + + void setHeader(String headerName, String value); + + String getHeader(String headerName); } From aac6b43bb4084e81db53445d9b22bc40f95f4fef Mon Sep 17 00:00:00 2001 From: dhleong Date: Sat, 4 May 2013 20:47:16 -0400 Subject: [PATCH 10/15] Implement setAuthentication with validation --- .../helper/ValidatorHelper.java | 4 ++++ .../processing/rest/RestProcessor.java | 18 ++++++++++++++++++ .../test15/rest/MyService.java | 3 +++ 3 files changed, 25 insertions(+) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java index 18b2aefe6b..826933967a 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java @@ -79,6 +79,7 @@ public class ValidatorHelper { private static final List ANDROID_FRAGMENT_QUALIFIED_NAMES = asList(CanonicalNameConstants.FRAGMENT, CanonicalNameConstants.SUPPORT_V4_FRAGMENT); private static final String METHOD_NAME_SET_ROOT_URL = "setRootUrl"; + private static final String METHOD_NAME_SET_AUTHENTICATION = "setAuthentication"; private static final String METHOD_NAME_GET_COOKIE = "getCookie"; private static final String METHOD_NAME_GET_HEADER = "getHeader"; @@ -764,6 +765,7 @@ public void unannotatedMethodReturnsRestTemplate(TypeElement typeElement, IsVali List enclosedElements = typeElement.getEnclosedElements(); boolean foundGetRestTemplateMethod = false; boolean foundSetRestTemplateMethod = false; + boolean foundSetAuthenticationMethod = false; boolean foundSetRootUrlMethod = false; boolean foundGetCookieMethod = false; boolean foundGetHeaderMethod = false; @@ -810,6 +812,8 @@ public void unannotatedMethodReturnsRestTemplate(TypeElement typeElement, IsVali } } else if (executableElement.getSimpleName().toString().equals(METHOD_NAME_SET_ROOT_URL) && !foundSetRootUrlMethod) { foundSetRootUrlMethod = true; + } else if (executableElement.getSimpleName().toString().equals(METHOD_NAME_SET_AUTHENTICATION) && !foundSetAuthenticationMethod) { + foundSetAuthenticationMethod = true; } else { valid.invalidate(); annotationHelper.printError(enclosedElement, "The method to set a RestTemplate should have only one RestTemplate parameter on a " + TargetAnnotationHelper.annotationName(Rest.class) + " annotated interface"); diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestProcessor.java index 138d6c5a2b..2dced2f80b 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestProcessor.java @@ -182,6 +182,24 @@ public void process(Element element, JCodeModel codeModel, EBeansHolder eBeansHo } } + // Implement setAuthentication method + for (ExecutableElement method : methods) { + List parameters = method.getParameters(); + if (parameters.size() == 1 && method.getReturnType().getKind() == TypeKind.VOID) { + VariableElement firstParameter = parameters.get(0); + if (firstParameter.asType().toString().equals("org.springframework.http.HttpAuthentication") && method.getSimpleName().toString().equals("setAuthentication")) { + JMethod setAuthMethod = holder.restImplementationClass.method(JMod.PUBLIC, codeModel.VOID, method.getSimpleName().toString()); + setAuthMethod.annotate(Override.class); + + JClass authClass = eBeansHolder.refClass("org.springframework.http.HttpAuthentication"); + JVar authParam = setAuthMethod.param(authClass, firstParameter.getSimpleName().toString()); + + setAuthMethod.body().assign(_this().ref(holder.authenticationField), authParam); + break; // Only one implementation + } + } + } + // Implement getCookie and getHeader methods implementMapGetMethod(holder, stringClass, methods, holder.availableCookiesField, "getCookie"); implementMapGetMethod(holder, stringClass, methods, holder.availableHeadersField, "getHeader"); diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java index bda4ba0207..f6775e33d0 100644 --- a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java @@ -19,6 +19,7 @@ import java.util.Map; import java.util.Set; +import org.springframework.http.HttpAuthentication; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; @@ -227,4 +228,6 @@ ResponseEntity getEventsArrayOfArrays2(String location, int year) void setHeader(String headerName, String value); String getHeader(String headerName); + + void setAuthentication(HttpAuthentication auth); } From 0afc92d5cc106bc77dba277bb723780e5fc4190a Mon Sep 17 00:00:00 2001 From: dhleong Date: Sat, 4 May 2013 20:50:53 -0400 Subject: [PATCH 11/15] Implement setHttpBasicAuth method --- .../helper/ValidatorHelper.java | 2 +- .../processing/rest/RestProcessor.java | 22 +++++++++++++++++++ .../test15/rest/MyService.java | 2 ++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java index 826933967a..761061f8b7 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java @@ -824,7 +824,7 @@ public void unannotatedMethodReturnsRestTemplate(TypeElement typeElement, IsVali VariableElement secondParameter = parameters.get(1); if (!(firstParameter.asType().toString().equals(CanonicalNameConstants.STRING) && secondParameter.asType().toString().equals(CanonicalNameConstants.STRING))) { valid.invalidate(); - annotationHelper.printError(enclosedElement, "The method to set headers or cookies should have only String parameters on a " + TargetAnnotationHelper.annotationName(Rest.class) + " annotated interface"); + annotationHelper.printError(enclosedElement, "The method to set headers, cookies, or HTTP Basic Auth should have only String parameters on a " + TargetAnnotationHelper.annotationName(Rest.class) + " annotated interface"); } } else { valid.invalidate(); diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestProcessor.java index 2dced2f80b..ec4bc4fbb3 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestProcessor.java @@ -182,6 +182,28 @@ public void process(Element element, JCodeModel codeModel, EBeansHolder eBeansHo } } + // Implement setHttpBasicAuth method + for (ExecutableElement method : methods) { + List parameters = method.getParameters(); + if (parameters.size() == 2 && method.getReturnType().getKind() == TypeKind.VOID) { + VariableElement firstParameter = parameters.get(0); + VariableElement secondParameter = parameters.get(1); + if (firstParameter.asType().toString().equals(STRING) && secondParameter.asType().toString().equals(STRING) && method.getSimpleName().toString().equals("setHttpBasicAuth")) { + JMethod setBasicAuthMethod = holder.restImplementationClass.method(JMod.PUBLIC, codeModel.VOID, method.getSimpleName().toString()); + setBasicAuthMethod.annotate(Override.class); + + JVar userParam = setBasicAuthMethod.param(stringClass, firstParameter.getSimpleName().toString()); + JVar passParam = setBasicAuthMethod.param(stringClass, secondParameter.getSimpleName().toString()); + + JClass basicAuthClass = eBeansHolder.refClass("org.springframework.http.HttpBasicAuthentication"); + JInvocation basicAuthentication = JExpr._new(basicAuthClass).arg(userParam).arg(passParam); + + setBasicAuthMethod.body().assign(_this().ref(holder.authenticationField), basicAuthentication); + break; // Only one implementation + } + } + } + // Implement setAuthentication method for (ExecutableElement method : methods) { List parameters = method.getParameters(); diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java index f6775e33d0..ee441b9fba 100644 --- a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java @@ -230,4 +230,6 @@ ResponseEntity getEventsArrayOfArrays2(String location, int year) String getHeader(String headerName); void setAuthentication(HttpAuthentication auth); + + void setHttpBasicAuth(String username, String password); } From 0d2dabde5bdbd83caac3dae7abb8d191438ced60 Mon Sep 17 00:00:00 2001 From: dhleong Date: Sat, 4 May 2013 21:00:50 -0400 Subject: [PATCH 12/15] Fix some validation; ensure HttpHeaders for auth --- .../androidannotations/helper/RestAnnotationHelper.java | 8 ++++++++ .../processing/rest/MethodProcessor.java | 7 ++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/RestAnnotationHelper.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/RestAnnotationHelper.java index be00303100..e7f2933bf3 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/RestAnnotationHelper.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/RestAnnotationHelper.java @@ -26,6 +26,7 @@ import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.VariableElement; +import org.androidannotations.processing.rest.MethodProcessor; import org.androidannotations.validation.IsValid; public class RestAnnotationHelper extends TargetAnnotationHelper { @@ -43,6 +44,13 @@ public void urlVariableNamesExistInParameters(ExecutableElement element, Set 0; - if (hasMediaTypeDefined || requiresCookies || requiresHeaders) { + boolean requiresAuth = requiresAuth(executableElement); + + if (hasMediaTypeDefined || requiresCookies || requiresHeaders || requiresAuth) { // we need the headers httpHeadersVar = body.decl(httpHeadersClass, "httpHeaders", JExpr._new(httpHeadersClass)); } @@ -343,7 +345,6 @@ protected JVar generateHttpHeadersVar(MethodProcessorHolder methodHolder, EBeanH } - boolean requiresAuth = requiresAuth(executableElement); if (requiresAuth) { // attach auth RestImplementationHolder restHolder = restImplementationsHolder.getEnclosingHolder(methodHolder.getElement()); @@ -389,7 +390,7 @@ private String[] retrieveRequiredCookieNames(ExecutableElement executableElement } } - private static String[] retrieveRequiredUrlCookieNames(ExecutableElement executableElement) { + public static String[] retrieveRequiredUrlCookieNames(ExecutableElement executableElement) { RequiresCookieInUrl cookieAnnotation = executableElement.getAnnotation(RequiresCookieInUrl.class); if (cookieAnnotation == null) { cookieAnnotation = executableElement.getEnclosingElement().getAnnotation(RequiresCookieInUrl.class); From b7684984ba0bc3aff40488299c7710c0f40c01dd Mon Sep 17 00:00:00 2001 From: dhleong Date: Sat, 4 May 2013 21:56:38 -0400 Subject: [PATCH 13/15] Properly handle returned cookie values (extract only ACTUAL value) --- .../processing/rest/MethodProcessor.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java index 57ab4ed5da..c6d9e9aedf 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java @@ -155,8 +155,15 @@ protected void generateRestTemplateCallBlock(MethodProcessorHolder methodHolder) JForEach innerForEach = forLoopBody.forEach(stringClass, "thisCookieName", requestedCookiesVar); JBlock innerBody = innerForEach.body(); JBlock thenBlock = innerBody._if(JExpr.invoke(rawCookieVar, "startsWith").arg(innerForEach.var()))._then(); + + // where does the cookie VALUE end? + JInvocation valueEnd = rawCookieVar.invoke("indexOf").arg(JExpr.lit(';')); + JVar valueEndVar = thenBlock.decl(methodHolder.getCodeModel().INT, "valueEnd", valueEnd); + JBlock fixValueEndBlock = thenBlock._if(valueEndVar.eq(JExpr.lit(-1)))._then(); + fixValueEndBlock.assign(valueEndVar, rawCookieVar.invoke("length")); + JExpression indexOfValue = rawCookieVar.invoke("indexOf").arg("=").plus(JExpr.lit(1)); - JInvocation cookieValue = rawCookieVar.invoke("substring").arg(indexOfValue); + JInvocation cookieValue = rawCookieVar.invoke("substring").arg(indexOfValue).arg(valueEndVar); thenBlock.invoke(holder.availableCookiesField, "put").arg(innerForEach.var()).arg(cookieValue); thenBlock._break(); From a0986a27c5b65f10b1e0a0e26e89e25cc1d1bc55 Mon Sep 17 00:00:00 2001 From: dhleong Date: Sun, 5 May 2013 00:39:32 -0400 Subject: [PATCH 14/15] Add license headers --- .../annotations/rest/RequiresAuthentication.java | 2 +- .../annotations/rest/RequiresCookie.java | 15 +++++++++++++++ .../annotations/rest/RequiresCookieInUrl.java | 15 +++++++++++++++ .../annotations/rest/RequiresHeader.java | 2 +- .../annotations/rest/SetsCookie.java | 15 +++++++++++++++ .../test15/ParcelableSerializableData.java | 15 +++++++++++++++ 6 files changed, 62 insertions(+), 2 deletions(-) diff --git a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresAuthentication.java b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresAuthentication.java index 127be1d929..4e11420bd2 100644 --- a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresAuthentication.java +++ b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresAuthentication.java @@ -1,5 +1,5 @@ /** - * Copyright (C) 2010-2012 eBusiness Information, Excilys Group + * Copyright (C) 2010-2013 eBusiness Information, Excilys Group * * 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 diff --git a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresCookie.java b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresCookie.java index 1b30f8c690..0135048dab 100644 --- a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresCookie.java +++ b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresCookie.java @@ -1,3 +1,18 @@ +/** + * Copyright (C) 2010-2013 eBusiness Information, Excilys Group + * + * 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.androidannotations.annotations.rest; import java.lang.annotation.ElementType; diff --git a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresCookieInUrl.java b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresCookieInUrl.java index 7c784ad74f..30291a5e5f 100644 --- a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresCookieInUrl.java +++ b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresCookieInUrl.java @@ -1,3 +1,18 @@ +/** + * Copyright (C) 2010-2013 eBusiness Information, Excilys Group + * + * 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.androidannotations.annotations.rest; import java.lang.annotation.ElementType; diff --git a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresHeader.java b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresHeader.java index 5b45edc9b4..4867293575 100644 --- a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresHeader.java +++ b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/RequiresHeader.java @@ -1,5 +1,5 @@ /** - * Copyright (C) 2010-2012 eBusiness Information, Excilys Group + * Copyright (C) 2010-2013 eBusiness Information, Excilys Group * * 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 diff --git a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/SetsCookie.java b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/SetsCookie.java index a400bd41eb..03189e526c 100644 --- a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/SetsCookie.java +++ b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/SetsCookie.java @@ -1,3 +1,18 @@ +/** + * Copyright (C) 2010-2013 eBusiness Information, Excilys Group + * + * 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.androidannotations.annotations.rest; import java.lang.annotation.ElementType; diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ParcelableSerializableData.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ParcelableSerializableData.java index 747b8ed9b5..d5ae931ff1 100644 --- a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ParcelableSerializableData.java +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ParcelableSerializableData.java @@ -1,3 +1,18 @@ +/** + * Copyright (C) 2010-2013 eBusiness Information, Excilys Group + * + * 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.androidannotations.test15; import java.io.Serializable; From 8c517c1effaef698e6da0b003df054cde5ebc332 Mon Sep 17 00:00:00 2001 From: dhleong Date: Sat, 25 May 2013 23:57:07 -0400 Subject: [PATCH 15/15] Add some functional tests for new authenticated rest stuff --- .../test15/rest/MyServiceTest.java | 90 ++++++++++++++++++- .../test15/rest/MyService.java | 13 +-- 2 files changed, 97 insertions(+), 6 deletions(-) diff --git a/AndroidAnnotations/functional-test-1-5-tests/src/test/java/org/androidannotations/test15/rest/MyServiceTest.java b/AndroidAnnotations/functional-test-1-5-tests/src/test/java/org/androidannotations/test15/rest/MyServiceTest.java index 477e2d8679..28e8340933 100644 --- a/AndroidAnnotations/functional-test-1-5-tests/src/test/java/org/androidannotations/test15/rest/MyServiceTest.java +++ b/AndroidAnnotations/functional-test-1-5-tests/src/test/java/org/androidannotations/test15/rest/MyServiceTest.java @@ -16,24 +16,31 @@ package org.androidannotations.test15.rest; import static junit.framework.Assert.assertEquals; +import static org.mockito.Matchers.argThat; +import static org.mockito.Matchers.eq; import static org.mockito.Matchers.startsWith; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import java.util.HashMap; import java.util.List; import java.util.Map; import org.androidannotations.test15.AndroidAnnotationsTestRunner; +import org.apache.http.Header; import org.apache.http.message.BasicHeader; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentMatcher; import org.mockito.Mockito; import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; +import com.google.inject.matcher.Matchers; import com.xtremelabs.robolectric.Robolectric; @RunWith(AndroidAnnotationsTestRunner.class) @@ -42,9 +49,18 @@ public class MyServiceTest { private MyService_ myService = new MyService_(); private void addPendingResponse(String jsonResponse) { - Robolectric.addPendingHttpResponse(HttpStatus.OK.value(), jsonResponse.replaceAll("'", "\""), new BasicHeader("content-type", "application/json")); + addPendingResponse(jsonResponse, "_=_"); + } + private void addPendingResponse(String jsonResponse, String... cookies) { + Header[] headers = new Header[1 + cookies.length/2]; + headers[0] = new BasicHeader("content-type", "application/json"); + for (int i=0, j=1; i < cookies.length-1; i += 2, j++) { + headers[j] = new BasicHeader("set-cookie", cookies[i] + "=" + cookies[i+1]); + } + Robolectric.addPendingHttpResponse(HttpStatus.OK.value(), jsonResponse.replaceAll("'", "\""), headers); } + @Test public void can_override_root_url() { MyService_ myService = new MyService_(); @@ -162,5 +178,77 @@ public void urlWithAParameterDeclaredTwiceTest() { } } } + + @Test + public void manualFullUrl() { + + MyService_ myService = new MyService_(); + + RestTemplate restTemplate = mock(RestTemplate.class); + myService.setRestTemplate(restTemplate); + + // make sure we used the full custom url. + // this may be used like in Google's APIs + // to fetch an oauth token; Mockito doesn't + // return a response with the mock'd template, + // so we just use this weird "ping" endpoint + addPendingResponse("fancyHeaderToken"); + myService.setHttpBasicAuth("fancyUser", "fancierPassword"); + myService.ping(); + verify(restTemplate).exchange(eq("http://company.com/client/ping"), Mockito. any(), Mockito.> any(), Mockito.> any()); + } + + @Test + public void cookieInUrl() { + + final String xtValue = "1234"; + final String sjsaidValue = "7890"; + final String locationValue = "somePlace"; + final int yearValue = 2013; + + MyService_ myService = new MyService_(); + + RestTemplate restTemplate = mock(RestTemplate.class); + myService.setRestTemplate(restTemplate); + + addPendingResponse("{'id':1,'name':'event1'}"); + + // normally this is set by a call like authenticate() + // which is annotated with @SetsCookie + myService.setCookie("xt", xtValue); + myService.setCookie("sjsaid", sjsaidValue); + myService.setHttpBasicAuth("fancyUser", "fancierPassword"); + myService.getEventsVoid(locationValue, yearValue); + + ArgumentMatcher> matcher = new ArgumentMatcher>() { + + @Override + public boolean matches(Object argument) { + final String expected = "sjsaid=" + sjsaidValue + ";"; + return expected.equals(((HttpEntity) argument).getHeaders().get("Cookie").get(0)); + } + }; + + Map urlVariables = new HashMap(); + urlVariables.put("location", locationValue); + urlVariables.put("year", yearValue); + urlVariables.put("xt", xtValue); + verify(restTemplate).exchange(Mockito.anyString(), Mockito. any(), argThat(matcher), Mockito.> any(), eq(urlVariables)); + } + + @Test + public void authenticatedRequests() { + + String xtValue = "1234"; + String sjsaidValue = "5678"; + myService.setHeader("SomeFancyHeader", "fancyHeaderToken"); + + addPendingResponse("[]", "xt", xtValue, "sjsaid", sjsaidValue); + myService.authenticate(); + + assertEquals(xtValue, myService.getCookie("xt")); + assertEquals(sjsaidValue, myService.getCookie("sjsaid")); + + } } diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java index ee441b9fba..ef95718992 100644 --- a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java @@ -119,22 +119,25 @@ ResponseEntity getEventsArrayOfArrays2(String location, int year) @Get("/events/{year}/{location}") Map getEventsGenericsMap(String location, int year) throws RestClientException; - @Get("/events/{year}/{location}") + @RequiresCookie("sjsaid") + @RequiresCookieInUrl("xt") + @Get("/events/{year}/{location}?xt={xt}") void getEventsVoid(String location, int year) throws RestClientException; // *** POST *** - @RequiresAuthentication @RequiresHeader("SomeFancyHeader") - @Post("http://company.com/oauth/token") + @Post("/login") @SetsCookie({"xt", "sjsaid"}) void authenticate(); + + @RequiresAuthentication + @Post("http://company.com/client/ping") + void ping(); // There should be max 1 parameter that is not mapped to an attribute. This // parameter will be used as the post entity. @Post("/events/") @Accept(MediaType.APPLICATION_JSON) - @RequiresCookie("sjsaid") - @RequiresCookieInUrl("xt") Event addEvent(Event event); @Post("/events/{year}/")