Issue 658:new annotations @SelectJson @OnlyElement

This commit is contained in:
Adrian Cole 2011-08-14 18:06:15 +01:00
parent 39f4817b66
commit 6b02b93cf7
11 changed files with 514 additions and 12 deletions

View File

@ -0,0 +1,38 @@
/**
*
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* 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.jclouds.functions;
import javax.inject.Singleton;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
/**
* @author Adrian Cole
*/
@Singleton
public class OnlyElementOrNull<T> implements Function<Iterable<T>, T> {
@Override
public T apply(Iterable<T> arg0) {
if (arg0 == null || Iterables.size(arg0) == 0)
return null;
return Iterables.getOnlyElement(arg0);
}
}

View File

@ -0,0 +1,143 @@
/**
*
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* 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.jclouds.http.functions;
import static com.google.common.base.Preconditions.checkNotNull;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import org.jclouds.http.HttpResponse;
import org.jclouds.json.internal.GsonWrapper;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.Closeables;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.inject.TypeLiteral;
/**
* @author Adrian Cole
*/
public class ParseFirstJsonValueNamed<T> implements Function<HttpResponse, T> {
private final GsonWrapper json;
private final TypeLiteral<T> type;
private final String name;
public ParseFirstJsonValueNamed(GsonWrapper json, TypeLiteral<T> type, String name) {
this.json = checkNotNull(json, "json");
this.type = checkNotNull(type, "type");
this.name = checkNotNull(name, "name");
}
@Override
public T apply(HttpResponse arg0) {
if (arg0.getPayload() == null)
return nothing();
JsonReader reader = null;
try {
reader = new JsonReader(new InputStreamReader(arg0.getPayload().getInput()));
// in case keys are not in quotes
reader.setLenient(true);
AtomicReference<String> name = new AtomicReference<String>();
JsonToken token = reader.peek();
for (; token != JsonToken.END_DOCUMENT && nnn(this.name, reader, token, name); token = skipAndPeek(token,
reader))
;
if (name.get().equals(this.name)) {
return json.delegate().fromJson(reader, type.getType());
} else {
return nothing();
}
} catch (IOException e) {
throw new RuntimeException(String.format(
"error reading from stream, parsing object named %s from http response %s", this.name, arg0), e);
} finally {
Closeables.closeQuietly(reader);
arg0.getPayload().release();
}
}
@SuppressWarnings("unchecked")
protected T nothing() {
if (type.getRawType().isAssignableFrom(Set.class))
return (T) ImmutableSet.of();
else if (type.getRawType().isAssignableFrom(List.class))
return (T) ImmutableList.of();
else if (type.getRawType().isAssignableFrom(Map.class))
return (T) ImmutableMap.of();
return null;
}
protected boolean nnn(String toFind, JsonReader reader, JsonToken token, AtomicReference<String> name)
throws IOException {
if (token == JsonToken.NAME) {
String name2 = reader.nextName();
if (toFind.equals(name2)) {
name.set(name2);
return false;
}
}
return true;
}
public JsonToken skipAndPeek(JsonToken token, JsonReader reader) throws IOException {
switch (token) {
case BEGIN_ARRAY:
reader.beginArray();
break;
case END_ARRAY:
reader.endArray();
break;
case BEGIN_OBJECT:
reader.beginObject();
break;
case END_OBJECT:
reader.endObject();
break;
case NAME:
// NOTE that we have already advanced on NAME in the eval block;
break;
case STRING:
reader.nextString();
break;
case NUMBER:
reader.nextString();
break;
case BOOLEAN:
reader.nextBoolean();
break;
case NULL:
reader.nextNull();
break;
case END_DOCUMENT:
break;
}
return reader.peek();
}
}

View File

@ -38,8 +38,8 @@ import org.jclouds.json.internal.EnumTypeAdapterThatReturnsFromValue;
import org.jclouds.json.internal.GsonWrapper; import org.jclouds.json.internal.GsonWrapper;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.collect.ImmutableMap.Builder; import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.Maps;
import com.google.common.primitives.Bytes; import com.google.common.primitives.Bytes;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;

