added profile to cloudsigma impl

This commit is contained in:
Adrian Cole 2011-01-26 10:53:59 -08:00
parent f1288fed4c
commit 722d81a90d
12 changed files with 603 additions and 4 deletions

View File

@ -35,9 +35,11 @@ import org.jclouds.cloudsigma.binders.BindServerToPlainTextString;
import org.jclouds.cloudsigma.domain.Drive; import org.jclouds.cloudsigma.domain.Drive;
import org.jclouds.cloudsigma.domain.DriveData; import org.jclouds.cloudsigma.domain.DriveData;
import org.jclouds.cloudsigma.domain.DriveInfo; import org.jclouds.cloudsigma.domain.DriveInfo;
import org.jclouds.cloudsigma.domain.ProfileInfo;
import org.jclouds.cloudsigma.domain.Server; import org.jclouds.cloudsigma.domain.Server;
import org.jclouds.cloudsigma.domain.ServerInfo; import org.jclouds.cloudsigma.domain.ServerInfo;
import org.jclouds.cloudsigma.functions.KeyValuesDelimitedByBlankLinesToDriveInfo; import org.jclouds.cloudsigma.functions.KeyValuesDelimitedByBlankLinesToDriveInfo;
import org.jclouds.cloudsigma.functions.KeyValuesDelimitedByBlankLinesToProfileInfo;
import org.jclouds.cloudsigma.functions.KeyValuesDelimitedByBlankLinesToServerInfo; import org.jclouds.cloudsigma.functions.KeyValuesDelimitedByBlankLinesToServerInfo;
import org.jclouds.cloudsigma.functions.ListOfKeyValuesDelimitedByBlankLinesToDriveInfoSet; import org.jclouds.cloudsigma.functions.ListOfKeyValuesDelimitedByBlankLinesToDriveInfoSet;
import org.jclouds.cloudsigma.functions.ListOfKeyValuesDelimitedByBlankLinesToServerInfoSet; import org.jclouds.cloudsigma.functions.ListOfKeyValuesDelimitedByBlankLinesToServerInfoSet;
@ -101,13 +103,22 @@ public interface CloudSigmaAsyncClient {
ListenableFuture<DriveInfo> cloneDrive(@PathParam("uuid") String sourceUuid, ListenableFuture<DriveInfo> cloneDrive(@PathParam("uuid") String sourceUuid,
@MapPayloadParam("name") String newName, CloneDriveOptions... options); @MapPayloadParam("name") String newName, CloneDriveOptions... options);
/**
* @see CloudSigmaClient#getProfileInfo
*/
@GET
@Path("/profile/info")
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
@ResponseParser(KeyValuesDelimitedByBlankLinesToProfileInfo.class)
ListenableFuture<ProfileInfo> getProfileInfo();
/** /**
* @see CloudSigmaClient#listDriveInfo * @see CloudSigmaClient#listDriveInfo
*/ */
@GET @GET
@Path("/drives/info") @Path("/drives/info")
@ResponseParser(ListOfKeyValuesDelimitedByBlankLinesToDriveInfoSet.class) @ResponseParser(ListOfKeyValuesDelimitedByBlankLinesToDriveInfoSet.class)
ListenableFuture<Set<org.jclouds.cloudsigma.domain.DriveInfo>> listDriveInfo(); ListenableFuture<Set<DriveInfo>> listDriveInfo();
/** /**
* @see CloudSigmaClient#getDriveInfo * @see CloudSigmaClient#getDriveInfo
@ -143,8 +154,7 @@ public interface CloudSigmaAsyncClient {
@ExceptionParser(ReturnNullOnNotFoundOr404.class) @ExceptionParser(ReturnNullOnNotFoundOr404.class)
@ResponseParser(KeyValuesDelimitedByBlankLinesToServerInfo.class) @ResponseParser(KeyValuesDelimitedByBlankLinesToServerInfo.class)
@Path("/servers/create") @Path("/servers/create")
ListenableFuture<ServerInfo> createServer( ListenableFuture<ServerInfo> createServer(@BinderParam(BindServerToPlainTextString.class) Server createServer);
@BinderParam(BindServerToPlainTextString.class) Server createServer);
/** /**
* @see CloudSigmaClient#listServerInfo * @see CloudSigmaClient#listServerInfo

View File

@ -25,6 +25,7 @@ import java.util.concurrent.TimeUnit;
import org.jclouds.cloudsigma.domain.Drive; import org.jclouds.cloudsigma.domain.Drive;
import org.jclouds.cloudsigma.domain.DriveData; import org.jclouds.cloudsigma.domain.DriveData;
import org.jclouds.cloudsigma.domain.DriveInfo; import org.jclouds.cloudsigma.domain.DriveInfo;
import org.jclouds.cloudsigma.domain.ProfileInfo;
import org.jclouds.cloudsigma.domain.Server; import org.jclouds.cloudsigma.domain.Server;
import org.jclouds.cloudsigma.domain.ServerInfo; import org.jclouds.cloudsigma.domain.ServerInfo;
import org.jclouds.cloudsigma.options.CloneDriveOptions; import org.jclouds.cloudsigma.options.CloneDriveOptions;
@ -40,6 +41,14 @@ import org.jclouds.concurrent.Timeout;
*/ */
@Timeout(duration = 60, timeUnit = TimeUnit.SECONDS) @Timeout(duration = 60, timeUnit = TimeUnit.SECONDS)
public interface CloudSigmaClient { public interface CloudSigmaClient {
/**
* Get profile info
*
* @return info or null, if not found
*/
ProfileInfo getProfileInfo();
/** /**
* list of server uuids in your account * list of server uuids in your account
* *

View File

@ -0,0 +1,198 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.cloudsigma.domain;
import static com.google.common.base.Preconditions.checkNotNull;
/**
*
* @author Adrian Cole
*/
public class ProfileInfo {
public static class Builder {
protected String uuid;
protected String email;
protected String firstName;
protected String lastName;
protected String nickName;
protected ProfileType type = ProfileType.REGULAR;
public Builder uuid(String uuid) {
this.uuid = uuid;
return this;
}
public Builder email(String email) {
this.email = email;
return this;
}
public Builder firstName(String firstName) {
this.firstName = firstName;
return this;
}
public Builder lastName(String lastName) {
this.lastName = lastName;
return this;
}
public Builder nickName(String nickName) {
this.nickName = nickName;
return this;
}
public Builder type(ProfileType type) {
this.type = type;
return this;
}
public ProfileInfo build() {
return new ProfileInfo(uuid, email, firstName, lastName, nickName, type);
}
}
protected final String uuid;
protected final String email;
protected final String firstName;
protected final String lastName;
protected final String nickName;
protected final ProfileType type;
public ProfileInfo(String uuid, String email, String firstName, String lastName, String nickName, ProfileType type) {
this.uuid = checkNotNull(uuid, "uuid");
this.email = checkNotNull(email, "email");
this.firstName = checkNotNull(firstName, "firstName");
this.lastName = checkNotNull(lastName, "lastName");
this.nickName = checkNotNull(nickName, "nickName");
this.type = checkNotNull(type, "type");
}
/**
*
* @return uuid of the profile.
*/
public String getUuid() {
return uuid;
}
/**
* Checks for valid email address
*
* @return email of the profile.
*/
public String getEmail() {
return email;
}
/**
*
* @return firstName of the profile.
*/
protected String getFirstName() {
return firstName;
}
/**
*
* @return lastName of the profile.
*/
protected String getLastName() {
return lastName;
}
/**
* Used in phpBB nick name
*
* @return nickName of the profile.
*/
protected String getNickName() {
return nickName;
}
/**
*
* @return type of the profile.
*/
protected ProfileType getType() {
return type;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((email == null) ? 0 : email.hashCode());
result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());
result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());
result = prime * result + ((nickName == null) ? 0 : nickName.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ProfileInfo other = (ProfileInfo) obj;
if (email == null) {
if (other.email != null)
return false;
} else if (!email.equals(other.email))
return false;
if (firstName == null) {
if (other.firstName != null)
return false;
} else if (!firstName.equals(other.firstName))
return false;
if (lastName == null) {
if (other.lastName != null)
return false;
} else if (!lastName.equals(other.lastName))
return false;
if (nickName == null) {
if (other.nickName != null)
return false;
} else if (!nickName.equals(other.nickName))
return false;
if (type != other.type)
return false;
if (uuid == null) {
if (other.uuid != null)
return false;
} else if (!uuid.equals(other.uuid))
return false;
return true;
}
@Override
public String toString() {
return "[uuid=" + uuid + ", email=" + email + ", firstName=" + firstName + ", lastName=" + lastName
+ ", nickName=" + nickName + ", type=" + type + "]";
}
}

View File

@ -0,0 +1,47 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.cloudsigma.domain;
import static com.google.common.base.Preconditions.checkNotNull;
/**
*
* @author Adrian Cole
*/
public enum ProfileType {
REGULAR, UNRECOGNIZED;
public String value() {
return name().toLowerCase();
}
@Override
public String toString() {
return value();
}
public static ProfileType fromValue(String type) {
try {
return valueOf(checkNotNull(type, "type").toUpperCase());
} catch (IllegalArgumentException e) {
return UNRECOGNIZED;
}
}
}

View File

@ -0,0 +1,53 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.cloudsigma.functions;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.cloudsigma.domain.ProfileInfo;
import org.jclouds.http.HttpResponse;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
/**
*
* @author Adrian Cole
*/
@Singleton
public class KeyValuesDelimitedByBlankLinesToProfileInfo implements Function<HttpResponse, ProfileInfo> {
private final ListOfKeyValuesDelimitedByBlankLinesToProfileInfoSet setParser;
@Inject
public KeyValuesDelimitedByBlankLinesToProfileInfo(ListOfKeyValuesDelimitedByBlankLinesToProfileInfoSet setParser) {
this.setParser = setParser;
}
@Override
public ProfileInfo apply(HttpResponse response) {
Set<ProfileInfo> drives = setParser.apply(response);
if (drives.size() == 0)
return null;
return Iterables.get(drives, 0);
}
}

View File

@ -0,0 +1,60 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.cloudsigma.functions;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.cloudsigma.domain.ProfileInfo;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.functions.ReturnStringIf2xx;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
/**
*
* @author Adrian Cole
*/
@Singleton
public class ListOfKeyValuesDelimitedByBlankLinesToProfileInfoSet implements Function<HttpResponse, Set<ProfileInfo>> {
private final ReturnStringIf2xx returnStringIf200;
private final ListOfKeyValuesDelimitedByBlankLinesToListOfMaps mapConverter;
private final MapToProfileInfo mapToProfile;
@Inject
ListOfKeyValuesDelimitedByBlankLinesToProfileInfoSet(ReturnStringIf2xx returnStringIf200,
ListOfKeyValuesDelimitedByBlankLinesToListOfMaps mapConverter, MapToProfileInfo mapToProfile) {
this.returnStringIf200 = returnStringIf200;
this.mapConverter = mapConverter;
this.mapToProfile = mapToProfile;
}
@Override
public Set<ProfileInfo> apply(HttpResponse response) {
String text = returnStringIf200.apply(response);
if (text == null || text.trim().equals(""))
return ImmutableSet.<ProfileInfo> of();
return ImmutableSet.copyOf(Iterables.transform(mapConverter.apply(text), mapToProfile));
}
}

View File

@ -0,0 +1,63 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.cloudsigma.functions;
import java.util.Map;
import javax.annotation.Resource;
import javax.inject.Singleton;
import org.jclouds.cloudsigma.domain.ProfileInfo;
import org.jclouds.cloudsigma.domain.ProfileType;
import org.jclouds.logging.Logger;
import com.google.common.base.Function;
/**
*
* @author Adrian Cole
*/
@Singleton
public class MapToProfileInfo implements Function<Map<String, String>, ProfileInfo> {
@Resource
protected Logger logger = Logger.NULL;
@Override
public ProfileInfo apply(Map<String, String> from) {
if (from.size() == 0)
return null;
if (from.size() == 0)
return null;
ProfileInfo.Builder builder = new ProfileInfo.Builder();
builder.uuid(from.get("uuid"));
builder.email(from.get("email"));
builder.firstName(from.get("first_name"));
builder.lastName(from.get("last_name"));
builder.nickName(from.get("nick_name"));
builder.type(ProfileType.fromValue(from.get("type")));
try {
return builder.build();
} catch (NullPointerException e) {
logger.trace("entry missing data: %s; %s", e.getMessage(), from);
return null;
}
}
}

View File

@ -31,6 +31,7 @@ import org.jclouds.cloudsigma.domain.Drive;
import org.jclouds.cloudsigma.domain.DriveData; import org.jclouds.cloudsigma.domain.DriveData;
import org.jclouds.cloudsigma.domain.Server; import org.jclouds.cloudsigma.domain.Server;
import org.jclouds.cloudsigma.functions.KeyValuesDelimitedByBlankLinesToDriveInfo; import org.jclouds.cloudsigma.functions.KeyValuesDelimitedByBlankLinesToDriveInfo;
import org.jclouds.cloudsigma.functions.KeyValuesDelimitedByBlankLinesToProfileInfo;
import org.jclouds.cloudsigma.functions.KeyValuesDelimitedByBlankLinesToServerInfo; import org.jclouds.cloudsigma.functions.KeyValuesDelimitedByBlankLinesToServerInfo;
import org.jclouds.cloudsigma.functions.ListOfKeyValuesDelimitedByBlankLinesToDriveInfoSet; import org.jclouds.cloudsigma.functions.ListOfKeyValuesDelimitedByBlankLinesToDriveInfoSet;
import org.jclouds.cloudsigma.functions.ListOfKeyValuesDelimitedByBlankLinesToServerInfoSet; import org.jclouds.cloudsigma.functions.ListOfKeyValuesDelimitedByBlankLinesToServerInfoSet;
@ -61,6 +62,21 @@ import com.google.inject.TypeLiteral;
@Test(groups = "unit", testName = "CloudSigmaAsyncClientTest") @Test(groups = "unit", testName = "CloudSigmaAsyncClientTest")
public class CloudSigmaAsyncClientTest extends RestClientTest<CloudSigmaAsyncClient> { public class CloudSigmaAsyncClientTest extends RestClientTest<CloudSigmaAsyncClient> {
public void testGetProfileInfo() throws SecurityException, NoSuchMethodException, IOException {
Method method = CloudSigmaAsyncClient.class.getMethod("getProfileInfo");
HttpRequest httpRequest = processor.createRequest(method);
assertRequestLineEquals(httpRequest, "GET https://api.cloudsigma.com/profile/info HTTP/1.1");
assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
assertPayloadEquals(httpRequest, null, null, false);
assertResponseParserClassEquals(method, httpRequest, KeyValuesDelimitedByBlankLinesToProfileInfo.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class);
checkFilters(httpRequest);
}
public void testListStandardDrives() throws SecurityException, NoSuchMethodException, IOException { public void testListStandardDrives() throws SecurityException, NoSuchMethodException, IOException {
Method method = CloudSigmaAsyncClient.class.getMethod("listStandardDrives"); Method method = CloudSigmaAsyncClient.class.getMethod("listStandardDrives");
HttpRequest httpRequest = processor.createRequest(method); HttpRequest httpRequest = processor.createRequest(method);

View File

@ -38,6 +38,7 @@ import org.jclouds.cloudsigma.domain.DriveStatus;
import org.jclouds.cloudsigma.domain.DriveType; import org.jclouds.cloudsigma.domain.DriveType;
import org.jclouds.cloudsigma.domain.IDEDevice; import org.jclouds.cloudsigma.domain.IDEDevice;
import org.jclouds.cloudsigma.domain.Model; import org.jclouds.cloudsigma.domain.Model;
import org.jclouds.cloudsigma.domain.ProfileInfo;
import org.jclouds.cloudsigma.domain.Server; import org.jclouds.cloudsigma.domain.Server;
import org.jclouds.cloudsigma.domain.ServerInfo; import org.jclouds.cloudsigma.domain.ServerInfo;
import org.jclouds.cloudsigma.domain.ServerStatus; import org.jclouds.cloudsigma.domain.ServerStatus;
@ -128,6 +129,12 @@ public class CloudSigmaClientLiveTest {
assertNotNull(servers); assertNotNull(servers);
} }
@Test
public void testGetProfileInfo() throws Exception {
ProfileInfo profile = client.getProfileInfo();
assertNotNull(profile);
}
@Test @Test
public void testListServerInfo() throws Exception { public void testListServerInfo() throws Exception {
Set<? extends ServerInfo> servers = client.listServerInfo(); Set<? extends ServerInfo> servers = client.listServerInfo();

View File

@ -0,0 +1,50 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.cloudsigma.functions;
import static org.testng.Assert.assertEquals;
import org.jclouds.http.HttpResponse;
import org.jclouds.io.Payloads;
import org.testng.annotations.Test;
import com.google.inject.Guice;
/**
*
* @author Adrian Cole
*/
@Test(groups = { "unit" })
public class KeyValuesDelimitedByBlankLinesToProfileInfoTest {
private static final KeyValuesDelimitedByBlankLinesToProfileInfo FN = Guice.createInjector().getInstance(
KeyValuesDelimitedByBlankLinesToProfileInfo.class);
public void testNone() {
assertEquals(FN.apply(new HttpResponse(200, "", Payloads.newStringPayload(""))), null);
assertEquals(FN.apply(new HttpResponse(200, "", Payloads.newStringPayload("\n\n"))), null);
assertEquals(FN.apply(new HttpResponse(200, "", null)), null);
}
public void testOne() {
assertEquals(FN.apply(new HttpResponse(200, "", Payloads.newInputStreamPayload(MapToProfileInfoTest.class
.getResourceAsStream("/profile.txt")))), MapToProfileInfoTest.ONE);
}
}

View File

@ -0,0 +1,58 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.cloudsigma.functions;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.util.Map;
import org.jclouds.cloudsigma.domain.ProfileInfo;
import org.jclouds.cloudsigma.domain.ProfileType;
import org.jclouds.util.Strings2;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
/**
*
* @author Adrian Cole
*/
@Test(groups = { "unit" })
public class MapToProfileInfoTest {
public static ProfileInfo ONE = new ProfileInfo.Builder().type(ProfileType.REGULAR)//
.uuid("58ca3c1f-7629-4771-9b71-863f40153ba4")//
.email("adrian@jclouds.org").firstName("Adrian").lastName("Cole").nickName("jclouds").build();
private static final MapToProfileInfo MAP_TO_PROFILE = new MapToProfileInfo();
public void testEmptyMapReturnsNull() {
assertEquals(MAP_TO_PROFILE.apply(ImmutableMap.<String, String> of()), null);
}
public void test() throws IOException {
Map<String, String> input = new ListOfKeyValuesDelimitedByBlankLinesToListOfMaps().apply(
Strings2.toStringAndClose(MapToProfileInfoTest.class.getResourceAsStream("/profile.txt"))).get(0);
assertEquals(MAP_TO_PROFILE.apply(input), ONE);
}
}

View File

@ -0,0 +1,28 @@
uuid 58ca3c1f-7629-4771-9b71-863f40153ba4
email adrian@jclouds.org
salutation
first_name Adrian
last_name Cole
nick_name jclouds
title Mr
company Cloud Conscious, LLC.
vat
country US
town 1200 Fulton St. #609
language en
currency USD
address 1200 Fulton St. #609
postcode 1200 Fulton St. #609
phone 4153180253
job_title
location
facebook
twitter
xing
linkedin
website
signature
ftp_disabled false
api_disabled false
api_https_only false
type regular