Merge branch 'master' of github.com:jclouds/jclouds into 1.3.x

* 'master' of github.com:jclouds/jclouds:
  Issue 797: converted many adapters to the new gson streaming method
  Issue 797: moved JsonLiteral out of the gson package hierarchy as it is unnecessarily there
  Issue 797: update to gson 2.1
  Issue 797: gson 2+ defaults numbers to double
  Issue 797: gson 2+ addresses gson issue 325
  Add tests for Virtual CPU attribute in Flavor json
  Refactor Flavor class so that it is immutable
  Add vcpus to the flavor entity.
  Fixed broken test in VirtualBox.
  [issue 795] Fixing a compilation failure in EC2ListNodesStrategy - looks like something happened with generic casts between Java 6 and 7 (see http://stackoverflow.com/questions/8637937/why-does-a-generic-cast-of-a-list-extends-set-to-listset-succeed-on-sun)
  [issue 795] Added a @DataProvider and modified failing tests to selectively run under Java6/7
  [issue 795] De-generified HttpMessage.Builder and HttpRequest.Builder, prevented the "ambiguous method" compile errors caused by GeneratedHttpRequest.builder() and from() (see [issue 461]) and added a convenience requestBuilder() and fromRequest() methods with the former signatures of builder() and from() resp.
  GAE SDK 1.5.5 -> 1.6.1
  fixed problem with creating nova client inside karaf
This commit is contained in:
Adrian Cole 2012-01-01 19:43:27 -08:00
commit 8d84f8c699
40 changed files with 554 additions and 729 deletions

View File

@ -18,17 +18,15 @@
*/
package org.jclouds.cloudstack.config;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import org.jclouds.date.DateService;
import org.jclouds.json.config.GsonModule;
import java.io.IOException;
import java.util.Date;
import javax.inject.Inject;
import java.lang.reflect.Type;
import java.util.Date;
import org.jclouds.date.DateService;
import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
import com.google.gson.stream.JsonReader;
/**
* Data adapter for the date formats used by CloudStack.
@ -39,27 +37,15 @@ import java.util.Date;
*
* @author Richard Downer
*/
public class CloudStackDateAdapter implements GsonModule.DateAdapter {
private final DateService dateService;
public class CloudStackDateAdapter extends Iso8601DateAdapter {
@Inject
private CloudStackDateAdapter(DateService dateService) {
this.dateService = dateService;
super(dateService);
}
public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(dateService.iso8601DateFormat(src));
}
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
String toParse = json.getAsJsonPrimitive().getAsString();
toParse = toParse.replaceAll("'T'", "T");
try {
return dateService.iso8601DateParse(toParse);
} catch (RuntimeException e) {
return dateService.iso8601SecondsDateParse(toParse);
}
public Date read(JsonReader reader) throws IOException {
return parseDate(reader.nextString().replaceAll("'T'", "T"));
}
}

View File

@ -96,12 +96,17 @@ public class EC2ListNodesStrategy implements ListNodesStrategy {
@SuppressWarnings("unchecked")
@Override
public Future<Set<? extends Reservation<? extends RunningInstance>>> apply(String from) {
return (Future<Set<? extends Reservation<? extends RunningInstance>>>) client
.getInstanceServices().describeInstancesInRegion(from);
return castToSpecificTypedFuture(client.getInstanceServices().describeInstancesInRegion(from));
}
}, executor, null, logger, "reservations");
return concat(concat(reservations));
}
// "hide" this cast (i.e. do not perform inline) from the Java 7 compiler - see http://stackoverflow.com/questions/8637937/why-does-a-generic-cast-of-a-list-extends-set-to-listset-succeed-on-sun
@SuppressWarnings("unchecked")
private static <T> Future<T> castToSpecificTypedFuture(Future<? extends T> input) {
return (Future<T>) input;
}
}

View File

