diff --git a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/Header.java b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/Header.java new file mode 100644 index 0000000000..9233743640 --- /dev/null +++ b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/Header.java @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2010-2014 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; + + +/** + * Use on methods in {@link Rest} annotated class to add headers to a particular method + * + * Example usage: + * + * @Header(headerName="keep-alive", value="300") + * @Post("/test") + * public void testRoute() + */ +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.METHOD}) +public @interface Header { + String headerName(); + String value(); +} diff --git a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/Headers.java b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/Headers.java new file mode 100644 index 0000000000..8c23899dad --- /dev/null +++ b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/rest/Headers.java @@ -0,0 +1,36 @@ +/** + * Copyright (C) 2010-2014 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; + +/** + * Use on methods in {@link Rest} annotated class to add multiple headers to a given method + * + * Example usage + * @Headers({@Header(headerName="cache-control" value="64000"), + * @Header(headerName="keep-alive" value="300")}) + * @Post("/test") + * public void getTest() + */ +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.METHOD}) +public @interface Headers { + Header[] value(); +} 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 ba4c68d645..1ce8cf02f6 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/RestAnnotationHelper.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/RestAnnotationHelper.java @@ -17,6 +17,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -40,6 +41,8 @@ import javax.lang.model.type.WildcardType; import org.androidannotations.annotations.rest.Accept; +import org.androidannotations.annotations.rest.Header; +import org.androidannotations.annotations.rest.Headers; import org.androidannotations.annotations.rest.RequiresAuthentication; import org.androidannotations.annotations.rest.RequiresCookie; import org.androidannotations.annotations.rest.RequiresCookieInUrl; @@ -183,17 +186,50 @@ public String acceptedHeaders(ExecutableElement executableElement) { } public String[] requiredHeaders(ExecutableElement executableElement) { - RequiresHeader cookieAnnotation = executableElement.getAnnotation(RequiresHeader.class); - if (cookieAnnotation == null) { - cookieAnnotation = executableElement.getEnclosingElement().getAnnotation(RequiresHeader.class); + RequiresHeader requiresHeaderAnnotation = executableElement.getAnnotation(RequiresHeader.class); + if (requiresHeaderAnnotation == null) { + requiresHeaderAnnotation = executableElement.getEnclosingElement().getAnnotation(RequiresHeader.class); } - if (cookieAnnotation != null) { - return cookieAnnotation.value(); + if (requiresHeaderAnnotation != null) { + return requiresHeaderAnnotation.value(); } else { return null; } } + private Map declaredHeaders(ExecutableElement executableElement) { + Headers headers = executableElement.getAnnotation(Headers.class); + //HEY CODE REVIEWER: DO I NEED TO CALL .getEnclosingElement HERE() WHY/WHY NOT? + Map headerMap = new HashMap(); + if (headers != null) { + Header[] headerList = headers.value(); + + // Prevent an empty annotation from crashing things + if (headerList != null) { + for (Header header : headerList) { + headerMap.putAll(processHeader(header)); + } + } + } + + Header header = executableElement.getAnnotation(Header.class); + if (header != null) { + headerMap.putAll(processHeader(header)); + } + + return headerMap; + } + + private Map processHeader(Header singleHeader) { + if (singleHeader == null) { + return null; + } else { + Map headerInfo = new HashMap(); + headerInfo.put(singleHeader.headerName(), singleHeader.value()); + return headerInfo; + } + } + public String[] requiredCookies(ExecutableElement executableElement) { RequiresCookie cookieAnnotation = executableElement.getAnnotation(RequiresCookie.class); if (cookieAnnotation == null) { @@ -252,7 +288,9 @@ public JVar declareHttpHeaders(ExecutableElement executableElement, RestHolder h boolean requiresAuth = requiredAuthentication(executableElement); - if (hasMediaTypeDefined || requiresCookies || requiresHeaders || requiresAuth) { + Map declaredHeaders = declaredHeaders(executableElement);; + + if (hasMediaTypeDefined || requiresCookies || requiresHeaders || requiresAuth || declaredHeaders.size() > 0) { // we need the headers httpHeadersVar = body.decl(holder.classes().HTTP_HEADERS, "httpHeaders", JExpr._new(holder.classes().HTTP_HEADERS)); } @@ -265,6 +303,14 @@ public JVar declareHttpHeaders(ExecutableElement executableElement, RestHolder h body.add(JExpr.invoke(httpHeadersVar, "setAccept").arg(mediaTypeListParam)); } + // Set pre-defined headers here so that they can be overridden by any runtime calls + + if (declaredHeaders != null) { + for (Map.Entry declaredHeader: declaredHeaders.entrySet()) { + body.add(JExpr.invoke(httpHeadersVar, "set").arg(declaredHeader.getKey()).arg(declaredHeader.getValue())); + } + } + if (requiresCookies) { JClass stringBuilderClass = holder.classes().STRING_BUILDER; JVar cookiesValueVar = body.decl(stringBuilderClass, "cookiesValue", JExpr._new(stringBuilderClass)); @@ -279,12 +325,11 @@ public JVar declareHttpHeaders(ExecutableElement executableElement, RestHolder h body.add(JExpr.invoke(httpHeadersVar, "set").arg("Cookie").arg(cookiesToString)); } - if (requiresHeaders) { + if (requiresHeaders) { for (String header : headers) { JInvocation headerValue = JExpr.invoke(holder.getAvailableHeadersField(), "get").arg(header); body.add(JExpr.invoke(httpHeadersVar, "set").arg(header).arg(headerValue)); } - } if (requiresAuth) { diff --git a/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/rest/ClientWithMultipleHeaders.java b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/rest/ClientWithMultipleHeaders.java new file mode 100644 index 0000000000..e4b3bdea30 --- /dev/null +++ b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/rest/ClientWithMultipleHeaders.java @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2010-2014 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.rest; + +import org.androidannotations.annotations.rest.Header; +import org.androidannotations.annotations.rest.Headers; +import org.androidannotations.annotations.rest.Post; +import org.androidannotations.annotations.rest.Rest; +import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter; + +@Rest(converters = MappingJacksonHttpMessageConverter.class) +public interface ClientWithMultipleHeaders { + + @Headers({ + @Header(headerName="testKey", value="testVal"), + @Header(headerName="testKey1", value="testVal1") + }) + @Post("/test/") + void requestWithOneHeader(); +} diff --git a/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/rest/ClientWithOneHeader.java b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/rest/ClientWithOneHeader.java new file mode 100644 index 0000000000..0b3ffcdbe6 --- /dev/null +++ b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/rest/ClientWithOneHeader.java @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2010-2014 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.rest; + +import org.androidannotations.annotations.rest.Header; +import org.androidannotations.annotations.rest.Post; +import org.androidannotations.annotations.rest.RequiresHeader; +import org.androidannotations.annotations.rest.Rest; +import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter; + +@Rest(converters = MappingJacksonHttpMessageConverter.class) +public interface ClientWithOneHeader { + + @Header(headerName="testKey", value="testVal") + @Post("/test/") + void requestWithHeader(); + +} diff --git a/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/rest/ClientWithOneHeaderInHeaders.java b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/rest/ClientWithOneHeaderInHeaders.java new file mode 100644 index 0000000000..5a75028d11 --- /dev/null +++ b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/rest/ClientWithOneHeaderInHeaders.java @@ -0,0 +1,32 @@ +/** + * Copyright (C) 2010-2014 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.rest; + + +import org.androidannotations.annotations.rest.Header; +import org.androidannotations.annotations.rest.Headers; +import org.androidannotations.annotations.rest.Post; +import org.androidannotations.annotations.rest.Rest; +import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter; + + +@Rest(converters = MappingJacksonHttpMessageConverter.class) +public interface ClientWithOneHeaderInHeaders { + + @Headers({@Header(headerName="testKey", value="testVal")}) + @Post("/test/") + void requestWithOneHeader(); +} diff --git a/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/rest/RestHeadersTest.java b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/rest/RestHeadersTest.java new file mode 100644 index 0000000000..67343240eb --- /dev/null +++ b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/rest/RestHeadersTest.java @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2010-2014 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.rest; + +import org.androidannotations.AndroidAnnotationProcessor; +import org.androidannotations.utils.AAProcessorTestHelper; +import org.junit.Before; +import org.junit.Test; + +import dalvik.annotation.TestTargetClass; + +public class RestHeadersTest extends AAProcessorTestHelper { + + @Before + public void setup() { + addManifestProcessorParameter(RestConverterTest.class); + addProcessor(AndroidAnnotationProcessor.class); + } + + @Test + public void client_with_one_header_compiles() { + CompileResult result = compileFiles(ClientWithOneHeader.class); + assertCompilationSuccessful(result); + } + + @Test + public void client_with_one_header_in_multiple_annotation_compiles() { + CompileResult result = compileFiles(ClientWithOneHeaderInHeaders.class); + assertCompilationSuccessful(result); + } + + @Test + public void client_with_multiple_headers_test() { + CompileResult result = compileFiles(ClientWithMultipleHeaders.class); + } +}