mirror of https://github.com/apache/jclouds.git
Issue 658:new annotations @SelectJson @OnlyElement
This commit is contained in:
parent
39f4817b66
commit
6b02b93cf7
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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 {
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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");
|
||||||
|
|
Loading…
Reference in New Issue