View File

@ -25,13 +25,14 @@ import javax.inject.Singleton;
import org.jclouds.json.Json; import org.jclouds.json.Json;
import com.google.common.collect.ForwardingObject;
import com.google.gson.Gson; import com.google.gson.Gson;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
@Singleton @Singleton
public class GsonWrapper implements Json { public class GsonWrapper extends ForwardingObject implements Json {
private final Gson gson; private final Gson gson;
@ -61,4 +62,9 @@ public class GsonWrapper implements Json {
return gson.toJson(src, type); return gson.toJson(src, type);
} }
@Override
public Gson delegate() {
return gson;
}
} }

View File

@ -0,0 +1,43 @@
/**
*
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* 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.jclouds.rest.annotations;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
/**
* Extracts the only element of a collection or null
*
* @author Adrian Cole
* @see SelectJson
*/
@Target( { ANNOTATION_TYPE, METHOD, FIELD, PARAMETER })
@Retention(RUNTIME)
@Qualifier
public @interface OnlyElement {
}

View File

@ -0,0 +1,44 @@
/**
*
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* 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.jclouds.rest.annotations;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* Selects a name in a json structure as opposed to parsing from root.
*
* ex. for "foo" { "foo" :"bar" } becomes "bar"
*
* @author Adrian Cole
*/
@Target(METHOD)
@Retention(RUNTIME)
public @interface SelectJson {
/**
*
* @return
*/
String value();
}

View File

@ -46,8 +46,14 @@ public @interface Unwrap {
* *
* { "foo" : {"bar" : ["baz"]} } becomes ["baz"] * { "foo" : {"bar" : ["baz"]} } becomes ["baz"]
* *
* @return nestingLevel * <h4>Deprecation</h4>
* <p/>
* Note that using @SelectJson("bar") is more effective than guessing the
* depth
*
* @see SelectJson
*/ */
@Deprecated
int depth() default 1; int depth() default 1;
/** /**
@ -62,9 +68,15 @@ public @interface Unwrap {
* { "foo" : {"bar" : ["baz"]} } becomes "baz" * { "foo" : {"bar" : ["baz"]} } becomes "baz"
* *
* <h4>Note</h4> only Map and Set are valid * <h4>Note</h4> only Map and Set are valid
* <p/>
* <h4>Deprecation</h4>
* <p/>
* Note that using @SelectJson("bar") @OnlyElement will have the same effect
* *
* @return * @see SelectJson
* @see OnlyElement
*/ */
@Deprecated
Class<?> edgeCollection() default Map.class; Class<?> edgeCollection() default Map.class;
} }

View File