@ -27,54 +27,47 @@ package org.jclouds.openstack.nova.domain;
*/
public class Flavor extends Resource {
public Flavor() {
private final int id;
private final String name;
private final Integer disk;
private final Integer ram;
private final Integer vcpus;
//Required because of how Gson is being used to do wire marshalling with the Server class
private Flavor(){
id=0;
name=null;
disk=null;
ram=null;
vcpus=null;
}
@Override
public String toString() {
return "Flavor [disk=" + disk + ", id=" + id + ", name=" + name + ", ram=" + ram + "]";
}
public Flavor(int id, String name) {
public Flavor(int id, String name, Integer disk, Integer ram, Integer vcpus) {
this.id = id;
this.name = name;
this.disk = disk;
this.ram = ram;
this.vcpus = vcpus;
}
private int id;
private String name;
private Integer disk;
private Integer ram;
public Integer getDisk() {
return disk;
}
public void setDisk(Integer value) {
this.disk = value;
}
public int getId() {
return id;
}
public void setId(int value) {
this.id = value;
}
public String getName() {
return name;
}
public void setName(String value) {
this.name = value;
}
public Integer getRam() {
return ram;
}
public void setRam(Integer value) {
this.ram = value;
public Integer getVcpus() {
return vcpus;
}
@Override
@ -85,6 +78,7 @@ public class Flavor extends Resource {
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((ram == null) ? 0 : ram.hashCode());
result = prime * result + ((vcpus == null) ? 0 : vcpus.hashCode());
return result;
}
@ -114,7 +108,16 @@ public class Flavor extends Resource {
return false;
} else if (!ram.equals(other.ram))
return false;
if (vcpus == null) {
if (other.vcpus != null)
return false;
} else if (!vcpus.equals(other.vcpus))
return false;
return true;
}
@Override
public String toString() {
return "Flavor [disk=" + disk + ", id=" + id + ", name=" + name + ", ram=" + ram + ", vcpus=" + vcpus +"]";
}
}

View File

@ -67,7 +67,11 @@ public class Resource {
};
}
private final ConcurrentSkipListMap<LinkType,URI> orderedSelfReferences = new ConcurrentSkipListMap<LinkType,URI>();
private final ConcurrentSkipListMap<LinkType,URI> orderedSelfReferences;
public Resource(){
orderedSelfReferences = new ConcurrentSkipListMap<LinkType,URI>();
}
private void populateOrderedSelfReferences() {
for (Map<String, String> linkProperties : links) {

View File

@ -44,7 +44,7 @@ public class FlavorToHardwareTest {
public void test() throws UnknownHostException, URISyntaxException {
Hardware flavor = convertFlavor();
Hardware tempFlavor = new HardwareBuilder().ids("1").name("256 MB Server")
.processors(ImmutableList.of(new Processor(1.0, 1.0)))
.processors(ImmutableList.of(new Processor(2.0, 2.0)))
.ram(256)
.volumes(ImmutableList.of(
new VolumeBuilder().type(Volume.Type.LOCAL).size(10.0f).durable(true).bootDevice(true).build()))

View File

@ -55,6 +55,7 @@ public class ParseFlavorFromJsonResponseTest {
assertEquals(response.getName(), "256 MB Server");
assertEquals(response.getDisk().intValue(), 10);
assertEquals(response.getRam().intValue(), 256);
assertEquals(response.getVcpus().intValue(), 2);
}
public static Flavor parseFlavor() {

View File

@ -50,7 +50,7 @@ public class ParseFlavorListFromJsonResponseTest {
public void testApplyInputStream() {
InputStream is = getClass().getResourceAsStream("/test_list_flavors.json");
List<Flavor> expects = ImmutableList.of(new Flavor(1, "256 MB Server"), new Flavor(2, "512 MB Server"));
List<Flavor> expects = ImmutableList.of(new Flavor(1, "256 MB Server", null, null, null), new Flavor(2, "512 MB Server", null, null, null));
UnwrapOnlyJsonValue<List<Flavor>> parser = i.getInstance(Key
.get(new TypeLiteral<UnwrapOnlyJsonValue<List<Flavor>>>() {
@ -69,13 +69,15 @@ public class ParseFlavorListFromJsonResponseTest {
List<Flavor> response = parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(is)));
assertEquals(response.get(0).getId(), 1);
assertEquals(response.get(0).getName(), "256 MB Server");
assertEquals(response.get(0).getDisk(), new Integer(10));
assertEquals(response.get(0).getRam(), new Integer(256));
assertEquals(response.get(0).getDisk(), Integer.valueOf(10));
assertEquals(response.get(0).getRam(), Integer.valueOf(256));
assertEquals(response.get(0).getVcpus(), Integer.valueOf(2));
assertEquals(response.get(1).getId(), 2);
assertEquals(response.get(1).getName(), "512 MB Server");
assertEquals(response.get(1).getDisk(), new Integer(20));
assertEquals(response.get(1).getRam(), new Integer(512));
assertEquals(response.get(1).getDisk(), Integer.valueOf(20));
assertEquals(response.get(1).getRam(), Integer.valueOf(512));
assertEquals(response.get(1).getVcpus(), Integer.valueOf(5));
}

View File

@ -4,6 +4,7 @@
"name" : "256 MB Server",
"ram" : 256,
"disk" : 10,
"vcpus" : 2,
"links": [
{
"rel" : "self",

View File

@ -5,6 +5,7 @@
"name" : "256 MB Server",
"ram" : 256,
"disk" : 10,
"vcpus" : 2,
"links": [
{
"rel" : "self",
@ -27,6 +28,7 @@
"name" : "512 MB Server",
"ram" : 512,
"disk" : 20,
"vcpus" : 5,
"links": [
{
"rel" : "self",

View File

@ -104,7 +104,7 @@
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>1.7.2</version>
<version>2.1</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>

View File

@ -1,92 +0,0 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you 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 com.google.gson;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Set;
import org.jclouds.json.internal.ParseObjectFromElement;
import com.google.gson.internal.$Gson$Types;
/**
* Default serialization and deserialization of a map type. This implementation really only works
* well with simple primitive types as the map key. If the key is not a simple primitive then the
* object is {@code toString}ed and that value is used as its key.
* <p/>
* Patched depending on <a href="http://code.google.com/p/google-gson/issues/detail?id=325">this</a>
* @author Joel Leitch
*/
@SuppressWarnings("unchecked")
public final class ObjectMapTypeAdapter extends BaseMapTypeAdapter {
public JsonElement serialize(Map src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject map = new JsonObject();
Type childGenericType = null;
if (typeOfSrc instanceof ParameterizedType) {
Class<?> rawTypeOfSrc = $Gson$Types.getRawType(typeOfSrc);
childGenericType = $Gson$Types.getMapKeyAndValueTypes(typeOfSrc, rawTypeOfSrc)[1];
}
for (Map.Entry entry : (Set<Map.Entry>) src.entrySet()) {
Object value = entry.getValue();
JsonElement valueElement;
if (value == null) {
valueElement = JsonNull.createJsonNull();
} else {
Type childType = (childGenericType == null)
? value.getClass() : childGenericType;
valueElement = serialize(context, value, childType);
}
map.add(String.valueOf(entry.getKey()), valueElement);
}
return map;
}
public Map deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
// Use ObjectConstructor to create instance instead of hard-coding a specific type.
// This handles cases where users are using their own subclass of Map.
Map<Object, Object> map = constructMapType(typeOfT, context);
Type[] keyAndValueTypes = $Gson$Types.getMapKeyAndValueTypes(typeOfT, $Gson$Types.getRawType(typeOfT));
for (Map.Entry<String, JsonElement> entry : json.getAsJsonObject().entrySet()) {
Object key = context.deserialize(new JsonPrimitive(entry.getKey()), keyAndValueTypes[0]);
// START JCLOUDS PATCH
// http://code.google.com/p/google-gson/issues/detail?id=325
Object value = null;
if (keyAndValueTypes[1] == Object.class) {
value = ParseObjectFromElement.SINGLETON.apply(entry.getValue());
}
if (value == null) {
value = context.deserialize(entry.getValue(), keyAndValueTypes[1]);
}
// END JCLOUDS PATCH
map.put(key, value);
}
return map;
}
@Override
public String toString() {
return MapTypeAdapter.class.getSimpleName();
}
}

View File

@ -1,195 +0,0 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you 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 com.google.gson;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.google.gson.stream.MalformedJsonException;
import java.io.EOFException;
import java.io.IOException;
import java.io.Writer;
import java.util.Map;
/**
* Reads and writes GSON parse trees over streams.
*/
final class Streams {
/**
* Takes a reader in any state and returns the next value as a JsonElement.
*/
static JsonElement parse(JsonReader reader) throws JsonParseException {
boolean isEmpty = true;
try {
reader.peek();
isEmpty = false;
return parseRecursive(reader);
} catch (EOFException e) {
/*
* For compatibility with JSON 1.5 and earlier, we return a JsonNull for
* empty documents instead of throwing.
*/
if (isEmpty) {
return JsonNull.createJsonNull();
}
throw new JsonIOException(e);
} catch (MalformedJsonException e) {
throw new JsonSyntaxException(e);
} catch (IOException e) {
throw new JsonIOException(e);
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
private static JsonElement parseRecursive(JsonReader reader) throws IOException {
switch (reader.peek()) {
case STRING:
return new JsonPrimitive(reader.nextString());
case NUMBER:
String number = reader.nextString();
return new JsonPrimitive(JsonPrimitive.stringToNumber(number));
case BOOLEAN:
return new JsonPrimitive(reader.nextBoolean());
case NULL:
reader.nextNull();
return JsonNull.createJsonNull();
case BEGIN_ARRAY:
JsonArray array = new JsonArray();
reader.beginArray();
while (reader.hasNext()) {
array.add(parseRecursive(reader));
}
reader.endArray();
return array;
case BEGIN_OBJECT:
JsonObject object = new JsonObject();
reader.beginObject();
while (reader.hasNext()) {
object.add(reader.nextName(), parseRecursive(reader));
}
reader.endObject();
return object;
case END_DOCUMENT:
case NAME:
case END_OBJECT:
case END_ARRAY:
default:
throw new IllegalArgumentException();
}
}
/**
* Writes the JSON element to the writer, recursively.
*/
static void write(JsonElement element, boolean serializeNulls, JsonWriter writer)
throws IOException {
if (element == null || element.isJsonNull()) {
if (serializeNulls) {
writer.nullValue();
}
//BEGIN JCLOUDS PATCH
// * @see <a href="http://code.google.com/p/google-gson/issues/detail?id=326"/>
} else if (element instanceof JsonLiteral ) {
writer.value(JsonLiteral.class.cast(element));
//END JCLOUDS PATCH
} else if (element.isJsonPrimitive()) {
JsonPrimitive primitive = element.getAsJsonPrimitive();
if (primitive.isNumber()) {
writer.value(primitive.getAsNumber());
} else if (primitive.isBoolean()) {
writer.value(primitive.getAsBoolean());
} else {
writer.value(primitive.getAsString());
}
} else if (element.isJsonArray()) {
writer.beginArray();
for (JsonElement e : element.getAsJsonArray()) {
/* always print null when its parent element is an array! */
if (e.isJsonNull()) {
writer.nullValue();
continue;
}
write(e, serializeNulls, writer);
}
writer.endArray();
} else if (element.isJsonObject()) {
writer.beginObject();
for (Map.Entry<String, JsonElement> e : element.getAsJsonObject().entrySet()) {
JsonElement value = e.getValue();
if (!serializeNulls && value.isJsonNull()) {
continue;
}
writer.name(e.getKey());
write(value, serializeNulls, writer);
}
writer.endObject();
} else {
throw new IllegalArgumentException("Couldn't write " + element.getClass());
}
}
static Writer writerForAppendable(Appendable appendable) {
return appendable instanceof Writer ? (Writer) appendable : new AppendableWriter(appendable);
}
/**
* Adapts an {@link Appendable} so it can be passed anywhere a {@link Writer}
* is used.
*/
private static class AppendableWriter extends Writer {
private final Appendable appendable;
private final CurrentWrite currentWrite = new CurrentWrite();
private AppendableWriter(Appendable appendable) {
this.appendable = appendable;
}
@Override public void write(char[] chars, int offset, int length) throws IOException {
currentWrite.chars = chars;
appendable.append(currentWrite, offset, offset + length);
}
@Override public void write(int i) throws IOException {
appendable.append((char) i);
}
@Override public void flush() {}
@Override public void close() {}
/**
* A mutable char sequence pointing at a single char[].
*/
static class CurrentWrite implements CharSequence {
char[] chars;
public int length() {
return chars.length;
}
public char charAt(int i) {
return chars[i];
}
public CharSequence subSequence(int start, int end) {
return new String(chars, start, end - start);
}
}
}
}

View File

@ -0,0 +1,124 @@
/*
* Copyright (C) 2010 Google Inc.
*
* 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 com.google.gson.internal;
import com.google.gson.JsonElement;
import com.google.gson.JsonIOException;
import com.google.gson.JsonNull;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSyntaxException;
import com.google.gson.internal.bind.TypeAdapters;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.google.gson.stream.MalformedJsonException;
import java.io.EOFException;
import java.io.IOException;
import java.io.Writer;
import org.jclouds.json.internal.JsonLiteral;
/**
* Reads and writes GSON parse trees over streams.
*/
public final class Streams {
/**
* Takes a reader in any state and returns the next value as a JsonElement.
*/
public static JsonElement parse(JsonReader reader) throws JsonParseException {
boolean isEmpty = true;
try {
reader.peek();
isEmpty = false;
return TypeAdapters.JSON_ELEMENT.read(reader);
} catch (EOFException e) {
/*
* For compatibility with JSON 1.5 and earlier, we return a JsonNull for
* empty documents instead of throwing.
*/
if (isEmpty) {
return JsonNull.INSTANCE;
}
throw new JsonIOException(e);
} catch (MalformedJsonException e) {
throw new JsonSyntaxException(e);
} catch (IOException e) {
throw new JsonIOException(e);
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
/**
* Writes the JSON element to the writer, recursively.
*/
public static void write(JsonElement element, JsonWriter writer) throws IOException {
//BEGIN JCLOUDS PATCH
// * @see <a href="http://code.google.com/p/google-gson/issues/detail?id=326"/>
if (element instanceof JsonLiteral ) {
writer.value(JsonLiteral.class.cast(element));
//END JCLOUDS PATCH
} else {
TypeAdapters.JSON_ELEMENT.write(writer, element);
}
}
public static Writer writerForAppendable(Appendable appendable) {
return appendable instanceof Writer ? (Writer) appendable : new AppendableWriter(appendable);
}
/**
* Adapts an {@link Appendable} so it can be passed anywhere a {@link Writer}
* is used.
*/
private static class AppendableWriter extends Writer {
private final Appendable appendable;
private final CurrentWrite currentWrite = new CurrentWrite();
private AppendableWriter(Appendable appendable) {
this.appendable = appendable;
}
@Override public void write(char[] chars, int offset, int length) throws IOException {
currentWrite.chars = chars;
appendable.append(currentWrite, offset, offset + length);
}
@Override public void write(int i) throws IOException {
appendable.append((char) i);
}
@Override public void flush() {}
@Override public void close() {}
/**
* A mutable char sequence pointing at a single char[].
*/
static class CurrentWrite implements CharSequence {
char[] chars;
public int length() {
return chars.length;
}
public char charAt(int i) {
return chars[i];
}
public CharSequence subSequence(int start, int end) {
return new String(chars, start, end - start);
}
}
}
}

View File

@ -1,21 +1,19 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you 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
/*
* Copyright (C) 2010 Google Inc.
*
* http://www.apache.org/licenses/LICENSE-2.0
* 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
*
* 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.
* 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 com.google.gson.stream;
import java.io.Closeable;
@ -24,7 +22,8 @@ import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import com.google.gson.JsonLiteral;
import org.jclouds.json.internal.JsonLiteral;
/**
* Writes a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
@ -124,7 +123,7 @@ import com.google.gson.JsonLiteral;
* @author Jesse Wilson
* @since 1.6
*/
public final class JsonWriter implements Closeable {
public class JsonWriter implements Closeable {
/** The output data, containing at most one top-level array or object. */
private final Writer out;
@ -149,6 +148,10 @@ public final class JsonWriter implements Closeable {
private boolean htmlSafe;
private String deferredName;
private boolean serializeNulls = true;
/**
* Creates a new instance that writes a JSON-encoded stream to {@code out}.
* For best performance, ensure {@link Writer} is buffered; wrapping in
@ -169,7 +172,7 @@ public final class JsonWriter implements Closeable {
*
* @param indent a string containing only whitespace.
*/
public void setIndent(String indent) {
public final void setIndent(String indent) {
if (indent.length() == 0) {
this.indent = null;
this.separator = ":";
@ -191,7 +194,7 @@ public final class JsonWriter implements Closeable {
* Double#isInfinite() infinities}.
* </ul>
*/
public void setLenient(boolean lenient) {
public final void setLenient(boolean lenient) {
this.lenient = lenient;
}
@ -209,7 +212,7 @@ public final class JsonWriter implements Closeable {
* setting, your XML/HTML encoder should replace these characters with the
* corresponding escape sequences.
*/
public void setHtmlSafe(boolean htmlSafe) {
public final void setHtmlSafe(boolean htmlSafe) {
this.htmlSafe = htmlSafe;
}
@ -217,10 +220,26 @@ public final class JsonWriter implements Closeable {
* Returns true if this writer writes JSON that's safe for inclusion in HTML
* and XML documents.
*/
public boolean isHtmlSafe() {
public final boolean isHtmlSafe() {
return htmlSafe;
}
/**
* Sets whether object members are serialized when their value is null.
* This has no impact on array elements. The default is true.
*/
public final void setSerializeNulls(boolean serializeNulls) {
this.serializeNulls = serializeNulls;
}
/**
* Returns true if object members are serialized when their value is null.
* This has no impact on array elements. The default is true.
*/
public final boolean getSerializeNulls() {
return serializeNulls;
}
/**
* Begins encoding a new array. Each call to this method must be paired with
* a call to {@link #endArray}.
@ -228,6 +247,7 @@ public final class JsonWriter implements Closeable {
* @return this writer.
*/
public JsonWriter beginArray() throws IOException {
writeDeferredName();
return open(JsonScope.EMPTY_ARRAY, "[");
}
@ -247,6 +267,7 @@ public final class JsonWriter implements Closeable {
* @return this writer.
*/
public JsonWriter beginObject() throws IOException {
writeDeferredName();
return open(JsonScope.EMPTY_OBJECT, "{");
}
@ -280,6 +301,9 @@ public final class JsonWriter implements Closeable {
if (context != nonempty && context != empty) {
throw new IllegalStateException("Nesting problem: " + stack);
}
if (deferredName != null) {
throw new IllegalStateException("Dangling name: " + deferredName);
}
stack.remove(stack.size() - 1);
if (context == nonempty) {
@ -313,11 +337,21 @@ public final class JsonWriter implements Closeable {
if (name == null) {
throw new NullPointerException("name == null");
}
beforeName();
string(name);
if (deferredName != null) {
throw new IllegalStateException();
}
deferredName = name;
return this;
}
private void writeDeferredName() throws IOException {
if (deferredName != null) {
beforeName();
string(deferredName);
deferredName = null;
}
}
/**
* Encodes {@code value}.
*
@ -328,6 +362,7 @@ public final class JsonWriter implements Closeable {
if (value == null) {
return nullValue();
}
writeDeferredName();
beforeValue(false);
string(value);
return this;
@ -339,29 +374,42 @@ public final class JsonWriter implements Closeable {
* @return this writer.
*/
public JsonWriter nullValue() throws IOException {
if (deferredName != null) {
if (serializeNulls) {
writeDeferredName();
} else {
deferredName = null;
return this; // skip the name and the value
}
}
beforeValue(false);
out.write("null");
return this;
}
//BEGIN JCLOUDS PATCH
// * @see <a href="http://code.google.com/p/google-gson/issues/detail?id=326"/>
//* @see <a href="http://code.google.com/p/google-gson/issues/detail?id=326"/>
/**
* Writes {@code value} literally
*
* @return this writer.
*/
public JsonWriter value(JsonLiteral value) throws IOException {
beforeValue(false);
out.write(value.toString());
return this;
if (value == null) {
return nullValue();
}
writeDeferredName();
beforeValue(false);
out.write(value.toString());
return this;
}
//END JCLOUDS PATCH
//END JCLOUDS PATCH
/**
* Encodes {@code value}.
*
* @return this writer.
*/
public JsonWriter value(boolean value) throws IOException {
writeDeferredName();
beforeValue(false);
out.write(value ? "true" : "false");
return this;
@ -378,6 +426,7 @@ public final class JsonWriter implements Closeable {
if (Double.isNaN(value) || Double.isInfinite(value)) {
throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
}
writeDeferredName();
beforeValue(false);
out.append(Double.toString(value));
return this;
@ -389,6 +438,7 @@ public final class JsonWriter implements Closeable {
* @return this writer.
*/
public JsonWriter value(long value) throws IOException {
writeDeferredName();
beforeValue(false);
out.write(Long.toString(value));
return this;
@ -406,6 +456,7 @@ public final class JsonWriter implements Closeable {
return nullValue();
}
writeDeferredName();
String string = value.toString();
if (!lenient
&& (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN"))) {
@ -447,6 +498,10 @@ public final class JsonWriter implements Closeable {
* quotation marks except for the characters that must be escaped:
* quotation mark, reverse solidus, and the control characters
* (U+0000 through U+001F)."
*
* We also escape '\u2028' and '\u2029', which JavaScript interprets as
* newline characters. This prevents eval() from failing with a syntax
* error. http://code.google.com/p/google-gson/issues/detail?id=341
*/
switch (c) {
case '"':
@ -487,6 +542,11 @@ public final class JsonWriter implements Closeable {
}
break;
case '\u2028':
case '\u2029':
out.write(String.format("\\u%04x", (int) c));
break;
default:
if (c <= 0x1F) {
out.write(String.format("\\u%04x", (int) c));

View File

@ -36,31 +36,30 @@ import com.google.common.collect.Multimap;
* @author Adrian Cole
*/
public class HttpMessage extends PayloadEnclosingImpl {
public static Builder<? extends HttpMessage> builder() {
return new Builder<HttpMessage>();
public static Builder builder() {
return new Builder();
}
public static class Builder<T extends HttpMessage> {
public static class Builder {
protected Payload payload;
protected Multimap<String, String> headers = ImmutableMultimap.of();
public Builder<T> payload(Payload payload) {
public Builder payload(Payload payload) {
this.payload = payload;
return this;
}
public Builder<T> headers(Multimap<String, String> headers) {
public Builder headers(Multimap<String, String> headers) {
this.headers = ImmutableMultimap.copyOf(checkNotNull(headers, "headers"));
return this;
}
@SuppressWarnings("unchecked")
public T build() {
return (T) new HttpMessage(payload, headers);
public HttpMessage build() {
return new HttpMessage(payload, headers);
}
public static <X extends HttpMessage> Builder<X> from(X input) {
return new Builder<X>().payload(input.getPayload()).headers(input.getHeaders());
public static Builder from(HttpMessage input) {
return new Builder().payload(input.getPayload()).headers(input.getHeaders());
}
}
@ -85,7 +84,7 @@ public class HttpMessage extends PayloadEnclosingImpl {
return (values.size() >= 1) ? values.iterator().next() : null;
}
public Builder<? extends HttpMessage> toBuilder() {
public Builder toBuilder() {
return Builder.from(this);
}

View File

@ -39,32 +39,32 @@ import com.google.common.collect.Multimap;
* @author Adrian Cole
*/
public class HttpRequest extends HttpMessage {
public static Builder<? extends HttpRequest> builder() {
return new Builder<HttpRequest>();
public static Builder builder() {
return new Builder();
}
public static class Builder<T extends HttpRequest> extends HttpMessage.Builder<T> {
public static class Builder extends HttpMessage.Builder {
protected String method;
protected URI endpoint;
protected char[] skips = new char[] {};
protected List<HttpRequestFilter> requestFilters = ImmutableList.of();
public Builder<T> filters(List<HttpRequestFilter> requestFilters) {
public Builder filters(List<HttpRequestFilter> requestFilters) {
this.requestFilters = ImmutableList.copyOf(checkNotNull(requestFilters, "requestFilters"));
return this;
}
public Builder<T> method(String method) {
public Builder method(String method) {
this.method = checkNotNull(method, "method");
return this;
}
public Builder<T> endpoint(URI endpoint) {
public Builder endpoint(URI endpoint) {
this.endpoint = checkNotNull(endpoint, "endpoint");
return this;
}
public Builder<T> skips(char[] skips) {
public Builder skips(char[] skips) {
char[] retval = new char[checkNotNull(skips, "skips").length];
System.arraycopy(skips, 0, retval, 0, skips.length);
this.skips = retval;
@ -72,23 +72,22 @@ public class HttpRequest extends HttpMessage {
}
@Override
public Builder<T> payload(Payload payload) {
return (Builder<T>) super.payload(payload);
public Builder payload(Payload payload) {
return (Builder) super.payload(payload);
}
@Override
public Builder<T> headers(Multimap<String, String> headers) {
return (Builder<T>) super.headers(headers);
public Builder headers(Multimap<String, String> headers) {
return (Builder) super.headers(headers);
}
@Override
@SuppressWarnings("unchecked")
public T build() {
return (T) new HttpRequest(method, endpoint, skips, requestFilters, payload, headers);
public HttpRequest build() {
return new HttpRequest(method, endpoint, skips, requestFilters, payload, headers);
}
public static <X extends HttpRequest> Builder<X> from(X input) {
return new Builder<X>().method(input.getMethod()).endpoint(input.getEndpoint()).skips(input.getSkips())
public static Builder from(HttpRequest input) {
return new Builder().method(input.getMethod()).endpoint(input.getEndpoint()).skips(input.getSkips())
.filters(input.getFilters()).payload(input.getPayload()).headers(input.getHeaders());
}
@ -189,7 +188,7 @@ public class HttpRequest extends HttpMessage {
}
@Override
public Builder<? extends HttpRequest> toBuilder() {
public Builder toBuilder() {
return Builder.from(this);
}

View File

@ -35,7 +35,7 @@ public class HttpResponse extends HttpMessage {
return new Builder();
}
public static class Builder extends HttpMessage.Builder<HttpResponse> {
public static class Builder extends HttpMessage.Builder {
private int statusCode;
private String message;

View File

@ -18,14 +18,13 @@
*/
package org.jclouds.json.config;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -36,6 +35,7 @@ import org.jclouds.domain.JsonBall;
import org.jclouds.json.Json;
import org.jclouds.json.internal.EnumTypeAdapterThatReturnsFromValue;
import org.jclouds.json.internal.GsonWrapper;
import org.jclouds.json.internal.JsonLiteral;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
@ -46,13 +46,14 @@ import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonLiteral;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.ObjectMapTypeAdapter;
import com.google.gson.TypeAdapter;
import com.google.gson.internal.JsonReaderInternalAccess;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.google.inject.AbstractModule;
import com.google.inject.ImplementedBy;
import com.google.inject.Provides;
@ -69,21 +70,25 @@ public class GsonModule extends AbstractModule {
@Provides
@Singleton
Gson provideGson(JsonBallAdapter jsonAdapter, DateAdapter adapter, ByteListAdapter byteListAdapter,
ByteArrayAdapter byteArrayAdapter, SerializePropertiesDefaults propertiesAdapter,
JsonAdapterBindings bindings) throws ClassNotFoundException, Exception {
ByteArrayAdapter byteArrayAdapter, PropertiesAdapter propertiesAdapter, JsonAdapterBindings bindings)
throws ClassNotFoundException, Exception {
GsonBuilder builder = new GsonBuilder();
Logger.getLogger("com.google.gson.ParameterizedTypeHandlerMap").setLevel(Level.OFF);
builder.registerTypeHierarchyAdapter(Enum.class, new EnumTypeAdapterThatReturnsFromValue());
builder.registerTypeHierarchyAdapter(Map.class, new ObjectMapTypeAdapter());
builder.registerTypeAdapter(JsonBall.class, jsonAdapter);
builder.registerTypeAdapter(Date.class, adapter);
builder.registerTypeAdapter(Properties.class, propertiesAdapter);
// simple (type adapters)
builder.registerTypeAdapter(Properties.class, propertiesAdapter.nullSafe());
builder.registerTypeAdapter(Date.class, adapter.nullSafe());
builder.registerTypeAdapter(new TypeToken<List<Byte>>() {
}.getType(), byteListAdapter);
builder.registerTypeAdapter(byte[].class, byteArrayAdapter);
}.getType(), byteListAdapter.nullSafe());
builder.registerTypeAdapter(byte[].class, byteArrayAdapter.nullSafe());
// complicated (serializers/deserializers as they need context to operate)
builder.registerTypeHierarchyAdapter(Enum.class, new EnumTypeAdapterThatReturnsFromValue());
builder.registerTypeAdapter(JsonBall.class, jsonAdapter);
for (Map.Entry<Type, Object> binding : bindings.getBindings().entrySet()) {
builder.registerTypeAdapter(binding.getKey(), binding.getValue());
}
return builder.create();
}
@ -101,74 +106,74 @@ public class GsonModule extends AbstractModule {
}
public JsonBall deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
throws JsonParseException {
return new JsonBall(json.toString());
}
}
@ImplementedBy(CDateAdapter.class)
public static interface DateAdapter extends JsonSerializer<Date>, JsonDeserializer<Date> {
public static abstract class DateAdapter extends TypeAdapter<Date> {
}
@ImplementedBy(HexByteListAdapter.class)
public static interface ByteListAdapter extends JsonSerializer<List<Byte>>, JsonDeserializer<List<Byte>> {
public static abstract class ByteListAdapter extends TypeAdapter<List<Byte>> {
}
@ImplementedBy(HexByteArrayAdapter.class)
public static interface ByteArrayAdapter extends JsonSerializer<byte[]>, JsonDeserializer<byte[]> {
public static abstract class ByteArrayAdapter extends TypeAdapter<byte[]> {
}
@Singleton
public static class HexByteListAdapter implements ByteListAdapter {
public static class HexByteListAdapter extends ByteListAdapter {
@Override
public List<Byte> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return Bytes.asList(CryptoStreams.hex(json.getAsString()));
public void write(JsonWriter writer, List<Byte> value) throws IOException {
writer.value(CryptoStreams.hex(Bytes.toArray(value)));
}
@Override
public JsonElement serialize(List<Byte> src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(CryptoStreams.hex(Bytes.toArray(src)));
public List<Byte> read(JsonReader reader) throws IOException {
return Bytes.asList(CryptoStreams.hex(reader.nextString()));
}
}
@Singleton
public static class HexByteArrayAdapter implements ByteArrayAdapter {
public static class HexByteArrayAdapter extends ByteArrayAdapter {
@Override
public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return CryptoStreams.hex(json.getAsString());
public void write(JsonWriter writer, byte[] value) throws IOException {
writer.value(CryptoStreams.hex(value));
}
@Override
public JsonElement serialize(byte[] src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(CryptoStreams.hex(src));
public byte[] read(JsonReader reader) throws IOException {
return CryptoStreams.hex(reader.nextString());
}
}
@Singleton
public static class Iso8601DateAdapter implements DateAdapter {
public static class Iso8601DateAdapter extends DateAdapter {
private final DateService dateService;
@Inject
private Iso8601DateAdapter(DateService dateService) {
public Iso8601DateAdapter(DateService dateService) {
this.dateService = dateService;
}
public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(dateService.iso8601DateFormat(src));
public void write(JsonWriter writer, Date value) throws IOException {
writer.value(dateService.iso8601DateFormat(value));
}
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
String toParse = json.getAsJsonPrimitive().getAsString();
public Date read(JsonReader reader) throws IOException {
return parseDate(reader.nextString());
}
protected Date parseDate(String toParse) {
try {
return dateService.iso8601DateParse(toParse);
} catch (RuntimeException e) {
@ -179,63 +184,71 @@ public class GsonModule extends AbstractModule {
}
@Singleton
public static class SerializePropertiesDefaults implements JsonSerializer<Properties> {
public static class PropertiesAdapter extends TypeAdapter<Properties> {
private final Json json;
private final Type mapType = new TypeLiteral<Map<String, String>>() {
}.getRawType();
@Inject
public SerializePropertiesDefaults(Json json) {
public PropertiesAdapter(Json json) {
this.json = json;
}
public JsonElement serialize(Properties src, Type typeOfSrc, JsonSerializationContext context) {
@Override
public void write(JsonWriter out, Properties value) throws IOException {
Builder<String, String> srcMap = ImmutableMap.<String, String> builder();
for (Enumeration<?> propNames = src.propertyNames(); propNames.hasMoreElements();) {
for (Enumeration<?> propNames = value.propertyNames(); propNames.hasMoreElements();) {
String propName = (String) propNames.nextElement();
srcMap.put(propName, src.getProperty(propName));
srcMap.put(propName, value.getProperty(propName));
}
return new JsonLiteral(json.toJson(srcMap.build(), mapType));
out.value(new JsonLiteral(json.toJson(srcMap.build(), mapType)));
}
@Override
public Properties read(JsonReader in) throws IOException {
Properties props = new Properties();
in.beginObject();
while (in.hasNext()) {
JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in);
props.setProperty(in.nextString(), in.nextString());
}
in.endObject();
return props;
}
}
@Singleton
public static class CDateAdapter implements DateAdapter {
public static class CDateAdapter extends DateAdapter {
private final DateService dateService;
@Inject
private CDateAdapter(DateService dateService) {
public CDateAdapter(DateService dateService) {
this.dateService = dateService;
}
public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(dateService.cDateFormat(src));
public void write(JsonWriter writer, Date value) throws IOException {
writer.value(dateService.cDateFormat(value));
}
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
String toParse = json.getAsJsonPrimitive().getAsString();
Date toReturn = dateService.cDateParse(toParse);
return toReturn;
public Date read(JsonReader reader) throws IOException {
return dateService.cDateParse(reader.nextString());
}
}
@Singleton
public static class LongDateAdapter implements DateAdapter {
public static class LongDateAdapter extends DateAdapter {
public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src.getTime());
public void write(JsonWriter writer, Date value) throws IOException {
writer.value(value.getTime());
}
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
long toParse = json.getAsJsonPrimitive().getAsLong();
public Date read(JsonReader reader) throws IOException {
long toParse = reader.nextLong();
if (toParse == -1)
return null;
Date toReturn = new Date(toParse);
return toReturn;
return new Date(toParse);
}
}

View File

@ -16,15 +16,15 @@
* specific language governing permissions and limitations
* under the License.
*/
package com.google.gson;
package org.jclouds.json.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import java.io.IOException;
import com.google.gson.JsonElement;
/**
* The gson project use package to control access to their objects. However, this prevents us from
* doing valid work, like controling the json emitted on a per-object basis. This is here to afford
* doing valid work, like controlling the json emitted on a per-object basis. This is here to afford
* us to do this.
*
* @author Adrian Cole
@ -38,8 +38,8 @@ public final class JsonLiteral extends JsonElement {
}
@Override
protected void toString(Appendable sb, Escaper escaper) throws IOException {
sb.append(literal);
public String toString() {
return literal.toString();
}
}

View File

@ -1,58 +0,0 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you 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.json.internal;
import java.lang.reflect.Field;
import java.util.Map;
import com.google.common.base.Function;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/**
* Exposes the JsonObject as a map so that we can use gauva apis on it.
* http://code.google.com/p/google-gson/issues/detail?id=325
* @author Adrian Cole
*/
public enum JsonObjectAsMap implements Function<JsonObject, Map<String, JsonElement>> {
INSTANCE;
private final Field members;
JsonObjectAsMap() {
try {
members = JsonObject.class.getDeclaredField("members");
members.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new UnsupportedOperationException("cannot access gson internals", e);
}
}
@SuppressWarnings("unchecked")
@Override
public Map<String, JsonElement> apply(JsonObject in) {
try {
return (Map<String, JsonElement>) members.get(in);
} catch (IllegalArgumentException e) {
throw new UnsupportedOperationException("cannot access gson internals", e);
} catch (IllegalAccessException e) {
throw new UnsupportedOperationException("cannot access gson internals", e);
}
}
}

View File

@ -1,57 +0,0 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you 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.json.internal;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
/**
* This is a class that helps the default {@link MapTypeAdapter} make a sane object graph when the
* value is set to {@code Object}
* http://code.google.com/p/google-gson/issues/detail?id=325
* @author Adrian Cole
*/
public enum ParseObjectFromElement implements Function<JsonElement, Object> {
SINGLETON;
public Object apply(JsonElement input) {
Object value = null;
if (input == null || input.isJsonNull()) {
value = null;
} else if (input.isJsonPrimitive()) {
JsonPrimitive primitive = input.getAsJsonPrimitive();
if (primitive.isNumber()) {
value = primitive.getAsNumber();
} else if (primitive.isBoolean()) {
value = primitive.getAsBoolean();
} else {
value = primitive.getAsString();
}
} else if (input.isJsonArray()) {
value = Lists.newArrayList(Iterables.transform(input.getAsJsonArray(), this));
} else if (input.isJsonObject()) {
value = Maps.<String,Object>newLinkedHashMap(Maps.transformValues(JsonObjectAsMap.INSTANCE.apply(input.getAsJsonObject()),
this));
}
return value;
}
}

View File

@ -40,11 +40,21 @@ import com.google.common.collect.Multimap;
* @author Adrian Cole
*/
public class GeneratedHttpRequest<T> extends HttpRequest {
public static <T> Builder<T> builder() {
return new Builder<T>();
public static Builder<?> builder() {
// empty builder, so can be safely cast to Builder<T> by the caller
return new Builder<Object>();
}
public static class Builder<T> extends HttpRequest.Builder<GeneratedHttpRequest<T>> {
/*
* Convenience method - cannot have the same signature as builder() - see
* http://code.google.com/p/jclouds/issues/detail?id=795
*/
@SuppressWarnings("unchecked")
public static <T> Builder<T> requestBuilder() {
return (Builder<T>) builder();
}
public static class Builder<T> extends HttpRequest.Builder {
protected Class<T> declaring;
protected Method javaMethod;
protected List<Object> args;
@ -69,31 +79,37 @@ public class GeneratedHttpRequest<T> extends HttpRequest {
return this;
}
@SuppressWarnings("unchecked")
@Override
public Builder<T> filters(List<HttpRequestFilter> requestFilters) {
return (Builder<T>) super.filters(requestFilters);
}
@SuppressWarnings("unchecked")
@Override
public Builder<T> method(String method) {
return (Builder<T>) super.method(method);
}
@SuppressWarnings("unchecked")
@Override
public Builder<T> endpoint(URI endpoint) {
return (Builder<T>) super.endpoint(endpoint);
}
@SuppressWarnings("unchecked")
@Override
public Builder<T> skips(char[] skips) {
return (Builder<T>) super.skips(skips);
}
@SuppressWarnings("unchecked")
@Override
public Builder<T> payload(Payload payload) {
return (Builder<T>) super.payload(payload);
}
@SuppressWarnings("unchecked")
@Override
public Builder<T> headers(Multimap<String, String> headers) {
return (Builder<T>) super.headers(headers);
@ -105,11 +121,24 @@ public class GeneratedHttpRequest<T> extends HttpRequest {
javaMethod, args);
}
public static <Y> Builder<Y> from(HttpRequest input) {
return new Builder<Y>().method(input.getMethod()).endpoint(input.getEndpoint()).skips(input.getSkips())
public static Builder<?> from(HttpRequest input) {
/*
* State added to builder will not conflict with return type so caller can
* safely cast result to Builder<T>
*/
return new Builder<Object>().method(input.getMethod()).endpoint(input.getEndpoint()).skips(input.getSkips())
.filters(input.getFilters()).payload(input.getPayload()).headers(input.getHeaders());
}
/*
* Convenience method - cannot have the same signature as from(HttpRequest) - see
* http://code.google.com/p/jclouds/issues/detail?id=795
*/
@SuppressWarnings("unchecked")
public static <Y> Builder<Y> fromRequest(HttpRequest input) {
return (Builder<Y>) from(input);
}
public static <Y> Builder<Y> from(GeneratedHttpRequest<Y> input) {
return new Builder<Y>().method(input.getMethod()).endpoint(input.getEndpoint()).skips(input.getSkips())
.filters(input.getFilters()).payload(input.getPayload()).headers(input.getHeaders())

View File

@ -420,18 +420,10 @@ public class RestAnnotationProcessor<T> {
GeneratedHttpRequest.Builder<T> requestBuilder;
HttpRequest r = RestAnnotationProcessor.findHttpRequestInArgs(args);
if (r != null) {
requestBuilder = GeneratedHttpRequest.Builder.<T> from(r);
requestBuilder = GeneratedHttpRequest.Builder.fromRequest(r);
endpoint = r.getEndpoint();
} else {
/*
* Can't use GeneratedHttpRequest.<T>builder() because the T is too
* general for the compiler to be able to distinguish between
* GeneratedHttpRequest.builder() and HttpMessage.builder() - the
* latter is available because GHR inherits from HM.
*
* See http://code.google.com/p/jclouds/issues/detail?id=461
*/
requestBuilder = new GeneratedHttpRequest.Builder<T>();
requestBuilder = GeneratedHttpRequest.requestBuilder();
requestBuilder.method(getHttpMethodOrConstantOrThrowException(method));
}

View File

@ -58,20 +58,22 @@ public class JsonObjectTest {
public void testHash() {
String json = "{\"tomcat6\":{\"ssl_port\":8433}}";
Map<String, Object> map = ImmutableMap.<String, Object> of("tomcat6", ImmutableMap.of("ssl_port", 8433));
// gson deserialized numbers to double, so integers end up changed to fractions
assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads.newStringPayload(json))),
ImmutableMap.<String, Object> of("tomcat6", ImmutableMap.of("ssl_port", 8433d)));
assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads.newStringPayload(json))), map);
assertEquals(mapper.toJson(map), json);
assertEquals(mapper.toJson(ImmutableMap.<String, Object> of("tomcat6", ImmutableMap.of("ssl_port", 8433))), json);
}
public void testList() {
String json = "{\"list\":[8431,8433]}";
Map<String, Object> map = ImmutableMap.<String, Object> of("list", ImmutableList.of(8431, 8433));
// gson deserialized numbers to double, so integers end up changed to fractions
assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads.newStringPayload(json))),
ImmutableMap.<String, Object> of("list", ImmutableList.of(8431d, 8433d)));
assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads.newStringPayload(json))), map);
assertEquals(mapper.toJson(map), json);
assertEquals(mapper.toJson(ImmutableMap.<String, Object> of("list", ImmutableList.of(8431, 8433))), json);
}
@ -86,9 +88,9 @@ public class JsonObjectTest {
}
public void testNumber() {
String json = "{\"number\":1}";
String json = "{\"number\":1.0}";
Map<String, Object> map = ImmutableMap.<String, Object> of("number", 1);
Map<String, Object> map = ImmutableMap.<String, Object> of("number", 1d);
assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads.newStringPayload(json))), map);
assertEquals(mapper.toJson(map), json);

View File

@ -22,6 +22,8 @@ import static org.easymock.EasyMock.expect;
import static org.easymock.classextension.EasyMock.createMock;
import static org.easymock.classextension.EasyMock.replay;
import static org.easymock.classextension.EasyMock.verify;
import static org.jclouds.utils.TestUtils.NO_INVOCATIONS;
import static org.jclouds.utils.TestUtils.SINGLE_NO_ARG_INVOCATION;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
@ -31,6 +33,8 @@ import java.util.concurrent.TimeoutException;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.utils.TestUtils;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.xml.sax.Locator;
import org.xml.sax.SAXParseException;
@ -54,6 +58,16 @@ public class ParseSaxTest extends BaseHandlerTest {
return factory.create(injector.getInstance(TestHandler.class));
}
@DataProvider
public Object[][] runUnderJava7() {
return (TestUtils.isJava7() ? SINGLE_NO_ARG_INVOCATION : NO_INVOCATIONS);
}
@DataProvider
public Object[][] ignoreUnderJava7() {
return (TestUtils.isJava7() ? NO_INVOCATIONS : SINGLE_NO_ARG_INVOCATION);
}
@Test
public void testAddDetailsAndPropagateOkWhenRequestWithNoDataAndRuntimeExceptionThrowsOriginalException()
throws ExecutionException, InterruptedException, TimeoutException, IOException {
@ -118,7 +132,7 @@ public class ParseSaxTest extends BaseHandlerTest {
}
}
@Test
@Test(dataProvider = "ignoreUnderJava7", description = "see http://code.google.com/p/jclouds/issues/detail?id=795")
public void testAddDetailsAndPropagateOkWithValidRequestResponseWithSAXParseException() throws ExecutionException,
InterruptedException, TimeoutException, IOException {
@ -144,4 +158,29 @@ public class ParseSaxTest extends BaseHandlerTest {
}
}
@Test(dataProvider = "runUnderJava7", description = "see http://code.google.com/p/jclouds/issues/detail?id=795")
public void testAddDetailsAndPropagateOkWithValidRequestResponseWithSAXParseException_java7() throws ExecutionException,
InterruptedException, TimeoutException, IOException {
ParseSax<String> parser = createParser();
HttpRequest request = new HttpRequest("GET", URI.create("http://foohost"));
HttpResponse response = new HttpResponse(304, "Not Modified", null);
Locator locator = createMock(Locator.class);
expect(locator.getColumnNumber()).andReturn(1);
expect(locator.getLineNumber()).andReturn(1);
expect(locator.getPublicId()).andReturn("publicId");
expect(locator.getSystemId()).andReturn("systemId");
replay(locator);
Exception input = new SAXParseException("foo", locator);
verify(locator);
try {
parser.setContext(request);
parser.addDetailsAndPropagate(response, input);
} catch (RuntimeException e) {
assertEquals(e.getMessage(),
"request: GET http://foohost HTTP/1.1; response: HTTP/1.1 304 Not Modified; error at 1:1 in document systemId; cause: org.xml.sax.SAXParseExceptionpublicId: publicId; systemId: systemId; lineNumber: 1; columnNumber: 1; foo");
assertEquals(e.getCause(), input);
}
}
}

View File

@ -29,7 +29,6 @@ import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.gson.JsonParseException;
import com.google.inject.Guice;
import com.google.inject.TypeLiteral;
@ -107,12 +106,12 @@ public class JsonTest {
public void testMapStringObjectWithAllValidValuesOneDeep() {
Map<String, Object> map = Maps.newHashMap();
map.put("string", "string");
map.put("number", 1);
map.put("number", 1.0);
map.put("boolean", true);
map.put("map", ImmutableMap.of("key", "value"));
map.put("list", ImmutableList.of("key", "value"));
assertEquals(json.toJson(map),
"{\"string\":\"string\",\"map\":{\"key\":\"value\"},\"list\":[\"key\",\"value\"],\"boolean\":true,\"number\":1}");
"{\"string\":\"string\",\"map\":{\"key\":\"value\"},\"list\":[\"key\",\"value\"],\"boolean\":true,\"number\":1.0}");
Map<String, Object> map2 = json.fromJson(json.toJson(map), new TypeLiteral<Map<String, Object>>() {
}.getType());
assertEquals(map2, map);
@ -143,7 +142,7 @@ public class JsonTest {
assertEquals(json.fromJson("{enumValue : \"FOO\"}", EnumInside.class).enumValue, EnumInside.Test.FOO);
}
@Test(expectedExceptions = JsonParseException.class)
@Test(expectedExceptions = IllegalArgumentException.class)
public void testDeserializeEnumWhenBadValue() {
assertEquals(json.fromJson("{enumValue : \"s\"}", EnumInside.class).enumValue, EnumInside.Test.FOO);
}

View File

@ -54,8 +54,7 @@ public class BindMapToStringPayloadTest {
@Test
public void testCorrect() throws SecurityException, NoSuchMethodException {
Method testPayload = TestPayload.class.getMethod("testPayload", String.class);
// can't use GHR.builder() - see http://code.google.com/p/jclouds/issues/detail?id=461
GeneratedHttpRequest<TestPayload> request = new GeneratedHttpRequest.Builder<TestPayload>()
GeneratedHttpRequest<TestPayload> request = GeneratedHttpRequest.<TestPayload>requestBuilder()
.declaring(TestPayload.class).javaMethod(testPayload).args(ImmutableList.<Object> of("robot"))
.method(HttpMethod.POST).endpoint(URI.create("http://localhost")).build();
@ -69,8 +68,7 @@ public class BindMapToStringPayloadTest {
@Test(expectedExceptions = IllegalArgumentException.class)
public void testMustHavePayloadAnnotation() throws SecurityException, NoSuchMethodException {
Method noPayload = TestPayload.class.getMethod("noPayload", String.class);
// can't use GHR.builder() - see http://code.google.com/p/jclouds/issues/detail?id=461
GeneratedHttpRequest<TestPayload> request = new GeneratedHttpRequest.Builder<TestPayload>()
GeneratedHttpRequest<TestPayload> request = GeneratedHttpRequest.<TestPayload>requestBuilder()
.declaring(TestPayload.class).javaMethod(noPayload).args(ImmutableList.<Object> of("robot"))
.method(HttpMethod.POST).endpoint(URI.create("http://localhost")).build();
binder().bindToRequest(request, ImmutableMap.of("fooble", "robot"));

View File

@ -1816,8 +1816,7 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
public void oneTransformerWithContext() throws SecurityException, NoSuchMethodException {
RestAnnotationProcessor<TestTransformers> processor = factory(TestTransformers.class);
Method method = TestTransformers.class.getMethod("oneTransformerWithContext");
// can't use GHR.builder() - see http://code.google.com/p/jclouds/issues/detail?id=461
GeneratedHttpRequest<TestTransformers> request = new GeneratedHttpRequest.Builder<TestTransformers>().method("GET")
GeneratedHttpRequest<TestTransformers> request = GeneratedHttpRequest.<TestTransformers>requestBuilder().method("GET")
.endpoint(URI.create("http://localhost")).declaring(TestTransformers.class).javaMethod(method)
.args(new Object[] {}).build();
Function<HttpResponse, ?> transformer = processor.createResponseParser(method, request);
@ -2349,8 +2348,7 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
public void testCreateJAXBResponseParserWithAnnotation() throws SecurityException, NoSuchMethodException {
RestAnnotationProcessor<TestJAXBResponseParser> processor = factory(TestJAXBResponseParser.class);
Method method = TestJAXBResponseParser.class.getMethod("jaxbGetWithAnnotation");
// can't use GHR.builder() - see http://code.google.com/p/jclouds/issues/detail?id=461
GeneratedHttpRequest<TestJAXBResponseParser> request = new GeneratedHttpRequest.Builder<TestJAXBResponseParser>()
GeneratedHttpRequest<TestJAXBResponseParser> request = GeneratedHttpRequest.<TestJAXBResponseParser>requestBuilder()
.method("GET").endpoint(URI.create("http://localhost")).declaring(TestJAXBResponseParser.class)
.javaMethod(method).args(new Object[] {}).build();
Function<HttpResponse, ?> transformer = processor.createResponseParser(method, request);
@ -2361,8 +2359,7 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
public void testCreateJAXBResponseParserWithAcceptHeader() throws SecurityException, NoSuchMethodException {
RestAnnotationProcessor<TestJAXBResponseParser> processor = factory(TestJAXBResponseParser.class);
Method method = TestJAXBResponseParser.class.getMethod("jaxbGetWithAcceptHeader");
// can't use GHR.builder() - see http://code.google.com/p/jclouds/issues/detail?id=461
GeneratedHttpRequest<TestJAXBResponseParser> request = new GeneratedHttpRequest.Builder<TestJAXBResponseParser>()
GeneratedHttpRequest<TestJAXBResponseParser> request = GeneratedHttpRequest.<TestJAXBResponseParser>requestBuilder()
.method("GET").endpoint(URI.create("http://localhost")).declaring(TestJAXBResponseParser.class)
.javaMethod(method).args(new Object[] {}).build();
Function<HttpResponse, ?> transformer = processor.createResponseParser(method, request);

View File

@ -0,0 +1,35 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you 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.utils;
/**
* Utility class for test
*
* @author Andrew Phillips
*
*/
public class TestUtils {
public static final Object[][] NO_INVOCATIONS = new Object[0][0];
public static final Object[][] SINGLE_NO_ARG_INVOCATION = new Object[][] { new Object[0] };
public static boolean isJava7() {
System.out.println(System.getProperty("java.version", "None??"));
return System.getProperty("java.version", "").contains("1.7.");
}
}

View File

@ -37,7 +37,7 @@
extraction of appengine-java-sdk
-->
<appengine.applicationid>jclouds-tweetstore-spring</appengine.applicationid>
<appengine.sdk.version>1.5.5</appengine.sdk.version>
<appengine.sdk.version>1.6.1</appengine.sdk.version>
<devappserver.address>localhost</devappserver.address>
<devappserver.port>8088</devappserver.port>
<jclouds.tweetstore.container>jclouds-gae-tweetstore-spring</jclouds.tweetstore.container>

View File

@ -37,7 +37,7 @@
extraction of appengine-java-sdk
-->
<appengine.applicationid>jclouds-tweetstore</appengine.applicationid>
<appengine.sdk.version>1.5.5</appengine.sdk.version>
<appengine.sdk.version>1.6.1</appengine.sdk.version>
<devappserver.address>localhost</devappserver.address>
<devappserver.port>8088</devappserver.port>
<jclouds.tweetstore.container>jclouds-gae-tweetstore</jclouds.tweetstore.container>

View File

@ -1,52 +0,0 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you 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.gogrid.config;
import java.lang.reflect.Type;
import java.util.Date;
import javax.inject.Singleton;
import org.jclouds.json.config.GsonModule.DateAdapter;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
/**
* Configures the GoGrid connection, including logging and http transport.
*
* @author Adrian Cole
* @author Oleksiy Yarmula
*/
@Singleton
public class DateSecondsAdapter implements DateAdapter {
public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src.getTime());
}
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
String toParse = json.getAsJsonPrimitive().getAsString();
return new Date(Long.valueOf(toParse));
}
}

View File

@ -35,6 +35,7 @@ import org.jclouds.gogrid.domain.ServerImageType;
import org.jclouds.gogrid.domain.ServerState;
import org.jclouds.gogrid.functions.internal.CustomDeserializers;
import org.jclouds.json.config.GsonModule.DateAdapter;
import org.jclouds.json.config.GsonModule.LongDateAdapter;
import com.google.common.collect.Maps;
import com.google.inject.AbstractModule;
@ -68,7 +69,7 @@ public class GoGridParserModule extends AbstractModule {
@Override
protected void configure() {
bind(DateAdapter.class).to(DateSecondsAdapter.class);
bind(DateAdapter.class).to(LongDateAdapter.class);
}
}

View File

@ -28,7 +28,6 @@ import java.util.Map;
import javax.inject.Singleton;
import org.jclouds.domain.Credentials;
import org.jclouds.gogrid.config.DateSecondsAdapter;
import org.jclouds.gogrid.domain.IpState;
import org.jclouds.gogrid.domain.ServerImageState;
import org.jclouds.gogrid.domain.ServerImageType;
@ -77,7 +76,7 @@ public class ParseCredentialsFromJsonResponseTest {
Injector i = Guice.createInjector(new GsonModule() {
@Override
protected void configure() {
bind(DateAdapter.class).to(DateSecondsAdapter.class);
bind(DateAdapter.class).to(LongDateAdapter.class);
super.configure();
}

View File

@ -21,7 +21,6 @@ package org.jclouds.gogrid.functions;
import java.io.InputStream;
import java.net.UnknownHostException;
import org.jclouds.gogrid.config.DateSecondsAdapter;
import org.jclouds.gogrid.domain.internal.ErrorResponse;
import org.jclouds.http.HttpResponse;
import org.jclouds.io.Payloads;
@ -42,7 +41,7 @@ public class ParseErrorFromJsonResponseTest {
Injector i = Guice.createInjector(new GsonModule() {
@Override
protected void configure() {
bind(DateAdapter.class).to(DateSecondsAdapter.class);
bind(DateAdapter.class).to(LongDateAdapter.class);
super.configure();
}
});

View File

@ -29,7 +29,6 @@ import java.util.SortedSet;
import javax.inject.Singleton;
import org.jclouds.gogrid.config.DateSecondsAdapter;
import org.jclouds.gogrid.domain.Job;
import org.jclouds.gogrid.domain.JobProperties;
import org.jclouds.gogrid.domain.JobState;
@ -80,7 +79,7 @@ public class ParseJobsFromJsonResponseTest {
Injector i = Guice.createInjector(new GsonModule() {
@Override
protected void configure() {
bind(DateAdapter.class).to(DateSecondsAdapter.class);
bind(DateAdapter.class).to(LongDateAdapter.class);
super.configure();
}

View File

@ -28,7 +28,6 @@ import java.util.SortedSet;
import javax.inject.Singleton;
import org.jclouds.gogrid.config.DateSecondsAdapter;
import org.jclouds.gogrid.domain.Ip;
import org.jclouds.gogrid.domain.IpPortPair;
import org.jclouds.gogrid.domain.IpState;
@ -79,7 +78,7 @@ public class ParseLoadBalancersFromJsonResponseTest {
Injector i = Guice.createInjector(new GsonModule() {
@Override
protected void configure() {
bind(DateAdapter.class).to(DateSecondsAdapter.class);
bind(DateAdapter.class).to(LongDateAdapter.class);
super.configure();
}

View File

@ -28,7 +28,6 @@ import java.util.Map;
import javax.inject.Singleton;
import org.jclouds.domain.Credentials;
import org.jclouds.gogrid.config.DateSecondsAdapter;
import org.jclouds.gogrid.domain.IpState;
import org.jclouds.gogrid.domain.ServerImageState;
import org.jclouds.gogrid.domain.ServerImageType;
@ -65,7 +64,7 @@ public class ParseServerNameToCredentialsMapFromJsonResponseTest {
Injector i = Guice.createInjector(new GsonModule() {
@Override
protected void configure() {
bind(DateAdapter.class).to(DateSecondsAdapter.class);
bind(DateAdapter.class).to(LongDateAdapter.class);
super.configure();
}

View File

@ -18,7 +18,7 @@
*/
package org.jclouds.rimuhosting.miro.config;
import java.lang.reflect.Type;
import java.io.IOException;
import java.util.Date;
import javax.inject.Inject;
@ -26,17 +26,16 @@ import javax.inject.Singleton;
import org.jclouds.date.DateService;
import org.jclouds.http.RequiresHttp;
import org.jclouds.json.Json;
import org.jclouds.json.config.GsonModule;
import org.jclouds.json.config.GsonModule.DateAdapter;
import org.jclouds.json.config.GsonModule.PropertiesAdapter;
import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.rest.config.RestClientModule;
import org.jclouds.rimuhosting.miro.RimuHostingAsyncClient;
import org.jclouds.rimuhosting.miro.RimuHostingClient;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
/**
*
@ -57,29 +56,25 @@ public class RimuHostingRestClientModule extends RestClientModule<RimuHostingCli
}
@Singleton
public static class RimuIso8601DateAdapter implements DateAdapter {
public static class RimuIso8601DateAdapter extends GsonModule.DateAdapter {
private final DateService dateService;
private final Json json;
private static class DateHolder {
String iso_format;
}
private final PropertiesAdapter propertiesAdapter;
@Inject
private RimuIso8601DateAdapter(DateService dateService, Json json) {
private RimuIso8601DateAdapter(DateService dateService, PropertiesAdapter propertiesAdapter) {
this.dateService = dateService;
this.json = json;
this.propertiesAdapter = propertiesAdapter;
}
public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
public void write(JsonWriter writer, Date value) throws IOException {
throw new UnsupportedOperationException();
}
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
String toParse = json.toString();
DateHolder dateHolder = this.json.fromJson(toParse, DateHolder.class);
return (dateHolder.iso_format != null) ? dateService.iso8601SecondsDateParse(dateHolder.iso_format) : null;
public Date read(JsonReader in) throws IOException {
String isoFormat = propertiesAdapter.read(in).getProperty("iso_format");
if (isoFormat != null)
return dateService.iso8601SecondsDateParse(isoFormat);
return null;
}
}

View File

@ -19,30 +19,28 @@
package org.jclouds.virtualbox.functions.admin;
import static org.easymock.EasyMock.expect;
import static org.easymock.classextension.EasyMock.createMock;
import static org.easymock.classextension.EasyMock.replay;
import static org.easymock.classextension.EasyMock.verify;
import static org.testng.Assert.assertEquals;
import java.net.URI;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.inject.AbstractModule;
import com.google.inject.Module;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceContextFactory;
import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.domain.Credentials;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.net.IPSocket;
import org.jclouds.ssh.ConfiguresSshClient;
import org.jclouds.ssh.SshClient;
import org.testng.annotations.Test;
import org.virtualbox_4_1.VirtualBoxManager;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.inject.AbstractModule;
import com.google.inject.Module;
import java.net.URI;
import static org.easymock.EasyMock.expect;
import static org.easymock.classextension.EasyMock.*;
import static org.testng.Assert.assertEquals;
@Test(groups = "unit", singleThreaded = true, testName = "StartVBoxIfNotAlreadyRunningTest")
public class StartVBoxIfNotAlreadyRunningTest {
@ -112,11 +110,11 @@ public class StartVBoxIfNotAlreadyRunningTest {
// these values. Right now, it is node 2 since the above test made node
// 1.
IPSocket expectedSshSockectFor2ndCreatedNode = new IPSocket("144.175.1.2", 22);
Credentials expectedCredentialsFor2ndCreatedNode = new Credentials("root", "password2");
expect(factory.create(expectedSshSockectFor2ndCreatedNode, expectedCredentialsFor2ndCreatedNode)).andReturn(
LoginCredentials loginCredentials = new LoginCredentials("root", "password2", null, false);
expect(factory.create(expectedSshSockectFor2ndCreatedNode, loginCredentials)).andReturn(
client).times(2);
expect(client.getUsername()).andReturn(expectedCredentialsFor2ndCreatedNode.identity).times(2);
expect(client.getUsername()).andReturn(loginCredentials.identity).times(2);
expect(client.getHostAddress()).andReturn(expectedSshSockectFor2ndCreatedNode.getAddress()).times(2);
client.disconnect();