Adding OptionalTypeAdaptorFactory to handle the parsing of Optional values

This commit is contained in:
Adam Lowe 2012-05-08 14:31:11 +01:00 committed by Adrian Cole
parent 47ad9e2bac
commit 1797b27ed4
20 changed files with 1016 additions and 278 deletions

View File

@ -42,6 +42,7 @@ import org.jclouds.openstack.nova.v1_1.compute.strategy.ApplyNovaTemplateOptions
import org.jclouds.openstack.nova.v1_1.domain.Flavor;
import org.jclouds.openstack.nova.v1_1.domain.Image;
import org.jclouds.openstack.nova.v1_1.domain.KeyPair;
import org.jclouds.openstack.nova.v1_1.domain.ServerCreated;
import org.jclouds.openstack.nova.v1_1.domain.RebootType;
import org.jclouds.openstack.nova.v1_1.domain.Server;
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.FlavorInZone;
@ -119,17 +120,14 @@ public class NovaComputeServiceAdapter implements
String flavorId = template.getHardware().getProviderId();
logger.debug(">> creating new server zone(%s) name(%s) image(%s) flavor(%s) options(%s)", zoneId, name, imageId, flavorId, options);
Server lightweightServer = novaClient.getServerClientForZone(zoneId).createServer(name, imageId, flavorId, options);
Server heavyweightServer = novaClient.getServerClientForZone(zoneId).getServer(lightweightServer.getId());
Server server = Server.builder().fromServer(heavyweightServer)
.adminPass(lightweightServer.getAdminPass())
.build();
ServerCreated lightweightServer = novaClient.getServerClientForZone(zoneId).createServer(name, imageId, flavorId, options);
Server server = novaClient.getServerClientForZone(zoneId).getServer(lightweightServer.getId());
logger.trace("<< server(%s)", server.getId());
ServerInZone serverInZone = new ServerInZone(server, zoneId);
if (!privateKey.isPresent())
credentialsBuilder.password(server.getAdminPass());
credentialsBuilder.password(lightweightServer.getAdminPass());
return new NodeAndInitialCredentials<ServerInZone>(serverInZone, serverInZone.slashEncode(), credentialsBuilder
.build());
}

View File