@ -46,9 +46,9 @@ import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -70,12 +70,15 @@ import javax.ws.rs.core.UriBuilderException;
import org.jclouds.Constants; import org.jclouds.Constants;
import org.jclouds.functions.IdentityFunction; import org.jclouds.functions.IdentityFunction;
import org.jclouds.functions.OnlyElementOrNull;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequestFilter; import org.jclouds.http.HttpRequestFilter;
import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpUtils; import org.jclouds.http.HttpUtils;
import org.jclouds.http.functions.ParseFirstJsonValueNamed;
import org.jclouds.http.functions.ParseJson; import org.jclouds.http.functions.ParseJson;
import org.jclouds.http.functions.ParseSax; import org.jclouds.http.functions.ParseSax;
import org.jclouds.http.functions.ParseSax.HandlerWithResult;
import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x; import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
import org.jclouds.http.functions.ReleasePayloadAndReturn; import org.jclouds.http.functions.ReleasePayloadAndReturn;
import org.jclouds.http.functions.ReturnInputStream; import org.jclouds.http.functions.ReturnInputStream;
@ -85,7 +88,6 @@ import org.jclouds.http.functions.UnwrapOnlyJsonValue;
import org.jclouds.http.functions.UnwrapOnlyJsonValueInSet; import org.jclouds.http.functions.UnwrapOnlyJsonValueInSet;
import org.jclouds.http.functions.UnwrapOnlyNestedJsonValue; import org.jclouds.http.functions.UnwrapOnlyNestedJsonValue;
import org.jclouds.http.functions.UnwrapOnlyNestedJsonValueInSet; import org.jclouds.http.functions.UnwrapOnlyNestedJsonValueInSet;
import org.jclouds.http.functions.ParseSax.HandlerWithResult;
import org.jclouds.http.options.HttpRequestOptions; import org.jclouds.http.options.HttpRequestOptions;
import org.jclouds.http.utils.ModifyRequest; import org.jclouds.http.utils.ModifyRequest;
import org.jclouds.internal.ClassMethodArgs; import org.jclouds.internal.ClassMethodArgs;
@ -96,6 +98,7 @@ import org.jclouds.io.Payloads;
import org.jclouds.io.payloads.MultipartForm; import org.jclouds.io.payloads.MultipartForm;
import org.jclouds.io.payloads.Part; import org.jclouds.io.payloads.Part;
import org.jclouds.io.payloads.Part.PartOptions; import org.jclouds.io.payloads.Part.PartOptions;
import org.jclouds.json.internal.GsonWrapper;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
import org.jclouds.rest.Binder; import org.jclouds.rest.Binder;
import org.jclouds.rest.InputParamValidator; import org.jclouds.rest.InputParamValidator;
@ -108,6 +111,7 @@ import org.jclouds.rest.annotations.FormParams;
import org.jclouds.rest.annotations.Headers; import org.jclouds.rest.annotations.Headers;
import org.jclouds.rest.annotations.MapBinder; import org.jclouds.rest.annotations.MapBinder;
import org.jclouds.rest.annotations.MatrixParams; import org.jclouds.rest.annotations.MatrixParams;
import org.jclouds.rest.annotations.OnlyElement;
import org.jclouds.rest.annotations.OverrideRequestFilters; import org.jclouds.rest.annotations.OverrideRequestFilters;
import org.jclouds.rest.annotations.ParamParser; import org.jclouds.rest.annotations.ParamParser;
import org.jclouds.rest.annotations.PartParam; import org.jclouds.rest.annotations.PartParam;
@ -116,6 +120,7 @@ import org.jclouds.rest.annotations.PayloadParams;
import org.jclouds.rest.annotations.QueryParams; import org.jclouds.rest.annotations.QueryParams;
import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser; import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.SelectJson;
import org.jclouds.rest.annotations.SkipEncoding; import org.jclouds.rest.annotations.SkipEncoding;
import org.jclouds.rest.annotations.Unwrap; import org.jclouds.rest.annotations.Unwrap;
import org.jclouds.rest.annotations.VirtualHost; import org.jclouds.rest.annotations.VirtualHost;
@ -129,18 +134,19 @@ import org.jclouds.util.Strings2;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.MapMaker; import com.google.common.collect.MapMaker;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.common.collect.ImmutableSet.Builder;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Injector; import com.google.inject.Injector;
@ -252,7 +258,7 @@ public class RestAnnotationProcessor<T> {
if (handler != null) { if (handler != null) {
transformer = parserFactory.create(injector.getInstance(handler)); transformer = parserFactory.create(injector.getInstance(handler));
} else { } else {
transformer = injector.getInstance(getParserOrThrowException(method)); transformer = getTransformerForMethod(method, injector);
} }
if (transformer instanceof InvocationContext<?>) { if (transformer instanceof InvocationContext<?>) {
((InvocationContext<?>) transformer).setContext(request); ((InvocationContext<?>) transformer).setContext(request);
@ -260,6 +266,23 @@ public class RestAnnotationProcessor<T> {
return transformer; return transformer;
} }
@SuppressWarnings({ "rawtypes", "unchecked" })
public static Function<HttpResponse, ?> getTransformerForMethod(Method method, Injector injector) {
Function<HttpResponse, ?> transformer;
if (method.isAnnotationPresent(SelectJson.class)) {
Type returnVal = getReturnTypeForMethod(method);
if (method.isAnnotationPresent(OnlyElement.class))
returnVal = Types.newParameterizedType(Set.class,returnVal);
transformer = new ParseFirstJsonValueNamed(injector.getInstance(GsonWrapper.class), TypeLiteral.get(returnVal),
method.getAnnotation(SelectJson.class).value());
if (method.isAnnotationPresent(OnlyElement.class))
transformer = Functions.compose(new OnlyElementOrNull(), transformer);
} else {
transformer = injector.getInstance(getParserOrThrowException(method));
}
return transformer;
}
@VisibleForTesting @VisibleForTesting
Function<Exception, ?> createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation(Method method) { Function<Exception, ?> createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation(Method method) {
return createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation(injector, method); return createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation(injector, method);

View File

@ -0,0 +1,128 @@
/**
*
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* 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.jclouds.http.functions;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jclouds.http.HttpResponse;
import org.jclouds.io.Payloads;
import org.jclouds.json.config.GsonModule;
import org.jclouds.json.internal.GsonWrapper;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Guice;
import com.google.inject.TypeLiteral;
/**
*
* @author Adrian Cole
*/
@Test
public class UnwrapFirstValueNamedTest {
GsonWrapper json = Guice.createInjector(new GsonModule()).getInstance(GsonWrapper.class);
static class Event {
private String name;
private String source;
private Event(String name, String source) {
this.name = name;
this.source = source;
}
@Override
public String toString() {
return String.format("(name=%s, source=%s)", name, source);
}
}
public void testParseNestedElements() throws IOException {
String nested = "{ \"count\":1 ,\"event\" : [ {name:'GREETINGS',source:'guest'} ] }";
HttpResponse response = HttpResponse.builder().statusCode(200).message("goodie")
.payload(Payloads.newPayload(nested)).build();
List<Event> val = new ParseFirstJsonValueNamed<List<Event>>(json, new TypeLiteral<List<Event>>() {
}, "event").apply(response);
assertEquals(val.toString(), "[(name=GREETINGS, source=guest)]");
}
public void testParseNestedElementsButNothing() throws IOException {
String nested = "{ \"count\":1 ,\"event\" : [ ] }";
HttpResponse response = HttpResponse.builder().statusCode(200).message("goodie")
.payload(Payloads.newPayload(nested)).build();
List<Event> val = new ParseFirstJsonValueNamed<List<Event>>(json, new TypeLiteral<List<Event>>() {
}, "event").apply(response);
assertEquals(val.toString(), "[]");
}
public void testParseNestedFurtherElements() throws IOException {
String nestedFurther = "{ \"listaccountsresponse\" : { \"count\":1 ,\"event\" : [ {name:'GREETINGS',source:'guest'} ] } }";
HttpResponse response = HttpResponse.builder().statusCode(200).message("goodie")
.payload(Payloads.newPayload(nestedFurther)).build();
List<Event> val = new ParseFirstJsonValueNamed<List<Event>>(json, new TypeLiteral<List<Event>>() {
}, "event").apply(response);
assertEquals(val.toString(), "[(name=GREETINGS, source=guest)]");
}
public void testParseNestedFurtherElementsButNothing() throws IOException {
String nestedFurther = "{ \"listaccountsresponse\" : { \"count\":1 ,\"event\" : [ ] } }";
HttpResponse response = HttpResponse.builder().statusCode(200).message("goodie")
.payload(Payloads.newPayload(nestedFurther)).build();
List<Event> val = new ParseFirstJsonValueNamed<List<Event>>(json, new TypeLiteral<List<Event>>() {
}, "event").apply(response);
assertEquals(val.toString(), "[]");
}
public void testParseNoPayloadEmptyList() throws IOException {
HttpResponse response = HttpResponse.builder().statusCode(200).message("goodie").build();
List<Event> val = new ParseFirstJsonValueNamed<List<Event>>(json, new TypeLiteral<List<Event>>() {
}, "event").apply(response);
assertEquals(val, ImmutableList.<Event> of());
}
public void testParseNoPayloadEmptyMap() throws IOException {
HttpResponse response = HttpResponse.builder().statusCode(200).message("goodie").build();
Map<String, String> val = new ParseFirstJsonValueNamed<Map<String, String>>(json,
new TypeLiteral<Map<String, String>>() {
}, "event").apply(response);
assertEquals(val, ImmutableMap.<String, String> of());
}
public void testParseNoPayloadEmptySet() throws IOException {
HttpResponse response = HttpResponse.builder().statusCode(200).message("goodie").build();
Set<Event> val = new ParseFirstJsonValueNamed<Set<Event>>(json, new TypeLiteral<Set<Event>>() {
}, "event").apply(response);
assertEquals(val, ImmutableSet.<Event> of());
}
}

View File

@ -55,8 +55,8 @@ public abstract class BaseParserTest<T, G> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected Function<HttpResponse, T> parser(Injector i) { protected Function<HttpResponse, T> parser(Injector i) {
try { try {
return (Function<HttpResponse, T>) i.getInstance(RestAnnotationProcessor.getJsonParserKeyForMethod(getClass() return (Function<HttpResponse, T>) RestAnnotationProcessor.getTransformerForMethod(getClass()
.getMethod("expected"))); .getMethod("expected"), i);
} catch (Exception e) { } catch (Exception e) {
Throwables.propagate(e); Throwables.propagate(e);
return null; return null;

View File

@ -89,6 +89,7 @@ import org.jclouds.http.HttpResponse;
import org.jclouds.http.IOExceptionRetryHandler; import org.jclouds.http.IOExceptionRetryHandler;
import org.jclouds.http.RequiresHttp; import org.jclouds.http.RequiresHttp;
import org.jclouds.http.TransformingHttpCommandExecutorService; import org.jclouds.http.TransformingHttpCommandExecutorService;
import org.jclouds.http.functions.ParseFirstJsonValueNamed;
import org.jclouds.http.functions.ParseJson; import org.jclouds.http.functions.ParseJson;
import org.jclouds.http.functions.ParseSax; import org.jclouds.http.functions.ParseSax;
import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x; import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
@ -122,6 +123,7 @@ import org.jclouds.rest.annotations.FormParams;
import org.jclouds.rest.annotations.Headers; import org.jclouds.rest.annotations.Headers;
import org.jclouds.rest.annotations.MapBinder; import org.jclouds.rest.annotations.MapBinder;
import org.jclouds.rest.annotations.MatrixParams; import org.jclouds.rest.annotations.MatrixParams;
import org.jclouds.rest.annotations.OnlyElement;
import org.jclouds.rest.annotations.OverrideRequestFilters; import org.jclouds.rest.annotations.OverrideRequestFilters;
import org.jclouds.rest.annotations.ParamParser; import org.jclouds.rest.annotations.ParamParser;
import org.jclouds.rest.annotations.PartParam; import org.jclouds.rest.annotations.PartParam;
@ -130,6 +132,7 @@ import org.jclouds.rest.annotations.PayloadParams;
import org.jclouds.rest.annotations.QueryParams; import org.jclouds.rest.annotations.QueryParams;
import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser; import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.SelectJson;
import org.jclouds.rest.annotations.SkipEncoding; import org.jclouds.rest.annotations.SkipEncoding;
import org.jclouds.rest.annotations.Unwrap; import org.jclouds.rest.annotations.Unwrap;
import org.jclouds.rest.annotations.VirtualHost; import org.jclouds.rest.annotations.VirtualHost;
@ -801,6 +804,12 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
String testUnwrap(); String testUnwrap();
@GET
@Path("/")
@SelectJson("foo")
@Consumes(MediaType.APPLICATION_JSON)
String testUnwrapValueNamed();
@POST @POST
@Path("/") @Path("/")
String testWrapWith(@WrapWith("foo") String param); String testWrapWith(@WrapWith("foo") String param);
@ -835,12 +844,24 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
ListenableFuture<Long> testUnwrapDepth2Long(); ListenableFuture<Long> testUnwrapDepth2Long();
@GET
@Path("/")
@SelectJson("jobid")
ListenableFuture<Long> selectLong();
@GET @GET
@Path("/") @Path("/")
@Unwrap(depth = 2, edgeCollection = Set.class) @Unwrap(depth = 2, edgeCollection = Set.class)
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
ListenableFuture<String> testUnwrapDepth2Set(); ListenableFuture<String> testUnwrapDepth2Set();
@GET
@Path("/")
@SelectJson("runit")
@OnlyElement
@Consumes(MediaType.APPLICATION_JSON)
ListenableFuture<String> selectOnlyElement();
@GET @GET
@Path("/") @Path("/")
@Unwrap(depth = 3, edgeCollection = Set.class) @Unwrap(depth = 3, edgeCollection = Set.class)
@ -984,6 +1005,21 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
} }
@SuppressWarnings("unchecked")
public void testUnwrapValueNamed() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPut.class.getMethod("testUnwrapValueNamed");
HttpRequest request = factory(TestPut.class).createRequest(method);
assertResponseParserClassEquals(method, request, ParseFirstJsonValueNamed.class);
// now test that it works!
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
.createResponseParser(parserFactory, injector, method, request);
assertEquals(parser.apply(new HttpResponse(200, "ok", newStringPayload("{ foo:\"bar\"}"))), "bar");
}
public void testWrapWith() throws SecurityException, NoSuchMethodException, IOException { public void testWrapWith() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPut.class.getMethod("testWrapWith", String.class); Method method = TestPut.class.getMethod("testWrapWith", String.class);
HttpRequest request = factory(TestPut.class).createRequest(method, "bar"); HttpRequest request = factory(TestPut.class).createRequest(method, "bar");
@ -1069,6 +1105,20 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
assertEquals(parser.apply(new HttpResponse(200, "ok", newStringPayload("{\"runit\":[]}"))), null); assertEquals(parser.apply(new HttpResponse(200, "ok", newStringPayload("{\"runit\":[]}"))), null);
} }
@SuppressWarnings("unchecked")
public void testSelectOnlyElement() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPut.class.getMethod("selectOnlyElement");
HttpRequest request = factory(TestPut.class).createRequest(method);
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
.createResponseParser(parserFactory, injector, method, request);
assertEquals(parser.apply(new HttpResponse(200, "ok", newStringPayload("{\"runit\":[\"0.7.0\"]}"))), "0.7.0");
assertEquals(parser.apply(new HttpResponse(200, "ok", newStringPayload("{\"runit\":[]}"))), null);
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void testUnwrapDepth2Long() throws SecurityException, NoSuchMethodException, IOException { public void testUnwrapDepth2Long() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPut.class.getMethod("testUnwrapDepth2Long"); Method method = TestPut.class.getMethod("testUnwrapDepth2Long");
@ -1084,6 +1134,21 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
newStringPayload("{ \"destroyvirtualmachineresponse\" : {\"jobid\":4} }"))), new Long(4)); newStringPayload("{ \"destroyvirtualmachineresponse\" : {\"jobid\":4} }"))), new Long(4));
} }
@SuppressWarnings("unchecked")
public void selectLong() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPut.class.getMethod("selectLong");
HttpRequest request = factory(TestPut.class).createRequest(method);
assertResponseParserClassEquals(method, request, ParseFirstJsonValueNamed.class);
// now test that it works!
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
.createResponseParser(parserFactory, injector, method, request);
assertEquals(parser.apply(new HttpResponse(200, "ok",
newStringPayload("{ \"destroyvirtualmachineresponse\" : {\"jobid\":4} }"))), new Long(4));
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void testUnwrapDepth3() throws SecurityException, NoSuchMethodException, IOException { public void testUnwrapDepth3() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPut.class.getMethod("testUnwrapDepth3"); Method method = TestPut.class.getMethod("testUnwrapDepth3");