@ -28,8 +28,11 @@ import org.jclouds.json.config.GsonModule;
import org.jclouds.json.config.GsonModule.DateAdapter;
import org.jclouds.openstack.nova.v1_1.domain.HostResourceUsage;
import org.jclouds.openstack.nova.v1_1.domain.Server;
import org.jclouds.openstack.nova.v1_1.domain.ServerExtendedAttributes;
import org.jclouds.openstack.nova.v1_1.domain.ServerExtendedStatus;
import org.jclouds.openstack.nova.v1_1.domain.ServerWithSecurityGroups;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.google.gson.JsonArray;
@ -51,9 +54,10 @@ public class NovaParserModule extends AbstractModule {
@Provides
@Singleton
public Map<Type, Object> provideCustomAdapterBindings() {
return ImmutableMap.<Type, Object> of(
return ImmutableMap.<Type, Object>of(
HostResourceUsage.class, new HostResourceUsageAdapter(),
ServerWithSecurityGroups.class, new ServerWithSecurityGroupsAdapter()
ServerWithSecurityGroups.class, new ServerWithSecurityGroupsAdapter(),
Server.class, new ServerAdapter()
);
}
@ -98,7 +102,7 @@ public class NovaParserModule extends AbstractModule {
Set<String> names = Sets.newLinkedHashSet();
if (jsonElement.getAsJsonObject().get("security_groups") != null) {
JsonArray x = jsonElement.getAsJsonObject().get("security_groups").getAsJsonArray();
for(JsonElement y : x) {
for (JsonElement y : x) {
names.add(y.getAsJsonObject().get("name").getAsString());
}
result.securityGroupNames(names);
@ -106,4 +110,33 @@ public class NovaParserModule extends AbstractModule {
return result.build();
}
}
@Singleton
public static class ServerAdapter implements JsonDeserializer<Server> {
@Override
public Server deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context)
throws JsonParseException {
Server serverBase = apply((ServerInternal) context.deserialize(jsonElement, ServerInternal.class));
Server.Builder result = Server.builder().fromServer(serverBase);
ServerExtendedStatus extendedStatus = context.deserialize(jsonElement, ServerExtendedStatus.class);
if (!Objects.equal(extendedStatus, ServerExtendedStatus.builder().build())) {
result.extendedStatus(extendedStatus);
}
ServerExtendedAttributes extraAttributes = context.deserialize(jsonElement, ServerExtendedAttributes.class);
if (!Objects.equal(extraAttributes, ServerExtendedAttributes.builder().build())) {
result.extraAttributes(extraAttributes);
}
return result.build();
}
public Server apply(ServerInternal in) {
return in.toBuilder().build();
}
private static class ServerInternal extends Server {
protected ServerInternal() {
}
}
}
}

View File

@ -30,6 +30,8 @@ import org.jclouds.openstack.domain.Resource;
import org.jclouds.openstack.nova.v1_1.extensions.KeyPairClient;
import org.jclouds.util.Multimaps2;
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.base.Optional;
import com.google.common.base.Predicates;
import com.google.common.base.Strings;
import com.google.common.base.Objects.ToStringHelper;
@ -67,7 +69,7 @@ public class Server extends Resource {
NodeState.TERMINATED), UNKNOWN(NodeState.UNRECOGNIZED), ERROR(NodeState.ERROR), UNRECOGNIZED(
NodeState.UNRECOGNIZED), PAUSED(NodeState.SUSPENDED);
private final NodeState nodeState;
protected final NodeState nodeState;
Status(NodeState nodeState) {
this.nodeState = nodeState;
@ -111,17 +113,15 @@ public class Server extends Resource {
private Server.Status status;
private Resource image;
private Resource flavor;
private String adminPass;
private String keyName;
private String configDrive;
private Multimap<String, Address> addresses = ImmutableMultimap.of();
private Map<String, String> metadata = ImmutableMap.of();
private String taskState;
private String vmState;
private String powerState;
private String instanceName;
private String hostName;
private String hypervisorName;
// Extended status extension
private ServerExtendedStatus extendedStatus;
// Extended server attributes extension
private ServerExtendedAttributes extendedAttributes;
// Disk Config extension
private String diskConfig;
/**
@ -212,14 +212,6 @@ public class Server extends Resource {
return self();
}
/**
* @see Server#getAdminPass()
*/
public T adminPass(String adminPass) {
this.adminPass = adminPass;
return self();
}
/**
* @see Server#getKeyName()
*/
@ -253,50 +245,18 @@ public class Server extends Resource {
}
/**
* @see Server#getTaskState()
* @see Server#getExtendedStatus()
*/
public T taskState(String taskState) {
this.taskState = taskState;
public T extendedStatus(ServerExtendedStatus extendedStatus) {
this.extendedStatus = extendedStatus;
return self();
}
/**
* @see Server#getVmState()
* @see Server#getExtendedAttributes()
*/
public T vmState(String vmState) {
this.vmState = vmState;
return self();
}
/**
* @see Server#getPowerState()
*/
public T powerState(String powerState) {
this.powerState = powerState;
return self();
}
/**
* @see Server#getInstanceName()
*/
public T instanceName(String instanceName) {
this.instanceName = instanceName;
return self();
}
/**
* @see Server#getHostName()
*/
public T hostName(String hostName) {
this.hostName = hostName;
return self();
}
/**
* @see Server#getHypervisorName()
*/
public T hypervisorName(String hypervisorName) {
this.hypervisorName = hypervisorName;
public T extraAttributes(ServerExtendedAttributes extendedAttributes) {
this.extendedAttributes = extendedAttributes;
return self();
}
@ -325,18 +285,13 @@ public class Server extends Resource {
.status(in.getStatus())
.image(in.getImage())
.flavor(in.getFlavor())
.adminPass(in.getAdminPass())
.keyName(in.getKeyName())
.configDrive(in.getConfigDrive())
.addresses(in.getAddresses())
.metadata(in.getMetadata())
.taskState(in.getTaskState())
.vmState(in.getVmState())
.powerState(in.getPowerState())
.instanceName(in.getInstanceName())
.hostName(in.getHostName())
.hypervisorName(in.getHypervisorName())
.diskConfig(in.getDiskConfig());
.extendedStatus(in.getExtendedStatus().orNull())
.extraAttributes(in.getExtendedAttributes().orNull())
.diskConfig(in.getDiskConfig().orNull());
}
}
@ -347,47 +302,38 @@ public class Server extends Resource {
}
}
protected final String uuid;
private final String uuid;
@SerializedName("tenant_id")
protected final String tenantId;
private final String tenantId;
@SerializedName("user_id")
protected final String userId;
protected final Date updated;
protected final Date created;
protected final String hostId;
protected final String accessIPv4;
protected final String accessIPv6;
protected final Status status;
protected final Resource image;
protected final Resource flavor;
protected final String adminPass;
private final String userId;
private final Date updated;
private final Date created;
private final String hostId;
private final String accessIPv4;
private final String accessIPv6;
private final Status status;
private final Resource image;
private final Resource flavor;
@SerializedName("key_name")
protected final String keyName;
private final String keyName;
@SerializedName("config_drive")
protected final String configDrive;
private final String configDrive;
// TODO: get gson multimap adapter!
protected final Map<String, Set<Address>> addresses;
protected final Map<String, String> metadata;
private final Map<String, Set<Address>> addresses;
private final Map<String, String> metadata;
// Extended status extension
@SerializedName("OS-EXT-STS:task_state")
protected final String taskState;
@SerializedName("OS-EXT-STS:vm_state")
protected final String vmState;
@SerializedName("OS-EXT-STS:power_state")
protected final String powerState;
// @Prefixed("OS-EXT-STS:")
private final Optional<ServerExtendedStatus> extendedStatus;
// Extended server attributes extension
@SerializedName("OS-EXT-SRV-ATTR:instance_name")
protected final String instanceName;
@SerializedName("OS-EXT-SRV-ATTR:host")
protected final String hostName;
@SerializedName("OS-EXT-SRV-ATTR:hypervisor_hostname")
protected final String hypervisorName;
// @Prefixed("OS-EXT-SRV-ATTR:")
private final Optional<ServerExtendedAttributes> extendedAttributes;
// Disk Config extension
@SerializedName("OS-DCF:diskConfig")
protected final String diskConfig;
private final Optional<String> diskConfig;
protected Server(Builder<?> builder) {
super(builder);
@ -405,15 +351,32 @@ public class Server extends Resource {
this.flavor = checkNotNull(builder.flavor, "flavor");
this.metadata = Maps.newHashMap(builder.metadata);
this.addresses = Multimaps2.toOldSchool(ImmutableMultimap.copyOf(checkNotNull(builder.addresses, "addresses")));
this.adminPass = builder.adminPass;
this.keyName = builder.keyName;
this.taskState = builder.taskState;
this.vmState = builder.vmState;
this.powerState = builder.powerState;
this.instanceName = builder.instanceName;
this.hostName = builder.hostName;
this.hypervisorName = builder.hypervisorName;
this.diskConfig = builder.diskConfig;
this.extendedStatus = Optional.fromNullable(builder.extendedStatus);
this.extendedAttributes = Optional.fromNullable(builder.extendedAttributes);
this.diskConfig = builder.diskConfig == null ? Optional.<String>absent() : Optional.of(builder.diskConfig);
}
protected Server() {
// for GSON
this.uuid = null;
this.tenantId = null;
this.userId = null;
this.updated = null;
this.created = null;
this.hostId = null;
this.accessIPv4 = null;
this.accessIPv6 = null;
this.status = null;
this.configDrive = null;
this.image = null;
this.flavor = null;
this.metadata = ImmutableMap.of();
this.addresses = ImmutableMap.of();
this.keyName = null;
this.extendedStatus = Optional.absent();
this.extendedAttributes = Optional.absent();
this.diskConfig = Optional.absent();
}
/**
@ -489,14 +452,6 @@ public class Server extends Resource {
return Multimaps2.fromOldSchool(addresses);
}
/**
* @return the administrative password for this server; only present on first request.
*/
@Nullable
public String getAdminPass() {
return adminPass;
}
/**
* @return keyName if extension is present and there is a valur for this server
* @see KeyPairClient
@ -508,64 +463,21 @@ public class Server extends Resource {
/**
* State of task running against this instance (e.g. "suspending")
* Retrieves the extended server status fields
* <p/>
* NOTE: This field is only present if the Extended Status extension is installed.
*/
@Nullable
public String getTaskState() {
return this.taskState;
public Optional<ServerExtendedStatus> getExtendedStatus() {
return this.extendedStatus;
}
/**
* State of task running against this instance (e.g. "suspending")
* <p/>
* NOTE: This field is only present if the Extended Status extension is installed.
*/
@Nullable
public String getVmState() {
return this.vmState;
}
/**
* State of task running against this instance (e.g. "suspending")
* <p/>
* NOTE: This field is only present if the Extended Status extension is installed.
*/
@Nullable
public String getPowerState() {
return this.powerState;
}
/**
* The name of the instance?
* Retrieves the extended server attributes fields
* <p/>
* NOTE: This field is only present if the The Extended Server Attributes API extension is installed.
*/
@Nullable
public String getInstanceName() {
return this.instanceName;
}
/**
* The host name of the host this Server is running on
* <p/>
* NOTE: This field is only present if the The Extended Server Attributes API extension is installed.
* @see #getHostId()
*/
@Nullable
public String getHostName() {
return this.hostName;
}
/**
* The name of the hypervisor this Server is running on
* <p/>
* NOTE: This field is only present if the The Extended Server Attributes API extension is installed.
*/
@Nullable
public String getHypervisorName() {
return this.hypervisorName;
public Optional<ServerExtendedAttributes> getExtendedAttributes() {
return this.extendedAttributes;
}
/**
@ -573,8 +485,7 @@ public class Server extends Resource {
* <p/>
* NOTE: This field is only present if the Disk Config extension is installed.
*/
@Nullable
public String getDiskConfig() {
public Optional<String> getDiskConfig() {
return this.diskConfig;
}
@ -587,6 +498,7 @@ public class Server extends Resource {
"userId", userId).add("hostId", getHostId()).add("updated", updated).add("created", created).add(
"accessIPv4", getAccessIPv4()).add("accessIPv6", getAccessIPv6()).add("status", status).add(
"configDrive", getConfigDrive()).add("image", image).add("flavor", flavor).add("metadata", metadata)
.add("addresses", getAddresses()).add("adminPass", adminPass);
.add("addresses", getAddresses()).add("diskConfig", diskConfig)
.add("extendedStatus", extendedStatus).add("extendedAttributes", extendedAttributes);
}
}

View File

@ -0,0 +1,95 @@
/**
* 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.openstack.nova.v1_1.domain;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.openstack.domain.Resource;
import com.google.common.base.Objects.ToStringHelper;
/**
* Server Resource with administrative password returned by ServerClient#CreateServer calls
*
* @author Adam Lowe
* @see <a href=
* "http://docs.openstack.org/api/openstack-compute/1.1/content/Get_Server_Details-d1e2623.html"
* />
*/
public class ServerCreated extends Resource {
public static Builder<?> builder() {
return new ConcreteBuilder();
}
public Builder<?> toBuilder() {
return new ConcreteBuilder().fromServerCreated(this);
}
public static abstract class Builder<T extends Builder<T>> extends Resource.Builder<T> {
private String adminPass;
/**
* @see ServerCreated#getAdminPass()
*/
public T adminPass(String adminPass) {
this.adminPass = adminPass;
return self();
}
public T fromServerCreated(ServerCreated in) {
return super.fromResource(in).adminPass(in.getAdminPass());
}
public ServerCreated build() {
return new ServerCreated(this);
}
}
private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@Override
protected ConcreteBuilder self() {
return this;
}
}
private final String adminPass;
protected ServerCreated(Builder<?> builder) {
super(builder);
this.adminPass = builder.adminPass;
}
protected ServerCreated() {
this.adminPass =null;
}
/**
* @return the administrative password for this server. Note: this is not available in Server responses.
*/
public String getAdminPass() {
return adminPass;
}
// hashCode/equals from super is ok
@Override
protected ToStringHelper string() {
return super.string().add("adminPass", adminPass);
}
}

View File

@ -0,0 +1,146 @@
/**
* 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.openstack.nova.v1_1.domain;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.google.gson.annotations.SerializedName;
/**
* Additional attributes delivered by Extended Server Attributes extension
*
* @author Adam Lowe
* @see <a href=
* "http://nova.openstack.org/api/nova.api.openstack.compute.contrib.extended_server_attributes.html"
* />
*/
public class ServerExtendedAttributes {
public static final String PREFIX = "OS-EXT-SRV-ATTR:";
public static Builder<?> builder() {
return new ConcreteBuilder();
}
public Builder<?> toBuilder() {
return new ConcreteBuilder().fromServerExtraAttributes(this);
}
public static abstract class Builder<T extends Builder<T>> {
protected abstract T self();
private String instanceName;
private String hostName;
private String hypervisorHostName;
/**
* @see ServerExtendedAttributes#getInstanceName()
*/
public T instanceName(String instanceName) {
this.instanceName = instanceName;
return self();
}
/**
* @see ServerExtendedAttributes#getHostName()
*/
public T hostName(String hostName) {
this.hostName = hostName;
return self();
}
/**
* @see ServerExtendedAttributes#getHypervisorHostName()
*/
public T hypervisorHostame(String hypervisorHostName) {
this.hypervisorHostName = hypervisorHostName;
return self();
}
public ServerExtendedAttributes build() {
return new ServerExtendedAttributes(this);
}
public T fromServerExtraAttributes(ServerExtendedAttributes in) {
return this
.instanceName(in.getInstanceName())
.hostName(in.getHostName())
.hypervisorHostame(in.getHypervisorHostName());
}
}
private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@Override
protected ConcreteBuilder self() {
return this;
}
}
@SerializedName(value=PREFIX + "instance_name")
private final String instanceName;
@SerializedName(value=PREFIX + "host")
private final String hostName;
@SerializedName(value=PREFIX + "hypervisor_hostname")
private final String hypervisorHostName;
protected ServerExtendedAttributes(Builder<?> builder) {
this.instanceName = builder.instanceName;
this.hostName = builder.hostName;
this.hypervisorHostName = builder.hypervisorHostName;
}
public String getInstanceName() {
return this.instanceName;
}
public String getHostName() {
return this.hostName;
}
public String getHypervisorHostName() {
return this.hypervisorHostName;
}
@Override
public int hashCode() {
return Objects.hashCode(instanceName, hostName, hypervisorHostName);
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
ServerExtendedAttributes that = ServerExtendedAttributes.class.cast(obj);
return Objects.equal(this.instanceName, that.instanceName)
&& Objects.equal(this.hostName, that.hostName)
&& Objects.equal(this.hypervisorHostName, that.hypervisorHostName);
}
protected ToStringHelper string() {
return Objects.toStringHelper("")
.add("instanceName", instanceName)
.add("hostName", hostName)
.add("hypervisorHostName", hypervisorHostName);
}
@Override
public String toString() {
return string().toString();
}
}

View File

@ -0,0 +1,158 @@
/**
* 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.openstack.nova.v1_1.domain;
import org.jclouds.javax.annotation.Nullable;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.google.gson.annotations.SerializedName;
/**
* Additional attributes delivered by Extended Server Status extension
*
* @author Adam Lowe
* @see <a href=
* "http://nova.openstack.org/api/nova.api.openstack.compute.contrib.extended_status.html"
* />
*/
public class ServerExtendedStatus {
public static final String PREFIX = "OS-EXT-STS:";
public static Builder<?> builder() {
return new ConcreteBuilder();
}
public Builder<?> toBuilder() {
return new ConcreteBuilder().fromServerExtendedStatus(this);
}
public static abstract class Builder<T extends Builder<T>> {
protected abstract T self();
private String taskState;
private String vmState;
private int powerState = Integer.MIN_VALUE;
/**
* @see ServerExtendedStatus#getTaskState()
*/
public T taskState(String taskState) {
this.taskState = taskState;
return self();
}
/**
* @see ServerExtendedStatus#getVmState()
*/
public T vmState(String vmState) {
this.vmState = vmState;
return self();
}
/**
* @see ServerExtendedStatus#getPowerState()
*/
public T powerState(int powerState) {
this.powerState = powerState;
return self();
}
public ServerExtendedStatus build() {
return new ServerExtendedStatus(this);
}
public T fromServerExtendedStatus(ServerExtendedStatus in) {
return this
.taskState(in.getTaskState())
.vmState(in.getVmState())
.powerState(in.getPowerState());
}
}
private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@Override
protected ConcreteBuilder self() {
return this;
}
}
@SerializedName(value=PREFIX + "task_state")
private final String taskState;
@SerializedName(value=PREFIX + "vm_state")
private final String vmState;
@SerializedName(value=PREFIX + "power_state")
private final int powerState;
protected ServerExtendedStatus(Builder<?> builder) {
this.taskState = builder.taskState;
this.vmState = builder.vmState;
this.powerState = builder.powerState;
}
protected ServerExtendedStatus() {
this.taskState = null;
this.vmState = null;
this.powerState = Integer.MIN_VALUE;
}
@Nullable
public String getTaskState() {
return this.taskState;
}
@Nullable
public String getVmState() {
return this.vmState;
}
public int getPowerState() {
return this.powerState;
}
@Override
public int hashCode() {
return Objects.hashCode(taskState, vmState, powerState);
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
ServerExtendedStatus that = ServerExtendedStatus.class.cast(obj);
return Objects.equal(this.taskState, that.taskState)
&& Objects.equal(this.vmState, that.vmState)
&& Objects.equal(this.powerState, that.powerState)
;
}
protected ToStringHelper string() {
return Objects.toStringHelper("")
.add("taskState", taskState)
.add("vmState", vmState)
.add("powerState", powerState)
;
}
@Override
public String toString() {
return string().toString();
}
}

View File

@ -32,6 +32,7 @@ import javax.ws.rs.core.MediaType;
import org.jclouds.openstack.domain.Resource;
import org.jclouds.openstack.filters.AuthenticateRequest;
import org.jclouds.openstack.nova.v1_1.domain.ServerCreated;
import org.jclouds.openstack.nova.v1_1.domain.RebootType;
import org.jclouds.openstack.nova.v1_1.domain.Server;
import org.jclouds.openstack.nova.v1_1.functions.ParseImageIdFromLocationHeader;
@ -154,7 +155,7 @@ public interface ServerAsyncClient {
@Consumes(MediaType.APPLICATION_JSON)
@Path("/servers")
@MapBinder(CreateServerOptions.class)
ListenableFuture<Server> createServer(@PayloadParam("name") String name, @PayloadParam("imageRef") String imageRef,
ListenableFuture<ServerCreated> createServer(@PayloadParam("name") String name, @PayloadParam("imageRef") String imageRef,
@PayloadParam("flavorRef") String flavorRef, CreateServerOptions... options);
/**

View File

@ -23,6 +23,7 @@ import java.util.concurrent.TimeUnit;
import org.jclouds.concurrent.Timeout;
import org.jclouds.openstack.domain.Resource;
import org.jclouds.openstack.nova.v1_1.domain.ServerCreated;
import org.jclouds.openstack.nova.v1_1.domain.RebootType;
import org.jclouds.openstack.nova.v1_1.domain.Server;
import org.jclouds.openstack.nova.v1_1.options.CreateServerOptions;
@ -80,7 +81,7 @@ public interface ServerClient {
*/
// blocking call
@Timeout(duration = 10, timeUnit = TimeUnit.MINUTES)
Server createServer(String name, String imageRef, String flavorRef, CreateServerOptions... options);
ServerCreated createServer(String name, String imageRef, String flavorRef, CreateServerOptions... options);
/**
* Terminate and delete a server.

View File

@ -64,7 +64,7 @@ public class OrphanedGroupsByZoneIdTest {
@Test
public void testWhenComputeServiceSaysAllNodesAreDeadBothGroupsAreReturned() {
ServerInZone withoutHost = new ServerInZone(new ParseCreatedServerTest().expected(), "az-1.region-a.geo-1");
ServerInZone withoutHost = new ServerInZone(new ServerInZoneToNodeMetadataTest().expectedServer(), "az-1.region-a.geo-1");
ServerInZone withHost = new ServerInZone(new ParseServerTest().expected(), "az-1.region-a.geo-1");
ServerInZoneToNodeMetadata converter = new ServerInZoneToNodeMetadata(locationIndex, Suppliers
@ -80,7 +80,7 @@ public class OrphanedGroupsByZoneIdTest {
@Test
public void testWhenComputeServiceSaysAllNodesAreDeadNoGroupsAreReturned() {
ServerInZone withoutHost = new ServerInZone(new ParseCreatedServerTest().expected(), "az-1.region-a.geo-1");
ServerInZone withoutHost = new ServerInZone(new ServerInZoneToNodeMetadataTest().expectedServer(), "az-1.region-a.geo-1");
ServerInZone withHost = new ServerInZone(new ParseServerTest().expected(), "az-1.region-a.geo-1");
ServerInZoneToNodeMetadata converter = new ServerInZoneToNodeMetadata(locationIndex, Suppliers

View File

@ -21,6 +21,7 @@ package org.jclouds.openstack.nova.v1_1.compute.functions;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import java.net.URI;
import java.util.Map;
import java.util.Set;
@ -32,9 +33,12 @@ import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.OperatingSystem;
import org.jclouds.compute.domain.OsFamily;
import org.jclouds.compute.functions.GroupNamingConvention;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.domain.Location;
import org.jclouds.domain.LocationBuilder;
import org.jclouds.domain.LocationScope;
import org.jclouds.openstack.domain.Link;
import org.jclouds.openstack.domain.Resource;
import org.jclouds.openstack.nova.v1_1.domain.Server;
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ServerInZone;
import org.jclouds.openstack.nova.v1_1.parse.ParseCreatedServerTest;
@ -144,7 +148,7 @@ public class ServerInZoneToNodeMetadataTest {
Set<Image> images = ImmutableSet.<Image> of();
Set<Hardware> hardwares = ImmutableSet.<Hardware> of();
Server serverToConvert = new ParseCreatedServerTest().expected();
Server serverToConvert = expectedServer();
ServerInZone serverInZoneToConvert = new ServerInZone(serverToConvert, "az-1.region-a.geo-1");
@ -160,4 +164,39 @@ public class ServerInZoneToNodeMetadataTest {
assertEquals(convertedNodeMetadata.getLocation(), zone);
}
public Server expectedServer() {
return Server
.builder()
.id("71752")
.uuid("47491020-6a78-4f63-9475-23195ac4515c")
.tenantId("37936628937291")
.userId("54297837463082")
.name("test-e92")
.updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-03-19T06:21:13Z"))
.created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-03-19T06:21:13Z"))
.status(Server.Status.BUILD)
.image(
Resource
.builder()
.id("1241")
.links(
Link.create(
Link.Relation.BOOKMARK,
URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/37936628937291/images/1241")))
.build())
.flavor(
Resource
.builder()
.id("100")
.links(
Link.create(
Link.Relation.BOOKMARK,
URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/37936628937291/flavors/100")))
.build())
.links(
Link.create(Link.Relation.SELF, URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/37936628937291/servers/71752")),
Link.create(Link.Relation.BOOKMARK, URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/37936628937291/servers/71752"))).build();
}
}

View File

@ -26,7 +26,7 @@ import static org.testng.Assert.fail;
import org.jclouds.http.HttpResponseException;
import org.jclouds.openstack.nova.v1_1.domain.BackupType;
import org.jclouds.openstack.nova.v1_1.domain.Image;
import org.jclouds.openstack.nova.v1_1.domain.Server;
import org.jclouds.openstack.nova.v1_1.domain.Server.Status;
import org.jclouds.openstack.nova.v1_1.features.ExtensionClient;
import org.jclouds.openstack.nova.v1_1.features.ImageClient;
import org.jclouds.openstack.nova.v1_1.features.ServerClient;
@ -89,7 +89,7 @@ public class AdminActionsClientLiveTest extends BaseNovaClientLiveTest {
@AfterMethod(alwaysRun = true)
public void ensureServerIsActiveAgain() {
blockUntilServerInState(testServerId, serverClient, Server.Status.ACTIVE);
blockUntilServerInState(testServerId, serverClient, Status.ACTIVE);
}
public void testSuspendAndResume() {
@ -103,14 +103,14 @@ public class AdminActionsClientLiveTest extends BaseNovaClientLiveTest {
} catch (HttpResponseException e) {
}
assertTrue(client.suspendServer(testServerId));
blockUntilServerInState(testServerId, serverClient, Server.Status.SUSPENDED);
blockUntilServerInState(testServerId, serverClient, Status.SUSPENDED);
try {
client.suspendServer(testServerId);
fail("Suspended an already suspended server!");
} catch (HttpResponseException e) {
}
assertTrue(client.resumeServer(testServerId));
blockUntilServerInState(testServerId, serverClient, Server.Status.ACTIVE);
blockUntilServerInState(testServerId, serverClient, Status.ACTIVE);
try {
client.resumeServer(testServerId);
fail("Resumed an already resumed server!");
@ -153,14 +153,14 @@ public class AdminActionsClientLiveTest extends BaseNovaClientLiveTest {
} catch (HttpResponseException e) {
}
assertTrue(client.pauseServer(testServerId));
blockUntilServerInState(testServerId, serverClient, Server.Status.PAUSED);
blockUntilServerInState(testServerId, serverClient, Status.PAUSED);
try {
client.pauseServer(testServerId);
fail("paused a paused server!");
} catch (HttpResponseException e) {
}
assertTrue(client.unpauseServer(testServerId));
blockUntilServerInState(testServerId, serverClient, Server.Status.ACTIVE);
blockUntilServerInState(testServerId, serverClient, Status.ACTIVE);
try {
client.unpauseServer(testServerId);
fail("Unpaused a server we just unpaused!");
@ -182,7 +182,7 @@ public class AdminActionsClientLiveTest extends BaseNovaClientLiveTest {
Thread.sleep(30000);
}
blockUntilServerInState(testServerId, serverClient, Server.Status.ACTIVE);
blockUntilServerInState(testServerId, serverClient, Status.ACTIVE);
Image backupImage = imageClient.getImage(backupImageId);
assertEquals(backupImage.getId(), backupImageId);

View File

@ -26,6 +26,7 @@ import org.jclouds.openstack.nova.v1_1.NovaAsyncClient;
import org.jclouds.openstack.nova.v1_1.NovaClient;
import org.jclouds.openstack.nova.v1_1.config.NovaProperties;
import org.jclouds.openstack.nova.v1_1.domain.Flavor;
import org.jclouds.openstack.nova.v1_1.domain.ServerCreated;
import org.jclouds.openstack.nova.v1_1.domain.Server;
import org.jclouds.openstack.nova.v1_1.domain.Server.Status;
import org.jclouds.openstack.nova.v1_1.features.FlavorClient;
@ -78,9 +79,9 @@ public class BaseNovaClientLiveTest extends BaseComputeServiceContextLiveTest {
protected Server createServerInZone(String zoneId) {
ServerClient serverClient = novaContext.getApi().getServerClientForZone(zoneId);
Server server = serverClient.createServer("test", imageIdForZone(zoneId), flavorRefForZone(zoneId));
ServerCreated server = serverClient.createServer("test", imageIdForZone(zoneId), flavorRefForZone(zoneId));
blockUntilServerInState(server.getId(), serverClient, Status.ACTIVE);
return server;
return serverClient.getServer(server.getId());
}
/**
@ -89,7 +90,9 @@ public class BaseNovaClientLiveTest extends BaseComputeServiceContextLiveTest {
*/
protected void blockUntilServerInState(String serverId, ServerClient client, Status status) {
Server currentDetails = null;
for (currentDetails = client.getServer(serverId); currentDetails.getStatus() != status || currentDetails.getTaskState() != null; currentDetails = client
for (currentDetails = client.getServer(serverId); currentDetails.getStatus() != status ||
(currentDetails.getExtendedStatus().isPresent() && currentDetails.getExtendedStatus().get().getTaskState() != null);
currentDetails = client
.getServer(serverId)) {
System.out.printf("blocking on status %s%n%s%n", status, currentDetails);
try {

View File

@ -27,11 +27,12 @@ import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.json.BaseItemParserTest;
import org.jclouds.json.config.GsonModule;
import org.jclouds.openstack.domain.Link;
import org.jclouds.openstack.domain.Resource;
import org.jclouds.openstack.domain.Link.Relation;
import org.jclouds.openstack.domain.Resource;
import org.jclouds.openstack.nova.v1_1.config.NovaParserModule;
import org.jclouds.openstack.nova.v1_1.domain.Server;
import org.jclouds.openstack.nova.v1_1.domain.Server.Status;
import org.jclouds.openstack.nova.v1_1.domain.ServerCreated;
import org.jclouds.rest.annotations.SelectJson;
import org.testng.annotations.Test;
@ -42,7 +43,7 @@ import com.google.inject.Injector;
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "ParseCreatedServerTest")
public class ParseCreatedServerTest extends BaseItemParserTest<Server> {
public class ParseCreatedServerTest extends BaseItemParserTest<ServerCreated> {
@Override
public String resource() {
@ -52,36 +53,12 @@ public class ParseCreatedServerTest extends BaseItemParserTest<Server> {
@Override
@SelectJson("server")
@Consumes(MediaType.APPLICATION_JSON)
public Server expected() {
return Server
public ServerCreated expected() {
return ServerCreated
.builder()
.id("71752")
.uuid("47491020-6a78-4f63-9475-23195ac4515c")
.tenantId("37936628937291")
.userId("54297837463082")
.name("test-e92")
.updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-03-19T06:21:13Z"))
.created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-03-19T06:21:13Z"))
.status(Status.BUILD)
.adminPass("ZWuHcmTMQ7eXoHeM")
.image(
Resource
.builder()
.id("1241")
.links(
Link.create(
Relation.BOOKMARK,
URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/37936628937291/images/1241")))
.build())
.flavor(
Resource
.builder()
.id("100")
.links(
Link.create(
Relation.BOOKMARK,
URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/37936628937291/flavors/100")))
.build())
.links(
Link.create(Relation.SELF, URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/37936628937291/servers/71752")),
Link.create(Relation.BOOKMARK, URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/37936628937291/servers/71752"))).build();

View File

@ -33,6 +33,8 @@ import org.jclouds.openstack.domain.Link.Relation;
import org.jclouds.openstack.nova.v1_1.config.NovaParserModule;
import org.jclouds.openstack.nova.v1_1.domain.Address;
import org.jclouds.openstack.nova.v1_1.domain.Server;
import org.jclouds.openstack.nova.v1_1.domain.Server.Status;
import org.jclouds.openstack.nova.v1_1.domain.ServerExtendedStatus;
import org.jclouds.rest.annotations.SelectJson;
import org.testng.annotations.Test;
@ -84,12 +86,14 @@ public class ParseServerDetailsEssexTest extends BaseSetParserTest<Server> {
URI.create("http://nova:8774/8d10e6646d5d4585937395b04839a353/flavors/1"))).build())
.id("0c80b392-db30-4736-ae02-4480090f1207")
.userId("df13814f6c354d00a8acf66502836323")
.status(Server.Status.ACTIVE)
.status(Status.ACTIVE)
.updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-04-12T11:21:33Z"))
.hostId("03d796ebb52b1b555e5f6d9262f7dbd52b3f7c181e3aa89b34ca5408")
.name("VM proxy")
.created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-04-12T11:21:23Z"))
.tenantId("8d10e6646d5d4585937395b04839a353").build(),
.tenantId("8d10e6646d5d4585937395b04839a353")
.extendedStatus(ServerExtendedStatus.builder().vmState("active").powerState(1).build())
.diskConfig("MANUAL").build(),
Server.builder()
.addresses(ImmutableMultimap.<String, Address>builder()
.putAll("Net TenantA Front-Middle", Address.createV4("172.16.11.4"))
@ -117,12 +121,14 @@ public class ParseServerDetailsEssexTest extends BaseSetParserTest<Server> {
URI.create("http://nova:8774/8d10e6646d5d4585937395b04839a353/flavors/1"))).build())
.id("b332b5cd-535e-4677-b68e-fc8badc13236")
.userId("df13814f6c354d00a8acf66502836323")
.status(Server.Status.ACTIVE)
.status(Status.ACTIVE)
.updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-04-12T11:18:58Z"))
.hostId("e5bbff80bebacfe1db63951e787b5341427060a602d33abfefb6a1bc")
.name("VM blog")
.created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-04-12T11:18:48Z"))
.tenantId("8d10e6646d5d4585937395b04839a353").build(),
.tenantId("8d10e6646d5d4585937395b04839a353")
.extendedStatus(ServerExtendedStatus.builder().vmState("active").powerState(1).build())
.diskConfig("MANUAL").build(),
Server.builder()
.addresses(ImmutableMultimap.<String, Address>builder()
.putAll("Net TenantA Middle-Back", Address.createV4("172.16.12.4")).build())
@ -149,12 +155,14 @@ public class ParseServerDetailsEssexTest extends BaseSetParserTest<Server> {
URI.create("http://nova:8774/8d10e6646d5d4585937395b04839a353/flavors/1"))).build())
.id("f9d43436-4572-4c9b-9b74-5fa6890a2f21")
.userId("df13814f6c354d00a8acf66502836323")
.status(Server.Status.ACTIVE)
.status(Status.ACTIVE)
.updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-04-12T11:15:09Z"))
.hostId("03d796ebb52b1b555e5f6d9262f7dbd52b3f7c181e3aa89b34ca5408")
.name("VM MySQL")
.created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-04-12T11:14:56Z"))
.tenantId("8d10e6646d5d4585937395b04839a353").build());
.tenantId("8d10e6646d5d4585937395b04839a353")
.extendedStatus(ServerExtendedStatus.builder().vmState("active").powerState(1).build())
.diskConfig("MANUAL").build());
}

View File

@ -0,0 +1,106 @@
/**
* 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.openstack.nova.v1_1.parse;
import java.net.URI;
import javax.ws.rs.Consumes;
import javax.ws.rs.core.MediaType;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.json.BaseItemParserTest;
import org.jclouds.json.config.GsonModule;
import org.jclouds.openstack.domain.Link;
import org.jclouds.openstack.domain.Link.Relation;
import org.jclouds.openstack.domain.Resource;
import org.jclouds.openstack.nova.v1_1.config.NovaParserModule;
import org.jclouds.openstack.nova.v1_1.domain.Address;
import org.jclouds.openstack.nova.v1_1.domain.Server;
import org.jclouds.openstack.nova.v1_1.domain.Server.Status;
import org.jclouds.openstack.nova.v1_1.domain.ServerExtendedAttributes;
import org.jclouds.openstack.nova.v1_1.domain.ServerExtendedStatus;
import org.jclouds.rest.annotations.SelectJson;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMultimap;
import com.google.inject.Guice;
import com.google.inject.Injector;
/**
* @author Adam Lowe
*/
@Test(groups = "unit", testName = "ParseServerWithAllExtensionsTest")
public class ParseServerWithAllExtensionsTest extends BaseItemParserTest<Server> {
@Override
public String resource() {
return "/server_details_devstack.json";
}
@Override
@SelectJson("server")
@Consumes(MediaType.APPLICATION_JSON)
public Server expected() {
return Server
.builder()
.id("141b775f-7ac1-45f0-9a95-146260f33a53")
.tenantId("7f312675f9b84c97bff8f5054e181419")
.userId("89c01b67395d4bea945f7f5bfd7f344a")
.name("test")
.updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-04T15:07:48Z"))
.created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-04T15:07:36Z"))
.hostId("eab9a77d1c44b8833e4a3dc6d2d9d50de556e780a319f184d8c82d9b")
.status(Status.PAUSED)
.image(
Resource
.builder()
.id("8e6f5bc4-a210-45b2-841f-c510eae14300")
.links(
Link.create(
Relation.BOOKMARK,
URI.create("http://172.16.89.149:8774/7f312675f9b84c97bff8f5054e181419/images/8e6f5bc4-a210-45b2-841f-c510eae14300")))
.build())
.flavor(
Resource
.builder()
.id("1")
.links(
Link.create(
Relation.BOOKMARK,
URI.create("http://172.16.89.149:8774/7f312675f9b84c97bff8f5054e181419/flavors/1")))
.build())
.links(
Link.create(
Relation.SELF,
URI.create("http://172.16.89.149:8774/v2/7f312675f9b84c97bff8f5054e181419/servers/141b775f-7ac1-45f0-9a95-146260f33a53")),
Link.create(
Relation.BOOKMARK,
URI.create("http://172.16.89.149:8774/7f312675f9b84c97bff8f5054e181419/servers/141b775f-7ac1-45f0-9a95-146260f33a53")))
.addresses(ImmutableMultimap.of("private", Address.createV4("10.0.0.8")))
.diskConfig("MANUAL")
.extendedStatus(ServerExtendedStatus.builder().vmState("paused").powerState(3).build())
.extraAttributes(ServerExtendedAttributes.builder().instanceName("instance-00000014").hostName("ubuntu").build())
.build();
}
protected Injector injector() {
return Guice.createInjector(new NovaParserModule(), new GsonModule());
}
}

View File

@ -0,0 +1,44 @@
{"server": {
"OS-EXT-STS:task_state": null,
"addresses": {
"private": [{"version": 4, "addr": "10.0.0.8"}]
},
"links":
[
{
"href": "http://172.16.89.149:8774/v2/7f312675f9b84c97bff8f5054e181419/servers/141b775f-7ac1-45f0-9a95-146260f33a53", "rel": "self"
},
{
"href": "http://172.16.89.149:8774/7f312675f9b84c97bff8f5054e181419/servers/141b775f-7ac1-45f0-9a95-146260f33a53", "rel": "bookmark"
}
],
"image": {
"id": "8e6f5bc4-a210-45b2-841f-c510eae14300", "links": [
{
"href": "http://172.16.89.149:8774/7f312675f9b84c97bff8f5054e181419/images/8e6f5bc4-a210-45b2-841f-c510eae14300", "rel": "bookmark"
}]
},
"OS-EXT-STS:vm_state": "paused",
"OS-EXT-SRV-ATTR:instance_name": "instance-00000014",
"flavor": {
"id": "1",
"links": [{"href": "http://172.16.89.149:8774/7f312675f9b84c97bff8f5054e181419/flavors/1", "rel": "bookmark"}]
},
"id": "141b775f-7ac1-45f0-9a95-146260f33a53",
"user_id": "89c01b67395d4bea945f7f5bfd7f344a",
"OS-DCF:diskConfig": "MANUAL",
"accessIPv4": "",
"accessIPv6": "",
"OS-EXT-STS:power_state": 3,
"config_drive": "",
"status": "PAUSED",
"updated": "2012-05-04T15:07:48Z",
"hostId": "eab9a77d1c44b8833e4a3dc6d2d9d50de556e780a319f184d8c82d9b",
"OS-EXT-SRV-ATTR:host": "ubuntu",
"key_name": "",
"OS-EXT-SRV-ATTR:hypervisor_hostname": null,
"name": "test",
"created": "2012-05-04T15:07:36Z",
"tenant_id": "7f312675f9b84c97bff8f5054e181419",
"metadata": {}
}}

View File

@ -116,6 +116,12 @@ public class Resource implements Comparable<Resource> {
this.links = ImmutableSet.copyOf(checkNotNull(builder.links, "links"));
}
protected Resource() {
this.id = null;
this.name = null;
this.links = ImmutableSet.of();
}
/**
* When providing an ID, it is assumed that the resource exists in the current OpenStack
* deployment

View File

@ -37,6 +37,7 @@ import org.jclouds.json.Json;
import org.jclouds.json.internal.EnumTypeAdapterThatReturnsFromValue;
import org.jclouds.json.internal.GsonWrapper;
import org.jclouds.json.internal.NullHackJsonLiteralAdapter;
import org.jclouds.json.internal.OptionalTypeAdapterFactory;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
@ -75,6 +76,7 @@ public class GsonModule extends AbstractModule {
}.getType(), byteListAdapter.nullSafe());
builder.registerTypeAdapter(byte[].class, byteArrayAdapter.nullSafe());
builder.registerTypeAdapter(JsonBall.class, jsonAdapter.nullSafe());
builder.registerTypeAdapterFactory(new OptionalTypeAdapterFactory());
// complicated (serializers/deserializers as they need context to operate)
builder.registerTypeHierarchyAdapter(Enum.class, new EnumTypeAdapterThatReturnsFromValue());

View File

@ -0,0 +1,79 @@
/**
* 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.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import com.google.common.base.Optional;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
/**
* Writes and reads Optional values as JSON
*
* @author Adam Lowe
*/
public class OptionalTypeAdapterFactory implements TypeAdapterFactory {
@SuppressWarnings("unchecked")
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType();
if (typeToken.getRawType() != Optional.class || !(type instanceof ParameterizedType)) {
return null;
}
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
return (TypeAdapter<T>) newMultisetAdapter(elementAdapter);
}
private <E> TypeAdapter<Optional<E>> newMultisetAdapter(
final TypeAdapter<E> elementAdapter) {
return new TypeAdapter<Optional<E>>() {
public void write(JsonWriter out, Optional<E> value) throws IOException {
if (!value.isPresent()) {
out.nullValue();
return;
}
elementAdapter.write(out, value.get());
}
public Optional<E> read(JsonReader in) throws IOException {
Optional<E> result = Optional.absent();
if (in.peek() == JsonToken.NULL) {
in.nextNull();
} else {
E element = elementAdapter.read(in);
if (element != null) {
result = Optional.of(element);
}
}
return result;
}
};
}
}

View File

@ -0,0 +1,130 @@
/**
* 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 static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import org.testng.annotations.Test;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/**
* Tests to verify and illustrate the behavior of the OptionalTypeAdaptorFactory.
*
* @author Adam Lowe
*/
@Test(testName = "OptionalTypeAdapterFactoryTest")
public class OptionalTypeAdapterFactoryTest {
/**
* Simple type with an Optional field
*/
static class SimpleBean {
private Optional<String> value;
private final String someOtherValue;
public SimpleBean(Optional<String> value, String someOtherValue) {
this.value = value;
this.someOtherValue = someOtherValue;
}
// Required to ensure GSON doesn't initialize our Optional to null!
private SimpleBean() {
this.value = Optional.absent();
this.someOtherValue = null;
}
public Optional<String> getValue() {
return value;
}
public String getSomeOtherValue() {
return someOtherValue;
}
@Override
public int hashCode() {
return Objects.hashCode(value, someOtherValue);
}
@Override
public boolean equals(Object that) {
if (that == null || that.getClass() != getClass())
return false;
SimpleBean other = (SimpleBean) that;
return Objects.equal(value, other.value) && Objects.equal(someOtherValue, other.someOtherValue);
}
@Override
public String toString() {
return Objects.toStringHelper("SimpleBean").add("value", value).add("someOtherValue", someOtherValue).toString();
}
}
// register the type adapter so that gson can serialize/deserialize to it
private Gson gsonAdapter = new GsonBuilder().registerTypeAdapterFactory(new OptionalTypeAdapterFactory()).create();
public void testValuePresent() {
String json = "{\"value\":\"a test string!\"}";
SimpleBean expected = new SimpleBean(Optional.of("a test string!"), null);
SimpleBean actual = gsonAdapter.fromJson(json, SimpleBean.class);
assertTrue(actual.value.isPresent());
assertEquals(actual.getValue().get(), "a test string!");
assertNull(actual.getSomeOtherValue());
assertEquals(actual, expected);
assertEquals(gsonAdapter.toJson(actual), json);
}
public void testValueAbsent() {
String json = "{\"someOtherValue\":\"testvalue\"}";
SimpleBean expected = new SimpleBean(Optional.<String>absent(), "testvalue");
SimpleBean actual = gsonAdapter.fromJson(json, SimpleBean.class);
assertFalse(actual.value.isPresent());
assertEquals(actual.getSomeOtherValue(), "testvalue");
assertEquals(actual, expected);
assertEquals(gsonAdapter.toJson(actual), json);
}
public void testValueNull() {
String json = "{\"value\":null}";
SimpleBean expected = new SimpleBean(Optional.<String>absent(), null);
SimpleBean actual = gsonAdapter.fromJson(json, SimpleBean.class);
assertFalse(actual.value.isPresent());
assertNull(actual.getSomeOtherValue());
assertEquals(actual, expected);
// Note: the field is omitted from serialized form!
assertEquals(gsonAdapter.toJson(actual), "{}");
}
}