From 519a3ac1934c1dbdc2773505cd3c12a412452b9b Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Thu, 26 Apr 2012 18:07:52 -0700 Subject: [PATCH 001/148] Recurse in subdirectories before deleting blobs This ensures that we have at most one PageSet of Futures awaiting completion. --- .../internal/DeleteAllKeysInList.java | 69 ++++++++----------- .../internal/DeleteAllKeysInListTest.java | 68 +++++++++++------- 2 files changed, 74 insertions(+), 63 deletions(-) diff --git a/blobstore/src/main/java/org/jclouds/blobstore/strategy/internal/DeleteAllKeysInList.java b/blobstore/src/main/java/org/jclouds/blobstore/strategy/internal/DeleteAllKeysInList.java index dc8b816df2..e028975226 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/strategy/internal/DeleteAllKeysInList.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/strategy/internal/DeleteAllKeysInList.java @@ -93,9 +93,9 @@ public class DeleteAllKeysInList implements ClearListStrategy, ClearContainerStr message = message + " recursively"; Map exceptions = Maps.newHashMap(); PageSet listing; - Iterable toDelete; int maxErrors = 3; // TODO parameterize for (int i = 0; i < maxErrors; ) { + // fetch partial directory listing try { listing = connection.list(containerName, options).get(); } catch (ExecutionException ee) { @@ -108,36 +108,52 @@ public class DeleteAllKeysInList implements ClearListStrategy, ClearContainerStr } catch (InterruptedException ie) { throw Throwables.propagate(ie); } - toDelete = filterListing(listing, options); - Map> responses = Maps.newHashMap(); - try { - for (final StorageMetadata md : toDelete) { + // recurse on subdirectories + if (options.isRecursive()) { + for (StorageMetadata md : listing) { String fullPath = parentIsFolder(options, md) ? options.getDir() + "/" + md.getName() : md.getName(); switch (md.getType()) { case BLOB: - responses.put(md, connection.removeBlob(containerName, fullPath)); break; case FOLDER: - if (options.isRecursive() && !fullPath.equals(options.getDir())) { - execute(containerName, options.clone().inDirectory(fullPath)); - } - responses.put(md, connection.deleteDirectory(containerName, fullPath)); - break; case RELATIVE_PATH: if (options.isRecursive() && !fullPath.equals(options.getDir())) { execute(containerName, options.clone().inDirectory(fullPath)); } - responses.put(md, connection.deleteDirectory(containerName, md.getName())); break; case CONTAINER: throw new IllegalArgumentException("Container type not supported"); } } - } finally { - exceptions = awaitCompletion(responses, userExecutor, maxTime, logger, message); } + + // remove blobs and now-empty subdirectories + Map> responses = Maps.newHashMap(); + for (StorageMetadata md : listing) { + String fullPath = parentIsFolder(options, md) ? options.getDir() + "/" + + md.getName() : md.getName(); + switch (md.getType()) { + case BLOB: + responses.put(md, connection.removeBlob(containerName, fullPath)); + break; + case FOLDER: + if (options.isRecursive()) { + responses.put(md, connection.deleteDirectory(containerName, fullPath)); + } + break; + case RELATIVE_PATH: + if (options.isRecursive()) { + responses.put(md, connection.deleteDirectory(containerName, md.getName())); + } + break; + case CONTAINER: + throw new IllegalArgumentException("Container type not supported"); + } + } + + exceptions = awaitCompletion(responses, userExecutor, maxTime, logger, message); if (!exceptions.isEmpty()) { ++i; retryHandler.imposeBackoffExponentialDelay(i, message); @@ -157,29 +173,4 @@ public class DeleteAllKeysInList implements ClearListStrategy, ClearContainerStr private boolean parentIsFolder(final ListContainerOptions options, final StorageMetadata md) { return (options.getDir() != null && md.getName().indexOf('/') == -1); } - - private Iterable filterListing( - final PageSet listing, - final ListContainerOptions options) { - Iterable toDelete = Iterables.filter(listing, - new Predicate() { - - @Override - public boolean apply(StorageMetadata input) { - switch (input.getType()) { - case BLOB: - return true; - case FOLDER: - case RELATIVE_PATH: - if (options.isRecursive()) - return true; - break; - } - return false; - } - - }); - return toDelete; - } - } diff --git a/blobstore/src/test/java/org/jclouds/blobstore/strategy/internal/DeleteAllKeysInListTest.java b/blobstore/src/test/java/org/jclouds/blobstore/strategy/internal/DeleteAllKeysInListTest.java index 47de7565e5..9565d20119 100644 --- a/blobstore/src/test/java/org/jclouds/blobstore/strategy/internal/DeleteAllKeysInListTest.java +++ b/blobstore/src/test/java/org/jclouds/blobstore/strategy/internal/DeleteAllKeysInListTest.java @@ -23,8 +23,8 @@ import static org.testng.Assert.assertEquals; import org.jclouds.ContextBuilder; import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.options.ListContainerOptions; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import com.google.common.io.Closeables; @@ -38,40 +38,60 @@ import com.google.inject.Injector; public class DeleteAllKeysInListTest { private BlobStore blobstore; private DeleteAllKeysInList deleter; + private static final String containerName = "container"; + private static final String directoryName = "directory"; - @BeforeClass + @BeforeMethod void setupBlobStore() { Injector injector = ContextBuilder.newBuilder("transient").buildInjector(); blobstore = injector.getInstance(BlobStore.class); deleter = injector.getInstance(DeleteAllKeysInList.class); + createDataSet(); + } + + @AfterMethod + void close() { + Closeables.closeQuietly(blobstore.getContext()); } public void testExecuteWithoutOptionsClearsRecursively() { - blobstore.createContainerInLocation(null, "goodies"); - for (int i = 0; i < 1001; i++) { - blobstore.putBlob("goodies", blobstore.blobBuilder(i + "").payload(i + "").build()); - } - assertEquals(blobstore.countBlobs("goodies"), 1001); - deleter.execute("goodies"); - assertEquals(blobstore.countBlobs("goodies"), 0); + deleter.execute(containerName); + assertEquals(blobstore.countBlobs(containerName), 0); + } + + public void testExecuteRecursive() { + deleter.execute(containerName, ListContainerOptions.Builder.recursive()); + assertEquals(blobstore.countBlobs(containerName), 0); } public void testExecuteNonRecursive() { - blobstore.createContainerInLocation(null, "foo"); - for (int i = 0; i < 1001; i++) { - blobstore.putBlob("foo", blobstore.blobBuilder(i + "").payload(i + "").build()); - } - for (int i = 0; i < 1001; i++) { - blobstore.putBlob("foo", blobstore.blobBuilder("dir/" + i + "").payload(i + "").build()); - } - assertEquals(blobstore.countBlobs("foo"), 2002); - deleter.execute("foo", ListContainerOptions.Builder.inDirectory("dir")); - assertEquals(blobstore.countBlobs("foo"), 1001); + deleter.execute(containerName, ListContainerOptions.NONE); + assertEquals(blobstore.countBlobs(containerName), 2222); } - @AfterClass - void close() { - if (blobstore != null) - Closeables.closeQuietly(blobstore.getContext()); + public void testExecuteInDirectory() { + deleter.execute(containerName, ListContainerOptions.Builder.inDirectory(directoryName)); + assertEquals(blobstore.countBlobs(containerName), 1111); + } + + /** + * Create a container "container" with 1111 blobs named "blob-%d". Create a + * subdirectory "directory" which contains 2222 more blobs named + * "directory/blob-%d". + */ + private void createDataSet() { + String blobNameFmt = "blob-%d"; + String directoryBlobNameFmt = "%s/blob-%d"; + + blobstore.createContainerInLocation(null, containerName); + for (int i = 0; i < 1111; i++) { + String blobName = String.format(blobNameFmt, i); + blobstore.putBlob(containerName, blobstore.blobBuilder(blobName).payload(blobName).build()); + } + for (int i = 0; i < 2222; i++) { + String directoryBlobName = String.format(directoryBlobNameFmt, directoryName, i); + blobstore.putBlob(containerName, blobstore.blobBuilder(directoryBlobName).payload(directoryBlobName).build()); + } + assertEquals(blobstore.countBlobs(containerName), 3333); } } From fd7c8c06a21aafedb3a76e4b697ab323df6a088a Mon Sep 17 00:00:00 2001 From: Andrea Turli Date: Tue, 1 May 2012 22:15:45 +0200 Subject: [PATCH 002/148] jenkins api: buildWithParameters added --- .../v1/binders/BindMapToOptionalParams.java | 59 +++++++++++++++++++ .../jenkins/v1/features/JobAsyncClient.java | 16 +++++ .../jenkins/v1/features/JobClient.java | 6 ++ .../v1/features/JobClientLiveTest.java | 22 ++++++- .../resources/sample_job_with_parameters.xml | 37 ++++++++++++ 5 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 labs/jenkins/src/main/java/org/jclouds/jenkins/v1/binders/BindMapToOptionalParams.java create mode 100644 labs/jenkins/src/test/resources/sample_job_with_parameters.xml diff --git a/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/binders/BindMapToOptionalParams.java b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/binders/BindMapToOptionalParams.java new file mode 100644 index 0000000000..d7400cd73e --- /dev/null +++ b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/binders/BindMapToOptionalParams.java @@ -0,0 +1,59 @@ +/** + * 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.jenkins.v1.binders; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.http.utils.ModifyRequest.addQueryParam; + +import java.util.Map; +import java.util.Map.Entry; + +import javax.inject.Inject; +import javax.inject.Provider; +import javax.ws.rs.core.UriBuilder; + +import org.jclouds.http.HttpRequest; +import org.jclouds.rest.Binder; + +/** + * Binds the map to parameters. + * + * @author Andrea Turli + */ +public class BindMapToOptionalParams implements Binder { + private final Provider builder; + + @Inject + BindMapToOptionalParams(Provider builder) { + this.builder = checkNotNull(builder, "builder"); + } + + @SuppressWarnings("unchecked") + @Override + public R bindToRequest(R request, Object input) { + checkArgument(checkNotNull(input, "input") instanceof Map, "this binder is only valid for Maps!"); + Map map = (Map) input; + for (Entry entry : map.entrySet()) { + request = addQueryParam(request, entry.getKey(), entry.getValue(), builder.get()); + } + return request; + } + +} \ No newline at end of file diff --git a/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/features/JobAsyncClient.java b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/features/JobAsyncClient.java index 45b612aa5d..f5b49a4114 100644 --- a/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/features/JobAsyncClient.java +++ b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/features/JobAsyncClient.java @@ -18,6 +18,8 @@ */ package org.jclouds.jenkins.v1.features; +import java.util.Map; + import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; @@ -25,14 +27,19 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; +import org.jclouds.jenkins.v1.binders.BindMapToOptionalParams; import org.jclouds.jenkins.v1.domain.JobDetails; import org.jclouds.jenkins.v1.filters.BasicAuthenticationUnlessAnonymous; import org.jclouds.jenkins.v1.functions.ReturnVoidOn302Or404; import org.jclouds.rest.annotations.BinderParam; import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.binders.BindMapToMatrixParams; +import org.jclouds.rest.binders.BindMapToStringPayload; import org.jclouds.rest.binders.BindToStringPayload; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; @@ -81,6 +88,15 @@ public interface JobAsyncClient { @ExceptionParser(ReturnNullOnNotFoundOr404.class) ListenableFuture build(@PathParam("displayName") String displayName); + /** + * @see JobClient#buildJobWithParameters + */ + @POST + @Path("/job/{displayName}/buildWithParameters") + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture buildWithParameters(@PathParam("displayName") String displayName, + @BinderParam(BindMapToOptionalParams.class) Map parameters); + /** * @see JobClient#fetchConfigXML */ diff --git a/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/features/JobClient.java b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/features/JobClient.java index cce44bcdfc..bff58a7735 100644 --- a/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/features/JobClient.java +++ b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/features/JobClient.java @@ -18,8 +18,12 @@ */ package org.jclouds.jenkins.v1.features; +import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; +import javax.ws.rs.core.UriInfo; + import org.jclouds.concurrent.Timeout; import org.jclouds.jenkins.v1.domain.JobDetails; @@ -54,6 +58,8 @@ public interface JobClient { * @param displayName */ void build(String displayName); + + void buildWithParameters(String displayName, Map parameters); String fetchConfigXML(String displayName); diff --git a/labs/jenkins/src/test/java/org/jclouds/jenkins/v1/features/JobClientLiveTest.java b/labs/jenkins/src/test/java/org/jclouds/jenkins/v1/features/JobClientLiveTest.java index ae7218b056..0312f90b95 100644 --- a/labs/jenkins/src/test/java/org/jclouds/jenkins/v1/features/JobClientLiveTest.java +++ b/labs/jenkins/src/test/java/org/jclouds/jenkins/v1/features/JobClientLiveTest.java @@ -22,6 +22,7 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import java.io.IOException; +import java.util.Map; import org.jclouds.jenkins.v1.domain.JobDetails; import org.jclouds.jenkins.v1.internal.BaseJenkinsClientLiveTest; @@ -29,6 +30,8 @@ import org.jclouds.util.Strings2; import org.testng.annotations.AfterClass; import org.testng.annotations.Test; +import com.google.common.collect.ImmutableMap; + /** * * @author Adrian Cole @@ -66,11 +69,28 @@ public class JobClientLiveTest extends BaseJenkinsClientLiveTest { getClient().delete("blagoo"); } + @Test(dependsOnMethods = "testDeleteJob") + public void testCreateJobWithParameters() throws IOException { + getClient().delete("jobWithParameters"); + getClient().createFromXML("jobWithParameters", Strings2.toStringAndClose(getClass().getResourceAsStream("/sample_job_with_parameters.xml"))); + } + + @Test(dependsOnMethods = "testCreateJobWithParameters") + public void testBuildJobWithParameters() throws IOException { + Map parameters = ImmutableMap.of("name", "test1", "password", "secret"); + getClient().buildWithParameters("jobWithParameters", parameters); + } + + @Test(dependsOnMethods = "testBuildJob") + public void testDeleteJobWithParameters() { + getClient().delete("jobWithParameters"); + } + @AfterClass(groups = { "integration", "live" }) @Override protected void tearDownContext() { getClient().delete("blagoo"); - getClient().delete("blagooCopy"); + getClient().delete("jobWithParameters"); super.tearDownContext(); } diff --git a/labs/jenkins/src/test/resources/sample_job_with_parameters.xml b/labs/jenkins/src/test/resources/sample_job_with_parameters.xml new file mode 100644 index 0000000000..3c65002ea7 --- /dev/null +++ b/labs/jenkins/src/test/resources/sample_job_with_parameters.xml @@ -0,0 +1,37 @@ + + + + + false + + + + + name + + + + + password + + + + + + + + true + false + false + + false + + false + + + echo hello + + + + + \ No newline at end of file From 253fd1f60853dc8ace35bfb91dab9d3a668ff30e Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Tue, 1 May 2012 13:54:57 -0700 Subject: [PATCH 003/148] Simplify transient blobstore logic Many more simplifications remain. --- .../blobstore/TransientAsyncBlobStore.java | 33 ++++++------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java index 28891d32a3..d6d460b3c5 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java @@ -27,8 +27,6 @@ import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.find; import static com.google.common.collect.Iterables.size; import static com.google.common.collect.Iterables.transform; -import static com.google.common.collect.Lists.newArrayList; -import static com.google.common.collect.Lists.partition; import static com.google.common.collect.Maps.newHashMap; import static com.google.common.collect.Sets.filter; import static com.google.common.collect.Sets.newTreeSet; @@ -179,13 +177,10 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { final String finalMarker = options.getMarker(); StorageMetadata lastMarkerMetadata = find(contents, new Predicate() { public boolean apply(StorageMetadata metadata) { - return metadata.getName().compareTo(finalMarker) >= 0; + return metadata.getName().compareTo(finalMarker) > 0; } }); contents = contents.tailSet(lastMarkerMetadata); - if (finalMarker.equals(lastMarkerMetadata.getName())) { - contents.remove(lastMarkerMetadata); - } } final String prefix = options.getDir(); @@ -198,26 +193,23 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { } String marker = null; - Integer maxResults = options.getMaxResults() != null ? options.getMaxResults() : 1000; - if (contents.size() > 0) { - SortedSet contentsSlice = firstSliceOfSize(contents, maxResults); - if (!contentsSlice.contains(contents.last())) { + int maxResults = options.getMaxResults() != null ? options.getMaxResults() : 1000; + if (!contents.isEmpty()) { + StorageMetadata lastElement = contents.last(); + contents = newTreeSet(Iterables.limit(contents, maxResults)); + if (!contents.contains(lastElement)) { // Partial listing - marker = contentsSlice.last().getName(); - } else { - marker = null; + marker = contents.last().getName(); } - contents = contentsSlice; } final String delimiter = options.isRecursive() ? null : "/"; if (delimiter != null) { - SortedSet commonPrefixes = null; - Iterable iterable = transform(contents, new CommonPrefixes(prefix != null ? prefix : null, delimiter)); - commonPrefixes = iterable != null ? newTreeSet(iterable) : new TreeSet(); + SortedSet commonPrefixes = newTreeSet( + transform(contents, new CommonPrefixes(prefix, delimiter))); commonPrefixes.remove(CommonPrefixes.NO_PREFIX); - contents = newTreeSet(filter(contents, new DelimiterFilter(prefix != null ? prefix : null, delimiter))); + contents = newTreeSet(filter(contents, new DelimiterFilter(prefix, delimiter))); Iterables. addAll(contents, transform(commonPrefixes, new Function() { @@ -443,11 +435,6 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { } } - public static > SortedSet firstSliceOfSize(Iterable elements, int size) { - List> slices = partition(newArrayList(elements), size); - return newTreeSet(slices.get(0)); - } - public static HttpResponseException returnResponseException(int code) { HttpResponse response = null; response = new HttpResponse(code, null, null); From bbe157576c048e877c248801b45e8a2b2999b72f Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Tue, 1 May 2012 13:48:05 -0700 Subject: [PATCH 004/148] Renamed properties rhcloud.jboss.* -> jboss.* --- demos/tweetstore/rhcloud-tweetstore/pom.xml | 14 +++++++------- .../tweetstore/integration/TweetStoreLiveTest.java | 2 +- .../jbossas7/configuration/standalone.xml | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/demos/tweetstore/rhcloud-tweetstore/pom.xml b/demos/tweetstore/rhcloud-tweetstore/pom.xml index b9135fb3ca..9bf5061fc5 100644 --- a/demos/tweetstore/rhcloud-tweetstore/pom.xml +++ b/demos/tweetstore/rhcloud-tweetstore/pom.xml @@ -33,8 +33,8 @@ 7.0.2.Final - localhost - 8088 + localhost + 8088 jclouds-rhcloud-tweetstore @@ -98,9 +98,9 @@ ${test.cloudonestorage.credential} ${test.ninefold-storage.identity} ${test.ninefold-storage.credential} - ${rhcloud.jboss.home} - ${rhcloud.jboss.address} - ${rhcloud.jboss.port} + ${rhcloud.jboss.home} + ${test.jboss.address} + ${test.jboss.port} ${jclouds.tweetstore.blobstores} test.${jclouds.tweetstore.container} ${project.build.directory}/rhcloud-jboss @@ -108,8 +108,8 @@ ${project.build.directory}/${project.build.finalName} - ${rhcloud.jboss.address} - ${rhcloud.jboss.port} + ${test.jboss.address} + ${test.jboss.port} diff --git a/demos/tweetstore/rhcloud-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/TweetStoreLiveTest.java b/demos/tweetstore/rhcloud-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/TweetStoreLiveTest.java index f66308f244..30dc27c70f 100644 --- a/demos/tweetstore/rhcloud-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/TweetStoreLiveTest.java +++ b/demos/tweetstore/rhcloud-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/TweetStoreLiveTest.java @@ -172,7 +172,7 @@ public class TweetStoreLiveTest { } @BeforeTest(dependsOnMethods = "clearAndCreateContainers") - @Parameters({ "warfile", "rhcloud.jboss.address", "rhcloud.jboss.port", "rhcloud.jboss.home" }) + @Parameters({ "warfile", "jboss.address", "jboss.port", "jboss.home" }) public void startDevAppServer(final String warfile, final String address, final String port, String serverHome) throws Exception { url = new URL(String.format("http://%s:%s", address, port)); diff --git a/demos/tweetstore/rhcloud-tweetstore/src/test/resources/jbossas7/configuration/standalone.xml b/demos/tweetstore/rhcloud-tweetstore/src/test/resources/jbossas7/configuration/standalone.xml index 8b2f988c68..dcdf6049b6 100644 --- a/demos/tweetstore/rhcloud-tweetstore/src/test/resources/jbossas7/configuration/standalone.xml +++ b/demos/tweetstore/rhcloud-tweetstore/src/test/resources/jbossas7/configuration/standalone.xml @@ -194,7 +194,7 @@ - + From b665733423aed5c71444471a844bf34d0162c2e5 Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Tue, 1 May 2012 13:59:04 -0700 Subject: [PATCH 005/148] Made the serverThread final --- .../jclouds/demo/tweetstore/integration/StaxSdkAppServer2.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demos/tweetstore/runatcloud-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/StaxSdkAppServer2.java b/demos/tweetstore/runatcloud-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/StaxSdkAppServer2.java index fd3d30ebc2..4dbece1747 100644 --- a/demos/tweetstore/runatcloud-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/StaxSdkAppServer2.java +++ b/demos/tweetstore/runatcloud-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/StaxSdkAppServer2.java @@ -76,7 +76,7 @@ class StaxSdkAppServer2 { } private final StaxSdkAppServer server; - private Thread serverThread; + private final Thread serverThread; private StaxSdkAppServer2(StaxSdkAppServer server) { this.server = server; From 7a7e40c0d6cc92c5200e1b86d49f8a9ce617ce81 Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Tue, 1 May 2012 17:32:47 -0700 Subject: [PATCH 006/148] First cut of heroku-tweetstore --- demos/tweetstore/heroku-tweetstore/README.txt | 41 ++ demos/tweetstore/heroku-tweetstore/pom.xml | 129 ++++++ .../jclouds/demo/paas/PlatformServices.java | 64 +++ .../demo/paas/RunnableHttpRequest.java | 126 ++++++ .../demo/paas/config/HttpClientModule.java | 65 +++ .../config/PlatformServicesInitializer.java | 87 ++++ .../demo/paas/reference/PaasConstants.java | 28 ++ .../service/scheduler/HttpRequestJob.java | 69 +++ .../paas/service/scheduler/Scheduler.java | 41 ++ ...nlessXmlSchedulingDataProcessorPlugin.java | 401 ++++++++++++++++++ .../paas/service/taskqueue/TaskQueue.java | 107 +++++ .../tweetstore/config/GuiceServletConfig.java | 153 +++++++ .../config/util/CredentialsCollector.java | 153 +++++++ .../config/util/PropertiesLoader.java | 59 +++ .../controller/AddTweetsController.java | 96 +++++ .../controller/EnqueueStoresController.java | 101 +++++ .../controller/StoreTweetsController.java | 130 ++++++ .../tweetstore/domain/StoredTweetStatus.java | 149 +++++++ .../functions/KeyToStoredTweetStatus.java | 71 ++++ .../ServiceToStoredTweetStatuses.java | 73 ++++ .../reference/TweetStoreConstants.java | 34 ++ .../reference/TwitterConstants.java | 31 ++ .../src/main/platform/.gitignore | 1 + .../src/main/resources/jobs.xml | 29 ++ .../src/main/resources/quartz.properties | 28 ++ .../src/main/webapp/WEB-INF/web.xml | 60 +++ .../src/main/webapp/index.jsp | 31 ++ .../src/main/webapp/tweets.jsp | 109 +++++ .../config/util/CredentialsCollectorTest.java | 82 ++++ .../controller/AddTweetsControllerTest.java | 78 ++++ .../EnqueueStoresControllerTest.java | 85 ++++ .../controller/StoreTweetsControllerTest.java | 120 ++++++ .../functions/KeyToStoredTweetStatusTest.java | 69 +++ .../ServiceToStoredTweetStatusesTest.java | 75 ++++ .../tweetstore/integration/JettyServer.java | 68 +++ .../demo/tweetstore/integration/Runner2.java | 72 ++++ .../integration/TweetStoreLiveTest.java | 237 +++++++++++ .../integration/util/ObjectFields.java | 56 +++ .../src/test/resources/log4j.xml | 95 +++++ demos/tweetstore/pom.xml | 1 + 40 files changed, 3504 insertions(+) create mode 100644 demos/tweetstore/heroku-tweetstore/README.txt create mode 100644 demos/tweetstore/heroku-tweetstore/pom.xml create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/PlatformServices.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/RunnableHttpRequest.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/config/HttpClientModule.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/config/PlatformServicesInitializer.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/reference/PaasConstants.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/HttpRequestJob.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/Scheduler.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/quartz/plugins/TransactionlessXmlSchedulingDataProcessorPlugin.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/taskqueue/TaskQueue.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/config/GuiceServletConfig.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/config/util/CredentialsCollector.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/config/util/PropertiesLoader.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/controller/AddTweetsController.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/controller/EnqueueStoresController.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/controller/StoreTweetsController.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/domain/StoredTweetStatus.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/functions/KeyToStoredTweetStatus.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/functions/ServiceToStoredTweetStatuses.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/reference/TweetStoreConstants.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/reference/TwitterConstants.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/platform/.gitignore create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/resources/jobs.xml create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/resources/quartz.properties create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/webapp/WEB-INF/web.xml create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/webapp/index.jsp create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/webapp/tweets.jsp create mode 100644 demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/config/util/CredentialsCollectorTest.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/AddTweetsControllerTest.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/EnqueueStoresControllerTest.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/StoreTweetsControllerTest.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/functions/KeyToStoredTweetStatusTest.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/functions/ServiceToStoredTweetStatusesTest.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/JettyServer.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/Runner2.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/TweetStoreLiveTest.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/util/ObjectFields.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/test/resources/log4j.xml diff --git a/demos/tweetstore/heroku-tweetstore/README.txt b/demos/tweetstore/heroku-tweetstore/README.txt new file mode 100644 index 0000000000..c6bf77b0dd --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/README.txt @@ -0,0 +1,41 @@ +==== + 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. +==== + +A guide to generating Twitter consumer keys and access tokens is at http://tinyurl.com/2fhebgb + +Please modify your maven settings.xml like below before attempting to run 'mvn -Plive install' + + + keys + + true + + + YOUR_ACCESS_KEY_ID + YOUR_SECRET_KEY + YOUR_USER + YOUR_HEX_KEY + YOUR_ACCOUNT + YOUR_BASE64_ENCODED_KEY + YOUR_TWITTER_CONSUMER_KEY + YOUR_TWITTER_CONSUMER_SECRET + YOUR_TWITTER_ACCESSTOKEN + YOUR_TWITTER_ACCESSTOKEN_SECRET + + \ No newline at end of file diff --git a/demos/tweetstore/heroku-tweetstore/pom.xml b/demos/tweetstore/heroku-tweetstore/pom.xml new file mode 100644 index 0000000000..118ef206b4 --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/pom.xml @@ -0,0 +1,129 @@ + + + + 4.0.0 + + org.jclouds + jclouds-demos-tweetstore-project + 1.5.0-SNAPSHOT + + jclouds-demo-heroku-tweetstore + war + jclouds TweetStore for Heroku + jclouds TweetStore for Heroku's Cedar using Guice for Dependency Injection + + + localhost + 8088 + jclouds-heroku-tweetstore + + + + + com.google.inject.extensions + guice-servlet + 3.0 + + + org.quartz-scheduler + quartz + 2.1.3 + + + org.slf4j + slf4j-api + + + + + + javax.servlet + servlet-api + 2.5 + + + + + org.mortbay.jetty + jetty-runner + 7.5.4.v20111024 + test + + + + + + live + + + + maven-surefire-plugin + + + integration + integration-test + + test + + + + ${test.twitter.runatcloud-tweetstore.consumer.identity} + ${test.twitter.runatcloud-tweetstore.consumer.credential} + ${test.twitter.runatcloud-tweetstore.access.identity} + ${test.twitter.runatcloud-tweetstore.access.credential} + ${test.azureblob.identity} + ${test.azureblob.credential} + ${test.cloudfiles-us.identity} + ${test.cloudfiles-us.credential} + ${test.aws-s3.identity} + ${test.aws-s3.credential} + ${test.cloudonestorage.identity} + ${test.cloudonestorage.credential} + ${test.ninefold-storage.identity} + ${test.ninefold-storage.credential} + ${test.jetty.address} + ${test.jetty.port} + ${project.build.directory}/jetty + ${jclouds.tweetstore.blobstores} + test.${jclouds.tweetstore.container} + ${project.build.directory}/${project.artifactId} + + + ${test.jetty.port} + + + + + + + + + + + deploy + + + heroku-tweetstore + + + + diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/PlatformServices.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/PlatformServices.java new file mode 100644 index 0000000000..0997005157 --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/PlatformServices.java @@ -0,0 +1,64 @@ +/** + * 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.demo.paas; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.demo.paas.config.PlatformServicesInitializer.PLATFORM_SERVICES_ATTRIBUTE_NAME; + +import java.util.Map; + +import javax.servlet.ServletContext; + +import org.jclouds.demo.paas.service.scheduler.Scheduler; +import org.jclouds.demo.paas.service.taskqueue.TaskQueue; +import org.jclouds.javax.annotation.Nullable; + +import com.google.common.collect.ImmutableMap; + +/** + * @author Andrew Phillips + */ +public class PlatformServices { + protected final String baseUrl; + protected final Scheduler scheduler; + private ImmutableMap taskQueues; + + public PlatformServices(String baseUrl, Scheduler scheduler, Map taskQueues) { + this.baseUrl = baseUrl; + this.scheduler = scheduler; + this.taskQueues = ImmutableMap.copyOf(taskQueues); + } + + public String getBaseUrl() { + return baseUrl; + } + + public Scheduler getScheduler() { + return scheduler; + } + + public @Nullable TaskQueue getTaskQueue(String name) { + return taskQueues.get(name); + } + + public static PlatformServices get(ServletContext context) { + return (PlatformServices) checkNotNull(context.getAttribute( + PLATFORM_SERVICES_ATTRIBUTE_NAME), PLATFORM_SERVICES_ATTRIBUTE_NAME); + } +} diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/RunnableHttpRequest.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/RunnableHttpRequest.java new file mode 100644 index 0000000000..ad72a1a58d --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/RunnableHttpRequest.java @@ -0,0 +1,126 @@ +/** + * 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.demo.paas; + +import static java.lang.String.format; + +import org.jclouds.http.HttpCommand; +import org.jclouds.http.HttpCommandExecutorService; +import org.jclouds.http.HttpRequest; + +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Multimap; + +public class RunnableHttpRequest implements Runnable { + public static final String PLATFORM_REQUEST_ORIGINATOR_HEADER = "X-Platform-Originator"; + + public static Factory factory(HttpCommandExecutorService httpClient) { + return factory(httpClient, format("%s@%d", Factory.class.getName(), System.currentTimeMillis())); + } + + public static Factory factory(HttpCommandExecutorService httpClient, String originator) { + return new Factory(httpClient, originator); + } + + public static class Factory { + protected final HttpCommandExecutorService httpClient; + protected final String originator; + + private Factory(HttpCommandExecutorService httpClient, String originator) { + this.httpClient = httpClient; + this.originator = originator; + } + + public RunnableHttpRequest create(HttpRequest request) { + HttpRequest requestWithSubmitter = request.toBuilder() + .headers(copyOfWithEntry(request.getHeaders(), + PLATFORM_REQUEST_ORIGINATOR_HEADER, originator)).build(); + return new RunnableHttpRequest(httpClient, requestWithSubmitter); + } + + private static Multimap copyOfWithEntry( + Multimap multimap, K k1, V v1) { + return ImmutableMultimap.builder().putAll(multimap).put(k1, v1).build(); + } + } + + private final HttpCommandExecutorService httpClient; + private final HttpRequest request; + + private RunnableHttpRequest(HttpCommandExecutorService httpClient, HttpRequest request) { + this.httpClient = httpClient; + this.request = request; + } + + @Override + public void run() { + httpClient.submit(new ImmutableHttpCommand(request)); + } + + private class ImmutableHttpCommand implements HttpCommand { + private final HttpRequest request; + + public ImmutableHttpCommand(HttpRequest request) { + this.request = request; + } + + @Override + public void setException(Exception exception) { + } + + @Override + public void setCurrentRequest(HttpRequest request) { + } + + @Override + public boolean isReplayable() { + return false; + } + + @Override + public int incrementRedirectCount() { + return 0; + } + + @Override + public int incrementFailureCount() { + return 0; + } + + @Override + public int getRedirectCount() { + return 0; + } + + @Override + public int getFailureCount() { + return 0; + } + + @Override + public Exception getException() { + return null; + } + + @Override + public HttpRequest getCurrentRequest() { + return request; + } + } +} diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/config/HttpClientModule.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/config/HttpClientModule.java new file mode 100644 index 0000000000..5aa077b6cd --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/config/HttpClientModule.java @@ -0,0 +1,65 @@ +/** + * 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.demo.paas.config; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.inject.name.Names.bindProperties; +import static org.jclouds.Constants.*; + +import java.util.Properties; + +import javax.servlet.ServletContext; +import javax.ws.rs.core.UriBuilder; + +import org.jclouds.demo.tweetstore.config.util.PropertiesLoader; + +import com.google.inject.AbstractModule; +import com.sun.jersey.api.uri.UriBuilderImpl; + +/** + * @author Andrew Phillips + */ +public class HttpClientModule extends AbstractModule { + private final ServletContext context; + + HttpClientModule(ServletContext context) { + this.context = context; + } + + @Override + protected void configure() { + // URL connection defaults + Properties toBind = defaultProperties(); + toBind.putAll(checkNotNull(new PropertiesLoader(context).get(), "properties")); + toBind.putAll(System.getProperties()); + bindProperties(binder(), toBind); + bind(UriBuilder.class).to(UriBuilderImpl.class); + } + + private static Properties defaultProperties() { + Properties props = new Properties(); + props.setProperty(PROPERTY_MAX_CONNECTIONS_PER_CONTEXT, 20 + ""); + props.setProperty(PROPERTY_MAX_CONNECTIONS_PER_HOST, 0 + ""); + props.setProperty(PROPERTY_SO_TIMEOUT, 60000 + ""); + props.setProperty(PROPERTY_CONNECTION_TIMEOUT, 60000 + ""); + props.setProperty(PROPERTY_USER_THREADS, 0 + ""); + props.setProperty(PROPERTY_IO_WORKER_THREADS, 20 + ""); + return props; + } +} diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/config/PlatformServicesInitializer.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/config/PlatformServicesInitializer.java new file mode 100644 index 0000000000..364a07f3ec --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/config/PlatformServicesInitializer.java @@ -0,0 +1,87 @@ +/** + * 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.demo.paas.config; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.String.format; +import static java.util.concurrent.TimeUnit.SECONDS; + +import javax.servlet.ServletContext; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +import org.jclouds.concurrent.config.ExecutorServiceModule; +import org.jclouds.demo.paas.PlatformServices; +import org.jclouds.demo.paas.service.scheduler.Scheduler; +import org.jclouds.demo.paas.service.taskqueue.TaskQueue; +import org.jclouds.http.HttpCommandExecutorService; +import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import com.google.inject.Guice; + +/** + * @author Andrew Phillips + */ +public class PlatformServicesInitializer implements ServletContextListener { + public static final String PLATFORM_SERVICES_ATTRIBUTE_NAME = PlatformServices.class.getName(); + + protected static final String PORT_VARIABLE = "PORT"; + + @Override + public void contextInitialized(ServletContextEvent contextEvent) { + ServletContext context = contextEvent.getServletContext(); + context.setAttribute(PLATFORM_SERVICES_ATTRIBUTE_NAME, createServices(context)); + } + + protected static PlatformServices createServices(ServletContext context) { + HttpCommandExecutorService httpClient = createHttpClient(context); + return new PlatformServices(getBaseUrl(context), new Scheduler(httpClient), + createTaskQueues(httpClient)); + } + + protected static HttpCommandExecutorService createHttpClient( + final ServletContext context) { + return Guice.createInjector(new ExecutorServiceModule(), + new JavaUrlHttpCommandExecutorServiceModule(), + new HttpClientModule(context)) + .getInstance(HttpCommandExecutorService.class); + } + + protected static String getBaseUrl(ServletContext context) { + return format("http://localhost:%s%s", checkNotNull(System.getenv(PORT_VARIABLE), PORT_VARIABLE), + context.getContextPath()); + } + + // TODO: make the number and names of queues configurable + protected static ImmutableMap createTaskQueues(HttpCommandExecutorService httpClient) { + Builder taskQueues = ImmutableMap.builder(); + taskQueues.put("twitter", TaskQueue.builder(httpClient) + .name("twitter").period(SECONDS.toMillis(30)) + .build()); + return taskQueues.build(); + } + + @Override + public void contextDestroyed(ServletContextEvent servletContextEvent) { + ServletContext context = servletContextEvent.getServletContext(); + context.removeAttribute(PLATFORM_SERVICES_ATTRIBUTE_NAME); + } +} diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/reference/PaasConstants.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/reference/PaasConstants.java new file mode 100644 index 0000000000..8af7021bd2 --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/reference/PaasConstants.java @@ -0,0 +1,28 @@ +/** + * 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.demo.paas.reference; + +/** + * Configuration properties and constants used in PaaS applications. + * + * @author Andrew Phillips + */ +public interface PaasConstants { + static final String PROPERTY_PLATFORM_BASE_URL = "jclouds.paas.baseurl"; +} diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/HttpRequestJob.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/HttpRequestJob.java new file mode 100644 index 0000000000..902f5fe356 --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/HttpRequestJob.java @@ -0,0 +1,69 @@ +/** + * 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.demo.paas.service.scheduler; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.net.URI; + +import javax.servlet.ServletContext; + +import org.jclouds.demo.paas.PlatformServices; +import org.jclouds.demo.paas.RunnableHttpRequest; +import org.jclouds.http.HttpRequest; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.SchedulerException; + +/** + * @author Andrew Phillips + */ +public class HttpRequestJob implements Job { + protected static final String URL_ATTRIBUTE_NAME = "url"; + + // keep in sync with "quartz:scheduler-context-servlet-context-key" param in web.xml + protected static final String SERVLET_CONTEXT_KEY = "servlet-context"; + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + PlatformServices platform = JobContexts.getPlatform(context); + RunnableHttpRequest request = platform.getScheduler().getHttpRequestFactory().create( + HttpRequest.builder() + .endpoint(JobContexts.getTargetUrl(platform.getBaseUrl(), context)) + .method("GET").build()); + request.run(); + } + + private static class JobContexts { + private static URI getTargetUrl(String baseUrl, JobExecutionContext context) { + return URI.create(baseUrl + (String) checkNotNull( + context.getMergedJobDataMap().get(URL_ATTRIBUTE_NAME), URL_ATTRIBUTE_NAME)); + } + + private static PlatformServices getPlatform(JobExecutionContext jobContext) throws JobExecutionException { + try { + return PlatformServices.get((ServletContext) checkNotNull( + jobContext.getScheduler().getContext().get(SERVLET_CONTEXT_KEY), SERVLET_CONTEXT_KEY)); + } catch (SchedulerException exception) { + throw new JobExecutionException("Unable to get platform services from the job execution context", exception); + } + } + } +} diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/Scheduler.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/Scheduler.java new file mode 100644 index 0000000000..dabdff877b --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/Scheduler.java @@ -0,0 +1,41 @@ +/** + * 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.demo.paas.service.scheduler; + +import org.jclouds.demo.paas.RunnableHttpRequest; +import org.jclouds.demo.paas.RunnableHttpRequest.Factory; +import org.jclouds.http.HttpCommandExecutorService; + +/** + * @author Andrew Phillips + */ +public class Scheduler { + protected static final String SCHEDULER_ORIGINATOR_NAME = "scheduler"; + + protected final Factory httpRequestFactory; + + public Scheduler(HttpCommandExecutorService httpClient) { + httpRequestFactory = + RunnableHttpRequest.factory(httpClient, SCHEDULER_ORIGINATOR_NAME); + } + + public Factory getHttpRequestFactory() { + return httpRequestFactory; + } +} diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/quartz/plugins/TransactionlessXmlSchedulingDataProcessorPlugin.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/quartz/plugins/TransactionlessXmlSchedulingDataProcessorPlugin.java new file mode 100644 index 0000000000..91659d9b16 --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/quartz/plugins/TransactionlessXmlSchedulingDataProcessorPlugin.java @@ -0,0 +1,401 @@ +/** + * 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.demo.paas.service.scheduler.quartz.plugins; + +import static org.quartz.SimpleScheduleBuilder.simpleSchedule; +import static org.quartz.TriggerBuilder.newTrigger; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.net.URLDecoder; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; + +import org.jclouds.logging.Logger; +import org.quartz.JobBuilder; +import org.quartz.JobDetail; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.SimpleTrigger; +import org.quartz.TriggerKey; +import org.quartz.jobs.FileScanJob; +import org.quartz.jobs.FileScanListener; +import org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin; +import org.quartz.simpl.CascadingClassLoadHelper; +import org.quartz.spi.ClassLoadHelper; +import org.quartz.spi.SchedulerPlugin; +import org.quartz.xml.XMLSchedulingDataProcessor; + +/** + * A copy of {@link XMLSchedulingDataProcessorPlugin} that does not reference + * {@code javax.transaction.UserTransaction} as so does not require a dependency + * on JTA. + * + * @author Andrew Phillips + * @see XMLSchedulingDataProcessorPlugin + */ +public class TransactionlessXmlSchedulingDataProcessorPlugin implements + FileScanListener, SchedulerPlugin { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + private static final int MAX_JOB_TRIGGER_NAME_LEN = 80; + private static final String JOB_INITIALIZATION_PLUGIN_NAME = "JobSchedulingDataLoaderPlugin"; + private static final String FILE_NAME_DELIMITERS = ","; + + private String name; + private Scheduler scheduler; + private final Logger log = Logger.CONSOLE; + + private boolean failOnFileNotFound = true; + + private String fileNames = XMLSchedulingDataProcessor.QUARTZ_XML_DEFAULT_FILE_NAME; + + // Populated by initialization + private Map jobFiles = new LinkedHashMap(); + + private long scanInterval = 0; + + boolean started = false; + + protected ClassLoadHelper classLoadHelper = null; + + private Set jobTriggerNameSet = new HashSet(); + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + * Comma separated list of file names (with paths) to the XML files that should be read. + */ + public String getFileNames() { + return fileNames; + } + + /** + * The file name (and path) to the XML file that should be read. + */ + public void setFileNames(String fileNames) { + this.fileNames = fileNames; + } + + /** + * The interval (in seconds) at which to scan for changes to the file. + * If the file has been changed, it is re-loaded and parsed. The default + * value for the interval is 0, which disables scanning. + * + * @return Returns the scanInterval. + */ + public long getScanInterval() { + return scanInterval / 1000; + } + + /** + * The interval (in seconds) at which to scan for changes to the file. + * If the file has been changed, it is re-loaded and parsed. The default + * value for the interval is 0, which disables scanning. + * + * @param scanInterval The scanInterval to set. + */ + public void setScanInterval(long scanInterval) { + this.scanInterval = scanInterval * 1000; + } + + /** + * Whether or not initialization of the plugin should fail (throw an + * exception) if the file cannot be found. Default is true. + */ + public boolean isFailOnFileNotFound() { + return failOnFileNotFound; + } + + /** + * Whether or not initialization of the plugin should fail (throw an + * exception) if the file cannot be found. Default is true. + */ + public void setFailOnFileNotFound(boolean failOnFileNotFound) { + this.failOnFileNotFound = failOnFileNotFound; + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * SchedulerPlugin Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *

+ * Called during creation of the Scheduler in order to give + * the SchedulerPlugin a chance to initialize. + *

+ * + * @throws org.quartz.SchedulerConfigException + * if there is an error initializing. + */ + @Override + public void initialize(String name, Scheduler scheduler) + throws SchedulerException { + this.name = name; + this.scheduler = scheduler; + + classLoadHelper = new CascadingClassLoadHelper(); + classLoadHelper.initialize(); + + log.info("Registering Quartz Job Initialization Plug-in."); + + // Create JobFile objects + StringTokenizer stok = new StringTokenizer(fileNames, FILE_NAME_DELIMITERS); + while (stok.hasMoreTokens()) { + final String fileName = stok.nextToken(); + final JobFile jobFile = new JobFile(fileName); + jobFiles.put(fileName, jobFile); + } + } + + @Override + public void start() { + try { + if (jobFiles.isEmpty() == false) { + + if (scanInterval > 0) { + scheduler.getContext().put(JOB_INITIALIZATION_PLUGIN_NAME + '_' + name, this); + } + + Iterator iterator = jobFiles.values().iterator(); + while (iterator.hasNext()) { + JobFile jobFile = iterator.next(); + + if (scanInterval > 0) { + String jobTriggerName = buildJobTriggerName(jobFile.getFileBasename()); + TriggerKey tKey = new TriggerKey(jobTriggerName, JOB_INITIALIZATION_PLUGIN_NAME); + + // remove pre-existing job/trigger, if any + scheduler.unscheduleJob(tKey); + + // TODO: convert to use builder + SimpleTrigger trig = newTrigger() + .withIdentity(jobTriggerName, JOB_INITIALIZATION_PLUGIN_NAME) + .startNow() + .endAt(null) + .withSchedule(simpleSchedule() + .repeatForever() + .withIntervalInMilliseconds(scanInterval)) + .build(); + + JobDetail job = JobBuilder.newJob(FileScanJob.class) + .withIdentity(jobTriggerName, JOB_INITIALIZATION_PLUGIN_NAME) + .build(); + job.getJobDataMap().put(FileScanJob.FILE_NAME, jobFile.getFileName()); + job.getJobDataMap().put(FileScanJob.FILE_SCAN_LISTENER_NAME, JOB_INITIALIZATION_PLUGIN_NAME + '_' + name); + + scheduler.scheduleJob(job, trig); + log.debug("Scheduled file scan job for data file: {}, at interval: {}", jobFile.getFileName(), scanInterval); + } + + processFile(jobFile); + } + } + } catch(SchedulerException se) { + log.error("Error starting background-task for watching jobs file.", se); + } finally { + started = true; + } + } + + /** + * Helper method for generating unique job/trigger name for the + * file scanning jobs (one per FileJob). The unique names are saved + * in jobTriggerNameSet. + */ + private String buildJobTriggerName( + String fileBasename) { + // Name w/o collisions will be prefix + _ + filename (with '.' of filename replaced with '_') + // For example: JobInitializationPlugin_jobInitializer_myjobs_xml + String jobTriggerName = JOB_INITIALIZATION_PLUGIN_NAME + '_' + name + '_' + fileBasename.replace('.', '_'); + + // If name is too long (DB column is 80 chars), then truncate to max length + if (jobTriggerName.length() > MAX_JOB_TRIGGER_NAME_LEN) { + jobTriggerName = jobTriggerName.substring(0, MAX_JOB_TRIGGER_NAME_LEN); + } + + // Make sure this name is unique in case the same file name under different + // directories is being checked, or had a naming collision due to length truncation. + // If there is a conflict, keep incrementing a _# suffix on the name (being sure + // not to get too long), until we find a unique name. + int currentIndex = 1; + while (jobTriggerNameSet.add(jobTriggerName) == false) { + // If not our first time through, then strip off old numeric suffix + if (currentIndex > 1) { + jobTriggerName = jobTriggerName.substring(0, jobTriggerName.lastIndexOf('_')); + } + + String numericSuffix = "_" + currentIndex++; + + // If the numeric suffix would make the name too long, then make room for it. + if (jobTriggerName.length() > (MAX_JOB_TRIGGER_NAME_LEN - numericSuffix.length())) { + jobTriggerName = jobTriggerName.substring(0, (MAX_JOB_TRIGGER_NAME_LEN - numericSuffix.length())); + } + + jobTriggerName += numericSuffix; + } + + return jobTriggerName; + } + + @Override + public void shutdown() { + // nothing to do + } + + private void processFile(JobFile jobFile) { + if (jobFile == null || !jobFile.getFileFound()) { + return; + } + + try { + XMLSchedulingDataProcessor processor = + new XMLSchedulingDataProcessor(this.classLoadHelper); + + processor.addJobGroupToNeverDelete(JOB_INITIALIZATION_PLUGIN_NAME); + processor.addTriggerGroupToNeverDelete(JOB_INITIALIZATION_PLUGIN_NAME); + + processor.processFileAndScheduleJobs( + jobFile.getFileName(), + jobFile.getFileName(), // systemId + scheduler); + } catch (Exception e) { + log.error("Error scheduling jobs: " + e.getMessage(), e); + } + } + + public void processFile(String filePath) { + processFile((JobFile)jobFiles.get(filePath)); + } + + /** + * @see org.quartz.jobs.FileScanListener#fileUpdated(java.lang.String) + */ + public void fileUpdated(String fileName) { + if (started) { + processFile(fileName); + } + } + + class JobFile { + private String fileName; + + // These are set by initialize() + private String filePath; + private String fileBasename; + private boolean fileFound; + + protected JobFile(String fileName) throws SchedulerException { + this.fileName = fileName; + initialize(); + } + + protected String getFileName() { + return fileName; + } + + protected boolean getFileFound() { + return fileFound; + } + + protected String getFilePath() { + return filePath; + } + + protected String getFileBasename() { + return fileBasename; + } + + private void initialize() throws SchedulerException { + InputStream f = null; + try { + String furl = null; + + File file = new File(getFileName()); // files in filesystem + if (!file.exists()) { + URL url = classLoadHelper.getResource(getFileName()); + if(url != null) { + try { + furl = URLDecoder.decode(url.getPath(), "UTF-8"); + } catch (UnsupportedEncodingException e) { + furl = url.getPath(); + } + file = new File(furl); + try { + f = url.openStream(); + } catch (IOException ignor) { + // Swallow the exception + } + } + } else { + try { + f = new java.io.FileInputStream(file); + }catch (FileNotFoundException e) { + // ignore + } + } + + if (f == null) { + if (isFailOnFileNotFound()) { + throw new SchedulerException( + "File named '" + getFileName() + "' does not exist."); + } else { + log.warn("File named '" + getFileName() + "' does not exist."); + } + } else { + fileFound = true; + } + filePath = (furl != null) ? furl : file.getAbsolutePath(); + fileBasename = file.getName(); + } finally { + try { + if (f != null) { + f.close(); + } + } catch (IOException ioe) { + log.warn("Error closing jobs file " + getFileName(), ioe); + } + } + } + } +} \ No newline at end of file diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/taskqueue/TaskQueue.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/taskqueue/TaskQueue.java new file mode 100644 index 0000000000..e317a305cf --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/taskqueue/TaskQueue.java @@ -0,0 +1,107 @@ +/** + * 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.demo.paas.service.taskqueue; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.String.format; + +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeUnit; + +import org.jclouds.demo.paas.RunnableHttpRequest; +import org.jclouds.demo.paas.RunnableHttpRequest.Factory; +import org.jclouds.http.HttpCommandExecutorService; + +import com.google.inject.Provider; + +public class TaskQueue { + protected final Factory httpRequestFactory; + private final Timer timer; + private final ConcurrentLinkedQueue tasks = new ConcurrentLinkedQueue(); + + private TaskQueue(String name, long pollingIntervalMillis, Factory httpRequestFactory) { + this.httpRequestFactory = httpRequestFactory; + timer = new Timer(name); + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + Runnable task = tasks.poll(); + if (task != null) { + task.run(); + } + } + }, 0, pollingIntervalMillis); + } + + public void add(final Runnable task) { + tasks.add(task); + } + + public Factory getHttpRequestFactory() { + return httpRequestFactory; + } + + public void destroy() { + timer.cancel(); + tasks.clear(); + } + + public static Builder builder(HttpCommandExecutorService httpClient) { + return new Builder(httpClient); + } + + public static class Builder implements Provider { + protected final HttpCommandExecutorService httpClient; + protected String name = "default"; + protected long pollingIntervalMillis = TimeUnit.SECONDS.toMillis(1); + + private Builder(HttpCommandExecutorService httpClient) { + this.httpClient = checkNotNull(httpClient, "httpClient"); + } + + public Builder name(String name) { + this.name = checkNotNull(name, "name"); + return this; + } + + public Builder period(TimeUnit period) { + this.pollingIntervalMillis = checkNotNull(period, "period").toMillis(1); + return this; + } + + public Builder period(long pollingIntervalMillis) { + checkArgument(pollingIntervalMillis > 0, "pollingIntervalMillis"); + this.pollingIntervalMillis = pollingIntervalMillis; + return this; + } + + public TaskQueue build() { + return new TaskQueue(name, pollingIntervalMillis, + RunnableHttpRequest.factory(httpClient, format("taskqueue-%s", name))); + } + + @Override + public TaskQueue get() { + return build(); + } + } +} \ No newline at end of file diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/config/GuiceServletConfig.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/config/GuiceServletConfig.java new file mode 100644 index 0000000000..24d0c94c06 --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/config/GuiceServletConfig.java @@ -0,0 +1,153 @@ +/** + * 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.demo.tweetstore.config; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Predicates.in; +import static com.google.common.collect.ImmutableSet.copyOf; +import static com.google.common.collect.Sets.filter; +import static org.jclouds.demo.paas.reference.PaasConstants.PROPERTY_PLATFORM_BASE_URL; +import static org.jclouds.demo.tweetstore.reference.TweetStoreConstants.PROPERTY_TWEETSTORE_BLOBSTORES; +import static org.jclouds.demo.tweetstore.reference.TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER; +import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_ACCESSTOKEN; +import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_ACCESSTOKEN_SECRET; +import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_CONSUMER_KEY; +import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_CONSUMER_SECRET; + +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import javax.servlet.ServletContext; +import javax.servlet.ServletContextEvent; + +import org.jclouds.ContextBuilder; +import org.jclouds.blobstore.BlobStoreContext; +import org.jclouds.demo.paas.PlatformServices; +import org.jclouds.demo.paas.service.taskqueue.TaskQueue; +import org.jclouds.demo.tweetstore.config.util.CredentialsCollector; +import org.jclouds.demo.tweetstore.config.util.PropertiesLoader; +import org.jclouds.demo.tweetstore.controller.AddTweetsController; +import org.jclouds.demo.tweetstore.controller.EnqueueStoresController; +import org.jclouds.demo.tweetstore.controller.StoreTweetsController; + +import twitter4j.Twitter; +import twitter4j.TwitterFactory; +import twitter4j.conf.Configuration; +import twitter4j.conf.ConfigurationBuilder; + +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Module; +import com.google.inject.TypeLiteral; +import com.google.inject.name.Names; +import com.google.inject.servlet.GuiceServletContextListener; +import com.google.inject.servlet.ServletModule; + +/** + * Setup Logging and create Injector for use in testing S3. + * + * @author Adrian Cole + */ +public class GuiceServletConfig extends GuiceServletContextListener { + private Map providerTypeToBlobStoreMap; + private Twitter twitterClient; + private String container; + private TaskQueue queue; + private String baseUrl; + + @Override + public void contextInitialized(ServletContextEvent servletContextEvent) { + ServletContext servletContext = servletContextEvent.getServletContext(); + + Properties props = new PropertiesLoader(servletContext).get(); + Set modules = ImmutableSet.of(); + // shared across all blobstores and used to retrieve tweets + try { + Configuration twitterConf = new ConfigurationBuilder() + .setOAuthConsumerKey(props.getProperty(PROPERTY_TWITTER_CONSUMER_KEY)) + .setOAuthConsumerSecret(props.getProperty(PROPERTY_TWITTER_CONSUMER_SECRET)) + .setOAuthAccessToken(props.getProperty(PROPERTY_TWITTER_ACCESSTOKEN)) + .setOAuthAccessTokenSecret(props.getProperty(PROPERTY_TWITTER_ACCESSTOKEN_SECRET)) + .build(); + twitterClient = new TwitterFactory(twitterConf).getInstance(); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("properties for twitter not configured properly in " + props.toString(), e); + } + // common namespace for storing tweets + container = checkNotNull(props.getProperty(PROPERTY_TWEETSTORE_CONTAINER), PROPERTY_TWEETSTORE_CONTAINER); + + // instantiate and store references to all blobstores by provider name + providerTypeToBlobStoreMap = Maps.newHashMap(); + for (String hint : getBlobstoreContexts(props)) { + providerTypeToBlobStoreMap.put(hint, ContextBuilder.newBuilder(hint) + .modules(modules).overrides(props).build(BlobStoreContext.class)); + } + + // get a queue for submitting store tweet requests and the application's base URL + PlatformServices platform = PlatformServices.get(servletContext); + queue = platform.getTaskQueue("twitter"); + baseUrl = platform.getBaseUrl(); + + super.contextInitialized(servletContextEvent); + } + + private static Iterable getBlobstoreContexts(Properties props) { + Set contexts = new CredentialsCollector().apply(props).keySet(); + String explicitContexts = props.getProperty(PROPERTY_TWEETSTORE_BLOBSTORES); + if (explicitContexts != null) { + contexts = filter(contexts, in(copyOf(Splitter.on(',').split(explicitContexts)))); + } + checkState(!contexts.isEmpty(), "no credentials available for any requested context"); + return contexts; + } + + @Override + protected Injector getInjector() { + return Guice.createInjector(new ServletModule() { + @Override + protected void configureServlets() { + bind(new TypeLiteral>() {}) + .toInstance(providerTypeToBlobStoreMap); + bind(Twitter.class).toInstance(twitterClient); + bind(TaskQueue.class).toInstance(queue); + bindConstant().annotatedWith(Names.named(PROPERTY_PLATFORM_BASE_URL)) + .to(baseUrl); + bindConstant().annotatedWith(Names.named(PROPERTY_TWEETSTORE_CONTAINER)) + .to(container); + serve("/store/*").with(StoreTweetsController.class); + serve("/tweets/*").with(AddTweetsController.class); + serve("/stores/*").with(EnqueueStoresController.class); + } + }); + } + + @Override + public void contextDestroyed(ServletContextEvent servletContextEvent) { + for (BlobStoreContext context : providerTypeToBlobStoreMap.values()) { + context.close(); + } + queue.destroy(); + super.contextDestroyed(servletContextEvent); + } +} \ No newline at end of file diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/config/util/CredentialsCollector.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/config/util/CredentialsCollector.java new file mode 100644 index 0000000000..ce3943376e --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/config/util/CredentialsCollector.java @@ -0,0 +1,153 @@ +/** + * 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.demo.tweetstore.config.util; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Predicates.notNull; +import static com.google.common.collect.Collections2.filter; +import static com.google.common.collect.Collections2.transform; +import static com.google.common.collect.ImmutableSet.copyOf; +import static com.google.common.collect.Maps.filterValues; +import static org.jclouds.util.Maps2.fromKeys; + +import java.util.Collection; +import java.util.Map; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.jclouds.demo.tweetstore.config.util.CredentialsCollector.Credential; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Function; +import com.google.common.base.Predicate; + +/** + * Reads provider credentials from a {@link Properties} bag. + * + * @author Andrew Phillips + * + */ +public class CredentialsCollector implements Function> { + private static final String IDENTITY_PROPERTY_SUFFIX = ".identity"; + private static final String CREDENTIAL_PROPERTY_SUFFIX = ".credential"; + + // using the identity for provider name extraction + private static final Pattern IDENTITY_PROPERTY_PATTERN = + Pattern.compile("([a-zA-Z0-9-]+)" + Pattern.quote(IDENTITY_PROPERTY_SUFFIX)); + + @Override + public Map apply(final Properties properties) { + Collection providerNames = transform( + filter(properties.stringPropertyNames(), MatchesPattern.matches(IDENTITY_PROPERTY_PATTERN)), + new Function() { + @Override + public String apply(String input) { + Matcher matcher = IDENTITY_PROPERTY_PATTERN.matcher(input); + // as a side-effect, sets the matching group! + checkState(matcher.matches(), "'%s' should match '%s'", input, IDENTITY_PROPERTY_PATTERN); + return matcher.group(1); + } + }); + /* + * Providers without a credential property result in null values, which are + * removed from the returned map. + */ + return filterValues(fromKeys(copyOf(providerNames), new Function() { + @Override + public Credential apply(String providerName) { + String identity = properties.getProperty(providerName + IDENTITY_PROPERTY_SUFFIX); + String credential = properties.getProperty(providerName + CREDENTIAL_PROPERTY_SUFFIX); + return (((identity != null) && (credential != null)) + ? new Credential(identity, credential) + : null); + } + }), notNull()); + } + + public static class Credential { + private final String identity; + private final String credential; + + public Credential(String identity, String credential) { + this.identity = checkNotNull(identity, "identity"); + this.credential = checkNotNull(credential, "credential"); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((credential == null) ? 0 : credential.hashCode()); + result = prime * result + + ((identity == null) ? 0 : identity.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; + Credential other = (Credential) obj; + if (credential == null) { + if (other.credential != null) + return false; + } else if (!credential.equals(other.credential)) + return false; + if (identity == null) { + if (other.identity != null) + return false; + } else if (!identity.equals(other.identity)) + return false; + return true; + } + + public String getIdentity() { + return identity; + } + + public String getCredential() { + return credential; + } + } + + @GwtIncompatible(value = "java.util.regex.Pattern") + private static class MatchesPattern implements Predicate { + private final Pattern pattern; + + private MatchesPattern(Pattern pattern) { + this.pattern = pattern; + } + + @Override + public boolean apply(String input) { + return pattern.matcher(input).matches(); + } + + private static MatchesPattern matches(Pattern pattern) { + return new MatchesPattern(pattern); + } + } +} diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/config/util/PropertiesLoader.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/config/util/PropertiesLoader.java new file mode 100644 index 0000000000..dafa5c311b --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/config/util/PropertiesLoader.java @@ -0,0 +1,59 @@ +/** + * 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.demo.tweetstore.config.util; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import javax.servlet.ServletContext; + +import com.google.common.io.Closeables; +import com.google.inject.Provider; + +/** + * @author Andrew Phillips + */ +public class PropertiesLoader implements Provider{ + private static final String PROPERTIES_FILE = "/WEB-INF/jclouds.properties"; + + private final Properties properties; + + public PropertiesLoader(ServletContext context) { + properties = loadJcloudsProperties(context); + } + + private static Properties loadJcloudsProperties(ServletContext context) { + InputStream input = context.getResourceAsStream(PROPERTIES_FILE); + Properties props = new Properties(); + try { + props.load(input); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + Closeables.closeQuietly(input); + } + return props; + } + + @Override + public Properties get() { + return properties; + } +} diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/controller/AddTweetsController.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/controller/AddTweetsController.java new file mode 100644 index 0000000000..007fbafdef --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/controller/AddTweetsController.java @@ -0,0 +1,96 @@ +/** + * 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.demo.tweetstore.controller; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.jclouds.blobstore.BlobStoreContext; +import org.jclouds.demo.tweetstore.domain.StoredTweetStatus; +import org.jclouds.demo.tweetstore.functions.ServiceToStoredTweetStatuses; +import org.jclouds.logging.Logger; + +import com.google.common.base.Function; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +/** + * Shows an example of how to use @{link BlobStoreContext} injected with Guice. + * + * @author Adrian Cole + */ +@Singleton +public class AddTweetsController extends HttpServlet implements + Function, List> { + + /** The serialVersionUID */ + private static final long serialVersionUID = 3888348023150822683L; + private final Map contexts; + private final ServiceToStoredTweetStatuses blobStoreContextToContainerResult; + + @Resource + protected Logger logger = Logger.NULL; + + @Inject + AddTweetsController(Map contexts, + ServiceToStoredTweetStatuses blobStoreContextToContainerResult) { + this.contexts = contexts; + this.blobStoreContextToContainerResult = blobStoreContextToContainerResult; + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + try { + addMyTweetsToRequest(request); + RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/tweets.jsp"); + dispatcher.forward(request, response); + } catch (Exception e) { + logger.error(e, "Error listing containers"); + throw new ServletException(e); + } + } + + void addMyTweetsToRequest(HttpServletRequest request) throws InterruptedException, + ExecutionException, TimeoutException { + request.setAttribute("tweets", apply(contexts.keySet())); + } + + public List apply(Set in) { + List statuses = Lists.newArrayList(); + for (Iterable list : Iterables.transform(in, + blobStoreContextToContainerResult)) { + Iterables.addAll(statuses, list); + } + return statuses; + } +} diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/controller/EnqueueStoresController.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/controller/EnqueueStoresController.java new file mode 100644 index 0000000000..592eaaa8bd --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/controller/EnqueueStoresController.java @@ -0,0 +1,101 @@ +/** + * 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.demo.tweetstore.controller; + +import static com.google.common.base.Strings.nullToEmpty; +import static org.jclouds.demo.paas.RunnableHttpRequest.PLATFORM_REQUEST_ORIGINATOR_HEADER; + +import java.io.IOException; +import java.net.URI; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.MediaType; + +import org.jclouds.blobstore.BlobStoreContext; +import org.jclouds.demo.paas.reference.PaasConstants; +import org.jclouds.demo.paas.service.taskqueue.TaskQueue; +import org.jclouds.http.HttpRequest; +import org.jclouds.logging.Logger; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableMultimap; + +/** + * Adds tasks to retrieve and store tweets in all registered contexts to an async + * task queue. + * + * @author Andrew Phillips + * @see StoreTweetsController + */ +@Singleton +public class EnqueueStoresController extends HttpServlet { + /** The serialVersionUID */ + private static final long serialVersionUID = 7215420527854203714L; + + private final Set contextNames; + private final TaskQueue taskQueue; + private final String baseUrl; + + @Resource + protected Logger logger = Logger.NULL; + + @Inject + public EnqueueStoresController(Map contexts, TaskQueue taskQueue, + @Named(PaasConstants.PROPERTY_PLATFORM_BASE_URL) String baseUrl) { + contextNames = contexts.keySet(); + this.taskQueue = taskQueue; + this.baseUrl = baseUrl; + } + + @VisibleForTesting + void enqueueStoreTweetTasks() { + for (String contextName : contextNames) { + logger.debug("enqueuing task to store tweets in blobstore '%s'", contextName); + taskQueue.add(taskQueue.getHttpRequestFactory().create(HttpRequest.builder() + .endpoint(URI.create(baseUrl + "/store/do")) + .headers(ImmutableMultimap.of("context", contextName)) + .method("GET").build())); + } + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + if (!nullToEmpty(request.getHeader(PLATFORM_REQUEST_ORIGINATOR_HEADER)).equals("scheduler")) { + response.sendError(401); + } + + try { + enqueueStoreTweetTasks(); + response.setContentType(MediaType.TEXT_PLAIN); + response.getWriter().println("Done!"); + } catch (Exception e) { + logger.error(e, "Error storing tweets"); + throw new ServletException(e); + } + } +} \ No newline at end of file diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/controller/StoreTweetsController.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/controller/StoreTweetsController.java new file mode 100644 index 0000000000..948c9ff4ca --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/controller/StoreTweetsController.java @@ -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.demo.tweetstore.controller; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Strings.nullToEmpty; +import static org.jclouds.demo.paas.RunnableHttpRequest.PLATFORM_REQUEST_ORIGINATOR_HEADER; + +import java.io.IOException; +import java.util.Map; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.MediaType; + +import org.jclouds.blobstore.BlobMap; +import org.jclouds.blobstore.BlobStoreContext; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.demo.tweetstore.reference.TweetStoreConstants; +import org.jclouds.logging.Logger; +import org.jclouds.rest.AuthorizationException; + +import twitter4j.Status; +import twitter4j.Twitter; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; + +/** + * Grab tweets related to me and store them into blobstores + * + * @author Adrian Cole + */ +@Singleton +public class StoreTweetsController extends HttpServlet { + + private static final class StatusToBlob implements Function { + private final BlobMap map; + + private StatusToBlob(BlobMap map) { + this.map = map; + } + + public Blob apply(Status from) { + Blob to = map.blobBuilder().name(from.getId() + "").build(); + to.setPayload(from.getText()); + to.getPayload().getContentMetadata().setContentType(MediaType.TEXT_PLAIN); + to.getMetadata().getUserMetadata().put(TweetStoreConstants.SENDER_NAME, from.getUser().getScreenName()); + return to; + } + } + + /** The serialVersionUID */ + private static final long serialVersionUID = 7215420527854203714L; + + private final Map contexts; + private final Twitter client; + private final String container; + + @Resource + protected Logger logger = Logger.NULL; + + @Inject + @VisibleForTesting + public StoreTweetsController(Map contexts, + @Named(TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER) String container, Twitter client) { + this.container = container; + this.contexts = contexts; + this.client = client; + } + + @VisibleForTesting + public void addMyTweets(String contextName, Iterable responseList) { + BlobStoreContext context = checkNotNull(contexts.get(contextName), "no context for " + contextName + " in " + + contexts.keySet()); + BlobMap map = context.createBlobMap(container); + for (Status status : responseList) { + Blob blob = null; + try { + blob = new StatusToBlob(map).apply(status); + map.put(status.getId() + "", blob); + } catch (AuthorizationException e) { + throw e; + } catch (Exception e) { + logger.error(e, "Error storing tweet %s (blob[%s]) on map %s/%s", status.getId(), blob, context, container); + } + } + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + if (nullToEmpty(request.getHeader(PLATFORM_REQUEST_ORIGINATOR_HEADER)).equals("taskqueue-twitter")) { + try { + String contextName = checkNotNull(request.getHeader("context"), "missing header context"); + logger.info("retrieving tweets"); + addMyTweets(contextName, client.getMentions()); + logger.debug("done storing tweets"); + response.setContentType(MediaType.TEXT_PLAIN); + response.getWriter().println("Done!"); + } catch (Exception e) { + logger.error(e, "Error storing tweets"); + throw new ServletException(e); + } + } else { + response.sendError(401); + } + } +} \ No newline at end of file diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/domain/StoredTweetStatus.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/domain/StoredTweetStatus.java new file mode 100644 index 0000000000..42ad65df01 --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/domain/StoredTweetStatus.java @@ -0,0 +1,149 @@ +/** + * 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.demo.tweetstore.domain; + +import java.io.Serializable; + +/** + * + * @author Adrian Cole + */ +public class StoredTweetStatus implements Comparable, Serializable { + + /** The serialVersionUID */ + private static final long serialVersionUID = -3257496189689220018L; + private final String service; + private final String host; + private final String container; + private final String id; + private final String from; + private final String tweet; + private final String status; + + @Override + public String toString() { + return "StoredTweetStatus [container=" + container + ", from=" + from + ", host=" + host + + ", id=" + id + ", service=" + service + ", status=" + status + ", tweet=" + tweet + + "]"; + } + + public StoredTweetStatus(String service, String host, String container, String id, String from, + String tweet, String status) { + this.service = service; + this.host = host; + this.container = container; + this.id = id; + this.from = from; + this.tweet = tweet; + this.status = status; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((container == null) ? 0 : container.hashCode()); + result = prime * result + ((from == null) ? 0 : from.hashCode()); + result = prime * result + ((host == null) ? 0 : host.hashCode()); + result = prime * result + ((id == null) ? 0 : id.hashCode()); + result = prime * result + ((service == null) ? 0 : service.hashCode()); + result = prime * result + ((tweet == null) ? 0 : tweet.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; + StoredTweetStatus other = (StoredTweetStatus) obj; + if (container == null) { + if (other.container != null) + return false; + } else if (!container.equals(other.container)) + return false; + if (from == null) { + if (other.from != null) + return false; + } else if (!from.equals(other.from)) + return false; + if (host == null) { + if (other.host != null) + return false; + } else if (!host.equals(other.host)) + return false; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + if (service == null) { + if (other.service != null) + return false; + } else if (!service.equals(other.service)) + return false; + if (tweet == null) { + if (other.tweet != null) + return false; + } else if (!tweet.equals(other.tweet)) + return false; + return true; + } + + + public String getService() { + return service; + } + + public String getHost() { + return host; + } + + public String getContainer() { + return container; + } + + public String getFrom() { + return from; + } + + public String getTweet() { + return tweet; + } + + public String getStatus() { + return status; + } + + public int compareTo(StoredTweetStatus o) { + if (id == null) + return -1; + return (int) ((this == o) ? 0 : id.compareTo(o.id)); + } + + + public String getId() { + return id; + } + +} diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/functions/KeyToStoredTweetStatus.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/functions/KeyToStoredTweetStatus.java new file mode 100644 index 0000000000..2a6ea0a69c --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/functions/KeyToStoredTweetStatus.java @@ -0,0 +1,71 @@ +/** + * 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.demo.tweetstore.functions; + +import static org.jclouds.util.Strings2.toStringAndClose; + +import javax.annotation.Resource; + +import org.jclouds.blobstore.BlobMap; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.demo.tweetstore.domain.StoredTweetStatus; +import org.jclouds.demo.tweetstore.reference.TweetStoreConstants; +import org.jclouds.logging.Logger; + +import com.google.common.base.Function; + +/** + * + * @author Adrian Cole + */ +public class KeyToStoredTweetStatus implements Function { + private final String host; + private final BlobMap map; + private final String service; + private final String container; + + @Resource + protected Logger logger = Logger.NULL; + + KeyToStoredTweetStatus(BlobMap map, String service, String host, String container) { + this.host = host; + this.map = map; + this.service = service; + this.container = container; + } + + public StoredTweetStatus apply(String id) { + String status; + String from; + String tweet; + try { + long start = System.currentTimeMillis(); + Blob blob = map.get(id); + status = ((System.currentTimeMillis() - start) + "ms"); + from = blob.getMetadata().getUserMetadata().get(TweetStoreConstants.SENDER_NAME); + tweet = toStringAndClose(blob.getPayload().getInput()); + } catch (Exception e) { + logger.error(e, "Error listing container %s//%s/%s", service, container, id); + status = (e.getMessage()); + tweet = ""; + from = ""; + } + return new StoredTweetStatus(service, host, container, id, from, tweet, status); + } +} diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/functions/ServiceToStoredTweetStatuses.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/functions/ServiceToStoredTweetStatuses.java new file mode 100644 index 0000000000..b29ec14549 --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/functions/ServiceToStoredTweetStatuses.java @@ -0,0 +1,73 @@ +/** + * 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.demo.tweetstore.functions; + +import java.net.URI; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.Context; +import org.jclouds.blobstore.BlobMap; +import org.jclouds.blobstore.BlobStoreContext; +import org.jclouds.demo.tweetstore.domain.StoredTweetStatus; +import org.jclouds.demo.tweetstore.reference.TweetStoreConstants; +import org.jclouds.logging.Logger; + +import com.google.common.base.Function; +import com.google.common.collect.Iterables; + +@Singleton +public class ServiceToStoredTweetStatuses implements Function> { + + private final Map contexts; + private final String container; + + @Inject + public ServiceToStoredTweetStatuses(Map contexts, + @Named(TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER) String container) { + this.contexts = contexts; + this.container = container; + } + + @Resource + protected Logger logger = Logger.NULL; + + public Iterable apply(String service) { + BlobStoreContext context = contexts.get(service); + String host = URI.create(context.unwrap(Context.class).getProviderMetadata().getEndpoint()).getHost(); + try { + BlobMap blobMap = context.createBlobMap(container); + Set blobs = blobMap.keySet(); + return Iterables.transform(blobs, new KeyToStoredTweetStatus(blobMap, service, host, + container)); + } catch (Exception e) { + StoredTweetStatus result = new StoredTweetStatus(service, host, container, null, null, + null, e.getMessage()); + logger.error(e, "Error listing service %s", service); + return Collections.singletonList(result); + } + + } +} \ No newline at end of file diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/reference/TweetStoreConstants.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/reference/TweetStoreConstants.java new file mode 100644 index 0000000000..42ec480ae2 --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/reference/TweetStoreConstants.java @@ -0,0 +1,34 @@ +/** + * 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.demo.tweetstore.reference; + +/** + * Configuration properties and constants used in TweetStore connections. + * + * @author Adrian Cole + */ +public interface TweetStoreConstants { + static final String PROPERTY_TWEETSTORE_BLOBSTORES = "jclouds.tweetstore.blobstores"; + static final String PROPERTY_TWEETSTORE_CONTAINER = "jclouds.tweetstore.container"; + /** + * Note that this has to conform to restrictions of all blobstores. for + * example, azure doesn't support periods. + */ + static final String SENDER_NAME = "sendername"; +} diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/reference/TwitterConstants.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/reference/TwitterConstants.java new file mode 100644 index 0000000000..dc8b97915f --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/reference/TwitterConstants.java @@ -0,0 +1,31 @@ +/** + * 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.demo.tweetstore.reference; + +/** + * Configuration properties and constants used in Twitter connections. + * + * @author Andrew Phillips + */ +public interface TwitterConstants { + static final String PROPERTY_TWITTER_CONSUMER_KEY = "twitter.consumer.identity"; + static final String PROPERTY_TWITTER_CONSUMER_SECRET = "twitter.consumer.credential"; + static final String PROPERTY_TWITTER_ACCESSTOKEN = "twitter.access.identity"; + static final String PROPERTY_TWITTER_ACCESSTOKEN_SECRET = "twitter.access.credential"; +} diff --git a/demos/tweetstore/heroku-tweetstore/src/main/platform/.gitignore b/demos/tweetstore/heroku-tweetstore/src/main/platform/.gitignore new file mode 100644 index 0000000000..843dfe79c0 --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/platform/.gitignore @@ -0,0 +1 @@ +# PaaS vendor specific files go in here \ No newline at end of file diff --git a/demos/tweetstore/heroku-tweetstore/src/main/resources/jobs.xml b/demos/tweetstore/heroku-tweetstore/src/main/resources/jobs.xml new file mode 100644 index 0000000000..b740fdd52f --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/resources/jobs.xml @@ -0,0 +1,29 @@ + + + + + + enqueue-store-tweet-tasks + Enqueue 'store tweet' tasks for all contexts + org.jclouds.demo.paas.service.scheduler.HttpRequestJob + + + url + /stores/do + + + + + + + submit-recurring-job + enqueue-store-tweet-tasks + 10 + MINUTE + + + + \ No newline at end of file diff --git a/demos/tweetstore/heroku-tweetstore/src/main/resources/quartz.properties b/demos/tweetstore/heroku-tweetstore/src/main/resources/quartz.properties new file mode 100644 index 0000000000..12a0fcfe91 --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/resources/quartz.properties @@ -0,0 +1,28 @@ +#============================================================================ +# Configure Main Scheduler Properties +#============================================================================ + +org.quartz.scheduler.skipUpdateCheck: true + +#============================================================================ +# Configure ThreadPool +#============================================================================ + +org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool +org.quartz.threadPool.threadCount: 1 + +#============================================================================ +# Configure JobStore +#============================================================================ + +org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore + +#============================================================================ +# Configure the Job Initialization Plugin +#============================================================================ + +org.quartz.plugin.jobInitializer.class: org.jclouds.demo.paas.service.scheduler.quartz.plugins.TransactionlessXmlSchedulingDataProcessorPlugin +org.quartz.plugin.jobInitializer.fileNames: jobs.xml +org.quartz.plugin.jobInitializer.failOnFileNotFound: true +org.quartz.plugin.jobInitializer.scanInterval: 0 +#org.quartz.plugin.jobInitializer.wrapInUserTransaction: false \ No newline at end of file diff --git a/demos/tweetstore/heroku-tweetstore/src/main/webapp/WEB-INF/web.xml b/demos/tweetstore/heroku-tweetstore/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000..9482bd9d9e --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,60 @@ + + + + jclouds-tweetstore + + + quartz:scheduler-context-servlet-context-key + servlet-context + + + + + guiceFilter + com.google.inject.servlet.GuiceFilter + + + + guiceFilter + /* + + + + + org.jclouds.demo.paas.config.PlatformServicesInitializer + + + + org.quartz.ee.servlet.QuartzInitializerListener + + + + org.jclouds.demo.tweetstore.config.GuiceServletConfig + + + + index.jsp + + + \ No newline at end of file diff --git a/demos/tweetstore/heroku-tweetstore/src/main/webapp/index.jsp b/demos/tweetstore/heroku-tweetstore/src/main/webapp/index.jsp new file mode 100644 index 0000000000..d8d1724bcf --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/webapp/index.jsp @@ -0,0 +1,31 @@ +<%-- + + 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. + +--%> + + +jclouds: anyweight cloudware for java + + +

Welcome!

+

Click here to see tweets about jclouds.

+

+ + diff --git a/demos/tweetstore/heroku-tweetstore/src/main/webapp/tweets.jsp b/demos/tweetstore/heroku-tweetstore/src/main/webapp/tweets.jsp new file mode 100644 index 0000000000..1b30f7ea11 --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/webapp/tweets.jsp @@ -0,0 +1,109 @@ +<%-- + + 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. + +--%> +<%@ page buffer="20kb"%> +<%@ taglib uri="http://displaytag.sf.net" prefix="display"%> + + +jclouds: anyweight cloudware for java + + + +

Tweets in Clouds

+ + + + + + + +
+
+ + + + + + + + +
+
+ + diff --git a/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/config/util/CredentialsCollectorTest.java b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/config/util/CredentialsCollectorTest.java new file mode 100644 index 0000000000..031bb199fc --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/config/util/CredentialsCollectorTest.java @@ -0,0 +1,82 @@ +/** + * 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.demo.tweetstore.config.util; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.Map; +import java.util.Properties; + +import org.jclouds.demo.tweetstore.config.util.CredentialsCollector; +import org.jclouds.demo.tweetstore.config.util.CredentialsCollector.Credential; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; + +/** + * Tests behavior of {@code CredentialsCollector} + * + * @author Andrew Phillips + */ +@Test(groups = "unit") +public class CredentialsCollectorTest { + private CredentialsCollector collector = new CredentialsCollector(); + + public void testEmptyProperties() { + assertTrue(collector.apply(new Properties()).isEmpty(), + "Expected returned map to be empty"); + } + + public void testNoCredentials() { + Properties properties = propertiesOf(ImmutableMap.of("not-an-identity", + "v1", "not-a-credential", "v2")); + assertTrue(collector.apply(properties).isEmpty(), + "Expected returned map to be empty"); + } + + private static Properties propertiesOf(Map entries) { + Properties properties = new Properties(); + properties.putAll(entries); + return properties; + } + + public void testNonMatchingCredentials() { + Properties properties = propertiesOf(ImmutableMap.of("non_matching.identity", "v1", + "non_matching.credential", "v2")); + assertTrue(collector.apply(properties).isEmpty(), + "Expected returned map to be empty"); + } + + public void testIncompleteCredentials() { + Properties properties = propertiesOf(ImmutableMap.of("acme.identity", "v1", + "acme-2.credential", "v2")); + assertTrue(collector.apply(properties).isEmpty(), + "Expected returned map to be empty"); + } + + public void testCredentials() { + Properties properties = propertiesOf(ImmutableMap.of("acme.identity", "v1", + "acme.credential", "v2", "acme-2.identity", "v3", + "acme-2.credential", "v4")); + assertEquals(collector.apply(properties), + ImmutableMap.of("acme", new Credential("v1", "v2"), + "acme-2", new Credential("v3", "v4"))); + } +} \ No newline at end of file diff --git a/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/AddTweetsControllerTest.java b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/AddTweetsControllerTest.java new file mode 100644 index 0000000000..1c93403650 --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/AddTweetsControllerTest.java @@ -0,0 +1,78 @@ +/** + * 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.demo.tweetstore.controller; + +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import org.jclouds.ContextBuilder; +import org.jclouds.blobstore.BlobStoreContext; +import org.jclouds.blobstore.TransientApiMetadata; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.demo.tweetstore.domain.StoredTweetStatus; +import org.jclouds.demo.tweetstore.functions.ServiceToStoredTweetStatuses; +import org.jclouds.demo.tweetstore.reference.TweetStoreConstants; +import org.testng.annotations.Test; +import org.testng.collections.Maps; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; + +/** + * Tests behavior of {@code AddTweetsController} + * + * @author Adrian Cole + */ +@Test(groups = "unit") +public class AddTweetsControllerTest { + + Map createServices(String container) throws InterruptedException, + ExecutionException { + Map services = Maps.newHashMap(); + TransientApiMetadata transientApiMetadata = TransientApiMetadata.builder().build(); + for (String name : new String[] { "1", "2" }) { + BlobStoreContext context = ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class); + context.getAsyncBlobStore().createContainerInLocation(null, container).get(); + Blob blob = context.getAsyncBlobStore().blobBuilder("1").build(); + blob.getMetadata().getUserMetadata().put(TweetStoreConstants.SENDER_NAME, "frank"); + blob.setPayload("I love beans!"); + context.getAsyncBlobStore().putBlob(container, blob).get(); + services.put(name, context); + } + return services; + } + + public void testStoreTweets() throws IOException, InterruptedException, ExecutionException { + String container = "container"; + Map contexts = createServices(container); + + ServiceToStoredTweetStatuses function = new ServiceToStoredTweetStatuses(contexts, container); + AddTweetsController controller = new AddTweetsController(contexts, function); + List list = controller.apply(ImmutableSet.of("1", "2")); + assertEquals(list.size(), 2); + assertEquals(list, ImmutableList.of(new StoredTweetStatus("1", "localhost", container, "1", + "frank", "I love beans!", null), new StoredTweetStatus("2", "localhost", container, + "1", "frank", "I love beans!", null))); + + } +} diff --git a/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/EnqueueStoresControllerTest.java b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/EnqueueStoresControllerTest.java new file mode 100644 index 0000000000..3c5e5b1d80 --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/EnqueueStoresControllerTest.java @@ -0,0 +1,85 @@ +/** + * 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.demo.tweetstore.controller; + +import static org.easymock.EasyMock.*; + +import java.net.URI; +import java.util.Map; + +import org.jclouds.ContextBuilder; +import org.jclouds.blobstore.BlobStoreContext; +import org.jclouds.blobstore.TransientApiMetadata; +import org.jclouds.demo.paas.RunnableHttpRequest; +import org.jclouds.demo.paas.RunnableHttpRequest.Factory; +import org.jclouds.demo.paas.service.taskqueue.TaskQueue; +import org.jclouds.http.HttpRequest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMultimap; + +/** + * Tests behavior of {@code EnqueueStoresController} + * + * @author Andrew Phillips + */ +@Test(groups = "unit") +public class EnqueueStoresControllerTest { + + Map createBlobStores() { + TransientApiMetadata transientApiMetadata = TransientApiMetadata.builder().build(); + Map contexts = ImmutableMap.of( + "test1", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class), + "test2", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class)); + return contexts; + } + + public void testEnqueueStores() { + Map stores = createBlobStores(); + TaskQueue taskQueue = createMock(TaskQueue.class); + Factory httpRequestFactory = createMock(Factory.class); + EnqueueStoresController function = new EnqueueStoresController(stores, + taskQueue, "http://localhost:8080"); + + expect(taskQueue.getHttpRequestFactory()).andStubReturn(httpRequestFactory); + + HttpRequest storeInTest1Request = HttpRequest.builder().endpoint( + URI.create("http://localhost:8080/store/do")) + .headers(ImmutableMultimap.of("context", "test1")).method("GET").build(); + RunnableHttpRequest storeInTest1Task = null; + expect(httpRequestFactory.create(eq(storeInTest1Request))).andReturn(storeInTest1Task); + + HttpRequest storeInTest2Request = HttpRequest.builder().endpoint( + URI.create("http://localhost:8080/store/do")) + .headers(ImmutableMultimap.of("context", "test2")).method("GET").build(); + RunnableHttpRequest storeInTest2Task = null; + expect(httpRequestFactory.create(eq(storeInTest2Request))).andReturn(storeInTest2Task); + + taskQueue.add(storeInTest1Task); + expectLastCall(); + taskQueue.add(storeInTest2Task); + expectLastCall(); + replay(httpRequestFactory, taskQueue); + + function.enqueueStoreTweetTasks(); + + verify(taskQueue); + } +} \ No newline at end of file diff --git a/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/StoreTweetsControllerTest.java b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/StoreTweetsControllerTest.java new file mode 100644 index 0000000000..9cc56351f2 --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/StoreTweetsControllerTest.java @@ -0,0 +1,120 @@ +/** + * 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.demo.tweetstore.controller; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.jclouds.util.Strings2.toStringAndClose; +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ExecutionException; + +import org.jclouds.ContextBuilder; +import org.jclouds.blobstore.BlobMap; +import org.jclouds.blobstore.BlobStoreContext; +import org.jclouds.blobstore.TransientApiMetadata; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.demo.tweetstore.reference.TweetStoreConstants; +import org.testng.annotations.Test; + +import twitter4j.Status; +import twitter4j.Twitter; +import twitter4j.User; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +/** + * Tests behavior of {@code StoreTweetsController} + * + * @author Adrian Cole + */ +@Test(groups = "unit") +public class StoreTweetsControllerTest { + + Twitter createTwitter() { + return createMock(Twitter.class); + } + + Map createBlobStores() throws InterruptedException, ExecutionException { + TransientApiMetadata transientApiMetadata = TransientApiMetadata.builder().build(); + Map contexts = ImmutableMap.of( + "test1", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class), + "test2", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class)); + for (BlobStoreContext blobstore : contexts.values()) { + blobstore.getAsyncBlobStore().createContainerInLocation(null, "favo").get(); + } + return contexts; + } + + public void testStoreTweets() throws IOException, InterruptedException, ExecutionException { + Map stores = createBlobStores(); + StoreTweetsController function = new StoreTweetsController(stores, "favo", createTwitter()); + + User frank = createMock(User.class); + expect(frank.getScreenName()).andReturn("frank").atLeastOnce(); + + Status frankStatus = createMock(Status.class); + expect(frankStatus.getId()).andReturn(1l).atLeastOnce(); + expect(frankStatus.getUser()).andReturn(frank).atLeastOnce(); + expect(frankStatus.getText()).andReturn("I love beans!").atLeastOnce(); + + User jimmy = createMock(User.class); + expect(jimmy.getScreenName()).andReturn("jimmy").atLeastOnce(); + + Status jimmyStatus = createMock(Status.class); + expect(jimmyStatus.getId()).andReturn(2l).atLeastOnce(); + expect(jimmyStatus.getUser()).andReturn(jimmy).atLeastOnce(); + expect(jimmyStatus.getText()).andReturn("cloud is king").atLeastOnce(); + + replay(frank); + replay(frankStatus); + replay(jimmy); + replay(jimmyStatus); + + function.addMyTweets("test1", ImmutableList.of(frankStatus, jimmyStatus)); + function.addMyTweets("test2", ImmutableList.of(frankStatus, jimmyStatus)); + + verify(frank); + verify(frankStatus); + verify(jimmy); + verify(jimmyStatus); + + for (Entry entry : stores.entrySet()) { + BlobMap map = entry.getValue().createBlobMap("favo"); + Blob frankBlob = map.get("1"); + assertEquals(frankBlob.getMetadata().getName(), "1"); + assertEquals(frankBlob.getMetadata().getUserMetadata().get(TweetStoreConstants.SENDER_NAME), "frank"); + assertEquals(frankBlob.getMetadata().getContentMetadata().getContentType(), "text/plain"); + assertEquals(toStringAndClose(frankBlob.getPayload().getInput()), "I love beans!"); + + Blob jimmyBlob = map.get("2"); + assertEquals(jimmyBlob.getMetadata().getName(), "2"); + assertEquals(jimmyBlob.getMetadata().getUserMetadata().get(TweetStoreConstants.SENDER_NAME), "jimmy"); + assertEquals(jimmyBlob.getMetadata().getContentMetadata().getContentType(), "text/plain"); + assertEquals(toStringAndClose(jimmyBlob.getPayload().getInput()), "cloud is king"); + } + + } +} diff --git a/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/functions/KeyToStoredTweetStatusTest.java b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/functions/KeyToStoredTweetStatusTest.java new file mode 100644 index 0000000000..aab06ec0b6 --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/functions/KeyToStoredTweetStatusTest.java @@ -0,0 +1,69 @@ +/** + * 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.demo.tweetstore.functions; + +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.util.concurrent.ExecutionException; + +import org.jclouds.ContextBuilder; +import org.jclouds.blobstore.BlobMap; +import org.jclouds.blobstore.BlobStoreContext; +import org.jclouds.blobstore.TransientApiMetadata; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.demo.tweetstore.domain.StoredTweetStatus; +import org.jclouds.demo.tweetstore.reference.TweetStoreConstants; +import org.testng.annotations.Test; + +/** + * Tests behavior of {@code KeyToStoredTweetStatus} + * + * @author Adrian Cole + */ +@Test(groups = "unit") +public class KeyToStoredTweetStatusTest { + + BlobMap createMap() throws InterruptedException, ExecutionException { + BlobStoreContext context = + ContextBuilder.newBuilder(TransientApiMetadata.builder().build()).build(BlobStoreContext.class); + context.getBlobStore().createContainerInLocation(null, "test1"); + return context.createBlobMap("test1"); + } + + public void testStoreTweets() throws IOException, InterruptedException, ExecutionException { + BlobMap map = createMap(); + Blob blob = map.blobBuilder().name("1").build(); + blob.getMetadata().getUserMetadata().put(TweetStoreConstants.SENDER_NAME, "frank"); + blob.setPayload("I love beans!"); + map.put("1", blob); + String host = "localhost"; + String service = "stub"; + String container = "tweetstore"; + + KeyToStoredTweetStatus function = new KeyToStoredTweetStatus(map, service, host, container); + StoredTweetStatus result = function.apply("1"); + + StoredTweetStatus expected = new StoredTweetStatus(service, host, container, "1", "frank", + "I love beans!", null); + + assertEquals(result, expected); + + } +} diff --git a/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/functions/ServiceToStoredTweetStatusesTest.java b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/functions/ServiceToStoredTweetStatusesTest.java new file mode 100644 index 0000000000..5fec52711e --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/functions/ServiceToStoredTweetStatusesTest.java @@ -0,0 +1,75 @@ +/** + * 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.demo.tweetstore.functions; + +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import org.jclouds.ContextBuilder; +import org.jclouds.blobstore.BlobStoreContext; +import org.jclouds.blobstore.TransientApiMetadata; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.demo.tweetstore.domain.StoredTweetStatus; +import org.jclouds.demo.tweetstore.reference.TweetStoreConstants; +import org.testng.annotations.Test; +import org.testng.collections.Maps; + +import com.google.common.collect.Iterables; + +/** + * Tests behavior of {@code ServiceToStoredTweetStatuses} + * + * @author Adrian Cole + */ +@Test(groups = "unit") +public class ServiceToStoredTweetStatusesTest { + + Map createServices(String container) throws InterruptedException, + ExecutionException { + Map services = Maps.newHashMap(); + TransientApiMetadata transientApiMetadata = TransientApiMetadata.builder().build(); + for (String name : new String[] { "1", "2" }) { + BlobStoreContext context = ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class); + context.getAsyncBlobStore().createContainerInLocation(null, container).get(); + Blob blob = context.getAsyncBlobStore().blobBuilder("1").build(); + blob.getMetadata().getUserMetadata().put(TweetStoreConstants.SENDER_NAME, "frank"); + blob.setPayload("I love beans!"); + context.getAsyncBlobStore().putBlob(container, blob).get(); + services.put(name, context); + } + return services; + } + + public void testStoreTweets() throws IOException, InterruptedException, ExecutionException { + String container = "container"; + Map contexts = createServices(container); + + ServiceToStoredTweetStatuses function = new ServiceToStoredTweetStatuses(contexts, container); + + assertEquals(Iterables.getLast(function.apply("1")), new StoredTweetStatus("1", "localhost", + container, "1", "frank", "I love beans!", null)); + + assertEquals(Iterables.getLast(function.apply("2")), new StoredTweetStatus("2", "localhost", + container, "1", "frank", "I love beans!", null)); + + } +} diff --git a/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/JettyServer.java b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/JettyServer.java new file mode 100644 index 0000000000..d358287781 --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/JettyServer.java @@ -0,0 +1,68 @@ +/** + * 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.demo.tweetstore.integration; + +import static com.google.common.io.Closeables.closeQuietly; +import static java.lang.String.format; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Properties; +import java.util.concurrent.TimeUnit; + +import javax.servlet.ServletException; + +/** + * Basic functionality to start a local WAR-supporting Jetty instance. + * + * @author Andrew Phillips + */ +public class JettyServer { + protected Runner2 server; + + public void writePropertiesAndStartServer(final String port, final String warfile, + Properties props) throws IOException, InterruptedException, ServletException { + String filename = String.format( + "%1$s/WEB-INF/jclouds.properties", warfile); + System.err.println("file: " + filename); + storeProperties(filename, props); + assert new File(filename).exists(); + // Jetty uses SLF4J by default + System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.JavaUtilLog"); + System.setProperty("java.util.logging.config.file", + format("%s/WEB-INF/logging.properties", warfile)); + server = Runner2.createRunner(new String[] { "--port", port, warfile }); + server.start(); + TimeUnit.SECONDS.sleep(30); + } + + private static void storeProperties(String filename, Properties props) throws IOException { + FileOutputStream targetFile = new FileOutputStream(filename); + try { + props.store(targetFile, "test"); + } finally { + closeQuietly(targetFile); + } + } + + public void stop() { + server.stop(); + } +} \ No newline at end of file diff --git a/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/Runner2.java b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/Runner2.java new file mode 100644 index 0000000000..feb9e2c408 --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/Runner2.java @@ -0,0 +1,72 @@ +/** + * 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.demo.tweetstore.integration; + +import javax.servlet.ServletException; + +import org.mortbay.jetty.runner.Runner; + +/** + * @see Runner + * @author Andrew Phillips + */ +class Runner2 extends Runner { + public static Runner2 createRunner(String[] args) throws ServletException { + Runner2 runner = new Runner2(); + try { + runner.configure(args); + } catch (Exception exception) { + throw new ServletException("Unable to configure runner", exception); + } + return runner; + } + + private final Thread serverThread; + + private Runner2() { + serverThread = new Thread(new Runnable() { + public void run() { + try { + Runner2.this.run(); + } catch (Exception exception) { + System.err.println("exception starting server: " + exception); + } + } + }); + } + + void start() throws ServletException { + Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { + public void run() { + stop(); + } + })); + serverThread.start(); + } + + void stop() { + try { + _server.stop(); + } catch (Exception exception) { + System.err.println("exception stopping server: " + exception); + } + serverThread.interrupt(); + } + +} diff --git a/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/TweetStoreLiveTest.java b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/TweetStoreLiveTest.java new file mode 100644 index 0000000000..71e483b44b --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/TweetStoreLiveTest.java @@ -0,0 +1,237 @@ +/** + * 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.demo.tweetstore.integration; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.demo.paas.RunnableHttpRequest.PLATFORM_REQUEST_ORIGINATOR_HEADER; +import static org.jclouds.demo.tweetstore.reference.TweetStoreConstants.PROPERTY_TWEETSTORE_BLOBSTORES; +import static org.jclouds.demo.tweetstore.reference.TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER; +import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_ACCESSTOKEN; +import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_ACCESSTOKEN_SECRET; +import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_CONSUMER_KEY; +import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_CONSUMER_SECRET; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import org.jclouds.Context; +import org.jclouds.ContextBuilder; +import org.jclouds.blobstore.BlobStoreContext; +import org.jclouds.demo.tweetstore.controller.StoreTweetsController; +import org.jclouds.logging.log4j.config.Log4JLoggingModule; +import org.jclouds.rest.AuthorizationException; +import org.jclouds.util.Strings2; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import twitter4j.ResponseList; +import twitter4j.Status; +import twitter4j.Twitter; +import twitter4j.TwitterException; +import twitter4j.TwitterFactory; +import twitter4j.conf.Configuration; +import twitter4j.conf.ConfigurationBuilder; + +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.google.inject.Module; + +/** + * Starts up the RUN@cloud for Java Development environment and deploys an application which + * tests accesses twitter and blobstores. + * + * @author Andrew Phillips + */ +@Test(groups = "live", singleThreaded = true) +public class TweetStoreLiveTest { + + JettyServer server; + private URL url; + private Map contexts; + private String container; + private static final Iterable blobstores = + Splitter.on(',').split(getRequiredSystemProperty(PROPERTY_TWEETSTORE_BLOBSTORES)); + private static final Properties props = new Properties(); + + @BeforeTest + void clearAndCreateContainers() throws InterruptedException, ExecutionException, TimeoutException, IOException, + TwitterException { + container = getRequiredSystemProperty(PROPERTY_TWEETSTORE_CONTAINER); + + props.setProperty(PROPERTY_TWEETSTORE_CONTAINER, container); + + // put all identity/credential pairs into the client + addCredentialsForBlobStores(props); + + // example of an ad-hoc client configuration + addConfigurationForTwitter(props); + + // for testing, capture logs. + final Set wiring = ImmutableSet. of(new Log4JLoggingModule()); + this.contexts = Maps.newConcurrentMap(); + + for (String provider : blobstores) { + contexts.put(provider, ContextBuilder.newBuilder(provider) + .modules(wiring).overrides(props).build(BlobStoreContext.class)); + } + + Configuration conf = new ConfigurationBuilder() + .setOAuthConsumerKey(props.getProperty(PROPERTY_TWITTER_CONSUMER_KEY)) + .setOAuthConsumerSecret(props.getProperty(PROPERTY_TWITTER_CONSUMER_SECRET)) + .setOAuthAccessToken(props.getProperty(PROPERTY_TWITTER_ACCESSTOKEN)) + .setOAuthAccessTokenSecret(props.getProperty(PROPERTY_TWITTER_ACCESSTOKEN_SECRET)) + .build(); + Twitter client = new TwitterFactory(conf).getInstance(); + StoreTweetsController controller = new StoreTweetsController(contexts, container, client); + + ResponseList statuses = client.getMentions(); + + boolean deleted = false; + for (BlobStoreContext context : contexts.values()) { + try { + if (context.getBlobStore().containerExists(container)) { + System.err.printf("deleting container %s at %s%n", container, + context.unwrap(Context.class).getProviderMetadata().getEndpoint()); + context.getBlobStore().deleteContainer(container); + deleted = true; + } + } catch (AuthorizationException e) { + throw new AuthorizationException("for context: " + context, e); + } + } + if (deleted) { + System.err.println("sleeping 60 seconds to allow containers to clear"); + Thread.sleep(60000); + } + for (BlobStoreContext context : contexts.values()) { + System.err.printf("creating container %s at %s%n", container, + context.unwrap(Context.class).getProviderMetadata().getEndpoint()); + context.getBlobStore().createContainerInLocation(null, container); + } + + if (deleted) { + System.err.println("sleeping 5 seconds to allow containers to create"); + Thread.sleep(5000); + } + + for (Entry entry : contexts.entrySet()) { + System.err.printf("filling container %s at %s%n", container, entry.getKey()); + controller.addMyTweets(entry.getKey(), statuses); + } + } + + private static String getRequiredSystemProperty(String key) { + return checkNotNull(System.getProperty(key), key); + } + + private void addConfigurationForTwitter(Properties props) { + props.setProperty(PROPERTY_TWITTER_CONSUMER_KEY, + getRequiredSystemProperty("test." + PROPERTY_TWITTER_CONSUMER_KEY)); + props.setProperty(PROPERTY_TWITTER_CONSUMER_SECRET, + getRequiredSystemProperty("test." + PROPERTY_TWITTER_CONSUMER_SECRET)); + props.setProperty(PROPERTY_TWITTER_ACCESSTOKEN, + getRequiredSystemProperty("test." + PROPERTY_TWITTER_ACCESSTOKEN)); + props.setProperty(PROPERTY_TWITTER_ACCESSTOKEN_SECRET, + getRequiredSystemProperty("test." + PROPERTY_TWITTER_ACCESSTOKEN_SECRET)); + } + + private void addCredentialsForBlobStores(Properties props) { + for (String provider : blobstores) { + props.setProperty(provider + ".identity", + getRequiredSystemProperty("test." + provider + ".identity")); + props.setProperty(provider + ".credential", + getRequiredSystemProperty("test." + provider + ".credential")); + } + } + + @BeforeTest(dependsOnMethods = "clearAndCreateContainers") + @Parameters({ "warfile", "jetty.address", "jetty.port" }) + public void startDevAppServer(final String warfile, final String address, final String port) throws Exception { + url = new URL(String.format("http://%s:%s", address, port)); + + server = new JettyServer(); + server.writePropertiesAndStartServer(port, warfile, props); + } + + @Test + public void shouldPass() throws InterruptedException, IOException { + InputStream i = url.openStream(); + String string = Strings2.toStringAndClose(i); + assert string.indexOf("Welcome") >= 0 : string; + } + + @Test(dependsOnMethods = "shouldPass", expectedExceptions = IOException.class) + public void shouldFail() throws InterruptedException, IOException { + new URL(url, "/store/do").openStream(); + } + + @Test(dependsOnMethods = "shouldFail") + public void testPrimeContainers() throws IOException, InterruptedException { + URL gurl = new URL(url, "/store/do"); + + for (String context : blobstores) { + System.out.println("storing at context: " + context); + HttpURLConnection connection = (HttpURLConnection) gurl.openConnection(); + connection.addRequestProperty(PLATFORM_REQUEST_ORIGINATOR_HEADER, "taskqueue-twitter"); + connection.addRequestProperty("context", context); + InputStream i = connection.getInputStream(); + String string = Strings2.toStringAndClose(i); + assert string.indexOf("Done!") >= 0 : string; + connection.disconnect(); + } + + System.err.println("sleeping 20 seconds to allow for eventual consistency delay"); + Thread.sleep(20000); + for (BlobStoreContext context : contexts.values()) { + assert context.createInputStreamMap(container).size() > 0 : context.unwrap(Context.class).getProviderMetadata().getEndpoint(); + } + } + + @Test(invocationCount = 5, dependsOnMethods = "testPrimeContainers") + public void testSerial() throws InterruptedException, IOException { + URL gurl = new URL(url, "/tweets/get"); + InputStream i = gurl.openStream(); + String string = Strings2.toStringAndClose(i); + assert string.indexOf("Tweets in Clouds") >= 0 : string; + } + + @Test(invocationCount = 10, dependsOnMethods = "testPrimeContainers", threadPoolSize = 3) + public void testParallel() throws InterruptedException, IOException { + URL gurl = new URL(url, "/tweets/get"); + InputStream i = gurl.openStream(); + String string = Strings2.toStringAndClose(i); + assert string.indexOf("Tweets in Clouds") >= 0 : string; + } + + @AfterTest + public void stopDevAppServer() throws Exception { + server.stop(); + } +} diff --git a/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/util/ObjectFields.java b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/util/ObjectFields.java new file mode 100644 index 0000000000..cc7c696c55 --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/integration/util/ObjectFields.java @@ -0,0 +1,56 @@ +/** + * 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.demo.tweetstore.integration.util; + +import java.lang.reflect.Field; + +public class ObjectFields { + + public static Object valueOf(String fieldName, Object source) { + return valueOf(fieldName, source, source.getClass()); + } + + public static Object valueOf(String fieldName, Object source, + Class fieldDeclaringClass) { + try { + return getAccessibleField(fieldName, fieldDeclaringClass).get(source); + } catch (Exception exception) { + throw new IllegalArgumentException(exception); + } + } + + private static Field getAccessibleField(String name, Class declaringClass) throws SecurityException, NoSuchFieldException { + Field field = declaringClass.getDeclaredField(name); + field.setAccessible(true); + return field; + } + + public static void set(String fieldName, Object target, Object value) { + set(fieldName, target, value, target.getClass()); + } + + public static void set(String fieldName, Object target, Object value, + Class fieldDeclaringClass) { + try { + getAccessibleField(fieldName, fieldDeclaringClass).set(target, value); + } catch (Exception exception) { + throw new IllegalArgumentException(exception); + } + } +} diff --git a/demos/tweetstore/heroku-tweetstore/src/test/resources/log4j.xml b/demos/tweetstore/heroku-tweetstore/src/test/resources/log4j.xml new file mode 100644 index 0000000000..2e5d01fb9e --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/test/resources/log4j.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/tweetstore/pom.xml b/demos/tweetstore/pom.xml index 22255d6eec..08d3e44a40 100644 --- a/demos/tweetstore/pom.xml +++ b/demos/tweetstore/pom.xml @@ -31,6 +31,7 @@ jclouds TweetStore demos project cf-tweetstore-spring + heroku-tweetstore gae-tweetstore gae-tweetstore-spring runatcloud-tweetstore From 2110145f4966ad0650de988533dab4a03ba9253f Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Tue, 1 May 2012 19:03:37 -0700 Subject: [PATCH 007/148] $%^$&# jetty-runner packages taglibs, jstl and a couple of other dependencies that result in duplicates --- demos/tweetstore/heroku-tweetstore/pom.xml | 18 ++++++ demos/tweetstore/pom.xml | 65 +++++++++++++++++++++- 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/demos/tweetstore/heroku-tweetstore/pom.xml b/demos/tweetstore/heroku-tweetstore/pom.xml index 118ef206b4..1ec0bdc4a8 100644 --- a/demos/tweetstore/heroku-tweetstore/pom.xml +++ b/demos/tweetstore/heroku-tweetstore/pom.xml @@ -67,6 +67,24 @@ jetty-runner 7.5.4.v20111024 test + + + org.eclipse.jetty + jetty-plus + + + org.eclipse.jetty + jetty-jndi + + + org.mortbay.jetty + jsp-2.1-glassfish + + + javax.transaction + jta + + diff --git a/demos/tweetstore/pom.xml b/demos/tweetstore/pom.xml index 08d3e44a40..54a9348141 100644 --- a/demos/tweetstore/pom.xml +++ b/demos/tweetstore/pom.xml @@ -229,7 +229,7 @@ - + javax.servlet @@ -249,6 +249,12 @@ 6.0.32 test + + org.mortbay.jetty + jetty-runner + 7.5.4.v20111024 + test + javax.servlet @@ -259,13 +265,22 @@ javax/servlet/resources/datatypes.dtd javax/servlet/resources/j2ee_1_4.xsd javax/servlet/resources/j2ee_web_services_client_1_1.xsd + javax/servlet/resources/javaee_5.xsd + javax/servlet/resources/javaee_web_services_client_1_2.xsd + javax/servlet/resources/jsp_2_0.xsd + javax/servlet/resources/jsp_2_1.xsd javax/servlet/resources/web-app_2_2.dtd javax/servlet/resources/web-app_2_3.dtd javax/servlet/resources/web-app_2_4.xsd javax/servlet/resources/web-app_2_5.xsd javax/servlet/resources/xml.xsd javax/servlet/LocalStrings.properties + javax/servlet/LocalStrings_fr.properties + javax/servlet/LocalStrings_ja.properties javax/servlet/http/LocalStrings.properties + javax/servlet/http/LocalStrings_es.properties + javax/servlet/http/LocalStrings_fr.properties + javax/servlet/http/LocalStrings_ja.properties @@ -289,6 +304,54 @@ org.apache.PeriodicEventListener + + + + + javax.servlet + jstl + 1.1.2 + runtime + + + taglibs + standard + 1.1.2 + runtime + + + org.mortbay.jetty + jetty-runner + 7.5.4.v20111024 + test + + + + javax.servlet.jsp.jstl + org.apache.taglibs + + + META-INF/c-1_0-rt.tld + META-INF/c-1_0.tld + META-INF/c.tld + META-INF/fmt-1_0-rt.tld + META-INF/fmt-1_0.tld + META-INF/fmt.tld + META-INF/fn.tld + META-INF/permittedTaglibs.tld + META-INF/scriptfree.tld + META-INF/sql-1_0-rt.tld + META-INF/sql-1_0.tld + META-INF/sql.tld + META-INF/x-1_0-rt.tld + META-INF/x-1_0.tld + META-INF/x.tld + org/apache/taglibs/standard/lang/jstl/Resources.properties + org/apache/taglibs/standard/lang/jstl/Resources_ja.properties + org/apache/taglibs/standard/resources/Resources.properties + org/apache/taglibs/standard/resources/Resources_ja.properties + + From 755d51ad27325db385e03b4385e52ec2fbc755e3 Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Tue, 1 May 2012 21:57:49 -0700 Subject: [PATCH 008/148] Remove unused transient blobstore methods Also make some helpers private. Generally, make the transient blobstore more similar to others. --- .../swift/internal/StubSwiftAsyncClient.java | 4 +- .../blobstore/TransientAsyncBlobStore.java | 38 +------------------ 2 files changed, 4 insertions(+), 38 deletions(-) diff --git a/apis/swift/src/test/java/org/jclouds/openstack/swift/internal/StubSwiftAsyncClient.java b/apis/swift/src/test/java/org/jclouds/openstack/swift/internal/StubSwiftAsyncClient.java index 308a62a2cf..114b3547ff 100644 --- a/apis/swift/src/test/java/org/jclouds/openstack/swift/internal/StubSwiftAsyncClient.java +++ b/apis/swift/src/test/java/org/jclouds/openstack/swift/internal/StubSwiftAsyncClient.java @@ -98,7 +98,7 @@ public class StubSwiftAsyncClient implements CommonSwiftAsyncClient { } public ListenableFuture containerExists(final String container) { - return immediateFuture(blobStore.getContainerToBlobs().containsKey(container)); + return blobStore.containerExists(container); } public ListenableFuture createContainer(String container) { @@ -106,7 +106,7 @@ public class StubSwiftAsyncClient implements CommonSwiftAsyncClient { } public ListenableFuture deleteContainerIfEmpty(String container) { - return blobStore.deleteContainerImpl(container); + return blobStore.deleteContainerIfEmpty(container); } public ListenableFuture disableCDN(String container) { diff --git a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java index d6d460b3c5..d01f168855 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java @@ -282,13 +282,6 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { return immediateFuture(null); } - public ListenableFuture removeBlobAndReturnOld(String container, String key) { - if (getContainerToBlobs().containsKey(container)) { - return immediateFuture(getContainerToBlobs().get(container).remove(key)); - } - return immediateFuture(null); - } - /** * {@inheritDoc} */ @@ -309,7 +302,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { return immediateFuture(null); } - public ListenableFuture deleteContainerImpl(final String container) { + public ListenableFuture deleteContainerIfEmpty(final String container) { Boolean returnVal = true; if (getContainerToBlobs().containsKey(container)) { if (getContainerToBlobs().get(container).size() == 0) @@ -362,20 +355,6 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { return immediateFuture(Boolean.TRUE); } - /** - * throws IllegalStateException if the container already exists - */ - public ListenableFuture createContainerInLocationIfAbsent(final Location location, final String name) { - ConcurrentMap container = getContainerToBlobs().putIfAbsent(name, - new ConcurrentHashMap()); - if (container == null) { - getContainerToLocation().put(name, location != null ? location : defaultLocation.get()); - return immediateFuture((Void) null); - } else { - return Futures.immediateFailedFuture(new IllegalStateException("container " + name + " already exists")); - } - } - public String getFirstQueryOrNull(String string, @Nullable HttpRequestOptions options) { if (options == null) return null; @@ -499,19 +478,6 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { return immediateFuture(Iterables.getOnlyElement(blob.getAllHeaders().get(HttpHeaders.ETAG))); } - public ListenableFuture putBlobAndReturnOld(String containerName, Blob in) { - ConcurrentMap container = getContainerToBlobs().get(containerName); - if (container == null) { - return Futures.immediateFailedFuture(new IllegalStateException("containerName not found: " + containerName)); - } - - Blob blob = createUpdatedCopyOfBlobInContainer(containerName, in); - - Blob old = container.put(blob.getMetadata().getName(), blob); - - return immediateFuture(old); - } - protected Blob createUpdatedCopyOfBlobInContainer(String containerName, Blob in) { checkNotNull(in, "blob"); checkNotNull(in.getPayload(), "blob.payload"); @@ -674,7 +640,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { return getContainerToBlobs().containsKey(container); } - public ConcurrentMap getContainerToLocation() { + private ConcurrentMap getContainerToLocation() { return containerToLocation; } From 96c98be9fd740705e2fca2ec0ef4c42a6fce8a5c Mon Sep 17 00:00:00 2001 From: Andrea Turli Date: Wed, 2 May 2012 09:21:08 +0200 Subject: [PATCH 009/148] jenkins api: multimaps.for(map) --- .../jenkins/v1/binders/BindMapToOptionalParams.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/binders/BindMapToOptionalParams.java b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/binders/BindMapToOptionalParams.java index d7400cd73e..b36ade65f7 100644 --- a/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/binders/BindMapToOptionalParams.java +++ b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/binders/BindMapToOptionalParams.java @@ -20,10 +20,9 @@ package org.jclouds.jenkins.v1.binders; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import static org.jclouds.http.utils.ModifyRequest.addQueryParam; +import static org.jclouds.http.utils.ModifyRequest.addQueryParams; import java.util.Map; -import java.util.Map.Entry; import javax.inject.Inject; import javax.inject.Provider; @@ -32,6 +31,8 @@ import javax.ws.rs.core.UriBuilder; import org.jclouds.http.HttpRequest; import org.jclouds.rest.Binder; +import com.google.common.collect.Multimaps; + /** * Binds the map to parameters. * @@ -50,9 +51,7 @@ public class BindMapToOptionalParams implements Binder { public R bindToRequest(R request, Object input) { checkArgument(checkNotNull(input, "input") instanceof Map, "this binder is only valid for Maps!"); Map map = (Map) input; - for (Entry entry : map.entrySet()) { - request = addQueryParam(request, entry.getKey(), entry.getValue(), builder.get()); - } + request = addQueryParams(request, Multimaps.forMap(map), builder.get()); return request; } From 4c35bc77568a57372aeb76074a42297d533969a4 Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Wed, 2 May 2012 00:43:09 -0700 Subject: [PATCH 010/148] Using the Heroku logo --- .../src/main/webapp/images/heroku-logo.png | Bin 0 -> 4239 bytes .../heroku-tweetstore/src/main/webapp/index.jsp | 3 +-- .../heroku-tweetstore/src/main/webapp/tweets.jsp | 3 +-- 3 files changed, 2 insertions(+), 4 deletions(-) create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/webapp/images/heroku-logo.png diff --git a/demos/tweetstore/heroku-tweetstore/src/main/webapp/images/heroku-logo.png b/demos/tweetstore/heroku-tweetstore/src/main/webapp/images/heroku-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..1964aeefb40991d96a24a7d166fec188bd27edb5 GIT binary patch literal 4239 zcmV;A5OD8_P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D5F|-NK~!i%?OSP3 zR96>X2#7T@>1kps2h{h!` zDh3f3kO&%9W#9MJ-Hch>&8kUere?nP9yZ+w29ac@5~^-d@ZP)U-us<%&-u=|muDOu zf=x{(2^yAWE_38}69Uheev^tP3rRi6^?&N7g&gQ?1m!*m^kD5EGKvL(v!m9oTm9i1 zYYuF9ec#a98*BEjdu?x6$TA^c3+0W#ZH@qiH9!H-$(xT~>iVg?=yGmW)9Di}Lu2mi!r-wXeZ6&p}b{%>f?ef>ZNMBpI^5RdQdv*<< zs~W+&FV>qjeDwC_G?uNWY1d1H2DYt_{TKxr+upZd=Zg)&e1OKX^FbGM$Cu8IQ}a)D z#w}P!a(qXy?g#6Xtw(@0^8%>(s~4O&(s=TCb53SU`hjwhKqHe_#5z7t7Zb6%Ah$dF zSkuYlEgv6mQhG(gsDbr(r?7&YuDAv3N3gc=*NhK=U7KSK$O8Q1>ME``R^Mu_y;D+f zQ9yV(NQ~wLQ9x?bQ!85Q?=@82>}dM-;NAjzTNkO=NMSt=Sf6_oYp@-Ht>M_KxGc3q zKy8BmH>=0wnHi#Cw!=HUX9mJr?-yHL@>y;9m-Ur5icg>8bG;lMayWG16S}!E>ziwC z*OYw$m7uAW(*%SsUO9;R#~M?YdqgEC9ov<1diVBIYGsU=uOHsUJf$>D9v1NSmSek9 za#KGjc6SYP5Dq8YgT@+mE$ZoN{jZht#_-mJAejvBuBNwL=ZMX~KBaabCATjdyQ`50?2ww;f8 z(TWbUMyQs{*05}S?5w@1&qm8_ZM?wu;m01OF-#W`KalFh((pKb$jF$9F_ks&AG@e__8WTG)c;|b*BPY5wzJ~Ni-WAjP=Jl0?uL$i5E4;F2`{|hxs}RRb85X{0Aa-||ZqzSc zICo?6=3|>TrgK>eMik%(3S6=+`9ywBCxl?;>8a&O8`CsCbNM_CBWED2>5O6O#Z_P-^6dE7#VIUd-N7vh89Uz21{x-~2Zc35 z>}t!e*H=;yH`M^{I|v|n4j$6p`1QP)S4pmVkg*XFmC|4Fr|8($@KsCQJw|z0e?!%c zAY-h6=T9e}Q;r_0Ip6-ErS2{oqhqwj>f2CxN7L5{YYz}?B>3_892p&hNkk2kDybM$8Z2or25j!CYgMU~k!kzRKn}oyKnRBoibxC2ClUn0DZld4d)Qc%`zpFJ!974o z`b&hyN%C34g|z`WdR9j5n!oRu;BK%UB_mjqgggznJ-ugxg=9cb;0v&whgfIQSPzO{ zQ~tX02J%dlU*K~kA|s-WLLQA>k5{le4%?07`pt=13s=xkb?Zo4#pqGvus301Xs9Tt z6tla)?kjJe&TbX*l?X&jV&4ST=$?C``Okk62?(`_)QLp_1WUbfO=?@iJph`vznEk1 zDkA+Hgpgwt5IU0U11CV0BC2j`H8Da$XMwdx2#4hxWOxxknBYdEon z^Ku>=>f&VJvL=Z5uqISFD2PpD1A!>m5D%|31gy<-8Ac{HsY)@A!Mf;lH;3g$u+^(p z?1an1rrDbG(I5XHu(R>Dvr(YgSRsSepKH61fN>`8yomHBI2seyr?cB^tvzK_&P7SK zw^hLD(_#E>{Nex(pPa{_z;RB@N*+fA4fW(Qt@jnat4?^3n7VY6!{KZNql@inK7B7VTIV9m(H<~7HfOJn`=OFMuy?b+|(zzFWx z)BrS|1ET@3_HcBdb{Kq`lPn~UCLV}^;bAZE35!gXZKvA#?n!uU?1`n%Z{o6iOjtuc zQb$B)jDio|dpC28m7_$|lSU3R){oCke@(2he-Z28o%=L+;fk|Y#gq@}W7j=c!{InP zg)$_ss^pqZJyXE*GhY{qc`3v_dxf|jGFOdbz(sP(|wq+W;p3U zi2rI>W1Z=#rT*%f;8lHEXi!|RVUdH-AK}Z=hxB6YGHxm==@3#=`gu^mJOR(5+6BAb zm6F5Q^)gGlMxc%gUlJO$sI=fRL&Ns%$pc%5I~``M0kp<Wj@abbl zd*_|(Wc+n>_%aKB-S)uEMQ>~E5f$+=ra{q|mhYk?RuJ?_({S1~Sm9*QjFM{s+Gj~F zPGtHP^?zxsNy2YV^qQ{bZ?T0dORm|B8pi}@CZzvzBrY)7k!goo*kji~c^gkThcqe@tJpttT6iEQ?UJ`;e{L)w>0Ws)e zsZ)q@%2`K8tb{XV%o&^)l`kaXNpBodPKuTY10RF6fY8pLy$&V`QPriNE4`5Np`f9| zennooZ%-i#52(T;OLCHGP(}Lk;0+cC!O&&r`T4FewRt1y9IbZYES&ojdkd;v* zIxE10)XMoFc8L4fr{H{`u~<*AaWSG6cmhMbn8FCt027`snztIcHLza#{8kV_*j-_6 z7o04^`Do@E3@e@QH;;SF?Q#G*kauueO0c53iKNEmGG3G*w+ z?UZ|>AZfu&hu}jHZ@nhOe7HORXj=J^v~oY+8A3r11B3fh$Y{Hd(?3Np#?u*FcVgCKx$;$oqG|DL=fX=OQC4W6z> ziHMp8UE9*Geq|?4ppJZ0c4&W*&!p+#e_XcD+E;gD(?A+z!iG@HK=)P4ll@f@w${!l ze^q>;X*ZMN3BHY!{d0PEw)dN>AglxQGFCPtOGmlVLgNHBzm8p2Vcl8LD@ELC3T zshr2^JzRny6OzZt{KZ0)#V6q?l{$vE3~~cv9TXT}c&bb8Y27W3-Jm>kr)2&LRKXl9sth7DpcI4sh@Xf{5JIc1*JP-b ziw#&F^@=XbJlgOLYA=WclpH$fNvZEGwL+5|W`=#(7oJm-M_Hz4bgCnjT?>uO+#nhI z01=|k-1@QWZEt0m?&4s1*wmh`!F(Qo?q}+hL-3p#AD0*(w_(}h#E7ssv5=}sQsJoQ z&O-OdU7SAp?y>8{^#^ySoUpTTGrh)cLJ_WCpdaR`s5dH5|0Gymd_pM|Q6)eIYiGyM zypKDcU$|l9-jg}pG}eR`x2+L>aXpoeoBeFSfs%DchY-5;bYbGYhszAyN?17ewUN7% z7FgpC0jZxcb>+F%`y1EobCMdZ{^n^jT0WF!B}bLg(=8kn7(CQ2iftB<`8vQMN zOx<;-Oq;7&61Tao;>+&V`(Q9bZ76&G1aemVNq2~G9?72cXi)ti002ovPDHLkV1ggMEN=h+ literal 0 HcmV?d00001 diff --git a/demos/tweetstore/heroku-tweetstore/src/main/webapp/index.jsp b/demos/tweetstore/heroku-tweetstore/src/main/webapp/index.jsp index d8d1724bcf..06924e3b75 100644 --- a/demos/tweetstore/heroku-tweetstore/src/main/webapp/index.jsp +++ b/demos/tweetstore/heroku-tweetstore/src/main/webapp/index.jsp @@ -25,7 +25,6 @@

Welcome!

Click here to see tweets about jclouds.

-

+

Powered by Heroku

diff --git a/demos/tweetstore/heroku-tweetstore/src/main/webapp/tweets.jsp b/demos/tweetstore/heroku-tweetstore/src/main/webapp/tweets.jsp index 1b30f7ea11..c203651ce0 100644 --- a/demos/tweetstore/heroku-tweetstore/src/main/webapp/tweets.jsp +++ b/demos/tweetstore/heroku-tweetstore/src/main/webapp/tweets.jsp @@ -101,8 +101,7 @@ overflow-y:auto; - + Powered by Heroku From e03208bfeaa9f07832374560f7dbb86eba47beaf Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Wed, 2 May 2012 01:53:43 -0700 Subject: [PATCH 011/148] Using absolute links to images to account for different starting paths --- demos/tweetstore/heroku-tweetstore/src/main/webapp/index.jsp | 2 +- demos/tweetstore/heroku-tweetstore/src/main/webapp/tweets.jsp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/demos/tweetstore/heroku-tweetstore/src/main/webapp/index.jsp b/demos/tweetstore/heroku-tweetstore/src/main/webapp/index.jsp index 06924e3b75..9674294c6e 100644 --- a/demos/tweetstore/heroku-tweetstore/src/main/webapp/index.jsp +++ b/demos/tweetstore/heroku-tweetstore/src/main/webapp/index.jsp @@ -25,6 +25,6 @@

Welcome!

Click here to see tweets about jclouds.

-

Powered by Heroku

+

Powered by Heroku

diff --git a/demos/tweetstore/heroku-tweetstore/src/main/webapp/tweets.jsp b/demos/tweetstore/heroku-tweetstore/src/main/webapp/tweets.jsp index c203651ce0..469bc9cd09 100644 --- a/demos/tweetstore/heroku-tweetstore/src/main/webapp/tweets.jsp +++ b/demos/tweetstore/heroku-tweetstore/src/main/webapp/tweets.jsp @@ -101,7 +101,7 @@ overflow-y:auto; - Powered by Heroku + Powered by Heroku From 42d57c5d158774f253408532cd68e0c3b0ca7403 Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Wed, 2 May 2012 08:04:40 -0700 Subject: [PATCH 012/148] Fix a test compilation error in Atmos caused by 755d51ad273 --- .../java/org/jclouds/atmos/internal/StubAtmosAsyncClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apis/atmos/src/test/java/org/jclouds/atmos/internal/StubAtmosAsyncClient.java b/apis/atmos/src/test/java/org/jclouds/atmos/internal/StubAtmosAsyncClient.java index 54ca02aa9b..7e6762270a 100644 --- a/apis/atmos/src/test/java/org/jclouds/atmos/internal/StubAtmosAsyncClient.java +++ b/apis/atmos/src/test/java/org/jclouds/atmos/internal/StubAtmosAsyncClient.java @@ -137,7 +137,7 @@ public class StubAtmosAsyncClient implements AtmosAsyncClient { public ListenableFuture deletePath(String path) { if (path.indexOf('/') == path.length() - 1) { // chop off the trailing slash - return Futures.compose(blobStore.deleteContainerImpl(path.substring(0, path.length() - 1)), + return Futures.compose(blobStore.deleteContainerIfEmpty(path.substring(0, path.length() - 1)), new Function() { public Void apply(Boolean from) { From 7c317b80e85dc0c370d5e432d3600580c4e97e4d Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Wed, 2 May 2012 17:34:00 +0100 Subject: [PATCH 013/148] openstack-nova-ec2: work-around for dates encoded as '-' when they should be null (the should not be present in the response!) --- .../nova/ec2/config/NovaEC2ParserModule.java | 119 ++++++++++++++++ .../ec2/config/NovaEC2RestClientModule.java | 2 + .../NovaEC2InstanceClientExpectTest.java | 54 ++++++++ .../resources/nova_ec2_describe_instances.xml | 128 ++++++++++++++++++ 4 files changed, 303 insertions(+) create mode 100644 apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2ParserModule.java create mode 100644 apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/services/NovaEC2InstanceClientExpectTest.java create mode 100644 apis/openstack-nova-ec2/src/test/resources/nova_ec2_describe_instances.xml diff --git a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2ParserModule.java b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2ParserModule.java new file mode 100644 index 0000000000..d8f7931c3f --- /dev/null +++ b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2ParserModule.java @@ -0,0 +1,119 @@ +/** + * 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.ec2.config; + +import java.util.Date; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.date.DateService; +import org.jclouds.date.internal.SimpleDateFormatDateService; + +import com.google.common.base.Objects; +import com.google.inject.AbstractModule; + +/** + * @author Adam Lowe + */ +public class NovaEC2ParserModule extends AbstractModule { + + @Override + protected void configure() { + bind(DateService.class).to(Iso8601WithDashesTreatedAsNullDateService.class); + } + + @Singleton + public static class Iso8601WithDashesTreatedAsNullDateService implements DateService { + DateService delegate; + + @Inject + public Iso8601WithDashesTreatedAsNullDateService(SimpleDateFormatDateService service) { + this.delegate = service; + } + + @Override + public Date fromSeconds(long seconds) { + return delegate.fromSeconds(seconds); + } + + @Override + public String cDateFormat(Date date) { + return delegate.cDateFormat(date); + } + + @Override + public String cDateFormat() { + return delegate.cDateFormat(); + } + + @Override + public Date cDateParse(String toParse) { + return delegate.cDateParse(toParse); + } + + @Override + public String rfc822DateFormat(Date date) { + return delegate.rfc822DateFormat(date); + } + + @Override + public String rfc822DateFormat() { + return delegate.rfc822DateFormat(); + } + + @Override + public Date rfc822DateParse(String toParse) { + return delegate.rfc822DateParse(toParse); + } + + @Override + public String iso8601SecondsDateFormat(Date dateTime) { + return delegate.iso8601SecondsDateFormat(dateTime); + } + + @Override + public String iso8601SecondsDateFormat() { + return delegate.iso8601SecondsDateFormat(); + } + + @Override + public String iso8601DateFormat(Date date) { + return delegate.iso8601DateFormat(date); + } + + @Override + public String iso8601DateFormat() { + return delegate.iso8601DateFormat(); + } + + @Override + public Date iso8601DateParse(String toParse) { + if (Objects.equal("-", toParse)) return null; + return delegate.iso8601DateParse(toParse); + } + + @Override + public Date iso8601SecondsDateParse(String toParse) { + if (Objects.equal("-", toParse)) return null; + return delegate.iso8601SecondsDateParse(toParse); + } + } + +} diff --git a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2RestClientModule.java b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2RestClientModule.java index 5d6358bf3b..da18c3733f 100644 --- a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2RestClientModule.java +++ b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2RestClientModule.java @@ -37,12 +37,14 @@ import com.google.inject.Scopes; /** * * @author Adrian Cole + * @author Adam Lowe */ @ConfiguresRestClient public class NovaEC2RestClientModule extends EC2RestClientModule { @Override protected void configure() { + install(new NovaEC2ParserModule()); super.configure(); bind(CreateVolumeResponseHandler.class).to(NovaCreateVolumeResponseHandler.class).in(Scopes.SINGLETON); bind(DescribeImagesResponseHandler.class).to(NovaDescribeImagesResponseHandler.class); diff --git a/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/services/NovaEC2InstanceClientExpectTest.java b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/services/NovaEC2InstanceClientExpectTest.java new file mode 100644 index 0000000000..5e55bb092b --- /dev/null +++ b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/services/NovaEC2InstanceClientExpectTest.java @@ -0,0 +1,54 @@ +package org.jclouds.openstack.nova.ec2.services; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertFalse; + +import java.net.URI; +import java.util.Set; + +import org.jclouds.ec2.domain.Attachment; +import org.jclouds.ec2.domain.BlockDevice; +import org.jclouds.ec2.domain.Reservation; +import org.jclouds.ec2.domain.RunningInstance; +import org.jclouds.ec2.services.InstanceClient; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.openstack.nova.ec2.internal.BaseNovaEC2RestClientExpectTest; + +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Iterables; + +/** + * @author Adam Lowe + */ +public class NovaEC2InstanceClientExpectTest extends BaseNovaEC2RestClientExpectTest { + + public void testDescribeInstancesWithDashesInPlaceOfNullDates() { + InstanceClient client = requestsSendResponses( + describeAvailabilityZonesRequest, + describeAvailabilityZonesResponse, + HttpRequest.builder().method("POST") + .endpoint(URI.create("http://localhost:8773/services/Cloud/")) + .headers(ImmutableMultimap.of("Host", "localhost:8773")) + .payload(payloadFromStringWithContentType("Action=DescribeInstances&Signature=kkCE1HzyntmkICEidOizw50B9yjLdNZvAWUXVse1c8o%3D&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2012-04-16T15%3A54%3A08.897Z&Version=2009-04-04&AWSAccessKeyId=identity", "application/x-www-form-urlencoded")).build(), + HttpResponse.builder().statusCode(200).payload(payloadFromResource("/nova_ec2_describe_instances.xml")).build() + ).getInstanceServices(); + + Set> response = client.describeInstancesInRegion("nova"); + + assertEquals(response.size(), 3); + + Reservation target = Iterables.get(response, 2); + RunningInstance runningInstance = Iterables.getOnlyElement(target); + BlockDevice bd = Iterables.getOnlyElement(runningInstance.getEbsBlockDevices().values()); + + // this is a '-' in the nova_ec2_describe_instances.xml + assertNull(bd.getAttachTime()); + + // double-check the other fields + assertFalse(bd.isDeleteOnTermination()); + assertEquals(bd.getVolumeId(), "1"); + } + +} diff --git a/apis/openstack-nova-ec2/src/test/resources/nova_ec2_describe_instances.xml b/apis/openstack-nova-ec2/src/test/resources/nova_ec2_describe_instances.xml new file mode 100644 index 0000000000..707fc4af22 --- /dev/null +++ b/apis/openstack-nova-ec2/src/test/resources/nova_ec2_describe_instances.xml @@ -0,0 +1,128 @@ + + + req-a68563bb-034e-4a32-aac7-5126e041cf91 + + + 2b25129754c145de8426896b09a42106 + + + default + + + r-1jywe5ye + + + + nova + + instance-store + None (2b25129754c145de8426896b09a42106, ubuntu) + i-00000003 + + 16 + running + + + ami-00000001 + + mishdemo + mishdemo + 2012-05-02T06:59:59.000Z + 0 + /dev/vda + aki-00000002 + ari-00000003 + 10.77.22.4 + m1.small + 10.77.22.4 + + + + + d8f0cd49971a44d187b454e14921056d + + + default + + + r-pctvv0ey + + + + nova + + instance-store + None (d8f0cd49971a44d187b454e14921056d, ubuntu) + i-00000001 + + 16 + running + + + ami-00000001 + + avi-server1 + avi-server1 + 2012-05-01T09:18:37.000Z + 0 + /dev/vda + aki-00000002 + ari-00000003 + 10.77.22.2 + m1.tiny + 10.77.22.2 + + + + + d8f0cd49971a44d187b454e14921056d + + + default + + + avi-sg1 + + + r-0iwpw2u8 + + + + + /dev/vdc + + in-use + false + 1 + - + + + + + nova + + instance-store + avi-kp1 (d8f0cd49971a44d187b454e14921056d, ubuntu) + i-00000002 + + 16 + running + + + ami-00000001 + + avi-server2 + avi-server2 + 2012-05-01T09:22:26.000Z + 0 + /dev/vda + aki-00000002 + ari-00000003 + 10.77.22.3 + m1.tiny + 10.77.22.3 + + + + + \ No newline at end of file From 646f8d8b13daceb302a0cc52751f0b4d34ac533b Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Wed, 2 May 2012 10:53:47 -0700 Subject: [PATCH 014/148] Improve use of Throwables.propagate Throwing the return value removes some unneeded asserts and return statements. Also remove some useless and misspelled propogate wrappers. --- .../filesystem/FilesystemAsyncBlobStore.java | 4 +-- .../binders/BindCloneParamsToXmlPayload.java | 4 +-- .../blobstore/TransientAsyncBlobStore.java | 4 +-- .../RetryOnTimeOutExceptionSupplier.java | 4 +-- .../JavaUrlHttpCommandExecutorService.java | 3 +- ...ndThrowAuthorizationExceptionSupplier.java | 4 +-- .../RetrieveActiveBridgedInterfaces.java | 9 ++---- .../compute/LibvirtComputeService.java | 9 +----- .../compute/functions/DomainToHardware.java | 18 ++++------- .../LibvirtComputeServiceAdapter.java | 32 ++++++------------- .../ning/NingHttpCommandExecutorService.java | 3 +- 11 files changed, 27 insertions(+), 67 deletions(-) diff --git a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java index 7c828ec7ff..412cf2d326 100644 --- a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java +++ b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java @@ -262,9 +262,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { .build())); return metadata; } catch (Exception e) { - propagate(e); - assert false : "exception should have propagated: " + e; - return null; + throw propagate(e); } } diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindCloneParamsToXmlPayload.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindCloneParamsToXmlPayload.java index 6fb9d55cd9..9241d53e26 100644 --- a/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindCloneParamsToXmlPayload.java +++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindCloneParamsToXmlPayload.java @@ -122,9 +122,7 @@ public abstract class BindCloneParamsToXmlPayload implem try { return getOptionClass().newInstance(); } catch (Exception e) { - Throwables.propagate(e); - assert false : "unreachable code"; - return null; + throw Throwables.propagate(e); } } diff --git a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java index d01f168855..629b7b4d1e 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java @@ -251,9 +251,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { HttpUtils.copy(in.getContentMetadata(), out.getContentMetadata()); return out; } catch (Exception e) { - propagate(e); - assert false : "exception should have propagated: " + e; - return null; + throw propagate(e); } } diff --git a/core/src/main/java/org/jclouds/concurrent/RetryOnTimeOutExceptionSupplier.java b/core/src/main/java/org/jclouds/concurrent/RetryOnTimeOutExceptionSupplier.java index 5c4b7c6a2e..c908c13d3f 100644 --- a/core/src/main/java/org/jclouds/concurrent/RetryOnTimeOutExceptionSupplier.java +++ b/core/src/main/java/org/jclouds/concurrent/RetryOnTimeOutExceptionSupplier.java @@ -48,9 +48,7 @@ public class RetryOnTimeOutExceptionSupplier implements Supplier { } catch (Exception e) { if ((ex = Throwables2.getFirstThrowableOfType(e, TimeoutException.class)) != null) continue; - propagate(e); - assert false; - return null; + throw propagate(e); } } if (ex != null) diff --git a/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java b/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java index 21108564c5..065c1f23d0 100644 --- a/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java +++ b/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java @@ -115,8 +115,7 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe in = bufferAndCloseStream(connection.getErrorStream()); } catch (RuntimeException e) { closeQuietly(in); - propagate(e); - assert false : "should have propagated exception"; + throw propagate(e); } int responseCode = connection.getResponseCode(); diff --git a/core/src/main/java/org/jclouds/rest/suppliers/SetAndThrowAuthorizationExceptionSupplier.java b/core/src/main/java/org/jclouds/rest/suppliers/SetAndThrowAuthorizationExceptionSupplier.java index d32070be41..412d85577a 100644 --- a/core/src/main/java/org/jclouds/rest/suppliers/SetAndThrowAuthorizationExceptionSupplier.java +++ b/core/src/main/java/org/jclouds/rest/suppliers/SetAndThrowAuthorizationExceptionSupplier.java @@ -56,9 +56,7 @@ public class SetAndThrowAuthorizationExceptionSupplier implements Supplier authException.set(aex); throw aex; } - propagate(e); - assert false : e; - return null; + throw propagate(e); } } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/RetrieveActiveBridgedInterfaces.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/RetrieveActiveBridgedInterfaces.java index 0cd9fc327c..81ffd7bc5d 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/RetrieveActiveBridgedInterfaces.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/RetrieveActiveBridgedInterfaces.java @@ -84,8 +84,7 @@ public class RetrieveActiveBridgedInterfaces implements Function T propogate(Exception e) { - Throwables.propagate(e); - assert false; - return null; - } - } diff --git a/sandbox-apis/libvirt/src/main/java/org/jclouds/libvirt/compute/functions/DomainToHardware.java b/sandbox-apis/libvirt/src/main/java/org/jclouds/libvirt/compute/functions/DomainToHardware.java index e6b092966d..75996727a4 100644 --- a/sandbox-apis/libvirt/src/main/java/org/jclouds/libvirt/compute/functions/DomainToHardware.java +++ b/sandbox-apis/libvirt/src/main/java/org/jclouds/libvirt/compute/functions/DomainToHardware.java @@ -83,22 +83,16 @@ public class DomainToHardware implements Function { } builder.volumes((List) volumes); } catch (LibvirtException e) { - propagate(e); + Throwables.propagate(e); } catch (XPathExpressionException e) { - propagate(e); + Throwables.propagate(e); } catch (ParserConfigurationException e) { - propagate(e); + Throwables.propagate(e); } catch (SAXException e) { - propagate(e); + Throwables.propagate(e); } catch (IOException e) { - propagate(e); + Throwables.propagate(e); } return builder.build(); } - - protected T propagate(Exception e) { - Throwables.propagate(e); - assert false; - return null; - } -} \ No newline at end of file +} diff --git a/sandbox-apis/libvirt/src/main/java/org/jclouds/libvirt/compute/strategy/LibvirtComputeServiceAdapter.java b/sandbox-apis/libvirt/src/main/java/org/jclouds/libvirt/compute/strategy/LibvirtComputeServiceAdapter.java index 732ac72c08..cde06ea53d 100644 --- a/sandbox-apis/libvirt/src/main/java/org/jclouds/libvirt/compute/strategy/LibvirtComputeServiceAdapter.java +++ b/sandbox-apis/libvirt/src/main/java/org/jclouds/libvirt/compute/strategy/LibvirtComputeServiceAdapter.java @@ -116,9 +116,9 @@ public class LibvirtComputeServiceAdapter implements ComputeServiceAdapter T propogate(LibvirtException e) { - Throwables.propagate(e); - assert false; - return null; - } - - protected T propogate(Exception e) { - Throwables.propagate(e); - assert false; - return null; - } - private static StorageVol cloneVolume(StoragePool storagePool, StorageVol from) throws LibvirtException, XPathExpressionException, ParserConfigurationException, SAXException, IOException, TransformerException { return storagePool.storageVolCreateXMLFrom(generateClonedVolumeXML(from.getXMLDesc(0)), from, 0); @@ -224,7 +212,7 @@ public class LibvirtComputeServiceAdapter implements ComputeServiceAdapter Date: Wed, 2 May 2012 11:31:20 -0700 Subject: [PATCH 015/148] Eliminate Throwables2.propagateOrNull Throwables.propagate always propagates the Throwable; there is no need to return null. --- .../atmos/functions/ReturnEndpointIfAlreadyExists.java | 4 ++-- .../functions/ReturnVoidOnRedirectedDelete.java | 7 +++---- .../ec2/functions/ReturnVoidOnVolumeAvailable.java | 5 ++--- ...eturnFalseIfBucketAlreadyOwnedByYouOrIllegalState.java | 5 ++--- .../ReturnTrueOn404OrNotFoundFalseOnIllegalState.java | 4 ++-- .../swift/functions/ReturnTrueOn404FalseOn409.java | 6 ++++-- .../vcloud/functions/VAppTemplatesForCatalogItems.java | 4 ++-- .../functions/ReturnFalseOnContainerNotFound.java | 6 +++--- .../blobstore/functions/ReturnFalseOnKeyNotFound.java | 7 +++---- .../functions/ReturnNullOnContainerNotFound.java | 7 +++---- .../blobstore/functions/ReturnNullOnKeyNotFound.java | 7 +++---- .../blobstore/functions/ThrowContainerNotFoundOn404.java | 5 ++--- .../blobstore/functions/ThrowKeyNotFoundOn404.java | 5 ++--- .../vcloud_0_8/functions/ReturnVoidOnDeleteDefaultIp.java | 7 +++---- .../jclouds/functions/ExceptionToValueOrPropagate.java | 3 +-- .../java/org/jclouds/http/functions/ReturnFalseOn404.java | 6 ++++-- .../java/org/jclouds/http/functions/ReturnTrueOn404.java | 8 +++++--- .../rest/functions/MapHttp4xxCodesToExceptions.java | 5 ++--- .../rest/functions/ReturnEmptyListOnNotFoundOr404.java | 5 ++--- .../rest/functions/ReturnEmptyMapOnNotFoundOr404.java | 5 ++--- .../functions/ReturnEmptyMultimapOnNotFoundOr404.java | 5 ++--- .../rest/functions/ReturnEmptySetOnNotFoundOr404.java | 5 ++--- .../jclouds/rest/functions/ReturnNullOnNotFoundOr404.java | 5 ++--- .../jclouds/rest/functions/ReturnVoidOnNotFoundOr404.java | 5 ++--- core/src/main/java/org/jclouds/util/Throwables2.java | 6 ------ .../org/jclouds/concurrent/FutureExceptionParserTest.java | 3 +-- .../v1_5/functions/VAppTemplatesForCatalogItems.java | 4 ++-- .../functions/ReturnFalseIfContainerAlreadyExists.java | 5 ++--- .../miro/functions/ParseRimuHostingException.java | 5 ++--- .../pcs/functions/ReturnFalseIfContainerNotFound.java | 5 ++--- .../pcs/functions/ReturnTrueIfContainerAlreadyExists.java | 5 ++--- 31 files changed, 71 insertions(+), 93 deletions(-) diff --git a/apis/atmos/src/main/java/org/jclouds/atmos/functions/ReturnEndpointIfAlreadyExists.java b/apis/atmos/src/main/java/org/jclouds/atmos/functions/ReturnEndpointIfAlreadyExists.java index 0fe81361d1..c45336cdd0 100644 --- a/apis/atmos/src/main/java/org/jclouds/atmos/functions/ReturnEndpointIfAlreadyExists.java +++ b/apis/atmos/src/main/java/org/jclouds/atmos/functions/ReturnEndpointIfAlreadyExists.java @@ -19,7 +19,6 @@ package org.jclouds.atmos.functions; import static com.google.common.base.Preconditions.checkNotNull; -import static org.jclouds.util.Throwables2.propagateOrNull; import java.net.URI; @@ -30,6 +29,7 @@ import org.jclouds.rest.InvocationContext; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; +import com.google.common.base.Throwables; /** * @@ -44,7 +44,7 @@ public class ReturnEndpointIfAlreadyExists implements Function, if (checkNotNull(from, "exception") instanceof KeyAlreadyExistsException) { return endpoint; } - return URI.class.cast(propagateOrNull(from)); + throw Throwables.propagate(from); } @Override diff --git a/apis/deltacloud/src/main/java/org/jclouds/deltacloud/functions/ReturnVoidOnRedirectedDelete.java b/apis/deltacloud/src/main/java/org/jclouds/deltacloud/functions/ReturnVoidOnRedirectedDelete.java index 99a0a0a1d1..41d0b55be4 100644 --- a/apis/deltacloud/src/main/java/org/jclouds/deltacloud/functions/ReturnVoidOnRedirectedDelete.java +++ b/apis/deltacloud/src/main/java/org/jclouds/deltacloud/functions/ReturnVoidOnRedirectedDelete.java @@ -18,8 +18,6 @@ */ package org.jclouds.deltacloud.functions; -import static org.jclouds.util.Throwables2.propagateOrNull; - import javax.inject.Singleton; import javax.ws.rs.HttpMethod; @@ -27,6 +25,7 @@ import org.jclouds.http.HttpResponseException; import org.jclouds.util.Throwables2; import com.google.common.base.Function; +import com.google.common.base.Throwables; /** * When a delete operation is performed, Deltacloud returns 302. @@ -42,6 +41,6 @@ public class ReturnVoidOnRedirectedDelete implements Function { && exception.getResponse().getStatusCode() == 302) { return null; } - return Void.class.cast(propagateOrNull(from)); + throw Throwables.propagate(from); } -} \ No newline at end of file +} diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/functions/ReturnVoidOnVolumeAvailable.java b/apis/ec2/src/main/java/org/jclouds/ec2/functions/ReturnVoidOnVolumeAvailable.java index 49c00676e9..32a77bc0a6 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/functions/ReturnVoidOnVolumeAvailable.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/functions/ReturnVoidOnVolumeAvailable.java @@ -18,13 +18,12 @@ */ package org.jclouds.ec2.functions; -import static org.jclouds.util.Throwables2.propagateOrNull; - import javax.inject.Singleton; import org.jclouds.aws.AWSResponseException; import com.google.common.base.Function; +import com.google.common.base.Throwables; @Singleton public class ReturnVoidOnVolumeAvailable implements Function { @@ -36,7 +35,7 @@ public class ReturnVoidOnVolumeAvailable implements Function { && e.getError().getCode().contains("available")) return null; } - return Void.class.cast(propagateOrNull(from)); + throw Throwables.propagate(from); } } diff --git a/apis/s3/src/main/java/org/jclouds/s3/functions/ReturnFalseIfBucketAlreadyOwnedByYouOrIllegalState.java b/apis/s3/src/main/java/org/jclouds/s3/functions/ReturnFalseIfBucketAlreadyOwnedByYouOrIllegalState.java index dd94e9c027..7f10f2fd73 100644 --- a/apis/s3/src/main/java/org/jclouds/s3/functions/ReturnFalseIfBucketAlreadyOwnedByYouOrIllegalState.java +++ b/apis/s3/src/main/java/org/jclouds/s3/functions/ReturnFalseIfBucketAlreadyOwnedByYouOrIllegalState.java @@ -18,14 +18,13 @@ */ package org.jclouds.s3.functions; -import static org.jclouds.util.Throwables2.propagateOrNull; - import javax.inject.Singleton; import org.jclouds.aws.AWSResponseException; import org.jclouds.util.Throwables2; import com.google.common.base.Function; +import com.google.common.base.Throwables; /** * @@ -42,6 +41,6 @@ public class ReturnFalseIfBucketAlreadyOwnedByYouOrIllegalState implements Funct } else if (Throwables2.getFirstThrowableOfType(from, IllegalStateException.class) != null) { return false; } - return Boolean.class.cast(propagateOrNull(from)); + throw Throwables.propagate(from); } } diff --git a/apis/s3/src/main/java/org/jclouds/s3/functions/ReturnTrueOn404OrNotFoundFalseOnIllegalState.java b/apis/s3/src/main/java/org/jclouds/s3/functions/ReturnTrueOn404OrNotFoundFalseOnIllegalState.java index b5f232ec6d..fc05c9670e 100644 --- a/apis/s3/src/main/java/org/jclouds/s3/functions/ReturnTrueOn404OrNotFoundFalseOnIllegalState.java +++ b/apis/s3/src/main/java/org/jclouds/s3/functions/ReturnTrueOn404OrNotFoundFalseOnIllegalState.java @@ -21,13 +21,13 @@ package org.jclouds.s3.functions; import static com.google.common.base.Predicates.equalTo; import static org.jclouds.http.HttpUtils.returnValueOnCodeOrNull; import static org.jclouds.util.Throwables2.getFirstThrowableOfType; -import static org.jclouds.util.Throwables2.propagateOrNull; import javax.inject.Singleton; import org.jclouds.blobstore.ContainerNotFoundException; import com.google.common.base.Function; +import com.google.common.base.Throwables; /** * @@ -46,6 +46,6 @@ public class ReturnTrueOn404OrNotFoundFalseOnIllegalState implements Function { public Boolean apply(Exception from) { Boolean returnVal = returnValueOnCodeOrNull(from, true, in(of(404, 409))); - return returnVal != null ? returnVal : Boolean.class.cast(propagateOrNull(from)); + if (returnVal != null) + return returnVal; + throw Throwables.propagate(from); } } diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/functions/VAppTemplatesForCatalogItems.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/functions/VAppTemplatesForCatalogItems.java index 4abf213bb4..e73d5a43d1 100644 --- a/apis/vcloud/src/main/java/org/jclouds/vcloud/functions/VAppTemplatesForCatalogItems.java +++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/functions/VAppTemplatesForCatalogItems.java @@ -20,7 +20,6 @@ package org.jclouds.vcloud.functions; import static com.google.common.collect.Iterables.filter; import static org.jclouds.concurrent.FutureIterables.transformParallel; -import static org.jclouds.util.Throwables2.propagateOrNull; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; @@ -44,6 +43,7 @@ import org.jclouds.vcloud.domain.VAppTemplate; import com.google.common.base.Function; import com.google.common.base.Predicate; +import com.google.common.base.Throwables; /** * @author Adrian Cole @@ -64,7 +64,7 @@ public class VAppTemplatesForCatalogItems implements Function { if (from instanceof KeyNotFoundException) { return false; } - return Boolean.class.cast(propagateOrNull(from)); + throw Throwables.propagate(from); } -} \ No newline at end of file +} diff --git a/blobstore/src/main/java/org/jclouds/blobstore/functions/ReturnNullOnContainerNotFound.java b/blobstore/src/main/java/org/jclouds/blobstore/functions/ReturnNullOnContainerNotFound.java index 6e05e3a45b..3970d28de0 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/functions/ReturnNullOnContainerNotFound.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/functions/ReturnNullOnContainerNotFound.java @@ -18,13 +18,12 @@ */ package org.jclouds.blobstore.functions; -import static org.jclouds.util.Throwables2.propagateOrNull; - import javax.inject.Singleton; import org.jclouds.blobstore.ContainerNotFoundException; import com.google.common.base.Function; +import com.google.common.base.Throwables; /** * @@ -37,6 +36,6 @@ public class ReturnNullOnContainerNotFound implements Function { if (from instanceof KeyNotFoundException) { return null; } - return propagateOrNull(from); + throw Throwables.propagate(from); } -} \ No newline at end of file +} diff --git a/blobstore/src/main/java/org/jclouds/blobstore/functions/ThrowContainerNotFoundOn404.java b/blobstore/src/main/java/org/jclouds/blobstore/functions/ThrowContainerNotFoundOn404.java index eb4f26ca35..56c38c644f 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/functions/ThrowContainerNotFoundOn404.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/functions/ThrowContainerNotFoundOn404.java @@ -18,12 +18,11 @@ */ package org.jclouds.blobstore.functions; -import static org.jclouds.util.Throwables2.propagateOrNull; - import org.jclouds.blobstore.ContainerNotFoundException; import org.jclouds.http.HttpResponseException; import com.google.common.base.Function; +import com.google.common.base.Throwables; /** * @@ -42,7 +41,7 @@ public class ThrowContainerNotFoundOn404 implements Function throw new ContainerNotFoundException(from); } } - return propagateOrNull(from); + throw Throwables.propagate(from); } } diff --git a/blobstore/src/main/java/org/jclouds/blobstore/functions/ThrowKeyNotFoundOn404.java b/blobstore/src/main/java/org/jclouds/blobstore/functions/ThrowKeyNotFoundOn404.java index a039023dd4..36f086698c 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/functions/ThrowKeyNotFoundOn404.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/functions/ThrowKeyNotFoundOn404.java @@ -18,12 +18,11 @@ */ package org.jclouds.blobstore.functions; -import static org.jclouds.util.Throwables2.propagateOrNull; - import org.jclouds.blobstore.KeyNotFoundException; import org.jclouds.http.HttpResponseException; import com.google.common.base.Function; +import com.google.common.base.Throwables; /** * @@ -42,7 +41,7 @@ public class ThrowKeyNotFoundOn404 implements Function { throw new KeyNotFoundException(from); } } - return propagateOrNull(from); + throw Throwables.propagate(from); } } diff --git a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/functions/ReturnVoidOnDeleteDefaultIp.java b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/functions/ReturnVoidOnDeleteDefaultIp.java index f18aef084c..f067b61090 100644 --- a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/functions/ReturnVoidOnDeleteDefaultIp.java +++ b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/functions/ReturnVoidOnDeleteDefaultIp.java @@ -18,8 +18,6 @@ */ package org.jclouds.trmk.vcloud_0_8.functions; -import static org.jclouds.util.Throwables2.propagateOrNull; - import java.util.regex.Pattern; import javax.inject.Singleton; @@ -28,6 +26,7 @@ import org.jclouds.http.HttpResponseException; import org.jclouds.rest.AuthorizationException; import com.google.common.base.Function; +import com.google.common.base.Throwables; /** * There's no current way to determine if an IP is the default outbound one. In this case, we may @@ -49,6 +48,6 @@ public class ReturnVoidOnDeleteDefaultIp implements Function { } else if (from instanceof AuthorizationException) { return null; } - return Void.class.cast(propagateOrNull(from)); + throw Throwables.propagate(from); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jclouds/functions/ExceptionToValueOrPropagate.java b/core/src/main/java/org/jclouds/functions/ExceptionToValueOrPropagate.java index 5f8b7e176e..acad8a399f 100644 --- a/core/src/main/java/org/jclouds/functions/ExceptionToValueOrPropagate.java +++ b/core/src/main/java/org/jclouds/functions/ExceptionToValueOrPropagate.java @@ -23,7 +23,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.List; import org.jclouds.javax.annotation.Nullable; -import org.jclouds.util.Throwables2; import com.google.common.base.Function; import com.google.common.base.Throwables; @@ -50,7 +49,7 @@ public class ExceptionToValueOrPropagate implements Func Iterable matchingThrowables = Iterables.filter(throwables, matchingClass); if (Iterables.size(matchingThrowables) >= 1) return value; - return (T) Throwables2.propagateOrNull(from); + throw Throwables.propagate(from); } } diff --git a/core/src/main/java/org/jclouds/http/functions/ReturnFalseOn404.java b/core/src/main/java/org/jclouds/http/functions/ReturnFalseOn404.java index 5030acd6fd..8b755f2e2a 100644 --- a/core/src/main/java/org/jclouds/http/functions/ReturnFalseOn404.java +++ b/core/src/main/java/org/jclouds/http/functions/ReturnFalseOn404.java @@ -20,11 +20,11 @@ package org.jclouds.http.functions; import static com.google.common.base.Predicates.equalTo; import static org.jclouds.http.HttpUtils.returnValueOnCodeOrNull; -import static org.jclouds.util.Throwables2.propagateOrNull; import javax.inject.Singleton; import com.google.common.base.Function; +import com.google.common.base.Throwables; /** * @@ -35,7 +35,9 @@ public class ReturnFalseOn404 implements Function { public Boolean apply(Exception from) { Boolean returnVal = returnValueOnCodeOrNull(from, false, equalTo(404)); - return returnVal != null ? returnVal : Boolean.class.cast(propagateOrNull(from)); + if (returnVal != null) + return returnVal; + throw Throwables.propagate(from); } } diff --git a/core/src/main/java/org/jclouds/http/functions/ReturnTrueOn404.java b/core/src/main/java/org/jclouds/http/functions/ReturnTrueOn404.java index b1a32ef445..bb4ae63ded 100644 --- a/core/src/main/java/org/jclouds/http/functions/ReturnTrueOn404.java +++ b/core/src/main/java/org/jclouds/http/functions/ReturnTrueOn404.java @@ -20,18 +20,20 @@ package org.jclouds.http.functions; import static com.google.common.base.Predicates.equalTo; import static org.jclouds.http.HttpUtils.returnValueOnCodeOrNull; -import static org.jclouds.util.Throwables2.propagateOrNull; import javax.inject.Singleton; import com.google.common.base.Function; +import com.google.common.base.Throwables; @Singleton public class ReturnTrueOn404 implements Function { public Boolean apply(Exception from) { Boolean returnVal = returnValueOnCodeOrNull(from, true, equalTo(404)); - return returnVal != null ? returnVal : Boolean.class.cast(propagateOrNull(from)); + if (returnVal != null) + return returnVal; + throw Throwables.propagate(from); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jclouds/rest/functions/MapHttp4xxCodesToExceptions.java b/core/src/main/java/org/jclouds/rest/functions/MapHttp4xxCodesToExceptions.java index 90189d5405..6675519683 100644 --- a/core/src/main/java/org/jclouds/rest/functions/MapHttp4xxCodesToExceptions.java +++ b/core/src/main/java/org/jclouds/rest/functions/MapHttp4xxCodesToExceptions.java @@ -18,8 +18,6 @@ */ package org.jclouds.rest.functions; -import static org.jclouds.util.Throwables2.propagateOrNull; - import javax.inject.Singleton; import org.jclouds.http.HttpResponseException; @@ -27,6 +25,7 @@ import org.jclouds.rest.AuthorizationException; import org.jclouds.rest.ResourceNotFoundException; import com.google.common.base.Function; +import com.google.common.base.Throwables; /** * @@ -50,7 +49,7 @@ public class MapHttp4xxCodesToExceptions implements Function throw new IllegalStateException(from); } } - return propagateOrNull(from); + throw Throwables.propagate(from); } } diff --git a/core/src/main/java/org/jclouds/rest/functions/ReturnEmptyListOnNotFoundOr404.java b/core/src/main/java/org/jclouds/rest/functions/ReturnEmptyListOnNotFoundOr404.java index 1d4314332a..e7ad30afcd 100644 --- a/core/src/main/java/org/jclouds/rest/functions/ReturnEmptyListOnNotFoundOr404.java +++ b/core/src/main/java/org/jclouds/rest/functions/ReturnEmptyListOnNotFoundOr404.java @@ -19,7 +19,6 @@ package org.jclouds.rest.functions; import static com.google.common.base.Preconditions.checkNotNull; -import static org.jclouds.util.Throwables2.propagateOrNull; import java.util.List; @@ -55,7 +54,7 @@ public class ReturnEmptyListOnNotFoundOr404 implements Function { } else if (rto404.apply(from)) { return null; } - return Object.class.cast(propagateOrNull(from)); + throw Throwables.propagate(from); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jclouds/rest/functions/ReturnVoidOnNotFoundOr404.java b/core/src/main/java/org/jclouds/rest/functions/ReturnVoidOnNotFoundOr404.java index 1a16d2f341..97484e2266 100644 --- a/core/src/main/java/org/jclouds/rest/functions/ReturnVoidOnNotFoundOr404.java +++ b/core/src/main/java/org/jclouds/rest/functions/ReturnVoidOnNotFoundOr404.java @@ -19,7 +19,6 @@ package org.jclouds.rest.functions; import static com.google.common.base.Preconditions.checkNotNull; -import static org.jclouds.util.Throwables2.propagateOrNull; import javax.inject.Inject; import javax.inject.Singleton; @@ -55,6 +54,6 @@ public class ReturnVoidOnNotFoundOr404 implements Function { if (value != null && value) return null; } - return Void.class.cast(propagateOrNull(from)); + throw Throwables.propagate(from); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jclouds/util/Throwables2.java b/core/src/main/java/org/jclouds/util/Throwables2.java index a3424cf53b..c3af6560e4 100644 --- a/core/src/main/java/org/jclouds/util/Throwables2.java +++ b/core/src/main/java/org/jclouds/util/Throwables2.java @@ -114,12 +114,6 @@ public class Throwables2 { return null; } - public static T propagateOrNull(Exception from) { - propagate(from); - assert false : "exception should have propogated"; - return null; - } - // Note this needs to be kept up-to-date with all top-level exceptions jclouds works against @SuppressWarnings( { "unchecked", "rawtypes" }) public static Exception returnFirstExceptionIfInListOrThrowStandardExceptionOrCause(Class[] exceptionTypes, diff --git a/core/src/test/java/org/jclouds/concurrent/FutureExceptionParserTest.java b/core/src/test/java/org/jclouds/concurrent/FutureExceptionParserTest.java index ab15f77e7b..e9a65b8a8e 100644 --- a/core/src/test/java/org/jclouds/concurrent/FutureExceptionParserTest.java +++ b/core/src/test/java/org/jclouds/concurrent/FutureExceptionParserTest.java @@ -18,7 +18,6 @@ */ package org.jclouds.concurrent; -import static org.jclouds.util.Throwables2.propagateOrNull; import static org.testng.Assert.assertEquals; import java.util.concurrent.Callable; @@ -87,7 +86,7 @@ public class FutureExceptionParserTest { public String apply(Exception from) { if (Iterables.size(Iterables.filter(Throwables.getCausalChain(from), RuntimeException.class)) >= 1) return from.getMessage(); - return String.class.cast(propagateOrNull(from)); + throw Throwables.propagate(from); } }); diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/functions/VAppTemplatesForCatalogItems.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/functions/VAppTemplatesForCatalogItems.java index 6e674c72f9..6f9f993645 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/functions/VAppTemplatesForCatalogItems.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/functions/VAppTemplatesForCatalogItems.java @@ -20,7 +20,6 @@ package org.jclouds.vcloud.director.v1_5.functions; import static com.google.common.collect.Iterables.filter; import static org.jclouds.concurrent.FutureIterables.transformParallel; -import static org.jclouds.util.Throwables2.propagateOrNull; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; @@ -43,6 +42,7 @@ import org.jclouds.vcloud.director.v1_5.user.VCloudDirectorAsyncClient; import com.google.common.base.Function; import com.google.common.base.Predicate; +import com.google.common.base.Throwables; /** * @author danikov @@ -63,7 +63,7 @@ public class VAppTemplatesForCatalogItems implements Function { throw new RuntimeException(firstResponse.getErrorInfo().getErrorMessage(), e); } } - return propagateOrNull(e); + throw Throwables.propagate(e); } } diff --git a/sandbox-apis/pcs/src/main/java/org/jclouds/mezeo/pcs/functions/ReturnFalseIfContainerNotFound.java b/sandbox-apis/pcs/src/main/java/org/jclouds/mezeo/pcs/functions/ReturnFalseIfContainerNotFound.java index a77ec91cad..7f8aca5ca4 100644 --- a/sandbox-apis/pcs/src/main/java/org/jclouds/mezeo/pcs/functions/ReturnFalseIfContainerNotFound.java +++ b/sandbox-apis/pcs/src/main/java/org/jclouds/mezeo/pcs/functions/ReturnFalseIfContainerNotFound.java @@ -18,13 +18,12 @@ */ package org.jclouds.mezeo.pcs.functions; -import static org.jclouds.util.Throwables2.propagateOrNull; - import javax.inject.Singleton; import org.jclouds.blobstore.ContainerNotFoundException; import com.google.common.base.Function; +import com.google.common.base.Throwables; /** * @@ -38,7 +37,7 @@ public class ReturnFalseIfContainerNotFound implements Function Date: Thu, 3 May 2012 03:55:40 +0100 Subject: [PATCH 016/148] image extension working on hpcloud, virtualbox and aws-ec2, cloudservers implemented but has issues --- .../compute/CloudServersImageExtension.java | 165 +++++++++++++++++ ...oudServersComputeServiceContextModule.java | 13 +- .../CloudServersImageExtensionLiveTest.java | 47 +++++ .../ec2/compute/EC2ImageExtension.java | 169 ++++++++++++++++++ .../EC2ComputeServiceContextModule.java | 8 + .../EC2ComputeServiceDependenciesModule.java | 4 + .../EC2CreateNodesInGroupThenAddToSet.java | 16 +- .../compute/EC2ImageExtensionLiveTest.java | 47 +++++ .../nova/v1_1/compute/NovaImageExtension.java | 79 +++++++- ...sWithGroupEncodedIntoNameThenAddToSet.java | 61 ++++--- .../compute/NovaImageExtensionLiveTest.java | 47 +++++ .../org/jclouds/compute/ImageExtension.java | 4 +- .../internal/BaseImageExtensionLiveTest.java | 91 ++++++++-- .../compute/VirtualBoxImageExtension.java | 6 +- .../AWSEC2ComputeServiceContextModule.java | 7 + ...WSEC2ComputeServiceDependenciesModule.java | 4 + .../compute/AWSEC2ImageExtensionLiveTest.java | 67 +++++++ .../HPCloudComputeImageExtensionLivetest.java | 5 + 18 files changed, 781 insertions(+), 59 deletions(-) create mode 100644 apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/CloudServersImageExtension.java create mode 100644 apis/cloudservers/src/test/java/org/jclouds/cloudservers/compute/CloudServersImageExtensionLiveTest.java create mode 100644 apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ImageExtension.java create mode 100644 apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ImageExtensionLiveTest.java create mode 100644 apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/NovaImageExtensionLiveTest.java create mode 100644 providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ImageExtensionLiveTest.java diff --git a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/CloudServersImageExtension.java b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/CloudServersImageExtension.java new file mode 100644 index 0000000000..2914789c53 --- /dev/null +++ b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/CloudServersImageExtension.java @@ -0,0 +1,165 @@ +/** + * 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.cloudservers.compute; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import java.util.NoSuchElementException; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.Constants; +import org.jclouds.cloudservers.CloudServersClient; +import org.jclouds.cloudservers.domain.Server; +import org.jclouds.cloudservers.options.ListOptions; +import org.jclouds.compute.ImageExtension; +import org.jclouds.compute.domain.CloneImageTemplate; +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.domain.ImageTemplate; +import org.jclouds.compute.domain.ImageTemplateBuilder; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.concurrent.Futures; +import org.jclouds.logging.Logger; +import org.jclouds.predicates.PredicateWithResult; +import org.jclouds.predicates.Retryables; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.util.concurrent.ListenableFuture; + +/** + * CloudServers implementation of {@link ImageExtension} + * + * @author David Alves + * + */ +@Singleton +public class CloudServersImageExtension implements ImageExtension { + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + private final CloudServersClient syncClient; + private final ExecutorService executor; + private final Function cloudserversImageToImage; + @com.google.inject.Inject(optional = true) + @Named("IMAGE_MAX_WAIT") + long maxWait = 3600; + @com.google.inject.Inject(optional = true) + @Named("IMAGE_WAIT_PERIOD") + long waitPeriod = 1; + + @Inject + public CloudServersImageExtension(CloudServersClient novaClient, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads, + Function cloudserversImageToImage) { + this.syncClient = checkNotNull(novaClient); + this.executor = userThreads; + this.cloudserversImageToImage = cloudserversImageToImage; + } + + @Override + public ImageTemplate buildImageTemplateFromNode(String name, final String id) { + Server server = syncClient.getServer(Integer.parseInt(id)); + if (server == null) + throw new NoSuchElementException("Cannot find server with id: " + id); + CloneImageTemplate template = new ImageTemplateBuilder.CloneImageTemplateBuilder().nodeId(id).name(name).build(); + return template; + } + + @Override + public ListenableFuture createImage(ImageTemplate template) { + checkState(template instanceof CloneImageTemplate, + " openstack-nova only supports creating images through cloning."); + CloneImageTemplate cloneTemplate = (CloneImageTemplate) template; + final org.jclouds.cloudservers.domain.Image image = syncClient.createImageFromServer(cloneTemplate.getName(), + Integer.parseInt(cloneTemplate.getSourceNodeId())); + return Futures.makeListenable(executor.submit(new Callable() { + @Override + public Image call() throws Exception { + return Retryables.retryGettingResultOrFailing(new PredicateWithResult() { + + org.jclouds.cloudservers.domain.Image result; + RuntimeException lastFailure; + + @Override + public boolean apply(Integer input) { + result = checkNotNull(findImage(input)); + switch (result.getStatus()) { + case ACTIVE: + logger.info("<< Image %s is available for use.", input); + return true; + case UNKNOWN: + case SAVING: + logger.debug("<< Image %s is not available yet.", input); + return false; + default: + lastFailure = new IllegalStateException("Image was not created: " + input); + throw lastFailure; + } + } + + @Override + public Image getResult() { + return cloudserversImageToImage.apply(image); + } + + @Override + public Throwable getLastFailure() { + return lastFailure; + } + }, image.getId(), maxWait, waitPeriod, TimeUnit.SECONDS, + "Image was not created within the time limit, Giving up! [Limit: " + maxWait + " secs.]"); + } + }), executor); + + } + + @Override + public boolean deleteImage(String id) { + try { + this.syncClient.deleteImage(Integer.parseInt(id)); + } catch (Exception e) { + return false; + } + return true; + } + + private org.jclouds.cloudservers.domain.Image findImage(final int id) { + return Iterables.tryFind(syncClient.listImages(ListOptions.NONE), + new Predicate() { + @Override + public boolean apply(org.jclouds.cloudservers.domain.Image input) { + return input.getId() == id; + } + }).orNull(); + + } + +} diff --git a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/config/CloudServersComputeServiceContextModule.java b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/config/CloudServersComputeServiceContextModule.java index 9747eca77f..6375c15277 100644 --- a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/config/CloudServersComputeServiceContextModule.java +++ b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/config/CloudServersComputeServiceContextModule.java @@ -22,6 +22,7 @@ import java.util.Map; import javax.inject.Singleton; +import org.jclouds.cloudservers.compute.CloudServersImageExtension; import org.jclouds.cloudservers.compute.functions.CloudServersImageToImage; import org.jclouds.cloudservers.compute.functions.CloudServersImageToOperatingSystem; import org.jclouds.cloudservers.compute.functions.FlavorToHardware; @@ -31,6 +32,7 @@ import org.jclouds.cloudservers.domain.Flavor; import org.jclouds.cloudservers.domain.Server; import org.jclouds.cloudservers.domain.ServerStatus; import org.jclouds.compute.ComputeServiceAdapter; +import org.jclouds.compute.ImageExtension; import org.jclouds.compute.config.ComputeServiceAdapterContextModule; import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.Image; @@ -43,7 +45,9 @@ import org.jclouds.functions.IdentityFunction; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; +import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; +import com.google.inject.Injector; import com.google.inject.Provides; import com.google.inject.TypeLiteral; @@ -77,6 +81,9 @@ public class CloudServersComputeServiceContextModule extends // we aren't converting location from a provider-specific type bind(new TypeLiteral>() { }).to((Class) IdentityFunction.class); + + bind(new TypeLiteral() { + }).to(CloudServersImageExtension.class); } @@ -112,6 +119,10 @@ public class CloudServersComputeServiceContextModule extends Map provideServerToNodeState() { return serverToNodeState; } - + + @Override + protected Optional provideImageExtension(Injector i) { + return Optional.of(i.getInstance(ImageExtension.class)); + } } diff --git a/apis/cloudservers/src/test/java/org/jclouds/cloudservers/compute/CloudServersImageExtensionLiveTest.java b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/compute/CloudServersImageExtensionLiveTest.java new file mode 100644 index 0000000000..14e7a889b0 --- /dev/null +++ b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/compute/CloudServersImageExtensionLiveTest.java @@ -0,0 +1,47 @@ +/** + * 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.cloudservers.compute; + +import org.jclouds.compute.ImageExtension; +import org.jclouds.compute.internal.BaseImageExtensionLiveTest; +import org.jclouds.sshj.config.SshjSshClientModule; +import org.testng.annotations.Test; + +import com.google.inject.Module; + +/** + * Live test for cloudservers {@link ImageExtension} implementation + * + * @author David Alves + * + */ +@Test(groups = "live", singleThreaded = true, testName = "CloudServersImageExtensionLiveTest") +public class CloudServersImageExtensionLiveTest extends BaseImageExtensionLiveTest { + + public CloudServersImageExtensionLiveTest() { + provider = "cloudservers"; + } + + @Override + protected Module getSshModule() { + return new SshjSshClientModule(); + } + +} diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ImageExtension.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ImageExtension.java new file mode 100644 index 0000000000..2dc958b934 --- /dev/null +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ImageExtension.java @@ -0,0 +1,169 @@ +/** + * 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.ec2.compute; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import java.util.NoSuchElementException; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; + +import org.jclouds.Constants; +import org.jclouds.aws.util.AWSUtils; +import org.jclouds.compute.ImageExtension; +import org.jclouds.compute.domain.CloneImageTemplate; +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.domain.ImageTemplate; +import org.jclouds.compute.domain.ImageTemplateBuilder; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.concurrent.Futures; +import org.jclouds.ec2.EC2Client; +import org.jclouds.ec2.compute.functions.EC2ImageParser; +import org.jclouds.ec2.domain.Reservation; +import org.jclouds.ec2.domain.RunningInstance; +import org.jclouds.ec2.options.CreateImageOptions; +import org.jclouds.ec2.options.DescribeImagesOptions; +import org.jclouds.logging.Logger; +import org.jclouds.predicates.PredicateWithResult; +import org.jclouds.predicates.Retryables; + +import com.google.common.base.Function; +import com.google.common.collect.Iterables; +import com.google.common.util.concurrent.ListenableFuture; + +/** + * EC2 implementation of {@link ImageExtension} please note that {@link #createImage(ImageTemplate)} + * only works by cloning EBS backed instances for the moment. + * + * @author David Alves + * + */ +public class EC2ImageExtension implements ImageExtension { + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + @com.google.inject.Inject(optional = true) + @Named("IMAGE_MAX_WAIT") + long maxWait = 3600; + @com.google.inject.Inject(optional = true) + @Named("IMAGE_WAIT_PERIOD") + long waitPeriod = 1; + private final EC2Client ec2Client; + private final ExecutorService executor; + private final Function ecImageToImage; + + @Inject + public EC2ImageExtension(EC2Client ec2Client, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads, + EC2ImageParser ec2ImageToImage) { + this.ec2Client = checkNotNull(ec2Client); + this.executor = checkNotNull(userThreads); + this.ecImageToImage = checkNotNull(ec2ImageToImage); + } + + @Override + public ImageTemplate buildImageTemplateFromNode(String name, String id) { + String[] parts = AWSUtils.parseHandle(id); + String region = parts[0]; + String instanceId = parts[1]; + Reservation instance = Iterables.getOnlyElement(ec2Client.getInstanceServices() + .describeInstancesInRegion(region, instanceId)); + if (instance == null) + throw new NoSuchElementException("Cannot find server with id: " + id); + CloneImageTemplate template = new ImageTemplateBuilder.CloneImageTemplateBuilder().nodeId(id).name(name).build(); + return template; + } + + @Override + public ListenableFuture createImage(ImageTemplate template) { + checkState(template instanceof CloneImageTemplate, " ec2 only supports creating images through cloning."); + CloneImageTemplate cloneTemplate = (CloneImageTemplate) template; + String[] parts = AWSUtils.parseHandle(cloneTemplate.getSourceNodeId()); + final String region = parts[0]; + String instanceId = parts[1]; + + final String imageId = ec2Client.getAMIServices().createImageInRegion(region, cloneTemplate.getName(), + instanceId, CreateImageOptions.NONE); + + return Futures.makeListenable(executor.submit(new Callable() { + @Override + public Image call() throws Exception { + return Retryables.retryGettingResultOrFailing(new PredicateWithResult() { + + org.jclouds.ec2.domain.Image result; + RuntimeException lastFailure; + + @Override + public boolean apply(String input) { + result = checkNotNull(findImage(region, input)); + switch (result.getImageState()) { + case AVAILABLE: + logger.info("<< Image %s is available for use.", input); + return true; + case UNRECOGNIZED: + logger.debug("<< Image %s is not available yet.", input); + return false; + default: + lastFailure = new IllegalStateException("Image was not created: " + input); + throw lastFailure; + } + } + + @Override + public Image getResult() { + return ecImageToImage.apply(result); + } + + @Override + public Throwable getLastFailure() { + return lastFailure; + } + }, imageId, maxWait, waitPeriod, TimeUnit.SECONDS, + "Image was not created within the time limit, Giving up! [Limit: " + maxWait + " secs.]"); + } + }), executor); + } + + @Override + public boolean deleteImage(String id) { + String[] parts = AWSUtils.parseHandle(id); + String region = parts[0]; + String instanceId = parts[1]; + try { + ec2Client.getAMIServices().deregisterImageInRegion(region, instanceId); + return true; + } catch (Exception e) { + return false; + } + } + + private org.jclouds.ec2.domain.Image findImage(String region, String id) { + return Iterables.getOnlyElement(ec2Client.getAMIServices().describeImagesInRegion(region, + new DescribeImagesOptions().imageIds(id))); + + } +} diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java index dd714a4d0f..4ffdde1766 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java @@ -30,16 +30,19 @@ import javax.inject.Named; import javax.inject.Singleton; import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.ImageExtension; import org.jclouds.compute.config.BaseComputeServiceContextModule; import org.jclouds.compute.domain.Image; import org.jclouds.concurrent.RetryOnTimeOutExceptionSupplier; import org.jclouds.ec2.compute.EC2ComputeService; +import org.jclouds.ec2.compute.EC2ImageExtension; import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.loaders.RegionAndIdToImage; import org.jclouds.ec2.compute.suppliers.RegionAndNameToImageSupplier; import org.jclouds.rest.AuthorizationException; import org.jclouds.rest.suppliers.SetAndThrowAuthorizationExceptionSupplier; +import com.google.common.base.Optional; import com.google.common.base.Splitter; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; @@ -136,6 +139,11 @@ public class EC2ComputeServiceContextModule extends BaseComputeServiceContextMod return new String[] {}; return toArray(Splitter.on(',').split(amiOwners), String.class); } + + @Override + protected Optional provideImageExtension(Injector i) { + return Optional.of(i.getInstance(ImageExtension.class)); + } } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java index f82fa97ec4..9b2beea9e4 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java @@ -29,6 +29,7 @@ import javax.inject.Named; import javax.inject.Singleton; import org.jclouds.compute.ComputeService; +import org.jclouds.compute.ImageExtension; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.NodeState; @@ -36,6 +37,7 @@ import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.domain.Credentials; import org.jclouds.ec2.compute.EC2ComputeService; +import org.jclouds.ec2.compute.EC2ImageExtension; import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.functions.AddElasticIpsToNodemetadata; import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair; @@ -104,6 +106,8 @@ public class EC2ComputeServiceDependenciesModule extends AbstractModule { bind(new TypeLiteral>() { }).annotatedWith(Names.named("ELASTICIP")).to(LoadPublicIpForInstanceOrNull.class); bind(WindowsLoginCredentialsFromEncryptedData.class); + bind(new TypeLiteral() { + }).to(EC2ImageExtension.class); } /** diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSet.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSet.java index 6ac3cd9b80..af2f4d648f 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSet.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSet.java @@ -132,13 +132,23 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen @Override public Map> execute(String group, int count, Template template, Set goodNodes, Map badNodes, Multimap customizationResponses) { + // ensure we don't mutate the input template - template = templateBuilderProvider.get().fromTemplate(template).build(); + Template mutableTemplate; + // ensure we don't mutate the input template, fromTemplate ignores imageId so + // build directly from imageId if we have it + if (template.getImage() != null && template.getImage().getId() != null) { + mutableTemplate = templateBuilderProvider.get().imageId(template.getImage().getId()).fromTemplate(template) + .build(); + // otherwise build from generic parameters + } else { + mutableTemplate = templateBuilderProvider.get().fromTemplate(template).build(); + } Iterable ips = allocateElasticIpsInRegion(count, template); Iterable started = createKeyPairAndSecurityGroupsAsNeededThenRunInstances(group, - count, template); + count, mutableTemplate); Iterable ids = transform(started, instanceToRegionAndName); @@ -152,7 +162,7 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen assignElasticIpsToInstances(ips, started); - return utils.customizeNodesAndAddToGoodMapOrPutExceptionIntoBadMap(template.getOptions(), transform(started, + return utils.customizeNodesAndAddToGoodMapOrPutExceptionIntoBadMap(mutableTemplate.getOptions(), transform(started, runningInstanceToNodeMetadata), goodNodes, badNodes, customizationResponses); } diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ImageExtensionLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ImageExtensionLiveTest.java new file mode 100644 index 0000000000..895958b1d8 --- /dev/null +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ImageExtensionLiveTest.java @@ -0,0 +1,47 @@ +/** + * 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.ec2.compute; + +import org.jclouds.compute.ImageExtension; +import org.jclouds.compute.internal.BaseImageExtensionLiveTest; +import org.jclouds.sshj.config.SshjSshClientModule; +import org.testng.annotations.Test; + +import com.google.inject.Module; + +/** + * Live test for ec2 {@link ImageExtension} implementation + * + * @author David Alves + * + */ +@Test(groups = "live", singleThreaded = true, testName = "EC2ImageExtensionLiveTest") +public class EC2ImageExtensionLiveTest extends BaseImageExtensionLiveTest { + + public EC2ImageExtensionLiveTest() { + provider = "ec2"; + } + + @Override + protected Module getSshModule() { + return new SshjSshClientModule(); + } + +} diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaImageExtension.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaImageExtension.java index 0b92881c5b..727a82bbb5 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaImageExtension.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaImageExtension.java @@ -23,34 +23,59 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import java.util.NoSuchElementException; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import javax.annotation.Resource; import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Singleton; +import org.jclouds.Constants; import org.jclouds.compute.ImageExtension; import org.jclouds.compute.domain.CloneImageTemplate; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.ImageTemplate; import org.jclouds.compute.domain.ImageTemplateBuilder; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.concurrent.Futures; +import org.jclouds.logging.Logger; import org.jclouds.openstack.nova.v1_1.NovaClient; import org.jclouds.openstack.nova.v1_1.domain.Server; import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ImageInZone; import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndId; +import org.jclouds.predicates.PredicateWithResult; +import org.jclouds.predicates.Retryables; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; +import com.google.common.util.concurrent.ListenableFuture; @Singleton public class NovaImageExtension implements ImageExtension { - + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + private final NovaClient novaClient; private final Function imageInZoneToImage; + private final ExecutorService executor; + @com.google.inject.Inject(optional = true) + @Named("IMAGE_MAX_WAIT") + long maxWait = 3600; + @com.google.inject.Inject(optional = true) + @Named("IMAGE_WAIT_PERIOD") + long waitPeriod = 1; @Inject - public NovaImageExtension(NovaClient novaClient, Function imageInZoneToImage) { + public NovaImageExtension(NovaClient novaClient, Function imageInZoneToImage, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads) { this.novaClient = checkNotNull(novaClient); this.imageInZoneToImage = imageInZoneToImage; + this.executor = userThreads; } @Override @@ -64,16 +89,54 @@ public class NovaImageExtension implements ImageExtension { } @Override - public Image createImage(ImageTemplate template) { + public ListenableFuture createImage(ImageTemplate template) { checkState(template instanceof CloneImageTemplate, " openstack-nova only supports creating images through cloning."); CloneImageTemplate cloneTemplate = (CloneImageTemplate) template; - ZoneAndId zoneAndId = ZoneAndId.fromSlashEncoded(cloneTemplate.getSourceNodeId()); - String newImageId = novaClient.getServerClientForZone(zoneAndId.getZone()).createImageFromServer( + final ZoneAndId zoneAndId = ZoneAndId.fromSlashEncoded(cloneTemplate.getSourceNodeId()); + + final String newImageId = novaClient.getServerClientForZone(zoneAndId.getZone()).createImageFromServer( cloneTemplate.getName(), zoneAndId.getId()); - org.jclouds.openstack.nova.v1_1.domain.Image newImage = checkNotNull(findImage(ZoneAndId.fromZoneAndId( - zoneAndId.getZone(), newImageId))); - return imageInZoneToImage.apply(new ImageInZone(newImage, zoneAndId.getZone())); + logger.info(">> Registered new Image %s, waiting for it to become available.", newImageId); + + return Futures.makeListenable(executor.submit(new Callable() { + @Override + public Image call() throws Exception { + return Retryables.retryGettingResultOrFailing(new PredicateWithResult() { + + org.jclouds.openstack.nova.v1_1.domain.Image result; + RuntimeException lastFailure; + + @Override + public boolean apply(String input) { + result = checkNotNull(findImage(ZoneAndId.fromZoneAndId(zoneAndId.getZone(), newImageId))); + switch (result.getStatus()) { + case ACTIVE: + logger.info("<< Image %s is available for use.", newImageId); + return true; + case UNKNOWN: + case SAVING: + logger.debug("<< Image %s is not available yet.", newImageId); + return false; + default: + lastFailure = new IllegalStateException("Image was not created: " + newImageId); + throw lastFailure; + } + } + + @Override + public Image getResult() { + return imageInZoneToImage.apply(new ImageInZone(result, zoneAndId.getZone())); + } + + @Override + public Throwable getLastFailure() { + return lastFailure; + } + }, newImageId, maxWait, waitPeriod, TimeUnit.SECONDS, + "Image was not created within the time limit, Giving up! [Limit: " + maxWait + " secs.]"); + } + }), executor); } @Override diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/strategy/ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/strategy/ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java index 6bd62df9c4..0ceca917cd 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/strategy/ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/strategy/ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java @@ -65,7 +65,7 @@ import com.google.common.primitives.Ints; */ @Singleton public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet extends - CreateNodesWithGroupEncodedIntoNameThenAddToSet { + CreateNodesWithGroupEncodedIntoNameThenAddToSet { private final AllocateAndAddFloatingIpToNode allocateAndAddFloatingIpToNode; private final LoadingCache securityGroupCache; @@ -75,56 +75,67 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT @Inject protected ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet( - CreateNodeWithGroupEncodedIntoName addNodeWithTagStrategy, - ListNodesStrategy listNodesStrategy, - GroupNamingConvention.Factory namingConvention, - CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory, - @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, - Provider templateBuilderProvider, - AllocateAndAddFloatingIpToNode allocateAndAddFloatingIpToNode, - LoadingCache securityGroupCache, - LoadingCache keyPairCache, NovaClient novaClient) { + CreateNodeWithGroupEncodedIntoName addNodeWithTagStrategy, + ListNodesStrategy listNodesStrategy, + GroupNamingConvention.Factory namingConvention, + CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, + Provider templateBuilderProvider, + AllocateAndAddFloatingIpToNode allocateAndAddFloatingIpToNode, + LoadingCache securityGroupCache, + LoadingCache keyPairCache, NovaClient novaClient) { super(addNodeWithTagStrategy, listNodesStrategy, namingConvention, executor, - customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory); + customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory); this.templateBuilderProvider = checkNotNull(templateBuilderProvider, "templateBuilderProvider"); this.securityGroupCache = checkNotNull(securityGroupCache, "securityGroupCache"); this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache"); this.allocateAndAddFloatingIpToNode = checkNotNull(allocateAndAddFloatingIpToNode, - "allocateAndAddFloatingIpToNode"); + "allocateAndAddFloatingIpToNode"); this.novaClient = checkNotNull(novaClient, "novaClient"); } @Override public Map> execute(String group, int count, Template template, Set goodNodes, - Map badNodes, Multimap customizationResponses) { - // ensure we don't mutate the input template - Template mutableTemplate = templateBuilderProvider.get().fromTemplate(template).build(); + Map badNodes, Multimap customizationResponses) { + + Template mutableTemplate; + // ensure we don't mutate the input template, fromTemplate ignores imageId so + // build directly from imageId if we have it + if (template.getImage() != null && template.getImage().getId() != null) { + mutableTemplate = templateBuilderProvider.get().imageId(template.getImage().getId()).fromTemplate(template) + .build(); + // otherwise build from generic parameters + } else { + mutableTemplate = templateBuilderProvider.get().fromTemplate(template).build(); + } + NovaTemplateOptions templateOptions = NovaTemplateOptions.class.cast(mutableTemplate.getOptions()); - + assert template.getOptions().equals(templateOptions) : "options didn't clone properly"; - + String zone = mutableTemplate.getLocation().getId(); if (templateOptions.shouldAutoAssignFloatingIp()) { checkArgument(novaClient.getFloatingIPExtensionForZone(zone).isPresent(), - "Floating IPs are required by options, but the extension is not available! options: %s", templateOptions); + "Floating IPs are required by options, but the extension is not available! options: %s", + templateOptions); } boolean keyPairExensionPresent = novaClient.getKeyPairExtensionForZone(zone).isPresent(); if (templateOptions.shouldGenerateKeyPair()) { checkArgument(keyPairExensionPresent, - "Key Pairs are required by options, but the extension is not available! options: %s", templateOptions); + "Key Pairs are required by options, but the extension is not available! options: %s", templateOptions); KeyPair keyPair = keyPairCache.getUnchecked(ZoneAndName.fromZoneAndName(zone, namingConvention.create() - .sharedNameForGroup(group))); + .sharedNameForGroup(group))); keyPairCache.asMap().put(ZoneAndName.fromZoneAndName(zone, keyPair.getName()), keyPair); templateOptions.keyPairName(keyPair.getName()); } else if (templateOptions.getKeyPairName() != null) { checkArgument(keyPairExensionPresent, - "Key Pairs are required by options, but the extension is not available! options: %s", templateOptions); + "Key Pairs are required by options, but the extension is not available! options: %s", templateOptions); if (templateOptions.getLoginPrivateKey() != null) { String pem = templateOptions.getLoginPrivateKey(); KeyPair keyPair = KeyPair.builder().name(templateOptions.getKeyPairName()) - .fingerprint(fingerprintPrivateKey(pem)).privateKey(pem).build(); + .fingerprint(fingerprintPrivateKey(pem)).privateKey(pem).build(); keyPairCache.asMap().put(ZoneAndName.fromZoneAndName(zone, keyPair.getName()), keyPair); } } @@ -133,8 +144,8 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT List inboundPorts = Ints.asList(templateOptions.getInboundPorts()); if (templateOptions.getSecurityGroupNames().size() > 0) { checkArgument(novaClient.getSecurityGroupExtensionForZone(zone).isPresent(), - "Security groups are required by options, but the extension is not available! options: %s", - templateOptions); + "Security groups are required by options, but the extension is not available! options: %s", + templateOptions); } else if (securityGroupExensionPresent && inboundPorts.size() > 0) { String securityGroupName = namingConvention.create().sharedNameForGroup(group); try { @@ -150,7 +161,7 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT @Override protected Future> createNodeInGroupWithNameAndTemplate(String group, - final String name, Template template) { + final String name, Template template) { Future> future = super.createNodeInGroupWithNameAndTemplate(group, name, template); NovaTemplateOptions templateOptions = NovaTemplateOptions.class.cast(template.getOptions()); diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/NovaImageExtensionLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/NovaImageExtensionLiveTest.java new file mode 100644 index 0000000000..1f8086610a --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/NovaImageExtensionLiveTest.java @@ -0,0 +1,47 @@ +/** + * 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.compute; + +import org.jclouds.compute.ImageExtension; +import org.jclouds.compute.internal.BaseImageExtensionLiveTest; +import org.jclouds.sshj.config.SshjSshClientModule; +import org.testng.annotations.Test; + +import com.google.inject.Module; + +/** + * Live test for openstack-nova {@link ImageExtension} implementation. + * + * @author David Alves + * + */ +@Test(groups = "live", singleThreaded = true, testName = "NovaImageExtensionLiveTest") +public class NovaImageExtensionLiveTest extends BaseImageExtensionLiveTest { + + public NovaImageExtensionLiveTest() { + provider = "openstack-nova"; + } + + @Override + protected Module getSshModule() { + return new SshjSshClientModule(); + } + +} diff --git a/compute/src/main/java/org/jclouds/compute/ImageExtension.java b/compute/src/main/java/org/jclouds/compute/ImageExtension.java index 56da218825..ea0d92837c 100644 --- a/compute/src/main/java/org/jclouds/compute/ImageExtension.java +++ b/compute/src/main/java/org/jclouds/compute/ImageExtension.java @@ -22,6 +22,8 @@ package org.jclouds.compute; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.ImageTemplate; +import com.google.common.util.concurrent.ListenableFuture; + /** * An extension to compute service to allow for the manipulation of {@link Image}s. Implementation * is optional by providers. @@ -49,7 +51,7 @@ public interface ImageExtension { * template to base the new image on * @return the image that was just built *after* it is registered on the provider */ - Image createImage(ImageTemplate template); + ListenableFuture createImage(ImageTemplate template); /** * Delete an {@link Image} on the provider. diff --git a/compute/src/test/java/org/jclouds/compute/internal/BaseImageExtensionLiveTest.java b/compute/src/test/java/org/jclouds/compute/internal/BaseImageExtensionLiveTest.java index 3bb31df3bf..92eb7a8cdb 100644 --- a/compute/src/test/java/org/jclouds/compute/internal/BaseImageExtensionLiveTest.java +++ b/compute/src/test/java/org/jclouds/compute/internal/BaseImageExtensionLiveTest.java @@ -22,16 +22,22 @@ package org.jclouds.compute.internal; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; -import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import javax.annotation.Resource; +import javax.inject.Named; import org.jclouds.compute.ComputeService; import org.jclouds.compute.ImageExtension; import org.jclouds.compute.RunNodesException; -import org.jclouds.compute.domain.ExecResponse; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.ImageTemplate; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.Template; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.logging.Logger; +import org.jclouds.predicates.RetryablePredicate; import org.jclouds.ssh.SshClient; import org.testng.annotations.Test; @@ -47,40 +53,74 @@ import com.google.common.collect.Iterables; */ public abstract class BaseImageExtensionLiveTest extends BaseComputeServiceContextLiveTest { + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + protected String imageId; + /** * Returns the template for the base node, override to test different templates. * * @return */ public Template getNodeTemplate() { - return view.getComputeService().templateBuilder().any().build(); + return view.getComputeService().templateBuilder().build(); + } + + /** + * Returns the maximum amount of time (in seconds) to wait for a node spawned from the new image + * to become available, override to increase this time. + * + * @return + */ + public long getSpawnNodeMaxWait() { + return 600L; + } + + /** + * Lists the images found in the {@link ComputeService}, subclasses may override to constrain + * search. + * + * @return + */ + protected Iterable listImages() { + return view.getComputeService().listImages(); } @Test(groups = { "integration", "live" }, singleThreaded = true) - public void testCreateImage() throws RunNodesException, InterruptedException { + public void testCreateImage() throws RunNodesException, InterruptedException, ExecutionException { ComputeService computeService = view.getComputeService(); Optional imageExtension = computeService.getImageExtension(); + assertTrue("image extension was not present", imageExtension.isPresent()); - Set imagesBefore = computeService.listImages(); + Template template = getNodeTemplate(); - NodeMetadata node = Iterables.getOnlyElement(computeService.createNodesInGroup("test-create-image", 1, - getNodeTemplate())); + NodeMetadata node = Iterables.getOnlyElement(computeService.createNodesInGroup("test-create-image", 1, template)); + + checkReachable(node); + + logger.info("Creating image from node %s, started with template: %s", node, template); ImageTemplate newImageTemplate = imageExtension.get().buildImageTemplateFromNode("test-create-image", node.getId()); - Image image = imageExtension.get().createImage(newImageTemplate); + Image image = imageExtension.get().createImage(newImageTemplate).get(); + + logger.info("Image created: %s", image); assertEquals("test-create-image", image.getName()); + imageId = image.getId(); + computeService.destroyNode(node.getId()); - Set imagesAfter = computeService.listImages(); + Optional optImage = getImage(); - assertTrue(imagesBefore.size() == imagesAfter.size() - 1); + assertTrue(optImage.isPresent()); } @@ -89,16 +129,16 @@ public abstract class BaseImageExtensionLiveTest extends BaseComputeServiceConte ComputeService computeService = view.getComputeService(); - Template template = computeService.templateBuilder().fromImage(getImage().get()).build(); + Optional optImage = getImage(); - NodeMetadata node = Iterables.getOnlyElement(computeService.createNodesInGroup("test-create-image", 1, template)); + assertTrue(optImage.isPresent()); - SshClient client = view.utils().sshForNode().apply(node); - client.connect(); + NodeMetadata node = Iterables.getOnlyElement(computeService.createNodesInGroup("test-create-image", 1, view + .getComputeService() + // fromImage does not use the arg image's id (but we do need to set location) + .templateBuilder().imageId(optImage.get().getId()).fromImage(optImage.get()).build())); - ExecResponse hello = client.exec("echo hello"); - - assertEquals(hello.getOutput().trim(), "hello"); + checkReachable(node); view.getComputeService().destroyNode(node.getId()); @@ -123,12 +163,25 @@ public abstract class BaseImageExtensionLiveTest extends BaseComputeServiceConte } private Optional getImage() { - return Iterables.tryFind(view.getComputeService().listImages(), new Predicate() { + return Iterables.tryFind(listImages(), new Predicate() { @Override public boolean apply(Image input) { - return input.getId().contains("test-create-image"); + return input.getId().equals(imageId); } }); } + private void checkReachable(NodeMetadata node) { + SshClient client = view.utils().sshForNode().apply(node); + assertTrue(new RetryablePredicate(new Predicate() { + @Override + public boolean apply(SshClient input) { + input.connect(); + if (input.exec("id").getExitStatus() == 0) { + return true; + } + return false; + } + }, getSpawnNodeMaxWait(), 1l, TimeUnit.SECONDS).apply(client)); + } } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/compute/VirtualBoxImageExtension.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/compute/VirtualBoxImageExtension.java index 3b859549bd..6da9fbfbfb 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/compute/VirtualBoxImageExtension.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/compute/VirtualBoxImageExtension.java @@ -60,6 +60,8 @@ import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Supplier; import com.google.common.collect.Iterables; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; @Singleton public class VirtualBoxImageExtension implements ImageExtension { @@ -104,7 +106,7 @@ public class VirtualBoxImageExtension implements ImageExtension { } @Override - public Image createImage(ImageTemplate template) { + public ListenableFuture createImage(ImageTemplate template) { checkState(template instanceof CloneImageTemplate, " vbox image extension only supports cloning for the moment."); CloneImageTemplate cloneTemplate = CloneImageTemplate.class.cast(template); @@ -133,7 +135,7 @@ public class VirtualBoxImageExtension implements ImageExtension { // registering manager.get().getVBox().registerMachine(clonedMachine); - return imachineToImage.apply(clonedMachine); + return Futures.immediateFuture(imachineToImage.apply(clonedMachine)); } @Override diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java index 3368d5c120..1edd61873d 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java @@ -39,6 +39,7 @@ import org.jclouds.aws.ec2.compute.strategy.AWSEC2ListNodesStrategy; import org.jclouds.aws.ec2.compute.strategy.AWSEC2ReviseParsedImage; import org.jclouds.aws.ec2.compute.strategy.CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions; import org.jclouds.aws.ec2.compute.suppliers.AWSEC2HardwareSupplier; +import org.jclouds.compute.ImageExtension; import org.jclouds.compute.config.BaseComputeServiceContextModule; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.TemplateBuilder; @@ -62,6 +63,7 @@ import org.jclouds.ec2.compute.suppliers.RegionAndNameToImageSupplier; import org.jclouds.rest.AuthorizationException; import org.jclouds.rest.suppliers.SetAndThrowAuthorizationExceptionSupplier; +import com.google.common.base.Optional; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.base.Throwables; @@ -180,4 +182,9 @@ public class AWSEC2ComputeServiceContextModule extends BaseComputeServiceContext protected TemplateOptions provideTemplateOptions(Injector injector, TemplateOptions options) { return options.as(EC2TemplateOptions.class).userData("#cloud-config\nrepo_upgrade: none\n".getBytes()); } + + @Override + protected Optional provideImageExtension(Injector i) { + return Optional.of(i.getInstance(ImageExtension.class)); + } } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java index e9c159ffe0..37b3593b53 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java @@ -40,11 +40,13 @@ import org.jclouds.aws.ec2.functions.ImportOrReturnExistingKeypair; import org.jclouds.aws.ec2.predicates.PlacementGroupAvailable; import org.jclouds.aws.ec2.predicates.PlacementGroupDeleted; import org.jclouds.compute.ComputeService; +import org.jclouds.compute.ImageExtension; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.config.ValueOfConfigurationKeyOrNull; import org.jclouds.domain.Credentials; +import org.jclouds.ec2.compute.EC2ImageExtension; import org.jclouds.ec2.compute.config.EC2ComputeServiceDependenciesModule; import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair; @@ -94,6 +96,8 @@ public class AWSEC2ComputeServiceDependenciesModule extends EC2ComputeServiceDep bind(new TypeLiteral>() { }).to(RegionAndIdToImage.class); install(new FactoryModuleBuilder().build(CallForImages.Factory.class)); + bind(new TypeLiteral() { + }).to(EC2ImageExtension.class); } @Provides diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ImageExtensionLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ImageExtensionLiveTest.java new file mode 100644 index 0000000000..37d255ba6d --- /dev/null +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ImageExtensionLiveTest.java @@ -0,0 +1,67 @@ +/** + * 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.aws.ec2.compute; + +import org.jclouds.aws.ec2.AWSEC2AsyncClient; +import org.jclouds.aws.ec2.AWSEC2Client; +import org.jclouds.aws.util.AWSUtils; +import org.jclouds.compute.ImageExtension; +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.internal.BaseImageExtensionLiveTest; +import org.jclouds.ec2.compute.functions.EC2ImageParser; +import org.jclouds.ec2.options.DescribeImagesOptions; +import org.jclouds.rest.RestContext; +import org.jclouds.sshj.config.SshjSshClientModule; +import org.testng.annotations.Test; + +import com.google.common.collect.Iterables; +import com.google.inject.Module; + +/** + * Live test for aws-ec2 {@link ImageExtension} implementation + * + * @author David Alves + * + */ +@Test(groups = "live", singleThreaded = true, testName = "AWSEC2ImageExtensionLiveTest") +public class AWSEC2ImageExtensionLiveTest extends BaseImageExtensionLiveTest { + + public AWSEC2ImageExtensionLiveTest() { + provider = "aws-ec2"; + } + + @Override + protected Iterable listImages() { + RestContext unwrapped = view.unwrap(); + String[] parts = AWSUtils.parseHandle(imageId); + String region = parts[0]; + String imageId = parts[1]; + EC2ImageParser parser = view.utils().getInjector().getInstance(EC2ImageParser.class); + return Iterables.transform( + unwrapped.getApi().getAMIServices() + .describeImagesInRegion(region, new DescribeImagesOptions().imageIds(imageId)), parser); + } + + @Override + protected Module getSshModule() { + return new SshjSshClientModule(); + } + +} diff --git a/providers/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/compute/HPCloudComputeImageExtensionLivetest.java b/providers/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/compute/HPCloudComputeImageExtensionLivetest.java index f632f51cc0..852ce623f5 100644 --- a/providers/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/compute/HPCloudComputeImageExtensionLivetest.java +++ b/providers/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/compute/HPCloudComputeImageExtensionLivetest.java @@ -41,4 +41,9 @@ public class HPCloudComputeImageExtensionLivetest extends BaseImageExtensionLive protected Module getSshModule() { return new SshjSshClientModule(); } + + @Override + public long getSpawnNodeMaxWait() { + return 2400L; + } } From 850cdc2fc810830844704bc3e05d0f2191d3872d Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Thu, 3 May 2012 12:31:39 +0100 Subject: [PATCH 017/148] Moving EncodedRSAPublicKeyToBase64 (and corresponding unit test) up into EC2 so both openstack-nova-ec2 and aws-ec2 can share the same class --- .../jclouds}/ec2/functions/EncodedRSAPublicKeyToBase64.java | 2 +- .../ec2/functions/EncodedRSAPublicKeyToBase64Test.java | 2 +- .../org/jclouds/aws/ec2/services/AWSKeyPairAsyncClient.java | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) rename {providers/aws-ec2/src/main/java/org/jclouds/aws => apis/ec2/src/main/java/org/jclouds}/ec2/functions/EncodedRSAPublicKeyToBase64.java (98%) rename {providers/aws-ec2/src/test/java/org/jclouds/aws => apis/ec2/src/test/java/org/jclouds}/ec2/functions/EncodedRSAPublicKeyToBase64Test.java (97%) diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/functions/EncodedRSAPublicKeyToBase64.java b/apis/ec2/src/main/java/org/jclouds/ec2/functions/EncodedRSAPublicKeyToBase64.java similarity index 98% rename from providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/functions/EncodedRSAPublicKeyToBase64.java rename to apis/ec2/src/main/java/org/jclouds/ec2/functions/EncodedRSAPublicKeyToBase64.java index de9d32374c..0d877d6343 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/functions/EncodedRSAPublicKeyToBase64.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/functions/EncodedRSAPublicKeyToBase64.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.jclouds.aws.ec2.functions; +package org.jclouds.ec2.functions; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/functions/EncodedRSAPublicKeyToBase64Test.java b/apis/ec2/src/test/java/org/jclouds/ec2/functions/EncodedRSAPublicKeyToBase64Test.java similarity index 97% rename from providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/functions/EncodedRSAPublicKeyToBase64Test.java rename to apis/ec2/src/test/java/org/jclouds/ec2/functions/EncodedRSAPublicKeyToBase64Test.java index fa4616a0e9..05ba97ab45 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/functions/EncodedRSAPublicKeyToBase64Test.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/functions/EncodedRSAPublicKeyToBase64Test.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.jclouds.aws.ec2.functions; +package org.jclouds.ec2.functions; import static org.testng.Assert.assertEquals; diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSKeyPairAsyncClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSKeyPairAsyncClient.java index cec98084a3..98b6ff4705 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSKeyPairAsyncClient.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSKeyPairAsyncClient.java @@ -24,11 +24,10 @@ import javax.ws.rs.FormParam; import javax.ws.rs.POST; import javax.ws.rs.Path; -import org.jclouds.aws.ec2.functions.EncodedRSAPublicKeyToBase64; import org.jclouds.aws.filters.FormSigner; import org.jclouds.ec2.domain.KeyPair; +import org.jclouds.ec2.functions.EncodedRSAPublicKeyToBase64; import org.jclouds.ec2.services.KeyPairAsyncClient; -import org.jclouds.ec2.services.KeyPairClient; import org.jclouds.ec2.xml.KeyPairResponseHandler; import org.jclouds.javax.annotation.Nullable; import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull; @@ -49,7 +48,7 @@ import com.google.common.util.concurrent.ListenableFuture; public interface AWSKeyPairAsyncClient extends KeyPairAsyncClient { /** - * @see KeyPairClient#importKeyPairInRegion + * @see AWSKeyPairClient#importKeyPairInRegion */ @POST @Path("/") From 204194ad61b93b81f372c6cc3a1d75f2a17e9d3c Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Thu, 3 May 2012 12:35:32 +0100 Subject: [PATCH 018/148] openstack-nova-ec2: configuring tests to use logback (so I can view the jclouds-wire log) --- apis/openstack-nova-ec2/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apis/openstack-nova-ec2/pom.xml b/apis/openstack-nova-ec2/pom.xml index d7c2abd444..e8ef79dbf7 100644 --- a/apis/openstack-nova-ec2/pom.xml +++ b/apis/openstack-nova-ec2/pom.xml @@ -78,7 +78,7 @@ org.jclouds.driver - jclouds-slf4j + jclouds-log4j ${project.version} test From 05521685cd99f61212c2a9d723a56544696323a5 Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Thu, 3 May 2012 12:38:30 +0100 Subject: [PATCH 019/148] openstack-nova-ec2: adding NovaEC2KeyPairClient with importKeyPair method and wiring it into rest context --- .../nova/ec2/NovaEC2ApiMetadata.java | 15 ++-- .../nova/ec2/NovaEC2AsyncClient.java | 37 ++++++++++ .../openstack/nova/ec2/NovaEC2Client.java | 42 +++++++++++ .../ec2/config/NovaEC2RestClientModule.java | 40 ++++++++++- .../services/NovaEC2KeyPairAsyncClient.java | 61 ++++++++++++++++ .../ec2/services/NovaEC2KeyPairClient.java | 69 +++++++++++++++++++ 6 files changed, 255 insertions(+), 9 deletions(-) create mode 100644 apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/NovaEC2AsyncClient.java create mode 100644 apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/NovaEC2Client.java create mode 100644 apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/services/NovaEC2KeyPairAsyncClient.java create mode 100644 apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/services/NovaEC2KeyPairClient.java diff --git a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/NovaEC2ApiMetadata.java b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/NovaEC2ApiMetadata.java index e0c53d8051..904c26f829 100644 --- a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/NovaEC2ApiMetadata.java +++ b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/NovaEC2ApiMetadata.java @@ -29,8 +29,6 @@ import java.util.Properties; import org.jclouds.apis.ApiMetadata; import org.jclouds.ec2.EC2ApiMetadata; -import org.jclouds.ec2.EC2AsyncClient; -import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.compute.config.EC2ResolveImagesModule; import org.jclouds.openstack.nova.ec2.config.NovaEC2ComputeServiceContextModule; import org.jclouds.openstack.nova.ec2.config.NovaEC2RestClientModule; @@ -48,12 +46,12 @@ import com.google.inject.Module; public class NovaEC2ApiMetadata extends EC2ApiMetadata { /** The serialVersionUID */ - private static final long serialVersionUID = -1492951757032303845L; - - public static final TypeToken> CONTEXT_TOKEN = new TypeToken>() { - private static final long serialVersionUID = -5070937833892503232L; + private static final long serialVersionUID = -8539835226183747429L; + + public static final TypeToken> CONTEXT_TOKEN = new TypeToken>() { + private static final long serialVersionUID = -6449920293625658712L; }; - + private static Builder builder() { return new Builder(); } @@ -90,12 +88,13 @@ public class NovaEC2ApiMetadata extends EC2ApiMetadata { public static class Builder extends EC2ApiMetadata.Builder { protected Builder(){ - super(EC2Client.class, EC2AsyncClient.class); + super(NovaEC2Client.class, NovaEC2AsyncClient.class); id("openstack-nova-ec2") .name("OpenStack Nova's EC2-clone API") .version("2009-04-04") .defaultEndpoint("http://localhost:8773/services/Cloud") .defaultProperties(NovaEC2ApiMetadata.defaultProperties()) + .context(CONTEXT_TOKEN) .defaultModules(ImmutableSet.>of(NovaEC2RestClientModule.class, EC2ResolveImagesModule.class, NovaEC2ComputeServiceContextModule.class)); } diff --git a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/NovaEC2AsyncClient.java b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/NovaEC2AsyncClient.java new file mode 100644 index 0000000000..7d09a25f04 --- /dev/null +++ b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/NovaEC2AsyncClient.java @@ -0,0 +1,37 @@ +/** + * 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.ec2; + +import org.jclouds.ec2.EC2AsyncClient; +import org.jclouds.openstack.nova.ec2.services.NovaEC2KeyPairAsyncClient; +import org.jclouds.rest.annotations.Delegate; + +/** + * Provides asynchronous access to EC2 services. + * + * @author Adam Lowe + */ +public interface NovaEC2AsyncClient extends EC2AsyncClient { + /** + * {@inheritDoc} + */ + @Delegate + @Override + NovaEC2KeyPairAsyncClient getKeyPairServices(); +} diff --git a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/NovaEC2Client.java b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/NovaEC2Client.java new file mode 100644 index 0000000000..ab9bab86b0 --- /dev/null +++ b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/NovaEC2Client.java @@ -0,0 +1,42 @@ +/** + * 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.ec2; + +import java.util.concurrent.TimeUnit; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.ec2.EC2Client; +import org.jclouds.openstack.nova.ec2.services.NovaEC2KeyPairClient; +import org.jclouds.rest.annotations.Delegate; + +/** + * Provides synchronous access to EC2 services. + * + * @author Adam Lowe + */ +@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS) +public interface NovaEC2Client extends EC2Client { + + /** + * {@inheritDoc} + */ + @Delegate + @Override + NovaEC2KeyPairClient getKeyPairServices(); +} diff --git a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2RestClientModule.java b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2RestClientModule.java index da18c3733f..311c9589a0 100644 --- a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2RestClientModule.java +++ b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2RestClientModule.java @@ -18,9 +18,14 @@ */ package org.jclouds.openstack.nova.ec2.config; +import java.util.Map; + +import javax.inject.Singleton; + import org.jclouds.ec2.EC2AsyncClient; import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.config.EC2RestClientModule; +import org.jclouds.ec2.services.*; import org.jclouds.ec2.suppliers.DescribeAvailabilityZonesInRegion; import org.jclouds.ec2.xml.CreateVolumeResponseHandler; import org.jclouds.ec2.xml.DescribeImagesResponseHandler; @@ -28,10 +33,17 @@ import org.jclouds.location.config.LocationModule; import org.jclouds.location.suppliers.RegionIdToZoneIdsSupplier; import org.jclouds.location.suppliers.ZoneIdsSupplier; import org.jclouds.location.suppliers.derived.ZoneIdsFromRegionIdToZoneIdsValues; +import org.jclouds.openstack.nova.ec2.NovaEC2AsyncClient; +import org.jclouds.openstack.nova.ec2.NovaEC2Client; +import org.jclouds.openstack.nova.ec2.services.NovaEC2KeyPairAsyncClient; +import org.jclouds.openstack.nova.ec2.services.NovaEC2KeyPairClient; import org.jclouds.openstack.nova.ec2.xml.NovaCreateVolumeResponseHandler; import org.jclouds.openstack.nova.ec2.xml.NovaDescribeImagesResponseHandler; import org.jclouds.rest.ConfiguresRestClient; +import com.google.common.collect.ImmutableMap; +import com.google.common.reflect.TypeToken; +import com.google.inject.Provides; import com.google.inject.Scopes; /** @@ -40,8 +52,22 @@ import com.google.inject.Scopes; * @author Adam Lowe */ @ConfiguresRestClient -public class NovaEC2RestClientModule extends EC2RestClientModule { +public class NovaEC2RestClientModule extends EC2RestClientModule { + public static final Map, Class> DELEGATE_MAP = ImmutableMap., Class> builder()// + .put(AMIClient.class, AMIAsyncClient.class)// + .put(ElasticIPAddressClient.class, ElasticIPAddressAsyncClient.class)// + .put(InstanceClient.class, InstanceAsyncClient.class)// + .put(NovaEC2KeyPairClient.class, NovaEC2KeyPairAsyncClient.class)// + .put(SecurityGroupClient.class, SecurityGroupAsyncClient.class)// + .put(WindowsClient.class, WindowsAsyncClient.class)// + .put(AvailabilityZoneAndRegionClient.class, AvailabilityZoneAndRegionAsyncClient.class)// + .put(ElasticBlockStoreClient.class, ElasticBlockStoreAsyncClient.class)// + .build(); + public NovaEC2RestClientModule() { + super(TypeToken.of(NovaEC2Client.class), TypeToken.of(NovaEC2AsyncClient.class), DELEGATE_MAP); + } + @Override protected void configure() { install(new NovaEC2ParserModule()); @@ -57,4 +83,16 @@ public class NovaEC2RestClientModule extends EC2RestClientModule importKeyPairInRegion( + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @FormParam("KeyName") String keyName, + @FormParam("PublicKeyMaterial") @ParamParser(EncodedRSAPublicKeyToBase64.class) String publicKeyMaterial); +} diff --git a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/services/NovaEC2KeyPairClient.java b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/services/NovaEC2KeyPairClient.java new file mode 100644 index 0000000000..ef4e88b714 --- /dev/null +++ b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/services/NovaEC2KeyPairClient.java @@ -0,0 +1,69 @@ +/** + * 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.ec2.services; + +import java.util.concurrent.TimeUnit; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.ec2.domain.KeyPair; +import org.jclouds.ec2.services.KeyPairClient; +import org.jclouds.javax.annotation.Nullable; + +/** + * + * @author Adrian Cole + */ +@Timeout(duration = 90, timeUnit = TimeUnit.SECONDS) +public interface NovaEC2KeyPairClient extends KeyPairClient { + + /** + * Imports the public key from an RSA key pair that you created with a third-party tool. Compare + * this with CreateKeyPair, in which AWS creates the key pair and gives the keys to you (Nova + * keeps a copy of the public key). With ImportKeyPair, you create the key pair and give Nova just + * the public key. The private key is never transferred between you and Nova. + * + *

+ * You can easily create an RSA key pair on Windows and Linux using the ssh-keygen command line + * tool (provided with the standard OpenSSH installation). Standard library support for RSA key + * pair creation is also available in Java, Ruby, Python, and many other programming languages. + * + *

+ *

Supported Formats

+ *
    + *
  • OpenSSH public key format (e.g., the format in ~/.ssh/authorized_keys)
  • + *
  • Base64 encoded DER format
  • + *
  • SSH public key file format as specified in RFC4716
  • + *
+ * DSA keys are not supported. Make sure your key generator is set up to create RSA keys. + *

+ * Supported lengths: 1024, 2048, and 4096. + *

+ * + * @param region + * region to import the key into + * @param keyName + * A unique name for the key pair. Accepts alphanumeric characters, spaces, dashes, and + * underscores. + * @param publicKeyMaterial + * The public key + * @return imported key including fingerprint + */ + KeyPair importKeyPairInRegion(@Nullable String region, String keyName, String publicKeyMaterial); + +} From 136e1eeff3ff92a0ba1dfaeac3de1e1e402762e3 Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Thu, 3 May 2012 12:38:57 +0100 Subject: [PATCH 020/148] openstack-nova-ec2: adding NovaEC2KeyPairClient with importKeyPair expect and live tests --- .../BaseNovaEC2RestClientExpectTest.java | 4 +- .../NovaEC2KeyPairClientExpectTest.java | 35 +++++ .../NovaEC2KeyPairClientLiveTest.java | 136 ++++++++++++++++++ .../nova_ec2_import_keypair_response.xml | 6 + 4 files changed, 179 insertions(+), 2 deletions(-) create mode 100644 apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/services/NovaEC2KeyPairClientExpectTest.java create mode 100644 apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/services/NovaEC2KeyPairClientLiveTest.java create mode 100644 apis/openstack-nova-ec2/src/test/resources/nova_ec2_import_keypair_response.xml diff --git a/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/internal/BaseNovaEC2RestClientExpectTest.java b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/internal/BaseNovaEC2RestClientExpectTest.java index 5f34f76241..5947fd1388 100644 --- a/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/internal/BaseNovaEC2RestClientExpectTest.java +++ b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/internal/BaseNovaEC2RestClientExpectTest.java @@ -8,9 +8,9 @@ import javax.ws.rs.core.MediaType; import org.jclouds.Constants; import org.jclouds.date.DateService; import org.jclouds.date.internal.SimpleDateFormatDateService; -import org.jclouds.ec2.EC2Client; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; +import org.jclouds.openstack.nova.ec2.NovaEC2Client; import org.jclouds.openstack.nova.ec2.config.NovaEC2RestClientModule; import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.internal.BaseRestClientExpectTest; @@ -19,7 +19,7 @@ import com.google.common.collect.ImmutableMultimap; import com.google.inject.Module; import com.google.inject.Provides; -public abstract class BaseNovaEC2RestClientExpectTest extends BaseRestClientExpectTest { +public abstract class BaseNovaEC2RestClientExpectTest extends BaseRestClientExpectTest { protected static final String CONSTANT_DATE = "2012-04-16T15:54:08.897Z"; protected DateService dateService = new SimpleDateFormatDateService(); protected URI endpoint = URI.create("http://localhost:8773/services/Cloud/"); diff --git a/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/services/NovaEC2KeyPairClientExpectTest.java b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/services/NovaEC2KeyPairClientExpectTest.java new file mode 100644 index 0000000000..9f44d213a0 --- /dev/null +++ b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/services/NovaEC2KeyPairClientExpectTest.java @@ -0,0 +1,35 @@ +package org.jclouds.openstack.nova.ec2.services; + +import static org.testng.Assert.assertEquals; + +import java.net.URI; + +import org.jclouds.ec2.domain.KeyPair; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.openstack.nova.ec2.internal.BaseNovaEC2RestClientExpectTest; + +import com.google.common.collect.ImmutableMultimap; + +/** + * @author Adam Lowe + */ +public class NovaEC2KeyPairClientExpectTest extends BaseNovaEC2RestClientExpectTest { + + public void testImportKeyPair() { + NovaEC2KeyPairClient client = requestsSendResponses( + describeAvailabilityZonesRequest, + describeAvailabilityZonesResponse, + HttpRequest.builder().method("POST") + .endpoint(URI.create("http://localhost:8773/services/Cloud/")) + .headers(ImmutableMultimap.of("Host", "localhost:8773")) + .payload(payloadFromStringWithContentType("Action=ImportKeyPair&KeyName=mykey&PublicKeyMaterial=c3NoLXJzYSBBQQ%3D%3D&Signature=wOOKOlDfJezRkx7NKcyOyaBQuY7PoVE3HFa9495RL7s%3D&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2012-04-16T15%3A54%3A08.897Z&Version=2009-04-04&AWSAccessKeyId=identity", "application/x-www-form-urlencoded")).build(), + HttpResponse.builder().statusCode(200).payload(payloadFromResource("/nova_ec2_import_keypair_response.xml")).build() + ).getKeyPairServices(); + + KeyPair result = client.importKeyPairInRegion(null, "mykey", "ssh-rsa AA"); + assertEquals(result.getKeyName(), "aplowe-nova-ec22"); + assertEquals(result.getSha1OfPrivateKey(), "e3:fd:de:f6:4c:36:7d:9b:8f:2f:4c:20:f8:ae:b0:ea"); + } + +} diff --git a/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/services/NovaEC2KeyPairClientLiveTest.java b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/services/NovaEC2KeyPairClientLiveTest.java new file mode 100644 index 0000000000..58630dbfbc --- /dev/null +++ b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/services/NovaEC2KeyPairClientLiveTest.java @@ -0,0 +1,136 @@ +/** + * 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.ec2.services; + +import static com.google.common.collect.Sets.newTreeSet; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.fail; + +import java.io.IOException; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; + +import org.jclouds.compute.ComputeTestUtils; +import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest; +import org.jclouds.crypto.SshKeys; +import org.jclouds.ec2.domain.KeyPair; +import org.jclouds.openstack.nova.ec2.NovaEC2ApiMetadata; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * Tests behavior of {@code NovaKeyPairClient} + * + * @author Adam Lowe + */ +@Test(groups = "live", singleThreaded = true) +public class NovaEC2KeyPairClientLiveTest extends BaseComputeServiceContextLiveTest { + + public static final String PREFIX = System.getProperty("user.name") + "-nova-ec2"; + + public NovaEC2KeyPairClientLiveTest() { + provider = "openstack-nova-ec2"; + } + + private NovaEC2KeyPairClient client; + private Set regions; + + @Override + @BeforeClass(groups = { "integration", "live" }) + public void setupContext() { + super.setupContext(); + client = view.unwrap(NovaEC2ApiMetadata.CONTEXT_TOKEN).getApi().getKeyPairServices(); + regions = view.unwrap(NovaEC2ApiMetadata.CONTEXT_TOKEN).getApi().getAvailabilityZoneAndRegionServices().describeRegions().keySet(); + } + + @Test + void testDescribeKeyPairs() { + for (String region : regions) { + SortedSet allResults = newTreeSet(client.describeKeyPairsInRegion(region)); + assertNotNull(allResults); + if (allResults.size() >= 1) { + KeyPair pair = allResults.last(); + SortedSet result = newTreeSet(client.describeKeyPairsInRegion(region, pair.getKeyName())); + assertNotNull(result); + KeyPair compare = result.last(); + assertEquals(compare, pair); + } + } + } + + @Test + void testCreateKeyPair() { + String keyName = PREFIX + "1"; + cleanupKeyPair(keyName); + try { + KeyPair keyPair = client.createKeyPairInRegion(null, keyName); + checkKeyPair(keyName, keyPair); + assertNotNull(keyPair.getKeyMaterial()); + } finally { + cleanupKeyPair(keyName); + } + } + + protected void cleanupKeyPair(String keyName) { + try { + client.deleteKeyPairInRegion(null, keyName); + } catch (Exception e) { + + } + client.deleteKeyPairInRegion(null, keyName); + } + + @Test + void testImportKeyPair() throws IOException { + String keyName = PREFIX + "2"; + cleanupKeyPair(keyName); + Map myKey = ComputeTestUtils.setupKeyPair(); + try { + KeyPair keyPair = client.importKeyPairInRegion(null, keyName, myKey.get("public")); + checkKeyPair(keyName, keyPair); + + // check the fingerprint of public key (in the sha10OfPrivateKey field) + assertEquals(keyPair.getSha1OfPrivateKey(), SshKeys.fingerprintPublicKey(myKey.get("public"))); + + // try again to see if there's an error + try { + client.importKeyPairInRegion(null, keyName, myKey.get("public")); + fail("Duplicate call importKeyPairInRegion should have failed!"); + } catch (IllegalStateException e) { + } + } finally { + cleanupKeyPair(keyName); + } + } + + protected void checkKeyPair(String keyName, KeyPair keyPair) { + assertNotNull(keyPair); + assertNotNull(keyPair.getSha1OfPrivateKey()); + assertEquals(keyPair.getKeyName(), keyName); + + Set twoResults = client.describeKeyPairsInRegion(null, keyName); + assertNotNull(twoResults); + assertEquals(twoResults.size(), 1); + KeyPair listPair = twoResults.iterator().next(); + assertEquals(listPair.getKeyName(), keyPair.getKeyName()); + assertEquals(listPair.getSha1OfPrivateKey(), keyPair.getSha1OfPrivateKey()); + } +} diff --git a/apis/openstack-nova-ec2/src/test/resources/nova_ec2_import_keypair_response.xml b/apis/openstack-nova-ec2/src/test/resources/nova_ec2_import_keypair_response.xml new file mode 100644 index 0000000000..24707220ec --- /dev/null +++ b/apis/openstack-nova-ec2/src/test/resources/nova_ec2_import_keypair_response.xml @@ -0,0 +1,6 @@ + + + req-dd54edc5-4beb-4b40-9694-0209fcf50459 + aplowe-nova-ec22 + e3:fd:de:f6:4c:36:7d:9b:8f:2f:4c:20:f8:ae:b0:ea + From 9ca544306ef3801c15bde0e22dbf92c138ada7ce Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Thu, 3 May 2012 12:49:09 +0100 Subject: [PATCH 021/148] openstack-nova-ec2: adding NovaEC2KeyPairClient with importKeyPair improving expect tests --- .../NovaEC2KeyPairClientExpectTest.java | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/services/NovaEC2KeyPairClientExpectTest.java b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/services/NovaEC2KeyPairClientExpectTest.java index 9f44d213a0..43df5d0de4 100644 --- a/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/services/NovaEC2KeyPairClientExpectTest.java +++ b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/services/NovaEC2KeyPairClientExpectTest.java @@ -1,3 +1,21 @@ +/** + * 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.ec2.services; import static org.testng.Assert.assertEquals; @@ -8,6 +26,8 @@ import org.jclouds.ec2.domain.KeyPair; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; import org.jclouds.openstack.nova.ec2.internal.BaseNovaEC2RestClientExpectTest; +import org.jclouds.rest.ResourceNotFoundException; +import org.testng.annotations.Test; import com.google.common.collect.ImmutableMultimap; @@ -26,10 +46,39 @@ public class NovaEC2KeyPairClientExpectTest extends BaseNovaEC2RestClientExpectT .payload(payloadFromStringWithContentType("Action=ImportKeyPair&KeyName=mykey&PublicKeyMaterial=c3NoLXJzYSBBQQ%3D%3D&Signature=wOOKOlDfJezRkx7NKcyOyaBQuY7PoVE3HFa9495RL7s%3D&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2012-04-16T15%3A54%3A08.897Z&Version=2009-04-04&AWSAccessKeyId=identity", "application/x-www-form-urlencoded")).build(), HttpResponse.builder().statusCode(200).payload(payloadFromResource("/nova_ec2_import_keypair_response.xml")).build() ).getKeyPairServices(); - + KeyPair result = client.importKeyPairInRegion(null, "mykey", "ssh-rsa AA"); assertEquals(result.getKeyName(), "aplowe-nova-ec22"); assertEquals(result.getSha1OfPrivateKey(), "e3:fd:de:f6:4c:36:7d:9b:8f:2f:4c:20:f8:ae:b0:ea"); } -} + @Test(expectedExceptions = ResourceNotFoundException.class) + public void testImportKeyPairFailsNotFound() { + NovaEC2KeyPairClient client = requestsSendResponses( + describeAvailabilityZonesRequest, + describeAvailabilityZonesResponse, + HttpRequest.builder().method("POST") + .endpoint(URI.create("http://localhost:8773/services/Cloud/")) + .headers(ImmutableMultimap.of("Host", "localhost:8773")) + .payload(payloadFromStringWithContentType("Action=ImportKeyPair&KeyName=mykey&PublicKeyMaterial=c3NoLXJzYSBBQQ%3D%3D&Signature=wOOKOlDfJezRkx7NKcyOyaBQuY7PoVE3HFa9495RL7s%3D&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2012-04-16T15%3A54%3A08.897Z&Version=2009-04-04&AWSAccessKeyId=identity", "application/x-www-form-urlencoded")).build(), + HttpResponse.builder().statusCode(404).build() + ).getKeyPairServices(); + + client.importKeyPairInRegion(null, "mykey", "ssh-rsa AA"); + } + + @Test(expectedExceptions = IllegalStateException.class) + public void testImportKeyPairFailsAlreadyExists() { + NovaEC2KeyPairClient client = requestsSendResponses( + describeAvailabilityZonesRequest, + describeAvailabilityZonesResponse, + HttpRequest.builder().method("POST") + .endpoint(URI.create("http://localhost:8773/services/Cloud/")) + .headers(ImmutableMultimap.of("Host", "localhost:8773")) + .payload(payloadFromStringWithContentType("Action=ImportKeyPair&KeyName=mykey&PublicKeyMaterial=c3NoLXJzYSBBQQ%3D%3D&Signature=wOOKOlDfJezRkx7NKcyOyaBQuY7PoVE3HFa9495RL7s%3D&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2012-04-16T15%3A54%3A08.897Z&Version=2009-04-04&AWSAccessKeyId=identity", "application/x-www-form-urlencoded")).build(), + HttpResponse.builder().statusCode(409).build() + ).getKeyPairServices(); + + client.importKeyPairInRegion(null, "mykey", "ssh-rsa AA"); + } +} \ No newline at end of file From 0903d8522ad7d5dcb3da6569cb127e9ce38731da Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Thu, 3 May 2012 06:43:01 -0700 Subject: [PATCH 022/148] Close streams in filesystem tests Windows cannot delete open files and this may have caused test failures. References #912. --- .../FilesystemAsyncBlobStoreTest.java | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemAsyncBlobStoreTest.java b/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemAsyncBlobStoreTest.java index 0045df0be7..ce936344eb 100644 --- a/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemAsyncBlobStoreTest.java +++ b/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemAsyncBlobStoreTest.java @@ -64,6 +64,7 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import com.google.common.io.Closeables; import com.google.inject.CreationException; /** @@ -730,23 +731,39 @@ public class FilesystemAsyncBlobStoreTest { */ final String containerName = "containerWithRanges"; String payload = "abcdefgh"; + InputStream is; Blob blob = blobStore.blobBuilder("test").payload(new StringPayload(payload)).build(); blobStore.putBlob(containerName, blob); GetOptions getOptionsRangeStartAt = new GetOptions(); getOptionsRangeStartAt.startAt(1); Blob blobRangeStartAt = blobStore.getBlob(containerName, blob.getMetadata().getName(), getOptionsRangeStartAt); - Assert.assertEquals("bcdefgh", IOUtils.toString(blobRangeStartAt.getPayload().getInput())); + is = blobRangeStartAt.getPayload().getInput(); + try { + Assert.assertEquals("bcdefgh", IOUtils.toString(is)); + } finally { + Closeables.closeQuietly(is); + } GetOptions getOptionsRangeTail = new GetOptions(); getOptionsRangeTail.tail(3); Blob blobRangeTail = blobStore.getBlob(containerName, blob.getMetadata().getName(), getOptionsRangeTail); - Assert.assertEquals("fgh", IOUtils.toString(blobRangeTail.getPayload().getInput())); + is = blobRangeTail.getPayload().getInput(); + try { + Assert.assertEquals("fgh", IOUtils.toString(is)); + } finally { + Closeables.closeQuietly(is); + } GetOptions getOptionsFragment = new GetOptions(); getOptionsFragment.range(4, 6); Blob blobFragment = blobStore.getBlob(containerName, blob.getMetadata().getName(), getOptionsFragment); - Assert.assertEquals("efg", IOUtils.toString(blobFragment.getPayload().getInput())); + is = blobFragment.getPayload().getInput(); + try { + Assert.assertEquals("efg", IOUtils.toString(is)); + } finally { + Closeables.closeQuietly(is); + } } /** Test that BlobRequestSigner creates expected URIs. */ From b0cd47befbb0c67558b74bb876e42302d12962f5 Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Thu, 3 May 2012 15:38:21 -0700 Subject: [PATCH 023/148] Prefer ImmutableMap for static ErrorCode map Also mark member as final and fix line wrapping. --- .../jclouds/virtualbox/domain/ErrorCode.java | 41 +++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/ErrorCode.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/ErrorCode.java index d8ba972968..f629051933 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/ErrorCode.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/ErrorCode.java @@ -19,12 +19,13 @@ package org.jclouds.virtualbox.domain; -import java.util.HashMap; import java.util.Map; import org.virtualbox_4_1.VBoxException; import org.virtualbox_4_1.jaxws.RuntimeFaultMsg; +import com.google.common.collect.ImmutableMap; + /** * Maps the error codes in the Virtual Box Java API into enum values. *

@@ -47,25 +48,41 @@ import org.virtualbox_4_1.jaxws.RuntimeFaultMsg; */ public enum ErrorCode { - VBOX_E_OBJECT_NOT_FOUND(2159738881L), VBOX_E_INVALID_VM_STATE(2159738882L), VBOX_E_VM_ERROR(2159738883L), VBOX_E_FILE_ERROR( - 2159738884L), VBOX_E_IPRT_ERROR(2159738885L), VBOX_E_PDM_ERROR(2159738886L), VBOX_E_INVALID_OBJECT_STATE( - 2159738887L), VBOX_E_HOST_ERROR(2159738888L), VBOX_E_NOT_SUPPORTED(2159738889L), VBOX_E_XML_ERROR(2159738890L), VBOX_E_INVALID_SESSION_STATE( - 2159738891L), VBOX_E_OBJECT_IN_USE(2159738892L), VBOX_E_ACCESSDENIED(2147942405L), VBOX_E_POINTER(2147500035L), VBOX_E_FAIL( - 2147500037L), VBOX_E_NOTIMPL(2147500033L), VBOX_E_OUTOFMEMORY(2147942414L), VBOX_E_INVALIDARG(2147942487L), VBOX_E_UNEXPECTED( - 2147549183L), VBOX_E_UNKNOWN_ERROR_CODE(-1L), VBOX_E_ERROR_CODE_UNAVAILABLE(-2L); + VBOX_E_OBJECT_NOT_FOUND(2159738881L), + VBOX_E_INVALID_VM_STATE(2159738882L), + VBOX_E_VM_ERROR(2159738883L), + VBOX_E_FILE_ERROR(2159738884L), + VBOX_E_IPRT_ERROR(2159738885L), + VBOX_E_PDM_ERROR(2159738886L), + VBOX_E_INVALID_OBJECT_STATE(2159738887L), + VBOX_E_HOST_ERROR(2159738888L), + VBOX_E_NOT_SUPPORTED(2159738889L), + VBOX_E_XML_ERROR(2159738890L), + VBOX_E_INVALID_SESSION_STATE(2159738891L), + VBOX_E_OBJECT_IN_USE(2159738892L), + VBOX_E_ACCESSDENIED(2147942405L), + VBOX_E_POINTER(2147500035L), + VBOX_E_FAIL(2147500037L), + VBOX_E_NOTIMPL(2147500033L), + VBOX_E_OUTOFMEMORY(2147942414L), + VBOX_E_INVALIDARG(2147942487L), + VBOX_E_UNEXPECTED(2147549183L), + VBOX_E_UNKNOWN_ERROR_CODE(-1L), + VBOX_E_ERROR_CODE_UNAVAILABLE(-2L); - private long code; + private final long code; ErrorCode(long code) { this.code = code; } - private static Map table = new HashMap(); - + private final static Map TABLE; static { + ImmutableMap.Builder builder = ImmutableMap.builder(); for (ErrorCode errorCode : ErrorCode.values()) { - table.put(errorCode.code, errorCode); + builder.put(errorCode.code, errorCode); } + TABLE = builder.build(); } /** @@ -80,7 +97,7 @@ public enum ErrorCode { if (backend instanceof RuntimeFaultMsg) { final RuntimeFaultMsg faultCode = (RuntimeFaultMsg) backend; final int resultCode = faultCode.getFaultInfo().getResultCode(); - final ErrorCode errorCode = table.get(unsignedIntToLong(resultCode)); + final ErrorCode errorCode = TABLE.get(unsignedIntToLong(resultCode)); if (errorCode != null) { return errorCode; } From b9188872f2260122833096bf279540bb883f6670 Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Fri, 4 May 2012 03:41:48 -0700 Subject: [PATCH 024/148] Made the application port configurable for local testing --- demos/tweetstore/heroku-tweetstore/pom.xml | 3 ++- .../demo/paas/config/PlatformServicesInitializer.java | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/demos/tweetstore/heroku-tweetstore/pom.xml b/demos/tweetstore/heroku-tweetstore/pom.xml index 1ec0bdc4a8..a99264e73e 100644 --- a/demos/tweetstore/heroku-tweetstore/pom.xml +++ b/demos/tweetstore/heroku-tweetstore/pom.xml @@ -126,7 +126,8 @@ ${project.build.directory}/${project.artifactId} - ${test.jetty.port} + ${test.jetty.address} + ${test.jetty.port} diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/config/PlatformServicesInitializer.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/config/PlatformServicesInitializer.java index 364a07f3ec..5b546c2593 100644 --- a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/config/PlatformServicesInitializer.java +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/config/PlatformServicesInitializer.java @@ -43,7 +43,8 @@ import com.google.inject.Guice; public class PlatformServicesInitializer implements ServletContextListener { public static final String PLATFORM_SERVICES_ATTRIBUTE_NAME = PlatformServices.class.getName(); - protected static final String PORT_VARIABLE = "PORT"; + protected static final String HOST_VARIABLE = "PUBLIC_HOST"; + protected static final String PORT_VARIABLE = "PUBLIC_PORT"; @Override public void contextInitialized(ServletContextEvent contextEvent) { @@ -66,8 +67,9 @@ public class PlatformServicesInitializer implements ServletContextListener { } protected static String getBaseUrl(ServletContext context) { - return format("http://localhost:%s%s", checkNotNull(System.getenv(PORT_VARIABLE), PORT_VARIABLE), - context.getContextPath()); + // use the public URL while https://support.heroku.com/requests/51088 is open + return format("http://%s:%s/%s", checkNotNull(System.getenv(HOST_VARIABLE), HOST_VARIABLE), + checkNotNull(System.getenv(PORT_VARIABLE), PORT_VARIABLE), context.getContextPath()); } // TODO: make the number and names of queues configurable From 6f75c85313939033da89713df1cbbd07351af651 Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Fri, 4 May 2012 03:42:49 -0700 Subject: [PATCH 025/148] Added a 'clear tweets' option to heroku-tweetstore. Using unique container names in unit tests. --- .../tweetstore/config/GuiceServletConfig.java | 2 + .../controller/ClearTweetsController.java | 96 +++++++++++++++++++ .../controller/AddTweetsControllerTest.java | 2 +- .../controller/ClearTweetsControllerTest.java | 72 ++++++++++++++ .../controller/StoreTweetsControllerTest.java | 11 ++- .../functions/KeyToStoredTweetStatusTest.java | 9 +- .../ServiceToStoredTweetStatusesTest.java | 2 +- 7 files changed, 183 insertions(+), 11 deletions(-) create mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/controller/ClearTweetsController.java create mode 100644 demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/ClearTweetsControllerTest.java diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/config/GuiceServletConfig.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/config/GuiceServletConfig.java index 24d0c94c06..c03434388a 100644 --- a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/config/GuiceServletConfig.java +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/config/GuiceServletConfig.java @@ -45,6 +45,7 @@ import org.jclouds.demo.paas.service.taskqueue.TaskQueue; import org.jclouds.demo.tweetstore.config.util.CredentialsCollector; import org.jclouds.demo.tweetstore.config.util.PropertiesLoader; import org.jclouds.demo.tweetstore.controller.AddTweetsController; +import org.jclouds.demo.tweetstore.controller.ClearTweetsController; import org.jclouds.demo.tweetstore.controller.EnqueueStoresController; import org.jclouds.demo.tweetstore.controller.StoreTweetsController; @@ -138,6 +139,7 @@ public class GuiceServletConfig extends GuiceServletContextListener { serve("/store/*").with(StoreTweetsController.class); serve("/tweets/*").with(AddTweetsController.class); serve("/stores/*").with(EnqueueStoresController.class); + serve("/clear/*").with(ClearTweetsController.class); } }); } diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/controller/ClearTweetsController.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/controller/ClearTweetsController.java new file mode 100644 index 0000000000..e08cfbc19e --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/controller/ClearTweetsController.java @@ -0,0 +1,96 @@ +/** + * 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.demo.tweetstore.controller; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Strings.nullToEmpty; + +import java.io.IOException; +import java.util.Map; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.MediaType; + +import org.jclouds.blobstore.BlobStoreContext; +import org.jclouds.demo.tweetstore.reference.TweetStoreConstants; +import org.jclouds.logging.Logger; + +import com.google.common.annotations.VisibleForTesting; + +/** + * Grab tweets related to me and store them into blobstores + * + * @author Adrian Cole + */ +@Singleton +public class ClearTweetsController extends HttpServlet { + /** The serialVersionUID */ + private static final long serialVersionUID = 7215420527854203714L; + + private final Map contexts; + private final String container; + + @Resource + protected Logger logger = Logger.NULL; + + @Inject + @VisibleForTesting + public ClearTweetsController(Map contexts, + @Named(TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER) String container) { + this.container = container; + this.contexts = contexts; + } + + @VisibleForTesting + public void clearContainer(String contextName) { + BlobStoreContext context = checkNotNull(contexts.get(contextName), + "no context for %s in %s", contextName, contexts.keySet()); + try { + context.getBlobStore().clearContainer(container); + } catch (Exception e) { + logger.error(e, "Error clearing tweets in %s/%s", container, context); + } + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + if (nullToEmpty(request.getHeader("X-Originator")).equals("admin")) { + try { + String contextName = checkNotNull(request.getHeader("context"), "missing header context"); + logger.info("clearing tweets in %s/%s", container, contextName); + clearContainer(contextName); + logger.debug("done clearing tweets"); + response.setContentType(MediaType.TEXT_PLAIN); + response.getWriter().println("Done!"); + } catch (Exception e) { + logger.error(e, "Error clearing tweets"); + throw new ServletException(e); + } + } else { + response.sendError(401); + } + } +} \ No newline at end of file diff --git a/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/AddTweetsControllerTest.java b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/AddTweetsControllerTest.java index 1c93403650..ccdb667ef3 100644 --- a/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/AddTweetsControllerTest.java +++ b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/AddTweetsControllerTest.java @@ -63,7 +63,7 @@ public class AddTweetsControllerTest { } public void testStoreTweets() throws IOException, InterruptedException, ExecutionException { - String container = "container"; + String container = AddTweetsControllerTest.class.getName() + "#container"; Map contexts = createServices(container); ServiceToStoredTweetStatuses function = new ServiceToStoredTweetStatuses(contexts, container); diff --git a/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/ClearTweetsControllerTest.java b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/ClearTweetsControllerTest.java new file mode 100644 index 0000000000..81f0b9f3c7 --- /dev/null +++ b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/ClearTweetsControllerTest.java @@ -0,0 +1,72 @@ +/** + * 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.demo.tweetstore.controller; + +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import org.jclouds.ContextBuilder; +import org.jclouds.blobstore.BlobStoreContext; +import org.jclouds.blobstore.TransientApiMetadata; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.demo.tweetstore.reference.TweetStoreConstants; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; + +/** + * Tests behavior of {@code AddTweetsController} + * + * @author Adrian Cole + */ +@Test(groups = "unit") +public class ClearTweetsControllerTest { + + Map createBlobStores(String container) throws InterruptedException, ExecutionException { + TransientApiMetadata transientApiMetadata = TransientApiMetadata.builder().build(); + Map contexts = ImmutableMap.of( + "test1", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class), + "test2", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class)); + for (BlobStoreContext blobstore : contexts.values()) { + blobstore.getBlobStore().createContainerInLocation(null, container); + Blob blob = blobstore.getAsyncBlobStore().blobBuilder("1").build(); + blob.getMetadata().getUserMetadata().put(TweetStoreConstants.SENDER_NAME, "frank"); + blob.setPayload("I love beans!"); + blobstore.getBlobStore().putBlob(container, blob); + } + return contexts; + } + + public void testClearTweets() throws IOException, InterruptedException, ExecutionException { + String container = ClearTweetsControllerTest.class.getName() + "#container"; + Map contexts = createBlobStores(container); + + ClearTweetsController controller = new ClearTweetsController(contexts, + container); + controller.clearContainer("test1"); + controller.clearContainer("test2"); + + for (BlobStoreContext context : contexts.values()) { + assertEquals(context.getBlobStore().countBlobs(container), 0, context.toString()); + } + } +} diff --git a/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/StoreTweetsControllerTest.java b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/StoreTweetsControllerTest.java index 9cc56351f2..2d26630f7e 100644 --- a/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/StoreTweetsControllerTest.java +++ b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/controller/StoreTweetsControllerTest.java @@ -57,20 +57,21 @@ public class StoreTweetsControllerTest { return createMock(Twitter.class); } - Map createBlobStores() throws InterruptedException, ExecutionException { + Map createBlobStores(String container) throws InterruptedException, ExecutionException { TransientApiMetadata transientApiMetadata = TransientApiMetadata.builder().build(); Map contexts = ImmutableMap.of( "test1", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class), "test2", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class)); for (BlobStoreContext blobstore : contexts.values()) { - blobstore.getAsyncBlobStore().createContainerInLocation(null, "favo").get(); + blobstore.getAsyncBlobStore().createContainerInLocation(null, container).get(); } return contexts; } public void testStoreTweets() throws IOException, InterruptedException, ExecutionException { - Map stores = createBlobStores(); - StoreTweetsController function = new StoreTweetsController(stores, "favo", createTwitter()); + String container = StoreTweetsControllerTest.class.getName() + "#container"; + Map stores = createBlobStores(container); + StoreTweetsController function = new StoreTweetsController(stores, container, createTwitter()); User frank = createMock(User.class); expect(frank.getScreenName()).andReturn("frank").atLeastOnce(); @@ -102,7 +103,7 @@ public class StoreTweetsControllerTest { verify(jimmyStatus); for (Entry entry : stores.entrySet()) { - BlobMap map = entry.getValue().createBlobMap("favo"); + BlobMap map = entry.getValue().createBlobMap(container); Blob frankBlob = map.get("1"); assertEquals(frankBlob.getMetadata().getName(), "1"); assertEquals(frankBlob.getMetadata().getUserMetadata().get(TweetStoreConstants.SENDER_NAME), "frank"); diff --git a/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/functions/KeyToStoredTweetStatusTest.java b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/functions/KeyToStoredTweetStatusTest.java index aab06ec0b6..36981b398a 100644 --- a/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/functions/KeyToStoredTweetStatusTest.java +++ b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/functions/KeyToStoredTweetStatusTest.java @@ -41,10 +41,11 @@ import org.testng.annotations.Test; public class KeyToStoredTweetStatusTest { BlobMap createMap() throws InterruptedException, ExecutionException { - BlobStoreContext context = - ContextBuilder.newBuilder(TransientApiMetadata.builder().build()).build(BlobStoreContext.class); - context.getBlobStore().createContainerInLocation(null, "test1"); - return context.createBlobMap("test1"); + BlobStoreContext context = + ContextBuilder.newBuilder(TransientApiMetadata.builder().build()).build(BlobStoreContext.class); + String container = KeyToStoredTweetStatusTest.class.getName() + "#container"; + context.getBlobStore().createContainerInLocation(null, container); + return context.createBlobMap(container); } public void testStoreTweets() throws IOException, InterruptedException, ExecutionException { diff --git a/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/functions/ServiceToStoredTweetStatusesTest.java b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/functions/ServiceToStoredTweetStatusesTest.java index 5fec52711e..70fde3c119 100644 --- a/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/functions/ServiceToStoredTweetStatusesTest.java +++ b/demos/tweetstore/heroku-tweetstore/src/test/java/org/jclouds/demo/tweetstore/functions/ServiceToStoredTweetStatusesTest.java @@ -60,7 +60,7 @@ public class ServiceToStoredTweetStatusesTest { } public void testStoreTweets() throws IOException, InterruptedException, ExecutionException { - String container = "container"; + String container = ServiceToStoredTweetStatusesTest.class.getName() + "#container"; Map contexts = createServices(container); ServiceToStoredTweetStatuses function = new ServiceToStoredTweetStatuses(contexts, container); From e5b45beb4c66df16d137660cdea559c26543270d Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Thu, 3 May 2012 14:18:42 +0100 Subject: [PATCH 026/148] openstack-nova: ensuring we use the smallest available flavor when creating servers in live tests (else devstack fails, badly) --- .../v1_1/internal/BaseNovaClientLiveTest.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaClientLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaClientLiveTest.java index 2a37f52f7e..99151fefaa 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaClientLiveTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaClientLiveTest.java @@ -18,13 +18,18 @@ */ package org.jclouds.openstack.nova.v1_1.internal; +import static org.jclouds.compute.util.ComputeServiceUtils.getCores; +import static org.jclouds.compute.util.ComputeServiceUtils.getSpace; + import java.util.Properties; +import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest; import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties; 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.Server; import org.jclouds.openstack.nova.v1_1.domain.Server.Status; import org.jclouds.openstack.nova.v1_1.features.FlavorClient; @@ -36,7 +41,9 @@ import org.testng.annotations.BeforeGroups; import org.testng.annotations.Test; import com.google.common.base.Throwables; +import com.google.common.collect.ComparisonChain; import com.google.common.collect.Iterables; +import com.google.common.collect.Ordering; /** * Tests behavior of {@code NovaClient} @@ -100,7 +107,13 @@ public class BaseNovaClientLiveTest extends BaseComputeServiceContextLiveTest { protected String flavorRefForZone(String zoneId) { FlavorClient flavorClient = novaContext.getApi().getFlavorClientForZone(zoneId); - return Iterables.getLast(flavorClient.listFlavors()).getId(); + return DEFAULT_FLAVOR_ORDERING.min(flavorClient.listFlavorsInDetail()).getId(); } + static final Ordering DEFAULT_FLAVOR_ORDERING = new Ordering() { + public int compare(Flavor left, Flavor right) { + return ComparisonChain.start().compare(left.getVcpus(), right.getVcpus()).compare(left.getRam(), right.getRam()) + .compare(left.getDisk(), right.getDisk()).result(); + } + }; } From 42df3d339c40c7d13e004610bd43a4b45960d8c6 Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Thu, 3 May 2012 14:24:47 +0100 Subject: [PATCH 027/148] openstack-nova: Adding Virtual Interface extension --- .../openstack/nova/v1_1/NovaAsyncClient.java | 8 ++ .../openstack/nova/v1_1/NovaClient.java | 8 ++ .../v1_1/config/NovaRestClientModule.java | 1 + .../nova/v1_1/domain/VirtualInterface.java | 129 ++++++++++++++++++ .../VirtualInterfaceAsyncClient.java | 60 ++++++++ .../extensions/VirtualInterfaceClient.java | 46 +++++++ ...paceEqualsAnyNamespaceInExtensionsSet.java | 2 + .../VirtualInterfaceClientExpectTest.java | 65 +++++++++ .../VirtualInterfaceClientLiveTest.java | 70 ++++++++++ .../resources/virtual_interfaces_list.json | 1 + 10 files changed, 390 insertions(+) create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/VirtualInterface.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VirtualInterfaceAsyncClient.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VirtualInterfaceClient.java create mode 100644 apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VirtualInterfaceClientExpectTest.java create mode 100644 apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VirtualInterfaceClientLiveTest.java create mode 100644 apis/openstack-nova/src/test/resources/virtual_interfaces_list.json diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java index 9f5d32b035..d00d5cce54 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java @@ -28,6 +28,7 @@ import org.jclouds.openstack.nova.v1_1.extensions.HostAdministrationAsyncClient; import org.jclouds.openstack.nova.v1_1.extensions.KeyPairAsyncClient; import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupAsyncClient; import org.jclouds.openstack.nova.v1_1.extensions.SimpleTenantUsageAsyncClient; +import org.jclouds.openstack.nova.v1_1.extensions.VirtualInterfaceAsyncClient; import org.jclouds.openstack.nova.v1_1.extensions.VolumeAsyncClient; import org.jclouds.openstack.nova.v1_1.features.ExtensionAsyncClient; import org.jclouds.openstack.nova.v1_1.features.FlavorAsyncClient; @@ -127,4 +128,11 @@ public interface NovaAsyncClient { @Delegate Optional getVolumeExtensionForZone( @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + + /** + * Provides asynchronous access to Virtual Interface features. + */ + @Delegate + Optional getVirtualInterfaceExtensionForZone( + @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java index 37dc81e228..866351c539 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java @@ -30,6 +30,7 @@ import org.jclouds.openstack.nova.v1_1.extensions.HostAdministrationClient; import org.jclouds.openstack.nova.v1_1.extensions.KeyPairClient; import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupClient; import org.jclouds.openstack.nova.v1_1.extensions.SimpleTenantUsageClient; +import org.jclouds.openstack.nova.v1_1.extensions.VirtualInterfaceClient; import org.jclouds.openstack.nova.v1_1.extensions.VolumeClient; import org.jclouds.openstack.nova.v1_1.features.ExtensionClient; import org.jclouds.openstack.nova.v1_1.features.FlavorClient; @@ -131,4 +132,11 @@ public interface NovaClient { Optional getVolumeExtensionForZone( @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + /** + * Provides synchronous access to Virtual Interface features. + */ + @Delegate + Optional getVirtualInterfaceExtensionForZone( + @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java index 8a9a6e20bc..f2956abe61 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java @@ -74,6 +74,7 @@ public class NovaRestClientModule extends RestClientModule builder() { + return new ConcreteBuilder(); + } + + public Builder toBuilder() { + return new ConcreteBuilder().fromVirtualInterface(this); + } + + public static abstract class Builder> { + protected abstract T self(); + + private String id; + private String macAddress; + + /** + * @see VirtualInterface#getId() + */ + public T id(String id) { + this.id = id; + return self(); + } + + /** + * @see VirtualInterface#getMacAddress() + */ + public T macAddress(String macAddress) { + this.macAddress = macAddress; + return self(); + } + + public VirtualInterface build() { + return new VirtualInterface(this); + } + + public T fromVirtualInterface(VirtualInterface in) { + return this + .id(in.getId()) + .macAddress(in.getMacAddress()) + ; + } + + } + + private static class ConcreteBuilder extends Builder { + @Override + protected ConcreteBuilder self() { + return this; + } + } + + private final String id; + @SerializedName(value="mac_address") + private final String macAddress; + + protected VirtualInterface(Builder builder) { + this.id = checkNotNull(builder.id, "id"); + this.macAddress = checkNotNull(builder.macAddress, "macAddress"); + } + + public String getId() { + return this.id; + } + + public String getMacAddress() { + return this.macAddress; + } + + @Override + public int hashCode() { + return Objects.hashCode(id, macAddress); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + VirtualInterface that = VirtualInterface.class.cast(obj); + return Objects.equal(this.id, that.id) + && Objects.equal(this.macAddress, that.macAddress) + ; + } + + protected ToStringHelper string() { + return Objects.toStringHelper("") + .add("id", id) + .add("macAddress", macAddress) + ; + } + + @Override + public String toString() { + return string().toString(); + } + +} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VirtualInterfaceAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VirtualInterfaceAsyncClient.java new file mode 100644 index 0000000000..4cd07f6737 --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VirtualInterfaceAsyncClient.java @@ -0,0 +1,60 @@ +/** + * 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.extensions; + +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.MediaType; + +import org.jclouds.openstack.filters.AuthenticateRequest; +import org.jclouds.openstack.nova.v1_1.domain.VirtualInterface; +import org.jclouds.openstack.services.Extension; +import org.jclouds.openstack.services.ServiceType; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.SelectJson; +import org.jclouds.rest.annotations.SkipEncoding; +import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; + +import com.google.common.util.concurrent.ListenableFuture; + +/** + * Provides asynchronous access to Virtual Interface features (VIFs). + * + * @see VirtualInterfaceClient + * @author Adam Lowe + */ +@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.VIRTUAL_INTERFACES) +@SkipEncoding({'/', '='}) +@RequestFilters(AuthenticateRequest.class) +public interface VirtualInterfaceAsyncClient { + /** + * @see VirtualInterfaceClient#listVirtualInterfacesForServer(String) + */ + @GET + @SelectJson("virtual_interfaces") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/servers/{server_id}/os-virtual-interfaces") + @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) + ListenableFuture> listVirtualInterfacesForServer(@PathParam("server_id") String serverId); +} diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VirtualInterfaceClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VirtualInterfaceClient.java new file mode 100644 index 0000000000..acf7692228 --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VirtualInterfaceClient.java @@ -0,0 +1,46 @@ +/** + * 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.extensions; + +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.openstack.nova.v1_1.domain.VirtualInterface; +import org.jclouds.openstack.services.Extension; +import org.jclouds.openstack.services.ServiceType; + +/** + * Provides synchronous access to Virtual Interface features (VIFs). + * + * @see VirtualInterfaceAsyncClient + * @author Adam Lowe + */ +@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.VIRTUAL_INTERFACES) +@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS) +public interface VirtualInterfaceClient { + + /** + * Returns the list of Virtual Interfaces for a given instance. + * + * @return the list of snapshots + */ + Set listVirtualInterfacesForServer(String serverId); + +} diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java index 85406b5e4c..b55e0830f2 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java @@ -68,6 +68,8 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio URI.create("http://docs.openstack.org/compute/ext/hosts/api/v1.1")) .put(URI.create(ExtensionNamespaces.VOLUMES), URI.create("http://docs.openstack.org/compute/ext/volumes/api/v1.1")) + .put(URI.create(ExtensionNamespaces.VIRTUAL_INTERFACES), + URI.create("http://docs.openstack.org/compute/ext/virtual_interfaces/api/v1.1")) .build(); @Inject diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VirtualInterfaceClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VirtualInterfaceClientExpectTest.java new file mode 100644 index 0000000000..553be0dac1 --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VirtualInterfaceClientExpectTest.java @@ -0,0 +1,65 @@ +/** + * 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.extensions; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.net.URI; + +import org.jclouds.openstack.nova.v1_1.domain.VirtualInterface; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientExpectTest; +import org.testng.annotations.Test; + +import com.google.common.collect.Iterables; + +/** + * Tests parsing and guice wiring of VirtualInterfaceClient + * + * @author Adam Lowe + */ +@Test(groups = "live", testName = "VirtualInterfaceClientLiveTest") +public class VirtualInterfaceClientExpectTest extends BaseNovaClientExpectTest { + + public void testListVirtualInterfaces() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/os-virtual-interfaces"); + VirtualInterfaceClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(200).payload(payloadFromResource("/virtual_interfaces_list.json")).build() + ).getVirtualInterfaceExtensionForZone("az-1.region-a.geo-1").get(); + + VirtualInterface vif = Iterables.getOnlyElement(client.listVirtualInterfacesForServer("1")); + assertEquals(vif.getId(), "02315827-b05c-4668-9c05-75c68838074a"); + assertEquals(vif.getMacAddress(), "fa:16:3e:09:71:34"); + } + + public void testListVirtualInterfacesFailNotFound() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/os-virtual-interfaces"); + VirtualInterfaceClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(404).build() + ).getVirtualInterfaceExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.listVirtualInterfacesForServer("1").isEmpty()); + } +} \ No newline at end of file diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VirtualInterfaceClientLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VirtualInterfaceClientLiveTest.java new file mode 100644 index 0000000000..9190502fe9 --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VirtualInterfaceClientLiveTest.java @@ -0,0 +1,70 @@ +/** + * 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.extensions; + +import static org.testng.Assert.assertNotNull; + +import java.util.Set; + +import org.jclouds.openstack.nova.v1_1.domain.Server; +import org.jclouds.openstack.nova.v1_1.domain.VirtualInterface; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientLiveTest; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import com.google.common.base.Optional; +import com.google.common.collect.Iterables; + +/** + * Tests behavior of VirtualInterfaceClient + * + * @author Adam Lowe + */ +@Test(groups = "live", testName = "VirtualInterfaceClientLiveTest", singleThreaded = true) +public class VirtualInterfaceClientLiveTest extends BaseNovaClientLiveTest { + private Optional clientOption; + private String zone; + + + @BeforeClass(groups = {"integration", "live"}) + @Override + public void setupContext() { + super.setupContext(); + zone = Iterables.getLast(novaContext.getApi().getConfiguredZones(), "nova"); + clientOption = novaContext.getApi().getVirtualInterfaceExtensionForZone(zone); + } + + public void testListVirtualInterfaces() { + if (clientOption.isPresent()) { + Server testServer = null; + try { + testServer = createServerInZone(zone); + Set results = clientOption.get().listVirtualInterfacesForServer(testServer.getId()); + for (VirtualInterface vif : results) { + assertNotNull(vif.getId()); + assertNotNull(vif.getMacAddress()); + } + } finally { + if (testServer != null) { + novaContext.getApi().getServerClientForZone(zone).deleteServer(testServer.getId()); + } + } + } +} +} diff --git a/apis/openstack-nova/src/test/resources/virtual_interfaces_list.json b/apis/openstack-nova/src/test/resources/virtual_interfaces_list.json new file mode 100644 index 0000000000..7df0182f03 --- /dev/null +++ b/apis/openstack-nova/src/test/resources/virtual_interfaces_list.json @@ -0,0 +1 @@ +{"virtual_interfaces": [{"id": "02315827-b05c-4668-9c05-75c68838074a", "mac_address": "fa:16:3e:09:71:34"}]} \ No newline at end of file From 06d3ef02badc1aaab93cc1820fe7d703e8e0d9e4 Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Fri, 4 May 2012 11:02:21 +0100 Subject: [PATCH 028/148] openstack-nova: Adding CREATESERVEREXT extension (renamed ServerWithSecurityGroupsClient to be clear about what's on offer) --- .../openstack/nova/v1_1/NovaAsyncClient.java | 10 + .../openstack/nova/v1_1/NovaClient.java | 9 + .../nova/v1_1/config/NovaParserModule.java | 29 +- .../v1_1/config/NovaRestClientModule.java | 1 + .../openstack/nova/v1_1/domain/Server.java | 35 ++- .../v1_1/domain/ServerWithSecurityGroups.java | 271 ++++++++++++++++++ .../ServerWithSecurityGroupsAsyncClient.java | 61 ++++ .../ServerWithSecurityGroupsClient.java | 49 ++++ ...paceEqualsAnyNamespaceInExtensionsSet.java | 4 +- ...verWithSecurityGroupsClientExpectTest.java | 64 +++++ ...erverWithSecurityGroupsClientLiveTest.java | 82 ++++++ .../server_with_security_groups.json | 1 + 12 files changed, 596 insertions(+), 20 deletions(-) create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerWithSecurityGroups.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsAsyncClient.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClient.java create mode 100644 apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClientExpectTest.java create mode 100644 apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClientLiveTest.java create mode 100644 apis/openstack-nova/src/test/resources/server_with_security_groups.json diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java index d00d5cce54..da1c3e16a9 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java @@ -27,6 +27,7 @@ import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPAsyncClient; import org.jclouds.openstack.nova.v1_1.extensions.HostAdministrationAsyncClient; import org.jclouds.openstack.nova.v1_1.extensions.KeyPairAsyncClient; import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupAsyncClient; +import org.jclouds.openstack.nova.v1_1.extensions.ServerWithSecurityGroupsAsyncClient; import org.jclouds.openstack.nova.v1_1.extensions.SimpleTenantUsageAsyncClient; import org.jclouds.openstack.nova.v1_1.extensions.VirtualInterfaceAsyncClient; import org.jclouds.openstack.nova.v1_1.extensions.VolumeAsyncClient; @@ -135,4 +136,13 @@ public interface NovaAsyncClient { @Delegate Optional getVirtualInterfaceExtensionForZone( @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + + + /** + * Provides asynchronous access to Server Extra Data features. + */ + @Delegate + Optional getServerExtraDataExtensionForZone( + @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java index 866351c539..3f1cbe269a 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java @@ -29,6 +29,7 @@ import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPClient; import org.jclouds.openstack.nova.v1_1.extensions.HostAdministrationClient; import org.jclouds.openstack.nova.v1_1.extensions.KeyPairClient; import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupClient; +import org.jclouds.openstack.nova.v1_1.extensions.ServerWithSecurityGroupsClient; import org.jclouds.openstack.nova.v1_1.extensions.SimpleTenantUsageClient; import org.jclouds.openstack.nova.v1_1.extensions.VirtualInterfaceClient; import org.jclouds.openstack.nova.v1_1.extensions.VolumeClient; @@ -139,4 +140,12 @@ public interface NovaClient { Optional getVirtualInterfaceExtensionForZone( @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + + /** + * Provides synchronous access to Server Extra Data features. + */ + @Delegate + Optional getServerExtraDataExtensionForZone( + @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaParserModule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaParserModule.java index dbe79c90a0..3911554c21 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaParserModule.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaParserModule.java @@ -20,14 +20,19 @@ package org.jclouds.openstack.nova.v1_1.config; import java.lang.reflect.Type; import java.util.Map; +import java.util.Set; import javax.inject.Singleton; 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.ServerWithSecurityGroups; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; +import com.google.gson.JsonArray; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; @@ -39,13 +44,17 @@ import com.google.inject.Provides; /** * @author Adrian Cole + * @author Adam Lowe */ public class NovaParserModule extends AbstractModule { @Provides @Singleton public Map provideCustomAdapterBindings() { - return ImmutableMap. of(HostResourceUsage.class, new HostResourceUsageAdapter()); + return ImmutableMap. of( + HostResourceUsage.class, new HostResourceUsageAdapter(), + ServerWithSecurityGroups.class, new ServerWithSecurityGroupsAdapter() + ); } @Override @@ -79,4 +88,22 @@ public class NovaParserModule extends AbstractModule { } } + @Singleton + public static class ServerWithSecurityGroupsAdapter implements JsonDeserializer { + @Override + public ServerWithSecurityGroups deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context) + throws JsonParseException { + Server server = context.deserialize(jsonElement, Server.class); + ServerWithSecurityGroups.Builder result = ServerWithSecurityGroups.builder().fromServer(server); + Set names = Sets.newLinkedHashSet(); + if (jsonElement.getAsJsonObject().get("security_groups") != null) { + JsonArray x = jsonElement.getAsJsonObject().get("security_groups").getAsJsonArray(); + for(JsonElement y : x) { + names.add(y.getAsJsonObject().get("name").getAsString()); + } + result.securityGroupNames(names); + } + return result.build(); + } + } } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java index f2956abe61..7544be56d4 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java @@ -75,6 +75,7 @@ public class NovaRestClientModule extends RestClientModule metadata = Maps.newHashMap(); + protected String uuid; + protected String tenantId; + protected String userId; + protected Date updated; + protected Date created; + protected String hostId; + protected String accessIPv4; + protected String accessIPv6; + protected Status status; + protected String configDrive; + protected Resource image; + protected Resource flavor; + protected Map metadata = Maps.newHashMap(); // TODO: get gson multimap ad - private Multimap addresses = LinkedHashMultimap.create(); - private String adminPass; - private String keyName; + protected Multimap addresses = LinkedHashMultimap.create(); + protected String adminPass; + protected String keyName; /** * @see Server#getUuid() @@ -375,8 +375,7 @@ public class Server extends Resource { } /** - * - * @return host identifier, or null if in {@link ServerState#BUILD} + * @return host identifier, or null if in {@link Status#BUILD} */ @Nullable public String getHostId() { diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerWithSecurityGroups.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerWithSecurityGroups.java new file mode 100644 index 0000000000..e718305913 --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerWithSecurityGroups.java @@ -0,0 +1,271 @@ +/** + * 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 java.util.Collections; +import java.util.Date; +import java.util.Map; +import java.util.Set; + +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.openstack.domain.Link; +import org.jclouds.openstack.domain.Resource; + +import com.google.common.base.Objects.ToStringHelper; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import com.google.gson.annotations.SerializedName; + +/** + * Extended server returned by ServerWithSecurityGroupsClient + * + * @author Adam Lowe + * @see org.jclouds.openstack.nova.v1_1.extensions.ServerWithSecurityGroupsClient + * @see + */ +public class ServerWithSecurityGroups extends Server { + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return new Builder().fromServerWithSecurityGroups(this); + } + + public static class Builder extends Server.Builder { + private Set securityGroupNames = Sets.newLinkedHashSet(); + + /** + * @see ServerWithSecurityGroups#getSecurityGroupNames() + */ + public Builder securityGroupNames(Set securityGroupNames) { + this.securityGroupNames = securityGroupNames; + return this; + } + + public Builder fromServerWithSecurityGroups(ServerWithSecurityGroups in) { + return fromServer(in).securityGroupNames(in.getSecurityGroupNames()); + } + + @Override + public ServerWithSecurityGroups build() { + return new ServerWithSecurityGroups(id, name, links, uuid, tenantId, userId, updated, created, hostId, accessIPv4, accessIPv6, + status, configDrive, image, flavor, adminPass, keyName, addresses, metadata, securityGroupNames); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder fromResource(Resource in) { + return Builder.class.cast(super.fromResource(in)); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder fromServer(Server in) { + return Builder.class.cast(super.fromServer(in)); + } + + + /** + * {@inheritDoc} + */ + @Override + public Builder id(String id) { + return Builder.class.cast(super.id(id)); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder name(String name) { + return Builder.class.cast(super.name(name)); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder links(Set links) { + return Builder.class.cast(super.links(links)); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder links(Link... links) { + return Builder.class.cast(super.links(links)); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder uuid(String uuid) { + return Builder.class.cast(super.uuid(uuid)); + } + /** + * {@inheritDoc} + */ + @Override + public Builder tenantId(String tenantId) { + return Builder.class.cast(super.tenantId(tenantId)); + } + /** + * {@inheritDoc} + */ + @Override + public Builder userId(String userId) { + return Builder.class.cast(super.userId(userId)); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder updated(Date updated) { + return Builder.class.cast(super.updated(updated)); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder created(Date created) { + return Builder.class.cast(super.created(created)); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder hostId(String hostId) { + return Builder.class.cast(super.hostId(hostId)); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder accessIPv4(String accessIPv4) { + return Builder.class.cast(super.accessIPv4(accessIPv4)); + } + /** + * {@inheritDoc} + */ + @Override + public Builder accessIPv6(String accessIPv6) { + return Builder.class.cast(super.accessIPv6(accessIPv6)); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder status(Status status) { + return Builder.class.cast(super.status(status)); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder configDrive(String configDrive) { + return Builder.class.cast(super.configDrive(configDrive)); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder image(Resource image) { + return Builder.class.cast(super.image(image)); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder flavor(Resource flavor) { + return Builder.class.cast(super.flavor(flavor)); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder adminPass(String adminPass) { + return Builder.class.cast(super.adminPass(adminPass)); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder keyName(String keyName) { + return Builder.class.cast(super.keyName(keyName)); + } + + /** + * {@inheritDoc} + */ + @Override + public Builder addresses(Multimap addresses) { + return Builder.class.cast(super.addresses(addresses)); + } + /** + * {@inheritDoc} + */ + @Override + public Builder metadata(Map metadata) { + return Builder.class.cast(super.metadata(metadata)); + } + } + + @SerializedName("security_groups") + private final Set securityGroupNames; + + public ServerWithSecurityGroups(String id, String name, Set links, @Nullable String uuid, String tenantId, + String userId, Date updated, Date created, @Nullable String hostId, + @Nullable String accessIPv4, @Nullable String accessIPv6, Status status, + @Nullable String configDrive, Resource image, Resource flavor, String adminPass, + @Nullable String keyName, Multimap addresses, + Map metadata, Set securityGroupNames) { + super(id, name, links, uuid, tenantId, userId, updated, created, hostId, accessIPv4, accessIPv6, status, configDrive, image, flavor, adminPass, keyName, addresses, metadata); + this.securityGroupNames = ImmutableSet.copyOf(securityGroupNames); + } + + public Set getSecurityGroupNames() { + return Collections.unmodifiableSet(securityGroupNames); + } + + protected ToStringHelper string() { + return super.string().add("securityGroupNames", securityGroupNames); + } + +} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsAsyncClient.java new file mode 100644 index 0000000000..3277b671da --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsAsyncClient.java @@ -0,0 +1,61 @@ +/** + * 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.extensions; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.MediaType; + +import org.jclouds.openstack.filters.AuthenticateRequest; +import org.jclouds.openstack.nova.v1_1.domain.ServerWithSecurityGroups; +import org.jclouds.openstack.services.Extension; +import org.jclouds.openstack.services.ServiceType; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.SelectJson; +import org.jclouds.rest.annotations.SkipEncoding; +import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; + +import com.google.common.util.concurrent.ListenableFuture; + +/** + * Provides synchronous access to Servers with Security Groups. + * + * @see org.jclouds.openstack.nova.v1_1.features.ServerAsyncClient + * @see ServerWithSecurityGroupsClient + * @author Adam Lowe + */ +@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.CREATESERVEREXT) +@SkipEncoding({'/', '='}) +@RequestFilters(AuthenticateRequest.class) +public interface ServerWithSecurityGroupsAsyncClient { + + /** + * @see ServerWithSecurityGroupsClient#getServer(String) + */ + @GET + @SelectJson("server") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/os-create-server-ext/{id}") + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getServer(@PathParam("id") String id); + +} diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClient.java new file mode 100644 index 0000000000..84c8d87b4e --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClient.java @@ -0,0 +1,49 @@ +/** + * 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.extensions; + +import java.util.concurrent.TimeUnit; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.openstack.nova.v1_1.domain.ServerWithSecurityGroups; +import org.jclouds.openstack.services.Extension; +import org.jclouds.openstack.services.ServiceType; + +/** + * Provides synchronous access to Server details including security groups. + *

+ * NOTE: the equivalent to listServersInDetail() doesn't work, so not extending ServerClient at this time. + * + * @author Adam Lowe + * @see org.jclouds.openstack.nova.v1_1.features.ServerClient + * @see ServerWithSecurityGroupsAsyncClient + */ +@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.CREATESERVEREXT) +@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS) +public interface ServerWithSecurityGroupsClient { + + /** + * Retrieve details of the specified server, including security groups + * + * @param id id of the server + * @return server or null if not found + */ + ServerWithSecurityGroups getServer(String id); + +} diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java index b55e0830f2..7975cfeda9 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java @@ -69,7 +69,9 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio .put(URI.create(ExtensionNamespaces.VOLUMES), URI.create("http://docs.openstack.org/compute/ext/volumes/api/v1.1")) .put(URI.create(ExtensionNamespaces.VIRTUAL_INTERFACES), - URI.create("http://docs.openstack.org/compute/ext/virtual_interfaces/api/v1.1")) + URI.create("http://docs.openstack.org/compute/ext/virtual_interfaces/api/v1.1")) + .put(URI.create(ExtensionNamespaces.CREATESERVEREXT), + URI.create("http://docs.openstack.org/compute/ext/createserverext/api/v1.1")) .build(); @Inject diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClientExpectTest.java new file mode 100644 index 0000000000..6ad9738cba --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClientExpectTest.java @@ -0,0 +1,64 @@ +/** + * 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.extensions; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; + +import java.net.URI; + +import org.jclouds.openstack.nova.v1_1.domain.ServerWithSecurityGroups; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientExpectTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; + +/** + * Tests parsing and guice wiring of ServerExtraDataClient + * + * @author Adam Lowe + */ +@Test(groups = "unit", testName = "ServerWithSecurityGroupsClientExpectTest") +public class ServerWithSecurityGroupsClientExpectTest extends BaseNovaClientExpectTest { + + public void testGetServerWithSecurityGroups() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-create-server-ext/8d0a6ca5-8849-4b3d-b86e-f24c92490ebb"); + ServerWithSecurityGroupsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(200).payload(payloadFromResource("/server_with_security_groups.json")).build() + ).getServerExtraDataExtensionForZone("az-1.region-a.geo-1").get(); + + ServerWithSecurityGroups server = client.getServer("8d0a6ca5-8849-4b3d-b86e-f24c92490ebb"); + assertEquals(server.getId(), "8d0a6ca5-8849-4b3d-b86e-f24c92490ebb"); + assertEquals(server.getSecurityGroupNames(), ImmutableSet.of("default", "group1")); + } + + public void testGetServerWithSecurityGroupsFailNotFound() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-create-server-ext/8d0a6ca5-8849-4b3d-b86e-f24c92490ebb"); + ServerWithSecurityGroupsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(404).build() + ).getServerExtraDataExtensionForZone("az-1.region-a.geo-1").get(); + assertNull(client.getServer("8d0a6ca5-8849-4b3d-b86e-f24c92490ebb")); + } +} \ No newline at end of file diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClientLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClientLiveTest.java new file mode 100644 index 0000000000..351a08ee45 --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClientLiveTest.java @@ -0,0 +1,82 @@ +/** + * 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.extensions; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +import org.jclouds.openstack.domain.Resource; +import org.jclouds.openstack.nova.v1_1.domain.Server; +import org.jclouds.openstack.nova.v1_1.domain.ServerWithSecurityGroups; +import org.jclouds.openstack.nova.v1_1.features.ServerClient; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientLiveTest; +import org.testng.annotations.BeforeGroups; +import org.testng.annotations.Test; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; + +/** + * Tests behavior of ServerWithSecurityGroupsClient + * + * @author Adam Lowe + */ +@Test(groups = "live", testName = "ServerWithSecurityGroupsClientLiveTest", singleThreaded = true) +public class ServerWithSecurityGroupsClientLiveTest extends BaseNovaClientLiveTest { + private ServerClient serverClient; + private Optional clientOption; + private String zone; + + @BeforeGroups(groups = {"integration", "live"}) + @Override + public void setupContext() { + super.setupContext(); + zone = Iterables.getLast(novaContext.getApi().getConfiguredZones(), "nova"); + serverClient = novaContext.getApi().getServerClientForZone(zone); + clientOption = novaContext.getApi().getServerExtraDataExtensionForZone(zone); + } + + public void testGetServer() { + if (clientOption.isPresent()) { + + for (Resource server : serverClient.listServers()) { + ServerWithSecurityGroups serverWithGroups = clientOption.get().getServer(server.getId()); + assertEquals(serverWithGroups.getId(), server.getId()); + assertEquals(serverWithGroups.getName(), server.getName()); + assertNotNull(serverWithGroups.getSecurityGroupNames()); + } + + // Create a new server to verify the groups work as expected + Server testServer = null; + try { + testServer = createServerInZone(zone); + + ServerWithSecurityGroups results = clientOption.get().getServer(testServer.getId()); + assertEquals(results.getId(), testServer.getId()); + assertEquals(results.getSecurityGroupNames(), ImmutableSet.of("default")); + } finally { + if (testServer != null) { + serverClient.deleteServer(testServer.getId()); + } + } + } + } + +} \ No newline at end of file diff --git a/apis/openstack-nova/src/test/resources/server_with_security_groups.json b/apis/openstack-nova/src/test/resources/server_with_security_groups.json new file mode 100644 index 0000000000..16e03fe9e1 --- /dev/null +++ b/apis/openstack-nova/src/test/resources/server_with_security_groups.json @@ -0,0 +1 @@ +{"server": {"status": "ACTIVE", "updated": "2012-05-04T12:15:01Z", "hostId": "02c7c81e36024d2bfdb473cb762900138bc07777922479d3d4f8f690", "user_id": "1e8a56719e0d4ab4b7edb85c77f7290f", "name": "test", "links": [{"href": "http://172.16.89.148:8774/v2/4287930c796741aa898425f40832cb3c/servers/8d0a6ca5-8849-4b3d-b86e-f24c92490ebb", "rel": "self"}, {"href": "http://172.16.89.148:8774/4287930c796741aa898425f40832cb3c/servers/8d0a6ca5-8849-4b3d-b86e-f24c92490ebb", "rel": "bookmark"}], "created": "2012-05-04T12:14:57Z", "tenant_id": "4287930c796741aa898425f40832cb3c", "image": {"id": "ea17cc36-f7c9-40cd-b6bf-a952b74870f2", "links": [{"href": "http://172.16.89.148:8774/4287930c796741aa898425f40832cb3c/images/ea17cc36-f7c9-40cd-b6bf-a952b74870f2", "rel": "bookmark"}]}, "addresses": {"private": [{"version": 4, "addr": "10.0.0.8"}]}, "accessIPv4": "", "accessIPv6": "", "key_name": "", "progress": 0, "flavor": {"id": "1", "links": [{"href": "http://172.16.89.148:8774/4287930c796741aa898425f40832cb3c/flavors/1", "rel": "bookmark"}]}, "config_drive": "", "id": "8d0a6ca5-8849-4b3d-b86e-f24c92490ebb", "security_groups": [{"name": "default"},{"name": "group1"}], "metadata": {}}} \ No newline at end of file From 49e60aa8bc9af494d07a395c25cd9ca9f2e53b47 Mon Sep 17 00:00:00 2001 From: Andrew Donald Kennedy Date: Fri, 4 May 2012 13:14:45 +0100 Subject: [PATCH 029/148] Bugfixes and tidying for vcloud-director and automated test tool --- .../director/v1_5/domain/dmtf/RasdItem.java | 3 +-- .../domain/network/NetworkConnection.java | 2 +- .../domain/params/ControlAccessParams.java | 5 ++-- .../v1_5/features/OrgClientLiveTest.java | 2 +- .../v1_5/features/VAppClientLiveTest.java | 5 ++-- .../features/VAppTemplateClientLiveTest.java | 26 ++++++++++--------- .../v1_5/features/VmClientLiveTest.java | 17 +++++++----- .../internal/VCloudDirectorTestSession.java | 2 +- .../v1_5/login/SessionClientLiveTest.java | 2 +- 9 files changed, 35 insertions(+), 29 deletions(-) diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/dmtf/RasdItem.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/dmtf/RasdItem.java index ec1bc84ce1..903468bf59 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/dmtf/RasdItem.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/dmtf/RasdItem.java @@ -20,7 +20,6 @@ package org.jclouds.vcloud.director.v1_5.domain.dmtf; import static com.google.common.base.Objects.equal; import static com.google.common.base.Preconditions.checkNotNull; -import static org.jclouds.dmtf.DMTFConstants.OVF_NS; import static org.jclouds.vcloud.director.v1_5.VCloudDirectorConstants.VCLOUD_1_5_NS; import java.net.URI; @@ -44,7 +43,7 @@ import com.google.common.collect.Sets; * * @author grkvlt@apache.org */ -@XmlRootElement(name = "Item", namespace = OVF_NS) +@XmlRootElement(name = "Item", namespace = VCLOUD_1_5_NS) public class RasdItem extends ResourceAllocationSettingData { public static Builder builder() { diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/network/NetworkConnection.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/network/NetworkConnection.java index 4c5303552d..54faf16f1b 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/network/NetworkConnection.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/network/NetworkConnection.java @@ -76,7 +76,7 @@ public class NetworkConnection { @XmlEnumValue("pool") POOL("pool"), @XmlEnumValue("dhcp") DHCP("dhcp"), @XmlEnumValue("manual") MANUAL("manual"), - @XmlEnumValue("none") NONE("none"), + @XmlEnumValue("NONE") NONE("none"), @XmlEnumValue("") UNRECOGNIZED("unrecognized"); public static final List ALL = ImmutableList.of(POOL, DHCP, MANUAL, NONE); diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/params/ControlAccessParams.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/params/ControlAccessParams.java index dd1c66f3c9..b97fd5ad24 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/params/ControlAccessParams.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/params/ControlAccessParams.java @@ -133,10 +133,11 @@ public class ControlAccessParams { this.sharedToEveryone = sharedToEveryone; if (sharedToEveryone) { this.everyoneAccessLevel = checkNotNull(everyoneAccessLevel, "everyoneAccessLevel"); + this.accessSettings = null; } else { - checkNotNull(accessSettings, "accessSettings"); + this.everyoneAccessLevel = null; + this.accessSettings = Iterables.isEmpty(checkNotNull(accessSettings, "accessSettings")) ? null : ImmutableList.copyOf(accessSettings); } - this.accessSettings = Iterables.isEmpty(accessSettings) ? null : ImmutableList.copyOf(accessSettings); } @XmlElement(name = "IsSharedToEveryone", required = true) diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/OrgClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/OrgClientLiveTest.java index 33dd982ad6..a65677b72d 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/OrgClientLiveTest.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/OrgClientLiveTest.java @@ -185,7 +185,7 @@ public class OrgClientLiveTest extends BaseVCloudDirectorClientLiveTest { checkControlAccessParams(params); } - @Test(description = "GET /org/{id}/catalog/{catalogId}/action/controlAccess", dependsOnMethods = { "testGetControlAccess" }) + @Test(description = "POST /org/{id}/catalog/{catalogId}/action/controlAccess", dependsOnMethods = { "testGetControlAccess" }) public void testModifyControlAccess() { // Setup params ControlAccessParams params = orgClient.getControlAccess(orgURI, testCatalogId); diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppClientLiveTest.java index c5ec6a0b17..e0594491e8 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppClientLiveTest.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppClientLiveTest.java @@ -395,8 +395,9 @@ public class VAppClientLiveTest extends AbstractVAppClientLiveTest { @Test(description = "POST /vApp/{id}/action/discardSuspendedState", dependsOnMethods = { "testDeployVApp" }) public void testDiscardSuspendedState() { - // Suspend the VApp - vApp = suspendVApp(vApp.getHref()); + // Power on, then suspend the VApp + vApp = powerOnVApp(vAppURI); + vApp = suspendVApp(vAppURI); // The method under test Task discardSuspendedState = vAppClient.discardSuspendedState(vApp.getHref()); diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateClientLiveTest.java index faf4c07531..303f45f384 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateClientLiveTest.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateClientLiveTest.java @@ -112,7 +112,7 @@ public class VAppTemplateClientLiveTest extends AbstractVAppClientLiveTest { if (waitForTask) { Task cloneTask = Iterables.getFirst(clonedVappTemplate.getTasks(), null); assertNotNull(cloneTask, "vdcClient.cloneVAppTemplate returned VAppTemplate that did not contain any tasks"); - retryTaskSuccess.apply(cloneTask); + assertTaskSucceeds(cloneTask); } return clonedVappTemplate; @@ -174,7 +174,6 @@ public class VAppTemplateClientLiveTest extends AbstractVAppClientLiveTest { @Test(description = "GET /vAppTemplate/{id}/leaseSettingsSection") public void testGetLeaseSettingsSection() { - // FIXME Wrong case for Vapp LeaseSettingsSection leaseSettingsSection = vAppTemplateClient.getLeaseSettingsSection(vAppTemplateURI); checkLeaseSettingsSection(leaseSettingsSection); @@ -240,7 +239,7 @@ public class VAppTemplateClientLiveTest extends AbstractVAppClientLiveTest { .build(); final Task task = vAppTemplateClient.modifyVAppTemplate(vAppTemplateURI, template); - retryTaskSuccess.apply(task); + assertTaskSucceeds(task); VAppTemplate newTemplate = vAppTemplateClient.getVAppTemplate(vAppTemplateURI); assertEquals(newTemplate.getName(), name); @@ -258,7 +257,7 @@ public class VAppTemplateClientLiveTest extends AbstractVAppClientLiveTest { Metadata metadata = Metadata.builder().fromMetadata(oldMetadata).entry(metadataEntry).build(); final Task task = vAppTemplateClient.getMetadataClient().mergeMetadata(vAppTemplateURI, metadata); - retryTaskSuccess.apply(task); + assertTaskSucceeds(task); Metadata newMetadata = vAppTemplateClient.getMetadataClient().getMetadata(vAppTemplateURI); Map expectedMetadataMap = ImmutableMap.builder() @@ -283,7 +282,7 @@ public class VAppTemplateClientLiveTest extends AbstractVAppClientLiveTest { @Test(description = "DELETE /vAppTemplate/{id}/metadata/{key}", dependsOnMethods = { "testGetMetadataValue" }) public void testDeleteVAppTemplateMetadataValue() { final Task deletionTask = vAppTemplateClient.getMetadataClient().deleteMetadataEntry(vAppTemplateURI, key); - retryTaskSuccess.apply(deletionTask); + assertTaskSucceeds(deletionTask); Metadata newMetadata = vAppTemplateClient.getMetadataClient().getMetadata(vAppTemplateURI); checkMetadataKeyAbsentFor("vAppTemplate", newMetadata, key); @@ -318,7 +317,7 @@ public class VAppTemplateClientLiveTest extends AbstractVAppClientLiveTest { .build(); final Task task = vAppTemplateClient.modifyCustomizationSection(vAppTemplateURI, customizationSection); - retryTaskSuccess.apply(task); + assertTaskSucceeds(task); CustomizationSection newCustomizationSection = vAppTemplateClient.getCustomizationSection(vAppTemplateURI); assertEquals(newCustomizationSection.isCustomizeOnInstantiate(), newVal); @@ -338,7 +337,7 @@ public class VAppTemplateClientLiveTest extends AbstractVAppClientLiveTest { .build(); final Task task = vAppTemplateClient.modifyLeaseSettingsSection(vAppTemplateURI, leaseSettingSection); - retryTaskSuccess.apply(task); + assertTaskSucceeds(task); LeaseSettingsSection newLeaseSettingsSection = vAppTemplateClient.getLeaseSettingsSection(vAppTemplateURI); assertEquals(newLeaseSettingsSection.getStorageLeaseInSeconds(), (Integer) storageLeaseInSeconds); @@ -411,7 +410,7 @@ public class VAppTemplateClientLiveTest extends AbstractVAppClientLiveTest { // Delete the template final Task task = vAppTemplateClient.deleteVappTemplate(clonedVappTemplate.getHref()); - retryTaskSuccess.apply(task); + assertTaskSucceeds(task); // Confirm that can't access post-delete, i.e. template has been deleted VAppTemplate deleted = vAppTemplateClient.getVAppTemplate(clonedVappTemplate.getHref()); @@ -436,7 +435,7 @@ public class VAppTemplateClientLiveTest extends AbstractVAppClientLiveTest { // First disable so that enable really has some work to do... vAppTemplateClient.disableDownload(vAppTemplateURI); final Task task = vAppTemplateClient.enableDownload(vAppTemplateURI); - retryTaskSuccess.apply(task); + assertTaskSucceeds(task); // TODO Check that it really is enabled. The only thing I can see for determining this // is the undocumented "download" link in the VAppTemplate. But that is brittle and we @@ -462,9 +461,9 @@ public class VAppTemplateClientLiveTest extends AbstractVAppClientLiveTest { // TODO Need assertion that command had effect } - @Test(description = "POST /vAppTemplate/{id}/action/relocate") // FIXME Need a datastore reference + // TODO How to obtain a datastore reference? + @Test(description = "POST /vAppTemplate/{id}/action/relocate") public void testRelocateVAppTemplate() throws Exception { - // TODO Need assertion that command had effect Reference dataStore = null; // FIXME RelocateParams relocateParams = RelocateParams.builder() .datastore(dataStore) @@ -472,9 +471,12 @@ public class VAppTemplateClientLiveTest extends AbstractVAppClientLiveTest { final Task task = vAppTemplateClient.relocateVm(vAppTemplateURI, relocateParams); assertTaskSucceedsLong(task); + + // TODO Need assertion that command had effect } - @Test(description = "GET /vAppTemplate/{id}/shadowVms") + // NOTE This will fail unless we can relocate a template to another datastore + @Test(description = "GET /vAppTemplate/{id}/shadowVms", dependsOnMethods = { "testRelocateVAppTemplate" }) public void testGetShadowVms() { References references = vAppTemplateClient.getShadowVms(vAppTemplateURI); diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VmClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VmClientLiveTest.java index bcb4f09413..5e5a09ca60 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VmClientLiveTest.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VmClientLiveTest.java @@ -198,7 +198,7 @@ public class VmClientLiveTest extends AbstractVAppClientLiveTest { checkVm(vm); // Check the required fields are set - assertEquals(vm.isDeployed(), Boolean.FALSE, String.format(OBJ_FIELD_EQ, VAPP, "deployed", "FALSE", vm.isDeployed().toString())); + assertEquals(vm.isDeployed(), Boolean.FALSE, String.format(OBJ_FIELD_EQ, VM, "deployed", "FALSE", vm.isDeployed().toString())); // Check status assertVmStatus(vm.getHref(), Status.POWERED_OFF); @@ -349,7 +349,7 @@ public class VmClientLiveTest extends AbstractVAppClientLiveTest { vm = vmClient.getVm(vm.getHref()); // Check status - assertFalse(vm.isDeployed(), String.format(OBJ_FIELD_EQ, VAPP, "deployed", "FALSE", vm.isDeployed().toString())); + assertFalse(vm.isDeployed(), String.format(OBJ_FIELD_EQ, VM, "deployed", "FALSE", vm.isDeployed().toString())); assertVmStatus(vmURI, Status.POWERED_OFF); } @@ -663,7 +663,7 @@ public class VmClientLiveTest extends AbstractVAppClientLiveTest { } } - @Test(description = "GET /vApp/{id}/screen/action/acquireTicket", dependsOnMethods = { "testDeployVm" }) + @Test(description = "POST /vApp/{id}/screen/action/acquireTicket", dependsOnMethods = { "testDeployVm" }) public void testGetScreenTicket() { // Power on Vm vm = powerOnVm(vm.getHref()); @@ -911,7 +911,7 @@ public class VmClientLiveTest extends AbstractVAppClientLiveTest { // See the description in testModifyVirtualHardwareSectionDisks } - @Test(description = "PUT /vApp/{id}/metadata", dependsOnMethods = { "testGetVm" }) + @Test(description = "PUT /vApp/{id}/metadata/{key}", dependsOnMethods = { "testGetVm" }) public void testSetMetadataValue() { key = name("key-"); String value = name("value-"); @@ -922,7 +922,7 @@ public class VmClientLiveTest extends AbstractVAppClientLiveTest { MetadataValue newMetadataValue = vmClient.getMetadataClient().getMetadataValue(vm.getHref(), key); // Check the retrieved object is well formed - checkMetadataValueFor(VAPP, newMetadataValue, value); + checkMetadataValueFor(VM, newMetadataValue, value); } @Test(description = "GET /vApp/{id}/metadata", dependsOnMethods = { "testSetMetadataValue" }) @@ -957,7 +957,7 @@ public class VmClientLiveTest extends AbstractVAppClientLiveTest { Metadata newMetadata = vmClient.getMetadataClient().getMetadata(vm.getHref()); // Check the retrieved object is well formed - checkMetadataKeyAbsentFor(VAPP, newMetadata, key); + checkMetadataKeyAbsentFor(VM, newMetadata, key); } @Test(description = "POST /vApp/{id}/metadata", dependsOnMethods = { "testGetMetadata" }) @@ -982,7 +982,7 @@ public class VmClientLiveTest extends AbstractVAppClientLiveTest { .build(); // Check the retrieved object is well formed - checkMetadataFor(VAPP, newMetadata, expectedMetadataMap); + checkMetadataFor(VM, newMetadata, expectedMetadataMap); } /** @@ -1004,6 +1004,9 @@ public class VmClientLiveTest extends AbstractVAppClientLiveTest { delete = vAppClient.getVApp(delete.getHref()); Vm temp = Iterables.getOnlyElement(delete.getChildren().getVms()); + // Power off the Vm + temp = powerOffVm(temp.getHref()); + // The method under test Task deleteVm = vmClient.deleteVm(temp.getHref()); assertTrue(retryTaskSuccess.apply(deleteVm), String.format(TASK_COMPLETE_TIMELY, "deleteVm")); diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/VCloudDirectorTestSession.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/VCloudDirectorTestSession.java index 4199435c16..4602187e5a 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/VCloudDirectorTestSession.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/VCloudDirectorTestSession.java @@ -107,8 +107,8 @@ public class VCloudDirectorTestSession implements Closeable { @Override public void close() { + // NOTE we only need to close the user context to log out Closeables.closeQuietly(userContext); - Closeables.closeQuietly(adminContext); } public VCloudDirectorContext getUserContext() { diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/login/SessionClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/login/SessionClientLiveTest.java index 3cb1e1743f..427520b52a 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/login/SessionClientLiveTest.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/login/SessionClientLiveTest.java @@ -64,7 +64,7 @@ public class SessionClientLiveTest extends BaseContextLiveTest Date: Fri, 4 May 2012 14:59:31 +0100 Subject: [PATCH 030/148] openstack-nova: Adjusting Resource and it's descendants to new builder pattern --- .../openstack/nova/v1_1/domain/Extension.java | 122 +++-- .../openstack/nova/v1_1/domain/Flavor.java | 102 ++-- .../openstack/nova/v1_1/domain/Image.java | 184 ++++---- .../openstack/nova/v1_1/domain/Server.java | 435 ++++++++++++------ .../v1_1/domain/ServerWithSecurityGroups.java | 262 ++--------- .../jclouds/openstack/domain/Resource.java | 111 +++-- .../keystone/v2_0/domain/ApiMetadata.java | 99 ++-- 7 files changed, 641 insertions(+), 674 deletions(-) diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Extension.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Extension.java index 17b756f491..6291c495c7 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Extension.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Extension.java @@ -18,15 +18,13 @@ */ package org.jclouds.openstack.nova.v1_1.domain; -import static com.google.common.base.Objects.toStringHelper; - import java.net.URI; import java.util.Date; -import java.util.Set; -import org.jclouds.openstack.domain.Link; import org.jclouds.openstack.domain.Resource; +import com.google.common.base.Objects; + /** * The OpenStack Compute API is extensible. Extensions serve two purposes: They * allow the introduction of new features in the API without requiring a version @@ -39,94 +37,86 @@ import org.jclouds.openstack.domain.Resource; * /> */ public class Extension extends Resource { - public static Builder builder() { - return new Builder(); + public static Builder builder() { + return new ConcreteBuilder(); } - public Builder toBuilder() { - return builder().fromExtension(this); + public Builder toBuilder() { + return new ConcreteBuilder().fromExtension(this); } - public static class Builder extends Resource.Builder { - + public static abstract class Builder> extends Resource.Builder { private URI namespace; private String alias; private Date updated; private String description; - public Builder namespace(URI namespace) { + /** + * @see Extension#getNamespace() + */ + public T namespace(URI namespace) { this.namespace = namespace; - return this; + return self(); } - public Builder alias(String alias) { + /** + * @see Extension#getAlias() + */ + public T alias(String alias) { + id(alias); this.alias = alias; - return this; + return self(); } - public Builder updated(Date updated) { + /** + * @see Extension#getUpdated() + */ + public T updated(Date updated) { this.updated = updated; - return this; + return self(); } - public Builder description(String description) { + /** + * @see Extension#getDescription() + */ + public T description(String description) { this.description = description; - return this; + return self(); } public Extension build() { - return new Extension(name, links, namespace, alias, updated, description); + return new Extension(this); } - public Builder fromExtension(Extension in) { - return fromResource(in).namespace(in.getNamespace()).alias(in.getAlias()).updated(in.getUpdated()) - .description(in.getDescription()); + public T fromExtension(Extension in) { + return super.fromResource(in) + .namespace(in.getNamespace()) + .alias(in.getAlias()) + .updated(in.getUpdated()) + .description(in.getDescription()) + ; } - /** - * {@inheritDoc} - */ + } + + private static class ConcreteBuilder extends Builder { @Override - public Builder id(String id) { - return alias(id); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder name(String name) { - return Builder.class.cast(super.name(name)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder links(Set links) { - return Builder.class.cast(super.links(links)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder fromResource(Resource in) { - return Builder.class.cast(super.fromResource(in)); + protected ConcreteBuilder self() { + return this; } } - private URI namespace; - private String alias; - private Date updated; - private String description; + private final URI namespace; + private final String alias; + private final Date updated; + private final String description; - protected Extension(String name, Set links, URI namespace, String alias, Date updated, String description) { - super(alias, name, links); - this.namespace = namespace; - this.alias = alias; - this.updated = updated; - this.description = description; + protected Extension(Builder builder) { + super(builder); + this.namespace = builder.namespace; + this.alias = builder.alias; + this.updated = builder.updated; + this.description = builder.description; } public URI getNamespace() { @@ -151,9 +141,11 @@ public class Extension extends Resource { } @Override - public String toString() { - return toStringHelper("").add("id", getId()).add("name", name).add("links", links).add("namespace", namespace) - .add("alias", alias).add("updated", updated).add("description", description).toString(); + public Objects.ToStringHelper string() { + return super.string() + .add("namespace", namespace) + .add("alias", alias) + .add("updated", updated) + .add("description", description); } - } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Flavor.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Flavor.java index 1c372ddeee..12d37cc2a3 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Flavor.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Flavor.java @@ -18,13 +18,10 @@ */ package org.jclouds.openstack.nova.v1_1.domain; -import static com.google.common.base.Objects.toStringHelper; - -import java.util.Set; - -import org.jclouds.openstack.domain.Link; import org.jclouds.openstack.domain.Resource; +import com.google.common.base.Objects; + /** * A flavor is an available hardware configuration for a server. Each flavor has * a unique combination of disk space and memory capacity. @@ -35,73 +32,61 @@ import org.jclouds.openstack.domain.Resource; * /> */ public class Flavor extends Resource { - public static Builder builder() { - return new Builder(); + public static Builder builder() { + return new ConcreteBuilder(); } - public Builder toBuilder() { - return builder().fromFlavor(this); + public Builder toBuilder() { + return new ConcreteBuilder().fromFlavor(this); } - public static class Builder extends Resource.Builder { - + public static abstract class Builder> extends Resource.Builder { private int ram; private int disk; private int vcpus; - public Builder ram(int ram) { + /** + * @see Flavor#getRam() + */ + public T ram(int ram) { this.ram = ram; - return this; + return self(); } - public Builder disk(int disk) { + /** + * @see Flavor#getDisk() + */ + public T disk(int disk) { this.disk = disk; - return this; + return self(); } - public Builder vcpus(int vcpus) { + /** + * @see Flavor#getVcpus() + */ + public T vcpus(int vcpus) { this.vcpus = vcpus; - return this; + return self(); } public Flavor build() { - return new Flavor(id, name, links, ram, disk, vcpus); + return new Flavor(this); } - public Builder fromFlavor(Flavor in) { - return fromResource(in).ram(in.getRam()).disk(in.getDisk()).vcpus(in.getVcpus()); + public T fromFlavor(Flavor in) { + return super.fromResource(in) + .ram(in.getRam()) + .disk(in.getDisk()) + .vcpus(in.getVcpus()) + ; } - /** - * {@inheritDoc} - */ + } + + private static class ConcreteBuilder extends Builder { @Override - public Builder id(String id) { - return Builder.class.cast(super.id(id)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder name(String name) { - return Builder.class.cast(super.name(name)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder links(Set links) { - return Builder.class.cast(super.links(links)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder fromResource(Resource in) { - return Builder.class.cast(super.fromResource(in)); + protected ConcreteBuilder self() { + return this; } } @@ -109,11 +94,11 @@ public class Flavor extends Resource { private int disk; private int vcpus; - protected Flavor(String id, String name, Set links, int ram, int disk, int vcpus) { - super(id, name, links); - this.ram = ram; - this.disk = disk; - this.vcpus = vcpus; + protected Flavor(Builder builder) { + super(builder); + this.ram = builder.ram; + this.disk = builder.disk; + this.vcpus = builder.vcpus; } public int getRam() { @@ -129,9 +114,10 @@ public class Flavor extends Resource { } @Override - public String toString() { - return toStringHelper("").add("id", id).add("name", name).add("links", links).add("ram", ram).add("disk", disk) - .add("vcpus", vcpus).toString(); + protected Objects.ToStringHelper string() { + return super.string() + .add("ram", ram) + .add("disk", disk) + .add("vcpus", vcpus); } - } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Image.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Image.java index 132bd9ae53..9670ba096e 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Image.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Image.java @@ -18,15 +18,12 @@ */ package org.jclouds.openstack.nova.v1_1.domain; -import static com.google.common.base.Objects.toStringHelper; - import java.util.Date; import java.util.Map; -import java.util.Set; -import org.jclouds.openstack.domain.Link; import org.jclouds.openstack.domain.Resource; +import com.google.common.base.Objects; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; @@ -68,117 +65,130 @@ public class Image extends Resource { } - public static Builder builder() { - return new Builder(); + public static Builder builder() { + return new ConcreteBuilder(); } - public Builder toBuilder() { - return builder().fromImage(this); + public Builder toBuilder() { + return new ConcreteBuilder().fromImage(this); } - public static class Builder extends Resource.Builder { - + public static abstract class Builder> extends Resource.Builder { private Date updated; private Date created; private String tenantId; private String userId; - private Status status; + private Image.Status status; private int progress; private int minDisk; private int minRam; private Resource server; - private Map metadata = Maps.newLinkedHashMap(); + private Map metadata = ImmutableMap.of(); - public Builder updated(Date updated) { + /** + * @see Image#getUpdated() + */ + public T updated(Date updated) { this.updated = updated; - return this; + return self(); } - public Builder created(Date created) { + /** + * @see Image#getCreated() + */ + public T created(Date created) { this.created = created; - return this; + return self(); } - public Builder tenantId(String tenantId) { + /** + * @see Image#getTenantId() + */ + public T tenantId(String tenantId) { this.tenantId = tenantId; - return this; + return self(); } - public Builder userId(String userId) { + /** + * @see Image#getUserId() + */ + public T userId(String userId) { this.userId = userId; - return this; + return self(); } - public Builder status(Status status) { + /** + * @see Image#getStatus() + */ + public T status(Image.Status status) { this.status = status; - return this; + return self(); } - public Builder progress(int progress) { + /** + * @see Image#getProgress() + */ + public T progress(int progress) { this.progress = progress; - return this; + return self(); } - public Builder minDisk(int minDisk) { + /** + * @see Image#getMinDisk() + */ + public T minDisk(int minDisk) { this.minDisk = minDisk; - return this; + return self(); } - public Builder minRam(int minRam) { + /** + * @see Image#getMinRam() + */ + public T minRam(int minRam) { this.minRam = minRam; - return this; + return self(); } - public Builder server(Resource server) { + /** + * @see Image#getServer() + */ + public T server(Resource server) { this.server = server; - return this; + return self(); } - public Builder metadata(Map metadata) { + /** + * @see Image#getMetadata() + */ + public T metadata(Map metadata) { this.metadata = metadata; - return this; + return self(); } public Image build() { - return new Image(id, name, links, updated, created, tenantId, userId, status, progress, minDisk, minRam, - server, metadata); + return new Image(this); } - public Builder fromImage(Image in) { - return fromResource(in).status(in.getStatus()).updated(in.getUpdated()).created(in.getCreated()).progress( - in.getProgress()).server(in.getServer()).metadata(in.getMetadata()); + public T fromImage(Image in) { + return super.fromResource(in) + .updated(in.getUpdated()) + .created(in.getCreated()) + .tenantId(in.getTenantId()) + .userId(in.getUserId()) + .status(in.getStatus()) + .progress(in.getProgress()) + .minDisk(in.getMinDisk()) + .minRam(in.getMinRam()) + .server(in.getServer()) + .metadata(in.getMetadata()); } - /** - * {@inheritDoc} - */ + } + + private static class ConcreteBuilder extends Builder { @Override - public Builder id(String id) { - return Builder.class.cast(super.id(id)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder name(String name) { - return Builder.class.cast(super.name(name)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder links(Set links) { - return Builder.class.cast(super.links(links)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder fromResource(Resource in) { - return Builder.class.cast(super.fromResource(in)); + protected ConcreteBuilder self() { + return this; } } @@ -195,19 +205,18 @@ public class Image extends Resource { private final Resource server; private final Map metadata; - protected Image(String id, String name, Set links, Date updated, Date created, String tenantId, String userId, - Status status, int progress, int minDisk, int minRam, Resource server, Map metadata) { - super(id, name, links); - this.updated = updated; - this.created = created; - this.tenantId = tenantId; - this.userId = userId; - this.status = status; - this.progress = progress; - this.minDisk = minDisk; - this.minRam = minRam; - this.server = server; - this.metadata = ImmutableMap. copyOf(metadata); + protected Image(Builder builder) { + super(builder); + this.updated = builder.updated; + this.created = builder.created; + this.tenantId = builder.tenantId; + this.userId = builder.userId; + this.status = builder.status; + this.progress = builder.progress; + this.minDisk = builder.minDisk; + this.minRam = builder.minRam; + this.server = builder.server; + this.metadata = ImmutableMap.copyOf(builder.metadata); } public Date getUpdated() { @@ -252,11 +261,18 @@ public class Image extends Resource { } @Override - public String toString() { - return toStringHelper("").add("id", id).add("name", name).add("links", links).add("updated", updated).add( - "created", created).add("tenantId", tenantId).add("userId", userId).add("status", status).add( - "progress", progress).add("minDisk", minDisk).add("minRam", minRam).add("server", server).add( - "metadata", metadata).toString(); + protected Objects.ToStringHelper string() { + return super.string() + .add("updated", updated) + .add("created", created) + .add("tenantId", tenantId) + .add("userId", userId) + .add("status", status) + .add("progress", progress) + .add("minDisk", minDisk) + .add("minRam", minRam) + .add("server", server) + .add("metadata", metadata); } } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Server.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Server.java index 6e2b128b27..e05c641a24 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Server.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Server.java @@ -26,7 +26,6 @@ import java.util.Set; import org.jclouds.compute.domain.NodeState; import org.jclouds.javax.annotation.Nullable; -import org.jclouds.openstack.domain.Link; import org.jclouds.openstack.domain.Resource; import org.jclouds.openstack.nova.v1_1.extensions.KeyPairClient; import org.jclouds.util.Multimaps2; @@ -36,7 +35,6 @@ import com.google.common.base.Predicates; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.gson.annotations.SerializedName; @@ -67,7 +65,7 @@ public class Server extends Resource { NodeState.PENDING), VERIFY_RESIZE(NodeState.PENDING), REVERT_RESIZE(NodeState.PENDING), PASSWORD( NodeState.PENDING), REBOOT(NodeState.PENDING), HARD_REBOOT(NodeState.PENDING), DELETED( NodeState.TERMINATED), UNKNOWN(NodeState.UNRECOGNIZED), ERROR(NodeState.ERROR), UNRECOGNIZED( - NodeState.UNRECOGNIZED); + NodeState.UNRECOGNIZED), PAUSED(NodeState.SUSPENDED); private final NodeState nodeState; @@ -92,216 +90,263 @@ public class Server extends Resource { } } - public static Builder builder() { - return new Builder(); + + public static Builder builder() { + return new ConcreteBuilder(); } - public Builder toBuilder() { - return builder().fromServer(this); + public Builder toBuilder() { + return new ConcreteBuilder().fromServer(this); } - public static class Builder extends Resource.Builder { - protected String uuid; - protected String tenantId; - protected String userId; - protected Date updated; - protected Date created; - protected String hostId; - protected String accessIPv4; - protected String accessIPv6; - protected Status status; - protected String configDrive; - protected Resource image; - protected Resource flavor; - protected Map metadata = Maps.newHashMap(); - // TODO: get gson multimap ad - protected Multimap addresses = LinkedHashMultimap.create(); - protected String adminPass; - protected String keyName; + public static abstract class Builder> extends Resource.Builder { + private String uuid; + private String tenantId; + private String userId; + private Date updated; + private Date created; + private String hostId; + private String accessIPv4; + private String accessIPv6; + private Server.Status status; + private Resource image; + private Resource flavor; + private String adminPass; + private String keyName; + private String configDrive; + private Multimap addresses = ImmutableMultimap.of(); + private Map metadata = ImmutableMap.of(); + private String taskState; + private String vmState; + private String powerState; + private String instanceName; + private String hostName; + private String hypervisorName; + private String diskConfig; /** * @see Server#getUuid() */ - public Builder uuid(@Nullable String uuid) { + public T uuid(String uuid) { this.uuid = uuid; - return this; + return self(); } /** * @see Server#getTenantId() */ - public Builder tenantId(String tenantId) { + public T tenantId(String tenantId) { this.tenantId = tenantId; - return this; + return self(); } /** * @see Server#getUserId() */ - public Builder userId(String userId) { + public T userId(String userId) { this.userId = userId; - return this; + return self(); } /** * @see Server#getUpdated() */ - public Builder updated(Date updated) { + public T updated(Date updated) { this.updated = updated; - return this; + return self(); } /** * @see Server#getCreated() */ - public Builder created(Date created) { + public T created(Date created) { this.created = created; - return this; + return self(); } /** * @see Server#getHostId() */ - public Builder hostId(@Nullable String hostId) { + public T hostId(String hostId) { this.hostId = hostId; - return this; + return self(); } /** * @see Server#getAccessIPv4() */ - public Builder accessIPv4(@Nullable String accessIPv4) { + public T accessIPv4(String accessIPv4) { this.accessIPv4 = accessIPv4; - return this; + return self(); } /** * @see Server#getAccessIPv6() */ - public Builder accessIPv6(@Nullable String accessIPv6) { + public T accessIPv6(String accessIPv6) { this.accessIPv6 = accessIPv6; - return this; + return self(); } /** * @see Server#getStatus() */ - public Builder status(Status status) { + public T status(Server.Status status) { this.status = status; - return this; - } - - /** - * @see Server#getConfigDrive() - */ - public Builder configDrive(@Nullable String configDrive) { - this.configDrive = configDrive; - return this; + return self(); } /** * @see Server#getImage() */ - public Builder image(Resource image) { + public T image(Resource image) { this.image = image; - return this; + return self(); } /** - * @see Server#getImage() + * @see Server#getFlavor() */ - public Builder flavor(Resource flavor) { + public T flavor(Resource flavor) { this.flavor = flavor; - return this; + return self(); } - /** - * @see Server#getMetadata() - */ - public Builder metadata(Map metadata) { - this.metadata = ImmutableMap.copyOf(metadata); - return this; - } - - /** - * @see Server#getAddresses() - */ - public Builder addresses(Multimap addresses) { - this.addresses = ImmutableMultimap.copyOf(checkNotNull(addresses, "addresses")); - return this; - } - /** * @see Server#getAdminPass() */ - public Builder adminPass(String adminPass) { + public T adminPass(String adminPass) { this.adminPass = adminPass; - return this; + return self(); } /** * @see Server#getKeyName() */ - public Builder keyName(@Nullable String keyName) { + public T keyName(String keyName) { this.keyName = keyName; - return this; + return self(); + } + + /** + * @see Server#getConfigDrive() + */ + public T configDrive(String configDrive) { + this.configDrive = configDrive; + return self(); + } + + /** + * @see Server#getAddresses() + */ + public T addresses(Multimap addresses) { + this.addresses = addresses; + return self(); + } + + /** + * @see Server#getMetadata() + */ + public T metadata(Map metadata) { + this.metadata = metadata; + return self(); + } + + /** + * @see Server#getTaskState() + */ + public T taskState(String taskState) { + this.taskState = taskState; + return self(); + } + + /** + * @see Server#getVmState() + */ + 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; + return self(); + } + + /** + * @see Server#getDiskConfig() + */ + public T diskConfig(String diskConfig) { + this.diskConfig = diskConfig; + return self(); } - @Override public Server build() { - return new Server(id, name, links, uuid, tenantId, userId, updated, created, hostId, accessIPv4, accessIPv6, - status, configDrive, image, flavor, adminPass, keyName, addresses, metadata); + return new Server(this); } - public Builder fromServer(Server in) { - return fromResource(in).uuid(in.getUuid()).tenantId(in.getTenantId()).userId(in.getUserId()).updated( - in.getUpdated()).created(in.getCreated()).hostId(in.getHostId()).accessIPv4(in.getAccessIPv4()) - .accessIPv6(in.getAccessIPv6()).status(in.getStatus()).configDrive(in.getConfigDrive()).image( - in.getImage()).flavor(in.getFlavor()).adminPass(in.getAdminPass()).keyName(in.getKeyName()) - .addresses(in.getAddresses()).metadata(in.getMetadata()); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder id(String id) { - return Builder.class.cast(super.id(id)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder name(String name) { - return Builder.class.cast(super.name(name)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder links(Set links) { - return Builder.class.cast(super.links(links)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder links(Link... links) { - return Builder.class.cast(super.links(links)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder fromResource(Resource in) { - return Builder.class.cast(super.fromResource(in)); + public T fromServer(Server in) { + return super.fromResource(in) + .uuid(in.getUuid()) + .tenantId(in.getTenantId()) + .userId(in.getUserId()) + .updated(in.getUpdated()) + .created(in.getCreated()) + .hostId(in.getHostId()) + .accessIPv4(in.getAccessIPv4()) + .accessIPv6(in.getAccessIPv6()) + .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()); } } + private static class ConcreteBuilder extends Builder { + @Override + protected ConcreteBuilder self() { + return this; + } + } + protected final String uuid; @SerializedName("tenant_id") protected final String tenantId; @@ -324,28 +369,51 @@ public class Server extends Resource { protected final Map> addresses; protected final Map metadata; - protected Server(String id, String name, Set links, @Nullable String uuid, String tenantId, String userId, - Date updated, Date created, @Nullable String hostId, @Nullable String accessIPv4, - @Nullable String accessIPv6, Status status, @Nullable String configDrive, Resource image, Resource flavor, - String adminPass, @Nullable String keyName, Multimap addresses, - Map metadata) { - super(id, name, links); - this.uuid = uuid; // TODO: see what version this came up in - this.tenantId = checkNotNull(tenantId, "tenantId"); - this.userId = checkNotNull(userId, "userId"); - this.updated = checkNotNull(updated, "updated"); - this.created = checkNotNull(created, "created"); - this.hostId = hostId; - this.accessIPv4 = accessIPv4; - this.accessIPv6 = accessIPv6; - this.status = checkNotNull(status, "status"); - this.configDrive = configDrive; - this.image = checkNotNull(image, "image"); - this.flavor = checkNotNull(flavor, "flavor"); - this.metadata = Maps.newHashMap(metadata); - this.addresses = Multimaps2.toOldSchool(ImmutableMultimap.copyOf(checkNotNull(addresses, "addresses"))); - this.adminPass = adminPass; - this.keyName = keyName; + // 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; + + // 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; + + // Disk Config extension + @SerializedName("OS-DCF:diskConfig") + protected final String diskConfig; + + protected Server(Builder builder) { + super(builder); + this.uuid = builder.uuid; // TODO: see what version this came up in + this.tenantId = checkNotNull(builder.tenantId, "tenantId"); + this.userId = checkNotNull(builder.userId, "userId"); + this.updated = checkNotNull(builder.updated, "updated"); + this.created = checkNotNull(builder.created, "created"); + this.hostId = builder.hostId; + this.accessIPv4 = builder.accessIPv4; + this.accessIPv6 = builder.accessIPv6; + this.status = checkNotNull(builder.status, "status"); + this.configDrive = builder.configDrive; + this.image = checkNotNull(builder.image, "image"); + 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; } /** @@ -438,6 +506,79 @@ public class Server extends Resource { return keyName; } + + /** + * State of task running against this instance (e.g. "suspending") + *

+ * NOTE: This field is only present if the Extended Status extension is installed. + */ + @Nullable + public String getTaskState() { + return this.taskState; + } + + /** + * State of task running against this instance (e.g. "suspending") + *

+ * 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") + *

+ * 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? + *

+ * 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 + *

+ * 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 + *

+ * NOTE: This field is only present if the The Extended Server Attributes API extension is installed. + */ + @Nullable + public String getHypervisorName() { + return this.hypervisorName; + } + + /** + * State of task running against this instance (e.g. "suspending") + *

+ * NOTE: This field is only present if the Disk Config extension is installed. + */ + @Nullable + public String getDiskConfig() { + return this.diskConfig; + } + + // hashCode/equals from super is ok @Override diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerWithSecurityGroups.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerWithSecurityGroups.java index e718305913..bfe1d3ca83 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerWithSecurityGroups.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerWithSecurityGroups.java @@ -18,254 +18,92 @@ */ package org.jclouds.openstack.nova.v1_1.domain; +import static com.google.common.base.Preconditions.checkNotNull; + import java.util.Collections; -import java.util.Date; -import java.util.Map; import java.util.Set; -import org.jclouds.javax.annotation.Nullable; -import org.jclouds.openstack.domain.Link; -import org.jclouds.openstack.domain.Resource; - +import com.google.common.base.Objects; import com.google.common.base.Objects.ToStringHelper; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Multimap; -import com.google.common.collect.Sets; import com.google.gson.annotations.SerializedName; /** * Extended server returned by ServerWithSecurityGroupsClient * * @author Adam Lowe - * @see org.jclouds.openstack.nova.v1_1.extensions.ServerWithSecurityGroupsClient * @see +"http://docs.openstack.org/api/openstack-compute/1.1/content/Get_Server_Details-d1e2623.html" +/> */ public class ServerWithSecurityGroups extends Server { - public static Builder builder() { - return new Builder(); + public static Builder builder() { + return new ConcreteBuilder(); } - public Builder toBuilder() { - return new Builder().fromServerWithSecurityGroups(this); + public Builder toBuilder() { + return new ConcreteBuilder().fromServerWithSecurityGroups(this); } - public static class Builder extends Server.Builder { - private Set securityGroupNames = Sets.newLinkedHashSet(); + public static abstract class Builder> extends Server.Builder { + private Set securityGroupNames = ImmutableSet.of(); /** * @see ServerWithSecurityGroups#getSecurityGroupNames() */ - public Builder securityGroupNames(Set securityGroupNames) { + public T securityGroupNames(Set securityGroupNames) { this.securityGroupNames = securityGroupNames; + return self(); + } + + public ServerWithSecurityGroups build() { + return new ServerWithSecurityGroups(this); + } + + public T fromServerWithSecurityGroups(ServerWithSecurityGroups in) { + return super.fromServer(in).securityGroupNames(in.getSecurityGroupNames()); + } + + } + + private static class ConcreteBuilder extends Builder { + @Override + protected ConcreteBuilder self() { return this; } - - public Builder fromServerWithSecurityGroups(ServerWithSecurityGroups in) { - return fromServer(in).securityGroupNames(in.getSecurityGroupNames()); - } - - @Override - public ServerWithSecurityGroups build() { - return new ServerWithSecurityGroups(id, name, links, uuid, tenantId, userId, updated, created, hostId, accessIPv4, accessIPv6, - status, configDrive, image, flavor, adminPass, keyName, addresses, metadata, securityGroupNames); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder fromResource(Resource in) { - return Builder.class.cast(super.fromResource(in)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder fromServer(Server in) { - return Builder.class.cast(super.fromServer(in)); - } - - - /** - * {@inheritDoc} - */ - @Override - public Builder id(String id) { - return Builder.class.cast(super.id(id)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder name(String name) { - return Builder.class.cast(super.name(name)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder links(Set links) { - return Builder.class.cast(super.links(links)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder links(Link... links) { - return Builder.class.cast(super.links(links)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder uuid(String uuid) { - return Builder.class.cast(super.uuid(uuid)); - } - /** - * {@inheritDoc} - */ - @Override - public Builder tenantId(String tenantId) { - return Builder.class.cast(super.tenantId(tenantId)); - } - /** - * {@inheritDoc} - */ - @Override - public Builder userId(String userId) { - return Builder.class.cast(super.userId(userId)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder updated(Date updated) { - return Builder.class.cast(super.updated(updated)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder created(Date created) { - return Builder.class.cast(super.created(created)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder hostId(String hostId) { - return Builder.class.cast(super.hostId(hostId)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder accessIPv4(String accessIPv4) { - return Builder.class.cast(super.accessIPv4(accessIPv4)); - } - /** - * {@inheritDoc} - */ - @Override - public Builder accessIPv6(String accessIPv6) { - return Builder.class.cast(super.accessIPv6(accessIPv6)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder status(Status status) { - return Builder.class.cast(super.status(status)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder configDrive(String configDrive) { - return Builder.class.cast(super.configDrive(configDrive)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder image(Resource image) { - return Builder.class.cast(super.image(image)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder flavor(Resource flavor) { - return Builder.class.cast(super.flavor(flavor)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder adminPass(String adminPass) { - return Builder.class.cast(super.adminPass(adminPass)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder keyName(String keyName) { - return Builder.class.cast(super.keyName(keyName)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder addresses(Multimap addresses) { - return Builder.class.cast(super.addresses(addresses)); - } - /** - * {@inheritDoc} - */ - @Override - public Builder metadata(Map metadata) { - return Builder.class.cast(super.metadata(metadata)); - } } - @SerializedName("security_groups") + @SerializedName(value="security_groups") private final Set securityGroupNames; - public ServerWithSecurityGroups(String id, String name, Set links, @Nullable String uuid, String tenantId, - String userId, Date updated, Date created, @Nullable String hostId, - @Nullable String accessIPv4, @Nullable String accessIPv6, Status status, - @Nullable String configDrive, Resource image, Resource flavor, String adminPass, - @Nullable String keyName, Multimap addresses, - Map metadata, Set securityGroupNames) { - super(id, name, links, uuid, tenantId, userId, updated, created, hostId, accessIPv4, accessIPv6, status, configDrive, image, flavor, adminPass, keyName, addresses, metadata); - this.securityGroupNames = ImmutableSet.copyOf(securityGroupNames); + protected ServerWithSecurityGroups(Builder builder) { + super(builder); + this.securityGroupNames = ImmutableSet.copyOf(checkNotNull(builder.securityGroupNames, "securityGroupNames")); } + /** + */ public Set getSecurityGroupNames() { - return Collections.unmodifiableSet(securityGroupNames); + return Collections.unmodifiableSet(this.securityGroupNames); + } + + @Override + public int hashCode() { + return Objects.hashCode(securityGroupNames); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + ServerWithSecurityGroups that = ServerWithSecurityGroups.class.cast(obj); + return super.equals(that) && Objects.equal(this.securityGroupNames, that.securityGroupNames); } protected ToStringHelper string() { - return super.string().add("securityGroupNames", securityGroupNames); + return super.string() + .add("securityGroupNames", securityGroupNames); } } \ No newline at end of file diff --git a/common/openstack/src/main/java/org/jclouds/openstack/domain/Resource.java b/common/openstack/src/main/java/org/jclouds/openstack/domain/Resource.java index dd72acd05f..1deb011da9 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/domain/Resource.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/domain/Resource.java @@ -19,10 +19,9 @@ package org.jclouds.openstack.domain; -import static com.google.common.base.Objects.equal; -import static com.google.common.base.Objects.toStringHelper; import static com.google.common.base.Preconditions.checkNotNull; +import java.util.Collections; import java.util.Set; import org.jclouds.javax.annotation.Nullable; @@ -41,73 +40,86 @@ import com.google.common.collect.ImmutableSet; */ public class Resource implements Comparable { - public static Builder builder() { - return new Builder(); + public static Builder builder() { + return new ConcreteBuilder(); } - public Builder toBuilder() { - return builder().fromResource(this); + public Builder toBuilder() { + return new ConcreteBuilder().fromResource(this); } - public static class Builder { - protected String id; - protected String name; - protected Set links = ImmutableSet.of(); + public static abstract class Builder> { + protected abstract T self(); + + private String id; + private String name; + private Set links = ImmutableSet.of(); /** * @see Resource#getId() */ - public Builder id(String id) { - this.id = checkNotNull(id, "id"); - return this; + public T id(String id) { + this.id = id; + return self(); } /** * @see Resource#getName() */ - public Builder name(@Nullable String name) { + public T name(String name) { this.name = name; - return this; + return self(); } /** * @see Resource#getLinks() */ - public Builder links(Link... links) { + public T links(Link... links) { return links(ImmutableSet.copyOf(checkNotNull(links, "links"))); } /** * @see Resource#getLinks() */ - public Builder links(Set links) { - this.links = ImmutableSet.copyOf(checkNotNull(links, "links")); - return this; + public T links(Set links) { + this.links = links; + return self(); } public Resource build() { - return new Resource(id, name, links); + return new Resource(this); } - public Builder fromResource(Resource from) { - return id(from.getId()).name(from.getName()).links(from.getLinks()); + public T fromResource(Resource in) { + return this + .id(in.getId()) + .name(in.getName()) + .links(in.getLinks()) + ; } } - protected final String id; - protected final String name; - protected final Set links; + private static class ConcreteBuilder extends Builder { + @Override + protected ConcreteBuilder self() { + return this; + } + } - public Resource(String id,@Nullable String name, Set links) { - this.id = checkNotNull(id, "id"); - this.name = name; - this.links = ImmutableSet.copyOf(checkNotNull(links, "links")); + private final String id; + private final String name; + private final Set links; + + protected Resource(Builder builder) { + this.id = checkNotNull(builder.id, "id"); + this.name = builder.name; + this.links = ImmutableSet.copyOf(checkNotNull(builder.links, "links")); } /** * When providing an ID, it is assumed that the resource exists in the current OpenStack * deployment - * + * * @return the id of the resource in the current OpenStack deployment */ public String getId() { @@ -126,36 +138,35 @@ public class Resource implements Comparable { * @return the links of the id address allocated to the new server */ public Set getLinks() { - return links; - } - - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } - if (object instanceof Resource) { - final Resource other = Resource.class.cast(object); - return equal(getId(), other.getId()) && equal(name, other.name) && equal(links, other.links); - } else { - return false; - } + return Collections.unmodifiableSet(this.links); } @Override public int hashCode() { - return Objects.hashCode(getId(), name, links); + return Objects.hashCode(id, name, links); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + Resource that = Resource.class.cast(obj); + return Objects.equal(this.getId(), that.getId()) + && Objects.equal(this.name, that.name) + && Objects.equal(this.links, that.links); + } + + protected ToStringHelper string() { + return Objects.toStringHelper("") + .add("id", getId()) + .add("name", name) + .add("links", links); } @Override public String toString() { return string().toString(); } - - protected ToStringHelper string() { - return toStringHelper("").add("id", getId()).add("name", name).add("links", links); - } - @Override public int compareTo(Resource that) { if (that == null) diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/ApiMetadata.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/ApiMetadata.java index 61057d76da..ef4f0bfe48 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/ApiMetadata.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/ApiMetadata.java @@ -25,7 +25,6 @@ import java.util.Date; import java.util.Set; import org.jclouds.javax.annotation.Nullable; -import org.jclouds.openstack.domain.Link; import org.jclouds.openstack.domain.Resource; import com.google.common.base.Objects; @@ -35,111 +34,96 @@ import com.google.common.collect.Sets; import com.google.gson.annotations.SerializedName; /** - * Class ApiMetadata + * @author Adam Lowe */ public class ApiMetadata extends Resource { - public static Builder builder() { - return new Builder(); + public static Builder builder() { + return new ConcreteBuilder(); } - public Builder toBuilder() { - return new Builder().fromApiMetadata(this); + public Builder toBuilder() { + return new ConcreteBuilder().fromApiMetadata(this); } - public static class Builder extends Resource.Builder { + public static abstract class Builder> extends Resource.Builder { private String status; private Date updated; private Set mediaTypes = Sets.newLinkedHashSet(); - public Builder status(String status) { + /** + * @see ApiMetadata#getStatus() + */ + public T status(String status) { this.status = status; - return this; + return self(); } - public Builder updated(Date updated) { + /** + * @see ApiMetadata#getUpdated() + */ + public T updated(Date updated) { this.updated = updated; - return this; + return self(); } - public Builder mediaTypes(Set mediaTypes) { + /** + * @see ApiMetadata#getMediaTypes() + */ + public T mediaTypes(Set mediaTypes) { this.mediaTypes = mediaTypes; - return this; + return self(); } public ApiMetadata build() { - return new ApiMetadata(id, name, links, updated, status, mediaTypes); + return new ApiMetadata(this); } - - public Builder fromApiMetadata(ApiMetadata in) { - return fromResource(in) + + public T fromApiMetadata(ApiMetadata in) { + return super.fromResource(in) .status(in.getStatus()) .updated(in.getUpdated()) .mediaTypes(in.getMediaTypes()); } - /** - * {@inheritDoc} - */ - @Override - public Builder id(String id) { - return Builder.class.cast(super.id(id)); - } + } - /** - * {@inheritDoc} - */ + private static class ConcreteBuilder extends Builder { @Override - public Builder name(String name) { - return Builder.class.cast(super.name(name)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder links(Set links) { - return Builder.class.cast(super.links(links)); - } - - /** - * {@inheritDoc} - */ - @Override - public Builder fromResource(Resource in) { - return Builder.class.cast(super.fromResource(in)); + protected ConcreteBuilder self() { + return this; } } - + + @Nullable private final String status; + @Nullable private final Date updated; - @SerializedName("media-types") + @SerializedName(value="media-types") + @Nullable private final Set mediaTypes; - protected ApiMetadata(String id, String name, Set links, Date updated, String status, Set mediaTypes) { - super(id, name, links); - this.status = status; - this.updated = updated; - this.mediaTypes = ImmutableSet.copyOf(checkNotNull(mediaTypes, "mediaTypes")); + protected ApiMetadata(Builder builder) { + super(builder); + this.status = checkNotNull(builder.status, "status"); + this.updated = checkNotNull(builder.updated, "updated"); + this.mediaTypes = ImmutableSet.copyOf(checkNotNull(builder.mediaTypes, "mediaTypes")); } /** */ - @Nullable public String getStatus() { return this.status; } /** */ - @Nullable public Date getUpdated() { return this.updated; } /** */ - @Nullable public Set getMediaTypes() { return Collections.unmodifiableSet(this.mediaTypes); } @@ -154,10 +138,9 @@ public class ApiMetadata extends Resource { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; ApiMetadata that = ApiMetadata.class.cast(obj); - return Objects.equal(this.status, that.status) + return super.equals(that) && Objects.equal(this.status, that.status) && Objects.equal(this.updated, that.updated) - && Objects.equal(this.mediaTypes, that.mediaTypes) - ; + && Objects.equal(this.mediaTypes, that.mediaTypes); } protected ToStringHelper string() { From 7678c6e776a2c898220dfa98d18d897fab50e805 Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Fri, 4 May 2012 16:21:01 +0100 Subject: [PATCH 031/148] openstack-nova: Adding Admin Actions client to allow extra actions to be performed on servers --- .../openstack/nova/v1_1/NovaAsyncClient.java | 8 + .../openstack/nova/v1_1/NovaClient.java | 9 + .../v1_1/config/NovaRestClientModule.java | 1 + .../nova/v1_1/domain/BackupType.java | 43 ++ .../extensions/AdminActionsAsyncClient.java | 167 ++++++++ .../v1_1/extensions/AdminActionsClient.java | 123 ++++++ .../v1_1/extensions/ExtensionNamespaces.java | 5 +- ...paceEqualsAnyNamespaceInExtensionsSet.java | 2 + .../options/CreateBackupOfServerOptions.java | 107 +++++ .../AdminActionsClientExpectTest.java | 352 +++++++++++++++ .../AdminActionsClientLiveTest.java | 191 +++++++++ .../v1_1/internal/BaseNovaClientLiveTest.java | 12 +- .../test/resources/extension_list_full.json | 402 ++++++++++++------ 13 files changed, 1295 insertions(+), 127 deletions(-) create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/BackupType.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsAsyncClient.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsClient.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateBackupOfServerOptions.java create mode 100644 apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsClientExpectTest.java create mode 100644 apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsClientLiveTest.java diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java index da1c3e16a9..78bec78997 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java @@ -23,6 +23,7 @@ import java.util.Set; import org.jclouds.javax.annotation.Nullable; import org.jclouds.location.Zone; import org.jclouds.location.functions.ZoneToEndpoint; +import org.jclouds.openstack.nova.v1_1.extensions.AdminActionsAsyncClient; import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPAsyncClient; import org.jclouds.openstack.nova.v1_1.extensions.HostAdministrationAsyncClient; import org.jclouds.openstack.nova.v1_1.extensions.KeyPairAsyncClient; @@ -145,4 +146,11 @@ public interface NovaAsyncClient { Optional getServerExtraDataExtensionForZone( @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + /** + * Provides asynchronous access to Server Admin Actions features. + */ + @Delegate + Optional getAdminActionsExtensionForZone( + @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java index 3f1cbe269a..4bd53198f3 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java @@ -25,6 +25,7 @@ import org.jclouds.concurrent.Timeout; import org.jclouds.javax.annotation.Nullable; import org.jclouds.location.Zone; import org.jclouds.location.functions.ZoneToEndpoint; +import org.jclouds.openstack.nova.v1_1.extensions.AdminActionsClient; import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPClient; import org.jclouds.openstack.nova.v1_1.extensions.HostAdministrationClient; import org.jclouds.openstack.nova.v1_1.extensions.KeyPairClient; @@ -148,4 +149,12 @@ public interface NovaClient { Optional getServerExtraDataExtensionForZone( @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + + /** + * Provides asynchronous access to Server Admin Actions features. + */ + @Delegate + Optional getAdminActionsExtensionForZone( + @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java index 7544be56d4..83e727cec8 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java @@ -76,6 +76,7 @@ public class NovaRestClientModule extends RestClientModule metadata = ImmutableMap.of(); + + @Override + public R bindToRequest(R request, Map postParams) { + Map data = Maps.newHashMap(); + data.putAll(postParams); + data.put("metadata", metadata); + return jsonBinder.bindToRequest(request, ImmutableMap.of("createBackup", data)); + } + + @Override + public R bindToRequest(R request, Object toBind) { + throw new IllegalStateException("createBackupOfServer is a POST operation"); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof CreateBackupOfServerOptions)) return false; + final CreateBackupOfServerOptions other = CreateBackupOfServerOptions.class.cast(object); + return equal(metadata, other.metadata); + } + + @Override + public int hashCode() { + return Objects.hashCode(metadata); + } + + protected ToStringHelper string() { + return toStringHelper("").add("metadata", metadata); + } + + @Override + public String toString() { + return string().toString(); + } + + /** @see #getMetadata() */ + public CreateBackupOfServerOptions metadata(Map metadata) { + this.metadata = metadata; + return this; + } + + /** + * Extra image properties to include + */ + public Map getMetadata() { + return metadata; + } + + public static class Builder { + /** + * @see CreateBackupOfServerOptions#getMetadata() + */ + public static CreateBackupOfServerOptions metadata(Map metadata) { + return new CreateBackupOfServerOptions().metadata(metadata); + } + } + +} diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsClientExpectTest.java new file mode 100644 index 0000000000..b7ef6aa329 --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsClientExpectTest.java @@ -0,0 +1,352 @@ +/** + * 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.extensions; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import java.net.URI; + +import javax.ws.rs.core.MediaType; + +import org.jclouds.http.HttpRequest; +import org.jclouds.openstack.nova.v1_1.domain.BackupType; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientExpectTest; +import org.jclouds.openstack.nova.v1_1.options.CreateBackupOfServerOptions; +import org.jclouds.rest.AuthorizationException; +import org.jclouds.rest.ResourceNotFoundException; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMultimap; + +/** + * Tests parsing and guice wiring of AdminActionsClient + * + * @author Adam Lowe + */ +@Test(groups = "unit", testName = "AdminActionsClientExpectTest") +public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { + + public void testSuspend() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "suspend").build(), + standardResponseBuilder(202).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.suspendServer("1")); + } + + public void testSuspendFailsNotFound() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "suspend").build(), + standardResponseBuilder(404).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + assertFalse(client.suspendServer("1")); + } + + @Test(expectedExceptions = AuthorizationException.class) + public void testSuspendFailsNotAuthorized() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "suspend").build(), + standardResponseBuilder(403).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + client.suspendServer("1"); + } + + public void testResume() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "resume").build(), + standardResponseBuilder(202).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.resumeServer("1")); + } + + public void testResumeFailsNotFound() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "resume").build(), + standardResponseBuilder(404).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + assertFalse(client.resumeServer("1")); + } + + @Test(expectedExceptions = AuthorizationException.class) + public void testResumeFailsNotAuthorized() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "resume").build(), + standardResponseBuilder(403).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + client.resumeServer("1"); + } + + public void testLock() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "lock").build(), + standardResponseBuilder(202).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.lockServer("1")); + } + + public void testLockFailsNotFound() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "lock").build(), + standardResponseBuilder(404).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + assertFalse(client.lockServer("1")); + } + + public void testUnlock() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "unlock").build(), + standardResponseBuilder(202).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.unlockServer("1")); + } + + public void testUnlockFailsNotFound() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "unlock").build(), + standardResponseBuilder(404).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + assertFalse(client.unlockServer("1")); + } + + public void testPause() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "pause").build(), + standardResponseBuilder(202).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.pauseServer("1")); + } + + public void testPauseFailsNotFound() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "pause").build(), + standardResponseBuilder(404).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + assertFalse(client.pauseServer("1")); + } + + public void testUnpause() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "unpause").build(), + standardResponseBuilder(202).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.unpauseServer("1")); + } + + public void testUnpauseFailsNotFound() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "unpause").build(), + standardResponseBuilder(404).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + assertFalse(client.unpauseServer("1")); + } + + public void testMigrateServer() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "migrate").build(), + standardResponseBuilder(202).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.migrateServer("1")); + } + + + public void testMigrateServerFailsNotFound() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "migrate").build(), + standardResponseBuilder(404).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + assertFalse(client.migrateServer("1")); + } + + public void testResetNetworkOfServer() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "resetNetwork").build(), + standardResponseBuilder(202).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.resetNetworkOfServer("1")); + } + + public void testResetNetworkOfServerFailsNotFound() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "resetNetwork").build(), + standardResponseBuilder(404).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + assertFalse(client.resetNetworkOfServer("1")); + } + + public void testInjectNetworkInfoIntoServer() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "injectNetworkInfo").build(), + standardResponseBuilder(202).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.injectNetworkInfoIntoServer("1")); + } + + public void testInjectNetworkInfoIntoServerFailsNotFound() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "injectNetworkInfo").build(), + standardResponseBuilder(404).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + assertFalse(client.injectNetworkInfoIntoServer("1")); + } + + public void testBackupServer() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).method("POST") + .payload(payloadFromStringWithContentType("{\"createBackup\":{\"backup_type\":\"weekly\",\"rotation\":\"3\",\"name\":\"mybackup\",\"metadata\":{\"some\":\"data or other\"}}}", MediaType.APPLICATION_JSON)).build(), + standardResponseBuilder(202).headers(ImmutableMultimap.of("Location", "http://172.16.89.149:8774/v2/images/1976b3b3-409a-468d-b16c-a9172c341b46")).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + String imageId = client.createBackupOfServer("1", "mybackup", BackupType.WEEKLY, 3, CreateBackupOfServerOptions.Builder.metadata(ImmutableMap.of("some", "data or other"))); + assertEquals(imageId, "1976b3b3-409a-468d-b16c-a9172c341b46"); + } + + @Test(expectedExceptions = ResourceNotFoundException.class) + public void testBackupServerFailNotFound() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).method("POST") + .payload(payloadFromStringWithContentType("{\"createBackup\":{\"backup_type\":\"weekly\",\"rotation\":\"3\",\"name\":\"mybackup\",\"metadata\":{\"some\":\"data or other\"}}}", MediaType.APPLICATION_JSON)).build(), + standardResponseBuilder(404).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + client.createBackupOfServer("1", "mybackup", BackupType.WEEKLY, 3, CreateBackupOfServerOptions.Builder.metadata(ImmutableMap.of("some", "data or other"))); + } + + public void testLiveMigrateServer() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "GONNAOVERWRITE") + .payload(payloadFromStringWithContentType("{\"os-migrateLive\":{\"host\":\"bighost\",\"block_migration\":\"true\",\"disk_over_commit\":\"false\"}}", MediaType.APPLICATION_JSON)).build(), + standardResponseBuilder(202).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.liveMigrateServer("1", "bighost", true, false)); + } + + public void testLiveMigrateServerFailsNotFound() { + URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + AdminActionsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardActionRequestBuilderVoidResponse(endpoint, "GONNAOVERWRITE") + .payload(payloadFromStringWithContentType("{\"os-migrateLive\":{\"host\":\"bighost\",\"block_migration\":\"true\",\"disk_over_commit\":\"false\"}}", MediaType.APPLICATION_JSON)).build(), + standardResponseBuilder(404).build() + ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); + + assertFalse(client.liveMigrateServer("1", "bighost", true, false)); + } + + protected HttpRequest.Builder standardActionRequestBuilderVoidResponse(URI endpoint, String actionName) { + return HttpRequest.builder().method("POST") + .headers(ImmutableMultimap.of("X-Auth-Token", authToken)) + .payload(payloadFromStringWithContentType("{\"" + actionName + "\":null}", MediaType.APPLICATION_JSON)) + .endpoint(endpoint); + } + +} diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsClientLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsClientLiveTest.java new file mode 100644 index 0000000000..547675421e --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsClientLiveTest.java @@ -0,0 +1,191 @@ +/** + * 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.extensions; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; +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.features.ExtensionClient; +import org.jclouds.openstack.nova.v1_1.features.ImageClient; +import org.jclouds.openstack.nova.v1_1.features.ServerClient; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientLiveTest; +import org.jclouds.openstack.nova.v1_1.options.CreateBackupOfServerOptions; +import org.testng.annotations.AfterGroups; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeGroups; +import org.testng.annotations.Test; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; + +/** + * Tests behavior of HostAdministrationClient + * + * TODO test migration methods + * + * @author Adam Lowe + */ +@Test(groups = "live", testName = "AdminActionsClientLiveTest", singleThreaded = true) +public class AdminActionsClientLiveTest extends BaseNovaClientLiveTest { + private ImageClient imageClient; + private ServerClient serverClient; + private ExtensionClient extensionClient; + private Optional clientOption; + private String zone; + + private String testServerId; + private String backupImageId; + + @BeforeGroups(groups = {"integration", "live"}) + @Override + public void setupContext() { + super.setupContext(); + zone = Iterables.getLast(novaContext.getApi().getConfiguredZones(), "nova"); + serverClient = novaContext.getApi().getServerClientForZone(zone); + extensionClient = novaContext.getApi().getExtensionClientForZone(zone); + imageClient = novaContext.getApi().getImageClientForZone(zone); + clientOption = novaContext.getApi().getAdminActionsExtensionForZone(zone); + if (clientOption.isPresent()) { + testServerId = createServerInZone(zone).getId(); + } + } + + @AfterGroups(groups = "live", alwaysRun = true) + @Override + protected void tearDown() { + if (clientOption.isPresent()) { + if (testServerId != null) { + assertTrue(novaContext.getApi().getServerClientForZone(zone).deleteServer(testServerId)); + } + if (backupImageId != null) { + imageClient.deleteImage(backupImageId); + } + } + super.tearDown(); + } + + @AfterMethod(alwaysRun = true) + public void ensureServerIsActiveAgain() { + blockUntilServerInState(testServerId, serverClient, Server.Status.ACTIVE); + } + + public void testSuspendAndResume() { + if (clientOption.isPresent()) { + AdminActionsClient client = clientOption.get(); + + // Suspend-resume + try { + client.resumeServer(testServerId); + fail("Resumed an active server!"); + } catch (HttpResponseException e) { + } + assertTrue(client.suspendServer(testServerId)); + blockUntilServerInState(testServerId, serverClient, Server.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); + try { + client.resumeServer(testServerId); + fail("Resumed an already resumed server!"); + } catch (HttpResponseException e) { + } + } + } + + public void testLockAndUnlock() { + if (clientOption.isPresent()) { + AdminActionsClient client = clientOption.get(); + + // TODO should we be able to double-lock (as it were) + assertTrue(client.unlockServer(testServerId)); + assertTrue(client.unlockServer(testServerId)); + assertTrue(client.lockServer(testServerId)); + assertTrue(client.lockServer(testServerId)); + assertTrue(client.unlockServer(testServerId)); + assertTrue(client.unlockServer(testServerId)); + } + } + + public void testResetNetworkAndInjectNetworkInfo() { + if (clientOption.isPresent()) { + AdminActionsClient client = clientOption.get(); + assertTrue(client.resetNetworkOfServer(testServerId)); + assertTrue(client.injectNetworkInfoIntoServer(testServerId)); + } + } + + @Test + public void testPauseAndUnpause() { + if (clientOption.isPresent()) { + AdminActionsClient client = clientOption.get(); + + // Unlock and lock (double-checking error contitions too) + try { + client.unpauseServer(testServerId); + fail("Unpaused active server!"); + } catch (HttpResponseException e) { + } + assertTrue(client.pauseServer(testServerId)); + blockUntilServerInState(testServerId, serverClient, Server.Status.PAUSED); + try { + client.pauseServer(testServerId); + fail("paused a paused server!"); + } catch (HttpResponseException e) { + } + assertTrue(client.unpauseServer(testServerId)); + blockUntilServerInState(testServerId, serverClient, Server.Status.ACTIVE); + try { + client.unpauseServer(testServerId); + fail("Unpaused a server we just unpaused!"); + } catch (HttpResponseException e) { + } + } + } + + @Test + public void testCreateBackupOfServer() throws InterruptedException { + if (clientOption.isPresent()) { + backupImageId = clientOption.get().createBackupOfServer(testServerId, "jclouds-test-backup", BackupType.DAILY, 0, + CreateBackupOfServerOptions.Builder.metadata(ImmutableMap.of("test", "metadata"))); + + assertNotNull(backupImageId); + + // If we don't have extended task status, we'll have to wait here! + if (extensionClient.getExtensionByAlias("OS-EXT-STS") == null) { + Thread.sleep(30000); + } + + blockUntilServerInState(testServerId, serverClient, Server.Status.ACTIVE); + + Image backupImage = imageClient.getImage(backupImageId); + assertEquals(backupImage.getId(), backupImageId); + } + } +} diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaClientLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaClientLiveTest.java index 99151fefaa..f5b9b025a5 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaClientLiveTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaClientLiveTest.java @@ -83,15 +83,19 @@ 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)); - blockUntilServerActive(server.getId(), serverClient); + blockUntilServerInState(server.getId(), serverClient, Status.ACTIVE); return server; } - private void blockUntilServerActive(String serverId, ServerClient client) { + /** + * Will block until the requested server is in the correct state, if Extended Server Status extension is loaded + * this will continue to block while any task is in progress. + */ + protected void blockUntilServerInState(String serverId, ServerClient client, Status status) { Server currentDetails = null; - for (currentDetails = client.getServer(serverId); currentDetails.getStatus() != Status.ACTIVE; currentDetails = client + for (currentDetails = client.getServer(serverId); currentDetails.getStatus() != status || currentDetails.getTaskState() != null; currentDetails = client .getServer(serverId)) { - System.out.printf("blocking on status active%n%s%n", currentDetails); + System.out.printf("blocking on status %s%n%s%n", status, currentDetails); try { Thread.sleep(5 * 1000); } catch (InterruptedException e) { diff --git a/apis/openstack-nova/src/test/resources/extension_list_full.json b/apis/openstack-nova/src/test/resources/extension_list_full.json index 298a2f2f4a..490bebc0fe 100644 --- a/apis/openstack-nova/src/test/resources/extension_list_full.json +++ b/apis/openstack-nova/src/test/resources/extension_list_full.json @@ -1,123 +1,281 @@ +{"extensions": [ { - "extensions": [{ - "updated": "2011-06-09T00:00:00+00:00", - "name": "Multinic", - "links": [], - "namespace": "https://docs.openstack.org/ext/multinic/api/v1.1", - "alias": "NMN", - "description": "Multiple network support" - }, { - "updated": "2011-06-29T00:00:00+00:00", - "name": "Hosts", - "links": [], - "namespace": "https://docs.openstack.org/ext/hosts/api/v1.1", - "alias": "os-hosts", - "description": "Host administration" - }, { - "updated": "2011-03-25T00:00:00+00:00", - "name": "Volumes", - "links": [], - "namespace": "https://docs.openstack.org/ext/volumes/api/v1.1", - "alias": "os-volumes", - "description": "Volumes support" - }, { - "updated": "2011-05-25 16:12:21.656723", - "name": "Admin Controller", - "links": [], - "namespace": "https:TODO/", - "alias": "ADMIN", - "description": "The Admin API Extension" - }, { - "updated": "2011-08-08T00:00:00+00:00", - "name": "Quotas", - "links": [], - "namespace": "https://docs.openstack.org/ext/quotas-sets/api/v1.1", - "alias": "os-quota-sets", - "description": "Quotas management support" - }, { - "updated": "2011-08-24T00:00:00+00:00", - "name": "VolumeTypes", - "links": [], - "namespace": "https://docs.openstack.org/ext/volume_types/api/v1.1", - "alias": "os-volume-types", - "description": "Volume types support" - }, { - "updated": "2011-06-23T00:00:00+00:00", - "name": "FlavorExtraSpecs", - "links": [], - "namespace": "https://docs.openstack.org/ext/flavor_extra_specs/api/v1.1", - "alias": "os-flavor-extra-specs", - "description": "Instance type (flavor) extra specs" - }, { - "updated": "2011-09-14T00:00:00+00:00", - "name": "FlavorExtraData", - "links": [], - "namespace": "https://docs.openstack.org/ext/flavor_extra_data/api/v1.1", - "alias": "os-flavor-extra-data", - "description": "Provide additional data for flavors" - }, { - "updated": "2011-08-17T00:00:00+00:00", - "name": "VirtualInterfaces", - "links": [], - "namespace": "https://docs.openstack.org/ext/virtual_interfaces/api/v1.1", - "alias": "virtual_interfaces", - "description": "Virtual interface support" - }, { - "updated": "2011-07-19T00:00:00+00:00", - "name": "Createserverext", - "links": [], - "namespace": "https://docs.openstack.org/ext/createserverext/api/v1.1", - "alias": "os-create-server-ext", - "description": "Extended support to the Create Server v1.1 API" - }, { - "updated": "2011-08-08T00:00:00+00:00", - "name": "Keypairs", - "links": [], - "namespace": "https://docs.openstack.org/ext/keypairs/api/v1.1", - "alias": "os-keypairs", - "description": "Keypair Support" - }, { - "updated": "2011-08-25T00:00:00+00:00", - "name": "VSAs", - "links": [], - "namespace": "https://docs.openstack.org/ext/vsa/api/v1.1", - "alias": "zadr-vsa", - "description": "Virtual Storage Arrays support" - }, { - "updated": "2011-08-19T00:00:00+00:00", - "name": "SimpleTenantUsage", - "links": [], - "namespace": "https://docs.openstack.org/ext/os-simple-tenant-usage/api/v1.1", - "alias": "os-simple-tenant-usage", - "description": "Simple tenant usage extension" - }, { - "updated": "2011-08-18T00:00:00+00:00", - "name": "Rescue", - "links": [], - "namespace": "https://docs.openstack.org/ext/rescue/api/v1.1", - "alias": "os-rescue", - "description": "Instance rescue mode" - }, { - "updated": "2011-07-21T00:00:00+00:00", - "name": "SecurityGroups", - "links": [], - "namespace": "https://docs.openstack.org/ext/securitygroups/api/v1.1", - "alias": "security_groups", - "description": "Security group support" - }, { - "updated": "2011-06-16T00:00:00+00:00", - "name": "Floating_ips", - "links": [], - "namespace": "https://docs.openstack.org/ext/floating_ips/api/v1.1", - "alias": "os-floating-ips", - "description": "Floating IPs support" - }, { - "updated": "2011-06-16T00:00:00+00:00", - "name": "Users", - "links": [], - "namespace": "http://docs.openstack.org/compute/ext/users/api/v1.1", - "alias": "os-users", - "description": "Users support" - } - ] -} \ No newline at end of file + "updated": "2011-09-27T00:00:00+00:00", + "name": "DiskConfig", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/disk_config/api/v1.1", + "alias": "OS-DCF", + "description": "Disk Management Extension" +}, +{ + "updated": "2011-06-29T00:00:00+00:00", + "name": "Hosts", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/hosts/api/v1.1", + "alias": "os-hosts", + "description": "Admin-only host administration" +}, +{ + "updated": "2011-07-19T00:00:00+00:00", + "name": "SchedulerHints", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/scheduler-hints/api/v2", + "alias": "os-scheduler-hints", + "description": "Pass arbitrary key/value pairs to the scheduler" +}, +{ + "updated": "2011-08-08T00:00:00+00:00", + "name": "Quotas", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/quotas-sets/api/v1.1", + "alias": "os-quota-sets", + "description": "Quotas management support" +}, +{ + "updated": "2011-12-23T00:00:00+00:00", + "name": "Floating_ip_dns", + "links": [], + "namespace": "http://docs.openstack.org/ext/floating_ip_dns/api/v1.1", + "alias": "os-floating-ip-dns", + "description": "Floating IP DNS support" +}, +{ + "updated": "2011-09-14T00:00:00+00:00", + "name": "FlavorExtraData", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/flavor_extra_data/api/v1.1", + "alias": "OS-FLV-EXT-DATA", + "description": "Provide additional data for flavors" +}, +{ + "updated": "2011-06-23T00:00:00+00:00", + "name": "FlavorExtraSpecs", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/flavor_extra_specs/api/v1.1", + "alias": "os-flavor-extra-specs", + "description": "Instance type (flavor) extra specs" +}, +{ + "updated": "2011-08-17T00:00:00+00:00", + "name": "VirtualInterfaces", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/virtual_interfaces/api/v1.1", + "alias": "virtual_interfaces", + "description": "Virtual interface support" +}, +{ + "updated": "2011-12-23T00:00:00+00:00", + "name": "Accounts", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/accounts/api/v1.1", + "alias": "os-accounts", + "description": "Admin-only access to accounts" +}, +{ + "updated": "2011-03-25T00:00:00+00:00", + "name": "Volumes", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/volumes/api/v1.1", + "alias": "os-volumes", + "description": "Volumes support" +}, +{ + "updated": "2011-11-03T00:00:00+00:00", + "name": "ExtendedStatus", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/extended_status/api/v1.1", + "alias": "OS-EXT-STS", + "description": "Extended Status support" +}, +{ + "updated": "2011-12-23T00:00:00+00:00", + "name": "Consoles", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/os-consoles/api/v2", + "alias": "os-consoles", + "description": "Interactive Console support." +}, +{ + "updated": "2011-07-21T00:00:00+00:00", + "name": "SecurityGroups", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/securitygroups/api/v1.1", + "alias": "security_groups", + "description": "Security group support" +}, +{ + "updated": "2012-01-12T00:00:00+00:00", + "name": "Aggregates", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/aggregates/api/v1.1", + "alias": "os-aggregates", + "description": "Admin-only aggregate administration" +}, +{ + "updated": "2011-07-19T00:00:00+00:00", + "name": "Createserverext", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/createserverext/api/v1.1", + "alias": "os-create-server-ext", + "description": "Extended support to the Create Server v1.1 API" +}, +{ + "updated": "2011-09-01T00:00:00+00:00", + "name": "DeferredDelete", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/deferred-delete/api/v1.1", + "alias": "os-deferred-delete", + "description": "Instance deferred delete" +}, +{ + "updated": "2011-12-21T00:00:00+00:00", + "name": "ServerDiagnostics", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/server-diagnostics/api/v1.1", + "alias": "os-server-diagnostics", + "description": "Allow Admins to view server diagnostics through server action" +}, +{ + "updated": "2011-12-23T00:00:00+00:00", + "name": "Networks", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/networks/api/v1.1", + "alias": "os-networks", + "description": "Admin-only Network Management Extension" +}, +{ + "updated": "2011-11-03T00:00:00+00:00", + "name": "ExtendedServerAttributes", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/extended_status/api/v1.1", + "alias": "OS-EXT-SRV-ATTR", + "description": "Extended Server Attributes support." +}, +{ + "updated": "2011-08-08T00:00:00+00:00", + "name": "Keypairs", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/keypairs/api/v1.1", + "alias": "os-keypairs", + "description": "Keypair Support" +}, +{ + "updated": "2011-08-24T00:00:00+00:00", + "name": "VolumeTypes", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/volume_types/api/v1.1", + "alias": "os-volume-types", + "description": "Volume types support" +}, +{ + "updated": "2011-08-19T00:00:00+00:00", + "name": "SimpleTenantUsage", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/os-simple-tenant-usage/api/v1.1", + "alias": "os-simple-tenant-usage", + "description": "Simple tenant usage extension" +}, +{ + "updated": "2012-01-04T00:00:00+00:00", + "name": "Floating_ip_pools", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/floating_ip_pools/api/v1.1", + "alias": "os-floating-ip-pools", + "description": "Floating IPs support" +}, +{ + "updated": "2012-01-23T00:00:00+00:00", + "name": "ServerStartStop", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/servers/api/v1.1", + "alias": "os-server-start-stop", + "description": "Start/Stop instance compute API support" +}, +{ + "updated": "2012-03-12T00:00:00+00:00", + "name": "QuotaClasses", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/quota-classes-sets/api/v1.1", + "alias": "os-quota-class-sets", + "description": "Quota classes management support" +}, +{ + "updated": "2012-01-19T00:00:00+00:00", + "name": "Certificates", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/certificates/api/v1.1", + "alias": "os-certificates", + "description": "Certificates support" +}, +{ + "updated": "2011-08-18T00:00:00+00:00", + "name": "Rescue", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/rescue/api/v1.1", + "alias": "os-rescue", + "description": "Instance rescue mode" +}, +{ + "updated": "2012-01-19T00:00:00+00:00", + "name": "FlavorManage", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/flavor_manage/api/v1.1", + "alias": "os-flavor-manage", + "description": "\n Flavor create/delete API support\n " +}, +{ + "updated": "2011-12-16T00:00:00+00:00", + "name": "Cloudpipe", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/cloudpipe/api/v1.1", + "alias": "os-cloudpipe", + "description": "Adds actions to create cloudpipe instances.\n\n When running with the Vlan network mode, you need a mechanism to route\n from the public Internet to your vlans. This mechanism is known as a\n cloudpipe.\n\n At the time of creating this class, only OpenVPN is supported. Support for\n a SSH Bastion host is forthcoming.\n " +}, +{ + "updated": "2011-06-09T00:00:00+00:00", + "name": "Multinic", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/multinic/api/v1.1", + "alias": "NMN", + "description": "Multiple network support" +}, +{ + "updated": "2011-08-08T00:00:00+00:00", + "name": "Users", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/users/api/v1.1", + "alias": "os-users", + "description": "Allow admins to acces user information" +}, +{ + "updated": "2011-09-20T00:00:00+00:00", + "name": "AdminActions", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/admin-actions/api/v1.1", + "alias": "os-admin-actions", + "description": "Enable admin-only server actions\n\n Actions include: pause,unpause, suspend, resume, migrate,\n resetNetwork, injectNetworkInfo, lock, unlock, createBackup\n " +}, +{ + "updated": "2011-12-21T00:00:00+00:00", + "name": "ServerActionList", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/server-actions-list/api/v1.1", + "alias": "os-server-action-list", + "description": "Allow Admins to view pending server actions" +}, +{ + "updated": "2011-12-08T00:00:00+00:00", + "name": "Console_output", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/os-console-output/api/v2", + "alias": "os-console-output", + "description": "Console log output support, with tailing ability." +}, +{ + "updated": "2011-06-16T00:00:00+00:00", + "name": "Floating_ips", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/floating_ips/api/v1.1", + "alias": "os-floating-ips", + "description": "Floating IPs support"} +]} \ No newline at end of file From 33b4a2d25321c13476f32dd5a65b516b12e98c12 Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Fri, 4 May 2012 16:44:32 +0100 Subject: [PATCH 032/148] openstack-nova: Wiring AdminActions extension into compute service to implement suspend and resume --- .../v1_1/compute/NovaComputeServiceAdapter.java | 12 ++++++++++-- .../v1_1/compute/NovaComputeServiceLiveTest.java | 14 ++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceAdapter.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceAdapter.java index 5dc476a9d0..1bc2e8a52e 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceAdapter.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceAdapter.java @@ -219,12 +219,20 @@ public class NovaComputeServiceAdapter implements @Override public void resumeNode(String id) { - throw new UnsupportedOperationException("suspend not supported"); + ZoneAndId zoneAndId = ZoneAndId.fromSlashEncoded(id); + if (novaClient.getAdminActionsExtensionForZone(zoneAndId.getZone()).isPresent()) { + novaClient.getAdminActionsExtensionForZone(zoneAndId.getZone()).get().resumeServer(zoneAndId.getId()); + } + throw new UnsupportedOperationException("resume requires installation of the Admin Actions extension"); } @Override public void suspendNode(String id) { - throw new UnsupportedOperationException("suspend not supported"); + ZoneAndId zoneAndId = ZoneAndId.fromSlashEncoded(id); + if (novaClient.getAdminActionsExtensionForZone(zoneAndId.getZone()).isPresent()) { + novaClient.getAdminActionsExtensionForZone(zoneAndId.getZone()).get().suspendServer(zoneAndId.getId()); + } + throw new UnsupportedOperationException("suspend requires installation of the Admin Actions extension"); } } diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceLiveTest.java index df697bc88a..8dca31a1c3 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceLiveTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceLiveTest.java @@ -1,10 +1,13 @@ package org.jclouds.openstack.nova.v1_1.compute; +import static java.util.logging.Logger.getAnonymousLogger; + import java.util.Properties; import org.jclouds.compute.internal.BaseComputeServiceLiveTest; import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties; import org.jclouds.openstack.nova.v1_1.config.NovaProperties; +import org.jclouds.rest.AuthorizationException; import org.jclouds.sshj.config.SshjSshClientModule; import org.testng.annotations.Test; @@ -31,9 +34,16 @@ public class NovaComputeServiceLiveTest extends BaseComputeServiceLiveTest { // start call is blocking anyway. } - @Test(enabled = true, dependsOnMethods = "testReboot", expectedExceptions = UnsupportedOperationException.class) + @Test(enabled = true, dependsOnMethods = "testReboot") public void testSuspendResume() throws Exception { - super.testSuspendResume(); + try { + // may fail because of lack of AdminActions extension or non-admin user, so log and continue + super.testSuspendResume(); + } catch (AuthorizationException e) { + getAnonymousLogger().info("testSuspendResume() threw, probably due to lack of privileges: " + e.getMessage()); + } catch (UnsupportedOperationException e) { + getAnonymousLogger().info("testSuspendResume() threw, probably due to unavailable AdminActions extension: " + e.getMessage()); + } } @Test(enabled = true, dependsOnMethods = "testSuspendResume") From 653d5ccc4be4208ca662681edfaea528f52b75f2 Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Fri, 4 May 2012 17:07:14 +0100 Subject: [PATCH 033/148] openstack-nova: Adjusting names of ServerWithSecurityGroups related calls --- .../org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java | 2 +- .../java/org/jclouds/openstack/nova/v1_1/NovaClient.java | 7 ++----- .../java/org/jclouds/openstack/nova/v1_1/domain/Image.java | 2 +- .../extensions/ServerWithSecurityGroupsAsyncClient.java | 5 +++-- .../v1_1/extensions/ServerWithSecurityGroupsClient.java | 7 +++++-- .../ServerWithSecurityGroupsClientExpectTest.java | 6 +++--- .../extensions/ServerWithSecurityGroupsClientLiveTest.java | 2 +- 7 files changed, 16 insertions(+), 15 deletions(-) diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java index 78bec78997..7a16d7c194 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java @@ -143,7 +143,7 @@ public interface NovaAsyncClient { * Provides asynchronous access to Server Extra Data features. */ @Delegate - Optional getServerExtraDataExtensionForZone( + Optional getServerWithSecurityGroupsExtensionForZone( @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); /** diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java index 4bd53198f3..75d3173bbd 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java @@ -126,7 +126,6 @@ public interface NovaClient { Optional getSimpleTenantUsageExtensionForZone( @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); - /** * Provides synchronous access to Volume features. */ @@ -141,17 +140,15 @@ public interface NovaClient { Optional getVirtualInterfaceExtensionForZone( @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); - /** * Provides synchronous access to Server Extra Data features. */ @Delegate - Optional getServerExtraDataExtensionForZone( + Optional getServerWithSecurityGroupsExtensionForZone( @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); - /** - * Provides asynchronous access to Server Admin Actions features. + * Provides synchronous access to Server Admin Actions features. */ @Delegate Optional getAdminActionsExtensionForZone( diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Image.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Image.java index 9670ba096e..28196bf454 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Image.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Image.java @@ -160,7 +160,7 @@ public class Image extends Resource { /** * @see Image#getMetadata() */ - public T metadata(Map metadata) { + public T metadata(Map metadata) { this.metadata = metadata; return self(); } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsAsyncClient.java index 3277b671da..423d7ec97c 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsAsyncClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsAsyncClient.java @@ -38,10 +38,11 @@ import com.google.common.util.concurrent.ListenableFuture; /** * Provides synchronous access to Servers with Security Groups. - * + * + * @author Adam Lowe * @see org.jclouds.openstack.nova.v1_1.features.ServerAsyncClient * @see ServerWithSecurityGroupsClient - * @author Adam Lowe + * @see */ @Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.CREATESERVEREXT) @SkipEncoding({'/', '='}) diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClient.java index 84c8d87b4e..3fab8ec83b 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClient.java @@ -26,13 +26,16 @@ import org.jclouds.openstack.services.Extension; import org.jclouds.openstack.services.ServiceType; /** - * Provides synchronous access to Server details including security groups. + * Provides synchronous access to Server details including security group, referred to as the CREATESERVEREXT extension + * in the nova documentation *

- * NOTE: the equivalent to listServersInDetail() doesn't work, so not extending ServerClient at this time. + * NOTE: the equivalent to listServersInDetail() isn't available at the other end, so not extending ServerClient at this + * time. * * @author Adam Lowe * @see org.jclouds.openstack.nova.v1_1.features.ServerClient * @see ServerWithSecurityGroupsAsyncClient + * @see */ @Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.CREATESERVEREXT) @Timeout(duration = 180, timeUnit = TimeUnit.SECONDS) diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClientExpectTest.java index 6ad9738cba..237526a83a 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClientExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClientExpectTest.java @@ -30,7 +30,7 @@ import org.testng.annotations.Test; import com.google.common.collect.ImmutableSet; /** - * Tests parsing and guice wiring of ServerExtraDataClient + * Tests parsing and guice wiring of ServerWithSecurityGroupsClient * * @author Adam Lowe */ @@ -44,7 +44,7 @@ public class ServerWithSecurityGroupsClientExpectTest extends BaseNovaClientExpe responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, standardRequestBuilder(endpoint).build(), standardResponseBuilder(200).payload(payloadFromResource("/server_with_security_groups.json")).build() - ).getServerExtraDataExtensionForZone("az-1.region-a.geo-1").get(); + ).getServerWithSecurityGroupsExtensionForZone("az-1.region-a.geo-1").get(); ServerWithSecurityGroups server = client.getServer("8d0a6ca5-8849-4b3d-b86e-f24c92490ebb"); assertEquals(server.getId(), "8d0a6ca5-8849-4b3d-b86e-f24c92490ebb"); @@ -58,7 +58,7 @@ public class ServerWithSecurityGroupsClientExpectTest extends BaseNovaClientExpe responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, standardRequestBuilder(endpoint).build(), standardResponseBuilder(404).build() - ).getServerExtraDataExtensionForZone("az-1.region-a.geo-1").get(); + ).getServerWithSecurityGroupsExtensionForZone("az-1.region-a.geo-1").get(); assertNull(client.getServer("8d0a6ca5-8849-4b3d-b86e-f24c92490ebb")); } } \ No newline at end of file diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClientLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClientLiveTest.java index 351a08ee45..75a4227fdc 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClientLiveTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClientLiveTest.java @@ -50,7 +50,7 @@ public class ServerWithSecurityGroupsClientLiveTest extends BaseNovaClientLiveTe super.setupContext(); zone = Iterables.getLast(novaContext.getApi().getConfiguredZones(), "nova"); serverClient = novaContext.getApi().getServerClientForZone(zone); - clientOption = novaContext.getApi().getServerExtraDataExtensionForZone(zone); + clientOption = novaContext.getApi().getServerWithSecurityGroupsExtensionForZone(zone); } public void testGetServer() { From 596cf4e044d651f42bba908ab86c7283aef0b412 Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Sat, 5 May 2012 18:54:51 -0700 Subject: [PATCH 034/148] Removed the scheduler component from heroku-tweetstore because the platform can provide it --- .../jclouds/demo/paas/PlatformServices.java | 9 +- .../config/PlatformServicesInitializer.java | 4 +- .../service/scheduler/HttpRequestJob.java | 69 --- .../paas/service/scheduler/Scheduler.java | 41 -- ...nlessXmlSchedulingDataProcessorPlugin.java | 401 ------------------ .../controller/EnqueueStoresController.java | 3 +- .../src/main/resources/jobs.xml | 29 -- .../src/main/resources/quartz.properties | 28 -- .../src/main/webapp/WEB-INF/web.xml | 9 - 9 files changed, 3 insertions(+), 590 deletions(-) delete mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/HttpRequestJob.java delete mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/Scheduler.java delete mode 100644 demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/quartz/plugins/TransactionlessXmlSchedulingDataProcessorPlugin.java delete mode 100644 demos/tweetstore/heroku-tweetstore/src/main/resources/jobs.xml delete mode 100644 demos/tweetstore/heroku-tweetstore/src/main/resources/quartz.properties diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/PlatformServices.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/PlatformServices.java index 0997005157..c768b42611 100644 --- a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/PlatformServices.java +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/PlatformServices.java @@ -25,7 +25,6 @@ import java.util.Map; import javax.servlet.ServletContext; -import org.jclouds.demo.paas.service.scheduler.Scheduler; import org.jclouds.demo.paas.service.taskqueue.TaskQueue; import org.jclouds.javax.annotation.Nullable; @@ -36,12 +35,10 @@ import com.google.common.collect.ImmutableMap; */ public class PlatformServices { protected final String baseUrl; - protected final Scheduler scheduler; private ImmutableMap taskQueues; - public PlatformServices(String baseUrl, Scheduler scheduler, Map taskQueues) { + public PlatformServices(String baseUrl, Map taskQueues) { this.baseUrl = baseUrl; - this.scheduler = scheduler; this.taskQueues = ImmutableMap.copyOf(taskQueues); } @@ -49,10 +46,6 @@ public class PlatformServices { return baseUrl; } - public Scheduler getScheduler() { - return scheduler; - } - public @Nullable TaskQueue getTaskQueue(String name) { return taskQueues.get(name); } diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/config/PlatformServicesInitializer.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/config/PlatformServicesInitializer.java index 5b546c2593..e0c3eec312 100644 --- a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/config/PlatformServicesInitializer.java +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/config/PlatformServicesInitializer.java @@ -28,7 +28,6 @@ import javax.servlet.ServletContextListener; import org.jclouds.concurrent.config.ExecutorServiceModule; import org.jclouds.demo.paas.PlatformServices; -import org.jclouds.demo.paas.service.scheduler.Scheduler; import org.jclouds.demo.paas.service.taskqueue.TaskQueue; import org.jclouds.http.HttpCommandExecutorService; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; @@ -54,8 +53,7 @@ public class PlatformServicesInitializer implements ServletContextListener { protected static PlatformServices createServices(ServletContext context) { HttpCommandExecutorService httpClient = createHttpClient(context); - return new PlatformServices(getBaseUrl(context), new Scheduler(httpClient), - createTaskQueues(httpClient)); + return new PlatformServices(getBaseUrl(context), createTaskQueues(httpClient)); } protected static HttpCommandExecutorService createHttpClient( diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/HttpRequestJob.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/HttpRequestJob.java deleted file mode 100644 index 902f5fe356..0000000000 --- a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/HttpRequestJob.java +++ /dev/null @@ -1,69 +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.demo.paas.service.scheduler; - -import static com.google.common.base.Preconditions.checkNotNull; - -import java.net.URI; - -import javax.servlet.ServletContext; - -import org.jclouds.demo.paas.PlatformServices; -import org.jclouds.demo.paas.RunnableHttpRequest; -import org.jclouds.http.HttpRequest; -import org.quartz.Job; -import org.quartz.JobExecutionContext; -import org.quartz.JobExecutionException; -import org.quartz.SchedulerException; - -/** - * @author Andrew Phillips - */ -public class HttpRequestJob implements Job { - protected static final String URL_ATTRIBUTE_NAME = "url"; - - // keep in sync with "quartz:scheduler-context-servlet-context-key" param in web.xml - protected static final String SERVLET_CONTEXT_KEY = "servlet-context"; - - @Override - public void execute(JobExecutionContext context) throws JobExecutionException { - PlatformServices platform = JobContexts.getPlatform(context); - RunnableHttpRequest request = platform.getScheduler().getHttpRequestFactory().create( - HttpRequest.builder() - .endpoint(JobContexts.getTargetUrl(platform.getBaseUrl(), context)) - .method("GET").build()); - request.run(); - } - - private static class JobContexts { - private static URI getTargetUrl(String baseUrl, JobExecutionContext context) { - return URI.create(baseUrl + (String) checkNotNull( - context.getMergedJobDataMap().get(URL_ATTRIBUTE_NAME), URL_ATTRIBUTE_NAME)); - } - - private static PlatformServices getPlatform(JobExecutionContext jobContext) throws JobExecutionException { - try { - return PlatformServices.get((ServletContext) checkNotNull( - jobContext.getScheduler().getContext().get(SERVLET_CONTEXT_KEY), SERVLET_CONTEXT_KEY)); - } catch (SchedulerException exception) { - throw new JobExecutionException("Unable to get platform services from the job execution context", exception); - } - } - } -} diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/Scheduler.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/Scheduler.java deleted file mode 100644 index dabdff877b..0000000000 --- a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/Scheduler.java +++ /dev/null @@ -1,41 +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.demo.paas.service.scheduler; - -import org.jclouds.demo.paas.RunnableHttpRequest; -import org.jclouds.demo.paas.RunnableHttpRequest.Factory; -import org.jclouds.http.HttpCommandExecutorService; - -/** - * @author Andrew Phillips - */ -public class Scheduler { - protected static final String SCHEDULER_ORIGINATOR_NAME = "scheduler"; - - protected final Factory httpRequestFactory; - - public Scheduler(HttpCommandExecutorService httpClient) { - httpRequestFactory = - RunnableHttpRequest.factory(httpClient, SCHEDULER_ORIGINATOR_NAME); - } - - public Factory getHttpRequestFactory() { - return httpRequestFactory; - } -} diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/quartz/plugins/TransactionlessXmlSchedulingDataProcessorPlugin.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/quartz/plugins/TransactionlessXmlSchedulingDataProcessorPlugin.java deleted file mode 100644 index 91659d9b16..0000000000 --- a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/paas/service/scheduler/quartz/plugins/TransactionlessXmlSchedulingDataProcessorPlugin.java +++ /dev/null @@ -1,401 +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.demo.paas.service.scheduler.quartz.plugins; - -import static org.quartz.SimpleScheduleBuilder.simpleSchedule; -import static org.quartz.TriggerBuilder.newTrigger; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.net.URL; -import java.net.URLDecoder; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; -import java.util.StringTokenizer; - -import org.jclouds.logging.Logger; -import org.quartz.JobBuilder; -import org.quartz.JobDetail; -import org.quartz.Scheduler; -import org.quartz.SchedulerException; -import org.quartz.SimpleTrigger; -import org.quartz.TriggerKey; -import org.quartz.jobs.FileScanJob; -import org.quartz.jobs.FileScanListener; -import org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin; -import org.quartz.simpl.CascadingClassLoadHelper; -import org.quartz.spi.ClassLoadHelper; -import org.quartz.spi.SchedulerPlugin; -import org.quartz.xml.XMLSchedulingDataProcessor; - -/** - * A copy of {@link XMLSchedulingDataProcessorPlugin} that does not reference - * {@code javax.transaction.UserTransaction} as so does not require a dependency - * on JTA. - * - * @author Andrew Phillips - * @see XMLSchedulingDataProcessorPlugin - */ -public class TransactionlessXmlSchedulingDataProcessorPlugin implements - FileScanListener, SchedulerPlugin { - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Data members. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - private static final int MAX_JOB_TRIGGER_NAME_LEN = 80; - private static final String JOB_INITIALIZATION_PLUGIN_NAME = "JobSchedulingDataLoaderPlugin"; - private static final String FILE_NAME_DELIMITERS = ","; - - private String name; - private Scheduler scheduler; - private final Logger log = Logger.CONSOLE; - - private boolean failOnFileNotFound = true; - - private String fileNames = XMLSchedulingDataProcessor.QUARTZ_XML_DEFAULT_FILE_NAME; - - // Populated by initialization - private Map jobFiles = new LinkedHashMap(); - - private long scanInterval = 0; - - boolean started = false; - - protected ClassLoadHelper classLoadHelper = null; - - private Set jobTriggerNameSet = new HashSet(); - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Interface. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - - /** - * Comma separated list of file names (with paths) to the XML files that should be read. - */ - public String getFileNames() { - return fileNames; - } - - /** - * The file name (and path) to the XML file that should be read. - */ - public void setFileNames(String fileNames) { - this.fileNames = fileNames; - } - - /** - * The interval (in seconds) at which to scan for changes to the file. - * If the file has been changed, it is re-loaded and parsed. The default - * value for the interval is 0, which disables scanning. - * - * @return Returns the scanInterval. - */ - public long getScanInterval() { - return scanInterval / 1000; - } - - /** - * The interval (in seconds) at which to scan for changes to the file. - * If the file has been changed, it is re-loaded and parsed. The default - * value for the interval is 0, which disables scanning. - * - * @param scanInterval The scanInterval to set. - */ - public void setScanInterval(long scanInterval) { - this.scanInterval = scanInterval * 1000; - } - - /** - * Whether or not initialization of the plugin should fail (throw an - * exception) if the file cannot be found. Default is true. - */ - public boolean isFailOnFileNotFound() { - return failOnFileNotFound; - } - - /** - * Whether or not initialization of the plugin should fail (throw an - * exception) if the file cannot be found. Default is true. - */ - public void setFailOnFileNotFound(boolean failOnFileNotFound) { - this.failOnFileNotFound = failOnFileNotFound; - } - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * SchedulerPlugin Interface. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - - /** - *

- * Called during creation of the Scheduler in order to give - * the SchedulerPlugin a chance to initialize. - *

- * - * @throws org.quartz.SchedulerConfigException - * if there is an error initializing. - */ - @Override - public void initialize(String name, Scheduler scheduler) - throws SchedulerException { - this.name = name; - this.scheduler = scheduler; - - classLoadHelper = new CascadingClassLoadHelper(); - classLoadHelper.initialize(); - - log.info("Registering Quartz Job Initialization Plug-in."); - - // Create JobFile objects - StringTokenizer stok = new StringTokenizer(fileNames, FILE_NAME_DELIMITERS); - while (stok.hasMoreTokens()) { - final String fileName = stok.nextToken(); - final JobFile jobFile = new JobFile(fileName); - jobFiles.put(fileName, jobFile); - } - } - - @Override - public void start() { - try { - if (jobFiles.isEmpty() == false) { - - if (scanInterval > 0) { - scheduler.getContext().put(JOB_INITIALIZATION_PLUGIN_NAME + '_' + name, this); - } - - Iterator iterator = jobFiles.values().iterator(); - while (iterator.hasNext()) { - JobFile jobFile = iterator.next(); - - if (scanInterval > 0) { - String jobTriggerName = buildJobTriggerName(jobFile.getFileBasename()); - TriggerKey tKey = new TriggerKey(jobTriggerName, JOB_INITIALIZATION_PLUGIN_NAME); - - // remove pre-existing job/trigger, if any - scheduler.unscheduleJob(tKey); - - // TODO: convert to use builder - SimpleTrigger trig = newTrigger() - .withIdentity(jobTriggerName, JOB_INITIALIZATION_PLUGIN_NAME) - .startNow() - .endAt(null) - .withSchedule(simpleSchedule() - .repeatForever() - .withIntervalInMilliseconds(scanInterval)) - .build(); - - JobDetail job = JobBuilder.newJob(FileScanJob.class) - .withIdentity(jobTriggerName, JOB_INITIALIZATION_PLUGIN_NAME) - .build(); - job.getJobDataMap().put(FileScanJob.FILE_NAME, jobFile.getFileName()); - job.getJobDataMap().put(FileScanJob.FILE_SCAN_LISTENER_NAME, JOB_INITIALIZATION_PLUGIN_NAME + '_' + name); - - scheduler.scheduleJob(job, trig); - log.debug("Scheduled file scan job for data file: {}, at interval: {}", jobFile.getFileName(), scanInterval); - } - - processFile(jobFile); - } - } - } catch(SchedulerException se) { - log.error("Error starting background-task for watching jobs file.", se); - } finally { - started = true; - } - } - - /** - * Helper method for generating unique job/trigger name for the - * file scanning jobs (one per FileJob). The unique names are saved - * in jobTriggerNameSet. - */ - private String buildJobTriggerName( - String fileBasename) { - // Name w/o collisions will be prefix + _ + filename (with '.' of filename replaced with '_') - // For example: JobInitializationPlugin_jobInitializer_myjobs_xml - String jobTriggerName = JOB_INITIALIZATION_PLUGIN_NAME + '_' + name + '_' + fileBasename.replace('.', '_'); - - // If name is too long (DB column is 80 chars), then truncate to max length - if (jobTriggerName.length() > MAX_JOB_TRIGGER_NAME_LEN) { - jobTriggerName = jobTriggerName.substring(0, MAX_JOB_TRIGGER_NAME_LEN); - } - - // Make sure this name is unique in case the same file name under different - // directories is being checked, or had a naming collision due to length truncation. - // If there is a conflict, keep incrementing a _# suffix on the name (being sure - // not to get too long), until we find a unique name. - int currentIndex = 1; - while (jobTriggerNameSet.add(jobTriggerName) == false) { - // If not our first time through, then strip off old numeric suffix - if (currentIndex > 1) { - jobTriggerName = jobTriggerName.substring(0, jobTriggerName.lastIndexOf('_')); - } - - String numericSuffix = "_" + currentIndex++; - - // If the numeric suffix would make the name too long, then make room for it. - if (jobTriggerName.length() > (MAX_JOB_TRIGGER_NAME_LEN - numericSuffix.length())) { - jobTriggerName = jobTriggerName.substring(0, (MAX_JOB_TRIGGER_NAME_LEN - numericSuffix.length())); - } - - jobTriggerName += numericSuffix; - } - - return jobTriggerName; - } - - @Override - public void shutdown() { - // nothing to do - } - - private void processFile(JobFile jobFile) { - if (jobFile == null || !jobFile.getFileFound()) { - return; - } - - try { - XMLSchedulingDataProcessor processor = - new XMLSchedulingDataProcessor(this.classLoadHelper); - - processor.addJobGroupToNeverDelete(JOB_INITIALIZATION_PLUGIN_NAME); - processor.addTriggerGroupToNeverDelete(JOB_INITIALIZATION_PLUGIN_NAME); - - processor.processFileAndScheduleJobs( - jobFile.getFileName(), - jobFile.getFileName(), // systemId - scheduler); - } catch (Exception e) { - log.error("Error scheduling jobs: " + e.getMessage(), e); - } - } - - public void processFile(String filePath) { - processFile((JobFile)jobFiles.get(filePath)); - } - - /** - * @see org.quartz.jobs.FileScanListener#fileUpdated(java.lang.String) - */ - public void fileUpdated(String fileName) { - if (started) { - processFile(fileName); - } - } - - class JobFile { - private String fileName; - - // These are set by initialize() - private String filePath; - private String fileBasename; - private boolean fileFound; - - protected JobFile(String fileName) throws SchedulerException { - this.fileName = fileName; - initialize(); - } - - protected String getFileName() { - return fileName; - } - - protected boolean getFileFound() { - return fileFound; - } - - protected String getFilePath() { - return filePath; - } - - protected String getFileBasename() { - return fileBasename; - } - - private void initialize() throws SchedulerException { - InputStream f = null; - try { - String furl = null; - - File file = new File(getFileName()); // files in filesystem - if (!file.exists()) { - URL url = classLoadHelper.getResource(getFileName()); - if(url != null) { - try { - furl = URLDecoder.decode(url.getPath(), "UTF-8"); - } catch (UnsupportedEncodingException e) { - furl = url.getPath(); - } - file = new File(furl); - try { - f = url.openStream(); - } catch (IOException ignor) { - // Swallow the exception - } - } - } else { - try { - f = new java.io.FileInputStream(file); - }catch (FileNotFoundException e) { - // ignore - } - } - - if (f == null) { - if (isFailOnFileNotFound()) { - throw new SchedulerException( - "File named '" + getFileName() + "' does not exist."); - } else { - log.warn("File named '" + getFileName() + "' does not exist."); - } - } else { - fileFound = true; - } - filePath = (furl != null) ? furl : file.getAbsolutePath(); - fileBasename = file.getName(); - } finally { - try { - if (f != null) { - f.close(); - } - } catch (IOException ioe) { - log.warn("Error closing jobs file " + getFileName(), ioe); - } - } - } - } -} \ No newline at end of file diff --git a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/controller/EnqueueStoresController.java b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/controller/EnqueueStoresController.java index 592eaaa8bd..552a7fc69a 100644 --- a/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/controller/EnqueueStoresController.java +++ b/demos/tweetstore/heroku-tweetstore/src/main/java/org/jclouds/demo/tweetstore/controller/EnqueueStoresController.java @@ -19,7 +19,6 @@ package org.jclouds.demo.tweetstore.controller; import static com.google.common.base.Strings.nullToEmpty; -import static org.jclouds.demo.paas.RunnableHttpRequest.PLATFORM_REQUEST_ORIGINATOR_HEADER; import java.io.IOException; import java.net.URI; @@ -85,7 +84,7 @@ public class EnqueueStoresController extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - if (!nullToEmpty(request.getHeader(PLATFORM_REQUEST_ORIGINATOR_HEADER)).equals("scheduler")) { + if (!nullToEmpty(request.getHeader("X-Heroku-Cron")).equals("true")) { response.sendError(401); } diff --git a/demos/tweetstore/heroku-tweetstore/src/main/resources/jobs.xml b/demos/tweetstore/heroku-tweetstore/src/main/resources/jobs.xml deleted file mode 100644 index b740fdd52f..0000000000 --- a/demos/tweetstore/heroku-tweetstore/src/main/resources/jobs.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - enqueue-store-tweet-tasks - Enqueue 'store tweet' tasks for all contexts - org.jclouds.demo.paas.service.scheduler.HttpRequestJob - - - url - /stores/do - - - - - - - submit-recurring-job - enqueue-store-tweet-tasks - 10 - MINUTE - - - - \ No newline at end of file diff --git a/demos/tweetstore/heroku-tweetstore/src/main/resources/quartz.properties b/demos/tweetstore/heroku-tweetstore/src/main/resources/quartz.properties deleted file mode 100644 index 12a0fcfe91..0000000000 --- a/demos/tweetstore/heroku-tweetstore/src/main/resources/quartz.properties +++ /dev/null @@ -1,28 +0,0 @@ -#============================================================================ -# Configure Main Scheduler Properties -#============================================================================ - -org.quartz.scheduler.skipUpdateCheck: true - -#============================================================================ -# Configure ThreadPool -#============================================================================ - -org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool -org.quartz.threadPool.threadCount: 1 - -#============================================================================ -# Configure JobStore -#============================================================================ - -org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore - -#============================================================================ -# Configure the Job Initialization Plugin -#============================================================================ - -org.quartz.plugin.jobInitializer.class: org.jclouds.demo.paas.service.scheduler.quartz.plugins.TransactionlessXmlSchedulingDataProcessorPlugin -org.quartz.plugin.jobInitializer.fileNames: jobs.xml -org.quartz.plugin.jobInitializer.failOnFileNotFound: true -org.quartz.plugin.jobInitializer.scanInterval: 0 -#org.quartz.plugin.jobInitializer.wrapInUserTransaction: false \ No newline at end of file diff --git a/demos/tweetstore/heroku-tweetstore/src/main/webapp/WEB-INF/web.xml b/demos/tweetstore/heroku-tweetstore/src/main/webapp/WEB-INF/web.xml index 9482bd9d9e..5bf6bdf0bb 100644 --- a/demos/tweetstore/heroku-tweetstore/src/main/webapp/WEB-INF/web.xml +++ b/demos/tweetstore/heroku-tweetstore/src/main/webapp/WEB-INF/web.xml @@ -24,11 +24,6 @@ version="2.5"> jclouds-tweetstore - - quartz:scheduler-context-servlet-context-key - servlet-context - - guiceFilter @@ -45,10 +40,6 @@ org.jclouds.demo.paas.config.PlatformServicesInitializer - - org.quartz.ee.servlet.QuartzInitializerListener - - org.jclouds.demo.tweetstore.config.GuiceServletConfig From a7412c40b5d0ff20766b74162a16065156624ccf Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Mon, 7 May 2012 10:25:30 -0700 Subject: [PATCH 035/148] Issue 916:update to gson 2.2 --- core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/pom.xml b/core/pom.xml index 566d4f1cdc..368cdddae3 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -94,7 +94,7 @@ com.google.code.gson gson - 2.1 + 2.2 com.google.guava From d99c18140eaecac612f53b399d1e5636df21c37f Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Mon, 7 May 2012 11:51:29 -0700 Subject: [PATCH 036/148] cleanup on vbox --- .../virtualbox/VirtualBoxApiMetadata.java | 4 +- .../config/DefaultCacheNodeStoreModule.java | 36 ----- ...rdcodeLocalhostAsNodeMetadataSupplier.java | 136 ++++++++++++++++++ ...VirtualBoxComputeServiceContextModule.java | 47 ------ .../functions/CreateAndInstallVm.java | 4 +- .../functions/MastersLoadingCache.java | 4 +- 6 files changed, 142 insertions(+), 89 deletions(-) delete mode 100644 labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/DefaultCacheNodeStoreModule.java create mode 100644 labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/HardcodeLocalhostAsNodeMetadataSupplier.java diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxApiMetadata.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxApiMetadata.java index 00d3069fda..c3fc23ef86 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxApiMetadata.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxApiMetadata.java @@ -33,7 +33,7 @@ import java.util.Properties; import org.jclouds.apis.ApiMetadata; import org.jclouds.apis.internal.BaseApiMetadata; import org.jclouds.compute.ComputeServiceContext; -import org.jclouds.virtualbox.config.DefaultCacheNodeStoreModule; +import org.jclouds.virtualbox.config.HardcodeLocalhostAsNodeMetadataSupplier; import org.jclouds.virtualbox.config.VirtualBoxComputeServiceContextModule; import com.google.common.collect.ImmutableSet; @@ -106,7 +106,7 @@ public class VirtualBoxApiMetadata extends BaseApiMetadata { .buildVersion("4.1.8r75467") .defaultProperties(VirtualBoxApiMetadata.defaultProperties()) .view(ComputeServiceContext.class) - .defaultModules(ImmutableSet.>of(DefaultCacheNodeStoreModule.class, VirtualBoxComputeServiceContextModule.class)); + .defaultModules(ImmutableSet.>of(HardcodeLocalhostAsNodeMetadataSupplier.class, VirtualBoxComputeServiceContextModule.class)); } @Override diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/DefaultCacheNodeStoreModule.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/DefaultCacheNodeStoreModule.java deleted file mode 100644 index 14b2165d6a..0000000000 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/DefaultCacheNodeStoreModule.java +++ /dev/null @@ -1,36 +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.virtualbox.config; - -import java.net.URI; - -import org.jclouds.byon.Node; -import org.jclouds.byon.config.CacheNodeStoreModule; -import org.jclouds.compute.domain.OsFamily; - -import com.google.common.collect.ImmutableMap; - -public class DefaultCacheNodeStoreModule extends CacheNodeStoreModule { - public DefaultCacheNodeStoreModule() { - super(ImmutableMap.of("host", Node.builder().id("host").name("host installing virtualbox").hostname("localhost") - .osFamily(OsFamily.LINUX.toString()).osDescription(System.getProperty("os.name")).osVersion( - System.getProperty("os.version")).group("ssh").username(System.getProperty("user.name")) - .credentialUrl(URI.create("file://" + System.getProperty("user.home") + "/.ssh/id_rsa")).build())); - } -} diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/HardcodeLocalhostAsNodeMetadataSupplier.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/HardcodeLocalhostAsNodeMetadataSupplier.java new file mode 100644 index 0000000000..eb8bf949f3 --- /dev/null +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/HardcodeLocalhostAsNodeMetadataSupplier.java @@ -0,0 +1,136 @@ +/** + * 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.virtualbox.config; + +import java.io.File; +import java.io.IOException; + +import javax.inject.Singleton; + +import org.jclouds.compute.callables.RunScriptOnNode; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.NodeMetadataBuilder; +import org.jclouds.compute.domain.NodeState; +import org.jclouds.compute.domain.OperatingSystem; +import org.jclouds.compute.domain.OsFamily; +import org.jclouds.domain.LocationBuilder; +import org.jclouds.domain.LocationScope; +import org.jclouds.domain.LoginCredentials; + +import com.google.common.base.Charsets; +import com.google.common.base.Supplier; +import com.google.common.base.Throwables; +import com.google.common.io.Files; +import com.google.inject.AbstractModule; +import com.google.inject.Provides; + +/** + * In particular, this binds {@code Supplier} so that it can be used in ssh commands. + * Ssh is necessary for operations that cannot be performed in the virtual box api, such as clearing + * sessions. + * + * ex. once this is loaded, use Guice to inject {@code Supplier} as {@code host} and + * {@link RunScriptOnNode#Factory} as factory, to start making commands like the following: + * + *
+ * import static org.jclouds.compute.options.RunScriptOptions.Builder.runAsRoot;
+ * import static org.jclouds.scriptbuilder.domain.Statements.*;
+ * 
+ *    ...
+ *  
+ *  // direct execute a script as opposed to using sudo, or an init wrapper
+ *  ListenableFuture fooInTheFuture = factory.submit(host.get(), 
+ *          exec("echo foo"), runAsRoot(false).wrapInInitScript(false));
+ * 
+ *  ExecResponse foo = Futures.getUnchecked(fooInTheFuture);
+ * 
+ *  // call a set of commands that are defined in classpath/functions/function_name.sh
+ *  ListenableFuture kill = factory.submit(host.get(), 
+ *          call("killsession"), runAsRoot(false).wrapInInitScript(false));
+ * 
+ * ...
+ * 
+ * 
+ * + *

Note

+ * + * People often forget to call {@link Future#get} when using {@link RunScriptOnNode.Factory#submit}. + * Don't forget! + * + * @author Adrian Cole + */ +public class HardcodeLocalhostAsNodeMetadataSupplier extends AbstractModule { + + public static final String HOST_ID = "host"; + public static final String HOSTNAME = System.getenv("HOSTNAME"); + + /** + * Lazy so that we don't hang up the injector reading a file + */ + @Provides + @Singleton + protected Supplier lazySupplyHostAsNodeMetadata() { + return new Supplier() { + + @Override + public NodeMetadata get() { + + String privateKey = readRsaIdentity(); + + return new NodeMetadataBuilder() + .id(HOST_ID) + .name("host installing virtualbox") + .hostname(HOSTNAME) + .operatingSystem(OperatingSystem.builder() + .family(OsFamily.LINUX) + .description(System.getProperty("os.name")) + .arch(System.getProperty("os.arch")) + .version(System.getProperty("os.version")) + .build()) + .state(NodeState.RUNNING) + .location(new LocationBuilder().id(HOST_ID) + .scope(LocationScope.HOST) + .description(HOSTNAME) + .build()) + .credentials(LoginCredentials.builder() + .user(System.getProperty("user.name")) + .privateKey(privateKey) + .build()) + .build(); + } + + }; + } + + static String readRsaIdentity() { + String privateKey; + try { + File keyFile = new File(System.getProperty("user.home") + "/.ssh/id_rsa"); + privateKey = Files.toString(keyFile, Charsets.UTF_8); + } catch (IOException e) { + throw Throwables.propagate(e); + } + return privateKey; + } + + @Override + protected void configure() { + + } +} \ No newline at end of file diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxComputeServiceContextModule.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxComputeServiceContextModule.java index 399813766a..0ac7cebc6d 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxComputeServiceContextModule.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxComputeServiceContextModule.java @@ -24,22 +24,14 @@ import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_DEFAU import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_DEFAULT_IMAGE_VERSION; import java.io.File; -import java.io.InputStream; import java.net.URI; import java.util.Map; -import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import javax.inject.Singleton; import org.eclipse.jetty.server.Server; -import org.jclouds.ContextBuilder; -import org.jclouds.byon.BYONApiMetadata; -import org.jclouds.byon.Node; -import org.jclouds.byon.functions.NodeToNodeMetadata; -import org.jclouds.byon.suppliers.SupplyFromProviderURIOrNodesProperty; import org.jclouds.compute.ComputeServiceAdapter; -import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.ImageExtension; import org.jclouds.compute.ComputeServiceAdapter.NodeAndInitialCredentials; import org.jclouds.compute.config.ComputeServiceAdapterContextModule; @@ -51,11 +43,8 @@ import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts; import org.jclouds.domain.Location; import org.jclouds.functions.IdentityFunction; -import org.jclouds.logging.slf4j.config.SLF4JLoggingModule; import org.jclouds.predicates.RetryablePredicate; import org.jclouds.ssh.SshClient; -import org.jclouds.sshj.config.SshjSshClientModule; -import org.jclouds.virtualbox.Host; import org.jclouds.virtualbox.compute.VirtualBoxComputeServiceAdapter; import org.jclouds.virtualbox.compute.VirtualBoxImageExtension; import org.jclouds.virtualbox.domain.CloneSpec; @@ -86,17 +75,13 @@ import org.virtualbox_4_1.VirtualBoxManager; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; -import com.google.common.base.Functions; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Supplier; -import com.google.common.base.Suppliers; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import com.google.inject.Injector; -import com.google.inject.Module; import com.google.inject.Provides; import com.google.inject.TypeLiteral; @@ -157,10 +142,6 @@ public class VirtualBoxComputeServiceContextModule extends bind(new TypeLiteral() { }).to((Class) PreseedCfgServer.class).asEagerSingleton(); - // for byon - bind(new TypeLiteral>() { - }).to(SupplyFromProviderURIOrNodesProperty.class); - bind(new TypeLiteral>() { }).to(IMachineToSshClient.class); @@ -168,15 +149,6 @@ public class VirtualBoxComputeServiceContextModule extends bind(LockType.class).toInstance(LockType.Write); } - @Provides - @Host - @Singleton - protected ComputeServiceContext provideHostController() { - return ContextBuilder.newBuilder(new BYONApiMetadata()).credentials("", "") - .modules(ImmutableSet. of(new SLF4JLoggingModule(), new SshjSshClientModule())) - .build(ComputeServiceContext.class); - } - @Provides @Singleton protected Function, VirtualBoxManager> provideVBox() { @@ -195,12 +167,6 @@ public class VirtualBoxComputeServiceContextModule extends }; } - @Provides - @Singleton - protected Supplier defaultClient(Supplier in) { - return in; - } - @Provides @Singleton protected Predicate sshResponds(SshResponds sshResponds, Timeouts timeouts) { @@ -213,19 +179,6 @@ public class VirtualBoxComputeServiceContextModule extends .osArchMatches(VIRTUALBOX_DEFAULT_IMAGE_ARCH); } - @Provides - @Singleton - protected Supplier host(Supplier> nodes, NodeToNodeMetadata converter) - throws ExecutionException { - return Suppliers.compose(Functions.compose(converter, new Function, Node>() { - - @Override - public Node apply(LoadingCache arg0) { - return arg0.apply("host"); - } - }), nodes); - } - @Override protected Optional provideImageExtension(Injector i) { return Optional.of(i.getInstance(ImageExtension.class)); diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java index a848671835..b359cd47ff 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java @@ -29,12 +29,12 @@ import javax.annotation.Resource; import javax.inject.Named; import javax.inject.Singleton; -import org.jclouds.Constants; import org.jclouds.compute.domain.ExecResponse; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.options.RunScriptOptions; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.logging.Logger; +import org.jclouds.rest.annotations.BuildVersion; import org.jclouds.ssh.SshClient; import org.jclouds.virtualbox.domain.IsoSpec; import org.jclouds.virtualbox.domain.MasterSpec; @@ -74,7 +74,7 @@ public class CreateAndInstallVm implements Function { CreateAndRegisterMachineFromIsoIfNotAlreadyExists CreateAndRegisterMachineFromIsoIfNotAlreadyExists, IMachineToNodeMetadata imachineToNodeMetadata, Predicate sshResponds, Function sshClientForIMachine, MachineUtils machineUtils, - MachineController machineController, @Named(Constants.PROPERTY_BUILD_VERSION) String version, + MachineController machineController, @BuildVersion String version, @Named(VIRTUALBOX_PRECONFIGURATION_URL) String preconfigurationUrl) { this.createAndRegisterMachineFromIsoIfNotAlreadyExists = CreateAndRegisterMachineFromIsoIfNotAlreadyExists; this.sshResponds = sshResponds; diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/MastersLoadingCache.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/MastersLoadingCache.java index e54c01101e..42f95069d0 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/MastersLoadingCache.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/MastersLoadingCache.java @@ -42,10 +42,10 @@ import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; -import org.jclouds.Constants; import org.jclouds.compute.domain.Image; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.logging.Logger; +import org.jclouds.rest.annotations.BuildVersion; import org.jclouds.virtualbox.domain.HardDisk; import org.jclouds.virtualbox.domain.IsoSpec; import org.jclouds.virtualbox.domain.Master; @@ -104,7 +104,7 @@ public class MastersLoadingCache extends AbstractLoadingCache { private final String preconfigurationUrl; @Inject - public MastersLoadingCache(@Named(Constants.PROPERTY_BUILD_VERSION) String version, + public MastersLoadingCache(@BuildVersion String version, @Named(VIRTUALBOX_INSTALLATION_KEY_SEQUENCE) String installationKeySequence, @Named(VIRTUALBOX_PRECONFIGURATION_URL) String preconfigurationUrl, @Named(VIRTUALBOX_WORKINGDIR) String workingDir, Function masterLoader, From 51e5e4c4bbb4409ff7e60b7140d147cd2e9166c6 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Mon, 7 May 2012 13:34:55 -0700 Subject: [PATCH 037/148] freshen ning --- sandbox-drivers/asynchttpclient/pom.xml | 2 +- .../ning/NingHttpCommandExecutorService.java | 18 +++++------------- .../NingHttpCommandExecutorServiceTest.java | 2 ++ 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/sandbox-drivers/asynchttpclient/pom.xml b/sandbox-drivers/asynchttpclient/pom.xml index ee0d487922..d4b7492d6c 100644 --- a/sandbox-drivers/asynchttpclient/pom.xml +++ b/sandbox-drivers/asynchttpclient/pom.xml @@ -60,7 +60,7 @@ com.ning async-http-client - 1.6.3 + 1.7.4 org.eclipse.jetty diff --git a/sandbox-drivers/asynchttpclient/src/main/java/org/jclouds/http/ning/NingHttpCommandExecutorService.java b/sandbox-drivers/asynchttpclient/src/main/java/org/jclouds/http/ning/NingHttpCommandExecutorService.java index ef2a647701..de50d577aa 100644 --- a/sandbox-drivers/asynchttpclient/src/main/java/org/jclouds/http/ning/NingHttpCommandExecutorService.java +++ b/sandbox-drivers/asynchttpclient/src/main/java/org/jclouds/http/ning/NingHttpCommandExecutorService.java @@ -31,6 +31,7 @@ import java.util.concurrent.Future; import javax.inject.Singleton; import javax.ws.rs.core.HttpHeaders; +import org.jclouds.JcloudsVersion; import org.jclouds.crypto.CryptoStreams; import org.jclouds.http.HttpCommand; import org.jclouds.http.HttpCommandExecutorService; @@ -50,7 +51,6 @@ import com.google.common.base.Throwables; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; import com.google.common.io.Closeables; -import com.google.common.util.concurrent.AbstractFuture; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.inject.Inject; @@ -68,7 +68,8 @@ import com.ning.http.client.generators.InputStreamBodyGenerator; */ public class NingHttpCommandExecutorService implements HttpCommandExecutorService { - public static final String USER_AGENT = "jclouds/1.0 ning http/1.0.0"; + //TODO: get async version from maven or something + public static final String USER_AGENT = String.format("jclouds/%s async-http-client/%s", JcloudsVersion.get(), "1.7.4"); private final AsyncHttpClient client; private final ConvertToNingRequest convertToNingRequest; @@ -99,10 +100,10 @@ public class NingHttpCommandExecutorService implements HttpCommandExecutorServic continue; } else { errorHandler.handleError(command, httpResponse); - return wrapAsFuture(httpResponse); + return Futures.immediateFuture(httpResponse); } } else { - return wrapAsFuture(httpResponse); + return Futures.immediateFuture(httpResponse); } } @@ -115,15 +116,6 @@ public class NingHttpCommandExecutorService implements HttpCommandExecutorServic } } - private ListenableFuture wrapAsFuture(final HttpResponse httpResponse) { - return Futures.makeListenable(new AbstractFuture() { - @Override - public HttpResponse get() throws InterruptedException, ExecutionException { - return httpResponse; - } - }); - } - @Singleton public static class ConvertToNingRequest implements Function { diff --git a/sandbox-drivers/asynchttpclient/src/test/java/org/jclouds/http/ning/NingHttpCommandExecutorServiceTest.java b/sandbox-drivers/asynchttpclient/src/test/java/org/jclouds/http/ning/NingHttpCommandExecutorServiceTest.java index 64fca3328a..51d9be9ef6 100644 --- a/sandbox-drivers/asynchttpclient/src/test/java/org/jclouds/http/ning/NingHttpCommandExecutorServiceTest.java +++ b/sandbox-drivers/asynchttpclient/src/test/java/org/jclouds/http/ning/NingHttpCommandExecutorServiceTest.java @@ -29,6 +29,7 @@ import java.util.Properties; import org.jclouds.http.BaseHttpCommandExecutorServiceIntegrationTest; import org.jclouds.http.ning.config.NingHttpCommandExecutorServiceModule; +import org.testng.annotations.Test; import com.google.inject.Module; @@ -37,6 +38,7 @@ import com.google.inject.Module; * * @author Adrian Cole */ +@Test(testName = "NingHttpCommandExecutorServiceTest") public class NingHttpCommandExecutorServiceTest extends BaseHttpCommandExecutorServiceIntegrationTest { static { System.setProperty("http.conn-manager.timeout", 1000 + ""); From 7cbfd427365701b484f9c1f2f3e47d88bc275e4e Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Mon, 7 May 2012 13:23:09 -0700 Subject: [PATCH 038/148] Do not check for equality on overwrites Firstly, this check never triggered because Payload.equals always returns false for File objects. Secondly, this would not reduce IO even if it worked since the common case is not overwriting a file with the same contents. Lastly, simplify a cast. --- .../strategy/internal/FilesystemStorageStrategyImpl.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/apis/filesystem/src/main/java/org/jclouds/filesystem/strategy/internal/FilesystemStorageStrategyImpl.java b/apis/filesystem/src/main/java/org/jclouds/filesystem/strategy/internal/FilesystemStorageStrategyImpl.java index bffac5a35c..2818cdfa97 100644 --- a/apis/filesystem/src/main/java/org/jclouds/filesystem/strategy/internal/FilesystemStorageStrategyImpl.java +++ b/apis/filesystem/src/main/java/org/jclouds/filesystem/strategy/internal/FilesystemStorageStrategyImpl.java @@ -199,15 +199,11 @@ public class FilesystemStorageStrategyImpl implements FilesystemStorageStrategy filesystemContainerNameValidator.validate(container); filesystemBlobKeyValidator.validate(blobKey); File outputFile = getFileForBlobKey(container, blobKey); - if (payload.getRawContent().equals(outputFile)){ - // we shouldn't re-copy the same contents - return; - } FileOutputStream output = null; try { Files.createParentDirs(outputFile); if (payload.getRawContent() instanceof File) - Files.copy(File.class.cast(payload.getRawContent()), outputFile); + Files.copy((File) payload.getRawContent(), outputFile); else { output = new FileOutputStream(outputFile); payload.writeTo(output); From 50074682ba94bc18c1a76cd5b536eeb9dd04793c Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Mon, 7 May 2012 14:15:07 -0700 Subject: [PATCH 039/148] updated to gae 1.6.1 --- drivers/gae/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gae/pom.xml b/drivers/gae/pom.xml index cc6e98b678..4de817934b 100644 --- a/drivers/gae/pom.xml +++ b/drivers/gae/pom.xml @@ -56,7 +56,7 @@ com.google.appengine appengine-api-1.0-sdk - 1.4.3 + 1.6.1 @@ -74,13 +74,13 @@ com.google.appengine appengine-api-stubs - 1.4.3 + 1.6.1 test com.google.appengine appengine-testing - 1.4.3 + 1.6.1 test From 3a0c15b345b0f2eb9a019f10adfb731a7a644b64 Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Mon, 7 May 2012 14:16:03 -0700 Subject: [PATCH 040/148] Reduce filesystem and transient differences These providers have a similar lineage but many gratuitous differences. This commit reduces the diff between them and is a prerequisite for upcoming changes to make them more similar to each other and real providers. Some future commit might unify these in some smarter way, e.g., having a TransientStrategy to match FilesystemStrategy. --- .../filesystem/FilesystemAsyncBlobStore.java | 121 +++---- .../blobstore/TransientAsyncBlobStore.java | 319 ++++++++++-------- 2 files changed, 228 insertions(+), 212 deletions(-) diff --git a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java index 412cf2d326..c462b44661 100644 --- a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java +++ b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java @@ -26,8 +26,6 @@ import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.find; import static com.google.common.collect.Iterables.size; import static com.google.common.collect.Iterables.transform; -import static com.google.common.collect.Lists.newArrayList; -import static com.google.common.collect.Lists.partition; import static com.google.common.collect.Maps.newHashMap; import static com.google.common.collect.Sets.filter; import static com.google.common.collect.Sets.newTreeSet; @@ -48,10 +46,10 @@ import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; -import java.util.Map.Entry; import java.util.concurrent.ExecutorService; import javax.annotation.Resource; @@ -126,10 +124,14 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { protected final FilesystemStorageStrategy storageStrategy; @Inject - protected FilesystemAsyncBlobStore(BlobStoreContext context, DateService dateService, Crypto crypto, - HttpGetOptionsListToGetOptions httpGetOptionsConverter, IfDirectoryReturnNameStrategy ifDirectoryReturnName, - BlobUtils blobUtils, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, - Supplier defaultLocation, @Memoized Supplier> locations, + protected FilesystemAsyncBlobStore(BlobStoreContext context, + DateService dateService, Crypto crypto, + HttpGetOptionsListToGetOptions httpGetOptionsConverter, + IfDirectoryReturnNameStrategy ifDirectoryReturnName, + BlobUtils blobUtils, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, + Supplier defaultLocation, + @Memoized Supplier> locations, FilesystemStorageStrategy storageStrategy) { super(context, blobUtils, service, defaultLocation, locations); this.dateService = dateService; @@ -146,9 +148,8 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { public ListenableFuture> list(final String container, ListContainerOptions options) { // Check if the container exists - if (!containerExistsSyncImpl(container)) { + if (!containerExistsSyncImpl(container)) return immediateFailedFuture(cnfe(container)); - } // Loading blobs from container Iterable blobBelongingToContainer = null; @@ -183,11 +184,10 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { final String finalMarker = options.getMarker(); StorageMetadata lastMarkerMetadata = find(contents, new Predicate() { public boolean apply(StorageMetadata metadata) { - return metadata.getName().equals(finalMarker); + return metadata.getName().compareTo(finalMarker) > 0; } }); contents = contents.tailSet(lastMarkerMetadata); - contents.remove(lastMarkerMetadata); } final String prefix = options.getDir(); @@ -199,30 +199,26 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { })); } - Integer maxResults = options.getMaxResults() != null ? options.getMaxResults() : 1000; - if (contents.size() > 0) { - SortedSet contentsSlice = firstSliceOfSize(contents, maxResults); - if (!contentsSlice.contains(contents.last())) { + int maxResults = options.getMaxResults() != null ? options.getMaxResults() : 1000; + if (!contents.isEmpty()) { + StorageMetadata lastElement = contents.last(); + contents = newTreeSet(Iterables.limit(contents, maxResults)); + if (!contents.contains(lastElement)) { // Partial listing - marker = contentsSlice.last().getName(); - } else { - marker = null; + marker = contents.last().getName(); } - contents = contentsSlice; } final String delimiter = options.isRecursive() ? null : File.separator; if (delimiter != null) { - SortedSet commonPrefixes = null; - Iterable iterable = transform(contents, new CommonPrefixes(prefix != null ? prefix : null, - delimiter)); - commonPrefixes = iterable != null ? newTreeSet(iterable) : new TreeSet(); + SortedSet commonPrefixes = newTreeSet( + transform(contents, new CommonPrefixes(prefix, delimiter))); commonPrefixes.remove(CommonPrefixes.NO_PREFIX); - contents = newTreeSet(filter(contents, new DelimiterFilter(prefix != null ? prefix : null, delimiter))); + contents = newTreeSet(filter(contents, new DelimiterFilter(prefix, delimiter))); - Iterables. addAll(contents, - transform(commonPrefixes, new Function() { + Iterables. addAll(contents, transform(commonPrefixes, + new Function() { public StorageMetadata apply(String o) { MutableStorageMetadata md = new MutableStorageMetadataImpl(); md.setType(StorageType.RELATIVE_PATH); @@ -245,7 +241,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { } - private ContainerNotFoundException cnfe(String name) { + private ContainerNotFoundException cnfe(final String name) { return new ContainerNotFoundException(name, String.format("container %s not in filesystem", name)); } @@ -284,18 +280,31 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { * {@inheritDoc} */ @Override - public ListenableFuture removeBlob(String container, String key) { + public ListenableFuture removeBlob(final String container, final String key) { storageStrategy.removeBlob(container, key); return immediateFuture(null); } + /** + * Override parent method because it uses strange futures and listenables + * that creates problem in the test if more than one test that deletes the + * container is executed + * + * @param container + * @return + */ + @Override + public ListenableFuture deleteContainer(final String container) { + deleteAndVerifyContainerGone(container); + return immediateFuture(null); + } + /** * {@inheritDoc} */ @Override - public ListenableFuture containerExists(String containerName) { - boolean exists = containerExistsSyncImpl(containerName); - return immediateFuture(exists); + public ListenableFuture containerExists(final String containerName) { + return immediateFuture(containerExistsSyncImpl(containerName)); } /** @@ -419,11 +428,6 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { } } - public static > SortedSet firstSliceOfSize(Iterable elements, int size) { - List> slices = partition(newArrayList(elements), size); - return newTreeSet(slices.get(0)); - } - public static HttpResponseException returnResponseException(int code) { HttpResponse response = null; response = new HttpResponse(code, null, null); @@ -474,18 +478,20 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { * {@inheritDoc} */ @Override - public ListenableFuture putBlob(String containerName, Blob object) { - String blobKey = object.getMetadata().getName(); + public ListenableFuture putBlob(String containerName, Blob blob) { + checkNotNull(containerName, "containerName must be set"); + checkNotNull(blob, "blob must be set"); + String blobKey = blob.getMetadata().getName(); - logger.debug("Put object with key [%s] to container [%s]", blobKey, containerName); - String eTag = getEtag(object); + logger.debug("Put blob with key [%s] to container [%s]", blobKey, containerName); + String eTag = getEtag(blob); try { // TODO // must override existing file? - storageStrategy.writePayloadOnFile(containerName, blobKey, object.getPayload()); + storageStrategy.writePayloadOnFile(containerName, blobKey, blob.getPayload()); } catch (IOException e) { - logger.error(e, "An error occurred storing the new object with name [%s] to container [%s].", blobKey, + logger.error(e, "An error occurred storing the new blob with name [%s] to container [%s].", blobKey, containerName); Throwables.propagate(e); } @@ -584,10 +590,10 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { * {@inheritDoc} */ @Override - public ListenableFuture blobMetadata(String container, String key) { + public ListenableFuture blobMetadata(final String container, final String key) { try { Blob blob = getBlob(container, key).get(); - return Futures. immediateFuture(blob != null ? blob.getMetadata() : null); + return immediateFuture(blob != null ? (BlobMetadata) blob.getMetadata() : null); } catch (Exception e) { if (size(filter(getCausalChain(e), KeyNotFoundException.class)) >= 1) return immediateFuture(null); @@ -595,26 +601,6 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { } } - @Override - protected boolean deleteAndVerifyContainerGone(String container) { - storageStrategy.deleteContainer(container); - return containerExistsSyncImpl(container); - } - - /** - * Override parent method because it uses strange futures and listenables - * that creates problem in the test if more than one test that deletes the - * container is executed - * - * @param container - * @return - */ - @Override - public ListenableFuture deleteContainer(String container) { - deleteAndVerifyContainerGone(container); - return immediateFuture(null); - } - /** * Each container is a directory, so in order to check if a container exists * the corresponding directory must exists. Synchronous implementation @@ -627,7 +613,6 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { } /** - * * Calculates the object MD5 and returns it as eTag * * @param object @@ -645,6 +630,12 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { return eTag; } + @Override + protected boolean deleteAndVerifyContainerGone(final String container) { + storageStrategy.deleteContainer(container); + return containerExistsSyncImpl(container); + } + @Override public ListenableFuture putBlob(String container, Blob blob, PutOptions options) { // TODO implement options diff --git a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java index 629b7b4d1e..b4cce8a694 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java @@ -18,7 +18,6 @@ */ package org.jclouds.blobstore; -import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Throwables.getCausalChain; @@ -55,6 +54,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; +import javax.annotation.Resource; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Provider; @@ -98,6 +98,7 @@ import org.jclouds.io.Payloads; import org.jclouds.io.payloads.ByteArrayPayload; import org.jclouds.io.payloads.DelegatingPayload; import org.jclouds.javax.annotation.Nullable; +import org.jclouds.logging.Logger; import com.google.common.base.Function; import com.google.common.base.Predicate; @@ -116,6 +117,9 @@ import com.google.common.util.concurrent.ListenableFuture; */ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { + @Resource + protected Logger logger = Logger.NULL; + protected final DateService dateService; protected final Crypto crypto; protected final ConcurrentMap> containerToBlobs; @@ -126,13 +130,17 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { protected final Factory blobFactory; @Inject - protected TransientAsyncBlobStore(BlobStoreContext context, DateService dateService, Crypto crypto, - ConcurrentMap> containerToBlobs, Provider uriBuilders, - ConcurrentMap containerToLocation, - HttpGetOptionsListToGetOptions httpGetOptionsConverter, - IfDirectoryReturnNameStrategy ifDirectoryReturnName, Factory blobFactory, BlobUtils blobUtils, - @Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, Supplier defaultLocation, - @Memoized Supplier> locations) { + protected TransientAsyncBlobStore(BlobStoreContext context, + DateService dateService, Crypto crypto, + HttpGetOptionsListToGetOptions httpGetOptionsConverter, + IfDirectoryReturnNameStrategy ifDirectoryReturnName, + BlobUtils blobUtils, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, + Supplier defaultLocation, + @Memoized Supplier> locations, + Factory blobFactory, + ConcurrentMap> containerToBlobs, Provider uriBuilders, + ConcurrentMap containerToLocation) { super(context, blobUtils, service, defaultLocation, locations); this.blobFactory = blobFactory; this.dateService = dateService; @@ -153,65 +161,67 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { public ListenableFuture> list(final String container, ListContainerOptions options) { final Map realContents = getContainerToBlobs().get(container); + // Check if the container exists if (realContents == null) return immediateFailedFuture(cnfe(container)); SortedSet contents = newTreeSet(transform(realContents.keySet(), - new Function() { - public StorageMetadata apply(String key) { - Blob oldBlob = realContents.get(key); - checkState(oldBlob != null, "blob " + key + " is not present although it was in the list of " - + container); - checkState(oldBlob.getMetadata() != null, "blob " + container + "/" + key + " has no metadata"); - MutableBlobMetadata md = copy(oldBlob.getMetadata()); - String directoryName = ifDirectoryReturnName.execute(md); - if (directoryName != null) { - md.setName(directoryName); - md.setType(StorageType.RELATIVE_PATH); - } - return md; + new Function() { + public StorageMetadata apply(String key) { + Blob oldBlob = realContents.get(key); + checkState(oldBlob != null, "blob " + key + " is not present although it was in the list of " + + container); + checkState(oldBlob.getMetadata() != null, "blob " + container + "/" + key + " has no metadata"); + MutableBlobMetadata md = copy(oldBlob.getMetadata()); + String directoryName = ifDirectoryReturnName.execute(md); + if (directoryName != null) { + md.setName(directoryName); + md.setType(StorageType.RELATIVE_PATH); } - })); - - if (options.getMarker() != null) { - final String finalMarker = options.getMarker(); - StorageMetadata lastMarkerMetadata = find(contents, new Predicate() { - public boolean apply(StorageMetadata metadata) { - return metadata.getName().compareTo(finalMarker) > 0; - } - }); - contents = contents.tailSet(lastMarkerMetadata); - } - - final String prefix = options.getDir(); - if (prefix != null) { - contents = newTreeSet(filter(contents, new Predicate() { - public boolean apply(StorageMetadata o) { - return (o != null && o.getName().startsWith(prefix) && !o.getName().equals(prefix)); - } - })); - } + return md; + } + })); String marker = null; - int maxResults = options.getMaxResults() != null ? options.getMaxResults() : 1000; - if (!contents.isEmpty()) { - StorageMetadata lastElement = contents.last(); - contents = newTreeSet(Iterables.limit(contents, maxResults)); - if (!contents.contains(lastElement)) { - // Partial listing - marker = contents.last().getName(); + if (options != null) { + if (options.getMarker() != null) { + final String finalMarker = options.getMarker(); + StorageMetadata lastMarkerMetadata = find(contents, new Predicate() { + public boolean apply(StorageMetadata metadata) { + return metadata.getName().compareTo(finalMarker) > 0; + } + }); + contents = contents.tailSet(lastMarkerMetadata); } - } - final String delimiter = options.isRecursive() ? null : "/"; - if (delimiter != null) { - SortedSet commonPrefixes = newTreeSet( - transform(contents, new CommonPrefixes(prefix, delimiter))); - commonPrefixes.remove(CommonPrefixes.NO_PREFIX); + final String prefix = options.getDir(); + if (prefix != null) { + contents = newTreeSet(filter(contents, new Predicate() { + public boolean apply(StorageMetadata o) { + return (o != null && o.getName().startsWith(prefix) && !o.getName().equals(prefix)); + } + })); + } - contents = newTreeSet(filter(contents, new DelimiterFilter(prefix, delimiter))); + int maxResults = options.getMaxResults() != null ? options.getMaxResults() : 1000; + if (!contents.isEmpty()) { + StorageMetadata lastElement = contents.last(); + contents = newTreeSet(Iterables.limit(contents, maxResults)); + if (!contents.contains(lastElement)) { + // Partial listing + marker = contents.last().getName(); + } + } - Iterables. addAll(contents, transform(commonPrefixes, + final String delimiter = options.isRecursive() ? null : "/"; + if (delimiter != null) { + SortedSet commonPrefixes = newTreeSet( + transform(contents, new CommonPrefixes(prefix, delimiter))); + commonPrefixes.remove(CommonPrefixes.NO_PREFIX); + + contents = newTreeSet(filter(contents, new DelimiterFilter(prefix, delimiter))); + + Iterables. addAll(contents, transform(commonPrefixes, new Function() { public StorageMetadata apply(String o) { MutableStorageMetadata md = new MutableStorageMetadataImpl(); @@ -220,17 +230,18 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { return md; } })); - } + } - // trim metadata, if the response isn't supposed to be detailed. - if (!options.isDetailed()) { - for (StorageMetadata md : contents) { - md.getUserMetadata().clear(); + // trim metadata, if the response isn't supposed to be detailed. + if (!options.isDetailed()) { + for (StorageMetadata md : contents) { + md.getUserMetadata().clear(); + } } } return Futures.> immediateFuture(new PageSetImpl(contents, - marker)); + marker)); } @@ -246,10 +257,10 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { os = new ObjectOutputStream(bout); os.writeObject(in); ObjectInput is = new ObjectInputStream(new ByteArrayInputStream(bout.toByteArray())); - MutableBlobMetadata out = (MutableBlobMetadata) is.readObject(); - convertUserMetadataKeysToLowercase(out); - HttpUtils.copy(in.getContentMetadata(), out.getContentMetadata()); - return out; + MutableBlobMetadata metadata = (MutableBlobMetadata) is.readObject(); + convertUserMetadataKeysToLowercase(metadata); + HttpUtils.copy(in.getContentMetadata(), metadata.getContentMetadata()); + return metadata; } catch (Exception e) { throw propagate(e); } @@ -315,8 +326,8 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { * {@inheritDoc} */ @Override - public ListenableFuture containerExists(final String container) { - return immediateFuture(getContainerToBlobs().containsKey(container)); + public ListenableFuture containerExists(final String containerName) { + return immediateFuture(getContainerToBlobs().containsKey(containerName)); } /** @@ -324,16 +335,18 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { */ @Override public ListenableFuture> list() { + Iterable containers = getContainerToBlobs().keySet(); + return Futures.> immediateFuture(new PageSetImpl(transform( - getContainerToBlobs().keySet(), new Function() { - public StorageMetadata apply(String name) { - MutableStorageMetadata cmd = create(); - cmd.setName(name); - cmd.setType(StorageType.CONTAINER); - cmd.setLocation(getContainerToLocation().get(name)); - return cmd; - } - }), null)); + containers, new Function() { + public StorageMetadata apply(String name) { + MutableStorageMetadata cmd = create(); + cmd.setName(name); + cmd.setType(StorageType.CONTAINER); + cmd.setLocation(getContainerToLocation().get(name)); + return cmd; + } + }), null)); } protected MutableStorageMetadata create() { @@ -437,10 +450,6 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { return 0; } - public HttpRequest getCurrentRequest() { - return new HttpRequest("GET", URI.create("http://stub")); - } - public int incrementFailureCount() { return 0; } @@ -449,6 +458,11 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { } + @Override + public HttpRequest getCurrentRequest() { + return new HttpRequest("GET", URI.create("http://stub")); + } + @Override public void setCurrentRequest(HttpRequest request) { @@ -461,15 +475,18 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { * {@inheritDoc} */ @Override - public ListenableFuture putBlob(String containerName, Blob in) { - checkArgument(containerName != null, "containerName must be set"); - checkArgument(in != null, "blob must be set"); + public ListenableFuture putBlob(String containerName, Blob blob) { + checkNotNull(containerName, "containerName must be set"); + checkNotNull(blob, "blob must be set"); ConcurrentMap container = getContainerToBlobs().get(containerName); + String blobKey = blob.getMetadata().getName(); + + logger.debug("Put blob with key [%s] to container [%s]", blobKey, containerName); if (container == null) { return Futures.immediateFailedFuture(new IllegalStateException("containerName not found: " + containerName)); } - Blob blob = createUpdatedCopyOfBlobInContainer(containerName, in); + blob = createUpdatedCopyOfBlobInContainer(containerName, blob); container.put(blob.getMetadata().getName(), blob); @@ -536,74 +553,83 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { */ @Override public ListenableFuture getBlob(final String containerName, final String key, GetOptions options) { - if (!getContainerToBlobs().containsKey(containerName)) + logger.debug("Retrieving blob with key %s from container %s", key, containerName); + // If the container doesn't exist, an exception is thrown + if (!getContainerToBlobs().containsKey(containerName)) { + logger.debug("Container %s does not exist", containerName); return immediateFailedFuture(cnfe(containerName)); + } + // If the blob doesn't exist, a null object is returned Map realContents = getContainerToBlobs().get(containerName); - if (!realContents.containsKey(key)) + if (!realContents.containsKey(key)) { + logger.debug("Item %s does not exist in container %s", key, containerName); return immediateFuture(null); - - Blob object = realContents.get(key); - - if (options.getIfMatch() != null) { - if (!object.getMetadata().getETag().equals(options.getIfMatch())) - return immediateFailedFuture(returnResponseException(412)); } - if (options.getIfNoneMatch() != null) { - if (object.getMetadata().getETag().equals(options.getIfNoneMatch())) - return immediateFailedFuture(returnResponseException(304)); - } - if (options.getIfModifiedSince() != null) { - Date modifiedSince = options.getIfModifiedSince(); - if (object.getMetadata().getLastModified().before(modifiedSince)) { - HttpResponse response = new HttpResponse(304, null, null); - return immediateFailedFuture(new HttpResponseException(String.format("%1$s is before %2$s", object + + Blob blob = realContents.get(key); + + if (options != null) { + if (options.getIfMatch() != null) { + if (!blob.getMetadata().getETag().equals(options.getIfMatch())) + return immediateFailedFuture(returnResponseException(412)); + } + if (options.getIfNoneMatch() != null) { + if (blob.getMetadata().getETag().equals(options.getIfNoneMatch())) + return immediateFailedFuture(returnResponseException(304)); + } + if (options.getIfModifiedSince() != null) { + Date modifiedSince = options.getIfModifiedSince(); + if (blob.getMetadata().getLastModified().before(modifiedSince)) { + HttpResponse response = new HttpResponse(304, null, null); + return immediateFailedFuture(new HttpResponseException(String.format("%1$s is before %2$s", blob .getMetadata().getLastModified(), modifiedSince), null, response)); - } - - } - if (options.getIfUnmodifiedSince() != null) { - Date unmodifiedSince = options.getIfUnmodifiedSince(); - if (object.getMetadata().getLastModified().after(unmodifiedSince)) { - HttpResponse response = new HttpResponse(412, null, null); - return immediateFailedFuture(new HttpResponseException(String.format("%1$s is after %2$s", object - .getMetadata().getLastModified(), unmodifiedSince), null, response)); - } - } - Blob returnVal = copyBlob(object); - - if (options.getRanges() != null && options.getRanges().size() > 0) { - byte[] data; - try { - data = toByteArray(returnVal.getPayload().getInput()); - } catch (IOException e) { - return immediateFailedFuture(new RuntimeException(e)); - } - ByteArrayOutputStream out = new ByteArrayOutputStream(); - for (String s : options.getRanges()) { - if (s.startsWith("-")) { - int length = Integer.parseInt(s.substring(1)); - out.write(data, data.length - length, length); - } else if (s.endsWith("-")) { - int offset = Integer.parseInt(s.substring(0, s.length() - 1)); - out.write(data, offset, data.length - offset); - } else if (s.contains("-")) { - String[] firstLast = s.split("\\-"); - int offset = Integer.parseInt(firstLast[0]); - int last = Integer.parseInt(firstLast[1]); - int length = (last < data.length) ? last + 1 : data.length - offset; - out.write(data, offset, length); - } else { - return immediateFailedFuture(new IllegalArgumentException("first and last were null!")); } } - ContentMetadata cmd = returnVal.getPayload().getContentMetadata(); - returnVal.setPayload(out.toByteArray()); - HttpUtils.copy(cmd, returnVal.getPayload().getContentMetadata()); - returnVal.getPayload().getContentMetadata().setContentLength(new Long(out.toByteArray().length)); + if (options.getIfUnmodifiedSince() != null) { + Date unmodifiedSince = options.getIfUnmodifiedSince(); + if (blob.getMetadata().getLastModified().after(unmodifiedSince)) { + HttpResponse response = new HttpResponse(412, null, null); + return immediateFailedFuture(new HttpResponseException(String.format("%1$s is after %2$s", blob + .getMetadata().getLastModified(), unmodifiedSince), null, response)); + } + } + blob = copyBlob(blob); + + if (options.getRanges() != null && options.getRanges().size() > 0) { + byte[] data; + try { + data = toByteArray(blob.getPayload().getInput()); + } catch (IOException e) { + return immediateFailedFuture(new RuntimeException(e)); + } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + for (String s : options.getRanges()) { + if (s.startsWith("-")) { + int length = Integer.parseInt(s.substring(1)); + out.write(data, data.length - length, length); + } else if (s.endsWith("-")) { + int offset = Integer.parseInt(s.substring(0, s.length() - 1)); + out.write(data, offset, data.length - offset); + } else if (s.contains("-")) { + String[] firstLast = s.split("\\-"); + int offset = Integer.parseInt(firstLast[0]); + int last = Integer.parseInt(firstLast[1]); + int length = (last < data.length) ? last + 1 : data.length - offset; + out.write(data, offset, length); + } else { + return immediateFailedFuture(new IllegalArgumentException("first and last were null!")); + } + + } + ContentMetadata cmd = blob.getPayload().getContentMetadata(); + blob.setPayload(out.toByteArray()); + HttpUtils.copy(cmd, blob.getPayload().getContentMetadata()); + blob.getPayload().getContentMetadata().setContentLength(new Long(out.toByteArray().length)); + } } - checkNotNull(returnVal.getPayload(), "payload " + returnVal); - return immediateFuture(returnVal); + checkNotNull(blob.getPayload(), "payload " + blob); + return immediateFuture(blob); } /** @@ -633,7 +659,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { } @Override - protected boolean deleteAndVerifyContainerGone(String container) { + protected boolean deleteAndVerifyContainerGone(final String container) { getContainerToBlobs().remove(container); return getContainerToBlobs().containsKey(container); } @@ -650,10 +676,9 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { @Override public ListenableFuture createContainerInLocation(Location location, String container, - CreateContainerOptions options) { + CreateContainerOptions options) { if (options.isPublicRead()) throw new UnsupportedOperationException("publicRead"); return createContainerInLocation(location, container); } - } From 05ab8b16df1b8a8b377bd8c555eb59ec9883d7b4 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Mon, 7 May 2012 16:22:12 -0700 Subject: [PATCH 041/148] Issue 918:update to latest GAE sdk 1.6.5 --- demos/perftest/pom.xml | 92 +++++++++++++++++++++++++++++++++++++--- demos/tweetstore/pom.xml | 6 +-- drivers/gae/pom.xml | 6 +-- project/pom.xml | 4 +- 4 files changed, 93 insertions(+), 15 deletions(-) diff --git a/demos/perftest/pom.xml b/demos/perftest/pom.xml index ca3e3a4b74..327d12ff70 100644 --- a/demos/perftest/pom.xml +++ b/demos/perftest/pom.xml @@ -34,9 +34,8 @@ 100 ${test.aws.identity} ${test.aws.credential} - org.jclouds.aws.s3.blobstore.integration.AWSS3TestInitializer - 1.0-SNAPSHOT - 1.4.3 + 1.5.0-SNAPSHOT + 1.6.5
@@ -48,18 +47,23 @@ com.amazonaws aws-java-sdk - 1.1.9 + 1.3.8 net.java.dev.jets3t jets3t - 0.8.0 + 0.9.0 org.jclouds.driver jclouds-enterprise ${jclouds.version} + + org.jclouds.driver + jclouds-netty + ${jclouds.version} + org.jclouds.driver jclouds-apachehc @@ -94,9 +98,9 @@ com.google.appengine - appengine-tools-sdk + appengine-api-1.0-sdk ${appengine.sdk.version} - test + compile com.google.appengine @@ -134,6 +138,80 @@ + + com.ning.maven.plugins + maven-duplicate-finder-plugin + + + + + + + com.amazonaws + aws-java-sdk + 1.3.8 + + + com.google.appengine + appengine-api-1.0-sdk + ${appengine.sdk.version} + + + + META-INF/javamail.providers + + + + + + + com.google.appengine + appengine-api-1.0-sdk + ${appengine.sdk.version} + + + com.google.appengine + appengine-testing + ${appengine.sdk.version} + test + + + + com.google + + + + + + + + live + + + + org.apache.maven.plugins + maven-surefire-plugin + + + integration + integration-test + + test + + + + ${test.aws-s3.identity} + ${test.aws-s3.credential} + ${test.aws-s3.loopcount} + + + + + + + + + diff --git a/demos/tweetstore/pom.xml b/demos/tweetstore/pom.xml index 54a9348141..6662f30abf 100644 --- a/demos/tweetstore/pom.xml +++ b/demos/tweetstore/pom.xml @@ -175,12 +175,12 @@ com.google.appengine appengine-api-1.0-sdk - 1.6.1 + 1.6.5 com.google.appengine appengine-tools-sdk - 1.6.1 + 1.6.5 test @@ -240,7 +240,7 @@ com.google.appengine appengine-tools-sdk - 1.6.1 + 1.6.5 test diff --git a/drivers/gae/pom.xml b/drivers/gae/pom.xml index 4de817934b..1722205f6c 100644 --- a/drivers/gae/pom.xml +++ b/drivers/gae/pom.xml @@ -56,7 +56,7 @@ com.google.appengine appengine-api-1.0-sdk - 1.6.1 + 1.6.5 @@ -74,13 +74,13 @@ com.google.appengine appengine-api-stubs - 1.6.1 + 1.6.5 test com.google.appengine appengine-testing - 1.6.1 + 1.6.5 test diff --git a/project/pom.xml b/project/pom.xml index eb5f893285..6992103b12 100644 --- a/project/pom.xml +++ b/project/pom.xml @@ -318,12 +318,12 @@ com.google.appengine appengine-api-1.0-sdk - 1.4.3 + 1.6.5 com.google.appengine appengine-testing - 1.4.3 + 1.6.5 test From 2698736301ce81eb4175dc2c327bfc1a845741d9 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Mon, 7 May 2012 16:22:25 -0700 Subject: [PATCH 042/148] made perftest compile and run again --- .../s3/BaseJCloudsPerformanceLiveTest.java | 19 +-------- .../aws/s3/BasePerformanceLiveTest.java | 6 +++ .../JCloudsApacheHCPerformanceLiveTest.java | 41 ++++++++++-------- .../aws/s3/JCloudsGaePerformanceLiveTest.java | 42 ++++++++----------- .../aws/s3/JCloudsPerformanceLiveTest.java | 39 +++++++++-------- 5 files changed, 70 insertions(+), 77 deletions(-) diff --git a/demos/perftest/src/test/java/org/jclouds/aws/s3/BaseJCloudsPerformanceLiveTest.java b/demos/perftest/src/test/java/org/jclouds/aws/s3/BaseJCloudsPerformanceLiveTest.java index 6387704b32..0a0b8fb71e 100644 --- a/demos/perftest/src/test/java/org/jclouds/aws/s3/BaseJCloudsPerformanceLiveTest.java +++ b/demos/perftest/src/test/java/org/jclouds/aws/s3/BaseJCloudsPerformanceLiveTest.java @@ -40,25 +40,8 @@ import com.google.common.base.Throwables; * @author Adrian Cole */ public abstract class BaseJCloudsPerformanceLiveTest extends BasePerformanceLiveTest { - // boolean get - // ( - // int id) throws Exception { - // S3Bucket s3Bucket = new S3Bucket(); - // s3Bucket.setName(bucketPrefix + "-jclouds-puts"); - // S3Object object = new - // S3Object(); - // object.setKey(id + ""); - // //object.setContentType("text/plain"); - // object.setContentType("application/octetstream"); - // //object.setPayload("this is a test"); - // object.setPayload(test); - // return getApi()Provider.getObject(s3Bucket, - // object.getKey()).get(120,TimeUnit.SECONDS) != - // S3Object.NOT_FOUND; - // } - protected void overrideWithSysPropertiesAndPrint(Properties overrides, String contextName) { - overrides.putAll(System.getProperties()); + protected void printPropertiesOfContext(Properties overrides, String contextName) { System.out.printf("%s: loopCount(%s), perContext(%s), perHost(%s), ioWorkers(%s), userThreads(%s)%n", contextName, loopCount, overrides.getProperty(PROPERTY_MAX_CONNECTIONS_PER_CONTEXT), overrides .getProperty(PROPERTY_MAX_CONNECTIONS_PER_HOST), overrides.getProperty(PROPERTY_IO_WORKER_THREADS), diff --git a/demos/perftest/src/test/java/org/jclouds/aws/s3/BasePerformanceLiveTest.java b/demos/perftest/src/test/java/org/jclouds/aws/s3/BasePerformanceLiveTest.java index ab0066d2db..1c3e14ce02 100644 --- a/demos/perftest/src/test/java/org/jclouds/aws/s3/BasePerformanceLiveTest.java +++ b/demos/perftest/src/test/java/org/jclouds/aws/s3/BasePerformanceLiveTest.java @@ -46,9 +46,15 @@ import com.google.common.util.concurrent.ListenableFuture; * @author Adrian Cole */ public abstract class BasePerformanceLiveTest extends BaseBlobStoreIntegrationTest { + + public BasePerformanceLiveTest(){ + provider = "aws-s3"; + } + static { containerCount = 1; } + protected int timeoutSeconds = 15; protected int loopCount = Integer.parseInt(System.getProperty("test.aws-s3.loopcount", "1000")); protected ExecutorService exec; diff --git a/demos/perftest/src/test/java/org/jclouds/aws/s3/JCloudsApacheHCPerformanceLiveTest.java b/demos/perftest/src/test/java/org/jclouds/aws/s3/JCloudsApacheHCPerformanceLiveTest.java index 9664fb9731..507f9fb22c 100644 --- a/demos/perftest/src/test/java/org/jclouds/aws/s3/JCloudsApacheHCPerformanceLiveTest.java +++ b/demos/perftest/src/test/java/org/jclouds/aws/s3/JCloudsApacheHCPerformanceLiveTest.java @@ -28,42 +28,47 @@ import static org.jclouds.Constants.PROPERTY_USER_THREADS; import java.util.Properties; import java.util.concurrent.Executors; -import org.jclouds.blobstore.BlobStoreContextFactory; import org.jclouds.enterprise.config.EnterpriseConfigurationModule; import org.jclouds.http.apachehc.config.ApacheHCHttpCommandExecutorServiceModule; import org.jclouds.logging.config.NullLoggingModule; +import org.jclouds.netty.config.NettyPayloadModule; import org.jclouds.s3.S3AsyncClient; -import org.testng.ITestContext; -import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import com.google.common.collect.ImmutableSet; +import com.google.inject.Module; -@Test(sequential = true, groups = { "live" }) +@Test(singleThreaded = true, timeOut = 2 * 60 * 1000, groups = "live", testName = "JCloudsApacheHCPerformanceLiveTest") public class JCloudsApacheHCPerformanceLiveTest extends BaseJCloudsPerformanceLiveTest { - - @Override - @BeforeClass(groups = { "integration", "live" }) - public void setUpResourcesOnThisThread(ITestContext testContext) throws Exception { + + public JCloudsApacheHCPerformanceLiveTest(){ exec = Executors.newCachedThreadPool(); - String accesskeyid = System.getProperty("test.aws-s3.identity"); - String secretkey = System.getProperty("test.aws-s3.credential"); - Properties overrides = new Properties(); + } + + @Override + protected Properties setupProperties() { + Properties overrides = super.setupProperties(); overrides.setProperty(PROPERTY_SO_TIMEOUT, 5000 + ""); overrides.setProperty(PROPERTY_CONNECTION_TIMEOUT, 5000 + ""); overrides.setProperty(PROPERTY_MAX_CONNECTIONS_PER_CONTEXT, 20 + ""); overrides.setProperty(PROPERTY_MAX_CONNECTIONS_PER_HOST, 0 + ""); overrides.setProperty(PROPERTY_IO_WORKER_THREADS, 50 + ""); overrides.setProperty(PROPERTY_USER_THREADS, 0 + ""); - String contextName = "enterprise"; - overrideWithSysPropertiesAndPrint(overrides, contextName); - context = new BlobStoreContextFactory().createContext("aws-s3", accesskeyid, secretkey, ImmutableSet.of( - new NullLoggingModule(), new ApacheHCHttpCommandExecutorServiceModule(), - new EnterpriseConfigurationModule()), overrides); + printPropertiesOfContext(overrides, "apachehc"); + return overrides; } - + + @Override + protected Iterable setupModules() { + return ImmutableSet.builder() + .add(new NullLoggingModule()) + .add(new NettyPayloadModule()) + .add(new ApacheHCHttpCommandExecutorServiceModule()) + .add(new EnterpriseConfigurationModule()).build(); + } + @Override public S3AsyncClient getApi() { - return (S3AsyncClient) context.getProviderSpecificContext().getAsyncApi(); + return view.unwrap(AWSS3ApiMetadata.CONTEXT_TOKEN).getAsyncApi(); } } diff --git a/demos/perftest/src/test/java/org/jclouds/aws/s3/JCloudsGaePerformanceLiveTest.java b/demos/perftest/src/test/java/org/jclouds/aws/s3/JCloudsGaePerformanceLiveTest.java index 1f51494d51..742d0f198d 100644 --- a/demos/perftest/src/test/java/org/jclouds/aws/s3/JCloudsGaePerformanceLiveTest.java +++ b/demos/perftest/src/test/java/org/jclouds/aws/s3/JCloudsGaePerformanceLiveTest.java @@ -25,19 +25,17 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeoutException; -import org.jclouds.blobstore.BlobStoreContext; -import org.jclouds.blobstore.BlobStoreContextFactory; -import org.jclouds.gae.config.GoogleAppEngineConfigurationModule; +import org.jclouds.enterprise.config.EnterpriseConfigurationModule; +import org.jclouds.gae.config.AsyncGoogleAppEngineConfigurationModule; import org.jclouds.logging.config.NullLoggingModule; import org.jclouds.s3.S3AsyncClient; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import com.google.appengine.tools.development.testing.LocalServiceTestHelper; import com.google.appengine.tools.development.testing.LocalURLFetchServiceTestConfig; import com.google.common.collect.ImmutableSet; +import com.google.inject.Module; /** * @@ -45,7 +43,7 @@ import com.google.common.collect.ImmutableSet; * * @author Adrian Cole */ -@Test(enabled = false, sequential = true, groups = { "disabled" }) +@Test(enabled = false, singleThreaded = true, timeOut = 2 * 60 * 1000, groups = "live", testName = "JCloudsGaePerformanceLiveTest") public class JCloudsGaePerformanceLiveTest extends BaseJCloudsPerformanceLiveTest { @Override @@ -134,27 +132,23 @@ public class JCloudsGaePerformanceLiveTest extends BaseJCloudsPerformanceLiveTes new LocalServiceTestHelper(new LocalURLFetchServiceTestConfig()).setUp(); } - private BlobStoreContext perfContext; - - @BeforeClass(groups = { "live" }) - void setup() { - String accesskeyid = System.getProperty("jclouds.test.identity"); - String secretkey = System.getProperty("jclouds.test.credential"); - Properties overrides = new Properties(); - String contextName = "gae"; - overrideWithSysPropertiesAndPrint(overrides, contextName); - context = new BlobStoreContextFactory().createContext("aws-s3", accesskeyid, secretkey, ImmutableSet.of( - new NullLoggingModule(), new GoogleAppEngineConfigurationModule()), overrides); + @Override + protected Properties setupProperties() { + Properties overrides = super.setupProperties(); + printPropertiesOfContext(overrides, "apachehc"); + return overrides; } - - @AfterClass(groups = { "live" }) - void tearDown() { - if (perfContext != null) - perfContext.close(); + + @Override + protected Iterable setupModules() { + return ImmutableSet.builder() + .add(new NullLoggingModule()) + .add(new AsyncGoogleAppEngineConfigurationModule()) + .add(new EnterpriseConfigurationModule()).build(); } - + @Override public S3AsyncClient getApi() { - return (S3AsyncClient) perfContext.getProviderSpecificContext().getAsyncApi(); + return view.unwrap(AWSS3ApiMetadata.CONTEXT_TOKEN).getAsyncApi(); } } \ No newline at end of file diff --git a/demos/perftest/src/test/java/org/jclouds/aws/s3/JCloudsPerformanceLiveTest.java b/demos/perftest/src/test/java/org/jclouds/aws/s3/JCloudsPerformanceLiveTest.java index 40b0c65517..4335999c7f 100644 --- a/demos/perftest/src/test/java/org/jclouds/aws/s3/JCloudsPerformanceLiveTest.java +++ b/demos/perftest/src/test/java/org/jclouds/aws/s3/JCloudsPerformanceLiveTest.java @@ -26,15 +26,14 @@ import static org.jclouds.Constants.PROPERTY_USER_THREADS; import java.util.Properties; import java.util.concurrent.Executors; -import org.jclouds.blobstore.BlobStoreContextFactory; import org.jclouds.enterprise.config.EnterpriseConfigurationModule; import org.jclouds.logging.config.NullLoggingModule; +import org.jclouds.netty.config.NettyPayloadModule; import org.jclouds.s3.S3AsyncClient; -import org.testng.ITestContext; -import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import com.google.common.collect.ImmutableSet; +import com.google.inject.Module; /** * Tests the default JClouds client. @@ -42,28 +41,34 @@ import com.google.common.collect.ImmutableSet; * @author Adrian Cole * */ -@Test(sequential = true, timeOut = 2 * 60 * 1000, groups = { "live" }) +@Test(singleThreaded = true, timeOut = 2 * 60 * 1000, groups = "live", testName = "JCloudsPerformanceLiveTest") public class JCloudsPerformanceLiveTest extends BaseJCloudsPerformanceLiveTest { - - @Override - @BeforeClass(groups = { "integration", "live" }) - public void setUpResourcesOnThisThread(ITestContext testContext) throws Exception { + + public JCloudsPerformanceLiveTest(){ exec = Executors.newCachedThreadPool(); - String accesskeyid = System.getProperty("test.aws-s3.identity"); - String secretkey = System.getProperty("test.aws-s3.credential"); - Properties overrides = new Properties(); + } + + @Override + protected Properties setupProperties() { + Properties overrides = super.setupProperties(); overrides.setProperty(PROPERTY_MAX_CONNECTIONS_PER_CONTEXT, 50 + ""); overrides.setProperty(PROPERTY_MAX_CONNECTIONS_PER_HOST, 30 + ""); overrides.setProperty(PROPERTY_IO_WORKER_THREADS, 50 + ""); overrides.setProperty(PROPERTY_USER_THREADS, 0 + ""); - String contextName = "standard"; - overrideWithSysPropertiesAndPrint(overrides, contextName); - context = new BlobStoreContextFactory().createContext("aws-s3", accesskeyid, secretkey, ImmutableSet.of( - new NullLoggingModule(), new EnterpriseConfigurationModule()), overrides); + printPropertiesOfContext(overrides, "default"); + return overrides; } - + + @Override + protected Iterable setupModules() { + return ImmutableSet.builder() + .add(new NullLoggingModule()) + .add(new NettyPayloadModule()) + .add(new EnterpriseConfigurationModule()).build(); + } + @Override public S3AsyncClient getApi() { - return (S3AsyncClient) context.getProviderSpecificContext().getAsyncApi(); + return view.unwrap(AWSS3ApiMetadata.CONTEXT_TOKEN).getAsyncApi(); } } \ No newline at end of file From 442c51eb3ca8a146e77208284bab65b4dc429a0d Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Mon, 7 May 2012 16:38:03 -0700 Subject: [PATCH 043/148] Introduce TransientStorageStrategy This allows code from the filesystem blobstore to be more similar to the transient blobstore. This commit also corrects a bug where blobExists did not throw an exception when the container did not exist. --- .../filesystem/FilesystemAsyncBlobStore.java | 25 +++----- .../FilesystemAsyncBlobStoreTest.java | 8 ++- .../blobstore/TransientAsyncBlobStore.java | 61 +++++++++++++------ 3 files changed, 57 insertions(+), 37 deletions(-) diff --git a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java index c462b44661..2f7b40e41e 100644 --- a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java +++ b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java @@ -148,7 +148,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { public ListenableFuture> list(final String container, ListContainerOptions options) { // Check if the container exists - if (!containerExistsSyncImpl(container)) + if (!storageStrategy.containerExists(container)) return immediateFailedFuture(cnfe(container)); // Loading blobs from container @@ -242,7 +242,9 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { } private ContainerNotFoundException cnfe(final String name) { - return new ContainerNotFoundException(name, String.format("container %s not in filesystem", name)); + return new ContainerNotFoundException(name, String.format( + "container %s not in %s", name, + storageStrategy.getAllContainerNames())); } public static MutableBlobMetadata copy(MutableBlobMetadata in) { @@ -304,7 +306,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { */ @Override public ListenableFuture containerExists(final String containerName) { - return immediateFuture(containerExistsSyncImpl(containerName)); + return immediateFuture(storageStrategy.containerExists(containerName)); } /** @@ -503,6 +505,8 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { */ @Override public ListenableFuture blobExists(final String containerName, final String key) { + if (!storageStrategy.containerExists(containerName)) + return immediateFailedFuture(cnfe(containerName)); return immediateFuture(storageStrategy.blobExists(containerName, key)); } @@ -513,7 +517,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { public ListenableFuture getBlob(final String containerName, final String key, GetOptions options) { logger.debug("Retrieving blob with key %s from container %s", key, containerName); // If the container doesn't exist, an exception is thrown - if (!containerExistsSyncImpl(containerName)) { + if (!storageStrategy.containerExists(containerName)) { logger.debug("Container %s does not exist", containerName); return immediateFailedFuture(cnfe(containerName)); } @@ -601,17 +605,6 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { } } - /** - * Each container is a directory, so in order to check if a container exists - * the corresponding directory must exists. Synchronous implementation - * - * @param containerName - * @return - */ - private boolean containerExistsSyncImpl(String containerName) { - return storageStrategy.containerExists(containerName); - } - /** * Calculates the object MD5 and returns it as eTag * @@ -633,7 +626,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { @Override protected boolean deleteAndVerifyContainerGone(final String container) { storageStrategy.deleteContainer(container); - return containerExistsSyncImpl(container); + return storageStrategy.containerExists(container); } @Override diff --git a/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemAsyncBlobStoreTest.java b/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemAsyncBlobStoreTest.java index ce936344eb..86860b1796 100644 --- a/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemAsyncBlobStoreTest.java +++ b/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemAsyncBlobStoreTest.java @@ -565,8 +565,12 @@ public class FilesystemAsyncBlobStoreTest { // when location doesn't exists blobKey = TestUtils.createRandomBlobKey(); - result = blobStore.blobExists(CONTAINER_NAME, blobKey); - assertFalse(result, "Blob exists"); + try { + blobStore.blobExists(CONTAINER_NAME, blobKey); + fail(); + } catch (ContainerNotFoundException cnfe) { + // expected + } // when location exists blobStore.createContainerInLocation(null, CONTAINER_NAME); diff --git a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java index b4cce8a694..6168e8b59a 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java @@ -128,6 +128,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { protected final HttpGetOptionsListToGetOptions httpGetOptionsConverter; protected final IfDirectoryReturnNameStrategy ifDirectoryReturnName; protected final Factory blobFactory; + protected final TransientStorageStrategy storageStrategy; @Inject protected TransientAsyncBlobStore(BlobStoreContext context, @@ -152,6 +153,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { this.ifDirectoryReturnName = ifDirectoryReturnName; getContainerToLocation().put("stub", defaultLocation.get()); getContainerToBlobs().put("stub", new ConcurrentHashMap()); + this.storageStrategy = new TransientStorageStrategy(); } /** @@ -246,8 +248,9 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { } private ContainerNotFoundException cnfe(final String name) { - return new ContainerNotFoundException(name, String.format("container %s not in %s", name, getContainerToBlobs() - .keySet())); + return new ContainerNotFoundException(name, String.format( + "container %s not in %s", name, + storageStrategy.getAllContainerNames())); } public static MutableBlobMetadata copy(MutableBlobMetadata in) { @@ -285,9 +288,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { */ @Override public ListenableFuture removeBlob(final String container, final String key) { - if (getContainerToBlobs().containsKey(container)) { - getContainerToBlobs().get(container).remove(key); - } + storageStrategy.removeBlob(container, key); return immediateFuture(null); } @@ -305,17 +306,15 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { */ @Override public ListenableFuture deleteContainer(final String container) { - if (getContainerToBlobs().containsKey(container)) { - getContainerToBlobs().remove(container); - } + deleteAndVerifyContainerGone(container); return immediateFuture(null); } public ListenableFuture deleteContainerIfEmpty(final String container) { Boolean returnVal = true; - if (getContainerToBlobs().containsKey(container)) { + if (storageStrategy.containerExists(container)) { if (getContainerToBlobs().get(container).size() == 0) - getContainerToBlobs().remove(container); + storageStrategy.deleteContainer(container); else returnVal = false; } @@ -327,7 +326,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { */ @Override public ListenableFuture containerExists(final String containerName) { - return immediateFuture(getContainerToBlobs().containsKey(containerName)); + return immediateFuture(storageStrategy.containerExists(containerName)); } /** @@ -335,7 +334,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { */ @Override public ListenableFuture> list() { - Iterable containers = getContainerToBlobs().keySet(); + Iterable containers = storageStrategy.getAllContainerNames(); return Futures.> immediateFuture(new PageSetImpl(transform( containers, new Function() { @@ -358,7 +357,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { */ @Override public ListenableFuture createContainerInLocation(final Location location, final String name) { - if (getContainerToBlobs().containsKey(name)) { + if (storageStrategy.containerExists(name)) { return immediateFuture(Boolean.FALSE); } getContainerToBlobs().put(name, new ConcurrentHashMap()); @@ -542,10 +541,9 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { */ @Override public ListenableFuture blobExists(final String containerName, final String key) { - if (!getContainerToBlobs().containsKey(containerName)) + if (!storageStrategy.containerExists(containerName)) return immediateFailedFuture(cnfe(containerName)); - Map realContents = getContainerToBlobs().get(containerName); - return immediateFuture(realContents.containsKey(key)); + return immediateFuture(storageStrategy.blobExists(containerName, key)); } /** @@ -555,7 +553,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { public ListenableFuture getBlob(final String containerName, final String key, GetOptions options) { logger.debug("Retrieving blob with key %s from container %s", key, containerName); // If the container doesn't exist, an exception is thrown - if (!getContainerToBlobs().containsKey(containerName)) { + if (!storageStrategy.containerExists(containerName)) { logger.debug("Container %s does not exist", containerName); return immediateFailedFuture(cnfe(containerName)); } @@ -660,8 +658,8 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { @Override protected boolean deleteAndVerifyContainerGone(final String container) { - getContainerToBlobs().remove(container); - return getContainerToBlobs().containsKey(container); + storageStrategy.deleteContainer(container); + return storageStrategy.containerExists(container); } private ConcurrentMap getContainerToLocation() { @@ -681,4 +679,29 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { throw new UnsupportedOperationException("publicRead"); return createContainerInLocation(location, container); } + + private class TransientStorageStrategy { + public Iterable getAllContainerNames() { + return getContainerToBlobs().keySet(); + } + + public boolean containerExists(final String containerName) { + return getContainerToBlobs().containsKey(containerName); + } + + public void deleteContainer(final String containerName) { + getContainerToBlobs().remove(containerName); + } + + public boolean blobExists(final String containerName, final String blobName) { + Map map = containerToBlobs.get(containerName); + return map != null && map.containsKey(blobName); + } + + public void removeBlob(final String containerName, final String blobName) { + if (storageStrategy.containerExists(containerName)) { + getContainerToBlobs().get(containerName).remove(blobName); + } + } + } } From 00e28b99107ef8ee9cb4f6925f60a3becdc8d8d6 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Mon, 7 May 2012 16:54:29 -0700 Subject: [PATCH 044/148] fixed syntax of perftest --- .../aws/s3/AmazonPerformanceLiveTest.java | 23 ++++++++------ .../aws/s3/Jets3tPerformanceLiveTest.java | 31 ++++++++++++------- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/demos/perftest/src/test/java/org/jclouds/aws/s3/AmazonPerformanceLiveTest.java b/demos/perftest/src/test/java/org/jclouds/aws/s3/AmazonPerformanceLiveTest.java index da9ee4ed29..9c6d63e9ea 100644 --- a/demos/perftest/src/test/java/org/jclouds/aws/s3/AmazonPerformanceLiveTest.java +++ b/demos/perftest/src/test/java/org/jclouds/aws/s3/AmazonPerformanceLiveTest.java @@ -21,13 +21,13 @@ package org.jclouds.aws.s3; import java.io.ByteArrayInputStream; import java.io.File; import java.io.InputStream; +import java.util.Properties; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.Future; -import org.testng.ITestContext; -import org.testng.annotations.BeforeClass; +import org.jclouds.blobstore.BlobStoreContext; import org.testng.annotations.Test; import com.amazonaws.auth.BasicAWSCredentials; @@ -35,24 +35,29 @@ import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.model.PutObjectRequest; +import com.google.inject.Module; /** * Runs operations that amazon s3 sample code is capable of performing. * * @author Adrian Cole */ -@Test(sequential = true, timeOut = 2 * 60 * 1000, groups = { "live" }) +@Test(singleThreaded = true, timeOut = 2 * 60 * 1000, groups = "live", testName = "AmazonPerformanceLiveTest") public class AmazonPerformanceLiveTest extends BasePerformanceLiveTest { + + public AmazonPerformanceLiveTest(){ + exec = Executors.newCachedThreadPool(); + } + private AmazonS3 s3; - @BeforeClass(groups = { "integration", "live" }) - public void setUpResourcesOnThisThread(ITestContext testContext) throws Exception { - super.setUpResourcesOnThisThread(testContext); - exec = Executors.newCachedThreadPool(); + @Override + protected BlobStoreContext createView(Properties props, Iterable modules) { s3 = new AmazonS3Client(new BasicAWSCredentials(System.getProperty("test.aws-s3.identity"), - System.getProperty("test.aws-s3.credential"))); + System.getProperty("test.aws-s3.credential"))); + return super.createView(props, modules); } - + @Override @Test(enabled = false) public void testPutStringSerial() throws Exception { diff --git a/demos/perftest/src/test/java/org/jclouds/aws/s3/Jets3tPerformanceLiveTest.java b/demos/perftest/src/test/java/org/jclouds/aws/s3/Jets3tPerformanceLiveTest.java index 7f442a456b..fc06a48a63 100644 --- a/demos/perftest/src/test/java/org/jclouds/aws/s3/Jets3tPerformanceLiveTest.java +++ b/demos/perftest/src/test/java/org/jclouds/aws/s3/Jets3tPerformanceLiveTest.java @@ -21,36 +21,46 @@ package org.jclouds.aws.s3; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.util.Properties; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import org.jclouds.blobstore.BlobStoreContext; import org.jets3t.service.S3Service; +import org.jets3t.service.S3ServiceException; import org.jets3t.service.impl.rest.httpclient.RestS3Service; import org.jets3t.service.model.S3Object; import org.jets3t.service.security.AWSCredentials; -import org.testng.ITestContext; -import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import com.google.appengine.repackaged.com.google.common.base.Throwables; +import com.google.inject.Module; /** * Runs operations that jets3t is capable of performing. * * @author Adrian Cole */ -@Test(sequential = true, timeOut = 2 * 60 * 1000, groups = { "live" }) +@Test(singleThreaded = true, timeOut = 2 * 60 * 1000, groups = "live", testName = "Jets3tPerformanceLiveTest") public class Jets3tPerformanceLiveTest extends BasePerformanceLiveTest { - private S3Service jetClient; - - @BeforeClass(groups = { "integration", "live" }) - public void setUpResourcesOnThisThread(ITestContext testContext) throws Exception { - super.setUpResourcesOnThisThread(testContext); + + public Jets3tPerformanceLiveTest(){ exec = Executors.newCachedThreadPool(); - jetClient = new RestS3Service(new AWSCredentials(System.getProperty("test.aws-s3.identity"), - System.getProperty("test.aws-s3.credential"))); + } + + private S3Service jetClient; + + @Override + protected BlobStoreContext createView(Properties props, Iterable modules) { + try { + jetClient = new RestS3Service(new AWSCredentials(System.getProperty("test.aws-s3.identity"), + System.getProperty("test.aws-s3.credential"))); + } catch (S3ServiceException e) { + throw Throwables.propagate(e); + } + return super.createView(props, modules); } @Override @@ -93,7 +103,6 @@ public class Jets3tPerformanceLiveTest extends BasePerformanceLiveTest { @Test(enabled = false) protected Future putByteArray(String bucket, String key, byte[] data, String contentType) { throw new UnsupportedOperationException(); - } @Override From 743773aeefb34c33c037c3df28afc55f70224f41 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Tue, 8 May 2012 10:14:14 -0700 Subject: [PATCH 045/148] formatting --- .../ec2/config/NovaEC2RestClientModule.java | 15 ++++++++++++++- .../NovaEC2InstanceClientExpectTest.java | 3 +-- .../v1_1/config/NovaRestClientModule.java | 19 ++++++++++++++++++- .../openstack/nova/v1_1/domain/Server.java | 2 +- .../extensions/AdminActionsAsyncClient.java | 3 --- .../v1_1/extensions/VolumeAsyncClient.java | 4 ++-- .../nova/v1_1/extensions/VolumeClient.java | 4 ++-- .../v1_1/options/CreateVolumeOptions.java | 12 ++---------- .../extensions/VolumeClientExpectTest.java | 5 ++++- .../v1_1/internal/BaseNovaClientLiveTest.java | 4 ---- .../parse/ParseServerDetailsEssexTest.java | 2 +- .../openstack/OpenStackAuthClient.java | 3 +++ .../keystone/v2_0/domain/Tenant.java | 2 -- .../v2_0/parse/ParseApiMetadataTest.java | 7 ------- .../v1_0/handlers/GlanceErrorHandler.java | 3 --- .../keystone/v2_0/UserClientLiveTest.java | 1 - .../internal/BaseKeystoneClientLiveTest.java | 3 --- .../BaseKeystoneRestClientExpectTest.java | 3 --- 18 files changed, 48 insertions(+), 47 deletions(-) diff --git a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2RestClientModule.java b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2RestClientModule.java index 311c9589a0..162b97b765 100644 --- a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2RestClientModule.java +++ b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2RestClientModule.java @@ -25,7 +25,20 @@ import javax.inject.Singleton; import org.jclouds.ec2.EC2AsyncClient; import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.config.EC2RestClientModule; -import org.jclouds.ec2.services.*; +import org.jclouds.ec2.services.AMIAsyncClient; +import org.jclouds.ec2.services.AMIClient; +import org.jclouds.ec2.services.AvailabilityZoneAndRegionAsyncClient; +import org.jclouds.ec2.services.AvailabilityZoneAndRegionClient; +import org.jclouds.ec2.services.ElasticBlockStoreAsyncClient; +import org.jclouds.ec2.services.ElasticBlockStoreClient; +import org.jclouds.ec2.services.ElasticIPAddressAsyncClient; +import org.jclouds.ec2.services.ElasticIPAddressClient; +import org.jclouds.ec2.services.InstanceAsyncClient; +import org.jclouds.ec2.services.InstanceClient; +import org.jclouds.ec2.services.SecurityGroupAsyncClient; +import org.jclouds.ec2.services.SecurityGroupClient; +import org.jclouds.ec2.services.WindowsAsyncClient; +import org.jclouds.ec2.services.WindowsClient; import org.jclouds.ec2.suppliers.DescribeAvailabilityZonesInRegion; import org.jclouds.ec2.xml.CreateVolumeResponseHandler; import org.jclouds.ec2.xml.DescribeImagesResponseHandler; diff --git a/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/services/NovaEC2InstanceClientExpectTest.java b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/services/NovaEC2InstanceClientExpectTest.java index 5e55bb092b..87c845577a 100644 --- a/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/services/NovaEC2InstanceClientExpectTest.java +++ b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/services/NovaEC2InstanceClientExpectTest.java @@ -1,13 +1,12 @@ package org.jclouds.openstack.nova.ec2.services; import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNull; import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNull; import java.net.URI; import java.util.Set; -import org.jclouds.ec2.domain.Attachment; import org.jclouds.ec2.domain.BlockDevice; import org.jclouds.ec2.domain.Reservation; import org.jclouds.ec2.domain.RunningInstance; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java index 83e727cec8..b03338efcd 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java @@ -35,7 +35,24 @@ import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule; import org.jclouds.openstack.nova.v1_1.NovaAsyncClient; import org.jclouds.openstack.nova.v1_1.NovaClient; import org.jclouds.openstack.nova.v1_1.domain.Extension; -import org.jclouds.openstack.nova.v1_1.extensions.*; +import org.jclouds.openstack.nova.v1_1.extensions.AdminActionsAsyncClient; +import org.jclouds.openstack.nova.v1_1.extensions.AdminActionsClient; +import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPAsyncClient; +import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPClient; +import org.jclouds.openstack.nova.v1_1.extensions.HostAdministrationAsyncClient; +import org.jclouds.openstack.nova.v1_1.extensions.HostAdministrationClient; +import org.jclouds.openstack.nova.v1_1.extensions.KeyPairAsyncClient; +import org.jclouds.openstack.nova.v1_1.extensions.KeyPairClient; +import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupAsyncClient; +import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupClient; +import org.jclouds.openstack.nova.v1_1.extensions.ServerWithSecurityGroupsAsyncClient; +import org.jclouds.openstack.nova.v1_1.extensions.ServerWithSecurityGroupsClient; +import org.jclouds.openstack.nova.v1_1.extensions.SimpleTenantUsageAsyncClient; +import org.jclouds.openstack.nova.v1_1.extensions.SimpleTenantUsageClient; +import org.jclouds.openstack.nova.v1_1.extensions.VirtualInterfaceAsyncClient; +import org.jclouds.openstack.nova.v1_1.extensions.VirtualInterfaceClient; +import org.jclouds.openstack.nova.v1_1.extensions.VolumeAsyncClient; +import org.jclouds.openstack.nova.v1_1.extensions.VolumeClient; import org.jclouds.openstack.nova.v1_1.features.ExtensionAsyncClient; import org.jclouds.openstack.nova.v1_1.features.ExtensionClient; import org.jclouds.openstack.nova.v1_1.features.FlavorAsyncClient; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Server.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Server.java index e05c641a24..452145d652 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Server.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Server.java @@ -30,9 +30,9 @@ 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.Predicates; import com.google.common.base.Strings; +import com.google.common.base.Objects.ToStringHelper; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Maps; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsAsyncClient.java index 823adcc5ee..8c30451fb0 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsAsyncClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsAsyncClient.java @@ -18,8 +18,6 @@ */ package org.jclouds.openstack.nova.v1_1.extensions; -import java.util.concurrent.TimeUnit; - import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; @@ -27,7 +25,6 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; -import org.jclouds.concurrent.Timeout; import org.jclouds.openstack.filters.AuthenticateRequest; import org.jclouds.openstack.nova.v1_1.domain.BackupType; import org.jclouds.openstack.nova.v1_1.functions.ParseImageIdFromLocationHeader; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeAsyncClient.java index 97584b1d07..b27af9b13e 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeAsyncClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeAsyncClient.java @@ -30,11 +30,11 @@ import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.jclouds.openstack.filters.AuthenticateRequest; +import org.jclouds.openstack.nova.v1_1.domain.Volume; import org.jclouds.openstack.nova.v1_1.domain.VolumeAttachment; import org.jclouds.openstack.nova.v1_1.domain.VolumeSnapshot; -import org.jclouds.openstack.nova.v1_1.domain.Volume; -import org.jclouds.openstack.nova.v1_1.options.CreateVolumeSnapshotOptions; import org.jclouds.openstack.nova.v1_1.options.CreateVolumeOptions; +import org.jclouds.openstack.nova.v1_1.options.CreateVolumeSnapshotOptions; import org.jclouds.openstack.services.Extension; import org.jclouds.openstack.services.ServiceType; import org.jclouds.rest.annotations.ExceptionParser; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeClient.java index 3f40523d79..b4b9c66eee 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeClient.java @@ -22,11 +22,11 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import org.jclouds.concurrent.Timeout; +import org.jclouds.openstack.nova.v1_1.domain.Volume; import org.jclouds.openstack.nova.v1_1.domain.VolumeAttachment; import org.jclouds.openstack.nova.v1_1.domain.VolumeSnapshot; -import org.jclouds.openstack.nova.v1_1.domain.Volume; -import org.jclouds.openstack.nova.v1_1.options.CreateVolumeSnapshotOptions; import org.jclouds.openstack.nova.v1_1.options.CreateVolumeOptions; +import org.jclouds.openstack.nova.v1_1.options.CreateVolumeSnapshotOptions; import org.jclouds.openstack.services.Extension; import org.jclouds.openstack.services.ServiceType; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateVolumeOptions.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateVolumeOptions.java index e92f3566db..7ae912d8b8 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateVolumeOptions.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateVolumeOptions.java @@ -20,30 +20,22 @@ package org.jclouds.openstack.nova.v1_1.options; import static com.google.common.base.Objects.equal; import static com.google.common.base.Objects.toStringHelper; -import static com.google.common.base.Preconditions.*; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; -import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Set; import javax.inject.Inject; -import org.jclouds.encryption.internal.Base64; import org.jclouds.http.HttpRequest; -import org.jclouds.openstack.nova.v1_1.domain.SecurityGroup; import org.jclouds.rest.MapBinder; import org.jclouds.rest.binders.BindToJsonPayload; -import org.jclouds.util.Preconditions2; import com.google.common.base.Objects; import com.google.common.base.Objects.ToStringHelper; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import com.google.gson.annotations.SerializedName; /** * @author Adam Lowe diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeClientExpectTest.java index 3349f0ac92..605255971a 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeClientExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeClientExpectTest.java @@ -18,7 +18,10 @@ */ package org.jclouds.openstack.nova.v1_1.extensions; -import static org.testng.Assert.*; +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 java.net.URI; import java.util.Set; diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaClientLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaClientLiveTest.java index f5b9b025a5..e17572fbe6 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaClientLiveTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaClientLiveTest.java @@ -18,12 +18,8 @@ */ package org.jclouds.openstack.nova.v1_1.internal; -import static org.jclouds.compute.util.ComputeServiceUtils.getCores; -import static org.jclouds.compute.util.ComputeServiceUtils.getSpace; - import java.util.Properties; -import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest; import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties; import org.jclouds.openstack.nova.v1_1.NovaAsyncClient; diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseServerDetailsEssexTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseServerDetailsEssexTest.java index a4e7f51e51..98f239593a 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseServerDetailsEssexTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseServerDetailsEssexTest.java @@ -28,8 +28,8 @@ import org.jclouds.date.internal.SimpleDateFormatDateService; import org.jclouds.json.BaseSetParserTest; 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.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; diff --git a/common/openstack/src/main/java/org/jclouds/openstack/OpenStackAuthClient.java b/common/openstack/src/main/java/org/jclouds/openstack/OpenStackAuthClient.java index ebd9256066..8bdfd9482c 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/OpenStackAuthClient.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/OpenStackAuthClient.java @@ -33,4 +33,7 @@ import org.jclouds.openstack.domain.AuthenticationResponse; public interface OpenStackAuthClient { AuthenticationResponse authenticate(String user, String key); + + AuthenticationResponse authenticateStorage(String user, String key); + } \ No newline at end of file diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Tenant.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Tenant.java index 35f884629c..b0efc5365c 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Tenant.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Tenant.java @@ -23,8 +23,6 @@ import static com.google.common.base.Objects.equal; import static com.google.common.base.Objects.toStringHelper; import static com.google.common.base.Preconditions.checkNotNull; -import javax.ws.rs.DefaultValue; - import org.jclouds.javax.annotation.Nullable; import com.google.common.base.Objects; diff --git a/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseApiMetadataTest.java b/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseApiMetadataTest.java index 83718ee799..a49337f776 100644 --- a/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseApiMetadataTest.java +++ b/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseApiMetadataTest.java @@ -26,14 +26,7 @@ import javax.ws.rs.core.MediaType; import org.jclouds.date.internal.SimpleDateFormatDateService; import org.jclouds.json.BaseItemParserTest; import org.jclouds.openstack.domain.Link; -import org.jclouds.openstack.keystone.v2_0.domain.Access; import org.jclouds.openstack.keystone.v2_0.domain.ApiMetadata; -import org.jclouds.openstack.keystone.v2_0.domain.Endpoint; -import org.jclouds.openstack.keystone.v2_0.domain.Role; -import org.jclouds.openstack.keystone.v2_0.domain.Service; -import org.jclouds.openstack.keystone.v2_0.domain.Tenant; -import org.jclouds.openstack.keystone.v2_0.domain.Token; -import org.jclouds.openstack.keystone.v2_0.domain.User; import org.jclouds.rest.annotations.SelectJson; import org.testng.annotations.Test; diff --git a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/handlers/GlanceErrorHandler.java b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/handlers/GlanceErrorHandler.java index f16cd66b1f..4793d8ae13 100644 --- a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/handlers/GlanceErrorHandler.java +++ b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/handlers/GlanceErrorHandler.java @@ -20,9 +20,6 @@ package org.jclouds.openstack.glance.v1_0.handlers; import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - import javax.inject.Singleton; import org.jclouds.http.HttpCommand; diff --git a/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/UserClientLiveTest.java b/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/UserClientLiveTest.java index 9399e9c190..ab6ff41634 100644 --- a/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/UserClientLiveTest.java +++ b/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/UserClientLiveTest.java @@ -18,7 +18,6 @@ */ package org.jclouds.openstack.keystone.v2_0; -import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; diff --git a/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/internal/BaseKeystoneClientLiveTest.java b/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/internal/BaseKeystoneClientLiveTest.java index ef64a89a4b..3f7be6f320 100644 --- a/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/internal/BaseKeystoneClientLiveTest.java +++ b/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/internal/BaseKeystoneClientLiveTest.java @@ -18,8 +18,6 @@ */ package org.jclouds.openstack.keystone.v2_0.internal; -import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.SERVICE_TYPE; - import java.util.Properties; import org.jclouds.apis.BaseContextLiveTest; @@ -27,7 +25,6 @@ import org.jclouds.openstack.keystone.v2_0.KeystoneApiMetadata; import org.jclouds.openstack.keystone.v2_0.KeystoneAsyncClient; import org.jclouds.openstack.keystone.v2_0.KeystoneClient; import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties; -import org.jclouds.openstack.services.ServiceType; import org.jclouds.rest.RestContext; import org.testng.annotations.AfterGroups; import org.testng.annotations.BeforeGroups; diff --git a/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/internal/BaseKeystoneRestClientExpectTest.java b/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/internal/BaseKeystoneRestClientExpectTest.java index 8d8afcffc0..a4ff58b447 100644 --- a/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/internal/BaseKeystoneRestClientExpectTest.java +++ b/labs/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/internal/BaseKeystoneRestClientExpectTest.java @@ -25,16 +25,13 @@ import java.util.Properties; import javax.ws.rs.core.MediaType; -import org.jclouds.Constants; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; -import org.jclouds.openstack.keystone.v1_1.config.AuthenticationServiceModule; import org.jclouds.openstack.services.ServiceType; import org.jclouds.rest.internal.BaseRestClientExpectTest; import com.google.common.base.Objects; import com.google.common.collect.ImmutableMultimap; -import com.google.common.net.HttpHeaders; /** * Base class for writing KeyStone 2.0 Rest Client Expect tests From 4ac7629f44e70bf4fd4a17da541745a9769fcdb1 Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Tue, 8 May 2012 11:28:13 -0700 Subject: [PATCH 046/148] Delegate blob storage to TransientStorageStrategy This further minimizes the drift between the filesystem and transient blobstores. We also now require that users keep the BlobStoreContext open instead of using ConcurrentMap singletons. Finally we remove the "stub" container. --- .../filesystem/FilesystemAsyncBlobStore.java | 14 +-- .../swift/internal/StubSwiftAsyncClient.java | 25 +++-- .../blobstore/TransientAsyncBlobStore.java | 92 +++++-------------- .../blobstore/TransientStorageStrategy.java | 92 +++++++++++++++++++ .../TransientBlobStoreContextModule.java | 12 --- 5 files changed, 135 insertions(+), 100 deletions(-) create mode 100644 blobstore/src/main/java/org/jclouds/blobstore/TransientStorageStrategy.java diff --git a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java index 2f7b40e41e..ec2d91f3f7 100644 --- a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java +++ b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java @@ -163,8 +163,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { SortedSet contents = newTreeSet(transform(blobBelongingToContainer, new Function() { public StorageMetadata apply(String key) { - Blob oldBlob = loadFileBlob(container, key); - + Blob oldBlob = loadBlob(container, key); checkState(oldBlob != null, "blob " + key + " is not present although it was in the list of " + container); checkState(oldBlob.getMetadata() != null, "blob " + container + "/" + key + " has no metadata"); @@ -342,13 +341,6 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { return immediateFuture(result); } - public String getFirstQueryOrNull(String string, @Nullable HttpRequestOptions options) { - if (options == null) - return null; - Collection values = options.buildQueryParameters().get(string); - return (values != null && values.size() >= 1) ? values.iterator().next() : null; - } - /** * Load the blob with the given key belonging to the container with the given * name. There must exist a resource on the file system whose complete name @@ -361,7 +353,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { * * @return the blob belonging to the given container with the given key */ - private Blob loadFileBlob(String container, String key) { + private Blob loadBlob(final String container, final String key) { logger.debug("Opening blob in container: %s - %s", container, key); BlobBuilder builder = blobUtils.blobBuilder(); builder.name(key); @@ -527,7 +519,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { return immediateFuture(null); } - Blob blob = loadFileBlob(containerName, key); + Blob blob = loadBlob(containerName, key); if (options != null) { if (options.getIfMatch() != null) { diff --git a/apis/swift/src/test/java/org/jclouds/openstack/swift/internal/StubSwiftAsyncClient.java b/apis/swift/src/test/java/org/jclouds/openstack/swift/internal/StubSwiftAsyncClient.java index 114b3547ff..7299227704 100644 --- a/apis/swift/src/test/java/org/jclouds/openstack/swift/internal/StubSwiftAsyncClient.java +++ b/apis/swift/src/test/java/org/jclouds/openstack/swift/internal/StubSwiftAsyncClient.java @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import javax.inject.Inject; @@ -37,6 +38,7 @@ import org.jclouds.blobstore.TransientAsyncBlobStore; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.PageSet; +import org.jclouds.blobstore.domain.StorageMetadata; import org.jclouds.blobstore.functions.HttpGetOptionsListToGetOptions; import org.jclouds.blobstore.options.ListContainerOptions; import org.jclouds.concurrent.Futures; @@ -55,6 +57,7 @@ import org.jclouds.openstack.swift.domain.ObjectInfo; import org.jclouds.openstack.swift.domain.SwiftObject; import com.google.common.base.Function; +import com.google.common.base.Throwables; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.google.common.util.concurrent.ListenableFuture; @@ -74,18 +77,16 @@ public class StubSwiftAsyncClient implements CommonSwiftAsyncClient { private final ResourceToObjectInfo blob2ObjectInfo; private final ListContainerOptionsToBlobStoreListContainerOptions container2ContainerListOptions; private final ResourceToObjectList resource2ObjectList; - private final ConcurrentMap> containerToBlobs; private final ExecutorService service; @Inject private StubSwiftAsyncClient(@Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, - TransientAsyncBlobStore blobStore, ConcurrentMap> containerToBlobs, + TransientAsyncBlobStore blobStore, SwiftObject.Factory objectProvider, HttpGetOptionsListToGetOptions httpGetOptionsConverter, ObjectToBlob object2Blob, BlobToObject blob2Object, ResourceToObjectInfo blob2ObjectInfo, ListContainerOptionsToBlobStoreListContainerOptions container2ContainerListOptions, ResourceToObjectList resource2ContainerList) { this.service = service; - this.containerToBlobs = containerToBlobs; this.blobStore = blobStore; this.objectProvider = objectProvider; this.httpGetOptionsConverter = httpGetOptionsConverter; @@ -145,10 +146,18 @@ public class StubSwiftAsyncClient implements CommonSwiftAsyncClient { public ListenableFuture> listContainers( org.jclouds.openstack.swift.options.ListContainerOptions... options) { - return immediateFuture(Sets.newHashSet(Iterables.transform(blobStore.getContainerToBlobs().keySet(), - new Function() { - public ContainerMetadata apply(String name) { - return new ContainerMetadata(name, -1, -1, null, new HashMap()); + PageSet listing; + try { + listing = blobStore.list().get(); + } catch (ExecutionException ee) { + throw Throwables.propagate(ee); + } catch (InterruptedException ie) { + throw Throwables.propagate(ie); + } + return immediateFuture(Sets.newHashSet(Iterables.transform(listing, + new Function() { + public ContainerMetadata apply(StorageMetadata md) { + return new ContainerMetadata(md.getName(), -1, -1, null, new HashMap()); } }))); } @@ -186,7 +195,7 @@ public class StubSwiftAsyncClient implements CommonSwiftAsyncClient { @Override public ListenableFuture objectExists(String bucketName, String key) { - return immediateFuture(containerToBlobs.get(bucketName).containsKey(key)); + return blobStore.blobExists(bucketName, key); } } diff --git a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java index 6168e8b59a..ca882c2661 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java @@ -122,9 +122,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { protected final DateService dateService; protected final Crypto crypto; - protected final ConcurrentMap> containerToBlobs; protected final Provider uriBuilders; - protected final ConcurrentMap containerToLocation; protected final HttpGetOptionsListToGetOptions httpGetOptionsConverter; protected final IfDirectoryReturnNameStrategy ifDirectoryReturnName; protected final Factory blobFactory; @@ -139,21 +137,15 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { @Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, Supplier defaultLocation, @Memoized Supplier> locations, - Factory blobFactory, - ConcurrentMap> containerToBlobs, Provider uriBuilders, - ConcurrentMap containerToLocation) { + Factory blobFactory, Provider uriBuilders) { super(context, blobUtils, service, defaultLocation, locations); this.blobFactory = blobFactory; this.dateService = dateService; this.crypto = crypto; - this.containerToBlobs = containerToBlobs; this.uriBuilders = uriBuilders; - this.containerToLocation = containerToLocation; this.httpGetOptionsConverter = httpGetOptionsConverter; this.ifDirectoryReturnName = ifDirectoryReturnName; - getContainerToLocation().put("stub", defaultLocation.get()); - getContainerToBlobs().put("stub", new ConcurrentHashMap()); - this.storageStrategy = new TransientStorageStrategy(); + this.storageStrategy = new TransientStorageStrategy(defaultLocation); } /** @@ -161,16 +153,18 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { */ @Override public ListenableFuture> list(final String container, ListContainerOptions options) { - final Map realContents = getContainerToBlobs().get(container); // Check if the container exists - if (realContents == null) + if (!storageStrategy.containerExists(container)) return immediateFailedFuture(cnfe(container)); - SortedSet contents = newTreeSet(transform(realContents.keySet(), + // Loading blobs from container + Iterable blobBelongingToContainer = storageStrategy.getBlobKeysInsideContainer(container); + + SortedSet contents = newTreeSet(transform(blobBelongingToContainer, new Function() { public StorageMetadata apply(String key) { - Blob oldBlob = realContents.get(key); + Blob oldBlob = loadBlob(container, key); checkState(oldBlob != null, "blob " + key + " is not present although it was in the list of " + container); checkState(oldBlob.getMetadata() != null, "blob " + container + "/" + key + " has no metadata"); @@ -297,7 +291,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { */ @Override public ListenableFuture clearContainer(final String container) { - getContainerToBlobs().get(container).clear(); + storageStrategy.clearContainer(container); return immediateFuture(null); } @@ -313,7 +307,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { public ListenableFuture deleteContainerIfEmpty(final String container) { Boolean returnVal = true; if (storageStrategy.containerExists(container)) { - if (getContainerToBlobs().get(container).size() == 0) + if (Iterables.isEmpty(storageStrategy.getBlobKeysInsideContainer(container))) storageStrategy.deleteContainer(container); else returnVal = false; @@ -342,7 +336,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { MutableStorageMetadata cmd = create(); cmd.setName(name); cmd.setType(StorageType.CONTAINER); - cmd.setLocation(getContainerToLocation().get(name)); + cmd.setLocation(storageStrategy.getLocation(name)); return cmd; } }), null)); @@ -356,20 +350,15 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { * {@inheritDoc} */ @Override - public ListenableFuture createContainerInLocation(final Location location, final String name) { - if (storageStrategy.containerExists(name)) { - return immediateFuture(Boolean.FALSE); - } - getContainerToBlobs().put(name, new ConcurrentHashMap()); - getContainerToLocation().put(name, location != null ? location : defaultLocation.get()); - return immediateFuture(Boolean.TRUE); + public ListenableFuture createContainerInLocation(final Location location, + final String name) { + boolean result = storageStrategy.createContainerInLocation(name, location); + return immediateFuture(result); } - public String getFirstQueryOrNull(String string, @Nullable HttpRequestOptions options) { - if (options == null) - return null; - Collection values = options.buildQueryParameters().get(string); - return (values != null && values.size() >= 1) ? values.iterator().next() : null; + private Blob loadBlob(final String container, final String key) { + logger.debug("Opening blob in container: %s - %s", container, key); + return storageStrategy.getBlob(container, key); } protected static class DelimiterFilter implements Predicate { @@ -477,22 +466,21 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { public ListenableFuture putBlob(String containerName, Blob blob) { checkNotNull(containerName, "containerName must be set"); checkNotNull(blob, "blob must be set"); - ConcurrentMap container = getContainerToBlobs().get(containerName); String blobKey = blob.getMetadata().getName(); logger.debug("Put blob with key [%s] to container [%s]", blobKey, containerName); - if (container == null) { + if (!storageStrategy.containerExists(containerName)) { return Futures.immediateFailedFuture(new IllegalStateException("containerName not found: " + containerName)); } blob = createUpdatedCopyOfBlobInContainer(containerName, blob); - container.put(blob.getMetadata().getName(), blob); + storageStrategy.putBlob(containerName, blob); return immediateFuture(Iterables.getOnlyElement(blob.getAllHeaders().get(HttpHeaders.ETAG))); } - protected Blob createUpdatedCopyOfBlobInContainer(String containerName, Blob in) { + private Blob createUpdatedCopyOfBlobInContainer(String containerName, Blob in) { checkNotNull(in, "blob"); checkNotNull(in.getPayload(), "blob.payload"); ByteArrayPayload payload = (in.getPayload() instanceof ByteArrayPayload) ? ByteArrayPayload.class.cast(in @@ -558,13 +546,12 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { return immediateFailedFuture(cnfe(containerName)); } // If the blob doesn't exist, a null object is returned - Map realContents = getContainerToBlobs().get(containerName); - if (!realContents.containsKey(key)) { + if (!storageStrategy.blobExists(containerName, key)) { logger.debug("Item %s does not exist in container %s", key, containerName); return immediateFuture(null); } - Blob blob = realContents.get(key); + Blob blob = loadBlob(containerName, key); if (options != null) { if (options.getIfMatch() != null) { @@ -652,20 +639,12 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { return returnVal; } - public ConcurrentMap> getContainerToBlobs() { - return containerToBlobs; - } - @Override protected boolean deleteAndVerifyContainerGone(final String container) { storageStrategy.deleteContainer(container); return storageStrategy.containerExists(container); } - private ConcurrentMap getContainerToLocation() { - return containerToLocation; - } - @Override public ListenableFuture putBlob(String container, Blob blob, PutOptions options) { // TODO implement options @@ -679,29 +658,4 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { throw new UnsupportedOperationException("publicRead"); return createContainerInLocation(location, container); } - - private class TransientStorageStrategy { - public Iterable getAllContainerNames() { - return getContainerToBlobs().keySet(); - } - - public boolean containerExists(final String containerName) { - return getContainerToBlobs().containsKey(containerName); - } - - public void deleteContainer(final String containerName) { - getContainerToBlobs().remove(containerName); - } - - public boolean blobExists(final String containerName, final String blobName) { - Map map = containerToBlobs.get(containerName); - return map != null && map.containsKey(blobName); - } - - public void removeBlob(final String containerName, final String blobName) { - if (storageStrategy.containerExists(containerName)) { - getContainerToBlobs().get(containerName).remove(blobName); - } - } - } } diff --git a/blobstore/src/main/java/org/jclouds/blobstore/TransientStorageStrategy.java b/blobstore/src/main/java/org/jclouds/blobstore/TransientStorageStrategy.java new file mode 100644 index 0000000000..7d3b26ffa6 --- /dev/null +++ b/blobstore/src/main/java/org/jclouds/blobstore/TransientStorageStrategy.java @@ -0,0 +1,92 @@ +/** + * 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.blobstore; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; + +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.domain.Location; + +public class TransientStorageStrategy { + private final ConcurrentMap> containerToBlobs = new ConcurrentHashMap>(); + private final ConcurrentMap containerToLocation = new ConcurrentHashMap(); + private final Supplier defaultLocation; + + public TransientStorageStrategy(final Supplier defaultLocation) { + this.defaultLocation = Preconditions.checkNotNull(defaultLocation); + } + + public Iterable getAllContainerNames() { + return containerToBlobs.keySet(); + } + + public boolean containerExists(final String containerName) { + return containerToBlobs.containsKey(containerName); + } + + public void clearContainer(final String containerName) { + containerToBlobs.get(containerName).clear(); + } + + public boolean createContainerInLocation(final String containerName, final Location location) { + ConcurrentMap origValue = containerToBlobs.putIfAbsent( + containerName, new ConcurrentHashMap()); + if (origValue != null) { + return false; + } + containerToLocation.put(containerName, location != null ? location : defaultLocation.get()); + return true; + } + + public void deleteContainer(final String containerName) { + containerToBlobs.remove(containerName); + } + + public boolean blobExists(final String containerName, final String blobName) { + Map map = containerToBlobs.get(containerName); + return map != null && map.containsKey(blobName); + } + + public Blob getBlob(final String containerName, final String blobName) { + Map map = containerToBlobs.get(containerName); + return map == null ? null : map.get(blobName); + } + + public void putBlob(final String containerName, final Blob blob) { + Map map = containerToBlobs.get(containerName); + map.put(blob.getMetadata().getName(), blob); + } + + public void removeBlob(final String containerName, final String blobName) { + containerToBlobs.get(containerName).remove(blobName); + } + + public Iterable getBlobKeysInsideContainer(final String containerName) { + return containerToBlobs.get(containerName).keySet(); + } + + public Location getLocation(final String containerName) { + return containerToLocation.get(containerName); + } +} diff --git a/blobstore/src/main/java/org/jclouds/blobstore/config/TransientBlobStoreContextModule.java b/blobstore/src/main/java/org/jclouds/blobstore/config/TransientBlobStoreContextModule.java index 32dcfca029..7177e5d2ad 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/config/TransientBlobStoreContextModule.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/config/TransientBlobStoreContextModule.java @@ -18,9 +18,6 @@ */ package org.jclouds.blobstore.config; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - import org.jclouds.blobstore.AsyncBlobStore; import org.jclouds.blobstore.BlobRequestSigner; import org.jclouds.blobstore.BlobStore; @@ -41,20 +38,11 @@ import com.google.inject.TypeLiteral; * @author Adrian Cole */ public class TransientBlobStoreContextModule extends AbstractModule { - - // must be singleton for all threads and all objects or tests may fail; - static final ConcurrentHashMap> map = new ConcurrentHashMap>(); - static final ConcurrentHashMap containerToLocation = new ConcurrentHashMap(); - @Override protected void configure() { bind(AsyncBlobStore.class).to(TransientAsyncBlobStore.class).asEagerSingleton(); // forward all requests from TransientBlobStore to TransientAsyncBlobStore. needs above binding as cannot proxy a class BinderUtils.bindClient(binder(), TransientBlobStore.class, AsyncBlobStore.class, ImmutableMap., Class>of()); - bind(new TypeLiteral>>() { - }).toInstance(map); - bind(new TypeLiteral>() { - }).toInstance(containerToLocation); install(new BlobStoreObjectModule()); install(new BlobStoreMapModule()); bind(BlobStore.class).to(TransientBlobStore.class); From 1797b27ed489a5b464352ba230c69c1e9c00edf8 Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Tue, 8 May 2012 14:31:11 +0100 Subject: [PATCH 047/148] Adding OptionalTypeAdaptorFactory to handle the parsing of Optional values --- .../compute/NovaComputeServiceAdapter.java | 10 +- .../nova/v1_1/config/NovaParserModule.java | 39 ++- .../openstack/nova/v1_1/domain/Server.java | 232 ++++++------------ .../nova/v1_1/domain/ServerCreated.java | 95 +++++++ .../v1_1/domain/ServerExtendedAttributes.java | 146 +++++++++++ .../v1_1/domain/ServerExtendedStatus.java | 158 ++++++++++++ .../nova/v1_1/features/ServerAsyncClient.java | 3 +- .../nova/v1_1/features/ServerClient.java | 3 +- .../functions/OrphanedGroupsByZoneIdTest.java | 4 +- .../ServerInZoneToNodeMetadataTest.java | 41 +++- .../AdminActionsClientLiveTest.java | 14 +- .../v1_1/internal/BaseNovaClientLiveTest.java | 9 +- .../v1_1/parse/ParseCreatedServerTest.java | 33 +-- .../parse/ParseServerDetailsEssexTest.java | 140 ++++++----- .../ParseServerWithAllExtensionsTest.java | 106 ++++++++ .../resources/server_details_devstack.json | 44 ++++ .../jclouds/openstack/domain/Resource.java | 6 + .../org/jclouds/json/config/GsonModule.java | 2 + .../internal/OptionalTypeAdapterFactory.java | 79 ++++++ .../OptionalTypeAdapterFactoryTest.java | 130 ++++++++++ 20 files changed, 1016 insertions(+), 278 deletions(-) create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerCreated.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedAttributes.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedStatus.java create mode 100644 apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseServerWithAllExtensionsTest.java create mode 100644 apis/openstack-nova/src/test/resources/server_details_devstack.json create mode 100644 core/src/main/java/org/jclouds/json/internal/OptionalTypeAdapterFactory.java create mode 100644 core/src/test/java/org/jclouds/json/internal/OptionalTypeAdapterFactoryTest.java diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceAdapter.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceAdapter.java index 1bc2e8a52e..6e5e26502a 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceAdapter.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceAdapter.java @@ -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.slashEncode(), credentialsBuilder .build()); } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaParserModule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaParserModule.java index 3911554c21..5915395cdf 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaParserModule.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaParserModule.java @@ -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 provideCustomAdapterBindings() { - return ImmutableMap. of( + return ImmutableMap.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 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 { + @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() { + } + } + } + } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Server.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Server.java index 452145d652..72cb75eb96 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Server.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Server.java @@ -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 addresses = ImmutableMultimap.of(); private Map 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() */ @@ -251,52 +243,20 @@ public class Server extends Resource { this.metadata = metadata; return self(); } - + /** - * @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> addresses; - protected final Map metadata; + private final Map> addresses; + private final Map 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 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 extendedAttributes; // Disk Config extension @SerializedName("OS-DCF:diskConfig") - protected final String diskConfig; + private final Optional diskConfig; protected Server(Builder builder) { super(builder); @@ -405,17 +351,34 @@ 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.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(); + } + /** * only present until the id is in uuid form * @@ -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 *

* NOTE: This field is only present if the Extended Status extension is installed. */ - @Nullable - public String getTaskState() { - return this.taskState; + public Optional getExtendedStatus() { + return this.extendedStatus; } /** - * State of task running against this instance (e.g. "suspending") - *

- * 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") - *

- * 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 *

* 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 - *

- * 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 - *

- * 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 getExtendedAttributes() { + return this.extendedAttributes; } /** @@ -573,8 +485,7 @@ public class Server extends Resource { *

* NOTE: This field is only present if the Disk Config extension is installed. */ - @Nullable - public String getDiskConfig() { + public Optional 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); } } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerCreated.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerCreated.java new file mode 100644 index 0000000000..607b2f3ae4 --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerCreated.java @@ -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 + */ +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> extends Resource.Builder { + 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 { + @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); + } +} diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedAttributes.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedAttributes.java new file mode 100644 index 0000000000..400f075a04 --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedAttributes.java @@ -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 + */ +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> { + 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 { + @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(); + } + +} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedStatus.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedStatus.java new file mode 100644 index 0000000000..8bef032193 --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedStatus.java @@ -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 + */ +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> { + 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 { + @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(); + } + +} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ServerAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ServerAsyncClient.java index 2640f7439e..93dbaea18b 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ServerAsyncClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ServerAsyncClient.java @@ -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 createServer(@PayloadParam("name") String name, @PayloadParam("imageRef") String imageRef, + ListenableFuture createServer(@PayloadParam("name") String name, @PayloadParam("imageRef") String imageRef, @PayloadParam("flavorRef") String flavorRef, CreateServerOptions... options); /** diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ServerClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ServerClient.java index 51003d3340..064d05eed2 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ServerClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ServerClient.java @@ -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. diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/functions/OrphanedGroupsByZoneIdTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/functions/OrphanedGroupsByZoneIdTest.java index 843407cbc3..4e9bd759d3 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/functions/OrphanedGroupsByZoneIdTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/functions/OrphanedGroupsByZoneIdTest.java @@ -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 diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/functions/ServerInZoneToNodeMetadataTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/functions/ServerInZoneToNodeMetadataTest.java index 08178fa869..c9bd2c0d6c 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/functions/ServerInZoneToNodeMetadataTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/functions/ServerInZoneToNodeMetadataTest.java @@ -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 images = ImmutableSet. of(); Set hardwares = ImmutableSet. 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(); + + } } diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsClientLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsClientLiveTest.java index 547675421e..048cc235bc 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsClientLiveTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsClientLiveTest.java @@ -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); diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaClientLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaClientLiveTest.java index e17572fbe6..fe683e4ea5 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaClientLiveTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaClientLiveTest.java @@ -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 { diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseCreatedServerTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseCreatedServerTest.java index b322aad8e5..944c8fd2d8 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseCreatedServerTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseCreatedServerTest.java @@ -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 { +public class ParseCreatedServerTest extends BaseItemParserTest { @Override public String resource() { @@ -52,36 +53,12 @@ public class ParseCreatedServerTest extends BaseItemParserTest { @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(); diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseServerDetailsEssexTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseServerDetailsEssexTest.java index 98f239593a..95f8a2ca7f 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseServerDetailsEssexTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseServerDetailsEssexTest.java @@ -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; @@ -59,102 +61,108 @@ public class ParseServerDetailsEssexTest extends BaseSetParserTest { return ImmutableSet.of( Server.builder() .addresses(ImmutableMultimap.builder() - .putAll("Net TenantA Front-Middle", Address.createV4("172.16.11.5")) - .putAll("Public network", Address.createV4("172.16.1.13"), Address.createV4("10.193.112.119")).build()) + .putAll("Net TenantA Front-Middle", Address.createV4("172.16.11.5")) + .putAll("Public network", Address.createV4("172.16.1.13"), Address.createV4("10.193.112.119")).build()) .links( - Link.create( - Relation.SELF, - URI.create("http://nova:8774/v1.1/8d10e6646d5d4585937395b04839a353/servers/0c80b392-db30-4736-ae02-4480090f1207")), - Link.create( - Relation.BOOKMARK, - URI.create("http://nova:8774/8d10e6646d5d4585937395b04839a353/servers/0c80b392-db30-4736-ae02-4480090f1207"))) + Link.create( + Relation.SELF, + URI.create("http://nova:8774/v1.1/8d10e6646d5d4585937395b04839a353/servers/0c80b392-db30-4736-ae02-4480090f1207")), + Link.create( + Relation.BOOKMARK, + URI.create("http://nova:8774/8d10e6646d5d4585937395b04839a353/servers/0c80b392-db30-4736-ae02-4480090f1207"))) .image( - Resource.builder() - .id("416af940-2d3c-4a7c-977c-a9030685ad5e") - .links( - Link.create( - Relation.BOOKMARK, - URI.create("http://nova:8774/8d10e6646d5d4585937395b04839a353/images/416af940-2d3c-4a7c-977c-a9030685ad5e"))).build()) + Resource.builder() + .id("416af940-2d3c-4a7c-977c-a9030685ad5e") + .links( + Link.create( + Relation.BOOKMARK, + URI.create("http://nova:8774/8d10e6646d5d4585937395b04839a353/images/416af940-2d3c-4a7c-977c-a9030685ad5e"))).build()) .flavor( - Resource.builder() - .id("1") - .links( - Link.create( - Relation.BOOKMARK, - URI.create("http://nova:8774/8d10e6646d5d4585937395b04839a353/flavors/1"))).build()) + Resource.builder() + .id("1") + .links( + Link.create( + Relation.BOOKMARK, + 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.builder() - .putAll("Net TenantA Front-Middle", Address.createV4("172.16.11.4")) - .putAll("Net TenantA Middle-Back", Address.createV4("172.16.12.5")).build()) + .putAll("Net TenantA Front-Middle", Address.createV4("172.16.11.4")) + .putAll("Net TenantA Middle-Back", Address.createV4("172.16.12.5")).build()) .links( - Link.create( - Relation.SELF, - URI.create("http://nova:8774/v1.1/8d10e6646d5d4585937395b04839a353/servers/b332b5cd-535e-4677-b68e-fc8badc13236")), - Link.create( - Relation.BOOKMARK, - URI.create("http://nova:8774/8d10e6646d5d4585937395b04839a353/servers/b332b5cd-535e-4677-b68e-fc8badc13236"))) + Link.create( + Relation.SELF, + URI.create("http://nova:8774/v1.1/8d10e6646d5d4585937395b04839a353/servers/b332b5cd-535e-4677-b68e-fc8badc13236")), + Link.create( + Relation.BOOKMARK, + URI.create("http://nova:8774/8d10e6646d5d4585937395b04839a353/servers/b332b5cd-535e-4677-b68e-fc8badc13236"))) .image( - Resource.builder() - .id("416af940-2d3c-4a7c-977c-a9030685ad5e") - .links( - Link.create( - Relation.BOOKMARK, - URI.create("http://nova:8774/8d10e6646d5d4585937395b04839a353/images/416af940-2d3c-4a7c-977c-a9030685ad5e"))).build()) + Resource.builder() + .id("416af940-2d3c-4a7c-977c-a9030685ad5e") + .links( + Link.create( + Relation.BOOKMARK, + URI.create("http://nova:8774/8d10e6646d5d4585937395b04839a353/images/416af940-2d3c-4a7c-977c-a9030685ad5e"))).build()) .flavor( - Resource.builder() - .id("1") - .links( - Link.create( - Relation.BOOKMARK, - URI.create("http://nova:8774/8d10e6646d5d4585937395b04839a353/flavors/1"))).build()) + Resource.builder() + .id("1") + .links( + Link.create( + Relation.BOOKMARK, + 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(), - Server.builder() + .tenantId("8d10e6646d5d4585937395b04839a353") + .extendedStatus(ServerExtendedStatus.builder().vmState("active").powerState(1).build()) + .diskConfig("MANUAL").build(), + Server.builder() .addresses(ImmutableMultimap.builder() - .putAll("Net TenantA Middle-Back", Address.createV4("172.16.12.4")).build()) + .putAll("Net TenantA Middle-Back", Address.createV4("172.16.12.4")).build()) .links( - Link.create( - Relation.SELF, - URI.create("http://nova:8774/v1.1/8d10e6646d5d4585937395b04839a353/servers/f9d43436-4572-4c9b-9b74-5fa6890a2f21")), - Link.create( - Relation.BOOKMARK, - URI.create("http://nova:8774/8d10e6646d5d4585937395b04839a353/servers/f9d43436-4572-4c9b-9b74-5fa6890a2f21"))) + Link.create( + Relation.SELF, + URI.create("http://nova:8774/v1.1/8d10e6646d5d4585937395b04839a353/servers/f9d43436-4572-4c9b-9b74-5fa6890a2f21")), + Link.create( + Relation.BOOKMARK, + URI.create("http://nova:8774/8d10e6646d5d4585937395b04839a353/servers/f9d43436-4572-4c9b-9b74-5fa6890a2f21"))) .image( - Resource.builder() - .id("416af940-2d3c-4a7c-977c-a9030685ad5e") - .links( - Link.create( - Relation.BOOKMARK, - URI.create("http://nova:8774/8d10e6646d5d4585937395b04839a353/images/416af940-2d3c-4a7c-977c-a9030685ad5e"))).build()) + Resource.builder() + .id("416af940-2d3c-4a7c-977c-a9030685ad5e") + .links( + Link.create( + Relation.BOOKMARK, + URI.create("http://nova:8774/8d10e6646d5d4585937395b04839a353/images/416af940-2d3c-4a7c-977c-a9030685ad5e"))).build()) .flavor( - Resource.builder() - .id("1") - .links( - Link.create( - Relation.BOOKMARK, - URI.create("http://nova:8774/8d10e6646d5d4585937395b04839a353/flavors/1"))).build()) + Resource.builder() + .id("1") + .links( + Link.create( + Relation.BOOKMARK, + 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()); } diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseServerWithAllExtensionsTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseServerWithAllExtensionsTest.java new file mode 100644 index 0000000000..52139fabac --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/parse/ParseServerWithAllExtensionsTest.java @@ -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 { + + @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()); + } +} diff --git a/apis/openstack-nova/src/test/resources/server_details_devstack.json b/apis/openstack-nova/src/test/resources/server_details_devstack.json new file mode 100644 index 0000000000..e41095f7af --- /dev/null +++ b/apis/openstack-nova/src/test/resources/server_details_devstack.json @@ -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": {} +}} \ No newline at end of file diff --git a/common/openstack/src/main/java/org/jclouds/openstack/domain/Resource.java b/common/openstack/src/main/java/org/jclouds/openstack/domain/Resource.java index 1deb011da9..a05e4b55e5 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/domain/Resource.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/domain/Resource.java @@ -116,6 +116,12 @@ public class Resource implements Comparable { 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 diff --git a/core/src/main/java/org/jclouds/json/config/GsonModule.java b/core/src/main/java/org/jclouds/json/config/GsonModule.java index 7e0712cacb..bc6585f0aa 100644 --- a/core/src/main/java/org/jclouds/json/config/GsonModule.java +++ b/core/src/main/java/org/jclouds/json/config/GsonModule.java @@ -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()); diff --git a/core/src/main/java/org/jclouds/json/internal/OptionalTypeAdapterFactory.java b/core/src/main/java/org/jclouds/json/internal/OptionalTypeAdapterFactory.java new file mode 100644 index 0000000000..1855e8e7db --- /dev/null +++ b/core/src/main/java/org/jclouds/json/internal/OptionalTypeAdapterFactory.java @@ -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 TypeAdapter create(Gson gson, TypeToken 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) newMultisetAdapter(elementAdapter); + } + + private TypeAdapter> newMultisetAdapter( + final TypeAdapter elementAdapter) { + return new TypeAdapter>() { + public void write(JsonWriter out, Optional value) throws IOException { + if (!value.isPresent()) { + out.nullValue(); + return; + } + elementAdapter.write(out, value.get()); + } + + public Optional read(JsonReader in) throws IOException { + Optional 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; + } + }; + } +} diff --git a/core/src/test/java/org/jclouds/json/internal/OptionalTypeAdapterFactoryTest.java b/core/src/test/java/org/jclouds/json/internal/OptionalTypeAdapterFactoryTest.java new file mode 100644 index 0000000000..6ccdd89816 --- /dev/null +++ b/core/src/test/java/org/jclouds/json/internal/OptionalTypeAdapterFactoryTest.java @@ -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 value; + private final String someOtherValue; + + public SimpleBean(Optional 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 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.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.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), "{}"); + } + +} From a45d079b8cf52abc94b8aa7bcd065ecb7d7fd240 Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Tue, 8 May 2012 19:07:44 +0100 Subject: [PATCH 048/148] openstack-nova: adding EXTENDED_STATUS to ExtensionNamespaces --- .../org/jclouds/openstack/nova/v1_1/domain/Server.java | 10 ++++++++-- .../nova/v1_1/domain/ServerExtendedAttributes.java | 4 +++- .../nova/v1_1/domain/ServerExtendedStatus.java | 4 +++- .../nova/v1_1/extensions/ExtensionNamespaces.java | 6 ++++++ ...tionNamespaceEqualsAnyNamespaceInExtensionsSet.java | 2 ++ 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Server.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Server.java index 72cb75eb96..514903c5ec 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Server.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Server.java @@ -463,18 +463,24 @@ public class Server extends Resource { /** - * Retrieves the extended server status fields + * Retrieves the extended server status fields (alias "OS-EXT-STS") *

* NOTE: This field is only present if the Extended Status extension is installed. + * + * @see org.jclouds.openstack.nova.v1_1.features.ExtensionClient#getExtensionByAlias + * @see org.jclouds.openstack.nova.v1_1.extensions.ExtensionNamespaces#EXTENDED_STATUS (extended status?) */ public Optional getExtendedStatus() { return this.extendedStatus; } /** - * Retrieves the extended server attributes fields + * Retrieves the extended server attributes fields (alias "OS-EXT-SRV-ATTR") *

* NOTE: This field is only present if the The Extended Server Attributes API extension is installed. + * + * @see org.jclouds.openstack.nova.v1_1.features.ExtensionClient#getExtensionByAlias + * @see org.jclouds.openstack.nova.v1_1.extensions.ExtensionNamespaces#EXTENDED_STATUS (extended status?) */ public Optional getExtendedAttributes() { return this.extendedAttributes; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedAttributes.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedAttributes.java index 400f075a04..9dcd471e8b 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedAttributes.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedAttributes.java @@ -23,12 +23,14 @@ import com.google.common.base.Objects.ToStringHelper; import com.google.gson.annotations.SerializedName; /** - * Additional attributes delivered by Extended Server Attributes extension + * Additional attributes delivered by Extended Server Attributes extension (alias "OS-EXT-SRV-ATTR") * * @author Adam Lowe * @see + * @see org.jclouds.openstack.nova.v1_1.features.ExtensionClient#getExtensionByAlias + * @see org.jclouds.openstack.nova.v1_1.extensions.ExtensionNamespaces#EXTENDED_STATUS (extended status?) */ public class ServerExtendedAttributes { public static final String PREFIX = "OS-EXT-SRV-ATTR:"; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedStatus.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedStatus.java index 8bef032193..02908d4a66 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedStatus.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedStatus.java @@ -25,12 +25,14 @@ import com.google.common.base.Objects.ToStringHelper; import com.google.gson.annotations.SerializedName; /** - * Additional attributes delivered by Extended Server Status extension + * Additional attributes delivered by Extended Server Status extension (alias "OS-EXT-STS") * * @author Adam Lowe * @see + * @see org.jclouds.openstack.nova.v1_1.features.ExtensionClient#getExtensionByAlias + * @see org.jclouds.openstack.nova.v1_1.extensions.ExtensionNamespaces#EXTENDED_STATUS (extended status?) */ public class ServerExtendedStatus { public static final String PREFIX = "OS-EXT-STS:"; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ExtensionNamespaces.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ExtensionNamespaces.java index 85f0931a1c..3c172cf9e3 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ExtensionNamespaces.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ExtensionNamespaces.java @@ -89,4 +89,10 @@ public interface ExtensionNamespaces { * Admin Action extension */ public static final String ADMIN_ACTIONS = "http://docs.openstack.org/ext/admin-actions/api/v1.1"; + + /** + * Extended Server Status extension + */ + public static final String EXTENDED_STATUS = "http://docs.openstack.org/ext/extended_status/api/v1.1"; + } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java index 5d82fbed1f..3d023c397f 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java @@ -74,6 +74,8 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio URI.create("http://docs.openstack.org/compute/ext/createserverext/api/v1.1")) .put(URI.create(ExtensionNamespaces.ADMIN_ACTIONS), URI.create("http://docs.openstack.org/compute/ext/admin-actions/api/v1.1")) + .put(URI.create(ExtensionNamespaces.EXTENDED_STATUS), + URI.create("http://docs.openstack.org/compute/ext/extended_status/api/v1.1")) .build(); @Inject From ab22b0606b840b66a0edea4a982bff184224f723 Mon Sep 17 00:00:00 2001 From: Mathieu Guillaume Date: Tue, 8 May 2012 11:27:41 +0200 Subject: [PATCH 049/148] openstack-nova: add support for user-data --- .../compute/NovaComputeServiceAdapter.java | 1 + .../compute/options/NovaTemplateOptions.java | 37 +++++++++++++++++-- .../v1_1/options/CreateServerOptions.java | 19 +++++++++- .../options/NovaTemplateOptionsTest.java | 6 +++ 4 files changed, 58 insertions(+), 5 deletions(-) diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceAdapter.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceAdapter.java index 6e5e26502a..afb54f1905 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceAdapter.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceAdapter.java @@ -104,6 +104,7 @@ public class NovaComputeServiceAdapter implements CreateServerOptions options = new CreateServerOptions(); options.metadata(templateOptions.getUserMetadata()); options.securityGroupNames(templateOptions.getSecurityGroupNames()); + options.userData(templateOptions.getUserData()); Optional privateKey = Optional.absent(); if (templateOptions.getKeyPairName() != null) { diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/options/NovaTemplateOptions.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/options/NovaTemplateOptions.java index bc0d9e43b4..0723c032e3 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/options/NovaTemplateOptions.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/options/NovaTemplateOptions.java @@ -19,8 +19,10 @@ package org.jclouds.openstack.nova.v1_1.compute.options; import static com.google.common.base.Objects.equal; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import java.util.Arrays; import java.util.Map; import java.util.Set; @@ -68,6 +70,9 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable { eTo.securityGroupNames(getSecurityGroupNames()); eTo.generateKeyPair(shouldGenerateKeyPair()); eTo.keyPairName(getKeyPairName()); + if (getUserData() != null) { + eTo.userData(getUserData()); + } } } @@ -75,6 +80,7 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable { protected Set securityGroupNames = ImmutableSet.of(); protected boolean generateKeyPair = false; protected String keyPairName; + protected byte[] userData; @Override public boolean equals(Object o) { @@ -85,19 +91,21 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable { NovaTemplateOptions that = NovaTemplateOptions.class.cast(o); return super.equals(that) && equal(this.autoAssignFloatingIp, that.autoAssignFloatingIp) && equal(this.securityGroupNames, that.securityGroupNames) - && equal(this.generateKeyPair, that.generateKeyPair) && equal(this.keyPairName, that.keyPairName); + && equal(this.generateKeyPair, that.generateKeyPair) + && equal(this.keyPairName, that.keyPairName) + && Arrays.equals(this.userData, that.userData); } @Override public int hashCode() { - return Objects.hashCode(super.hashCode(), autoAssignFloatingIp, securityGroupNames, generateKeyPair, keyPairName); + return Objects.hashCode(super.hashCode(), autoAssignFloatingIp, securityGroupNames, generateKeyPair, keyPairName, userData); } @Override public ToStringHelper string() { return super.string().add("autoAssignFloatingIp", autoAssignFloatingIp) .add("securityGroupNames", securityGroupNames).add("generateKeyPair", generateKeyPair) - .add("keyPairName", keyPairName); + .add("keyPairName", keyPairName).add("UserData", userData); } public static final NovaTemplateOptions NONE = new NovaTemplateOptions(); @@ -183,6 +191,10 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable { return securityGroupNames; } + public byte[] getUserData() { + return userData; + } + public static class Builder { /** @@ -312,6 +324,14 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable { return options.blockUntilRunning(blockUntilRunning); } + /** + * @see NovaTemplateOptions#userData + */ + public static NovaTemplateOptions userData(byte[] userData) { + NovaTemplateOptions options = new NovaTemplateOptions(); + return NovaTemplateOptions.class.cast(options.userData(userData)); + } + } // methods that only facilitate returning the correct object type @@ -444,4 +464,15 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable { return NovaTemplateOptions.class.cast(super.userMetadata(key, value)); } + /** + * User data as bytes (not base64-encoded) + */ + public NovaTemplateOptions userData(byte[] userData) { + // This limit may not be needed for nova + checkArgument(checkNotNull(userData, "userData").length <= 16 * 1024, + "userData cannot be larger than 16kb"); + this.userData = userData; + return this; + } + } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateServerOptions.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateServerOptions.java index 3bd7fad2b3..fcec713058 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateServerOptions.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateServerOptions.java @@ -110,6 +110,7 @@ public class CreateServerOptions implements MapBinder { private Set securityGroupNames = ImmutableSet.of(); private Map metadata = ImmutableMap.of(); private List personality = Lists.newArrayList(); + private byte[] userData; @Override public boolean equals(Object object) { @@ -132,8 +133,9 @@ public class CreateServerOptions implements MapBinder { } protected ToStringHelper string() { - return toStringHelper("").add("keyName", "keyName").add("securityGroupNames", securityGroupNames).add("metadata", - metadata).add("personality", personality).add("adminPassPresent", adminPass != null); + return toStringHelper("").add("keyName", "keyName").add("securityGroupNames", securityGroupNames) + .add("metadata", metadata).add("personality", personality) + .add("adminPassPresent", adminPass != null).add("userData", new String(userData)); } @Override @@ -152,6 +154,7 @@ public class CreateServerOptions implements MapBinder { String key_name; @SerializedName(value = "security_groups") Set securityGroupNames; + String user_data; private ServerRequest(String name, String imageRef, String flavorRef) { this.name = name; @@ -172,6 +175,8 @@ public class CreateServerOptions implements MapBinder { server.personality = personality; if (keyName != null) server.key_name = keyName; + if (userData != null) + server.user_data = Base64.encodeBytes(userData); if (securityGroupNames.size() > 0) { server.securityGroupNames = Sets.newHashSet(); for (String groupName : securityGroupNames) { @@ -240,6 +245,16 @@ public class CreateServerOptions implements MapBinder { return this; } + /** + * Custom user-data can be also be supplied at launch time. + * It is retrievable by the instance and is often used for launch-time configuration + * by instance scripts. + */ + public CreateServerOptions userData(byte[] userData) { + this.userData = userData; + return this; + } + /** * A keypair name can be defined when creating a server. This key will be * linked to the server and used to SSH connect to the machine diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/options/NovaTemplateOptionsTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/options/NovaTemplateOptionsTest.java index 952367d46a..097213e76d 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/options/NovaTemplateOptionsTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/options/NovaTemplateOptionsTest.java @@ -190,6 +190,12 @@ public class NovaTemplateOptionsTest { authorizePublicKey(null); } + @Test + public void testUserData() { + NovaTemplateOptions options = new NovaTemplateOptions(); + options.userData("test".getBytes()); + assertEquals(new String(options.getUserData()), "test"); + } @Test(expectedExceptions = IllegalArgumentException.class) public void testblockOnPortBadFormat() { NovaTemplateOptions options = new NovaTemplateOptions(); From d8472b799a0187751f02972e7b450f62361ae060 Mon Sep 17 00:00:00 2001 From: Mathieu Guillaume Date: Tue, 8 May 2012 19:17:42 +0200 Subject: [PATCH 050/148] openstack-nova: fix camelCase for userData in NovaTemplateOptions --- .../nova/v1_1/compute/options/NovaTemplateOptions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/options/NovaTemplateOptions.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/options/NovaTemplateOptions.java index 0723c032e3..a4223627f9 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/options/NovaTemplateOptions.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/options/NovaTemplateOptions.java @@ -105,7 +105,7 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable { public ToStringHelper string() { return super.string().add("autoAssignFloatingIp", autoAssignFloatingIp) .add("securityGroupNames", securityGroupNames).add("generateKeyPair", generateKeyPair) - .add("keyPairName", keyPairName).add("UserData", userData); + .add("keyPairName", keyPairName).add("userData", userData); } public static final NovaTemplateOptions NONE = new NovaTemplateOptions(); From 5033b982529ac6a776a65dbe0fbb7ab648ddb3be Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Tue, 8 May 2012 17:50:07 -0700 Subject: [PATCH 051/148] NPE guard --- .../java/org/jclouds/blobstore/TransientStorageStrategy.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/blobstore/src/main/java/org/jclouds/blobstore/TransientStorageStrategy.java b/blobstore/src/main/java/org/jclouds/blobstore/TransientStorageStrategy.java index 7d3b26ffa6..1c011141f7 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/TransientStorageStrategy.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/TransientStorageStrategy.java @@ -79,7 +79,8 @@ public class TransientStorageStrategy { } public void removeBlob(final String containerName, final String blobName) { - containerToBlobs.get(containerName).remove(blobName); + if (containerToBlobs.containsKey(containerName)) + containerToBlobs.get(containerName).remove(blobName); } public Iterable getBlobKeysInsideContainer(final String containerName) { From d13e8aec4aab491e21f3e8fc8412c5ae3718bd0e Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Tue, 8 May 2012 21:08:03 -0700 Subject: [PATCH 052/148] Remove unused copy method Also make second copy method private access. --- .../org/jclouds/filesystem/FilesystemAsyncBlobStore.java | 8 +------- .../org/jclouds/blobstore/TransientAsyncBlobStore.java | 8 +------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java index ec2d91f3f7..708eff4b19 100644 --- a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java +++ b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java @@ -246,7 +246,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { storageStrategy.getAllContainerNames())); } - public static MutableBlobMetadata copy(MutableBlobMetadata in) { + private static MutableBlobMetadata copy(MutableBlobMetadata in) { ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutput os; try { @@ -271,12 +271,6 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { metadata.setUserMetadata(lowerCaseUserMetadata); } - public static MutableBlobMetadata copy(MutableBlobMetadata in, String newKey) { - MutableBlobMetadata newMd = copy(in); - newMd.setName(newKey); - return newMd; - } - /** * {@inheritDoc} */ diff --git a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java index ca882c2661..f2d237bc65 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java @@ -247,7 +247,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { storageStrategy.getAllContainerNames())); } - public static MutableBlobMetadata copy(MutableBlobMetadata in) { + private static MutableBlobMetadata copy(MutableBlobMetadata in) { ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutput os; try { @@ -271,12 +271,6 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { metadata.setUserMetadata(lowerCaseUserMetadata); } - public static MutableBlobMetadata copy(MutableBlobMetadata in, String newKey) { - MutableBlobMetadata newMd = copy(in); - newMd.setName(newKey); - return newMd; - } - /** * {@inheritDoc} */ From 29e7e7e85ef8b542c9e284665f23fafd877ec262 Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Tue, 8 May 2012 23:37:19 -0700 Subject: [PATCH 053/148] Revert "Merge pull request #617 from andrewgaul/filesystem-transient-remove-copy" This reverts commit 0fe120d659e4c934389b14c9ecd326ea81edda7c, reversing changes made to 5033b982529ac6a776a65dbe0fbb7ab648ddb3be. --- .../org/jclouds/filesystem/FilesystemAsyncBlobStore.java | 8 +++++++- .../org/jclouds/blobstore/TransientAsyncBlobStore.java | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java index 708eff4b19..ec2d91f3f7 100644 --- a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java +++ b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java @@ -246,7 +246,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { storageStrategy.getAllContainerNames())); } - private static MutableBlobMetadata copy(MutableBlobMetadata in) { + public static MutableBlobMetadata copy(MutableBlobMetadata in) { ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutput os; try { @@ -271,6 +271,12 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { metadata.setUserMetadata(lowerCaseUserMetadata); } + public static MutableBlobMetadata copy(MutableBlobMetadata in, String newKey) { + MutableBlobMetadata newMd = copy(in); + newMd.setName(newKey); + return newMd; + } + /** * {@inheritDoc} */ diff --git a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java index f2d237bc65..ca882c2661 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java @@ -247,7 +247,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { storageStrategy.getAllContainerNames())); } - private static MutableBlobMetadata copy(MutableBlobMetadata in) { + public static MutableBlobMetadata copy(MutableBlobMetadata in) { ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutput os; try { @@ -271,6 +271,12 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { metadata.setUserMetadata(lowerCaseUserMetadata); } + public static MutableBlobMetadata copy(MutableBlobMetadata in, String newKey) { + MutableBlobMetadata newMd = copy(in); + newMd.setName(newKey); + return newMd; + } + /** * {@inheritDoc} */ From 034e9d47f26db17e1105b744ebed57e440ae7211 Mon Sep 17 00:00:00 2001 From: Richard Downer Date: Wed, 9 May 2012 10:55:19 +0100 Subject: [PATCH 054/148] Regex-quote the image description and version in TemplateBuilder.fromImage() to prevent problems with image descriptions containing + and other regex special characters --- .../jclouds/compute/domain/internal/TemplateBuilderImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateBuilderImpl.java b/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateBuilderImpl.java index 272d4a8445..8440333164 100644 --- a/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateBuilderImpl.java +++ b/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateBuilderImpl.java @@ -33,6 +33,7 @@ import static org.jclouds.compute.util.ComputeServiceUtils.getSpace; import java.util.List; import java.util.NoSuchElementException; import java.util.Set; +import java.util.regex.Pattern; import javax.annotation.Resource; import javax.inject.Inject; @@ -499,13 +500,13 @@ public class TemplateBuilderImpl implements TemplateBuilder { if (image.getName() != null) this.imageName = image.getName(); if (image.getDescription() != null) - this.imageDescription = String.format("^%s$", image.getDescription()); + this.imageDescription = String.format("^%s$", Pattern.quote(image.getDescription())); if (image.getOperatingSystem().getName() != null) this.osName = image.getOperatingSystem().getName(); if (image.getOperatingSystem().getDescription() != null) this.osDescription = image.getOperatingSystem().getDescription(); if (image.getVersion() != null) - this.imageVersion = String.format("^%s$", image.getVersion()); + this.imageVersion = String.format("^%s$", Pattern.quote(image.getVersion())); if (image.getOperatingSystem().getVersion() != null) this.osVersion = image.getOperatingSystem().getVersion(); this.os64Bit = image.getOperatingSystem().is64Bit(); From 2c82ab283207776c1127a78f7f6eb392a50f832f Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Wed, 9 May 2012 16:21:26 +0100 Subject: [PATCH 055/148] openstack-nova: fixing NPE in toString() --- .../openstack/nova/v1_1/options/CreateServerOptions.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateServerOptions.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateServerOptions.java index fcec713058..ae36ea1455 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateServerOptions.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateServerOptions.java @@ -133,9 +133,10 @@ public class CreateServerOptions implements MapBinder { } protected ToStringHelper string() { - return toStringHelper("").add("keyName", "keyName").add("securityGroupNames", securityGroupNames) + return toStringHelper("").add("keyName", keyName).add("securityGroupNames", securityGroupNames) .add("metadata", metadata).add("personality", personality) - .add("adminPassPresent", adminPass != null).add("userData", new String(userData)); + .add("adminPassPresent", adminPass != null) + .add("userData", userData == null ? null : new String(userData)); } @Override @@ -258,9 +259,6 @@ public class CreateServerOptions implements MapBinder { /** * A keypair name can be defined when creating a server. This key will be * linked to the server and used to SSH connect to the machine - * - * @param keyName - * @return */ public String getKeyPairName() { return keyName; From cffeeb0eab903d27a9c90fb861054227b59b1aea Mon Sep 17 00:00:00 2001 From: Andrew Donald Kennedy Date: Wed, 9 May 2012 16:21:04 +0100 Subject: [PATCH 056/148] Not everyone lives in US-CA --- .../jclouds/cloudwatch/features/MetricClientExpectTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apis/cloudwatch/src/test/java/org/jclouds/cloudwatch/features/MetricClientExpectTest.java b/apis/cloudwatch/src/test/java/org/jclouds/cloudwatch/features/MetricClientExpectTest.java index 0701846d48..18d3301dae 100644 --- a/apis/cloudwatch/src/test/java/org/jclouds/cloudwatch/features/MetricClientExpectTest.java +++ b/apis/cloudwatch/src/test/java/org/jclouds/cloudwatch/features/MetricClientExpectTest.java @@ -22,6 +22,7 @@ import static org.testng.Assert.assertEquals; import java.net.URI; import java.util.Date; +import java.util.TimeZone; import org.jclouds.cloudwatch.CloudWatchClient; import org.jclouds.cloudwatch.domain.Dimension; @@ -45,6 +46,11 @@ import com.google.common.collect.ImmutableMultimap; */ @Test(groups = "unit", testName = "MetricClientExpectTest") public class MetricClientExpectTest extends BaseCloudWatchClientExpectTest { + + public MetricClientExpectTest() { + TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles")); + } + HttpRequest listMetrics = HttpRequest.builder() .method("POST") .endpoint(URI.create("https://monitoring.us-east-1.amazonaws.com/")) From 176647110a7f8b94aa27bf58ec2cc80346dc2d5d Mon Sep 17 00:00:00 2001 From: David Ribeiro Alves Date: Thu, 10 May 2012 01:33:31 +0100 Subject: [PATCH 057/148] added expect test --- .../nova/v1_1/compute/NovaImageExtension.java | 62 +++------ .../NovaComputeServiceContextModule.java | 5 + ...oneHasActiveStatusPredicateWithResult.java | 89 +++++++++++++ ...veStatusPredicateWithResultExpectTest.java | 70 ++++++++++ .../image_list_detail_imageextension.json | 121 ++++++++++++++++++ .../predicates/PredicateWithResult.java | 8 +- 6 files changed, 306 insertions(+), 49 deletions(-) create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResult.java create mode 100644 apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest.java create mode 100644 apis/openstack-nova/src/test/resources/image_list_detail_imageextension.json diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaImageExtension.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaImageExtension.java index 727a82bbb5..d9c6dfe28f 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaImageExtension.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaImageExtension.java @@ -43,12 +43,10 @@ import org.jclouds.concurrent.Futures; import org.jclouds.logging.Logger; import org.jclouds.openstack.nova.v1_1.NovaClient; import org.jclouds.openstack.nova.v1_1.domain.Server; -import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ImageInZone; import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndId; import org.jclouds.predicates.PredicateWithResult; import org.jclouds.predicates.Retryables; -import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.util.concurrent.ListenableFuture; @@ -61,21 +59,22 @@ public class NovaImageExtension implements ImageExtension { protected Logger logger = Logger.NULL; private final NovaClient novaClient; - private final Function imageInZoneToImage; private final ExecutorService executor; @com.google.inject.Inject(optional = true) @Named("IMAGE_MAX_WAIT") - long maxWait = 3600; + private long maxWait = 3600; @com.google.inject.Inject(optional = true) @Named("IMAGE_WAIT_PERIOD") - long waitPeriod = 1; + private long waitPeriod = 1; + private PredicateWithResult imageReadyPredicate; @Inject - public NovaImageExtension(NovaClient novaClient, Function imageInZoneToImage, - @Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads) { + public NovaImageExtension(NovaClient novaClient, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads, + PredicateWithResult imageReadyPredicate) { this.novaClient = checkNotNull(novaClient); - this.imageInZoneToImage = imageInZoneToImage; this.executor = userThreads; + this.imageReadyPredicate = imageReadyPredicate; } @Override @@ -93,48 +92,21 @@ public class NovaImageExtension implements ImageExtension { checkState(template instanceof CloneImageTemplate, " openstack-nova only supports creating images through cloning."); CloneImageTemplate cloneTemplate = (CloneImageTemplate) template; - final ZoneAndId zoneAndId = ZoneAndId.fromSlashEncoded(cloneTemplate.getSourceNodeId()); + ZoneAndId sourceImageZoneAndId = ZoneAndId.fromSlashEncoded(cloneTemplate.getSourceNodeId()); + + String newImageId = novaClient.getServerClientForZone(sourceImageZoneAndId.getZone()).createImageFromServer( + cloneTemplate.getName(), sourceImageZoneAndId.getId()); + + final ZoneAndId targetImageZoneAndId = ZoneAndId.fromZoneAndId(sourceImageZoneAndId.getZone(), newImageId); - final String newImageId = novaClient.getServerClientForZone(zoneAndId.getZone()).createImageFromServer( - cloneTemplate.getName(), zoneAndId.getId()); logger.info(">> Registered new Image %s, waiting for it to become available.", newImageId); return Futures.makeListenable(executor.submit(new Callable() { @Override public Image call() throws Exception { - return Retryables.retryGettingResultOrFailing(new PredicateWithResult() { - - org.jclouds.openstack.nova.v1_1.domain.Image result; - RuntimeException lastFailure; - - @Override - public boolean apply(String input) { - result = checkNotNull(findImage(ZoneAndId.fromZoneAndId(zoneAndId.getZone(), newImageId))); - switch (result.getStatus()) { - case ACTIVE: - logger.info("<< Image %s is available for use.", newImageId); - return true; - case UNKNOWN: - case SAVING: - logger.debug("<< Image %s is not available yet.", newImageId); - return false; - default: - lastFailure = new IllegalStateException("Image was not created: " + newImageId); - throw lastFailure; - } - } - - @Override - public Image getResult() { - return imageInZoneToImage.apply(new ImageInZone(result, zoneAndId.getZone())); - } - - @Override - public Throwable getLastFailure() { - return lastFailure; - } - }, newImageId, maxWait, waitPeriod, TimeUnit.SECONDS, - "Image was not created within the time limit, Giving up! [Limit: " + maxWait + " secs.]"); + return Retryables.retryGettingResultOrFailing(imageReadyPredicate, targetImageZoneAndId, maxWait, + waitPeriod, TimeUnit.SECONDS, "Image was not created within the time limit, Giving up! [Limit: " + + maxWait + " secs.]"); } }), executor); } @@ -150,7 +122,7 @@ public class NovaImageExtension implements ImageExtension { return true; } - private org.jclouds.openstack.nova.v1_1.domain.Image findImage(final ZoneAndId zoneAndId) { + public static org.jclouds.openstack.nova.v1_1.domain.Image findImage(NovaClient novaClient, final ZoneAndId zoneAndId) { return Iterables.tryFind(novaClient.getImageClientForZone(zoneAndId.getZone()).listImagesInDetail(), new Predicate() { @Override diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/config/NovaComputeServiceContextModule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/config/NovaComputeServiceContextModule.java index 2ceaa9db49..ed35f3d96e 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/config/NovaComputeServiceContextModule.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/config/NovaComputeServiceContextModule.java @@ -58,6 +58,7 @@ import org.jclouds.openstack.nova.v1_1.compute.loaders.CreateUniqueKeyPair; import org.jclouds.openstack.nova.v1_1.compute.loaders.FindSecurityGroupOrCreate; import org.jclouds.openstack.nova.v1_1.compute.loaders.LoadFloatingIpsForInstance; import org.jclouds.openstack.nova.v1_1.compute.options.NovaTemplateOptions; +import org.jclouds.openstack.nova.v1_1.compute.predicates.GetImageWhenImageInZoneHasActiveStatusPredicateWithResult; import org.jclouds.openstack.nova.v1_1.compute.strategy.ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet; import org.jclouds.openstack.nova.v1_1.domain.KeyPair; import org.jclouds.openstack.nova.v1_1.domain.zonescoped.FlavorInZone; @@ -68,6 +69,7 @@ import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndId; import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndName; import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneSecurityGroupNameAndPorts; import org.jclouds.openstack.nova.v1_1.predicates.FindSecurityGroupWithNameAndReturnTrue; +import org.jclouds.predicates.PredicateWithResult; import org.jclouds.predicates.RetryablePredicate; import com.google.common.base.Function; @@ -141,6 +143,9 @@ public class NovaComputeServiceContextModule extends bind(new TypeLiteral() { }).to(NovaImageExtension.class); + + bind(new TypeLiteral>() { + }).to(GetImageWhenImageInZoneHasActiveStatusPredicateWithResult.class); } @Override diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResult.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResult.java new file mode 100644 index 0000000000..2ca35ad10d --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResult.java @@ -0,0 +1,89 @@ +/** + * 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.compute.predicates; + +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; + +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.logging.Logger; +import org.jclouds.openstack.nova.v1_1.NovaClient; +import org.jclouds.openstack.nova.v1_1.compute.NovaImageExtension; +import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ImageInZone; +import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndId; +import org.jclouds.predicates.PredicateWithResult; + +import com.google.common.base.Function; + +/** + * @author David Alves + */ +public final class GetImageWhenImageInZoneHasActiveStatusPredicateWithResult implements + PredicateWithResult { + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + private org.jclouds.openstack.nova.v1_1.domain.Image result; + private ZoneAndId resultZoneAndId; + private RuntimeException lastFailure; + private Function imageInZoneToImage; + private NovaClient client; + + @Inject + public GetImageWhenImageInZoneHasActiveStatusPredicateWithResult(Function imageInZoneToImage, + NovaClient client) { + this.imageInZoneToImage = imageInZoneToImage; + this.client = client; + } + + @Override + public boolean apply(ZoneAndId input) { + result = checkNotNull(NovaImageExtension.findImage(client, + ZoneAndId.fromZoneAndId(input.getZone(), input.getId()))); + resultZoneAndId = input; + switch (result.getStatus()) { + case ACTIVE: + logger.info("<< Image %s is available for use.", input.getId()); + return true; + case SAVING: + logger.debug("<< Image %s is not available yet.", input.getId()); + return false; + default: + lastFailure = new IllegalStateException("Image was not created: " + input.getId()); + throw lastFailure; + } + } + + @Override + public Image getResult() { + return imageInZoneToImage.apply(new ImageInZone(result, resultZoneAndId.getZone())); + } + + @Override + public Throwable getLastFailure() { + return lastFailure; + } +} \ No newline at end of file diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest.java new file mode 100644 index 0000000000..a72be63ce4 --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest.java @@ -0,0 +1,70 @@ +package org.jclouds.openstack.nova.v1_1.compute.predicates; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; + +import java.util.Map; + +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.domain.Image; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndId; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaComputeServiceContextExpectTest; +import org.jclouds.predicates.PredicateWithResult; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; +import com.google.inject.Injector; + +@Test(groups = "unit", testName = "GetImageWhenImageInZoneHasActiveStatucPredicateWithResultExpectTest") +public class GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest extends + BaseNovaComputeServiceContextExpectTest { + + private final HttpResponse listImagesDetailImageExtensionResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/image_list_detail_imageextension.json")).build(); + + private Map requestResponseMap = ImmutableMap. builder() + .put(keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess) + .put(listImagesDetail, listImagesDetailImageExtensionResponse).build(); + + public void testReturnsFalseOnImageStatusSavingAndTrueOnActive() { + Injector injector = requestsSendResponses(requestResponseMap); + PredicateWithResult predicate = injector + .getInstance(GetImageWhenImageInZoneHasActiveStatusPredicateWithResult.class); + ZoneAndId zoneAdnId0 = ZoneAndId.fromZoneAndId("az-1.region-a.geo-1", "13"); + ZoneAndId zoneAdnId1 = ZoneAndId.fromZoneAndId("az-1.region-a.geo-1", "12"); + assertTrue(!predicate.apply(zoneAdnId1)); + assertTrue(predicate.apply(zoneAdnId0)); + assertEquals("natty-server-cloudimg-amd64", predicate.getResult().getName()); + } + + public void testFailsOnOtherStatuses() { + Injector injector = requestsSendResponses(requestResponseMap); + PredicateWithResult predicate = injector + .getInstance(GetImageWhenImageInZoneHasActiveStatusPredicateWithResult.class); + ZoneAndId zoneAdnId0 = ZoneAndId.fromZoneAndId("az-1.region-a.geo-1", "15"); + ZoneAndId zoneAdnId1 = ZoneAndId.fromZoneAndId("az-1.region-a.geo-1", "14"); + ZoneAndId zoneAdnId2 = ZoneAndId.fromZoneAndId("az-1.region-a.geo-1", "11"); + ZoneAndId zoneAdnId3 = ZoneAndId.fromZoneAndId("az-1.region-a.geo-1", "10"); + assertTrue(illegalStateExceptionThrown(predicate, zoneAdnId0)); + assertTrue(illegalStateExceptionThrown(predicate, zoneAdnId1)); + assertTrue(illegalStateExceptionThrown(predicate, zoneAdnId2)); + assertTrue(illegalStateExceptionThrown(predicate, zoneAdnId3)); + } + + private boolean illegalStateExceptionThrown(PredicateWithResult predicate, ZoneAndId zoneAndId) { + try { + predicate.apply(zoneAndId); + } catch (IllegalStateException e) { + return true; + } + return false; + } + + @Override + public Injector apply(ComputeServiceContext input) { + return input.utils().injector(); + } + +} diff --git a/apis/openstack-nova/src/test/resources/image_list_detail_imageextension.json b/apis/openstack-nova/src/test/resources/image_list_detail_imageextension.json new file mode 100644 index 0000000000..d1112f3181 --- /dev/null +++ b/apis/openstack-nova/src/test/resources/image_list_detail_imageextension.json @@ -0,0 +1,121 @@ +{ + "images": [{ + "status": "UNRECOGNIZED", + "updated": "2012-02-02T19:11:00Z", + "name": "oneiric-server-cloudimg-amd64", + "links": [{ + "href": "https://nova-api.trystack.org:9774/v1.1/37/images/15", + "rel": "self" + }, { + "href": "https://nova-api.trystack.org:9774/37/images/15", + "rel": "bookmark" + }], + "created": "2012-02-02T19:10:52Z", + "progress": 100, + "id": "15", + "metadata": { + "kernel_id": "14", + "min_disk": 0, + "min_ram": 0, + "owner": "1" + } + }, { + "status": "UNKNOWN", + "updated": "2012-02-02T19:10:51Z", + "name": "oneiric-server-cloudimg-amd64-kernel", + "links": [{ + "href": "https://nova-api.trystack.org:9774/v1.1/37/images/14", + "rel": "self" + }, { + "href": "https://nova-api.trystack.org:9774/37/images/14", + "rel": "bookmark" + }], + "created": "2012-02-02T19:10:50Z", + "progress": 100, + "id": "14", + "metadata": { + "min_disk": 0, + "owner": "1", + "min_ram": 0 + } + }, { + "status": "ACTIVE", + "updated": "2012-02-02T19:10:41Z", + "name": "natty-server-cloudimg-amd64", + "links": [{ + "href": "https://nova-api.trystack.org:9774/v1.1/37/images/13", + "rel": "self" + }, { + "href": "https://nova-api.trystack.org:9774/37/images/13", + "rel": "bookmark" + }], + "created": "2012-02-02T19:10:33Z", + "progress": 100, + "id": "13", + "metadata": { + "kernel_id": "12", + "min_disk": 0, + "min_ram": 0, + "owner": "1" + } + }, { + "status": "SAVING", + "updated": "2012-02-02T19:10:33Z", + "name": "natty-server-cloudimg-amd64-kernel", + "links": [{ + "href": "https://nova-api.trystack.org:9774/v1.1/37/images/12", + "rel": "self" + }, { + "href": "https://nova-api.trystack.org:9774/37/images/12", + "rel": "bookmark" + }], + "created": "2012-02-02T19:10:32Z", + "progress": 100, + "id": "12", + "metadata": { + "min_disk": 0, + "owner": "1", + "min_ram": 0 + } + }, { + "status": "ERROR", + "updated": "2012-02-02T19:10:41Z", + "name": "natty-server-cloudimg-amd64", + "links": [{ + "href": "https://nova-api.trystack.org:9774/v1.1/37/images/11", + "rel": "self" + }, { + "href": "https://nova-api.trystack.org:9774/37/images/11", + "rel": "bookmark" + }], + "created": "2012-02-02T19:10:33Z", + "progress": 100, + "id": "11", + "metadata": { + "kernel_id": "12", + "min_disk": 0, + "min_ram": 0, + "owner": "1" + } + }, { + "status": "ERROR", + "updated": "2012-02-02T19:10:41Z", + "name": "natty-server-cloudimg-amd64", + "links": [{ + "href": "https://nova-api.trystack.org:9774/v1.1/37/images/10", + "rel": "self" + }, { + "href": "https://nova-api.trystack.org:9774/37/images/10", + "rel": "bookmark" + }], + "created": "2012-02-02T19:10:33Z", + "progress": 100, + "id": "10", + "metadata": { + "kernel_id": "12", + "min_disk": 0, + "min_ram": 0, + "owner": "1" + } + }] +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/predicates/PredicateWithResult.java b/core/src/main/java/org/jclouds/predicates/PredicateWithResult.java index 41ce34b1bb..a424b3a991 100644 --- a/core/src/main/java/org/jclouds/predicates/PredicateWithResult.java +++ b/core/src/main/java/org/jclouds/predicates/PredicateWithResult.java @@ -22,10 +22,10 @@ import com.google.common.annotations.Beta; import com.google.common.base.Predicate; @Beta -public interface PredicateWithResult extends Predicate { - +public interface PredicateWithResult extends Predicate { + Result getResult(); - + Throwable getLastFailure(); - + } From acf1bc2d2238b87d829c85b35499760f5bec5427 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Wed, 9 May 2012 18:20:42 -0700 Subject: [PATCH 058/148] updated template expectations --- .../aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java | 6 +++--- .../compute/HPCloudComputeTemplateBuilderLiveTest.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java index 057a5a33ab..1f56d26043 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java @@ -170,7 +170,7 @@ public class AWSEC2TemplateBuilderLiveTest extends EC2TemplateBuilderLiveTest { Template defaultTemplate = view.getComputeService().templateBuilder().build(); assert (defaultTemplate.getImage().getProviderId().startsWith("ami-")) : defaultTemplate; - assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "pv-2012.03.rc-0"); + assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "pv-2012.03.1"); assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true); assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.AMZN_LINUX); assertEquals(defaultTemplate.getImage().getUserMetadata().get("rootDeviceType"), "ebs"); @@ -185,7 +185,7 @@ public class AWSEC2TemplateBuilderLiveTest extends EC2TemplateBuilderLiveTest { Template defaultTemplate = view.getComputeService().templateBuilder().osFamily(OsFamily.AMZN_LINUX) .imageMatches(EC2ImagePredicates.rootDeviceType(RootDeviceType.INSTANCE_STORE)).build(); assert (defaultTemplate.getImage().getProviderId().startsWith("ami-")) : defaultTemplate; - assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "pv-2012.03.rc-0"); + assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "pv-2012.03.1"); assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true); assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.AMZN_LINUX); assertEquals(defaultTemplate.getImage().getUserMetadata().get("rootDeviceType"), "instance-store"); @@ -212,7 +212,7 @@ public class AWSEC2TemplateBuilderLiveTest extends EC2TemplateBuilderLiveTest { System.out.println(fastestTemplate.getImage()); assert (fastestTemplate.getImage().getProviderId().startsWith("ami-")) : fastestTemplate; assertEquals(fastestTemplate.getHardware().getProviderId(), InstanceType.CC2_8XLARGE); - assertEquals(fastestTemplate.getImage().getOperatingSystem().getVersion(), "11.10"); + assertEquals(fastestTemplate.getImage().getOperatingSystem().getVersion(), "12.04"); assertEquals(fastestTemplate.getImage().getOperatingSystem().is64Bit(), true); assertEquals(fastestTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU); assertEquals(fastestTemplate.getImage().getUserMetadata().get("rootDeviceType"), "ebs"); diff --git a/providers/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/compute/HPCloudComputeTemplateBuilderLiveTest.java b/providers/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/compute/HPCloudComputeTemplateBuilderLiveTest.java index 59f7b5d1be..2ca43be460 100644 --- a/providers/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/compute/HPCloudComputeTemplateBuilderLiveTest.java +++ b/providers/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/compute/HPCloudComputeTemplateBuilderLiveTest.java @@ -53,7 +53,7 @@ public class HPCloudComputeTemplateBuilderLiveTest extends BaseTemplateBuilderLi public boolean apply(OsFamilyVersion64Bit input) { switch (input.family) { case UBUNTU: - return (input.version.equals("") || input.version.matches("^1[01].*")) && input.is64Bit; + return (input.version.equals("") || input.version.equals("12.04") || input.version.matches("^1[01].*")) && input.is64Bit; case DEBIAN: return input.is64Bit && !input.version.equals("5.0"); case CENTOS: @@ -71,9 +71,9 @@ public class HPCloudComputeTemplateBuilderLiveTest extends BaseTemplateBuilderLi public void testTemplateBuilder() { Template defaultTemplate = this.view.getComputeService().templateBuilder().build(); assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true); - assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "11.10"); + assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "12.04"); assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU); - assertEquals(defaultTemplate.getImage().getName(), "Ubuntu Oneiric 11.10 Server 64-bit 20120311"); + assertEquals(defaultTemplate.getImage().getName(), "Ubuntu Precise 12.04 LTS Server 64-bit 20120424"); assertEquals(defaultTemplate.getImage().getDefaultCredentials().getUser(), "ubuntu"); assertEquals(defaultTemplate.getLocation().getId(), "az-2.region-a.geo-1"); assertEquals(defaultTemplate.getImage().getLocation().getId(), "az-2.region-a.geo-1"); From 9701d80bbb62ed4cafdee77f5753de2b95da3c37 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Wed, 9 May 2012 18:21:56 -0700 Subject: [PATCH 059/148] Issue 919: added RegionIdFilter and ZoneIdFilter --- .../location/config/LocationModule.java | 46 +++++++++--- .../location/predicates/RegionIdFilter.java | 35 +++++++++ .../location/predicates/ZoneIdFilter.java | 35 +++++++++ .../fromconfig/AnyOrConfiguredRegionId.java | 56 ++++++++++++++ .../fromconfig/AnyOrConfiguredZoneId.java | 56 ++++++++++++++ .../org/jclouds/json/GsonExperimentsTest.java | 68 ++++++++++++++++- .../AnyOrConfiguredRegionIdTest.java | 74 +++++++++++++++++++ .../fromconfig/AnyOrConfiguredZoneIdTest.java | 74 +++++++++++++++++++ 8 files changed, 432 insertions(+), 12 deletions(-) create mode 100644 core/src/main/java/org/jclouds/location/predicates/RegionIdFilter.java create mode 100644 core/src/main/java/org/jclouds/location/predicates/ZoneIdFilter.java create mode 100644 core/src/main/java/org/jclouds/location/predicates/fromconfig/AnyOrConfiguredRegionId.java create mode 100644 core/src/main/java/org/jclouds/location/predicates/fromconfig/AnyOrConfiguredZoneId.java create mode 100644 core/src/test/java/org/jclouds/location/predicates/fromconfig/AnyOrConfiguredRegionIdTest.java create mode 100644 core/src/test/java/org/jclouds/location/predicates/fromconfig/AnyOrConfiguredZoneIdTest.java diff --git a/core/src/main/java/org/jclouds/location/config/LocationModule.java b/core/src/main/java/org/jclouds/location/config/LocationModule.java index 1a9a09648b..c3737ffec0 100644 --- a/core/src/main/java/org/jclouds/location/config/LocationModule.java +++ b/core/src/main/java/org/jclouds/location/config/LocationModule.java @@ -18,6 +18,7 @@ */ package org.jclouds.location.config; +import static com.google.common.base.Preconditions.checkNotNull; import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL; import java.net.URI; @@ -35,6 +36,8 @@ import org.jclouds.location.Iso3166; import org.jclouds.location.Provider; import org.jclouds.location.Region; import org.jclouds.location.Zone; +import org.jclouds.location.predicates.RegionIdFilter; +import org.jclouds.location.predicates.ZoneIdFilter; import org.jclouds.location.suppliers.ImplicitLocationSupplier; import org.jclouds.location.suppliers.ImplicitRegionIdSupplier; import org.jclouds.location.suppliers.LocationIdToIso3166CodesSupplier; @@ -51,7 +54,10 @@ import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExc import com.google.common.base.Function; import com.google.common.base.Optional; +import com.google.common.base.Predicate; import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.collect.Sets; import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.google.inject.TypeLiteral; @@ -107,10 +113,36 @@ public class LocationModule extends AbstractModule { @Singleton @Region protected Supplier> regionIdsSupplier(AtomicReference authException, - @Named(PROPERTY_SESSION_INTERVAL) long seconds, RegionIdsSupplier uncached) { - return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, seconds, uncached); + @Named(PROPERTY_SESSION_INTERVAL) long seconds, RegionIdFilter filter, RegionIdsSupplier uncached) { + return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, seconds, + Suppliers.compose(new FilterStrings(filter), uncached)); + } + + @Provides + @Singleton + @Zone + protected Supplier> zoneIdsSupplier( + AtomicReference authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds, + ZoneIdFilter filter, ZoneIdsSupplier uncached) { + return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, seconds, + Suppliers.compose(new FilterStrings(filter), uncached)); } + static class FilterStrings implements Function, Set>{ + public final Predicate filter; + + public FilterStrings(Predicate filter) { + this.filter = checkNotNull(filter, "filter"); + } + + + @Override + public Set apply(Set input) { + return Sets.filter(input, filter); + } + + } + @Provides @Singleton @Region @@ -128,15 +160,7 @@ public class LocationModule extends AbstractModule { return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, seconds, uncached); } - @Provides - @Singleton - @Zone - protected Supplier> regionIdsSupplier( - AtomicReference authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds, - ZoneIdsSupplier uncached) { - return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, seconds, uncached); - } - + @Provides @Singleton @Zone diff --git a/core/src/main/java/org/jclouds/location/predicates/RegionIdFilter.java b/core/src/main/java/org/jclouds/location/predicates/RegionIdFilter.java new file mode 100644 index 0000000000..67dc550acc --- /dev/null +++ b/core/src/main/java/org/jclouds/location/predicates/RegionIdFilter.java @@ -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.location.predicates; + +import org.jclouds.location.predicates.fromconfig.AnyOrConfiguredRegionId; + +import com.google.common.base.Predicate; +import com.google.inject.ImplementedBy; + +/** + * Means to constrain regions returned to abstraction calls. Particularly useful + * to whitelist certain regions. + * + * @author Adrian Cole + */ +@ImplementedBy(AnyOrConfiguredRegionId.class) +public interface RegionIdFilter extends Predicate { + +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/location/predicates/ZoneIdFilter.java b/core/src/main/java/org/jclouds/location/predicates/ZoneIdFilter.java new file mode 100644 index 0000000000..8393c45926 --- /dev/null +++ b/core/src/main/java/org/jclouds/location/predicates/ZoneIdFilter.java @@ -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.location.predicates; + +import org.jclouds.location.predicates.fromconfig.AnyOrConfiguredZoneId; + +import com.google.common.base.Predicate; +import com.google.inject.ImplementedBy; + +/** + * Means to constrain zones returned to abstraction calls. Particularly useful + * to whitelist certain zones. + * + * @author Adrian Cole + */ +@ImplementedBy(AnyOrConfiguredZoneId.class) +public interface ZoneIdFilter extends Predicate { + +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/location/predicates/fromconfig/AnyOrConfiguredRegionId.java b/core/src/main/java/org/jclouds/location/predicates/fromconfig/AnyOrConfiguredRegionId.java new file mode 100644 index 0000000000..5b1aa2d93f --- /dev/null +++ b/core/src/main/java/org/jclouds/location/predicates/fromconfig/AnyOrConfiguredRegionId.java @@ -0,0 +1,56 @@ +/** + * 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.location.predicates.fromconfig; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.location.predicates.RegionIdFilter; +import org.jclouds.location.suppliers.fromconfig.RegionIdsFromConfiguration; + +/** + * + * If there are regions configured in {@link RegionIdsFromConfiguration}, return + * true if that set contains the input param. If there aren't, return true. + * + * @author Adrian Cole + */ +@Singleton +public class AnyOrConfiguredRegionId implements RegionIdFilter { + + private RegionIdsFromConfiguration idsInConfigSupplier; + + @Inject + protected AnyOrConfiguredRegionId(RegionIdsFromConfiguration idsInConfigSupplier) { + this.idsInConfigSupplier = checkNotNull(idsInConfigSupplier, "idsInConfigSupplier"); + } + + @Override + public boolean apply(String input) { + Set idsInConfig = idsInConfigSupplier.get(); + if (idsInConfig.size() == 0) + return true; + return idsInConfig.contains(input); + } + +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/location/predicates/fromconfig/AnyOrConfiguredZoneId.java b/core/src/main/java/org/jclouds/location/predicates/fromconfig/AnyOrConfiguredZoneId.java new file mode 100644 index 0000000000..6dd68f3e84 --- /dev/null +++ b/core/src/main/java/org/jclouds/location/predicates/fromconfig/AnyOrConfiguredZoneId.java @@ -0,0 +1,56 @@ +/** + * 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.location.predicates.fromconfig; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.location.predicates.ZoneIdFilter; +import org.jclouds.location.suppliers.fromconfig.ZoneIdsFromConfiguration; + +/** + * + * If there are zones configured in {@link ZoneIdsFromConfiguration}, return + * true if that set contains the input param. If there aren't, return true. + * + * @author Adrian Cole + */ +@Singleton +public class AnyOrConfiguredZoneId implements ZoneIdFilter { + + private ZoneIdsFromConfiguration idsInConfigSupplier; + + @Inject + protected AnyOrConfiguredZoneId(ZoneIdsFromConfiguration idsInConfigSupplier) { + this.idsInConfigSupplier = checkNotNull(idsInConfigSupplier, "idsInConfigSupplier"); + } + + @Override + public boolean apply(String input) { + Set idsInConfig = idsInConfigSupplier.get(); + if (idsInConfig.size() == 0) + return true; + return idsInConfig.contains(input); + } + +} \ No newline at end of file diff --git a/core/src/test/java/org/jclouds/json/GsonExperimentsTest.java b/core/src/test/java/org/jclouds/json/GsonExperimentsTest.java index 938aae0393..4f8493c664 100644 --- a/core/src/test/java/org/jclouds/json/GsonExperimentsTest.java +++ b/core/src/test/java/org/jclouds/json/GsonExperimentsTest.java @@ -18,10 +18,12 @@ */ package org.jclouds.json; +import static com.google.common.base.Objects.equal; import static org.testng.Assert.assertEquals; import java.io.IOException; import java.io.StringReader; +import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; @@ -31,11 +33,17 @@ import java.util.concurrent.atomic.AtomicReference; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; +import com.google.common.base.Optional; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonParser; +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; import com.google.inject.TypeLiteral; /** @@ -75,6 +83,64 @@ public class GsonExperimentsTest { assertEquals(json2, "[\"hello\",5,{\"name\":\"GREETINGS\",\"source\":\"guest\"}]"); } + public class OptionalTypeAdapterFactory implements TypeAdapterFactory { + public TypeAdapter create(Gson gson, TypeToken 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) newOptionalAdapter(elementAdapter); + } + + private TypeAdapter> newOptionalAdapter(final TypeAdapter elementAdapter) { + return new TypeAdapter>() { + public void write(JsonWriter out, Optional value) throws IOException { + if (value == null || !value.isPresent()) { + out.nullValue(); + return; + } + elementAdapter.write(out, value.get()); + } + + public Optional read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return Optional.absent(); + } + return Optional.of(elementAdapter.read(in)); + } + }; + } + } + + static class OptionalType { + Optional present = Optional.of("hello"); + Optional notPresent = Optional.absent(); + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object instanceof OptionalType) { + final OptionalType other = OptionalType.class.cast(object); + return equal(present, other.present) && equal(notPresent, other.notPresent); + } else { + return false; + } + } + } + + public void testPersistOptional() { + Gson gson = new GsonBuilder().registerTypeAdapterFactory(new OptionalTypeAdapterFactory()).create(); + String json = gson.toJson(new OptionalType()); + assertEquals(json, "{\"present\":\"hello\"}"); + assertEquals(gson.fromJson(json, OptionalType.class), new OptionalType()); + } + // inspired by // http://code.google.com/p/google-gson/source/browse/trunk/extras/src/main/java/com/google/gson/extras/examples/rawcollections/RawCollectionsExample.java public void testRawCollectionsWithParser() { @@ -111,7 +177,7 @@ public class GsonExperimentsTest { JsonToken token = reader.peek(); for (; token != JsonToken.END_DOCUMENT && nnn(toFind, reader, token, name); token = skipAndPeek(token, reader)) ; - T val = gson.fromJson(reader, type); + T val = gson. fromJson(reader, type); reader.close(); return val; } diff --git a/core/src/test/java/org/jclouds/location/predicates/fromconfig/AnyOrConfiguredRegionIdTest.java b/core/src/test/java/org/jclouds/location/predicates/fromconfig/AnyOrConfiguredRegionIdTest.java new file mode 100644 index 0000000000..cf675af648 --- /dev/null +++ b/core/src/test/java/org/jclouds/location/predicates/fromconfig/AnyOrConfiguredRegionIdTest.java @@ -0,0 +1,74 @@ +/** + * 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.location.predicates.fromconfig; + +import static org.testng.Assert.assertEquals; + +import java.util.Set; + +import org.jclouds.location.Provider; +import org.jclouds.location.predicates.RegionIdFilter; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.name.Names; + +/** + * Tests behavior of {@code AnyOrConfiguredRegionId} + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "AnyOrConfiguredRegionIdTest") +public class AnyOrConfiguredRegionIdTest { + + @Test + public void testWithoutConfigAllIdsMatch() { + Set regionIds = ImmutableSet.of("us-east-1", "eu-west-1"); + + RegionIdFilter filter = Guice.createInjector(new AbstractModule(){ + + @Override + protected void configure() { + bindConstant().annotatedWith(Provider.class).to("aws-ec2"); + } + + }).getInstance(AnyOrConfiguredRegionId.class); + assertEquals(Sets.filter(regionIds, filter), ImmutableSet.of("us-east-1", "eu-west-1")); + } + + @Test + public void testWithConfigOnlyMatchingIds() { + Set regionIds = ImmutableSet.of("us-east-1", "eu-west-1"); + + RegionIdFilter filter = Guice.createInjector(new AbstractModule(){ + + @Override + protected void configure() { + bindConstant().annotatedWith(Provider.class).to("aws-ec2"); + bindConstant().annotatedWith(Names.named("jclouds.regions")).to("us-east-1,unknown-1"); + } + + }).getInstance(AnyOrConfiguredRegionId.class); + + assertEquals(Sets.filter(regionIds, filter), ImmutableSet.of("us-east-1")); + } +} diff --git a/core/src/test/java/org/jclouds/location/predicates/fromconfig/AnyOrConfiguredZoneIdTest.java b/core/src/test/java/org/jclouds/location/predicates/fromconfig/AnyOrConfiguredZoneIdTest.java new file mode 100644 index 0000000000..372f70254f --- /dev/null +++ b/core/src/test/java/org/jclouds/location/predicates/fromconfig/AnyOrConfiguredZoneIdTest.java @@ -0,0 +1,74 @@ +/** + * 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.location.predicates.fromconfig; + +import static org.testng.Assert.assertEquals; + +import java.util.Set; + +import org.jclouds.location.Provider; +import org.jclouds.location.predicates.ZoneIdFilter; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.name.Names; + +/** + * Tests behavior of {@code AnyOrConfiguredZoneId} + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "AnyOrConfiguredZoneIdTest") +public class AnyOrConfiguredZoneIdTest { + + @Test + public void testWithoutConfigAllIdsMatch() { + Set zoneIds = ImmutableSet.of("us-east-1a", "us-east-1b"); + + ZoneIdFilter filter = Guice.createInjector(new AbstractModule(){ + + @Override + protected void configure() { + bindConstant().annotatedWith(Provider.class).to("aws-ec2"); + } + + }).getInstance(AnyOrConfiguredZoneId.class); + assertEquals(Sets.filter(zoneIds, filter), ImmutableSet.of("us-east-1a", "us-east-1b")); + } + + @Test + public void testWithConfigOnlyMatchingIds() { + Set zoneIds = ImmutableSet.of("us-east-1a", "us-east-1b"); + + ZoneIdFilter filter = Guice.createInjector(new AbstractModule(){ + + @Override + protected void configure() { + bindConstant().annotatedWith(Provider.class).to("aws-ec2"); + bindConstant().annotatedWith(Names.named("jclouds.zones")).to("us-east-1a,us-east-1d"); + } + + }).getInstance(AnyOrConfiguredZoneId.class); + + assertEquals(Sets.filter(zoneIds, filter), ImmutableSet.of("us-east-1a")); + } +} From f0a8870b800112dd5051175953e9080454ca2e07 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Wed, 9 May 2012 18:22:34 -0700 Subject: [PATCH 060/148] Issue 919: ensure filters work on multi-zone openstack-nova --- .../PasswordAuthenticationExpectTest.java | 2 +- .../NovaComputeServiceAdapterExpectTest.java | 8 +-- .../compute/NovaComputeServiceExpectTest.java | 2 +- ...ocateAndAddFloatingIpToNodeExpectTest.java | 6 +- .../AdminActionsClientExpectTest.java | 48 +++++++-------- .../FloatingIPAsyncClientExpectTest.java | 12 ++-- .../FloatingIPClientExpectTest.java | 12 ++-- .../HostAdministrationClientExpectTest.java | 4 +- .../extensions/KeyPairClientExpectTest.java | 10 ++-- .../SecurityGroupClientExpectTest.java | 18 +++--- ...verWithSecurityGroupsClientExpectTest.java | 4 +- .../SimpleTenantUsageClientExpectTest.java | 4 +- .../VirtualInterfaceClientExpectTest.java | 4 +- .../extensions/VolumeClientExpectTest.java | 56 +++++++++--------- .../features/ExtensionClientExpectTest.java | 8 +-- .../v1_1/features/FlavorClientExpectTest.java | 8 +-- .../v1_1/features/ImageClientExpectTest.java | 8 +-- .../v1_1/features/ServerClientExpectTest.java | 14 ++--- .../CreateSecurityGroupIfNeededTest.java | 10 ++-- ...yGroupWithNameAndReturnTrueExpectTest.java | 4 +- ...seNovaComputeServiceContextExpectTest.java | 8 +-- .../v1_1/internal/BaseNovaExpectTest.java | 11 +++- .../keystone/v2_0/parse/ParseAccessTest.java | 25 +++++--- ...omAccessForTypeAndVersionSupplierTest.java | 2 + ...omAccessForTypeAndVersionSupplierTest.java | 6 +- ...omAccessForTypeAndVersionSupplierTest.java | 8 +-- .../test/resources/keystoneAuthResponse.json | 58 +++++++++++-------- 27 files changed, 195 insertions(+), 165 deletions(-) diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/PasswordAuthenticationExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/PasswordAuthenticationExpectTest.java index 607f103c29..5fffe532e7 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/PasswordAuthenticationExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/PasswordAuthenticationExpectTest.java @@ -55,7 +55,7 @@ public class PasswordAuthenticationExpectTest extends BaseNovaClientExpectTest { HttpRequest listServers = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/servers")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceAdapterExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceAdapterExpectTest.java index b2df3a5eee..78eb42ab5c 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceAdapterExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceAdapterExpectTest.java @@ -58,7 +58,7 @@ public class NovaComputeServiceAdapterExpectTest extends BaseNovaComputeServiceC HttpRequest createServer = HttpRequest .builder() .method("POST") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/servers")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()) @@ -72,7 +72,7 @@ public class NovaComputeServiceAdapterExpectTest extends BaseNovaComputeServiceC HttpRequest serverDetail = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/servers/71752")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/71752")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -111,7 +111,7 @@ public class NovaComputeServiceAdapterExpectTest extends BaseNovaComputeServiceC HttpRequest createServer = HttpRequest .builder() .method("POST") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/servers")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()) @@ -126,7 +126,7 @@ public class NovaComputeServiceAdapterExpectTest extends BaseNovaComputeServiceC HttpRequest serverDetail = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/servers/71752")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/71752")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceExpectTest.java index ac69ac22b6..a71c072104 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/NovaComputeServiceExpectTest.java @@ -125,7 +125,7 @@ public class NovaComputeServiceExpectTest extends BaseNovaComputeServiceExpectTe HttpRequest listServers = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/servers/detail")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/detail")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/functions/AllocateAndAddFloatingIpToNodeExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/functions/AllocateAndAddFloatingIpToNodeExpectTest.java index 8e4e0df4a7..2aac0bedf3 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/functions/AllocateAndAddFloatingIpToNodeExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/functions/AllocateAndAddFloatingIpToNodeExpectTest.java @@ -58,7 +58,7 @@ public class AllocateAndAddFloatingIpToNodeExpectTest extends BaseNovaComputeSer .credentials(LoginCredentials.builder().password("foo").build()).build(); HttpRequest allocateFloatingIP = HttpRequest.builder().method("POST").endpoint( - URI.create("https://compute.north.host/v1.1/3456/os-floating-ips")).headers( + URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-floating-ips")).headers( ImmutableMultimap. builder().put("Accept", "application/json").put("X-Auth-Token", authToken).build()).payload(payloadFromStringWithContentType("{}", "application/json")).build(); @@ -88,7 +88,7 @@ public class AllocateAndAddFloatingIpToNodeExpectTest extends BaseNovaComputeSer private HttpRequest addFloatingIPForAddress(String address) { HttpRequest addFloatingIPRequest = HttpRequest.builder().method("POST").endpoint( - URI.create("https://compute.north.host/v1.1/3456/servers/71592/action")) + URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/71592/action")) .headers( ImmutableMultimap. builder().put("Accept", "*/*") .put("X-Auth-Token", authToken).build()).payload( @@ -107,7 +107,7 @@ public class AllocateAndAddFloatingIpToNodeExpectTest extends BaseNovaComputeSer "application/json")).build(); HttpRequest listFloatingIPs = HttpRequest.builder().method("GET").endpoint( - URI.create("https://compute.north.host/v1.1/3456/os-floating-ips")).headers( + URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-floating-ips")).headers( ImmutableMultimap. builder().put("Accept", "application/json").put("X-Auth-Token", authToken).build()).build(); diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsClientExpectTest.java index b7ef6aa329..7a1cc628f4 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsClientExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsClientExpectTest.java @@ -46,7 +46,7 @@ import com.google.common.collect.ImmutableMultimap; public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { public void testSuspend() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -58,7 +58,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { } public void testSuspendFailsNotFound() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -71,7 +71,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { @Test(expectedExceptions = AuthorizationException.class) public void testSuspendFailsNotAuthorized() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -83,7 +83,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { } public void testResume() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -95,7 +95,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { } public void testResumeFailsNotFound() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -108,7 +108,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { @Test(expectedExceptions = AuthorizationException.class) public void testResumeFailsNotAuthorized() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -120,7 +120,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { } public void testLock() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -132,7 +132,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { } public void testLockFailsNotFound() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -144,7 +144,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { } public void testUnlock() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -156,7 +156,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { } public void testUnlockFailsNotFound() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -168,7 +168,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { } public void testPause() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -180,7 +180,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { } public void testPauseFailsNotFound() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -192,7 +192,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { } public void testUnpause() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -204,7 +204,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { } public void testUnpauseFailsNotFound() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -216,7 +216,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { } public void testMigrateServer() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -229,7 +229,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { public void testMigrateServerFailsNotFound() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -241,7 +241,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { } public void testResetNetworkOfServer() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -253,7 +253,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { } public void testResetNetworkOfServerFailsNotFound() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -265,7 +265,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { } public void testInjectNetworkInfoIntoServer() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -277,7 +277,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { } public void testInjectNetworkInfoIntoServerFailsNotFound() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -289,7 +289,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { } public void testBackupServer() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -304,7 +304,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { @Test(expectedExceptions = ResourceNotFoundException.class) public void testBackupServerFailNotFound() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -317,7 +317,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { } public void testLiveMigrateServer() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -330,7 +330,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { } public void testLiveMigrateServerFailsNotFound() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/action"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/action"); AdminActionsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPAsyncClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPAsyncClientExpectTest.java index 9f5afdc23d..fd63ec2aad 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPAsyncClientExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPAsyncClientExpectTest.java @@ -70,7 +70,7 @@ public class FloatingIPAsyncClientExpectTest extends BaseNovaAsyncClientExpectTe HttpRequest listFloatingIPs = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-floating-ips")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-floating-ips")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -91,7 +91,7 @@ public class FloatingIPAsyncClientExpectTest extends BaseNovaAsyncClientExpectTe HttpRequest listFloatingIPs = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-floating-ips")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-floating-ips")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -109,7 +109,7 @@ public class FloatingIPAsyncClientExpectTest extends BaseNovaAsyncClientExpectTe HttpRequest getFloatingIP = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-floating-ips/1")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-floating-ips/1")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -128,7 +128,7 @@ public class FloatingIPAsyncClientExpectTest extends BaseNovaAsyncClientExpectTe HttpRequest getFloatingIP = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-floating-ips/1")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-floating-ips/1")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -145,7 +145,7 @@ public class FloatingIPAsyncClientExpectTest extends BaseNovaAsyncClientExpectTe HttpRequest allocateFloatingIP = HttpRequest .builder() .method("POST") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-floating-ips")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-floating-ips")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()) @@ -167,7 +167,7 @@ public class FloatingIPAsyncClientExpectTest extends BaseNovaAsyncClientExpectTe HttpRequest allocateFloatingIP = HttpRequest .builder() .method("POST") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-floating-ips")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-floating-ips")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()) diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClientExpectTest.java index 1f44b4977a..6a0c94d9bb 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClientExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClientExpectTest.java @@ -69,7 +69,7 @@ public class FloatingIPClientExpectTest extends BaseNovaClientExpectTest { HttpRequest listFloatingIPs = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-floating-ips")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-floating-ips")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -90,7 +90,7 @@ public class FloatingIPClientExpectTest extends BaseNovaClientExpectTest { HttpRequest listFloatingIPs = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-floating-ips")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-floating-ips")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -107,7 +107,7 @@ public class FloatingIPClientExpectTest extends BaseNovaClientExpectTest { HttpRequest getFloatingIP = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-floating-ips/1")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-floating-ips/1")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -126,7 +126,7 @@ public class FloatingIPClientExpectTest extends BaseNovaClientExpectTest { HttpRequest getFloatingIP = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-floating-ips/1")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-floating-ips/1")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -143,7 +143,7 @@ public class FloatingIPClientExpectTest extends BaseNovaClientExpectTest { HttpRequest allocateFloatingIP = HttpRequest .builder() .method("POST") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-floating-ips")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-floating-ips")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()) @@ -165,7 +165,7 @@ public class FloatingIPClientExpectTest extends BaseNovaClientExpectTest { HttpRequest allocateFloatingIP = HttpRequest .builder() .method("POST") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-floating-ips")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-floating-ips")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()) diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/HostAdministrationClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/HostAdministrationClientExpectTest.java index 54d7036994..0bf78b04c9 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/HostAdministrationClientExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/HostAdministrationClientExpectTest.java @@ -46,7 +46,7 @@ public class HostAdministrationClientExpectTest extends BaseNovaClientExpectTest public void testList() throws Exception { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-hosts"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-hosts"); HostAdministrationClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, HttpRequest.builder().method("GET").headers(ImmutableMultimap.of("Accept", MediaType.APPLICATION_JSON, "X-Auth-Token", authToken)) @@ -64,7 +64,7 @@ public class HostAdministrationClientExpectTest extends BaseNovaClientExpectTest } public void testGet() throws Exception { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-hosts/xyz"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-hosts/xyz"); HostAdministrationClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, HttpRequest.builder().method("GET").headers(ImmutableMultimap.of("Accept", MediaType.APPLICATION_JSON, "X-Auth-Token", authToken)) diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairClientExpectTest.java index d0ec314d8b..1edf6fc8e2 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairClientExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairClientExpectTest.java @@ -46,7 +46,7 @@ public class KeyPairClientExpectTest extends BaseNovaClientExpectTest { HttpRequest listKeyPairs = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-keypairs")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-keypairs")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -67,7 +67,7 @@ public class KeyPairClientExpectTest extends BaseNovaClientExpectTest { HttpRequest listKeyPairs = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-keypairs")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-keypairs")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -85,7 +85,7 @@ public class KeyPairClientExpectTest extends BaseNovaClientExpectTest { HttpRequest createKeyPair = HttpRequest .builder() .method("POST") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-keypairs")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-keypairs")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()) @@ -107,7 +107,7 @@ public class KeyPairClientExpectTest extends BaseNovaClientExpectTest { HttpRequest createKeyPair = HttpRequest .builder() .method("POST") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-keypairs")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-keypairs")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()) @@ -136,7 +136,7 @@ public class KeyPairClientExpectTest extends BaseNovaClientExpectTest { HttpRequest deleteKeyPair = HttpRequest .builder() .method("DELETE") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-keypairs/testkeypair")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-keypairs/testkeypair")) .headers( ImmutableMultimap. builder().put("Accept", "*/*").put("X-Auth-Token", authToken) .build()).build(); diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClientExpectTest.java index a8baa07416..9518debaf8 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClientExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClientExpectTest.java @@ -48,7 +48,7 @@ import com.google.common.collect.ImmutableSet; public class SecurityGroupClientExpectTest extends BaseNovaClientExpectTest { public void testListSecurityGroupsWhenResponseIs2xx() throws Exception { HttpRequest listSecurityGroups = HttpRequest.builder().method("GET").endpoint( - URI.create("https://compute.north.host/v1.1/3456/os-security-groups")).headers( + URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-security-groups")).headers( ImmutableMultimap. builder().put("Accept", "application/json").put("X-Auth-Token", authToken).build()).build(); @@ -67,7 +67,7 @@ public class SecurityGroupClientExpectTest extends BaseNovaClientExpectTest { public void testListSecurityGroupsWhenReponseIs404IsEmpty() throws Exception { HttpRequest listListSecurityGroups = HttpRequest.builder().method("GET").endpoint( - URI.create("https://compute.north.host/v1.1/3456/os-security-groups")).headers( + URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-security-groups")).headers( ImmutableMultimap. builder().put("Accept", "application/json").put("X-Auth-Token", authToken).build()).build(); @@ -84,7 +84,7 @@ public class SecurityGroupClientExpectTest extends BaseNovaClientExpectTest { public void testGetSecurityGroupWhenResponseIs2xx() throws Exception { HttpRequest getSecurityGroup = HttpRequest.builder().method("GET").endpoint( - URI.create("https://compute.north.host/v1.1/3456/os-security-groups/0")).headers( + URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-security-groups/0")).headers( ImmutableMultimap. builder().put("Accept", "application/json").put("X-Auth-Token", authToken).build()).build(); @@ -101,7 +101,7 @@ public class SecurityGroupClientExpectTest extends BaseNovaClientExpectTest { public void testGetSecurityGroupWhenResponseIs404() throws Exception { HttpRequest getSecurityGroup = HttpRequest.builder().method("GET").endpoint( - URI.create("https://compute.north.host/v1.1/3456/os-security-groups/0")).headers( + URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-security-groups/0")).headers( ImmutableMultimap. builder().put("Accept", "application/json").put("X-Auth-Token", authToken).build()).build(); @@ -118,7 +118,7 @@ public class SecurityGroupClientExpectTest extends BaseNovaClientExpectTest { public void testCreateSecurityGroupWhenResponseIs2xx() throws Exception { HttpRequest createSecurityGroup = HttpRequest.builder().method("POST").endpoint( - URI.create("https://compute.north.host/v1.1/3456/os-security-groups")).headers( + URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-security-groups")).headers( ImmutableMultimap. builder().put("Accept", "application/json").put("X-Auth-Token", authToken).build()) .payload( @@ -140,7 +140,7 @@ public class SecurityGroupClientExpectTest extends BaseNovaClientExpectTest { public void testDeleteSecurityGroupWhenResponseIs2xx() throws Exception { HttpRequest deleteSecurityGroup = HttpRequest.builder().method("DELETE").endpoint( - URI.create("https://compute.north.host/v1.1/3456/os-security-groups/160")) + URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-security-groups/160")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -160,7 +160,7 @@ public class SecurityGroupClientExpectTest extends BaseNovaClientExpectTest { HttpRequest createSecurityGroupRule = HttpRequest .builder() .method("POST") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-security-group-rules")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-security-group-rules")) .headers( ImmutableMultimap. builder().put("Accept", "application/json").put( "X-Auth-Token", authToken).build()) @@ -186,7 +186,7 @@ public class SecurityGroupClientExpectTest extends BaseNovaClientExpectTest { HttpRequest createSecurityGroupRule = HttpRequest .builder() .method("POST") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-security-group-rules")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-security-group-rules")) .headers( ImmutableMultimap. builder().put("Accept", "application/json").put( "X-Auth-Token", authToken).build()) @@ -210,7 +210,7 @@ public class SecurityGroupClientExpectTest extends BaseNovaClientExpectTest { public void testDeleteSecurityGroupRuleWhenResponseIs2xx() throws Exception { HttpRequest deleteSecurityGroupRule = HttpRequest.builder().method("DELETE").endpoint( - URI.create("https://compute.north.host/v1.1/3456/os-security-group-rules/161")) + URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-security-group-rules/161")) .headers( ImmutableMultimap. builder().put("Accept", "*/*") .put("X-Auth-Token", authToken).build()).build(); diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClientExpectTest.java index 237526a83a..bb0d5cc477 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClientExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/ServerWithSecurityGroupsClientExpectTest.java @@ -38,7 +38,7 @@ import com.google.common.collect.ImmutableSet; public class ServerWithSecurityGroupsClientExpectTest extends BaseNovaClientExpectTest { public void testGetServerWithSecurityGroups() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-create-server-ext/8d0a6ca5-8849-4b3d-b86e-f24c92490ebb"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-create-server-ext/8d0a6ca5-8849-4b3d-b86e-f24c92490ebb"); ServerWithSecurityGroupsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -52,7 +52,7 @@ public class ServerWithSecurityGroupsClientExpectTest extends BaseNovaClientExpe } public void testGetServerWithSecurityGroupsFailNotFound() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-create-server-ext/8d0a6ca5-8849-4b3d-b86e-f24c92490ebb"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-create-server-ext/8d0a6ca5-8849-4b3d-b86e-f24c92490ebb"); ServerWithSecurityGroupsClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SimpleTenantUsageClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SimpleTenantUsageClientExpectTest.java index 3a85a3c7c5..b3b05fa266 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SimpleTenantUsageClientExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SimpleTenantUsageClientExpectTest.java @@ -50,7 +50,7 @@ public class SimpleTenantUsageClientExpectTest extends BaseNovaClientExpectTest private DateService dateService = new SimpleDateFormatDateService(); public void testList() throws Exception { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-simple-tenant-usage"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-simple-tenant-usage"); SimpleTenantUsageClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, HttpRequest.builder().method("GET").headers(ImmutableMultimap.of("Accept", MediaType.APPLICATION_JSON, "X-Auth-Token", authToken)) @@ -74,7 +74,7 @@ public class SimpleTenantUsageClientExpectTest extends BaseNovaClientExpectTest } public void testGet() throws Exception { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-simple-tenant-usage/test-1234"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-simple-tenant-usage/test-1234"); SimpleTenantUsageClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, HttpRequest.builder().method("GET").headers(ImmutableMultimap.of("Accept", MediaType.APPLICATION_JSON, "X-Auth-Token", authToken)) diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VirtualInterfaceClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VirtualInterfaceClientExpectTest.java index 553be0dac1..568a9e825b 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VirtualInterfaceClientExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VirtualInterfaceClientExpectTest.java @@ -38,7 +38,7 @@ import com.google.common.collect.Iterables; public class VirtualInterfaceClientExpectTest extends BaseNovaClientExpectTest { public void testListVirtualInterfaces() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/os-virtual-interfaces"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/os-virtual-interfaces"); VirtualInterfaceClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -52,7 +52,7 @@ public class VirtualInterfaceClientExpectTest extends BaseNovaClientExpectTest { } public void testListVirtualInterfacesFailNotFound() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/1/os-virtual-interfaces"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/1/os-virtual-interfaces"); VirtualInterfaceClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeClientExpectTest.java index 605255971a..3e67d534d9 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeClientExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeClientExpectTest.java @@ -53,7 +53,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { private DateService dateService = new SimpleDateFormatDateService(); public void testListVolumes() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-volumes"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volumes"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -66,7 +66,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testListVolumesFail() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-volumes"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volumes"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -79,7 +79,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testListVolumesInDetail() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-volumes/detail"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volumes/detail"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -92,7 +92,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testListVolumesInDetailFail() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-volumes/detail"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volumes/detail"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -105,7 +105,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testCreateVolume() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-volumes"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volumes"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -122,7 +122,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { @Test(expectedExceptions = ResourceNotFoundException.class) public void testCreateVolumeFail() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-volumes"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volumes"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -138,7 +138,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testGetVolume() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-volumes/1"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volumes/1"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -158,7 +158,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testGetVolumeFail() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-volumes/1"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volumes/1"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -170,7 +170,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testDeleteVolume() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-volumes/1"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volumes/1"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -182,7 +182,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testDeleteVolumeFail() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-volumes/1"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volumes/1"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -194,7 +194,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testListAttachments() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/instance-1/os-volume_attachments"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/instance-1/os-volume_attachments"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -214,7 +214,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { @Test(expectedExceptions = AuthorizationException.class) public void testListAttachmentsFail() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/instance-2/os-volume_attachments"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/instance-2/os-volume_attachments"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -226,7 +226,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testGetAttachment() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/instance-1/os-volume_attachments/1"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/instance-1/os-volume_attachments/1"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -239,7 +239,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testGetAttachmentFail() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/instance-1/os-volume_attachments/1"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/instance-1/os-volume_attachments/1"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -251,7 +251,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testAttachVolume() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/instance-1/os-volume_attachments"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/instance-1/os-volume_attachments"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -266,7 +266,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { @Test(expectedExceptions = ResourceNotFoundException.class) public void testAttachVolumeFail() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/instance-1/os-volume_attachments"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/instance-1/os-volume_attachments"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -279,7 +279,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testDetachVolume() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/instance-1/os-volume_attachments/1"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/instance-1/os-volume_attachments/1"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -291,7 +291,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testDetachVolumeFail() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/servers/instance-1/os-volume_attachments/1"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/instance-1/os-volume_attachments/1"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -303,7 +303,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testListSnapshots() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-snapshots"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-snapshots"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -316,7 +316,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testListSnapshotsFail() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-snapshots"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-snapshots"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -329,7 +329,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testGetSnapshot() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-snapshots/1"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-snapshots/1"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -342,7 +342,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testGetSnapshotFail() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-snapshots/1"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-snapshots/1"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -354,7 +354,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testListSnapshotsInDetail() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-snapshots/detail"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-snapshots/detail"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -376,7 +376,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testListSnapshotsInDetailFail() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-snapshots/detail"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-snapshots/detail"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -389,7 +389,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testCreateSnapshot() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-snapshots"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-snapshots"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -406,7 +406,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { @Test(expectedExceptions = AuthorizationException.class) public void testCreateSnapshotFail() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-snapshots"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-snapshots"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -421,7 +421,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { } public void testDeleteSnapshot() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-snapshots/1"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-snapshots/1"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -434,7 +434,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { @Test(expectedExceptions = AuthorizationException.class) public void testDeleteSnapshotFail() { - URI endpoint = URI.create("https://compute.north.host/v1.1/3456/os-snapshots/1"); + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-snapshots/1"); VolumeClient client = requestsSendResponses( keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ExtensionClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ExtensionClientExpectTest.java index 578ec46109..afed2fa6d9 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ExtensionClientExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ExtensionClientExpectTest.java @@ -47,7 +47,7 @@ public class ExtensionClientExpectTest extends BaseNovaClientExpectTest { HttpRequest listExtensions = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/extensions")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/extensions")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -68,7 +68,7 @@ public class ExtensionClientExpectTest extends BaseNovaClientExpectTest { HttpRequest listExtensions = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/extensions")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/extensions")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -87,7 +87,7 @@ public class ExtensionClientExpectTest extends BaseNovaClientExpectTest { HttpRequest getExtension = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/extensions/RS-PIE")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/extensions/RS-PIE")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -106,7 +106,7 @@ public class ExtensionClientExpectTest extends BaseNovaClientExpectTest { HttpRequest getExtension = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/extensions/RS-PIE")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/extensions/RS-PIE")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/FlavorClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/FlavorClientExpectTest.java index a95aeda7e2..6ddb50c980 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/FlavorClientExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/FlavorClientExpectTest.java @@ -47,7 +47,7 @@ public class FlavorClientExpectTest extends BaseNovaClientExpectTest { HttpRequest listFlavors = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/flavors")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/flavors")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -68,7 +68,7 @@ public class FlavorClientExpectTest extends BaseNovaClientExpectTest { HttpRequest listFlavors = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/flavors")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/flavors")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -87,7 +87,7 @@ public class FlavorClientExpectTest extends BaseNovaClientExpectTest { HttpRequest getFlavor = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/flavors/52415800-8b69-11e0-9b19-734f1195ff37")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/flavors/52415800-8b69-11e0-9b19-734f1195ff37")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -107,7 +107,7 @@ public class FlavorClientExpectTest extends BaseNovaClientExpectTest { HttpRequest getFlavor = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/flavors/123")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/flavors/123")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ImageClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ImageClientExpectTest.java index cf5e2c5eaa..f5884f6253 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ImageClientExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ImageClientExpectTest.java @@ -46,7 +46,7 @@ public class ImageClientExpectTest extends BaseNovaClientExpectTest { HttpRequest listImages = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/images")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/images")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -67,7 +67,7 @@ public class ImageClientExpectTest extends BaseNovaClientExpectTest { HttpRequest listImages = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/images")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/images")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -85,7 +85,7 @@ public class ImageClientExpectTest extends BaseNovaClientExpectTest { HttpRequest getImage = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/images/52415800-8b69-11e0-9b19-734f5736d2a2")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/images/52415800-8b69-11e0-9b19-734f5736d2a2")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -105,7 +105,7 @@ public class ImageClientExpectTest extends BaseNovaClientExpectTest { HttpRequest getImage = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/images/52415800-8b69-11e0-9b19-734f5736d2a2")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/images/52415800-8b69-11e0-9b19-734f5736d2a2")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ServerClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ServerClientExpectTest.java index 35de130921..79a5b2aadc 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ServerClientExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ServerClientExpectTest.java @@ -48,7 +48,7 @@ public class ServerClientExpectTest extends BaseNovaClientExpectTest { HttpRequest listServers = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/servers")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -69,7 +69,7 @@ public class ServerClientExpectTest extends BaseNovaClientExpectTest { HttpRequest listServers = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/servers")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -86,7 +86,7 @@ public class ServerClientExpectTest extends BaseNovaClientExpectTest { HttpRequest createServer = HttpRequest .builder() .method("POST") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/servers")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()) @@ -110,7 +110,7 @@ public class ServerClientExpectTest extends BaseNovaClientExpectTest { HttpRequest createServer = HttpRequest .builder() .method("POST") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/servers")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()) @@ -139,7 +139,7 @@ public class ServerClientExpectTest extends BaseNovaClientExpectTest { HttpRequest createImage = HttpRequest .builder() .method("POST") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/servers/" + serverId + "/action")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/" + serverId + "/action")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()) @@ -151,7 +151,7 @@ public class ServerClientExpectTest extends BaseNovaClientExpectTest { .statusCode(200) .headers( ImmutableMultimap. builder() - .put("Location", "https://compute.north.host/v1.1/3456/images/" + imageId).build()).build(); + .put("Location", "https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/images/" + imageId).build()).build(); NovaClient clientWhenServerExists = requestsSendResponses(keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, createImage, createImageResponse); @@ -167,7 +167,7 @@ public class ServerClientExpectTest extends BaseNovaClientExpectTest { HttpRequest createImage = HttpRequest .builder() .method("POST") - .endpoint(URI.create("https://compute.north.host/v1.1/3456/servers/" + serverId + "/action")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/" + serverId + "/action")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken) diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/functions/CreateSecurityGroupIfNeededTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/functions/CreateSecurityGroupIfNeededTest.java index 3b15224a9d..7cc1c5a015 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/functions/CreateSecurityGroupIfNeededTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/functions/CreateSecurityGroupIfNeededTest.java @@ -45,7 +45,7 @@ import com.google.common.collect.ImmutableMap.Builder; @Test(groups = "unit", testName = "CreateSecurityGroupIfNeededTest") public class CreateSecurityGroupIfNeededTest extends BaseNovaClientExpectTest { HttpRequest createSecurityGroup = HttpRequest.builder().method("POST").endpoint( - URI.create("https://compute.north.host/v1.1/3456/os-security-groups")).headers( + URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-security-groups")).headers( ImmutableMultimap. builder().put("Accept", "application/json").put("X-Auth-Token", authToken).build()) .payload( @@ -74,7 +74,7 @@ public class CreateSecurityGroupIfNeededTest extends BaseNovaClientExpectTest { for (int port : ImmutableList.of(22,8080)){ HttpRequest createCidrRule = HttpRequest.builder().method("POST").endpoint( - URI.create("https://compute.north.host/v1.1/3456/os-security-group-rules")).headers( + URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-security-group-rules")).headers( ImmutableMultimap. builder().put("Accept", "application/json").put("X-Auth-Token", authToken).build()) .payload( @@ -91,7 +91,7 @@ public class CreateSecurityGroupIfNeededTest extends BaseNovaClientExpectTest { builder.put(createCidrRule, createCidrRuleResponse); HttpRequest createSelfRule = HttpRequest.builder().method("POST").endpoint( - URI.create("https://compute.north.host/v1.1/3456/os-security-group-rules")).headers( + URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-security-group-rules")).headers( ImmutableMultimap. builder().put("Accept", "application/json").put("X-Auth-Token", authToken).build()) .payload( @@ -110,7 +110,7 @@ public class CreateSecurityGroupIfNeededTest extends BaseNovaClientExpectTest { } HttpRequest getSecurityGroup = HttpRequest.builder().method("GET").endpoint( - URI.create("https://compute.north.host/v1.1/3456/os-security-groups/" + groupId)).headers( + URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-security-groups/" + groupId)).headers( ImmutableMultimap. builder().put("Accept", "application/json").put("X-Auth-Token", authToken).build()).build(); @@ -147,7 +147,7 @@ public class CreateSecurityGroupIfNeededTest extends BaseNovaClientExpectTest { builder.put(createSecurityGroup, createSecurityGroupResponse); HttpRequest listSecurityGroups = HttpRequest.builder().method("GET").endpoint( - URI.create("https://compute.north.host/v1.1/3456/os-security-groups")).headers( + URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-security-groups")).headers( ImmutableMultimap. builder().put("Accept", "application/json").put("X-Auth-Token", authToken).build()).build(); diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/functions/FindSecurityGroupWithNameAndReturnTrueExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/functions/FindSecurityGroupWithNameAndReturnTrueExpectTest.java index 0c58bfdf5f..04aae9925b 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/functions/FindSecurityGroupWithNameAndReturnTrueExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/functions/FindSecurityGroupWithNameAndReturnTrueExpectTest.java @@ -47,7 +47,7 @@ public class FindSecurityGroupWithNameAndReturnTrueExpectTest extends BaseNovaCl public void testUpdateReferenceWhenSecurityGroupListContainsGroupName() throws Exception { HttpRequest listSecurityGroups = HttpRequest.builder().method("GET").endpoint( - URI.create("https://compute.north.host/v1.1/3456/os-security-groups")).headers( + URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-security-groups")).headers( ImmutableMultimap. builder().put("Accept", "application/json").put("X-Auth-Token", authToken).build()).build(); @@ -75,7 +75,7 @@ public class FindSecurityGroupWithNameAndReturnTrueExpectTest extends BaseNovaCl public void testDoesNotUpdateReferenceWhenSecurityGroupListMissingGroupName() throws Exception { HttpRequest listSecurityGroups = HttpRequest.builder().method("GET").endpoint( - URI.create("https://compute.north.host/v1.1/3456/os-security-groups")).headers( + URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-security-groups")).headers( ImmutableMultimap. builder().put("Accept", "application/json").put("X-Auth-Token", authToken).build()).build(); diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaComputeServiceContextExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaComputeServiceContextExpectTest.java index 9105e3a6ed..befa3ae642 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaComputeServiceContextExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaComputeServiceContextExpectTest.java @@ -40,7 +40,7 @@ public abstract class BaseNovaComputeServiceContextExpectTest extends BaseNov Function { protected final HttpRequest listImagesDetail = HttpRequest.builder().method("GET").endpoint( - URI.create("https://compute.north.host/v1.1/3456/images/detail")).headers( + URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/images/detail")).headers( ImmutableMultimap. builder().put("Accept", "application/json").put("X-Auth-Token", authToken).build()).build(); @@ -48,7 +48,7 @@ public abstract class BaseNovaComputeServiceContextExpectTest extends BaseNov payloadFromResource("/image_list_detail.json")).build(); protected final HttpRequest listFlavorsDetail = HttpRequest.builder().method("GET").endpoint( - URI.create("https://compute.north.host/v1.1/3456/flavors/detail")).headers( + URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/flavors/detail")).headers( ImmutableMultimap. builder().put("Accept", "application/json").put("X-Auth-Token", authToken).build()).build(); @@ -56,7 +56,7 @@ public abstract class BaseNovaComputeServiceContextExpectTest extends BaseNov payloadFromResource("/flavor_list_detail.json")).build(); protected final HttpRequest listServers = HttpRequest.builder().method("GET").endpoint( - URI.create("https://compute.north.host/v1.1/3456/servers/detail")).headers( + URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/detail")).headers( ImmutableMultimap. builder().put("Accept", "application/json").put("X-Auth-Token", authToken).build()).build(); @@ -64,7 +64,7 @@ public abstract class BaseNovaComputeServiceContextExpectTest extends BaseNov payloadFromResource("/server_list_details.json")).build(); protected final HttpRequest listFloatingIps = HttpRequest.builder().method("GET").endpoint( - URI.create("https://compute.north.host/v1.1/3456/os-floating-ips")).headers( + URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-floating-ips")).headers( ImmutableMultimap. builder().put("Accept", "application/json").put("X-Auth-Token", authToken).build()).build(); diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaExpectTest.java index 8815279eb4..6212654523 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaExpectTest.java @@ -19,6 +19,7 @@ package org.jclouds.openstack.nova.v1_1.internal; import java.net.URI; +import java.util.Properties; import javax.ws.rs.core.MediaType; @@ -59,7 +60,7 @@ public class BaseNovaExpectTest extends BaseRestClientExpectTest { .builder() .method("GET") // NOTE THIS IS NOVA, NOT KEYSTONE - .endpoint(URI.create("https://compute.north.host/v1.1/3456/extensions")) + .endpoint(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/extensions")) .headers( ImmutableMultimap. builder().put("Accept", "application/json") .put("X-Auth-Token", authToken).build()).build(); @@ -71,6 +72,14 @@ public class BaseNovaExpectTest extends BaseRestClientExpectTest { .payload(payloadFromResource("/extension_list.json")).build(); } + @Override + protected Properties setupProperties() { + Properties overrides = super.setupProperties(); + // hpcloud or trystack + overrides.setProperty("jclouds.zones", "az-1.region-a.geo-1,RegionOne"); + return overrides; + } + protected HttpRequest.Builder standardRequestBuilder(URI endpoint) { return HttpRequest.builder().method("GET") .headers(ImmutableMultimap.of("Accept", MediaType.APPLICATION_JSON, "X-Auth-Token", authToken)) diff --git a/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseAccessTest.java b/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseAccessTest.java index 146a376533..2a95cb6947 100644 --- a/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseAccessTest.java +++ b/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/parse/ParseAccessTest.java @@ -80,14 +80,23 @@ public class ParseAccessTest extends BaseItemParserTest { URI.create("https://glance.jclouds.org:9292/v1.0")).region("az-1.region-a.geo-1") .versionId("1.0").build()).build(), - Service.builder().name("Cloud Servers").type("compute").endpoints( - Endpoint.builder().tenantId("1").publicURL(URI.create("https://compute.north.host/v1/1234")) - .internalURL(URI.create("https://compute.north.host/v1/1234")).region("az-1.region-a.geo-1") - .versionId("1.0").build(), - Endpoint.builder().tenantId("2").publicURL(URI.create("https://compute.north.host/v1.1/3456")) - .internalURL(URI.create("https://compute.north.host/v1.1/3456")).region("az-1.region-a.geo-1") - .versionId("1.1").build()).build()).build(); - + Service.builder().name("Compute").type("compute").endpoints( + Endpoint.builder() + .tenantId("3456") + .publicURL(URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456")) + .region("az-1.region-a.geo-1") + .versionId("1.1").build(), + Endpoint.builder() + .tenantId("3456") + .publicURL(URI.create("https://az-2.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456")) + .region("az-2.region-a.geo-1") + .versionId("1.1").build(), + Endpoint.builder() + .tenantId("3456") + .publicURL(URI.create("https://az-3.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456")) + .region("az-3.region-a.geo-1") + .versionId("1.1").build()).build() + ).build(); } } diff --git a/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/RegionIdToAdminURIFromAccessForTypeAndVersionSupplierTest.java b/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/RegionIdToAdminURIFromAccessForTypeAndVersionSupplierTest.java index de4453dccc..fffde689c6 100644 --- a/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/RegionIdToAdminURIFromAccessForTypeAndVersionSupplierTest.java +++ b/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/RegionIdToAdminURIFromAccessForTypeAndVersionSupplierTest.java @@ -64,6 +64,8 @@ public class RegionIdToAdminURIFromAccessForTypeAndVersionSupplierTest { . supplierFunction()), ImmutableMap.of("region-a.geo-1", URI.create("https://csnode.jclouds.org:35357/v2.0/"))); Map map = Maps.newLinkedHashMap(); map.put("region-a.geo-1", null); + map.put("region-b.geo-1", null); + map.put("region-c.geo-1", null); assertEquals(Maps.transformValues(factory.createForApiTypeAndVersion("compute", "1.1").get(), Suppliers . supplierFunction()), map); } diff --git a/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/RegionIdToURIFromAccessForTypeAndVersionSupplierTest.java b/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/RegionIdToURIFromAccessForTypeAndVersionSupplierTest.java index 6965e710fb..ef993f1211 100644 --- a/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/RegionIdToURIFromAccessForTypeAndVersionSupplierTest.java +++ b/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/RegionIdToURIFromAccessForTypeAndVersionSupplierTest.java @@ -61,9 +61,11 @@ public class RegionIdToURIFromAccessForTypeAndVersionSupplierTest { public void testRegionMatches() { assertEquals(Maps.transformValues(factory.createForApiTypeAndVersion("compute", "1.0").get(), Suppliers - . supplierFunction()), ImmutableMap.of("az-1.region-a.geo-1", URI.create("https://compute.north.host/v1/1234"))); + . supplierFunction()), ImmutableMap.of()); assertEquals(Maps.transformValues(factory.createForApiTypeAndVersion("compute", "1.1").get(), Suppliers - . supplierFunction()), ImmutableMap.of("az-1.region-a.geo-1", URI.create("https://compute.north.host/v1.1/3456"))); + . supplierFunction()), ImmutableMap.of("az-1.region-a.geo-1", URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456"), + "az-2.region-a.geo-1", URI.create("https://az-2.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456"), + "az-3.region-a.geo-1", URI.create("https://az-3.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456"))); } } diff --git a/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/ZoneIdToURIFromAccessForTypeAndVersionSupplierTest.java b/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/ZoneIdToURIFromAccessForTypeAndVersionSupplierTest.java index 037b71104a..b8c72edd78 100644 --- a/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/ZoneIdToURIFromAccessForTypeAndVersionSupplierTest.java +++ b/common/openstack/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/ZoneIdToURIFromAccessForTypeAndVersionSupplierTest.java @@ -61,11 +61,11 @@ public class ZoneIdToURIFromAccessForTypeAndVersionSupplierTest { public void testZoneMatches() { assertEquals(Maps.transformValues(factory.createForApiTypeAndVersion("compute", "1.0").get(), Suppliers - . supplierFunction()), ImmutableMap.of("az-1.region-a.geo-1", URI - .create("https://compute.north.host/v1/1234"))); + . supplierFunction()), ImmutableMap.of()); assertEquals(Maps.transformValues(factory.createForApiTypeAndVersion("compute", "1.1").get(), Suppliers - . supplierFunction()), ImmutableMap.of("az-1.region-a.geo-1", URI - .create("https://compute.north.host/v1.1/3456"))); + . supplierFunction()), ImmutableMap.of("az-1.region-a.geo-1", URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456"), + "az-2.region-a.geo-1", URI.create("https://az-2.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456"), + "az-3.region-a.geo-1", URI.create("https://az-3.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456"))); } } diff --git a/common/openstack/src/test/resources/keystoneAuthResponse.json b/common/openstack/src/test/resources/keystoneAuthResponse.json index 4751ca3d2b..a781a32a89 100644 --- a/common/openstack/src/test/resources/keystoneAuthResponse.json +++ b/common/openstack/src/test/resources/keystoneAuthResponse.json @@ -77,30 +77,38 @@ } ] }, - { - "name":"Cloud Servers", - "type":"compute", - "endpoints":[{ - "tenantId":"1", - "publicURL":"https://compute.north.host/v1/1234", - "internalURL":"https://compute.north.host/v1/1234", - "region":"az-1.region-a.geo-1", - "versionId":"1.0", - "versionInfo":"https://compute.north.host/v1.0/", - "versionList":"https://compute.north.host/" - }, - { - "tenantId":"2", - "publicURL":"https://compute.north.host/v1.1/3456", - "internalURL":"https://compute.north.host/v1.1/3456", - "region":"az-1.region-a.geo-1", - "versionId":"1.1", - "versionInfo":"https://compute.north.host/v1.1/", - "versionList":"https://compute.north.host/" - } - ], - "endpoints_links":[] - } - ] + { + "name": "Compute", + "type": "compute", + "endpoints": [ + { + "tenantId": "3456", + "publicURL": "https:\/\/az-1.region-a.geo-1.compute.hpcloudsvc.com\/v1.1\/3456", + "publicURL2": "https:\/\/az-1.region-a.geo-1.ec2-compute.hpcloudsvc.com\/services\/Cloud", + "region": "az-1.region-a.geo-1", + "versionId": "1.1", + "versionInfo": "https:\/\/az-1.region-a.geo-1.compute.hpcloudsvc.com\/v1.1\/", + "versionList": "https:\/\/az-1.region-a.geo-1.compute.hpcloudsvc.com" + }, + { + "tenantId": "3456", + "publicURL": "https:\/\/az-2.region-a.geo-1.compute.hpcloudsvc.com\/v1.1\/3456", + "publicURL2": "https:\/\/az-2.region-a.geo-1.ec2-compute.hpcloudsvc.com\/services\/Cloud", + "region": "az-2.region-a.geo-1", + "versionId": "1.1", + "versionInfo": "https:\/\/az-2.region-a.geo-1.compute.hpcloudsvc.com\/v1.1\/", + "versionList": "https:\/\/az-2.region-a.geo-1.compute.hpcloudsvc.com" + }, + { + "tenantId": "3456", + "publicURL": "https:\/\/az-3.region-a.geo-1.compute.hpcloudsvc.com\/v1.1\/3456", + "publicURL2": "https:\/\/az-3.region-a.geo-1.ec2-compute.hpcloudsvc.com\/services\/Cloud", + "region": "az-3.region-a.geo-1", + "versionId": "1.1", + "versionInfo": "https:\/\/az-3.region-a.geo-1.compute.hpcloudsvc.com\/v1.1\/", + "versionList": "https:\/\/az-3.region-a.geo-1.compute.hpcloudsvc.com" + } + ] } + ] } \ No newline at end of file From b820b326b4502eb55bc55b27e1723f13106c3910 Mon Sep 17 00:00:00 2001 From: Jeremy Whitlock Date: Wed, 9 May 2012 19:26:45 -0600 Subject: [PATCH 061/148] Issue 922: Create CloudWatch.listMetrics(MetricClient, ListMetricsOptions) API. --- .../org/jclouds/cloudwatch/CloudWatch.java | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/apis/cloudwatch/src/main/java/org/jclouds/cloudwatch/CloudWatch.java b/apis/cloudwatch/src/main/java/org/jclouds/cloudwatch/CloudWatch.java index e544a8a411..32f1bcec1f 100644 --- a/apis/cloudwatch/src/main/java/org/jclouds/cloudwatch/CloudWatch.java +++ b/apis/cloudwatch/src/main/java/org/jclouds/cloudwatch/CloudWatch.java @@ -33,24 +33,13 @@ import java.util.Iterator; */ public class CloudWatch { - /** - * List metrics based on the criteria in the {@link ListMetricsOptions} passed in. - * - * @param cloudWatchClient the CloudWatch client - * @param region the region to list metrics in - * @param options the options describing the ListMetrics request - * - * @return iterable of metrics fitting the criteria - */ - public static Iterable listMetrics(CloudWatchClient cloudWatchClient, String region, - final ListMetricsOptions options) { - final MetricClient metricClientForRegion = cloudWatchClient.getMetricClientForRegion(region); + public static Iterable listMetrics(final MetricClient metricClient, final ListMetricsOptions options) { return new Iterable() { public Iterator iterator() { return new AbstractIterator() { private ListMetricsOptions lastOptions = options; - private ListMetricsResponse response = metricClientForRegion.listMetrics(lastOptions); + private ListMetricsResponse response = metricClient.listMetrics(lastOptions); private Iterator iterator = response.iterator(); /** @@ -66,7 +55,7 @@ public class CloudWatch { .namespace(lastOptions.getNamespace()) .nextToken(response.getNextToken()) .build(); - response = metricClientForRegion.listMetrics(lastOptions); + response = metricClient.listMetrics(lastOptions); iterator = response.iterator(); } if (iterator.hasNext()) { @@ -84,4 +73,18 @@ public class CloudWatch { }; } + /** + * List metrics based on the criteria in the {@link ListMetricsOptions} passed in. + * + * @param cloudWatchClient the CloudWatch client + * @param region the region to list metrics in + * @param options the options describing the ListMetrics request + * + * @return iterable of metrics fitting the criteria + */ + public static Iterable listMetrics(CloudWatchClient cloudWatchClient, String region, + final ListMetricsOptions options) { + return listMetrics(cloudWatchClient.getMetricClientForRegion(region), options); + } + } From 934b8388e66e5c72454f95b0068bed99dc3c18b7 Mon Sep 17 00:00:00 2001 From: Jeremy Whitlock Date: Wed, 9 May 2012 20:43:33 -0600 Subject: [PATCH 062/148] Issue 922: Add missing javadoc to CloudWatch.listMetrics(MetricClient, ListMetricsOption). * Follow-up to b820b326b4502eb55bc55b27e1723f13106c3910 --- .../main/java/org/jclouds/cloudwatch/CloudWatch.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/apis/cloudwatch/src/main/java/org/jclouds/cloudwatch/CloudWatch.java b/apis/cloudwatch/src/main/java/org/jclouds/cloudwatch/CloudWatch.java index 32f1bcec1f..f5f2a01830 100644 --- a/apis/cloudwatch/src/main/java/org/jclouds/cloudwatch/CloudWatch.java +++ b/apis/cloudwatch/src/main/java/org/jclouds/cloudwatch/CloudWatch.java @@ -33,6 +33,14 @@ import java.util.Iterator; */ public class CloudWatch { + /** + * List metrics based on the criteria in the {@link ListMetricsOptions} passed in. + * + * @param metricClient the {@link MetricClient} to use for the request + * @param options the {@link ListMetricsOptions} describing the ListMetrics request + * + * @return iterable of metrics fitting the criteria + */ public static Iterable listMetrics(final MetricClient metricClient, final ListMetricsOptions options) { return new Iterable() { public Iterator iterator() { @@ -76,7 +84,7 @@ public class CloudWatch { /** * List metrics based on the criteria in the {@link ListMetricsOptions} passed in. * - * @param cloudWatchClient the CloudWatch client + * @param cloudWatchClient the {@link CloudWatchClient} to use for the request * @param region the region to list metrics in * @param options the options describing the ListMetrics request * From c7469bbf4a71649acdcc3932492afb327651c216 Mon Sep 17 00:00:00 2001 From: David Ribeiro Alves Date: Thu, 10 May 2012 06:54:13 +0100 Subject: [PATCH 063/148] cloudservers and ec2 imageextension expect tests on the way --- .../compute/CloudServersImageExtension.java | 73 ++++------------- ...oudServersComputeServiceContextModule.java | 5 ++ ...geWhenStatusActivePredicateWithResult.java | 75 +++++++++++++++++ ...usActivePredicateWithResultExpectTest.java | 82 +++++++++++++++++++ ...est_list_images_detail_imageextension.json | 60 ++++++++++++++ .../ec2/compute/EC2ImageExtension.java | 62 +++----------- ...henStatusAvailablePredicateWithResult.java | 68 +++++++++++++++ .../nova/v1_1/compute/NovaImageExtension.java | 13 --- ...oneHasActiveStatusPredicateWithResult.java | 17 +++- 9 files changed, 330 insertions(+), 125 deletions(-) create mode 100644 apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResult.java create mode 100644 apis/cloudservers/src/test/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResultExpectTest.java create mode 100644 apis/cloudservers/src/test/resources/test_list_images_detail_imageextension.json create mode 100644 apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/GetImageWhenStatusAvailablePredicateWithResult.java diff --git a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/CloudServersImageExtension.java b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/CloudServersImageExtension.java index 2914789c53..0cffdb1d71 100644 --- a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/CloudServersImageExtension.java +++ b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/CloudServersImageExtension.java @@ -35,7 +35,6 @@ import javax.inject.Singleton; import org.jclouds.Constants; import org.jclouds.cloudservers.CloudServersClient; import org.jclouds.cloudservers.domain.Server; -import org.jclouds.cloudservers.options.ListOptions; import org.jclouds.compute.ImageExtension; import org.jclouds.compute.domain.CloneImageTemplate; import org.jclouds.compute.domain.Image; @@ -47,9 +46,6 @@ import org.jclouds.logging.Logger; import org.jclouds.predicates.PredicateWithResult; import org.jclouds.predicates.Retryables; -import com.google.common.base.Function; -import com.google.common.base.Predicate; -import com.google.common.collect.Iterables; import com.google.common.util.concurrent.ListenableFuture; /** @@ -65,28 +61,28 @@ public class CloudServersImageExtension implements ImageExtension { @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; - private final CloudServersClient syncClient; + private final CloudServersClient client; private final ExecutorService executor; - private final Function cloudserversImageToImage; + private final PredicateWithResult imageAvailablePredicate; @com.google.inject.Inject(optional = true) @Named("IMAGE_MAX_WAIT") - long maxWait = 3600; + private long maxWait = 3600; @com.google.inject.Inject(optional = true) @Named("IMAGE_WAIT_PERIOD") - long waitPeriod = 1; + private long waitPeriod = 1; @Inject - public CloudServersImageExtension(CloudServersClient novaClient, + public CloudServersImageExtension(CloudServersClient client, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads, - Function cloudserversImageToImage) { - this.syncClient = checkNotNull(novaClient); + PredicateWithResult imageAvailablePredicate) { + this.client = checkNotNull(client); this.executor = userThreads; - this.cloudserversImageToImage = cloudserversImageToImage; + this.imageAvailablePredicate = imageAvailablePredicate; } @Override public ImageTemplate buildImageTemplateFromNode(String name, final String id) { - Server server = syncClient.getServer(Integer.parseInt(id)); + Server server = client.getServer(Integer.parseInt(id)); if (server == null) throw new NoSuchElementException("Cannot find server with id: " + id); CloneImageTemplate template = new ImageTemplateBuilder.CloneImageTemplateBuilder().nodeId(id).name(name).build(); @@ -98,44 +94,14 @@ public class CloudServersImageExtension implements ImageExtension { checkState(template instanceof CloneImageTemplate, " openstack-nova only supports creating images through cloning."); CloneImageTemplate cloneTemplate = (CloneImageTemplate) template; - final org.jclouds.cloudservers.domain.Image image = syncClient.createImageFromServer(cloneTemplate.getName(), + final org.jclouds.cloudservers.domain.Image image = client.createImageFromServer(cloneTemplate.getName(), Integer.parseInt(cloneTemplate.getSourceNodeId())); return Futures.makeListenable(executor.submit(new Callable() { @Override public Image call() throws Exception { - return Retryables.retryGettingResultOrFailing(new PredicateWithResult() { - - org.jclouds.cloudservers.domain.Image result; - RuntimeException lastFailure; - - @Override - public boolean apply(Integer input) { - result = checkNotNull(findImage(input)); - switch (result.getStatus()) { - case ACTIVE: - logger.info("<< Image %s is available for use.", input); - return true; - case UNKNOWN: - case SAVING: - logger.debug("<< Image %s is not available yet.", input); - return false; - default: - lastFailure = new IllegalStateException("Image was not created: " + input); - throw lastFailure; - } - } - - @Override - public Image getResult() { - return cloudserversImageToImage.apply(image); - } - - @Override - public Throwable getLastFailure() { - return lastFailure; - } - }, image.getId(), maxWait, waitPeriod, TimeUnit.SECONDS, - "Image was not created within the time limit, Giving up! [Limit: " + maxWait + " secs.]"); + return Retryables.retryGettingResultOrFailing(imageAvailablePredicate, image.getId(), maxWait, waitPeriod, + TimeUnit.SECONDS, "Image was not created within the time limit, Giving up! [Limit: " + maxWait + + " secs.]"); } }), executor); @@ -144,22 +110,11 @@ public class CloudServersImageExtension implements ImageExtension { @Override public boolean deleteImage(String id) { try { - this.syncClient.deleteImage(Integer.parseInt(id)); + this.client.deleteImage(Integer.parseInt(id)); } catch (Exception e) { return false; } return true; } - private org.jclouds.cloudservers.domain.Image findImage(final int id) { - return Iterables.tryFind(syncClient.listImages(ListOptions.NONE), - new Predicate() { - @Override - public boolean apply(org.jclouds.cloudservers.domain.Image input) { - return input.getId() == id; - } - }).orNull(); - - } - } diff --git a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/config/CloudServersComputeServiceContextModule.java b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/config/CloudServersComputeServiceContextModule.java index 6375c15277..d40bbbe5ac 100644 --- a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/config/CloudServersComputeServiceContextModule.java +++ b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/config/CloudServersComputeServiceContextModule.java @@ -27,6 +27,7 @@ import org.jclouds.cloudservers.compute.functions.CloudServersImageToImage; import org.jclouds.cloudservers.compute.functions.CloudServersImageToOperatingSystem; import org.jclouds.cloudservers.compute.functions.FlavorToHardware; import org.jclouds.cloudservers.compute.functions.ServerToNodeMetadata; +import org.jclouds.cloudservers.compute.predicates.GetImageWhenStatusActivePredicateWithResult; import org.jclouds.cloudservers.compute.strategy.CloudServersComputeServiceAdapter; import org.jclouds.cloudservers.domain.Flavor; import org.jclouds.cloudservers.domain.Server; @@ -42,6 +43,7 @@ import org.jclouds.compute.domain.OperatingSystem; import org.jclouds.compute.internal.BaseComputeService; import org.jclouds.domain.Location; import org.jclouds.functions.IdentityFunction; +import org.jclouds.predicates.PredicateWithResult; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; @@ -84,6 +86,9 @@ public class CloudServersComputeServiceContextModule extends bind(new TypeLiteral() { }).to(CloudServersImageExtension.class); + + bind(new TypeLiteral>() { + }).to(GetImageWhenStatusActivePredicateWithResult.class); } diff --git a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResult.java b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResult.java new file mode 100644 index 0000000000..e4d1122f77 --- /dev/null +++ b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResult.java @@ -0,0 +1,75 @@ +package org.jclouds.cloudservers.compute.predicates; + +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; + +import org.jclouds.cloudservers.CloudServersClient; +import org.jclouds.cloudservers.options.ListOptions; +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.logging.Logger; +import org.jclouds.predicates.PredicateWithResult; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; + +public final class GetImageWhenStatusActivePredicateWithResult implements PredicateWithResult { + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + private final CloudServersClient client; + private final Function cloudserversImageToImage; + private org.jclouds.cloudservers.domain.Image result; + private RuntimeException lastFailure; + + @Inject + public GetImageWhenStatusActivePredicateWithResult(CloudServersClient client, + Function cloudserversImageToImage) { + this.client = client; + this.cloudserversImageToImage = cloudserversImageToImage; + } + + @Override + public boolean apply(Integer input) { + result = checkNotNull(findImage(input)); + switch (result.getStatus()) { + case ACTIVE: + logger.info("<< Image %s is available for use.", input); + return true; + case QUEUED: + case SAVING: + logger.debug("<< Image %s is not available yet.", input); + return false; + default: + lastFailure = new IllegalStateException("Image was not created: " + input); + throw lastFailure; + } + } + + @Override + public Image getResult() { + return cloudserversImageToImage.apply(result); + } + + @Override + public Throwable getLastFailure() { + return lastFailure; + } + + private org.jclouds.cloudservers.domain.Image findImage(final int id) { + return Iterables.tryFind(client.listImages(new ListOptions().withDetails()), + new Predicate() { + @Override + public boolean apply(org.jclouds.cloudservers.domain.Image input) { + return input.getId() == id; + } + }).orNull(); + + } +} \ No newline at end of file diff --git a/apis/cloudservers/src/test/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResultExpectTest.java b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResultExpectTest.java new file mode 100644 index 0000000000..7ae0c636c3 --- /dev/null +++ b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResultExpectTest.java @@ -0,0 +1,82 @@ +package org.jclouds.cloudservers.compute.predicates; + +import static junit.framework.Assert.assertTrue; +import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS; + +import java.net.URI; +import java.util.Map; +import java.util.Properties; + +import org.jclouds.apis.ApiMetadata; +import org.jclouds.cloudservers.CloudServersApiMetadata; +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.domain.Image; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.openstack.keystone.v1_1.internal.BaseKeystoneRestClientExpectTest; +import org.jclouds.predicates.PredicateWithResult; +import org.testng.annotations.Test; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.net.HttpHeaders; +import com.google.inject.Injector; +import com.google.inject.Module; + +@Test(groups = "unit", testName = "GetImageWhenStatusActivePredicateWithResultExpectTest") +public class GetImageWhenStatusActivePredicateWithResultExpectTest extends BaseKeystoneRestClientExpectTest + implements Function { + + private final HttpRequest listImagesDetail = HttpRequest + .builder() + .method("GET") + .endpoint(URI.create("https://lon.servers.api.rackspacecloud.com/v1.0/10001786/images/detail?format=json")) + .headers(ImmutableMultimap. builder().put("X-Auth-Token", authToken) + .put(HttpHeaders.ACCEPT, "application/json").build()).build(); + + private final HttpResponse listImagesResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/test_list_images_detail_imageextension.json")).build(); + + Map requestResponseMap = ImmutableMap. builder() + .put(initialAuth, responseWithAuth).put(listImagesDetail, listImagesResponse).build(); + + public GetImageWhenStatusActivePredicateWithResultExpectTest() { + provider = "cloudservers"; + } + + @Override + protected ApiMetadata createApiMetadata() { + return new CloudServersApiMetadata(); + } + + @Override + protected Properties setupProperties() { + Properties overrides = new Properties(); + overrides.setProperty(PROPERTY_REGIONS, "US"); + overrides.setProperty(provider + ".endpoint", endpoint); + return overrides; + } + + @Override + public Injector createClient(Function fn, Module module, Properties props) { + return apply(createComputeServiceContext(fn, module, props)); + } + + private ComputeServiceContext createComputeServiceContext(Function fn, Module module, + Properties props) { + return createInjector(fn, module, props).getInstance(ComputeServiceContext.class); + } + + @Override + public Injector apply(ComputeServiceContext input) { + return input.utils().injector(); + } + + public void testReturnsFalseOnQueuedAndSavingAndTrueOnActive() { + Injector injector = requestsSendResponses(requestResponseMap); + PredicateWithResult predicate = injector + .getInstance(GetImageWhenStatusActivePredicateWithResult.class); + assertTrue(predicate.apply(2)); + } +} diff --git a/apis/cloudservers/src/test/resources/test_list_images_detail_imageextension.json b/apis/cloudservers/src/test/resources/test_list_images_detail_imageextension.json new file mode 100644 index 0000000000..af18ac6233 --- /dev/null +++ b/apis/cloudservers/src/test/resources/test_list_images_detail_imageextension.json @@ -0,0 +1,60 @@ +{ + "images" : [ + { + "id" : 2, + "name" : "CentOS 5.2", + "updated" : "2010-10-10T12:00:00Z", + "created" : "2010-08-10T12:00:00Z", + "status" : "ACTIVE" + }, + { + "id" : 743, + "name" : "My Server Backup1", + "serverId" : 12, + "updated" : "2010-10-10T12:00:00Z", + "created" : "2009-07-07T09:56:16-05:00", + "status" : "SAVING", + "progress" : 80 + } + { + "id" : 744, + "name" : "My Server Backup2", + "serverId" : 12, + "updated" : "2010-10-10T12:00:00Z", + "created" : "2009-07-07T09:56:16-05:00", + "status" : "UNRECOGNIZED", + } + { + "id" : 745, + "name" : "My Server Backup3", + "serverId" : 12, + "updated" : "2010-10-10T12:00:00Z", + "created" : "2009-07-07T09:56:16-05:00", + "status" : "UNKNOWN", + } + { + "id" : 746, + "name" : "My Server Backup4", + "serverId" : 12, + "updated" : "2010-10-10T12:00:00Z", + "created" : "2009-07-07T09:56:16-05:00", + "status" : "PREPARING", + } + { + "id" : 747, + "name" : "My Server Backup5", + "serverId" : 12, + "updated" : "2010-10-10T12:00:00Z", + "created" : "2009-07-07T09:56:16-05:00", + "status" : "QUEUED", + } + { + "id" : 748, + "name" : "My Server Backup6", + "serverId" : 12, + "updated" : "2010-10-10T12:00:00Z", + "created" : "2009-07-07T09:56:16-05:00", + "status" : "FAILED", + } + ] +} diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ImageExtension.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ImageExtension.java index 2dc958b934..276fb230a4 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ImageExtension.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ImageExtension.java @@ -41,16 +41,13 @@ import org.jclouds.compute.domain.ImageTemplateBuilder; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.concurrent.Futures; import org.jclouds.ec2.EC2Client; -import org.jclouds.ec2.compute.functions.EC2ImageParser; import org.jclouds.ec2.domain.Reservation; import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.ec2.options.CreateImageOptions; -import org.jclouds.ec2.options.DescribeImagesOptions; import org.jclouds.logging.Logger; import org.jclouds.predicates.PredicateWithResult; import org.jclouds.predicates.Retryables; -import com.google.common.base.Function; import com.google.common.collect.Iterables; import com.google.common.util.concurrent.ListenableFuture; @@ -66,23 +63,22 @@ public class EC2ImageExtension implements ImageExtension { @Resource @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; - - @com.google.inject.Inject(optional = true) - @Named("IMAGE_MAX_WAIT") - long maxWait = 3600; - @com.google.inject.Inject(optional = true) - @Named("IMAGE_WAIT_PERIOD") - long waitPeriod = 1; private final EC2Client ec2Client; private final ExecutorService executor; - private final Function ecImageToImage; + private final PredicateWithResult imageReadyPredicate; + @com.google.inject.Inject(optional = true) + @Named("IMAGE_MAX_WAIT") + private long maxWait = 3600; + @com.google.inject.Inject(optional = true) + @Named("IMAGE_WAIT_PERIOD") + private long waitPeriod = 1; @Inject public EC2ImageExtension(EC2Client ec2Client, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads, - EC2ImageParser ec2ImageToImage) { + PredicateWithResult imageReadyPredicate) { this.ec2Client = checkNotNull(ec2Client); this.executor = checkNotNull(userThreads); - this.ecImageToImage = checkNotNull(ec2ImageToImage); + this.imageReadyPredicate = imageReadyPredicate; } @Override @@ -112,38 +108,9 @@ public class EC2ImageExtension implements ImageExtension { return Futures.makeListenable(executor.submit(new Callable() { @Override public Image call() throws Exception { - return Retryables.retryGettingResultOrFailing(new PredicateWithResult() { - - org.jclouds.ec2.domain.Image result; - RuntimeException lastFailure; - - @Override - public boolean apply(String input) { - result = checkNotNull(findImage(region, input)); - switch (result.getImageState()) { - case AVAILABLE: - logger.info("<< Image %s is available for use.", input); - return true; - case UNRECOGNIZED: - logger.debug("<< Image %s is not available yet.", input); - return false; - default: - lastFailure = new IllegalStateException("Image was not created: " + input); - throw lastFailure; - } - } - - @Override - public Image getResult() { - return ecImageToImage.apply(result); - } - - @Override - public Throwable getLastFailure() { - return lastFailure; - } - }, imageId, maxWait, waitPeriod, TimeUnit.SECONDS, - "Image was not created within the time limit, Giving up! [Limit: " + maxWait + " secs.]"); + return Retryables.retryGettingResultOrFailing(imageReadyPredicate, imageId, maxWait, waitPeriod, + TimeUnit.SECONDS, "Image was not created within the time limit, Giving up! [Limit: " + maxWait + + " secs.]"); } }), executor); } @@ -161,9 +128,4 @@ public class EC2ImageExtension implements ImageExtension { } } - private org.jclouds.ec2.domain.Image findImage(String region, String id) { - return Iterables.getOnlyElement(ec2Client.getAMIServices().describeImagesInRegion(region, - new DescribeImagesOptions().imageIds(id))); - - } } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/GetImageWhenStatusAvailablePredicateWithResult.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/GetImageWhenStatusAvailablePredicateWithResult.java new file mode 100644 index 0000000000..8db7a7067c --- /dev/null +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/GetImageWhenStatusAvailablePredicateWithResult.java @@ -0,0 +1,68 @@ +package org.jclouds.ec2.compute.predicates; + +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.annotation.Resource; +import javax.inject.Named; + +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.ec2.EC2Client; +import org.jclouds.ec2.compute.functions.EC2ImageParser; +import org.jclouds.ec2.options.DescribeImagesOptions; +import org.jclouds.logging.Logger; +import org.jclouds.predicates.PredicateWithResult; + +import com.google.common.collect.Iterables; + +public final class GetImageWhenStatusAvailablePredicateWithResult implements PredicateWithResult { + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + private final String region; + private final EC2Client ec2Client; + private final EC2ImageParser ec2ImageToImage; + private org.jclouds.ec2.domain.Image result; + private RuntimeException lastFailure; + + public GetImageWhenStatusAvailablePredicateWithResult(EC2Client ec2Client, EC2ImageParser ec2ImageToImage, + String region) { + this.region = region; + this.ec2Client = ec2Client; + this.ec2ImageToImage = ec2ImageToImage; + } + + @Override + public boolean apply(String input) { + result = checkNotNull(findImage(input)); + switch (result.getImageState()) { + case AVAILABLE: + logger.info("<< Image %s is available for use.", input); + return true; + case UNRECOGNIZED: + logger.debug("<< Image %s is not available yet.", input); + return false; + default: + lastFailure = new IllegalStateException("Image was not created: " + input); + throw lastFailure; + } + } + + @Override + public Image getResult() { + return ec2ImageToImage.apply(result); + } + + @Override + public Throwable getLastFailure() { + return lastFailure; + } + + private org.jclouds.ec2.domain.Image findImage(String id) { + return Iterables.getOnlyElement(ec2Client.getAMIServices().describeImagesInRegion(region, + new DescribeImagesOptions().imageIds(id))); + + } +} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaImageExtension.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaImageExtension.java index d9c6dfe28f..900a2d4547 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaImageExtension.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaImageExtension.java @@ -47,8 +47,6 @@ import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndId; import org.jclouds.predicates.PredicateWithResult; import org.jclouds.predicates.Retryables; -import com.google.common.base.Predicate; -import com.google.common.collect.Iterables; import com.google.common.util.concurrent.ListenableFuture; @Singleton @@ -122,15 +120,4 @@ public class NovaImageExtension implements ImageExtension { return true; } - public static org.jclouds.openstack.nova.v1_1.domain.Image findImage(NovaClient novaClient, final ZoneAndId zoneAndId) { - return Iterables.tryFind(novaClient.getImageClientForZone(zoneAndId.getZone()).listImagesInDetail(), - new Predicate() { - @Override - public boolean apply(org.jclouds.openstack.nova.v1_1.domain.Image input) { - return input.getId().equals(zoneAndId.getId()); - } - }).orNull(); - - } - } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResult.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResult.java index 2ca35ad10d..fb8f73e83e 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResult.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResult.java @@ -29,12 +29,13 @@ import org.jclouds.compute.domain.Image; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.logging.Logger; import org.jclouds.openstack.nova.v1_1.NovaClient; -import org.jclouds.openstack.nova.v1_1.compute.NovaImageExtension; import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ImageInZone; import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndId; import org.jclouds.predicates.PredicateWithResult; import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; /** * @author David Alves @@ -61,8 +62,7 @@ public final class GetImageWhenImageInZoneHasActiveStatusPredicateWithResult imp @Override public boolean apply(ZoneAndId input) { - result = checkNotNull(NovaImageExtension.findImage(client, - ZoneAndId.fromZoneAndId(input.getZone(), input.getId()))); + result = checkNotNull(findImage(ZoneAndId.fromZoneAndId(input.getZone(), input.getId()))); resultZoneAndId = input; switch (result.getStatus()) { case ACTIVE: @@ -86,4 +86,15 @@ public final class GetImageWhenImageInZoneHasActiveStatusPredicateWithResult imp public Throwable getLastFailure() { return lastFailure; } + + public org.jclouds.openstack.nova.v1_1.domain.Image findImage(final ZoneAndId zoneAndId) { + return Iterables.tryFind(client.getImageClientForZone(zoneAndId.getZone()).listImagesInDetail(), + new Predicate() { + @Override + public boolean apply(org.jclouds.openstack.nova.v1_1.domain.Image input) { + return input.getId().equals(zoneAndId.getId()); + } + }).orNull(); + + } } \ No newline at end of file From fc159d1686328984f6fd420e1659af9ab855e584 Mon Sep 17 00:00:00 2001 From: David Ribeiro Alves Date: Thu, 10 May 2012 07:33:53 +0100 Subject: [PATCH 064/148] cloudservers imageextension expect tests are passing --- ...usActivePredicateWithResultExpectTest.java | 96 +++++++++------- ...eCloudServersComputeServiceExpectTest.java | 103 ++++++++++++++++++ ...est_list_images_detail_imageextension.json | 65 ++++++----- 3 files changed, 193 insertions(+), 71 deletions(-) create mode 100644 apis/cloudservers/src/test/java/org/jclouds/cloudservers/internal/BaseCloudServersComputeServiceExpectTest.java diff --git a/apis/cloudservers/src/test/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResultExpectTest.java b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResultExpectTest.java index 7ae0c636c3..a55022804d 100644 --- a/apis/cloudservers/src/test/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResultExpectTest.java +++ b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResultExpectTest.java @@ -1,71 +1,91 @@ +/** + * 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.cloudservers.compute.predicates; +import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; -import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS; import java.net.URI; import java.util.Map; -import java.util.Properties; -import org.jclouds.apis.ApiMetadata; -import org.jclouds.cloudservers.CloudServersApiMetadata; +import org.jclouds.cloudservers.internal.BaseCloudServersComputeServiceExpectTest; import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.domain.Image; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; -import org.jclouds.openstack.keystone.v1_1.internal.BaseKeystoneRestClientExpectTest; import org.jclouds.predicates.PredicateWithResult; import org.testng.annotations.Test; -import com.google.common.base.Function; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; import com.google.common.net.HttpHeaders; import com.google.inject.Injector; -import com.google.inject.Module; +/** + * + * @author David Alves + * + */ @Test(groups = "unit", testName = "GetImageWhenStatusActivePredicateWithResultExpectTest") -public class GetImageWhenStatusActivePredicateWithResultExpectTest extends BaseKeystoneRestClientExpectTest - implements Function { +public class GetImageWhenStatusActivePredicateWithResultExpectTest extends + BaseCloudServersComputeServiceExpectTest { private final HttpRequest listImagesDetail = HttpRequest .builder() .method("GET") - .endpoint(URI.create("https://lon.servers.api.rackspacecloud.com/v1.0/10001786/images/detail?format=json")) - .headers(ImmutableMultimap. builder().put("X-Auth-Token", authToken) - .put(HttpHeaders.ACCEPT, "application/json").build()).build(); + .endpoint( + URI.create("https://lon.servers.api.rackspacecloud.com/v1.0/10001786/images/detail?format=json&now=1257695648897")) + .headers(ImmutableMultimap. builder().put(HttpHeaders.ACCEPT, "application/json") + .put("X-Auth-Token", authToken).build()).build(); private final HttpResponse listImagesResponse = HttpResponse.builder().statusCode(200) .payload(payloadFromResource("/test_list_images_detail_imageextension.json")).build(); - Map requestResponseMap = ImmutableMap. builder() - .put(initialAuth, responseWithAuth).put(listImagesDetail, listImagesResponse).build(); + private final Map requestResponseMap = ImmutableMap. builder() + .put(listImagesDetail, listImagesResponse).put(initialAuth, responseWithAuth).build(); - public GetImageWhenStatusActivePredicateWithResultExpectTest() { - provider = "cloudservers"; + public void testReturnsFalseOnQueuedAndSavingAndTrueOnActive() { + Injector injector = requestsSendResponses(requestResponseMap); + PredicateWithResult predicate = injector + .getInstance(GetImageWhenStatusActivePredicateWithResult.class); + assertTrue(predicate.apply(2)); + assertFalse(predicate.apply(743)); + assertFalse(predicate.apply(744)); } - @Override - protected ApiMetadata createApiMetadata() { - return new CloudServersApiMetadata(); + public void testFailsOnOtherStatuses() { + Injector injector = requestsSendResponses(requestResponseMap); + PredicateWithResult predicate = injector + .getInstance(GetImageWhenStatusActivePredicateWithResult.class); + assertTrue(illegalStateExceptionThrown(predicate, 745)); + assertTrue(illegalStateExceptionThrown(predicate, 746)); + assertTrue(illegalStateExceptionThrown(predicate, 747)); + assertTrue(illegalStateExceptionThrown(predicate, 748)); } - @Override - protected Properties setupProperties() { - Properties overrides = new Properties(); - overrides.setProperty(PROPERTY_REGIONS, "US"); - overrides.setProperty(provider + ".endpoint", endpoint); - return overrides; - } - - @Override - public Injector createClient(Function fn, Module module, Properties props) { - return apply(createComputeServiceContext(fn, module, props)); - } - - private ComputeServiceContext createComputeServiceContext(Function fn, Module module, - Properties props) { - return createInjector(fn, module, props).getInstance(ComputeServiceContext.class); + private boolean illegalStateExceptionThrown(PredicateWithResult predicate, Integer id) { + try { + predicate.apply(id); + } catch (IllegalStateException e) { + return true; + } + return false; } @Override @@ -73,10 +93,4 @@ public class GetImageWhenStatusActivePredicateWithResultExpectTest extends BaseK return input.utils().injector(); } - public void testReturnsFalseOnQueuedAndSavingAndTrueOnActive() { - Injector injector = requestsSendResponses(requestResponseMap); - PredicateWithResult predicate = injector - .getInstance(GetImageWhenStatusActivePredicateWithResult.class); - assertTrue(predicate.apply(2)); - } } diff --git a/apis/cloudservers/src/test/java/org/jclouds/cloudservers/internal/BaseCloudServersComputeServiceExpectTest.java b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/internal/BaseCloudServersComputeServiceExpectTest.java new file mode 100644 index 0000000000..fac67a523c --- /dev/null +++ b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/internal/BaseCloudServersComputeServiceExpectTest.java @@ -0,0 +1,103 @@ +/** + * 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.cloudservers.internal; + +import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS; + +import java.util.Date; +import java.util.Properties; + +import org.jclouds.apis.ApiMetadata; +import org.jclouds.cloudservers.CloudServersApiMetadata; +import org.jclouds.cloudservers.config.CloudServersRestClientModule; +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.openstack.keystone.v1_1.config.AuthenticationServiceModule; +import org.jclouds.openstack.keystone.v1_1.internal.BaseKeystoneRestClientExpectTest; +import org.jclouds.rest.ConfiguresRestClient; + +import com.google.common.base.Function; +import com.google.common.base.Supplier; +import com.google.inject.Module; + +/** + * + * @author David Alves + * + */ +public abstract class BaseCloudServersComputeServiceExpectTest extends BaseKeystoneRestClientExpectTest implements + Function { + + public BaseCloudServersComputeServiceExpectTest() { + provider = "cloudservers"; + } + + protected static final String CONSTANT_DATE = "2009-11-08T15:54:08.897Z"; + + public static class TestAuthenticationServiceModule extends AuthenticationServiceModule { + @Override + protected void configure() { + super.configure(); + } + } + + @Override + protected Module createModule() { + return new TestCloudServersRestClientModule(); + } + + @ConfiguresRestClient + protected static class TestCloudServersRestClientModule extends CloudServersRestClientModule { + + @Override + public Supplier provideCacheBusterDate() { + return new Supplier() { + public Date get() { + return new SimpleDateFormatDateService().iso8601DateParse(CONSTANT_DATE); + } + }; + } + } + + @Override + protected ApiMetadata createApiMetadata() { + return new CloudServersApiMetadata(); + } + + @Override + protected Properties setupProperties() { + Properties overrides = new Properties(); + overrides.setProperty(PROPERTY_REGIONS, "US"); + overrides.setProperty(provider + ".endpoint", endpoint); + return overrides; + } + + @Override + public T createClient(Function fn, Module module, Properties props) { + return apply(createComputeServiceContext(fn, module, props)); + } + + private ComputeServiceContext createComputeServiceContext(Function fn, Module module, + Properties props) { + return createInjector(fn, module, props).getInstance(ComputeServiceContext.class); + } + +} diff --git a/apis/cloudservers/src/test/resources/test_list_images_detail_imageextension.json b/apis/cloudservers/src/test/resources/test_list_images_detail_imageextension.json index af18ac6233..e01acb20ba 100644 --- a/apis/cloudservers/src/test/resources/test_list_images_detail_imageextension.json +++ b/apis/cloudservers/src/test/resources/test_list_images_detail_imageextension.json @@ -3,58 +3,63 @@ { "id" : 2, "name" : "CentOS 5.2", - "updated" : "2010-10-10T12:00:00Z", + "updated" : "2010-10-10T12:00:00Z", "created" : "2010-08-10T12:00:00Z", - "status" : "ACTIVE" + "status" : "ACTIVE" }, { "id" : 743, - "name" : "My Server Backup1", - "serverId" : 12, - "updated" : "2010-10-10T12:00:00Z", + "name" : "My Server Backup", + "serverId" : 12, + "updated" : "2010-10-10T12:00:00Z", "created" : "2009-07-07T09:56:16-05:00", - "status" : "SAVING", - "progress" : 80 + "status" : "SAVING", + "progress" : 80 } + , { "id" : 744, - "name" : "My Server Backup2", - "serverId" : 12, - "updated" : "2010-10-10T12:00:00Z", + "name" : "My Server Backup", + "serverId" : 12, + "updated" : "2010-10-10T12:00:00Z", "created" : "2009-07-07T09:56:16-05:00", - "status" : "UNRECOGNIZED", + "status" : "QUEUED" } + , { "id" : 745, - "name" : "My Server Backup3", - "serverId" : 12, - "updated" : "2010-10-10T12:00:00Z", + "name" : "My Server Backup", + "serverId" : 12, + "updated" : "2010-10-10T12:00:00Z", "created" : "2009-07-07T09:56:16-05:00", - "status" : "UNKNOWN", + "status" : "UNRECOGNIZED" } + , { "id" : 746, - "name" : "My Server Backup4", - "serverId" : 12, - "updated" : "2010-10-10T12:00:00Z", + "name" : "My Server Backup", + "serverId" : 12, + "updated" : "2010-10-10T12:00:00Z", "created" : "2009-07-07T09:56:16-05:00", - "status" : "PREPARING", - } + "status" : "UNKNOWN" + } + , { "id" : 747, - "name" : "My Server Backup5", - "serverId" : 12, - "updated" : "2010-10-10T12:00:00Z", + "name" : "My Server Backup", + "serverId" : 12, + "updated" : "2010-10-10T12:00:00Z", "created" : "2009-07-07T09:56:16-05:00", - "status" : "QUEUED", - } + "status" : "PREPARING" + } + , { "id" : 748, - "name" : "My Server Backup6", - "serverId" : 12, - "updated" : "2010-10-10T12:00:00Z", + "name" : "My Server Backup", + "serverId" : 12, + "updated" : "2010-10-10T12:00:00Z", "created" : "2009-07-07T09:56:16-05:00", - "status" : "FAILED", + "status" : "FAILED" } ] -} +} \ No newline at end of file From 129d82735516235920c22aeffa06b34240fa50e0 Mon Sep 17 00:00:00 2001 From: David Ribeiro Alves Date: Thu, 10 May 2012 08:42:45 +0100 Subject: [PATCH 065/148] implemented ec2 expect tests --- .../ec2/compute/EC2ImageExtension.java | 6 +- .../EC2ComputeServiceDependenciesModule.java | 4 + ...henStatusAvailablePredicateWithResult.java | 15 +- .../BaseEC2ComputeServiceExpectTest.java | 88 ++++++++++++ ...vailablePredicateWithResultExpectTest.java | 132 ++++++++++++++++++ .../describe_images_imageextension0.xml | 24 ++++ .../describe_images_imageextension1.xml | 24 ++++ .../describe_images_imageextension2.xml | 24 ++++ .../BaseNovaEC2RestClientExpectTest.java | 22 +-- ...veStatusPredicateWithResultExpectTest.java | 24 ++++ 10 files changed, 345 insertions(+), 18 deletions(-) create mode 100644 apis/ec2/src/test/java/org/jclouds/ec2/compute/BaseEC2ComputeServiceExpectTest.java create mode 100644 apis/ec2/src/test/java/org/jclouds/ec2/compute/predicates/GetImageWhenStatusAvailablePredicateWithResultExpectTest.java create mode 100644 apis/ec2/src/test/resources/describe_images_imageextension0.xml create mode 100644 apis/ec2/src/test/resources/describe_images_imageextension1.xml create mode 100644 apis/ec2/src/test/resources/describe_images_imageextension2.xml diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ImageExtension.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ImageExtension.java index 276fb230a4..32b244d78f 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ImageExtension.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ImageExtension.java @@ -108,9 +108,9 @@ public class EC2ImageExtension implements ImageExtension { return Futures.makeListenable(executor.submit(new Callable() { @Override public Image call() throws Exception { - return Retryables.retryGettingResultOrFailing(imageReadyPredicate, imageId, maxWait, waitPeriod, - TimeUnit.SECONDS, "Image was not created within the time limit, Giving up! [Limit: " + maxWait - + " secs.]"); + return Retryables.retryGettingResultOrFailing(imageReadyPredicate, region + "/" + imageId, maxWait, + waitPeriod, TimeUnit.SECONDS, "Image was not created within the time limit, Giving up! [Limit: " + + maxWait + " secs.]"); } }), executor); } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java index 9b2beea9e4..d3368edd76 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java @@ -49,11 +49,13 @@ import org.jclouds.ec2.compute.loaders.CreateSecurityGroupIfNeeded; import org.jclouds.ec2.compute.loaders.LoadPublicIpForInstanceOrNull; import org.jclouds.ec2.compute.loaders.RegionAndIdToImage; import org.jclouds.ec2.compute.options.EC2TemplateOptions; +import org.jclouds.ec2.compute.predicates.GetImageWhenStatusAvailablePredicateWithResult; import org.jclouds.ec2.compute.predicates.SecurityGroupPresent; import org.jclouds.ec2.domain.InstanceState; import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.ec2.reference.EC2Constants; +import org.jclouds.predicates.PredicateWithResult; import org.jclouds.predicates.RetryablePredicate; import com.google.common.base.Function; @@ -108,6 +110,8 @@ public class EC2ComputeServiceDependenciesModule extends AbstractModule { bind(WindowsLoginCredentialsFromEncryptedData.class); bind(new TypeLiteral() { }).to(EC2ImageExtension.class); + bind(new TypeLiteral>() { + }).to(GetImageWhenStatusAvailablePredicateWithResult.class); } /** diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/GetImageWhenStatusAvailablePredicateWithResult.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/GetImageWhenStatusAvailablePredicateWithResult.java index 8db7a7067c..4e6333ad77 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/GetImageWhenStatusAvailablePredicateWithResult.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/GetImageWhenStatusAvailablePredicateWithResult.java @@ -3,8 +3,10 @@ package org.jclouds.ec2.compute.predicates; import static com.google.common.base.Preconditions.checkNotNull; import javax.annotation.Resource; +import javax.inject.Inject; import javax.inject.Named; +import org.jclouds.aws.util.AWSUtils; import org.jclouds.compute.domain.Image; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.ec2.EC2Client; @@ -21,22 +23,23 @@ public final class GetImageWhenStatusAvailablePredicateWithResult implements Pre @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; - private final String region; private final EC2Client ec2Client; private final EC2ImageParser ec2ImageToImage; private org.jclouds.ec2.domain.Image result; private RuntimeException lastFailure; - public GetImageWhenStatusAvailablePredicateWithResult(EC2Client ec2Client, EC2ImageParser ec2ImageToImage, - String region) { - this.region = region; + @Inject + public GetImageWhenStatusAvailablePredicateWithResult(EC2Client ec2Client, EC2ImageParser ec2ImageToImage) { this.ec2Client = ec2Client; this.ec2ImageToImage = ec2ImageToImage; } @Override public boolean apply(String input) { - result = checkNotNull(findImage(input)); + String[] parts = AWSUtils.parseHandle(input); + String region = parts[0]; + String imageId = parts[1]; + result = checkNotNull(findImage(imageId, region)); switch (result.getImageState()) { case AVAILABLE: logger.info("<< Image %s is available for use.", input); @@ -60,7 +63,7 @@ public final class GetImageWhenStatusAvailablePredicateWithResult implements Pre return lastFailure; } - private org.jclouds.ec2.domain.Image findImage(String id) { + private org.jclouds.ec2.domain.Image findImage(String id, String region) { return Iterables.getOnlyElement(ec2Client.getAMIServices().describeImagesInRegion(region, new DescribeImagesOptions().imageIds(id))); diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/BaseEC2ComputeServiceExpectTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/BaseEC2ComputeServiceExpectTest.java new file mode 100644 index 0000000000..f663fde11b --- /dev/null +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/BaseEC2ComputeServiceExpectTest.java @@ -0,0 +1,88 @@ +/** + * 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.ec2.compute; + +import java.util.Properties; + +import javax.inject.Named; + +import org.jclouds.Constants; +import org.jclouds.apis.ApiMetadata; +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.date.DateService; +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.ec2.EC2ApiMetadata; +import org.jclouds.ec2.EC2AsyncClient; +import org.jclouds.ec2.EC2Client; +import org.jclouds.ec2.config.EC2RestClientModule; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.rest.ConfiguresRestClient; +import org.jclouds.rest.internal.BaseRestClientExpectTest; + +import com.google.common.base.Function; +import com.google.inject.Module; +import com.google.inject.Provides; + +/** + * + * @author David Alves + */ +public abstract class BaseEC2ComputeServiceExpectTest extends BaseRestClientExpectTest implements + Function { + + protected static final String CONSTANT_DATE = "2012-04-16T15:54:08.897Z"; + protected DateService dateService = new SimpleDateFormatDateService(); + + public BaseEC2ComputeServiceExpectTest() { + provider = "ec2"; + } + + @ConfiguresRestClient + private static final class TestEC2RestClientModule extends EC2RestClientModule { + @Override + @Provides + protected String provideTimeStamp(final DateService dateService, + @Named(Constants.PROPERTY_SESSION_INTERVAL) final int expiration) { + return CONSTANT_DATE; + } + } + + @Override + public T createClient(Function fn, Module module, Properties props) { + return apply(createComputeServiceContext(fn, module, props)); + } + + private ComputeServiceContext createComputeServiceContext(Function fn, Module module, + Properties props) { + return createInjector(fn, module, props).getInstance(ComputeServiceContext.class); + } + + @Override + protected Module createModule() { + return new TestEC2RestClientModule(); + } + + @Override + protected ApiMetadata createApiMetadata() { + return new EC2ApiMetadata(); + } + +} diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/predicates/GetImageWhenStatusAvailablePredicateWithResultExpectTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/predicates/GetImageWhenStatusAvailablePredicateWithResultExpectTest.java new file mode 100644 index 0000000000..744119fba3 --- /dev/null +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/predicates/GetImageWhenStatusAvailablePredicateWithResultExpectTest.java @@ -0,0 +1,132 @@ +/** + * 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.ec2.compute.predicates; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import java.net.URI; +import java.util.Map; + +import javax.ws.rs.core.MediaType; + +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.domain.Image; +import org.jclouds.ec2.compute.BaseEC2ComputeServiceExpectTest; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.predicates.PredicateWithResult; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMultimap; +import com.google.inject.Injector; + +/** + * + * @author David Alves + */ +@Test(groups = "unit", testName = "GetImageWhenStatusAvailablePredicateWithResultExpectTest") +public class GetImageWhenStatusAvailablePredicateWithResultExpectTest extends BaseEC2ComputeServiceExpectTest { + + protected HttpRequest describeRegionsRequest = HttpRequest + .builder() + .method("POST") + .endpoint(URI.create("https://ec2.us-east-1.amazonaws.com/")) + .headers(ImmutableMultimap.of("Host", "ec2.us-east-1.amazonaws.com")) + .payload(payloadFromStringWithContentType( + "Action=DescribeRegions&Signature=s5OXKqaaeKhJW5FVrRntuMsUL4Ed5fjzgUWeukU96ko%3D&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2012-04-16T15%3A54%3A08.897Z&Version=2010-06-15&AWSAccessKeyId=identity", + "application/x-www-form-urlencoded")).build(); + + protected HttpRequest describeImagesRequest0 = HttpRequest + .builder() + .method("POST") + .endpoint(URI.create("https://ec2.us-east-1.amazonaws.com/")) + .headers(ImmutableMultimap.of("Host", "ec2.us-east-1.amazonaws.com")) + .payload(payloadFromStringWithContentType( + "Action=DescribeImages&ImageId.1=ami-0&Signature=k9douTXFWkAZecPiZfBLUm3LIS3bTLanMV%2F%2BWrB1jFA%3D&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2012-04-16T15%3A54%3A08.897Z&Version=2010-06-15&AWSAccessKeyId=identity", + "application/x-www-form-urlencoded")).build(); + + protected HttpRequest describeImagesRequest1 = HttpRequest + .builder() + .method("POST") + .endpoint(URI.create("https://ec2.us-east-1.amazonaws.com/")) + .headers(ImmutableMultimap.of("Host", "ec2.us-east-1.amazonaws.com")) + .payload(payloadFromStringWithContentType( + "Action=DescribeImages&ImageId.1=ami-1&Signature=IVunQEvp8vTKTIxXex2Uh5SWQY1PJCx0ExUe9FRujBY%3D&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2012-04-16T15%3A54%3A08.897Z&Version=2010-06-15&AWSAccessKeyId=identity", + "application/x-www-form-urlencoded")).build(); + + protected HttpRequest describeImagesRequest2 = HttpRequest + .builder() + .method("POST") + .endpoint(URI.create("https://ec2.us-east-1.amazonaws.com/")) + .headers(ImmutableMultimap.of("Host", "ec2.us-east-1.amazonaws.com")) + .payload(payloadFromStringWithContentType( + "Action=DescribeImages&ImageId.1=ami-2&Signature=8TfP8BJlg1hiY6EqUbS73A7PQO7dlpqnRMyi7hPu76U%3D&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2012-04-16T15%3A54%3A08.897Z&Version=2010-06-15&AWSAccessKeyId=identity", + "application/x-www-form-urlencoded")).build(); + + protected HttpResponse describeRegionsResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType("/regionEndpoints.xml", MediaType.APPLICATION_XML)).build(); + + protected HttpResponse describeImagesResponse0 = HttpResponse + .builder() + .statusCode(200) + .payload(payloadFromResourceWithContentType("/describe_images_imageextension0.xml", + MediaType.APPLICATION_XML)).build(); + + protected HttpResponse describeImagesResponse1 = HttpResponse + .builder() + .statusCode(200) + .payload(payloadFromResourceWithContentType("/describe_images_imageextension1.xml", + MediaType.APPLICATION_XML)).build(); + + protected HttpResponse describeImagesResponse2 = HttpResponse + .builder() + .statusCode(200) + .payload(payloadFromResourceWithContentType("/describe_images_imageextension2.xml", + MediaType.APPLICATION_XML)).build(); + + private final Map requestResponseMap = ImmutableMap. builder() + .put(describeRegionsRequest, describeRegionsResponse).put(describeImagesRequest0, describeImagesResponse0) + .put(describeImagesRequest1, describeImagesResponse1).put(describeImagesRequest2, describeImagesResponse2) + .build(); + + public void testReturnsFalseOnQueuedAndSavingAndTrueOnActive() { + Injector injector = requestsSendResponses(requestResponseMap); + PredicateWithResult predicate = injector + .getInstance(GetImageWhenStatusAvailablePredicateWithResult.class); + assertTrue(predicate.apply("us-east-1/ami-0")); + assertFalse(predicate.apply("us-east-1/ami-2")); + } + + @Test(groups = "unit", testName = "GetImageWhenStatusAvailablePredicateWithResultExpectTest", expectedExceptions = IllegalStateException.class) + public void testFailsOnOtherStatuses() { + Injector injector = requestsSendResponses(requestResponseMap); + PredicateWithResult predicate = injector + .getInstance(GetImageWhenStatusAvailablePredicateWithResult.class); + predicate.apply("us-east-1/ami-1"); + } + + @Override + public Injector apply(ComputeServiceContext input) { + return input.utils().injector(); + } + +} diff --git a/apis/ec2/src/test/resources/describe_images_imageextension0.xml b/apis/ec2/src/test/resources/describe_images_imageextension0.xml new file mode 100644 index 0000000000..fde8b1d86b --- /dev/null +++ b/apis/ec2/src/test/resources/describe_images_imageextension0.xml @@ -0,0 +1,24 @@ + + + + ami-0 + ec2-public-images/fedora-8-i386-base-v1.04.manifest.xml + + available + 206029621532 + false + + + 9961934F + + + i386 + machine + aki-4438dd2d + ari-4538dd2c + instance-store + + paravirtual + + + \ No newline at end of file diff --git a/apis/ec2/src/test/resources/describe_images_imageextension1.xml b/apis/ec2/src/test/resources/describe_images_imageextension1.xml new file mode 100644 index 0000000000..5b2bc97519 --- /dev/null +++ b/apis/ec2/src/test/resources/describe_images_imageextension1.xml @@ -0,0 +1,24 @@ + + + + ami-1 + ec2-public-images/fedora-8-i386-base-v1.04.manifest.xml + + deregistered + 206029621532 + false + + + 9961934F + + + i386 + machine + aki-4438dd2d + ari-4538dd2c + instance-store + + paravirtual + + + \ No newline at end of file diff --git a/apis/ec2/src/test/resources/describe_images_imageextension2.xml b/apis/ec2/src/test/resources/describe_images_imageextension2.xml new file mode 100644 index 0000000000..bef5c03b41 --- /dev/null +++ b/apis/ec2/src/test/resources/describe_images_imageextension2.xml @@ -0,0 +1,24 @@ + + + + ami-2 + ec2-public-images/fedora-8-i386-base-v1.04.manifest.xml + + unrecognized + 206029621532 + false + + + 9961934F + + + i386 + machine + aki-4438dd2d + ari-4538dd2c + instance-store + + paravirtual + + + \ No newline at end of file diff --git a/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/internal/BaseNovaEC2RestClientExpectTest.java b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/internal/BaseNovaEC2RestClientExpectTest.java index 5f34f76241..deb7f8bc44 100644 --- a/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/internal/BaseNovaEC2RestClientExpectTest.java +++ b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/internal/BaseNovaEC2RestClientExpectTest.java @@ -21,17 +21,21 @@ import com.google.inject.Provides; public abstract class BaseNovaEC2RestClientExpectTest extends BaseRestClientExpectTest { protected static final String CONSTANT_DATE = "2012-04-16T15:54:08.897Z"; + protected DateService dateService = new SimpleDateFormatDateService(); protected URI endpoint = URI.create("http://localhost:8773/services/Cloud/"); - - protected HttpRequest describeAvailabilityZonesRequest = HttpRequest.builder().method("POST") - .endpoint(endpoint) - .headers(ImmutableMultimap.of("Host", "localhost:8773")) - .payload(payloadFromStringWithContentType("Action=DescribeAvailabilityZones&Signature=S3fa5fybw4KAq4o11IpKHlqwx3cVJdKfeAKw3FIJYvM%3D&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2012-04-16T15%3A54%3A08.897Z&Version=2009-04-04&AWSAccessKeyId=identity", MediaType.APPLICATION_FORM_URLENCODED)) - .build(); - protected HttpResponse describeAvailabilityZonesResponse = HttpResponse.builder() - .statusCode(200).payload(payloadFromResourceWithContentType("/nova_ec2_availabilityZones.xml", MediaType.APPLICATION_XML)).build(); + protected HttpRequest describeAvailabilityZonesRequest = HttpRequest + .builder() + .method("POST") + .endpoint(endpoint) + .headers(ImmutableMultimap.of("Host", "localhost:8773")) + .payload(payloadFromStringWithContentType( + "Action=DescribeAvailabilityZones&Signature=s5OXKqaaeKhJW5FVrRntuMsUL4Ed5fjzgUWeukU96ko%3D&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2012-04-16T15%3A54%3A08.897Z&Version=2009-04-04&AWSAccessKeyId=identity", + MediaType.APPLICATION_FORM_URLENCODED)).build(); + protected HttpResponse describeAvailabilityZonesResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType("/nova_ec2_availabilityZones.xml", MediaType.APPLICATION_XML)) + .build(); public BaseNovaEC2RestClientExpectTest() { provider = "openstack-nova-ec2"; @@ -42,7 +46,7 @@ public abstract class BaseNovaEC2RestClientExpectTest extends BaseRestClientExpe @Override @Provides protected String provideTimeStamp(final DateService dateService, - @Named(Constants.PROPERTY_SESSION_INTERVAL) final int expiration) { + @Named(Constants.PROPERTY_SESSION_INTERVAL) final int expiration) { return CONSTANT_DATE; } } diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest.java index a72be63ce4..a6c52ec9bb 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest.java @@ -1,3 +1,22 @@ +/** + * 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.compute.predicates; import static junit.framework.Assert.assertEquals; @@ -17,6 +36,11 @@ import org.testng.annotations.Test; import com.google.common.collect.ImmutableMap; import com.google.inject.Injector; +/** + * + * @author David Alves + * + */ @Test(groups = "unit", testName = "GetImageWhenImageInZoneHasActiveStatucPredicateWithResultExpectTest") public class GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest extends BaseNovaComputeServiceContextExpectTest { From 5a8f0c17432824c62809b9dd4c9241d62ea020b1 Mon Sep 17 00:00:00 2001 From: David Ribeiro Alves Date: Thu, 10 May 2012 08:48:40 +0100 Subject: [PATCH 066/148] added image extension state checker to aws-ec2 module --- .../config/AWSEC2ComputeServiceDependenciesModule.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java index 37b3593b53..2ff6c0db12 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java @@ -55,8 +55,10 @@ import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl; import org.jclouds.ec2.compute.loaders.CreateSecurityGroupIfNeeded; import org.jclouds.ec2.compute.loaders.LoadPublicIpForInstanceOrNull; import org.jclouds.ec2.compute.loaders.RegionAndIdToImage; +import org.jclouds.ec2.compute.predicates.GetImageWhenStatusAvailablePredicateWithResult; import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.domain.RunningInstance; +import org.jclouds.predicates.PredicateWithResult; import org.jclouds.predicates.RetryablePredicate; import com.google.common.base.Function; @@ -98,6 +100,8 @@ public class AWSEC2ComputeServiceDependenciesModule extends EC2ComputeServiceDep install(new FactoryModuleBuilder().build(CallForImages.Factory.class)); bind(new TypeLiteral() { }).to(EC2ImageExtension.class); + bind(new TypeLiteral>() { + }).to(GetImageWhenStatusAvailablePredicateWithResult.class); } @Provides From f63c5a44c67f25164babff880665f1f780cd46c0 Mon Sep 17 00:00:00 2001 From: David Ribeiro Alves Date: Thu, 10 May 2012 09:07:12 +0100 Subject: [PATCH 067/148] corrected formating issues and added headers --- ...geWhenStatusActivePredicateWithResult.java | 23 +++++++++++++++++++ ...henStatusAvailablePredicateWithResult.java | 23 +++++++++++++++++++ .../nova/v1_1/compute/NovaImageExtension.java | 6 +++++ .../predicates/PredicateWithResult.java | 8 +++---- .../compute/VirtualBoxImageExtension.java | 6 +++++ 5 files changed, 62 insertions(+), 4 deletions(-) diff --git a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResult.java b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResult.java index e4d1122f77..a124f7a6f2 100644 --- a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResult.java +++ b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResult.java @@ -1,3 +1,21 @@ +/** + * 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.cloudservers.compute.predicates; import static com.google.common.base.Preconditions.checkNotNull; @@ -17,6 +35,11 @@ import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; +/** + * + * @author David Alves + * + */ public final class GetImageWhenStatusActivePredicateWithResult implements PredicateWithResult { @Resource diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/GetImageWhenStatusAvailablePredicateWithResult.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/GetImageWhenStatusAvailablePredicateWithResult.java index 4e6333ad77..8fe5d7c19e 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/GetImageWhenStatusAvailablePredicateWithResult.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/GetImageWhenStatusAvailablePredicateWithResult.java @@ -1,3 +1,21 @@ +/** + * 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.ec2.compute.predicates; import static com.google.common.base.Preconditions.checkNotNull; @@ -17,6 +35,11 @@ import org.jclouds.predicates.PredicateWithResult; import com.google.common.collect.Iterables; +/** + * § + * @author David Alves + * + */ public final class GetImageWhenStatusAvailablePredicateWithResult implements PredicateWithResult { @Resource diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaImageExtension.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaImageExtension.java index 900a2d4547..121fde2209 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaImageExtension.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/NovaImageExtension.java @@ -49,6 +49,12 @@ import org.jclouds.predicates.Retryables; import com.google.common.util.concurrent.ListenableFuture; +/** + * Nova implementation of {@link ImageExtension} + * + * @author David Alves + * + */ @Singleton public class NovaImageExtension implements ImageExtension { diff --git a/core/src/main/java/org/jclouds/predicates/PredicateWithResult.java b/core/src/main/java/org/jclouds/predicates/PredicateWithResult.java index a424b3a991..41ce34b1bb 100644 --- a/core/src/main/java/org/jclouds/predicates/PredicateWithResult.java +++ b/core/src/main/java/org/jclouds/predicates/PredicateWithResult.java @@ -22,10 +22,10 @@ import com.google.common.annotations.Beta; import com.google.common.base.Predicate; @Beta -public interface PredicateWithResult extends Predicate { - +public interface PredicateWithResult extends Predicate { + Result getResult(); - + Throwable getLastFailure(); - + } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/compute/VirtualBoxImageExtension.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/compute/VirtualBoxImageExtension.java index 6da9fbfbfb..c8daf663be 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/compute/VirtualBoxImageExtension.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/compute/VirtualBoxImageExtension.java @@ -63,6 +63,12 @@ import com.google.common.collect.Iterables; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +/** + * Virtualbox implementation of {@link ImageExtension} + * + * @author David Alves + * + */ @Singleton public class VirtualBoxImageExtension implements ImageExtension { From f11518fa1e6e71237c53475edea2cf0f187f4a8d Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Thu, 10 May 2012 15:21:48 +0100 Subject: [PATCH 068/148] Adding requestLine check to ensure all HttpRequestComparisonTypes compare endpoints and methods --- .../jclouds/rest/internal/BaseRestClientExpectTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/test/java/org/jclouds/rest/internal/BaseRestClientExpectTest.java b/core/src/test/java/org/jclouds/rest/internal/BaseRestClientExpectTest.java index f4ab26c2c8..d90abf86fa 100644 --- a/core/src/test/java/org/jclouds/rest/internal/BaseRestClientExpectTest.java +++ b/core/src/test/java/org/jclouds/rest/internal/BaseRestClientExpectTest.java @@ -367,7 +367,8 @@ public abstract class BaseRestClientExpectTest { */ public boolean httpRequestsAreEqual(HttpRequest a, HttpRequest b) { try { - if (a == null || b == null) { + if (a == null || b == null || !Objects.equal(a.getRequestLine(), b.getRequestLine()) + || !Objects.equal(a.getHeaders(), b.getHeaders())) { return false; } if (a.getPayload() == null || b.getPayload() == null) { @@ -405,13 +406,13 @@ public abstract class BaseRestClientExpectTest { } }); - return diff.identical() && Objects.equal(a.getHeaders(), b.getHeaders()); + return diff.identical(); } case JSON: { JsonParser parser = new JsonParser(); JsonElement payloadA = parser.parse(Strings2.toStringAndClose(a.getPayload().getInput())); JsonElement payloadB = parser.parse(Strings2.toStringAndClose(b.getPayload().getInput())); - return Objects.equal(payloadA, payloadB) && Objects.equal(a.getHeaders(), b.getHeaders()); + return Objects.equal(payloadA, payloadB); } default: { return Objects.equal(a, b); From 305a593d9aed8f228a93e1f18d47d87436d0ecbe Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Thu, 10 May 2012 15:09:35 +0100 Subject: [PATCH 069/148] vcloud-director: fixing expect tests that were incorrectly passing due to missing requestline check in BaseRestClientExpectTest --- .../vcloud/director/v1_5/features/MediaClientExpectTest.java | 4 ++-- .../director/v1_5/features/VAppTemplateClientExpectTest.java | 4 ++-- .../v1_5/features/admin/AdminCatalogClientExpectTest.java | 2 +- .../v1_5/features/admin/AdminNetworkClientExpectTest.java | 2 +- .../director/v1_5/features/admin/UserClientExpectTest.java | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/MediaClientExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/MediaClientExpectTest.java index d485fc1614..14562ceace 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/MediaClientExpectTest.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/MediaClientExpectTest.java @@ -80,7 +80,7 @@ public class MediaClientExpectTest extends VCloudDirectorAdminClientExpectTest { @Test public void testCloneMedia() { - URI cloneUri = URI.create(endpoint + "/vdc/e9cd3387-ac57-4d27-a481-9bee75e0690f/action/cloneMedia"); + URI vdcURI = URI.create(endpoint + "/vdc/e9cd3387-ac57-4d27-a481-9bee75e0690f"); VCloudDirectorClient client = requestsSendResponses(loginRequest, sessionResponse, new VcloudHttpRequestPrimer() @@ -105,7 +105,7 @@ public class MediaClientExpectTest extends VCloudDirectorAdminClientExpectTest { .build(); Media expected = cloneMedia(); - assertEquals(client.getMediaClient().cloneMedia(cloneUri, params), expected); + assertEquals(client.getMediaClient().cloneMedia(vdcURI, params), expected); } @Test diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateClientExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateClientExpectTest.java index c78a3f5746..ccfce48a5a 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateClientExpectTest.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateClientExpectTest.java @@ -394,7 +394,7 @@ public class VAppTemplateClientExpectTest extends VCloudDirectorAdminClientExpec VAppTemplateClient client = orderedRequestsSendResponses(loginRequest, sessionResponse, new VcloudHttpRequestPrimer().apiCommand("GET", templateId + "/metadata").acceptMedia(ANY).httpRequestBuilder().build(), new VcloudHttpResponsePrimer().xmlFilePayload("/vapptemplate/metadata.xml", METADATA).httpResponseBuilder().build(), - new VcloudHttpRequestPrimer().apiCommand("PUT", templateId + "/metadata").xmlFilePayload("/vapptemplate/metadata.xml", METADATA).acceptMedia(TASK).httpRequestBuilder().build(), + new VcloudHttpRequestPrimer().apiCommand("POST", templateId + "/metadata").xmlFilePayload("/vapptemplate/metadata.xml", METADATA).acceptMedia(TASK).httpRequestBuilder().build(), new VcloudHttpResponsePrimer().xmlFilePayload("/task/task.xml", TASK).httpResponseBuilder().build() ).getVAppTemplateClient(); @@ -425,7 +425,7 @@ public class VAppTemplateClientExpectTest extends VCloudDirectorAdminClientExpec URI uri = URI.create(endpoint + templateId); VAppTemplateClient client = orderedRequestsSendResponses(loginRequest, sessionResponse, - new VcloudHttpRequestPrimer().apiCommand("PUT", templateId + "/metadata").xmlFilePayload("/vapptemplate/metadata.xml", METADATA).acceptMedia(TASK).httpRequestBuilder().build(), + new VcloudHttpRequestPrimer().apiCommand("POST", templateId + "/metadata").xmlFilePayload("/vapptemplate/metadata.xml", METADATA).acceptMedia(TASK).httpRequestBuilder().build(), new VcloudHttpResponsePrimer().xmlFilePayload("/vapptemplate/error400.xml", ERROR).httpResponseBuilder().statusCode(400).build()).getVAppTemplateClient(); client.getMetadataClient().mergeMetadata(uri, exampleMetadata()); diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminCatalogClientExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminCatalogClientExpectTest.java index ad2ab171a6..f8d3e208c5 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminCatalogClientExpectTest.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminCatalogClientExpectTest.java @@ -53,7 +53,7 @@ public class AdminCatalogClientExpectTest extends VCloudDirectorAdminClientExpec public void testCreateCatalog() { VCloudDirectorAdminClient client = requestsSendResponses(loginRequest, sessionResponse, new VcloudHttpRequestPrimer() - .apiCommand("POST", "/admin/org/6f312e42-cd2b-488d-a2bb-97519cd57ed0/catalogs") + .apiCommand("POST", "/admin/catalog/7212e451-76e1-4631-b2de-ba1dfd8080e4/catalogs") .xmlFilePayload("/catalog/admin/createCatalogSource.xml", VCloudDirectorMediaType.ADMIN_CATALOG) .acceptMedia(VCloudDirectorMediaType.ADMIN_CATALOG) .httpRequestBuilder().build(), diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminNetworkClientExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminNetworkClientExpectTest.java index 77ed62c295..347cc6beda 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminNetworkClientExpectTest.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminNetworkClientExpectTest.java @@ -66,7 +66,7 @@ public class AdminNetworkClientExpectTest extends VCloudDirectorAdminClientExpec public void testUpdateNetwork() { VCloudDirectorAdminClient client = requestsSendResponses(loginRequest, sessionResponse, new VcloudHttpRequestPrimer() - .apiCommand("GET", "/admin/network/b466c0c5-8a5c-4335-b703-a2e2e6b5f3e1") + .apiCommand("PUT", "/admin/network/b466c0c5-8a5c-4335-b703-a2e2e6b5f3e1") .xmlFilePayload("/network/admin/updateNetworkSource.xml", VCloudDirectorMediaType.ORG_NETWORK) .acceptMedia(VCloudDirectorMediaType.TASK) .httpRequestBuilder().build(), diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/UserClientExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/UserClientExpectTest.java index 2e56895584..38eb0b767d 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/UserClientExpectTest.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/UserClientExpectTest.java @@ -52,7 +52,7 @@ public class UserClientExpectTest extends VCloudDirectorAdminClientExpectTest { public void testCreateUser() { VCloudDirectorAdminClient client = requestsSendResponses(loginRequest, sessionResponse, new VcloudHttpRequestPrimer() - .apiCommand("POST", "/admin/org/6f312e42-cd2b-488d-a2bb-97519cd57ed0/catalogs") + .apiCommand("POST", "/admin/org/6f312e42-cd2b-488d-a2bb-97519cd57ed0/users") .xmlFilePayload("/user/createUserSource.xml", VCloudDirectorMediaType.USER) .acceptMedia(VCloudDirectorMediaType.USER) .httpRequestBuilder().build(), From 8631b90c328548f8a1d3f9454d1420720ffee870 Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Thu, 10 May 2012 16:45:54 +0100 Subject: [PATCH 070/148] Fix aws-ec2 template live tests: update OS versions --- .../aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java index 057a5a33ab..1f56d26043 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java @@ -170,7 +170,7 @@ public class AWSEC2TemplateBuilderLiveTest extends EC2TemplateBuilderLiveTest { Template defaultTemplate = view.getComputeService().templateBuilder().build(); assert (defaultTemplate.getImage().getProviderId().startsWith("ami-")) : defaultTemplate; - assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "pv-2012.03.rc-0"); + assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "pv-2012.03.1"); assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true); assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.AMZN_LINUX); assertEquals(defaultTemplate.getImage().getUserMetadata().get("rootDeviceType"), "ebs"); @@ -185,7 +185,7 @@ public class AWSEC2TemplateBuilderLiveTest extends EC2TemplateBuilderLiveTest { Template defaultTemplate = view.getComputeService().templateBuilder().osFamily(OsFamily.AMZN_LINUX) .imageMatches(EC2ImagePredicates.rootDeviceType(RootDeviceType.INSTANCE_STORE)).build(); assert (defaultTemplate.getImage().getProviderId().startsWith("ami-")) : defaultTemplate; - assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "pv-2012.03.rc-0"); + assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "pv-2012.03.1"); assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true); assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.AMZN_LINUX); assertEquals(defaultTemplate.getImage().getUserMetadata().get("rootDeviceType"), "instance-store"); @@ -212,7 +212,7 @@ public class AWSEC2TemplateBuilderLiveTest extends EC2TemplateBuilderLiveTest { System.out.println(fastestTemplate.getImage()); assert (fastestTemplate.getImage().getProviderId().startsWith("ami-")) : fastestTemplate; assertEquals(fastestTemplate.getHardware().getProviderId(), InstanceType.CC2_8XLARGE); - assertEquals(fastestTemplate.getImage().getOperatingSystem().getVersion(), "11.10"); + assertEquals(fastestTemplate.getImage().getOperatingSystem().getVersion(), "12.04"); assertEquals(fastestTemplate.getImage().getOperatingSystem().is64Bit(), true); assertEquals(fastestTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU); assertEquals(fastestTemplate.getImage().getUserMetadata().get("rootDeviceType"), "ebs"); From 94ac48070d4fcc983ba66b28d7c08fd5593f4811 Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Thu, 10 May 2012 16:47:37 +0100 Subject: [PATCH 071/148] Issue 888: use GroupNamingConvention for keyName/securityGroup; don't include region in name --- .../ec2/compute/EC2ComputeService.java | 22 ++++---- .../functions/CreateUniqueKeyPair.java | 27 ++++------ .../RunningInstanceToNodeMetadata.java | 28 ++++------ ...rityGroupsAsNeededAndReturnRunOptions.java | 15 +++--- .../functions/CreateUniqueKeyPairTest.java | 51 +++++++++++++------ .../RunningInstanceToNodeMetadataTest.java | 21 ++++++-- ...GroupsAsNeededAndReturnRunOptionsTest.java | 18 +++++-- .../functions/GroupNamingConvention.java | 11 +++- ...dAppendUniqueStringToThoseWhichRepeat.java | 33 ++++++++++-- .../aws/ec2/compute/AWSEC2ComputeService.java | 5 +- .../AWSRunningInstanceToNodeMetadata.java | 6 ++- ...rityGroupsAsNeededAndReturnRunOptions.java | 6 ++- .../compute/AWSEC2ComputeServiceLiveTest.java | 7 ++- .../AWSRunningInstanceToNodeMetadataTest.java | 24 +++++++-- ...GroupsAsNeededAndReturnRunOptionsTest.java | 20 +++++--- 15 files changed, 196 insertions(+), 98 deletions(-) diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java index c30ec5a7e8..57816cd25e 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java @@ -45,6 +45,8 @@ import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.TemplateBuilder; +import org.jclouds.compute.functions.GroupNamingConvention; +import org.jclouds.compute.functions.GroupNamingConvention.Factory; import org.jclouds.compute.internal.BaseComputeService; import org.jclouds.compute.internal.PersistNodeCredentials; import org.jclouds.compute.options.TemplateOptions; @@ -87,6 +89,7 @@ public class EC2ComputeService extends BaseComputeService { private final EC2Client ec2Client; private final ConcurrentMap credentialsMap; private final LoadingCache securityGroupMap; + private final Factory namingConvention; @Inject protected EC2ComputeService(ComputeServiceContext context, Map credentialStore, @@ -104,7 +107,7 @@ public class EC2ComputeService extends BaseComputeService { PersistNodeCredentials persistNodeCredentials, Timeouts timeouts, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, EC2Client ec2Client, ConcurrentMap credentialsMap, @Named("SECURITY") LoadingCache securityGroupMap, - Optional imageExtension) { + Optional imageExtension, GroupNamingConvention.Factory namingConvention) { super(context, credentialStore, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, nodeSuspended, @@ -113,6 +116,7 @@ public class EC2ComputeService extends BaseComputeService { this.ec2Client = ec2Client; this.credentialsMap = credentialsMap; this.securityGroupMap = securityGroupMap; + this.namingConvention = namingConvention; } @Inject(optional = true) @@ -126,7 +130,8 @@ public class EC2ComputeService extends BaseComputeService { void deleteSecurityGroup(String region, String group) { checkNotEmpty(region, "region"); checkNotEmpty(group, "group"); - String groupName = String.format("jclouds#%s#%s", group, region).replace('#', delimiter); + String groupName = namingConvention.create().sharedNameForGroup(group); + if (ec2Client.getSecurityGroupServices().describeSecurityGroupsInRegion(region, groupName).size() > 0) { logger.debug(">> deleting securityGroup(%s)", groupName); ec2Client.getSecurityGroupServices().deleteSecurityGroupInRegion(region, groupName); @@ -139,13 +144,12 @@ public class EC2ComputeService extends BaseComputeService { @VisibleForTesting void deleteKeyPair(String region, String group) { for (KeyPair keyPair : ec2Client.getKeyPairServices().describeKeyPairsInRegion(region)) { - if ( - // when the keypair is unique per group - keyPair.getKeyName().equals("jclouds"+ delimiter + group) - || keyPair.getKeyName().matches(String.format("jclouds#%s#%s", group, "[0-9a-f]+").replace('#', delimiter)) - // old keypair pattern too verbose as it has an unnecessary - // region qualifier - || keyPair.getKeyName().matches(String.format("jclouds#%s#%s#%s", group, region, "[0-9a-f]+").replace('#', delimiter))) { + String keyName = keyPair.getKeyName(); + Predicate keyNameMatcher = namingConvention.create().containsGroup(group); + String oldKeyNameRegex = String.format("jclouds#%s#%s#%s", group, region, "[0-9a-f]+").replace('#', delimiter); + // old keypair pattern too verbose as it has an unnecessary region qualifier + + if (keyNameMatcher.apply(keyName) || keyName.matches(oldKeyNameRegex)) { Set instancesUsingKeyPair = extractIdsFromInstances(filter(concat(ec2Client.getInstanceServices() .describeInstancesInRegion(region)), usingKeyPairAndNotDead(keyPair))); if (instancesUsingKeyPair.size() > 0) { diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateUniqueKeyPair.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateUniqueKeyPair.java index 2d7e0190f2..e11771395b 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateUniqueKeyPair.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateUniqueKeyPair.java @@ -19,12 +19,12 @@ package org.jclouds.ec2.compute.functions; import static com.google.common.base.Preconditions.checkNotNull; -import static org.jclouds.compute.config.ComputeServiceProperties.RESOURCENAME_DELIMITER; import javax.annotation.Resource; import javax.inject.Named; import javax.inject.Singleton; +import org.jclouds.compute.functions.GroupNamingConvention; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.compute.domain.RegionAndName; @@ -33,7 +33,6 @@ import org.jclouds.logging.Logger; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; -import com.google.common.base.Supplier; import com.google.inject.Inject; /** @@ -46,12 +45,12 @@ public class CreateUniqueKeyPair implements Function { @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; protected final EC2Client ec2Client; - protected final Supplier randomSuffix; + protected final GroupNamingConvention.Factory namingConvention; @Inject - public CreateUniqueKeyPair(EC2Client ec2Client, Supplier randomSuffix) { + public CreateUniqueKeyPair(EC2Client ec2Client, GroupNamingConvention.Factory namingConvention) { this.ec2Client = ec2Client; - this.randomSuffix = randomSuffix; + this.namingConvention = checkNotNull(namingConvention, "namingConvention"); } @Override @@ -65,22 +64,18 @@ public class CreateUniqueKeyPair implements Function { checkNotNull(group, "group"); logger.debug(">> creating keyPair region(%s) group(%s)", region, group); KeyPair keyPair = null; + String prefix = group; + while (keyPair == null) { + String keyName = namingConvention.create().uniqueNameForGroup(prefix); try { - keyPair = ec2Client.getKeyPairServices().createKeyPairInRegion(region, getNextName(region, group)); - logger.debug("<< created keyPair(%s)", keyPair); + keyPair = ec2Client.getKeyPairServices().createKeyPairInRegion(region, keyName); } catch (IllegalStateException e) { - + logger.trace(" invalid keyname (%s in %s); retrying", keyName, region); } } + + logger.debug("<< created keyPair(%s)", keyPair); return keyPair; } - - @Inject(optional=true) - @Named(RESOURCENAME_DELIMITER) - char delimiter = '#'; - - private String getNextName(String region, String group) { - return String.format("jclouds#%s#%s#%s", group, region, randomSuffix.get()).replace('#', delimiter); - } } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java index ae286b251c..fa66954a23 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java @@ -21,16 +21,14 @@ package org.jclouds.ec2.compute.functions; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Predicates.not; import static com.google.common.collect.Iterables.filter; -import static org.jclouds.compute.config.ComputeServiceProperties.RESOURCENAME_DELIMITER; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.NoSuchElementException; import java.util.Set; -import java.util.Map.Entry; import javax.annotation.Resource; -import javax.inject.Named; import javax.inject.Singleton; import org.jclouds.collect.Memoized; @@ -42,6 +40,7 @@ import org.jclouds.compute.domain.NodeMetadataBuilder; import org.jclouds.compute.domain.NodeState; import org.jclouds.compute.domain.Volume; import org.jclouds.compute.domain.internal.VolumeImpl; +import org.jclouds.compute.functions.GroupNamingConvention; import org.jclouds.domain.Credentials; import org.jclouds.domain.Location; import org.jclouds.domain.LoginCredentials; @@ -61,10 +60,10 @@ import com.google.common.base.Supplier; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; -import com.google.common.collect.ImmutableSet.Builder; import com.google.common.util.concurrent.UncheckedExecutionException; import com.google.inject.Inject; @@ -82,16 +81,19 @@ public class RunningInstanceToNodeMetadata implements Function> imageMap; protected final Map credentialStore; protected final Map instanceToNodeState; + protected final GroupNamingConvention.Factory namingConvention; @Inject protected RunningInstanceToNodeMetadata(Map instanceToNodeState, Map credentialStore, Supplier> imageMap, - @Memoized Supplier> locations, @Memoized Supplier> hardware) { + @Memoized Supplier> locations, @Memoized Supplier> hardware, + GroupNamingConvention.Factory namingConvention) { this.locations = checkNotNull(locations, "locations"); this.hardware = checkNotNull(hardware, "hardware"); this.imageMap = checkNotNull(imageMap, "imageMap"); this.instanceToNodeState = checkNotNull(instanceToNodeState, "instanceToNodeState"); this.credentialStore = checkNotNull(credentialStore, "credentialStore"); + this.namingConvention = checkNotNull(namingConvention, "namingConvention"); } @Override @@ -197,24 +199,16 @@ public class RunningInstanceToNodeMetadata implements Function data) { String group = null; try { - group = Iterables.getOnlyElement(Iterables.filter(data, new Predicate() { - - @Override - public boolean apply(String input) { - return input.startsWith("jclouds" + delimiter) && input.contains(delimiter + instance.getRegion()); - } - })).split(delimiter + "")[1]; + Predicate containsAnyGroup = namingConvention.create().containsAnyGroup(); + String encodedGroup = Iterables.getOnlyElement(Iterables.filter(data, containsAnyGroup)); + group = namingConvention.create().extractGroup(encodedGroup); } catch (NoSuchElementException e) { logger.debug("no group parsed from %s's data: %s", instance.getId(), data); } catch (IllegalArgumentException e) { - logger.debug("too many groups match %s%s; %s's data: %s", "jclouds", delimiter, instance.getId(), data); + logger.debug("too many groups match naming convention; %s's data: %s", instance.getId(), data); } return group; } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java index 31b2a03f5a..75b29e79e1 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java @@ -21,7 +21,6 @@ package org.jclouds.ec2.compute.strategy; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; -import static org.jclouds.compute.config.ComputeServiceProperties.RESOURCENAME_DELIMITER; import static org.jclouds.crypto.SshKeys.fingerprintPrivateKey; import static org.jclouds.crypto.SshKeys.sha1PrivateKey; @@ -33,6 +32,8 @@ import javax.inject.Provider; import javax.inject.Singleton; import org.jclouds.compute.domain.Template; +import org.jclouds.compute.functions.GroupNamingConvention; +import org.jclouds.compute.functions.GroupNamingConvention.Factory; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; @@ -63,16 +64,19 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions { public final LoadingCache securityGroupMap; @VisibleForTesting public final Provider optionsProvider; + private final Factory namingConvention; @Inject public CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions(Function makeKeyPair, ConcurrentMap credentialsMap, @Named("SECURITY") LoadingCache securityGroupMap, - Provider optionsProvider) { + Provider optionsProvider, + GroupNamingConvention.Factory namingConvention) { this.makeKeyPair = checkNotNull(makeKeyPair, "makeKeyPair"); this.credentialsMap = checkNotNull(credentialsMap, "credentialsMap"); this.securityGroupMap = checkNotNull(securityGroupMap, "securityGroupMap"); this.optionsProvider = checkNotNull(optionsProvider, "optionsProvider"); + this.namingConvention = checkNotNull(namingConvention, "namingConvention"); } public RunInstancesOptions execute(String region, String group, Template template) { @@ -160,16 +164,13 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions { return keyPair.getKeyName(); } - @Inject(optional = true) - @Named(RESOURCENAME_DELIMITER) - char delimiter = '#'; - @VisibleForTesting public Set getSecurityGroupsForTagAndOptions(String region, @Nullable String group, TemplateOptions options) { Builder groups = ImmutableSet.builder(); if (group != null) { - String markerGroup = String.format("jclouds#%s#%s", group, region).replace('#', delimiter); + String markerGroup = namingConvention.create().sharedNameForGroup(group); + groups.add(markerGroup); RegionNameAndIngressRules regionNameAndIngessRulesForMarkerGroup; diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/CreateUniqueKeyPairTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/CreateUniqueKeyPairTest.java index 45fa06b887..1cfe6fc1d3 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/CreateUniqueKeyPairTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/CreateUniqueKeyPairTest.java @@ -26,73 +26,92 @@ import static org.testng.Assert.assertEquals; import java.net.UnknownHostException; +import org.jclouds.ec2.EC2ApiMetadata; import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.services.KeyPairClient; import org.testng.annotations.Test; import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.TypeLiteral; +import com.google.inject.name.Names; /** * @author Adrian Cole */ @Test(groups = "unit", testName = "CreateUniqueKeyPairTest") public class CreateUniqueKeyPairTest { - @SuppressWarnings( { "unchecked" }) + @Test public void testApply() throws UnknownHostException { - EC2Client client = createMock(EC2Client.class); + final EC2Client client = createMock(EC2Client.class); KeyPairClient keyClient = createMock(KeyPairClient.class); - Supplier uniqueIdSupplier = createMock(Supplier.class); - KeyPair pair = createMock(KeyPair.class); expect(client.getKeyPairServices()).andReturn(keyClient).atLeastOnce(); - expect(uniqueIdSupplier.get()).andReturn("1"); - expect(keyClient.createKeyPairInRegion("region", "jclouds#group#region#1")).andReturn(pair); + expect(keyClient.createKeyPairInRegion("region", "jclouds#group#1")).andReturn(pair); replay(client); replay(keyClient); - replay(uniqueIdSupplier); - CreateUniqueKeyPair parser = new CreateUniqueKeyPair(client, uniqueIdSupplier); + CreateUniqueKeyPair parser = Guice.createInjector(new AbstractModule() { + + @Override + protected void configure() { + Names.bindProperties(binder(),new EC2ApiMetadata().getDefaultProperties()); + bind(new TypeLiteral>() { + }).toInstance(Suppliers.ofInstance("1")); + bind(EC2Client.class).toInstance(client); + } + + }).getInstance(CreateUniqueKeyPair.class); assertEquals(parser.createNewKeyPairInRegion("region", "group"), pair); verify(client); verify(keyClient); - verify(uniqueIdSupplier); } @SuppressWarnings( { "unchecked" }) @Test public void testApplyWithIllegalStateException() throws UnknownHostException { - EC2Client client = createMock(EC2Client.class); + final EC2Client client = createMock(EC2Client.class); KeyPairClient keyClient = createMock(KeyPairClient.class); - Supplier uniqueIdSupplier = createMock(Supplier.class); + final Supplier uniqueIdSupplier = createMock(Supplier.class); KeyPair pair = createMock(KeyPair.class); expect(client.getKeyPairServices()).andReturn(keyClient).atLeastOnce(); expect(uniqueIdSupplier.get()).andReturn("1"); - expect(keyClient.createKeyPairInRegion("region", "jclouds#group#region#1")).andThrow(new IllegalStateException()); + expect(keyClient.createKeyPairInRegion("region", "jclouds#group#1")).andThrow(new IllegalStateException()); expect(uniqueIdSupplier.get()).andReturn("2"); - expect(keyClient.createKeyPairInRegion("region", "jclouds#group#region#2")).andReturn(pair); + expect(keyClient.createKeyPairInRegion("region", "jclouds#group#2")).andReturn(pair); replay(client); replay(keyClient); replay(uniqueIdSupplier); - CreateUniqueKeyPair parser = new CreateUniqueKeyPair(client, uniqueIdSupplier); + CreateUniqueKeyPair parser = Guice.createInjector(new AbstractModule() { + + @Override + protected void configure() { + Names.bindProperties(binder(),new EC2ApiMetadata().getDefaultProperties()); + bind(new TypeLiteral>() { + }).toInstance(uniqueIdSupplier); + bind(EC2Client.class).toInstance(client); + } + + }).getInstance(CreateUniqueKeyPair.class); assertEquals(parser.createNewKeyPairInRegion("region", "group"), pair); verify(client); verify(keyClient); verify(uniqueIdSupplier); - } - } diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadataTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadataTest.java index 7370e74ddf..9cdd4205cc 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadataTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadataTest.java @@ -32,11 +32,13 @@ import org.jclouds.compute.domain.NodeMetadataBuilder; import org.jclouds.compute.domain.NodeState; import org.jclouds.compute.domain.OperatingSystem; import org.jclouds.compute.domain.OsFamily; +import org.jclouds.compute.functions.GroupNamingConvention; import org.jclouds.domain.Credentials; import org.jclouds.domain.Location; import org.jclouds.domain.LocationBuilder; import org.jclouds.domain.LocationScope; import org.jclouds.domain.LoginCredentials; +import org.jclouds.ec2.EC2ApiMetadata; import org.jclouds.ec2.compute.config.EC2ComputeServiceDependenciesModule; import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.domain.InstanceState; @@ -53,6 +55,9 @@ import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.name.Names; /** * @author Adrian Cole @@ -221,14 +226,14 @@ public class RunningInstanceToNodeMetadataTest { public void testGroupNameIsSetWhenCustomKeyNameIsSetAndSecurityGroupIsGenerated() { checkGroupName(RunningInstance.builder().instanceId("id").imageId("image").instanceType("m1.small") .instanceState(InstanceState.RUNNING).region("us-east-1").keyName("custom-key") - .groupId("jclouds#groupname#us-east-1").build()); + .groupId("jclouds#groupname").build()); } @Test public void testGroupNameIsSetWhenCustomSecurityGroupIsSetAndKeyNameIsGenerated() { checkGroupName(RunningInstance.builder().instanceId("id").imageId("image").instanceType("m1.small") .instanceState(InstanceState.RUNNING).region("us-east-1").groupId("custom-sec") - .keyName("jclouds#groupname#us-east-1#23").build()); + .keyName("jclouds#groupname#23").build()); } protected RunningInstance firstInstanceFromResource(String resource) { @@ -278,9 +283,19 @@ public class RunningInstanceToNodeMetadataTest { } }; + + GroupNamingConvention.Factory namingConvention = Guice.createInjector(new AbstractModule() { + + @Override + protected void configure() { + Names.bindProperties(binder(),new EC2ApiMetadata().getDefaultProperties()); + } + + }).getInstance(GroupNamingConvention.Factory.class); + RunningInstanceToNodeMetadata parser = new RunningInstanceToNodeMetadata(instanceToNodeState, credentialStore, Suppliers.> ofInstance(instanceToImage), locationSupplier, - hardwareSupplier); + hardwareSupplier, namingConvention); return parser; } diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java index d07797015e..c81e0fedee 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java @@ -34,6 +34,7 @@ import javax.inject.Provider; import org.jclouds.aws.domain.Region; import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.Template; +import org.jclouds.compute.functions.GroupNamingConvention; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.domain.LoginCredentials; import org.jclouds.ec2.compute.domain.EC2HardwareBuilder; @@ -416,7 +417,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { // setup constants String region = Region.AP_SOUTHEAST_1; String group = "group"; - String generatedMarkerGroup = "jclouds#group#" + Region.AP_SOUTHEAST_1; + String generatedMarkerGroup = "jclouds#group"; Set groupIds = ImmutableSet. of(); int[] ports = new int[] {}; boolean shouldAuthorizeSelf = true; @@ -450,7 +451,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { // setup constants String region = Region.AP_SOUTHEAST_1; String group = "group"; - String generatedMarkerGroup = "jclouds#group#" + Region.AP_SOUTHEAST_1; + String generatedMarkerGroup = "jclouds#group"; Set groupIds = ImmutableSet. of(); int[] ports = new int[] { 22, 80 }; boolean shouldAuthorizeSelf = true; @@ -484,7 +485,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { // setup constants String region = Region.AP_SOUTHEAST_1; String group = "group"; - String generatedMarkerGroup = "jclouds#group#" + Region.AP_SOUTHEAST_1; + String generatedMarkerGroup = "jclouds#group"; Set groupIds = ImmutableSet. of(); int[] ports = new int[] {}; boolean shouldAuthorizeSelf = true; @@ -517,7 +518,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { // setup constants String region = Region.AP_SOUTHEAST_1; String group = "group"; - String generatedMarkerGroup = "jclouds#group#" + Region.AP_SOUTHEAST_1; + String generatedMarkerGroup = "jclouds#group"; Set groupIds = ImmutableSet. of("group1", "group2"); int[] ports = new int[] {}; boolean shouldAuthorizeSelf = true; @@ -559,8 +560,15 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { Function makeKeyPair = createMock(Function.class); ConcurrentMap credentialsMap = createMock(ConcurrentMap.class); LoadingCache securityGroupMap = createMock(LoadingCache.class); + GroupNamingConvention.Factory namingConventionFactory = createMock(GroupNamingConvention.Factory.class); + GroupNamingConvention namingConvention = createMock(GroupNamingConvention.class); + expect(namingConventionFactory.create()).andReturn(namingConvention).anyTimes(); + expect(namingConvention.sharedNameForGroup("group")).andReturn("jclouds#group").anyTimes(); + replay(namingConventionFactory); + replay(namingConvention); + return new CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions(makeKeyPair, credentialsMap, - securityGroupMap, OPTIONS_PROVIDER); + securityGroupMap, OPTIONS_PROVIDER, namingConventionFactory); } private void replayStrategy(CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions strategy) { diff --git a/compute/src/main/java/org/jclouds/compute/functions/GroupNamingConvention.java b/compute/src/main/java/org/jclouds/compute/functions/GroupNamingConvention.java index 0fc1867f2a..1e785c8942 100644 --- a/compute/src/main/java/org/jclouds/compute/functions/GroupNamingConvention.java +++ b/compute/src/main/java/org/jclouds/compute/functions/GroupNamingConvention.java @@ -156,8 +156,17 @@ public interface GroupNamingConvention { String groupInSharedNameOrNull(String encoded); /** - * identifies if this name has a group encoded in it. + * A predicate that identifies if an input has the given group encoded in it. */ Predicate containsGroup(String group); + /** + * A predicate that identifies if an input has any group encoded in it. + */ + Predicate containsAnyGroup(); + + /** + * Extracts the group from a shared/unique name. Or returns null if not in correct format to contain a group. + */ + String extractGroup(String encoded); } \ No newline at end of file diff --git a/compute/src/main/java/org/jclouds/compute/internal/FormatSharedNamesAndAppendUniqueStringToThoseWhichRepeat.java b/compute/src/main/java/org/jclouds/compute/internal/FormatSharedNamesAndAppendUniqueStringToThoseWhichRepeat.java index 5edb0ebabb..0ff449939b 100644 --- a/compute/src/main/java/org/jclouds/compute/internal/FormatSharedNamesAndAppendUniqueStringToThoseWhichRepeat.java +++ b/compute/src/main/java/org/jclouds/compute/internal/FormatSharedNamesAndAppendUniqueStringToThoseWhichRepeat.java @@ -111,9 +111,8 @@ public class FormatSharedNamesAndAppendUniqueStringToThoseWhichRepeat implements this.delimiter = delimiter; this.suffixSupplier = checkNotNull(suffixSupplier, "suffixSupplier"); this.groupValidator = checkNotNull(groupValidator, "groupValidator"); - this.sharedFormat = "".equals(prefix) ? "%s" : new StringBuilder().append(prefix).append(delimiter).append("%s") - .toString(); - this.uniqueFormat = new StringBuilder(sharedFormat).append(delimiter).append("%s").toString(); + this.sharedFormat = "".equals(prefix) ? "%s" : prefix + delimiter + "%s"; + this.uniqueFormat = sharedFormat + delimiter + "%s"; this.uniqueGroupPattern = Pattern.compile("^" + ("".equals(prefix) ? "" : (prefix + delimiter)) + "(.+)" + delimiter + "[^" + delimiter + "]+"); this.sharedGroupPattern = Pattern.compile("^" + ("".equals(prefix) ? "" : (prefix + delimiter)) + "(.+)$"); @@ -172,4 +171,32 @@ public class FormatSharedNamesAndAppendUniqueStringToThoseWhichRepeat implements }; } + @Override + public Predicate containsAnyGroup() { + return new Predicate() { + + @Override + public boolean apply(String input) { + try { + return groupInUniqueNameOrNull(input) != null || groupInSharedNameOrNull(input) != null; + } catch (NoSuchElementException e) { + return false; + } + } + + @Override + public String toString() { + return "containsAnyGroup()"; + } + }; + } + + @Override + public String extractGroup(String encoded) { + String result = groupInUniqueNameOrNull(encoded); + if (result != null) return result; + + result = groupInSharedNameOrNull(encoded); + return result; + } } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java index eccd369768..c838f9daa3 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java @@ -51,6 +51,7 @@ import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.NodeMetadataBuilder; import org.jclouds.compute.domain.Template; import org.jclouds.compute.domain.TemplateBuilder; +import org.jclouds.compute.functions.GroupNamingConvention; import org.jclouds.compute.internal.PersistNodeCredentials; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts; @@ -114,12 +115,12 @@ public class AWSEC2ComputeService extends EC2ComputeService { @Named("PLACEMENT") LoadingCache placementGroupMap, @Named("DELETED") Predicate placementGroupDeleted, @Named(PROPERTY_EC2_GENERATE_INSTANCE_NAMES) boolean generateInstanceNames, AWSEC2AsyncClient aclient, - Optional imageExtension) { + Optional imageExtension, GroupNamingConvention.Factory namingConvention) { super(context, credentialStore, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, nodeSuspended, initScriptRunnerFactory, runScriptOnNodeFactory, initAdminAccess, persistNodeCredentials, - timeouts, executor, ec2Client, credentialsMap, securityGroupMap, imageExtension); + timeouts, executor, ec2Client, credentialsMap, securityGroupMap, imageExtension, namingConvention); this.ec2Client = ec2Client; this.placementGroupMap = placementGroupMap; this.placementGroupDeleted = placementGroupDeleted; diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadata.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadata.java index 66ba9e35fc..6bba366dc7 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadata.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadata.java @@ -35,6 +35,7 @@ import org.jclouds.compute.domain.HardwareBuilder; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadataBuilder; import org.jclouds.compute.domain.NodeState; +import org.jclouds.compute.functions.GroupNamingConvention; import org.jclouds.domain.Credentials; import org.jclouds.domain.Location; import org.jclouds.domain.LoginCredentials; @@ -55,8 +56,9 @@ public class AWSRunningInstanceToNodeMetadata extends RunningInstanceToNodeMetad @Inject protected AWSRunningInstanceToNodeMetadata(Map instanceToNodeState, Map credentialStore, Supplier> imageMap, - @Memoized Supplier> locations, @Memoized Supplier> hardware) { - super(instanceToNodeState, credentialStore, imageMap, locations, hardware); + @Memoized Supplier> locations, @Memoized Supplier> hardware, + GroupNamingConvention.Factory namingConvention) { + super(instanceToNodeState, credentialStore, imageMap, locations, hardware, namingConvention); } @Override diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.java index 4872ffdfbc..bbfec00caa 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.java @@ -34,6 +34,7 @@ import org.jclouds.aws.ec2.domain.RegionNameAndPublicKeyMaterial; import org.jclouds.aws.ec2.functions.CreatePlacementGroupIfNeeded; import org.jclouds.aws.ec2.options.AWSRunInstancesOptions; import org.jclouds.compute.domain.Template; +import org.jclouds.compute.functions.GroupNamingConvention; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.ec2.compute.domain.RegionAndName; @@ -72,8 +73,9 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions Provider optionsProvider, @Named("PLACEMENT") LoadingCache placementGroupMap, CreatePlacementGroupIfNeeded createPlacementGroupIfNeeded, - Function importExistingKeyPair) { - super(makeKeyPair, credentialsMap, securityGroupMap, optionsProvider); + Function importExistingKeyPair, + GroupNamingConvention.Factory namingConvention) { + super(makeKeyPair, credentialsMap, securityGroupMap, optionsProvider, namingConvention); this.placementGroupMap = placementGroupMap; this.createPlacementGroupIfNeeded = createPlacementGroupIfNeeded; this.importExistingKeyPair = importExistingKeyPair; diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceLiveTest.java index 7ef63ee4aa..6d3163dd73 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceLiveTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeServiceLiveTest.java @@ -180,12 +180,11 @@ public class AWSEC2ComputeServiceLiveTest extends EC2ComputeServiceLiveTest { } // make sure we made our dummy group and also let in the user's group - assertEquals(newTreeSet(instance.getGroupIds()), ImmutableSortedSet. of("jclouds#" + group + "#" - + instance.getRegion(), group)); + assertEquals(newTreeSet(instance.getGroupIds()), ImmutableSortedSet. of("jclouds#" + group, group)); // make sure our dummy group has no rules SecurityGroup secgroup = getOnlyElement(securityGroupClient.describeSecurityGroupsInRegion(instance - .getRegion(), "jclouds#" + group + "#" + instance.getRegion())); + .getRegion(), "jclouds#" + group)); assert secgroup.getIpPermissions().size() == 0 : secgroup; @@ -258,7 +257,7 @@ public class AWSEC2ComputeServiceLiveTest extends EC2ComputeServiceLiveTest { // Assert the two instances are in the same groups region = instance1.getRegion(); - String expectedSecurityGroupName = "jclouds#" + group + "#" + region; + String expectedSecurityGroupName = "jclouds#" + group; assertEquals(instance1.getRegion(), region); assertNotNull(instance1.getKeyName()); diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadataTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadataTest.java index 00a0c7e2fc..1f7e7d58fd 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadataTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadataTest.java @@ -23,12 +23,14 @@ import static org.testng.Assert.assertEquals; import java.util.Map; import java.util.Set; +import org.jclouds.aws.ec2.AWSEC2ApiMetadata; import org.jclouds.aws.ec2.domain.AWSRunningInstance; import org.jclouds.aws.ec2.domain.MonitoringState; import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadataBuilder; import org.jclouds.compute.domain.NodeState; +import org.jclouds.compute.functions.GroupNamingConvention; import org.jclouds.date.DateService; import org.jclouds.domain.Credentials; import org.jclouds.domain.Location; @@ -53,7 +55,9 @@ import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import com.google.inject.AbstractModule; import com.google.inject.Guice; +import com.google.inject.name.Names; /** * @author Adrian Cole @@ -86,7 +90,7 @@ public class AWSRunningInstanceToNodeMetadataTest { .instanceState(InstanceState.RUNNING) .privateDnsName("ip-10-212-81-7.ec2.internal") .dnsName("ec2-174-129-173-155.compute-1.amazonaws.com") - .keyName("jclouds#zkclustertest#us-east-1#23") + .keyName("jclouds#zkclustertest#23") .amiLaunchIndex("0") .instanceType("t1.micro") .launchTime(dateService.iso8601DateParse("2011-08-16T13:40:50.000Z")) @@ -95,7 +99,7 @@ public class AWSRunningInstanceToNodeMetadataTest { .monitoringState(MonitoringState.DISABLED) .privateIpAddress("10.212.81.7") .ipAddress("174.129.173.155") - .securityGroupIdToNames(ImmutableMap. of("sg-ef052b86", "jclouds#zkclustertest#us-east-1")) + .securityGroupIdToNames(ImmutableMap. of("sg-ef052b86", "jclouds#zkclustertest")) .rootDeviceType(RootDeviceType.EBS) .rootDeviceName("/dev/sda1") .device("/dev/sda1", new BlockDevice("vol-5829fc32", Attachment.Status.ATTACHED, dateService.iso8601DateParse("2011-08-16T13:41:19.000Z"), true)) @@ -111,7 +115,7 @@ public class AWSRunningInstanceToNodeMetadataTest { .instanceState(InstanceState.RUNNING) .privateDnsName("ip-10-212-185-8.ec2.internal") .dnsName("ec2-50-19-207-248.compute-1.amazonaws.com") - .keyName("jclouds#zkclustertest#us-east-1#23") + .keyName("jclouds#zkclustertest#23") .amiLaunchIndex("0") .instanceType("t1.micro") .launchTime(dateService.iso8601DateParse("2011-08-16T13:40:50.000Z")) @@ -120,7 +124,7 @@ public class AWSRunningInstanceToNodeMetadataTest { .monitoringState(MonitoringState.DISABLED) .privateIpAddress("10.212.185.8") .ipAddress("50.19.207.248") - .securityGroupIdToNames(ImmutableMap.of("sg-ef052b86", "jclouds#zkclustertest#us-east-1")) + .securityGroupIdToNames(ImmutableMap.of("sg-ef052b86", "jclouds#zkclustertest")) .rootDeviceType(RootDeviceType.EBS) .rootDeviceName("/dev/sda1") .device("/dev/sda1", new BlockDevice("vol-5029fc3a", Attachment.Status.ATTACHED, dateService.iso8601DateParse("2011-08-16T13:41:19.000Z"), true)) @@ -195,9 +199,19 @@ public class AWSRunningInstanceToNodeMetadataTest { } }; + + GroupNamingConvention.Factory namingConvention = Guice.createInjector(new AbstractModule() { + + @Override + protected void configure() { + Names.bindProperties(binder(),new AWSEC2ApiMetadata().getDefaultProperties()); + } + + }).getInstance(GroupNamingConvention.Factory.class); + AWSRunningInstanceToNodeMetadata parser = new AWSRunningInstanceToNodeMetadata(instanceToNodeState, credentialStore, Suppliers.> ofInstance(instanceToImage), - locationSupplier, hardwareSupplier); + locationSupplier, hardwareSupplier, namingConvention); return parser; } diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java index 59f1727cc4..5efc2db5cf 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java @@ -41,6 +41,7 @@ import org.jclouds.aws.ec2.functions.CreatePlacementGroupIfNeeded; import org.jclouds.aws.ec2.options.AWSRunInstancesOptions; import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.Template; +import org.jclouds.compute.functions.GroupNamingConvention; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.domain.LoginCredentials; import org.jclouds.ec2.compute.EC2TemplateBuilderTest; @@ -659,7 +660,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT // setup constants String region = Region.AP_SOUTHEAST_1; String group = "group"; - String generatedMarkerGroup = "jclouds#group#" + Region.AP_SOUTHEAST_1; + String generatedMarkerGroup = "jclouds#group"; Set groupNames = ImmutableSet. of(); int[] ports = new int[] {}; boolean shouldAuthorizeSelf = true; @@ -693,7 +694,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT // setup constants String region = Region.AP_SOUTHEAST_1; String group = "group"; - String generatedMarkerGroup = "jclouds#group#" + Region.AP_SOUTHEAST_1; + String generatedMarkerGroup = "jclouds#group"; Set groupNames = ImmutableSet. of(); int[] ports = new int[] { 22, 80 }; boolean shouldAuthorizeSelf = true; @@ -727,7 +728,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT // setup constants String region = Region.AP_SOUTHEAST_1; String group = "group"; - String generatedMarkerGroup = "jclouds#group#" + Region.AP_SOUTHEAST_1; + String generatedMarkerGroup = "jclouds#group"; Set groupNames = ImmutableSet. of(); int[] ports = new int[] {}; boolean shouldAuthorizeSelf = true; @@ -761,7 +762,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT // setup constants String region = Region.AP_SOUTHEAST_1; String group = "group"; - String generatedMarkerGroup = "jclouds#group#" + Region.AP_SOUTHEAST_1; + String generatedMarkerGroup = "jclouds#group"; Set groupNames = ImmutableSet. of("group1", "group2"); int[] ports = new int[] {}; boolean shouldAuthorizeSelf = true; @@ -797,7 +798,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT // setup constants String region = Region.AP_SOUTHEAST_1; String group = "group"; - String generatedMarkerGroup = "jclouds#group#" + Region.AP_SOUTHEAST_1; + String generatedMarkerGroup = "jclouds#group"; Set groupNames = ImmutableSet. of(); int[] ports = new int[] {}; boolean shouldAuthorizeSelf = true; @@ -938,9 +939,16 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT LoadingCache placementGroupMap = createMock(LoadingCache.class); Function importExistingKeyPair = createMock(Function.class); CreatePlacementGroupIfNeeded createPlacementGroupIfNeeded = createMock(CreatePlacementGroupIfNeeded.class); + GroupNamingConvention.Factory namingConventionFactory = createMock(GroupNamingConvention.Factory.class); + GroupNamingConvention namingConvention = createMock(GroupNamingConvention.class); + expect(namingConventionFactory.create()).andReturn(namingConvention).anyTimes(); + expect(namingConvention.sharedNameForGroup("group")).andReturn("jclouds#group").anyTimes(); + replay(namingConventionFactory); + replay(namingConvention); return new CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions(makeKeyPair, credentialsMap, - securityGroupMap, OPTIONS_PROVIDER, placementGroupMap, createPlacementGroupIfNeeded, importExistingKeyPair); + securityGroupMap, OPTIONS_PROVIDER, placementGroupMap, createPlacementGroupIfNeeded, importExistingKeyPair, + namingConventionFactory); } private void replayStrategy(CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy) { From fe7d426e2e0781b755bd17aa0865e00f2569ac32 Mon Sep 17 00:00:00 2001 From: David Ribeiro Alves Date: Thu, 10 May 2012 18:27:49 +0100 Subject: [PATCH 072/148] fixed a cloudservers bug --- .../GetImageWhenStatusActivePredicateWithResult.java | 7 ++++--- .../main/java/org/jclouds/cloudservers/domain/Image.java | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResult.java b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResult.java index a124f7a6f2..8f71f12361 100644 --- a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResult.java +++ b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResult.java @@ -63,14 +63,15 @@ public final class GetImageWhenStatusActivePredicateWithResult implements Predic result = checkNotNull(findImage(input)); switch (result.getStatus()) { case ACTIVE: - logger.info("<< Image %s is available for use.", input); + logger.info("<< Image %s is available for use. %s", input, result); return true; case QUEUED: + case PREPARING: case SAVING: - logger.debug("<< Image %s is not available yet.", input); + logger.debug("<< Image %s is not available yet. %s", input, result); return false; default: - lastFailure = new IllegalStateException("Image was not created: " + input); + lastFailure = new IllegalStateException("Image " + input + " was not created. " + result); throw lastFailure; } } diff --git a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/domain/Image.java b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/domain/Image.java index 77320ede4c..7ed580d302 100644 --- a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/domain/Image.java +++ b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/domain/Image.java @@ -138,8 +138,8 @@ public class Image { @Override public String toString() { - return "Image [created=" + created + ", id=" + id + ", name=" + name + ", serverId=" - + serverId + "]"; + return "Image [created=" + created + ", id=" + id + ", name=" + name + ", serverId=" + serverId + ", status=" + + status + "]"; } } From f71e1d57807b6aed784e530e8ca8eca9549f754e Mon Sep 17 00:00:00 2001 From: David Ribeiro Alves Date: Thu, 10 May 2012 19:31:50 +0100 Subject: [PATCH 073/148] fixed a transient status bug in openstack-nova (working on hpcloud) --- ...eWhenImageInZoneHasActiveStatusPredicateWithResult.java | 7 ++++--- .../compute/HPCloudComputeImageExtensionLivetest.java | 5 ----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResult.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResult.java index fb8f73e83e..ddf0eb5c1c 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResult.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResult.java @@ -66,13 +66,14 @@ public final class GetImageWhenImageInZoneHasActiveStatusPredicateWithResult imp resultZoneAndId = input; switch (result.getStatus()) { case ACTIVE: - logger.info("<< Image %s is available for use.", input.getId()); + logger.info("<< Image %s is available for use. %s", input.getId(), result); return true; + case UNRECOGNIZED: case SAVING: - logger.debug("<< Image %s is not available yet.", input.getId()); + logger.debug("<< Image %s is not available yet. %s", input.getId(), result); return false; default: - lastFailure = new IllegalStateException("Image was not created: " + input.getId()); + lastFailure = new IllegalStateException("Image " + input.getId() + " was not created. " + result); throw lastFailure; } } diff --git a/providers/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/compute/HPCloudComputeImageExtensionLivetest.java b/providers/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/compute/HPCloudComputeImageExtensionLivetest.java index 852ce623f5..f632f51cc0 100644 --- a/providers/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/compute/HPCloudComputeImageExtensionLivetest.java +++ b/providers/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/compute/HPCloudComputeImageExtensionLivetest.java @@ -41,9 +41,4 @@ public class HPCloudComputeImageExtensionLivetest extends BaseImageExtensionLive protected Module getSshModule() { return new SshjSshClientModule(); } - - @Override - public long getSpawnNodeMaxWait() { - return 2400L; - } } From 5340e947d3b3c6c4f09cc655dccc90271bd98031 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Thu, 10 May 2012 17:19:29 -0700 Subject: [PATCH 074/148] while image can be null in templateBuilder, image will never be null in a template --- .../strategy/EC2CreateNodesInGroupThenAddToSet.java | 11 +---------- .../EC2CreateNodesInGroupThenAddToSetTest.java | 4 ++++ 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSet.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSet.java index af2f4d648f..a1f2dd27e4 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSet.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSet.java @@ -133,17 +133,8 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen public Map> execute(String group, int count, Template template, Set goodNodes, Map badNodes, Multimap customizationResponses) { - // ensure we don't mutate the input template - Template mutableTemplate; - // ensure we don't mutate the input template, fromTemplate ignores imageId so - // build directly from imageId if we have it - if (template.getImage() != null && template.getImage().getId() != null) { - mutableTemplate = templateBuilderProvider.get().imageId(template.getImage().getId()).fromTemplate(template) + Template mutableTemplate = templateBuilderProvider.get().imageId(template.getImage().getId()).fromTemplate(template) .build(); - // otherwise build from generic parameters - } else { - mutableTemplate = templateBuilderProvider.get().fromTemplate(template).build(); - } Iterable ips = allocateElasticIpsInRegion(count, template); diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSetTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSetTest.java index 6f89f7f057..400c954a57 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSetTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSetTest.java @@ -95,6 +95,7 @@ public class EC2CreateNodesInGroupThenAddToSetTest { strategy.autoAllocateElasticIps = true; // setup expectations + expect(templateBuilder.imageId(region + "/" + imageId)).andReturn(templateBuilder); expect(templateBuilder.fromTemplate(input.template)).andReturn(templateBuilder); expect(templateBuilder.build()).andReturn(input.template); expect(strategy.client.getInstanceServices()).andReturn(instanceClient).atLeastOnce(); @@ -105,6 +106,7 @@ public class EC2CreateNodesInGroupThenAddToSetTest { expect(input.template.getLocation()).andReturn(input.location).atLeastOnce(); expect(input.template.getImage()).andReturn(input.image).atLeastOnce(); + expect(input.image.getId()).andReturn(region + "/" + imageId).atLeastOnce(); expect(input.image.getProviderId()).andReturn(imageId).atLeastOnce(); // differences when ip allocation @@ -202,6 +204,7 @@ public class EC2CreateNodesInGroupThenAddToSetTest { "reservationId"); // setup expectations + expect(templateBuilder.imageId(region + "/" + imageId)).andReturn(templateBuilder); expect(templateBuilder.fromTemplate(input.template)).andReturn(templateBuilder); expect(templateBuilder.build()).andReturn(input.template); expect(strategy.client.getInstanceServices()).andReturn(instanceClient).atLeastOnce(); @@ -210,6 +213,7 @@ public class EC2CreateNodesInGroupThenAddToSetTest { .execute(region, input.tag, input.template)).andReturn(ec2Options); expect(input.template.getLocation()).andReturn(input.location).atLeastOnce(); expect(input.template.getImage()).andReturn(input.image).atLeastOnce(); + expect(input.image.getId()).andReturn(region + "/" + imageId).atLeastOnce(); expect(input.image.getProviderId()).andReturn(imageId).atLeastOnce(); expect(instanceClient.runInstancesInRegion(region, zone, imageId, 1, input.count, ec2Options)).andReturn( Reservation.class.cast(reservation)); From 5bd59d50cdd11efe3bd30c1caac22d02289f464b Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Thu, 10 May 2012 19:29:19 -0700 Subject: [PATCH 075/148] fixed test configuration --- .../jclouds/aws/ec2/services/AWSKeyPairClientLiveTest.java | 5 +++++ .../aws/ec2/services/PlacementGroupClientLiveTest.java | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSKeyPairClientLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSKeyPairClientLiveTest.java index eb4f4f8ddf..15a4030046 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSKeyPairClientLiveTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSKeyPairClientLiveTest.java @@ -48,6 +48,7 @@ import org.jclouds.compute.options.TemplateOptions; import org.jclouds.domain.LoginCredentials; import org.jclouds.ec2.domain.KeyPair; import org.jclouds.logging.log4j.config.Log4JLoggingModule; +import org.jclouds.sshj.config.SshjSshClientModule; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -202,4 +203,8 @@ public class AWSKeyPairClientLiveTest extends BaseComputeServiceContextLiveTest return getOnlyElement(getOnlyElement(instanceClient.describeInstancesInRegion(null, id))); } + @Override + protected Module getSshModule() { + return new SshjSshClientModule(); + } } diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/PlacementGroupClientLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/PlacementGroupClientLiveTest.java index facb340cf0..74b2be2578 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/PlacementGroupClientLiveTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/PlacementGroupClientLiveTest.java @@ -46,11 +46,13 @@ import org.jclouds.predicates.RetryablePredicate; import org.jclouds.scriptbuilder.domain.Statements; import org.jclouds.scriptbuilder.statements.java.InstallJDK; import org.jclouds.scriptbuilder.statements.login.AdminAccess; +import org.jclouds.sshj.config.SshjSshClientModule; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import com.google.common.base.Throwables; +import com.google.inject.Module; /** * Tests behavior of {@code PlacementGroupClient} @@ -168,4 +170,9 @@ public class PlacementGroupClientLiveTest extends BaseComputeServiceContextLiveT assert deletedTester.apply(group) : group; } } + + @Override + protected Module getSshModule() { + return new SshjSshClientModule(); + } } From 8abd56b1c7c2c14a9213e7a239e3aee40d8d9e57 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Thu, 10 May 2012 19:35:10 -0700 Subject: [PATCH 076/148] fixed test expectation glitch --- ...atusActivePredicateWithResultExpectTest.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/apis/cloudservers/src/test/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResultExpectTest.java b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResultExpectTest.java index a55022804d..e479d22897 100644 --- a/apis/cloudservers/src/test/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResultExpectTest.java +++ b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/compute/predicates/GetImageWhenStatusActivePredicateWithResultExpectTest.java @@ -51,7 +51,8 @@ public class GetImageWhenStatusActivePredicateWithResultExpectTest extends .method("GET") .endpoint( URI.create("https://lon.servers.api.rackspacecloud.com/v1.0/10001786/images/detail?format=json&now=1257695648897")) - .headers(ImmutableMultimap. builder().put(HttpHeaders.ACCEPT, "application/json") + .headers(ImmutableMultimap. builder() + .put(HttpHeaders.ACCEPT, "application/json") .put("X-Auth-Token", authToken).build()).build(); private final HttpResponse listImagesResponse = HttpResponse.builder().statusCode(200) @@ -64,19 +65,19 @@ public class GetImageWhenStatusActivePredicateWithResultExpectTest extends Injector injector = requestsSendResponses(requestResponseMap); PredicateWithResult predicate = injector .getInstance(GetImageWhenStatusActivePredicateWithResult.class); - assertTrue(predicate.apply(2)); - assertFalse(predicate.apply(743)); - assertFalse(predicate.apply(744)); + assertTrue(predicate.apply(2));// ACTIVE + assertFalse(predicate.apply(743));// SAVING + assertFalse(predicate.apply(744));// QUEUED + assertFalse(predicate.apply(747));// PREPARING } public void testFailsOnOtherStatuses() { Injector injector = requestsSendResponses(requestResponseMap); PredicateWithResult predicate = injector .getInstance(GetImageWhenStatusActivePredicateWithResult.class); - assertTrue(illegalStateExceptionThrown(predicate, 745)); - assertTrue(illegalStateExceptionThrown(predicate, 746)); - assertTrue(illegalStateExceptionThrown(predicate, 747)); - assertTrue(illegalStateExceptionThrown(predicate, 748)); + assertTrue(illegalStateExceptionThrown(predicate, 745));// UNRECOGNIZED + assertTrue(illegalStateExceptionThrown(predicate, 746));// UNKNOWN + assertTrue(illegalStateExceptionThrown(predicate, 748));// FAILED } private boolean illegalStateExceptionThrown(PredicateWithResult predicate, Integer id) { From bd1531460903dd10e751974a8591f15049f94afe Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Thu, 10 May 2012 19:39:22 -0700 Subject: [PATCH 077/148] fixed test expectation glitch --- ...sActiveStatusPredicateWithResultExpectTest.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest.java index a6c52ec9bb..9e40b868a3 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest.java @@ -58,8 +58,10 @@ public class GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest .getInstance(GetImageWhenImageInZoneHasActiveStatusPredicateWithResult.class); ZoneAndId zoneAdnId0 = ZoneAndId.fromZoneAndId("az-1.region-a.geo-1", "13"); ZoneAndId zoneAdnId1 = ZoneAndId.fromZoneAndId("az-1.region-a.geo-1", "12"); - assertTrue(!predicate.apply(zoneAdnId1)); - assertTrue(predicate.apply(zoneAdnId0)); + ZoneAndId zoneAdnId2 = ZoneAndId.fromZoneAndId("az-1.region-a.geo-1", "15"); + assertTrue(predicate.apply(zoneAdnId0)); // ACTIVE + assertTrue(!predicate.apply(zoneAdnId1)); // SAVING + assertTrue(!predicate.apply(zoneAdnId2)); // UNRECOGNIZED assertEquals("natty-server-cloudimg-amd64", predicate.getResult().getName()); } @@ -67,14 +69,12 @@ public class GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest Injector injector = requestsSendResponses(requestResponseMap); PredicateWithResult predicate = injector .getInstance(GetImageWhenImageInZoneHasActiveStatusPredicateWithResult.class); - ZoneAndId zoneAdnId0 = ZoneAndId.fromZoneAndId("az-1.region-a.geo-1", "15"); ZoneAndId zoneAdnId1 = ZoneAndId.fromZoneAndId("az-1.region-a.geo-1", "14"); ZoneAndId zoneAdnId2 = ZoneAndId.fromZoneAndId("az-1.region-a.geo-1", "11"); ZoneAndId zoneAdnId3 = ZoneAndId.fromZoneAndId("az-1.region-a.geo-1", "10"); - assertTrue(illegalStateExceptionThrown(predicate, zoneAdnId0)); - assertTrue(illegalStateExceptionThrown(predicate, zoneAdnId1)); - assertTrue(illegalStateExceptionThrown(predicate, zoneAdnId2)); - assertTrue(illegalStateExceptionThrown(predicate, zoneAdnId3)); + assertTrue(illegalStateExceptionThrown(predicate, zoneAdnId1)); // UNKNOWN + assertTrue(illegalStateExceptionThrown(predicate, zoneAdnId2)); // ERROR + assertTrue(illegalStateExceptionThrown(predicate, zoneAdnId3)); // ERROR } private boolean illegalStateExceptionThrown(PredicateWithResult predicate, ZoneAndId zoneAndId) { From 3d67f268118e33ccd971bf00f1760cf6d7884ae6 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Thu, 10 May 2012 19:40:46 -0700 Subject: [PATCH 078/148] fixed test expectation glitch --- ...ImageInZoneHasActiveStatusPredicateWithResultExpectTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest.java index 9e40b868a3..ccfaabcafb 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/compute/predicates/GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest.java @@ -62,7 +62,7 @@ public class GetImageWhenImageInZoneHasActiveStatusPredicateWithResultExpectTest assertTrue(predicate.apply(zoneAdnId0)); // ACTIVE assertTrue(!predicate.apply(zoneAdnId1)); // SAVING assertTrue(!predicate.apply(zoneAdnId2)); // UNRECOGNIZED - assertEquals("natty-server-cloudimg-amd64", predicate.getResult().getName()); + assertEquals("oneiric-server-cloudimg-amd64", predicate.getResult().getName()); } public void testFailsOnOtherStatuses() { From e461db59526fcffba22c37809f49e34ec3d07685 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Thu, 10 May 2012 19:47:21 -0700 Subject: [PATCH 079/148] fixed test expectation glitch --- .../nova/ec2/internal/BaseNovaEC2RestClientExpectTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/internal/BaseNovaEC2RestClientExpectTest.java b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/internal/BaseNovaEC2RestClientExpectTest.java index bc6ca9b6f7..69b7eed4b2 100644 --- a/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/internal/BaseNovaEC2RestClientExpectTest.java +++ b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/internal/BaseNovaEC2RestClientExpectTest.java @@ -31,7 +31,7 @@ public abstract class BaseNovaEC2RestClientExpectTest extends BaseRestClientExpe .endpoint(endpoint) .headers(ImmutableMultimap.of("Host", "localhost:8773")) .payload(payloadFromStringWithContentType( - "Action=DescribeAvailabilityZones&Signature=s5OXKqaaeKhJW5FVrRntuMsUL4Ed5fjzgUWeukU96ko%3D&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2012-04-16T15%3A54%3A08.897Z&Version=2009-04-04&AWSAccessKeyId=identity", + "Action=DescribeAvailabilityZones&Signature=S3fa5fybw4KAq4o11IpKHlqwx3cVJdKfeAKw3FIJYvM%3D&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2012-04-16T15%3A54%3A08.897Z&Version=2009-04-04&AWSAccessKeyId=identity", MediaType.APPLICATION_FORM_URLENCODED)).build(); protected HttpResponse describeAvailabilityZonesResponse = HttpResponse.builder().statusCode(200) .payload(payloadFromResourceWithContentType("/nova_ec2_availabilityZones.xml", MediaType.APPLICATION_XML)) From e96f2333eefc2b8968af04f22ef2698df82091ce Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Thu, 10 May 2012 20:08:04 -0700 Subject: [PATCH 080/148] in openstack-swift there are regions, not zones --- .../objectstorage/HPCloudObjectStorageProviderMetadata.java | 4 ---- .../config/HPCloudObjectStorageRestClientModule.java | 4 ---- .../internal/HPCloudObjectStorageExpectTest.java | 3 ++- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageProviderMetadata.java b/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageProviderMetadata.java index e464575931..b221d52d99 100644 --- a/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageProviderMetadata.java +++ b/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageProviderMetadata.java @@ -18,8 +18,6 @@ */ package org.jclouds.hpcloud.objectstorage; -import static org.jclouds.Constants.PROPERTY_BUILD_VERSION; - import java.net.URI; import java.util.Properties; @@ -55,8 +53,6 @@ public class HPCloudObjectStorageProviderMetadata extends BaseProviderMetadata { public static Properties defaultProperties() { Properties properties = new Properties(); - properties.setProperty(PROPERTY_BUILD_VERSION, "???"); //FIXME -// properties.setProperty(PROPERTY_VCLOUD_DEFAULT_NETWORK, "orgNet-.*-External"); FIXME: needed? return properties; } diff --git a/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/config/HPCloudObjectStorageRestClientModule.java b/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/config/HPCloudObjectStorageRestClientModule.java index 1419e37b7a..a165856a0b 100644 --- a/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/config/HPCloudObjectStorageRestClientModule.java +++ b/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/config/HPCloudObjectStorageRestClientModule.java @@ -37,9 +37,7 @@ import org.jclouds.http.annotation.Redirection; import org.jclouds.http.annotation.ServerError; import org.jclouds.json.config.GsonModule.DateAdapter; import org.jclouds.json.config.GsonModule.Iso8601DateAdapter; -import org.jclouds.location.suppliers.ImplicitLocationSupplier; import org.jclouds.location.suppliers.RegionIdToURISupplier; -import org.jclouds.location.suppliers.implicit.OnlyLocationOrFirstZone; import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule; import org.jclouds.openstack.services.ServiceType; import org.jclouds.openstack.swift.CommonSwiftAsyncClient; @@ -54,7 +52,6 @@ import org.jclouds.rest.config.RestClientModule; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableMap; import com.google.inject.Provides; -import com.google.inject.Scopes; /** * @@ -81,7 +78,6 @@ public class HPCloudObjectStorageRestClientModule extends super.installLocations(); // TODO: select this from KeystoneProperties.VERSION; install(KeystoneAuthenticationModule.forRegions()); - bind(ImplicitLocationSupplier.class).to(OnlyLocationOrFirstZone.class).in(Scopes.SINGLETON); } @Override diff --git a/providers/hpcloud-objectstorage/src/test/java/org/jclouds/hpcloud/objectstorage/internal/HPCloudObjectStorageExpectTest.java b/providers/hpcloud-objectstorage/src/test/java/org/jclouds/hpcloud/objectstorage/internal/HPCloudObjectStorageExpectTest.java index 352efdeafb..87c9e4b926 100644 --- a/providers/hpcloud-objectstorage/src/test/java/org/jclouds/hpcloud/objectstorage/internal/HPCloudObjectStorageExpectTest.java +++ b/providers/hpcloud-objectstorage/src/test/java/org/jclouds/hpcloud/objectstorage/internal/HPCloudObjectStorageExpectTest.java @@ -51,7 +51,8 @@ public class HPCloudObjectStorageExpectTest extends BaseRestClientExpectTest locations = clientWhenServersExist.listAssignableLocations(); assertNotNull(locations); assertEquals(locations.size(), 1); - assertEquals(locations.iterator().next().getId(), "region-a.geo-1"); + // TODO: does this location make sense? + assertEquals(locations.iterator().next().getId(), "hpcloud-objectstorage"); } @Override From c43d89d6d8146b1265e24e207619641cbd2c0393 Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Fri, 11 May 2012 09:54:56 -0700 Subject: [PATCH 081/148] Use Objects.equal in ContentMetadataBuilder.equals Inspired by a discussion with @aledsage. --- .../jclouds/io/ContentMetadataBuilder.java | 35 ++++--------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/core/src/main/java/org/jclouds/io/ContentMetadataBuilder.java b/core/src/main/java/org/jclouds/io/ContentMetadataBuilder.java index 04980c475e..11694871c1 100644 --- a/core/src/main/java/org/jclouds/io/ContentMetadataBuilder.java +++ b/core/src/main/java/org/jclouds/io/ContentMetadataBuilder.java @@ -30,6 +30,7 @@ import org.jclouds.crypto.CryptoStreams; import org.jclouds.io.payloads.BaseImmutableContentMetadata; import org.jclouds.javax.annotation.Nullable; +import com.google.common.base.Objects; import com.google.common.base.Predicate; import com.google.common.collect.Multimap; @@ -145,34 +146,12 @@ public class ContentMetadataBuilder implements Serializable { if (getClass() != obj.getClass()) return false; ContentMetadataBuilder other = (ContentMetadataBuilder) obj; - if (contentDisposition == null) { - if (other.contentDisposition != null) - return false; - } else if (!contentDisposition.equals(other.contentDisposition)) - return false; - if (contentEncoding == null) { - if (other.contentEncoding != null) - return false; - } else if (!contentEncoding.equals(other.contentEncoding)) - return false; - if (contentLanguage == null) { - if (other.contentLanguage != null) - return false; - } else if (!contentLanguage.equals(other.contentLanguage)) - return false; - if (contentLength == null) { - if (other.contentLength != null) - return false; - } else if (!contentLength.equals(other.contentLength)) - return false; - if (!Arrays.equals(contentMD5, other.contentMD5)) - return false; - if (contentType == null) { - if (other.contentType != null) - return false; - } else if (!contentType.equals(other.contentType)) - return false; - return true; + return Objects.equal(contentDisposition, other.contentDisposition) && + Objects.equal(contentEncoding, other.contentEncoding) && + Objects.equal(contentLanguage, other.contentLanguage) && + Objects.equal(contentLength, other.contentLength) && + Arrays.equals(contentMD5, other.contentMD5) && + Objects.equal(contentType, other.contentType); } @Override From 191a4e73413fc9cc7b01d3e3c6d65f283c60efa9 Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Fri, 11 May 2012 13:38:30 -0700 Subject: [PATCH 082/148] Identity should be tenameName:accessKey tenantId is something else. Tested against hpcloud. --- .../java/org/jclouds/openstack/nova/v1_1/NovaApiMetadata.java | 2 +- .../v2_0/functions/AuthenticateApiAccessKeyCredentials.java | 2 +- .../jclouds/hpcloud/compute/HPCloudComputeProviderMetadata.java | 2 +- .../hpcloud/objectstorage/HPCloudObjectStorageApiMetadata.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaApiMetadata.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaApiMetadata.java index efe4e7a773..570fa8b7ce 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaApiMetadata.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaApiMetadata.java @@ -88,7 +88,7 @@ public class NovaApiMetadata extends BaseRestApiMetadata { super(NovaClient.class, NovaAsyncClient.class); id("openstack-nova") .name("OpenStack Nova Diablo+ API") - .identityName("tenantId:user") + .identityName("tenantName:accessKey") .credentialName("password") .documentation(URI.create("http://api.openstack.org/")) .version("1.1") diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/AuthenticateApiAccessKeyCredentials.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/AuthenticateApiAccessKeyCredentials.java index 29be1b1ded..68844857de 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/AuthenticateApiAccessKeyCredentials.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/AuthenticateApiAccessKeyCredentials.java @@ -41,7 +41,7 @@ public class AuthenticateApiAccessKeyCredentials implements Function>of(NovaRestClientModule.class, HPCloudComputeServiceContextModule.class)) .build()) diff --git a/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageApiMetadata.java b/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageApiMetadata.java index e39e556d6c..a990daedb7 100644 --- a/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageApiMetadata.java +++ b/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageApiMetadata.java @@ -83,7 +83,7 @@ public class HPCloudObjectStorageApiMetadata extends BaseRestApiMetadata { super(HPCloudObjectStorageClient.class, HPCloudObjectStorageAsyncClient.class); id("hpcloud-objectstorage") .name("HP Cloud Services Object Storage API") - .identityName("tenantId:accessKey") + .identityName("tenantName:accessKey") .credentialName("secretKey") .version("1.0") .documentation(URI.create("https://build.hpcloud.com/object-storage/api")) From f790607ffb825760ba3410f756603a654b4f1e17 Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Thu, 10 May 2012 15:59:39 +0100 Subject: [PATCH 083/148] openstack-nova: Adding field from Flavor Extra Data extension --- .../openstack/nova/v1_1/domain/Flavor.java | 78 ++++++++++++++++++- 1 file changed, 74 insertions(+), 4 deletions(-) diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Flavor.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Flavor.java index 12d37cc2a3..6462539625 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Flavor.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Flavor.java @@ -21,11 +21,13 @@ package org.jclouds.openstack.nova.v1_1.domain; import org.jclouds.openstack.domain.Resource; import com.google.common.base.Objects; +import com.google.common.base.Optional; +import com.google.gson.annotations.SerializedName; /** * A flavor is an available hardware configuration for a server. Each flavor has * a unique combination of disk space and memory capacity. - * + * * @author Jeremy Daggett * @see swap; + @SerializedName("rxtx_factor") + private final Optional rxtxFactor; + @SerializedName("OS-FLV-EXT-DATA:ephemeral") + private final Optional ephemeral; protected Flavor(Builder builder) { super(builder); this.ram = builder.ram; this.disk = builder.disk; this.vcpus = builder.vcpus; + this.swap = Optional.fromNullable(builder.swap); + this.rxtxFactor = Optional.fromNullable(builder.rxtxFactor); + this.ephemeral = Optional.fromNullable(builder.ephemeral); + } + + protected Flavor() { + this.swap = Optional.absent(); + this.rxtxFactor = Optional.absent(); + this.ephemeral = Optional.absent(); } public int getRam() { @@ -113,11 +160,34 @@ public class Flavor extends Resource { return this.vcpus; } + public Optional getSwap() { + return swap; + } + + public Optional getRxtxFactor() { + return rxtxFactor; + } + + /** + * Retrieves ephemeral disk space in GB + *

+ * NOTE: This field is only present if the Flavor Extra Data extension is installed (alias "OS-FLV-EXT-DATA"). + * + * @see org.jclouds.openstack.nova.v1_1.features.ExtensionClient#getExtensionByAlias + * @see org.jclouds.openstack.nova.v1_1.extensions.ExtensionNamespaces#FLAVOR_EXTRA_DATA + */ + public Optional getEphemeral() { + return ephemeral; + } + @Override protected Objects.ToStringHelper string() { return super.string() .add("ram", ram) .add("disk", disk) - .add("vcpus", vcpus); + .add("vcpus", vcpus) + .add("swap", swap) + .add("rxtxFactor", rxtxFactor) + .add("ephemeral", ephemeral); } -} +} \ No newline at end of file From 5228a14fbb7a2280d026349a4c23d8845816ed99 Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Fri, 11 May 2012 12:37:07 +0100 Subject: [PATCH 084/148] openstack-nova: Adding actions to HostAdministration extension --- .../HostAdministrationAsyncClient.java | 86 ++++++++++- .../extensions/HostAdministrationClient.java | 58 ++++++- .../functions/FieldValueResponseParsers.java | 89 +++++++++++ .../HostAdministrationClientExpectTest.java | 145 +++++++++++++++++- .../HostAdministrationClientLiveTest.java | 84 ++++++++-- .../v1_1/internal/BaseNovaExpectTest.java | 5 + 6 files changed, 441 insertions(+), 26 deletions(-) create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/FieldValueResponseParsers.java diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/HostAdministrationAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/HostAdministrationAsyncClient.java index 9e1819f52d..93c9e32eb2 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/HostAdministrationAsyncClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/HostAdministrationAsyncClient.java @@ -22,21 +22,31 @@ import java.util.Set; import javax.ws.rs.Consumes; import javax.ws.rs.GET; +import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.jclouds.openstack.filters.AuthenticateRequest; import org.jclouds.openstack.nova.v1_1.domain.Host; import org.jclouds.openstack.nova.v1_1.domain.HostResourceUsage; +import org.jclouds.openstack.nova.v1_1.functions.FieldValueResponseParsers.MaintenanceModeDisabledResponseParser; +import org.jclouds.openstack.nova.v1_1.functions.FieldValueResponseParsers.MaintenanceModeEnabledResponseParser; +import org.jclouds.openstack.nova.v1_1.functions.FieldValueResponseParsers.PowerIsRebootResponseParser; +import org.jclouds.openstack.nova.v1_1.functions.FieldValueResponseParsers.PowerIsShutdownResponseParser; +import org.jclouds.openstack.nova.v1_1.functions.FieldValueResponseParsers.PowerIsStartupResponseParser; +import org.jclouds.openstack.nova.v1_1.functions.FieldValueResponseParsers.StatusDisabledResponseParser; +import org.jclouds.openstack.nova.v1_1.functions.FieldValueResponseParsers.StatusEnabledResponseParser; import org.jclouds.openstack.services.Extension; import org.jclouds.openstack.services.ServiceType; import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.Payload; import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.ResponseParser; import org.jclouds.rest.annotations.SelectJson; import org.jclouds.rest.annotations.SkipEncoding; import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; -import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; import com.google.common.util.concurrent.ListenableFuture; @@ -45,7 +55,7 @@ import com.google.common.util.concurrent.ListenableFuture; *

* * @author Adam Lowe - * @see SimpleTenantUsageClient + * @see HostAdministrationClient * @see * @see * @see @@ -53,15 +63,15 @@ import com.google.common.util.concurrent.ListenableFuture; @Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.HOSTS) @SkipEncoding({'/', '='}) @RequestFilters(AuthenticateRequest.class) +@Path("/os-hosts") +@Consumes(MediaType.APPLICATION_JSON) public interface HostAdministrationAsyncClient { /** * @see HostAdministrationClient#listHosts() */ @GET - @Path("/os-hosts") @SelectJson("hosts") - @Consumes(MediaType.APPLICATION_JSON) @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) ListenableFuture> listHosts(); @@ -69,10 +79,72 @@ public interface HostAdministrationAsyncClient { * @see HostAdministrationClient#getHostResourceUsage(String) */ @GET - @Path("/os-hosts/{id}") + @Path("/{id}") @SelectJson("host") - @Consumes(MediaType.APPLICATION_JSON) - @ExceptionParser(ReturnNullOnNotFoundOr404.class) + @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) ListenableFuture> getHostResourceUsage(@PathParam("id") String hostId); + /** + * @see HostAdministrationClient#enableHost(String) + */ + @PUT + @Produces(MediaType.APPLICATION_JSON) + @Path("/{id}") + @Payload("{\"status\":\"enable\"}") + @ResponseParser(StatusEnabledResponseParser.class) + ListenableFuture enableHost(@PathParam("id") String hostId); + + /** + * @see HostAdministrationClient#disableHost(String) + */ + @PUT + @Produces(MediaType.APPLICATION_JSON) + @Path("/{id}") + @Payload("{\"status\":\"disable\"}") + @ResponseParser(StatusDisabledResponseParser.class) + ListenableFuture disableHost(@PathParam("id") String hostId); + + /** + * @see HostAdministrationClient#startHostMaintenance(String) + */ + @PUT + @Produces(MediaType.APPLICATION_JSON) + @Path("/{id}") + @Payload("{\"maintenance_mode\":\"enable\"}") + @ResponseParser(MaintenanceModeEnabledResponseParser.class) + ListenableFuture startHostMaintenance(@PathParam("id") String hostId); + + /** + * @see HostAdministrationClient#stopHostMaintenance(String) + */ + @PUT + @Produces(MediaType.APPLICATION_JSON) + @Path("/{id}") + @Payload("{\"maintenance_mode\":\"disable\"}") + @ResponseParser(MaintenanceModeDisabledResponseParser.class) + ListenableFuture stopHostMaintenance(@PathParam("id") String hostId); + + /** + * @see HostAdministrationClient#startupHost(String) + */ + @GET + @Path("/{id}/startup") + @ResponseParser(PowerIsStartupResponseParser.class) + ListenableFuture startupHost(@PathParam("id") String hostId); + + /** + * @see HostAdministrationClient#shutdownHost(String) + */ + @GET + @Path("/{id}/shutdown") + @ResponseParser(PowerIsShutdownResponseParser.class) + ListenableFuture shutdownHost(@PathParam("id") String hostId); + + /** + * @see HostAdministrationClient#rebootHost(String) + */ + @GET + @Path("/{id}/reboot") + @ResponseParser(PowerIsRebootResponseParser.class) + ListenableFuture rebootHost(@PathParam("id") String hostId); } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/HostAdministrationClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/HostAdministrationClient.java index fa23767b6b..1e43480a97 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/HostAdministrationClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/HostAdministrationClient.java @@ -21,8 +21,6 @@ package org.jclouds.openstack.nova.v1_1.extensions; import java.util.Set; import java.util.concurrent.TimeUnit; -import javax.ws.rs.PathParam; - import org.jclouds.concurrent.Timeout; import org.jclouds.openstack.nova.v1_1.domain.Host; import org.jclouds.openstack.nova.v1_1.domain.HostResourceUsage; @@ -32,7 +30,6 @@ import org.jclouds.openstack.services.ServiceType; /** * Provides asynchronous access to Host Administration features via the REST API. *

- * TODO reboot, shutdown, startup, update * * @author Adam Lowe * @see HostAdministrationAsyncClient @@ -43,14 +40,67 @@ public interface HostAdministrationClient { /** * Returns the list of hosts + * * @return the usage information */ Set listHosts(); /** * Retrieves the physical/usage resource on a specific host + * * @return the usage information */ - Set getHostResourceUsage(@PathParam("id") String hostId); + Set getHostResourceUsage(String hostId); + + /** + * Allow the specified host to accept new instances. + * + * @return true if successful + */ + Boolean enableHost(String hostId); + + /** + * Prevent the specified host from accepting new instances. + * + * @return true if successful + */ + Boolean disableHost(String hostId); + + /** + * Start host maintenance window. + *

+ * Note: this triggers guest VMs evacuation. + * + * @return true if successful + */ + Boolean startHostMaintenance(String hostId); + + /** + * Stop host maintenance window. + * + * @return true if successful + */ + Boolean stopHostMaintenance(String hostId); + + /** + * Startup a host. + * + * @return true if successful + */ + Boolean startupHost(String hostId); + + /** + * Shutdown a host. + * + * @return true if successful + */ + Boolean shutdownHost(String hostId); + + /** + * Reboot a host. + * + * @return true if successful + */ + Boolean rebootHost(String hostId); } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/FieldValueResponseParsers.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/FieldValueResponseParsers.java new file mode 100644 index 0000000000..e81636b9a8 --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/FieldValueResponseParsers.java @@ -0,0 +1,89 @@ +package org.jclouds.openstack.nova.v1_1.functions; + +import org.jclouds.http.HttpResponse; +import org.jclouds.http.functions.ParseFirstJsonValueNamed; +import org.jclouds.json.internal.GsonWrapper; + +import com.google.common.base.Function; +import com.google.common.base.Objects; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.google.inject.TypeLiteral; + +/** + * Parsers for extracting a single field value from a response and comparing it to an expected value + */ +public class FieldValueResponseParsers { + @Singleton + public static class StatusEnabledResponseParser extends FieldValueResponseParser { + @Inject + public StatusEnabledResponseParser(GsonWrapper wrapper) { + super(wrapper, "status", "enabled"); + } + } + + @Singleton + public static class StatusDisabledResponseParser extends FieldValueResponseParser { + @Inject + public StatusDisabledResponseParser(GsonWrapper wrapper) { + super(wrapper, "status", "disabled"); + } + } + + @Singleton + public static class MaintenanceModeEnabledResponseParser extends FieldValueResponseParser { + @Inject + public MaintenanceModeEnabledResponseParser(GsonWrapper wrapper) { + super(wrapper, "maintenance_mode", "on_maintenance"); + } + } + + @Singleton + public static class MaintenanceModeDisabledResponseParser extends FieldValueResponseParser { + @Inject + public MaintenanceModeDisabledResponseParser(GsonWrapper wrapper) { + super(wrapper, "maintenance_mode", "off_maintenance"); + } + } + + @Singleton + public static class PowerIsStartupResponseParser extends FieldValueResponseParser { + @Inject + public PowerIsStartupResponseParser(GsonWrapper wrapper) { + super(wrapper, "power_action", "startup"); + } + } + + @Singleton + public static class PowerIsShutdownResponseParser extends FieldValueResponseParser { + @Inject + public PowerIsShutdownResponseParser(GsonWrapper wrapper) { + super(wrapper, "power_action", "shutdown"); + } + } + + @Singleton + public static class PowerIsRebootResponseParser extends FieldValueResponseParser { + @Inject + public PowerIsRebootResponseParser(GsonWrapper wrapper) { + super(wrapper, "power_action", "reboot"); + } + } + + public static abstract class FieldValueResponseParser implements Function { + private final T expectedValue; + private final ParseFirstJsonValueNamed valueParser; + + public FieldValueResponseParser(GsonWrapper wrapper, String fieldName, T expectedValue) { + valueParser = new ParseFirstJsonValueNamed(wrapper, new TypeLiteral() { + }, fieldName); + this.expectedValue = expectedValue; + } + + @Override + public Boolean apply(HttpResponse response) { + return Objects.equal(expectedValue, valueParser.apply(response)); + } + } + +} \ No newline at end of file diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/HostAdministrationClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/HostAdministrationClientExpectTest.java index 0bf78b04c9..79e923bf11 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/HostAdministrationClientExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/HostAdministrationClientExpectTest.java @@ -19,6 +19,8 @@ package org.jclouds.openstack.nova.v1_1.extensions; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; import java.net.URI; import java.util.Set; @@ -30,6 +32,7 @@ import org.jclouds.http.HttpResponse; import org.jclouds.openstack.nova.v1_1.domain.Host; import org.jclouds.openstack.nova.v1_1.domain.HostResourceUsage; import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientExpectTest; +import org.jclouds.rest.ResourceNotFoundException; import org.testng.annotations.Test; import com.google.common.collect.ImmutableMultimap; @@ -37,7 +40,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; /** - * Tests HostAdministrationClient guice wiring and parsing + * Tests HostAdministrationClient guice wiring and parsing (including the Response parsers in FieldValueResponseParsers) * * @author Adam Lowe */ @@ -45,7 +48,7 @@ import com.google.common.collect.Iterables; public class HostAdministrationClientExpectTest extends BaseNovaClientExpectTest { - public void testList() throws Exception { + public void testList() { URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-hosts"); HostAdministrationClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -63,7 +66,7 @@ public class HostAdministrationClientExpectTest extends BaseNovaClientExpectTest assertEquals(host, expected); } - public void testGet() throws Exception { + public void testGet() { URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-hosts/xyz"); HostAdministrationClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, @@ -80,5 +83,141 @@ public class HostAdministrationClientExpectTest extends BaseNovaClientExpectTest assertEquals(client.getHostResourceUsage("xyz"), expected); } + + public void testEnableHost() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-hosts/ubuntu"); + HostAdministrationClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + HttpRequest.builder().method("PUT").headers(ImmutableMultimap.of("Accept", MediaType.APPLICATION_JSON, "X-Auth-Token", authToken)) + .payload(payloadFromStringWithContentType("{\"status\":\"enable\"}", MediaType.APPLICATION_JSON)) + .endpoint(endpoint).build(), + HttpResponse.builder().statusCode(200) + .payload(payloadFromStringWithContentType("{\"host\":\"ubuntu\",\"status\":\"enabled\"}", MediaType.APPLICATION_JSON)) + .build()).getHostAdministrationExtensionForZone("az-1.region-a.geo-1").get(); + assertTrue(client.enableHost("ubuntu")); + } + @Test(expectedExceptions = ResourceNotFoundException.class) + public void testEnableHostFailNotFound() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-hosts/ubuntu"); + HostAdministrationClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + HttpRequest.builder().method("PUT").headers(ImmutableMultimap.of("Accept", MediaType.APPLICATION_JSON, "X-Auth-Token", authToken)) + .payload(payloadFromStringWithContentType("{\"status\":\"enable\"}", MediaType.APPLICATION_JSON)) + .endpoint(endpoint).build(), + HttpResponse.builder().statusCode(404) + .build()).getHostAdministrationExtensionForZone("az-1.region-a.geo-1").get(); + client.enableHost("ubuntu"); + } + + public void testEnableHostFailNotEnabled() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-hosts/ubuntu"); + HostAdministrationClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + HttpRequest.builder().method("PUT").headers(ImmutableMultimap.of("Accept", MediaType.APPLICATION_JSON, "X-Auth-Token", authToken)) + .payload(payloadFromStringWithContentType("{\"status\":\"enable\"}", MediaType.APPLICATION_JSON)) + .endpoint(endpoint).build(), + HttpResponse.builder().statusCode(200) + .payload(payloadFromStringWithContentType("{\"host\":\"ubuntu\",\"status\":\"disabled\"}", MediaType.APPLICATION_JSON)) + .build()).getHostAdministrationExtensionForZone("az-1.region-a.geo-1").get(); + assertFalse(client.enableHost("ubuntu")); + } + + public void testDisableHost() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-hosts/ubuntu"); + HostAdministrationClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + HttpRequest.builder().method("PUT").headers(ImmutableMultimap.of("Accept", MediaType.APPLICATION_JSON, "X-Auth-Token", authToken)) + .payload(payloadFromStringWithContentType("{\"status\":\"disable\"}", MediaType.APPLICATION_JSON)) + .endpoint(endpoint).build(), + HttpResponse.builder().statusCode(200) + .payload(payloadFromStringWithContentType("{\"host\":\"ubuntu\",\"status\":\"disabled\"}", MediaType.APPLICATION_JSON)) + .build()).getHostAdministrationExtensionForZone("az-1.region-a.geo-1").get(); + assertTrue(client.disableHost("ubuntu")); + } + + public void testStartMaintenance() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-hosts/ubuntu"); + HostAdministrationClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + HttpRequest.builder().method("PUT").headers(ImmutableMultimap.of("Accept", MediaType.APPLICATION_JSON, "X-Auth-Token", authToken)) + .payload(payloadFromStringWithContentType("{\"maintenance_mode\":\"enable\"}", MediaType.APPLICATION_JSON)) + .endpoint(endpoint).build(), + HttpResponse.builder().statusCode(200) + .payload(payloadFromStringWithContentType("{\"host\":\"ubuntu\",\"maintenance_mode\":\"on_maintenance\"}", MediaType.APPLICATION_JSON)) + .build()).getHostAdministrationExtensionForZone("az-1.region-a.geo-1").get(); + assertTrue(client.startHostMaintenance("ubuntu")); + } + + public void testStopMaintenance() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-hosts/ubuntu"); + HostAdministrationClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + HttpRequest.builder().method("PUT").headers(ImmutableMultimap.of("Accept", MediaType.APPLICATION_JSON, "X-Auth-Token", authToken)) + .payload(payloadFromStringWithContentType("{\"maintenance_mode\":\"disable\"}", MediaType.APPLICATION_JSON)) + .endpoint(endpoint).build(), + HttpResponse.builder().statusCode(200) + .payload(payloadFromStringWithContentType("{\"host\":\"ubuntu\",\"maintenance_mode\":\"off_maintenance\"}", MediaType.APPLICATION_JSON)) + .build()).getHostAdministrationExtensionForZone("az-1.region-a.geo-1").get(); + assertTrue(client.stopHostMaintenance("ubuntu")); + } + + public void testStartupHost() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-hosts/ubuntu/startup"); + HostAdministrationClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + HttpRequest.builder().method("GET").headers(ImmutableMultimap.of("Accept", MediaType.APPLICATION_JSON, "X-Auth-Token", authToken)) + .endpoint(endpoint).build(), + HttpResponse.builder().statusCode(200) + .payload(payloadFromStringWithContentType("{\"host\":\"ubuntu\",\"power_action\":\"startup\"}", MediaType.APPLICATION_JSON)) + .build()).getHostAdministrationExtensionForZone("az-1.region-a.geo-1").get(); + assertTrue(client.startupHost("ubuntu")); + } + + @Test(expectedExceptions = ResourceNotFoundException.class) + public void testStartupHostFailNotFound() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-hosts/ubuntu/startup"); + HostAdministrationClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + HttpRequest.builder().method("GET").headers(ImmutableMultimap.of("Accept", MediaType.APPLICATION_JSON, "X-Auth-Token", authToken)) + .endpoint(endpoint).build(), + HttpResponse.builder().statusCode(404).build()).getHostAdministrationExtensionForZone("az-1.region-a.geo-1").get(); + assertTrue(client.startupHost("ubuntu")); + } + + public void testStartupHostFailWrongActionInProgress() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-hosts/ubuntu/startup"); + HostAdministrationClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + HttpRequest.builder().method("GET").headers(ImmutableMultimap.of("Accept", MediaType.APPLICATION_JSON, "X-Auth-Token", authToken)) + .endpoint(endpoint).build(), + HttpResponse.builder().statusCode(200) + .payload(payloadFromStringWithContentType("{\"host\":\"ubuntu\",\"power_action\":\"shutdown\"}", MediaType.APPLICATION_JSON)) + .build()).getHostAdministrationExtensionForZone("az-1.region-a.geo-1").get(); + assertFalse(client.startupHost("ubuntu")); + } + + public void testShutdownHost() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-hosts/ubuntu/shutdown"); + HostAdministrationClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + HttpRequest.builder().method("GET").headers(ImmutableMultimap.of("Accept", MediaType.APPLICATION_JSON, "X-Auth-Token", authToken)) + .endpoint(endpoint).build(), + HttpResponse.builder().statusCode(200) + .payload(payloadFromStringWithContentType("{\"host\":\"ubuntu\",\"power_action\":\"shutdown\"}", MediaType.APPLICATION_JSON)) + .build()).getHostAdministrationExtensionForZone("az-1.region-a.geo-1").get(); + assertTrue(client.shutdownHost("ubuntu")); + } + + public void testRebootHost() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-hosts/ubuntu/reboot"); + HostAdministrationClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + HttpRequest.builder().method("GET").headers(ImmutableMultimap.of("Accept", MediaType.APPLICATION_JSON, "X-Auth-Token", authToken)) + .endpoint(endpoint).build(), + HttpResponse.builder().statusCode(200) + .payload(payloadFromStringWithContentType("{\"host\":\"ubuntu\",\"power_action\":\"reboot\"}", MediaType.APPLICATION_JSON)) + .build()).getHostAdministrationExtensionForZone("az-1.region-a.geo-1").get(); + assertTrue(client.rebootHost("ubuntu")); + } } diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/HostAdministrationClientLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/HostAdministrationClientLiveTest.java index e79dd89891..5d563ab195 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/HostAdministrationClientLiveTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/HostAdministrationClientLiveTest.java @@ -20,39 +20,99 @@ package org.jclouds.openstack.nova.v1_1.extensions; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; import java.util.Set; import org.jclouds.openstack.nova.v1_1.domain.Host; import org.jclouds.openstack.nova.v1_1.domain.HostResourceUsage; import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientLiveTest; +import org.testng.annotations.BeforeGroups; import org.testng.annotations.Test; +import com.google.common.base.Objects; import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; /** * Tests behavior of HostAdministrationClient * * @author Adam Lowe */ -@Test(groups = "live", testName = "HostAdministrationClientLiveTest") +@Test(groups = "live", testName = "HostAdministrationClientLiveTest", singleThreaded = true) public class HostAdministrationClientLiveTest extends BaseNovaClientLiveTest { + private Optional optClient = Optional.absent(); + + Predicate isComputeHost = new Predicate() { + @Override + public boolean apply(Host input) { + return Objects.equal("compute", input.getService()); + } + }; + + @BeforeGroups(groups = {"integration", "live"}) + @Override + public void setupContext() { + super.setupContext(); + + if (identity.endsWith(":admin")) { + String zone = Iterables.getLast(novaContext.getApi().getConfiguredZones(), "nova"); + optClient = novaContext.getApi().getHostAdministrationExtensionForZone(zone); + } + } public void testListAndGet() throws Exception { - for (String zoneId : novaContext.getApi().getConfiguredZones()) { - Optional optClient = novaContext.getApi().getHostAdministrationExtensionForZone(zoneId); - if (optClient.isPresent() && identity.endsWith(":admin")) { - HostAdministrationClient client = optClient.get(); - Set hosts = client.listHosts(); - assertNotNull(hosts); - for(Host host : hosts) { - for (HostResourceUsage usage : client.getHostResourceUsage(host.getName())) { - assertEquals(usage.getHost(), host.getName()); - assertNotNull(usage); - } + if (optClient.isPresent()) { + HostAdministrationClient client = optClient.get(); + Set hosts = client.listHosts(); + assertNotNull(hosts); + for (Host host : hosts) { + for (HostResourceUsage usage : client.getHostResourceUsage(host.getName())) { + assertEquals(usage.getHost(), host.getName()); + assertNotNull(usage); } } } } + @Test(enabled = false) + public void testEnableDisable() throws Exception { + if (optClient.isPresent()) { + HostAdministrationClient client = optClient.get(); + Host host = Iterables.find(client.listHosts(), isComputeHost); + + assertTrue(client.disableHost(host.getName())); + assertTrue(client.enableHost(host.getName())); + } + } + + @Test(enabled = false) + public void testMaintenanceMode() throws Exception { + if (optClient.isPresent()) { + HostAdministrationClient client = optClient.get(); + Host host = Iterables.find(client.listHosts(), isComputeHost); + assertTrue(client.startHostMaintenance(host.getName())); + assertTrue(client.stopHostMaintenance(host.getName())); + } + } + + @Test(enabled = false) + public void testReboot() throws Exception { + if (optClient.isPresent()) { + HostAdministrationClient client = optClient.get(); + Host host = Iterables.find(client.listHosts(), isComputeHost); + assertTrue(client.rebootHost(host.getName())); + } + } + + @Test(enabled = false) + public void testShutdownAndStartup() throws Exception { + if (optClient.isPresent()) { + HostAdministrationClient client = optClient.get(); + Host host = Iterables.find(client.listHosts(), isComputeHost); + assertTrue(client.shutdownHost(host.getName())); + assertTrue(client.startupHost(host.getName())); + } + } } diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaExpectTest.java index 6212654523..dda5a43348 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaExpectTest.java @@ -89,4 +89,9 @@ public class BaseNovaExpectTest extends BaseRestClientExpectTest { protected HttpResponse.Builder standardResponseBuilder(int status) { return HttpResponse.builder().statusCode(status); } + + @Override + protected HttpRequestComparisonType compareHttpRequestAsType(HttpRequest input) { + return HttpRequestComparisonType.JSON; + } } From d5b2968a54f33a0ae84df9ae3065e5d537eb546c Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Fri, 11 May 2012 15:13:03 +0100 Subject: [PATCH 085/148] openstack-nova: Adding HostAggregates extension --- .../openstack/nova/v1_1/NovaAsyncClient.java | 17 +- .../openstack/nova/v1_1/NovaClient.java | 17 +- .../BindAggregateMetadataToJsonPayload.java | 39 +++ .../v1_1/binders/BindObjectToJsonPayload.java | 86 ++++++ .../v1_1/config/NovaRestClientModule.java | 20 +- .../nova/v1_1/domain/HostAggregate.java | 269 ++++++++++++++++++ .../v1_1/extensions/ExtensionNamespaces.java | 20 +- .../extensions/HostAggregateAsyncClient.java | 155 ++++++++++ .../v1_1/extensions/HostAggregateClient.java | 93 ++++++ ...paceEqualsAnyNamespaceInExtensionsSet.java | 12 +- .../HostAggregateClientExpectTest.java | 180 ++++++++++++ .../HostAggregateClientLiveTest.java | 150 ++++++++++ .../resources/host_aggregate_details.json | 1 + .../test/resources/host_aggregate_list.json | 1 + .../host_aggregate_with_host_details.json | 1 + 15 files changed, 1020 insertions(+), 41 deletions(-) create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindAggregateMetadataToJsonPayload.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindObjectToJsonPayload.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/HostAggregate.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/HostAggregateAsyncClient.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/HostAggregateClient.java create mode 100644 apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/HostAggregateClientExpectTest.java create mode 100644 apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/HostAggregateClientLiveTest.java create mode 100644 apis/openstack-nova/src/test/resources/host_aggregate_details.json create mode 100644 apis/openstack-nova/src/test/resources/host_aggregate_list.json create mode 100644 apis/openstack-nova/src/test/resources/host_aggregate_with_host_details.json diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java index 7a16d7c194..a3ea5134de 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java @@ -23,15 +23,7 @@ import java.util.Set; import org.jclouds.javax.annotation.Nullable; import org.jclouds.location.Zone; import org.jclouds.location.functions.ZoneToEndpoint; -import org.jclouds.openstack.nova.v1_1.extensions.AdminActionsAsyncClient; -import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPAsyncClient; -import org.jclouds.openstack.nova.v1_1.extensions.HostAdministrationAsyncClient; -import org.jclouds.openstack.nova.v1_1.extensions.KeyPairAsyncClient; -import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupAsyncClient; -import org.jclouds.openstack.nova.v1_1.extensions.ServerWithSecurityGroupsAsyncClient; -import org.jclouds.openstack.nova.v1_1.extensions.SimpleTenantUsageAsyncClient; -import org.jclouds.openstack.nova.v1_1.extensions.VirtualInterfaceAsyncClient; -import org.jclouds.openstack.nova.v1_1.extensions.VolumeAsyncClient; +import org.jclouds.openstack.nova.v1_1.extensions.*; import org.jclouds.openstack.nova.v1_1.features.ExtensionAsyncClient; import org.jclouds.openstack.nova.v1_1.features.FlavorAsyncClient; import org.jclouds.openstack.nova.v1_1.features.ImageAsyncClient; @@ -153,4 +145,11 @@ public interface NovaAsyncClient { Optional getAdminActionsExtensionForZone( @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + /** + * Provides asynchronous access to HostAggregate features. + */ + @Delegate + Optional getHostAggregateExtensionForZone( + @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java index 75d3173bbd..546a3b4809 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java @@ -25,15 +25,7 @@ import org.jclouds.concurrent.Timeout; import org.jclouds.javax.annotation.Nullable; import org.jclouds.location.Zone; import org.jclouds.location.functions.ZoneToEndpoint; -import org.jclouds.openstack.nova.v1_1.extensions.AdminActionsClient; -import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPClient; -import org.jclouds.openstack.nova.v1_1.extensions.HostAdministrationClient; -import org.jclouds.openstack.nova.v1_1.extensions.KeyPairClient; -import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupClient; -import org.jclouds.openstack.nova.v1_1.extensions.ServerWithSecurityGroupsClient; -import org.jclouds.openstack.nova.v1_1.extensions.SimpleTenantUsageClient; -import org.jclouds.openstack.nova.v1_1.extensions.VirtualInterfaceClient; -import org.jclouds.openstack.nova.v1_1.extensions.VolumeClient; +import org.jclouds.openstack.nova.v1_1.extensions.*; import org.jclouds.openstack.nova.v1_1.features.ExtensionClient; import org.jclouds.openstack.nova.v1_1.features.FlavorClient; import org.jclouds.openstack.nova.v1_1.features.ImageClient; @@ -153,5 +145,12 @@ public interface NovaClient { @Delegate Optional getAdminActionsExtensionForZone( @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + + /** + * Provides synchronous access to Aggregate features. + */ + @Delegate + Optional getHostAggregateExtensionForZone( + @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindAggregateMetadataToJsonPayload.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindAggregateMetadataToJsonPayload.java new file mode 100644 index 0000000000..f3c867f7c5 --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindAggregateMetadataToJsonPayload.java @@ -0,0 +1,39 @@ +/** + * 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.binders; + +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.json.Json; + +import com.google.inject.TypeLiteral; + +/** + * @author Adam Lowe + */ +@Singleton +public class BindAggregateMetadataToJsonPayload extends BindObjectToJsonPayload> { + @Inject + public BindAggregateMetadataToJsonPayload(Json jsonBinder) { + super(jsonBinder, "metadata", new TypeLiteral>(){}, "set_metadata"); + } +} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindObjectToJsonPayload.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindObjectToJsonPayload.java new file mode 100644 index 0000000000..4f4541b111 --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindObjectToJsonPayload.java @@ -0,0 +1,86 @@ +/** + * 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.binders; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Map; + +import org.jclouds.http.HttpRequest; +import org.jclouds.json.Json; +import org.jclouds.rest.MapBinder; +import org.jclouds.rest.binders.BindToJsonPayload; +import org.jclouds.rest.internal.GeneratedHttpRequest; + +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import com.google.common.collect.Iterables; +import com.google.inject.TypeLiteral; + +/** + * @author Adam Lowe + */ +public abstract class BindObjectToJsonPayload extends BindToJsonPayload implements MapBinder { + private final String fieldName; + private final String wrapperName; + private final TypeLiteral type; + + /** Bind a specific argument to the json payload + * + * @param fieldName the name of the output json field + * @param fieldType the type of the object to select from the method arguments + * @param wrapperName the name of the json field wrapper (if any) + */ + public BindObjectToJsonPayload(Json jsonBinder, String fieldName, TypeLiteral fieldType, String wrapperName) { + super(jsonBinder); + this.fieldName = fieldName; + this.wrapperName = wrapperName; + this.type = fieldType; + } + + public BindObjectToJsonPayload(Json jsonBinder, String fieldName, TypeLiteral fieldType) { + this(jsonBinder, fieldName, fieldType, null); + } + + @Override + public R bindToRequest(R request, Object toBind) { + throw new IllegalStateException("BindMapToJsonPayload needs parameters"); + } + + @SuppressWarnings("unchecked") + @Override + public R bindToRequest(R request, Map postParams) { + Builder payload = ImmutableMap.builder(); + payload.putAll(postParams); + checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, + "this binder is only valid for GeneratedHttpRequests!"); + GeneratedHttpRequest gRequest = (GeneratedHttpRequest) request; + + T specs = (T) Iterables.find(gRequest.getArgs(), Predicates.instanceOf(type.getRawType())); + payload.put(fieldName, specs); + + if (wrapperName != null) { + return super.bindToRequest(request, ImmutableMap.of(wrapperName, payload.build())); + } + + return super.bindToRequest(request, payload.build()); + } +} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java index b03338efcd..9e3d851a05 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java @@ -35,24 +35,7 @@ import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule; import org.jclouds.openstack.nova.v1_1.NovaAsyncClient; import org.jclouds.openstack.nova.v1_1.NovaClient; import org.jclouds.openstack.nova.v1_1.domain.Extension; -import org.jclouds.openstack.nova.v1_1.extensions.AdminActionsAsyncClient; -import org.jclouds.openstack.nova.v1_1.extensions.AdminActionsClient; -import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPAsyncClient; -import org.jclouds.openstack.nova.v1_1.extensions.FloatingIPClient; -import org.jclouds.openstack.nova.v1_1.extensions.HostAdministrationAsyncClient; -import org.jclouds.openstack.nova.v1_1.extensions.HostAdministrationClient; -import org.jclouds.openstack.nova.v1_1.extensions.KeyPairAsyncClient; -import org.jclouds.openstack.nova.v1_1.extensions.KeyPairClient; -import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupAsyncClient; -import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupClient; -import org.jclouds.openstack.nova.v1_1.extensions.ServerWithSecurityGroupsAsyncClient; -import org.jclouds.openstack.nova.v1_1.extensions.ServerWithSecurityGroupsClient; -import org.jclouds.openstack.nova.v1_1.extensions.SimpleTenantUsageAsyncClient; -import org.jclouds.openstack.nova.v1_1.extensions.SimpleTenantUsageClient; -import org.jclouds.openstack.nova.v1_1.extensions.VirtualInterfaceAsyncClient; -import org.jclouds.openstack.nova.v1_1.extensions.VirtualInterfaceClient; -import org.jclouds.openstack.nova.v1_1.extensions.VolumeAsyncClient; -import org.jclouds.openstack.nova.v1_1.extensions.VolumeClient; +import org.jclouds.openstack.nova.v1_1.extensions.*; import org.jclouds.openstack.nova.v1_1.features.ExtensionAsyncClient; import org.jclouds.openstack.nova.v1_1.features.ExtensionClient; import org.jclouds.openstack.nova.v1_1.features.FlavorAsyncClient; @@ -94,6 +77,7 @@ public class NovaRestClientModule extends RestClientModule builder() { + return new ConcreteBuilder(); + } + + public Builder toBuilder() { + return new ConcreteBuilder().fromAggregate(this); + } + + public static abstract class Builder> { + protected abstract T self(); + + private String id; + private String name; + private String availabilityZone; + private Set hosts = ImmutableSet.of(); + private String state; + private Date created = new Date(); + private Date updated; + private Map metadata = ImmutableMap.of(); + + /** + * @see HostAggregate#getId() + */ + public T id(String id) { + this.id = id; + return self(); + } + + /** + * @see HostAggregate#getName() + */ + public T name(String name) { + this.name = name; + return self(); + } + + /** + * @see HostAggregate#getAvailabilityZone() + */ + public T availabilityZone(String availabilityZone) { + this.availabilityZone = availabilityZone; + return self(); + } + + /** + * @see HostAggregate#getHosts() + */ + public T hosts(String... hosts) { + return hosts(ImmutableSet.copyOf(hosts)); + } + + /** + * @see HostAggregate#getHosts() + */ + public T hosts(Set hosts) { + this.hosts = hosts; + return self(); + } + + /** + * @see HostAggregate#getState() + */ + public T state(String state) { + this.state = state; + return self(); + } + + /** + * @see HostAggregate#getCreated() + */ + public T created(Date created) { + this.created = created; + return self(); + } + + /** + * @see HostAggregate#getUpdated() + */ + public T updated(Date updated) { + this.updated = updated; + return self(); + } + + /** + * @see HostAggregate#getMetadata() + */ + public T metadata(Map metadata) { + this.metadata = metadata; + return self(); + } + + public HostAggregate build() { + return new HostAggregate(this); + } + + public T fromAggregate(HostAggregate in) { + return this + .id(in.getId()) + .name(in.getName()) + .availabilityZone(in.getAvailabilityZone()) + .hosts(in.getHosts()) + .state(in.getState()) + .created(in.getCreated()) + .updated(in.getUpdated().orNull()) + .metadata(in.getMetadata()); + } + + } + + private static class ConcreteBuilder extends Builder { + @Override + protected ConcreteBuilder self() { + return this; + } + } + + private final String id; + private final String name; + @SerializedName(value = "availability_zone") + private final String availabilityZone; + private final Set hosts; + @SerializedName(value = "operational_state") + private final String state; + @SerializedName(value = "created_at") + private final Date created; + @SerializedName(value = "updated_at") + private final Optional updated; + private final Map metadata; + + protected HostAggregate(Builder builder) { + this.id = checkNotNull(builder.id, "id"); + this.name = checkNotNull(builder.name, "name"); + this.availabilityZone = checkNotNull(builder.availabilityZone, "availabilityZone"); + this.hosts = ImmutableSet.copyOf(checkNotNull(builder.hosts, "hosts")); + this.state = checkNotNull(builder.state, "state"); + this.created = checkNotNull(builder.created, "created"); + this.updated = Optional.fromNullable(builder.updated); + this.metadata = ImmutableMap.copyOf(checkNotNull(builder.metadata, "metadata")); + } + + // Ensure GSON parsed objects don't have null collections or optionals + protected HostAggregate() { + this.id = null; + this.name = null; + this.availabilityZone = null; + this.hosts = ImmutableSet.of(); + this.state = null; + this.created = null; + this.updated = Optional.absent(); + this.metadata = ImmutableMap.of(); + } + + public String getId() { + return this.id; + } + + public String getName() { + return this.name; + } + + /** + * note: an "Availability Zone" is different from a Nova "Zone" + * + * @return the availability zone this aggregate is in + */ + public String getAvailabilityZone() { + return this.availabilityZone; + } + + public Set getHosts() { + return Collections.unmodifiableSet(this.hosts); + } + + public String getState() { + return this.state; + } + + public Date getCreated() { + return this.created; + } + + public Optional getUpdated() { + return this.updated; + } + + public Map getMetadata() { + return this.metadata; + } + + @Override + public int hashCode() { + return Objects.hashCode(id, name, availabilityZone, hosts, state, created, updated, metadata); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + HostAggregate that = HostAggregate.class.cast(obj); + return Objects.equal(this.id, that.id) + && Objects.equal(this.name, that.name) + && Objects.equal(this.availabilityZone, that.availabilityZone) + && Objects.equal(this.hosts, that.hosts) + && Objects.equal(this.state, that.state) + && Objects.equal(this.created, that.created) + && Objects.equal(this.updated, that.updated) + && Objects.equal(this.metadata, that.metadata) + ; + } + + protected ToStringHelper string() { + return Objects.toStringHelper("") + .add("id", id) + .add("name", name) + .add("availabilityZone", availabilityZone) + .add("hosts", hosts) + .add("state", state) + .add("created", created) + .add("updated", updated) + .add("metadata", metadata); + } + + @Override + public String toString() { + return string().toString(); + } + +} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ExtensionNamespaces.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ExtensionNamespaces.java index 3c172cf9e3..d4a9e86147 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ExtensionNamespaces.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ExtensionNamespaces.java @@ -20,7 +20,7 @@ package org.jclouds.openstack.nova.v1_1.extensions; /** * Extension namespaces - * + * * @author Adrian Cole * @see */ @@ -93,6 +93,20 @@ public interface ExtensionNamespaces { /** * Extended Server Status extension */ - public static final String EXTENDED_STATUS = "http://docs.openstack.org/ext/extended_status/api/v1.1"; + public static final String EXTENDED_STATUS = "http://docs.openstack.org/compute/ext/extended_status/api/v1.1"; -} + /** + * Quota Classes extension + */ + public static final String QUOTA_CLASSES = "http://docs.openstack.org/ext/quota-classes-sets/api/v1.1"; + + /** + * Disk Config extension + */ + public static final String DISK_CONFIG = "http://docs.openstack.org/compute/ext/disk_config/api/v1.1"; + + /** + * Aggregates extension + */ + public static final String AGGREGATES = "http://docs.openstack.org/ext/aggregates/api/v1.1"; +} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/HostAggregateAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/HostAggregateAsyncClient.java new file mode 100644 index 0000000000..79cb209bba --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/HostAggregateAsyncClient.java @@ -0,0 +1,155 @@ +/** + * 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.extensions; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.openstack.filters.AuthenticateRequest; +import org.jclouds.openstack.nova.v1_1.binders.BindAggregateMetadataToJsonPayload; +import org.jclouds.openstack.nova.v1_1.domain.HostAggregate; +import org.jclouds.openstack.services.Extension; +import org.jclouds.openstack.services.ServiceType; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.MapBinder; +import org.jclouds.rest.annotations.Payload; +import org.jclouds.rest.annotations.PayloadParam; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.SelectJson; +import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; +import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404; +import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; + +import com.google.common.util.concurrent.ListenableFuture; + +/** + * Provide access to Aggregates in Nova. + * + * @author Adam Lowe + * @see HostAggregateClient + */ +@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.AGGREGATES) +@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS) +@RequestFilters(AuthenticateRequest.class) +@Path("/os-aggregates") +public interface HostAggregateAsyncClient { + + /** + * @see HostAggregateClient#listAggregates() + */ + @GET + @SelectJson("aggregates") + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) + ListenableFuture> listAggregates(); + + /** + * @see HostAggregateClient#getAggregate(String) + */ + @GET + @Path("/{id}") + @SelectJson("aggregate") + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getAggregate(@PathParam("id") String id); + + /** + * @see HostAggregateClient#createAggregate(String, String) + */ + @POST + @SelectJson("aggregate") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Payload("%7B\"aggregate\":%7B\"name\":\"{name}\",\"availability_zone\":\"{zone}\"%7D%7D") + ListenableFuture createAggregate(@PayloadParam("name") String name, @PayloadParam("zone") String availablityZone); + + /** + * @see HostAggregateClient#updateName + */ + @POST + @Path("/{id}") + @SelectJson("aggregate") + @Consumes(MediaType.APPLICATION_JSON) + @Payload("%7B\"aggregate\":%7B\"name\":\"{name}\"%7D%7D") + ListenableFuture updateName(@PathParam("id") String id, @PayloadParam("name") String name); + + /** + * @see HostAggregateClient#updateAvailabilityZone + */ + @POST + @Path("/{id}") + @SelectJson("aggregate") + @Consumes(MediaType.APPLICATION_JSON) + @Payload("%7B\"aggregate\":%7B\"availability_zone\":\"{zone}\"%7D%7D") + ListenableFuture updateAvailabilityZone(@PathParam("id") String id, @PayloadParam("zone") String availabilityZone); + + /** + * @see HostAggregateClient#deleteAggregate(String) + */ + @DELETE + @Path("/{id}") + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnFalseOnNotFoundOr404.class) + ListenableFuture deleteAggregate(@PathParam("id") String id); + + /** + * @see HostAggregateClient#addHost(String,String) + */ + @POST + @Path("/{id}/action") + @SelectJson("aggregate") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Payload("%7B\"add_host\":%7B\"host\":\"{host}\"%7D%7D") + ListenableFuture addHost(@PathParam("id") String id, @PayloadParam("host") String host); + + + /** + * @see HostAggregateClient#removeHost(String,String) + */ + @POST + @Path("/{id}/action") + @SelectJson("aggregate") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Payload("%7B\"remove_host\":%7B\"host\":\"{host}\"%7D%7D") + ListenableFuture removeHost(@PathParam("id") String id, @PayloadParam("host") String host); + + /** + * @see HostAggregateClient#setMetadata + */ + @POST + @Path("/{id}/action") + @SelectJson("aggregate") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @MapBinder(BindAggregateMetadataToJsonPayload.class) + ListenableFuture setMetadata(@PathParam("id") String id, Map metadata); +} diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/HostAggregateClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/HostAggregateClient.java new file mode 100644 index 0000000000..2548e2e301 --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/HostAggregateClient.java @@ -0,0 +1,93 @@ +/** + * 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.extensions; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.openstack.filters.AuthenticateRequest; +import org.jclouds.openstack.nova.v1_1.domain.HostAggregate; +import org.jclouds.openstack.services.Extension; +import org.jclouds.openstack.services.ServiceType; +import org.jclouds.rest.annotations.RequestFilters; + +/** + * Provide access to Host Aggregates in Nova (alias "OS-AGGREGATES") + * + * @author Adam Lowe + * @see HostAggregateAsyncClient + * @see + * @see + */ +@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.AGGREGATES) +@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS) +@RequestFilters(AuthenticateRequest.class) +public interface HostAggregateClient { + + /** + * @return the set of host aggregates. + */ + Set listAggregates(); + + /** + * Retrieves the details of an aggregate, hosts and metadata included. + * + * @return the details of the aggregate requested. + */ + HostAggregate getAggregate(String id); + + /** + * Creates an aggregate, given its name and availability zone. + * + * @return the newly created Aggregate + */ + HostAggregate createAggregate(String name, String availabilityZone); + + /** + * Updates the name of an aggregate. + */ + HostAggregate updateName(String id, String name); + + /** + * Updates the availability zone an aggregate. + */ + HostAggregate updateAvailabilityZone(String id, String availabilityZone); + + /** + * Removes an aggregate. + */ + Boolean deleteAggregate(String id); + + /** + * Adds a host to an aggregate + */ + HostAggregate addHost(String id, String host); + + /** + * Removes a host from an aggregate + */ + HostAggregate removeHost(String id, String host); + + /** + * Adds metadata to an aggregate + */ + HostAggregate setMetadata(String id, Map metadata); +} diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java index 3d023c397f..c5d3219083 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java @@ -74,8 +74,16 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio URI.create("http://docs.openstack.org/compute/ext/createserverext/api/v1.1")) .put(URI.create(ExtensionNamespaces.ADMIN_ACTIONS), URI.create("http://docs.openstack.org/compute/ext/admin-actions/api/v1.1")) - .put(URI.create(ExtensionNamespaces.EXTENDED_STATUS), - URI.create("http://docs.openstack.org/compute/ext/extended_status/api/v1.1")) + .put(URI.create(ExtensionNamespaces.AGGREGATES), + URI.create("http://docs.openstack.org/compute/ext/aggregates/api/v1.1")) + .put(URI.create(ExtensionNamespaces.FLAVOR_EXTRA_SPECS), + URI.create("http://docs.openstack.org/compute/ext/flavor_extra_specs/api/v1.1")) + .put(URI.create(ExtensionNamespaces.QUOTAS), + URI.create("http://docs.openstack.org/compute/ext/quotas-sets/api/v1.1")) + .put(URI.create(ExtensionNamespaces.QUOTA_CLASSES), + URI.create("http://docs.openstack.org/compute/ext/quota-classes-sets/api/v1.1")) + .put(URI.create(ExtensionNamespaces.VOLUME_TYPES), + URI.create("http://docs.openstack.org/compute/ext/volume_types/api/v1.1")) .build(); @Inject diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/HostAggregateClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/HostAggregateClientExpectTest.java new file mode 100644 index 0000000000..55ea9d6dc7 --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/HostAggregateClientExpectTest.java @@ -0,0 +1,180 @@ +/** + * 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.extensions; + +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 java.net.URI; + +import javax.ws.rs.core.MediaType; + +import org.jclouds.date.DateService; +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.openstack.nova.v1_1.domain.HostAggregate; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientExpectTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; + +/** + * Tests HostAggregateClient guice wiring and parsing + * + * @author Adam Lowe + */ +@Test(groups = "unit", testName = "HostAggregateClientExpectTest") +public class HostAggregateClientExpectTest extends BaseNovaClientExpectTest { + private DateService dateService = new SimpleDateFormatDateService(); + + public void testList() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates"); + HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(200).payload(payloadFromResource("/host_aggregate_list.json")).build()) + .getHostAggregateExtensionForZone("az-1.region-a.geo-1").get(); + + HostAggregate result = Iterables.getOnlyElement(client.listAggregates()); + assertEquals(result, exampleHostAggregate()); + } + + public void testGet() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates/1"); + HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(200).payload(payloadFromResource("/host_aggregate_with_host_details.json")).build()) + .getHostAggregateExtensionForZone("az-1.region-a.geo-1").get(); + + assertEquals(client.getAggregate("1"), exampleHostAggregateWithHost()); + } + + public void testGetFailNotFound() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates/1"); + HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(404).build()).getHostAggregateExtensionForZone("az-1.region-a.geo-1").get(); + + assertNull(client.getAggregate("1")); + } + + public void testCreateAggregate() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates"); + HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).method("POST") + .payload(payloadFromStringWithContentType("{\"aggregate\":{\"name\":\"ubuntu1\",\"availability_zone\":\"nova\"}}", MediaType.APPLICATION_JSON)) + .endpoint(endpoint).build(), + standardResponseBuilder(200).payload(payloadFromResource("/host_aggregate_details.json")).build()) + .getHostAggregateExtensionForZone("az-1.region-a.geo-1").get(); + + assertEquals(client.createAggregate("ubuntu1", "nova"), exampleHostAggregate()); + } + + public void testDeleteAggregate() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates/1"); + HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).method("DELETE").build(), + standardResponseBuilder(200).build()).getHostAggregateExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.deleteAggregate("1")); + } + + public void testDeleteAggregateFailNotFound() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates/1"); + HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).method("DELETE").build(), + standardResponseBuilder(404).build()).getHostAggregateExtensionForZone("az-1.region-a.geo-1").get(); + + assertFalse(client.deleteAggregate("1")); + } + + public void testUpdateName() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates/1"); + HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).method("POST") + .payload(payloadFromStringWithContentType("{\"aggregate\":{\"name\":\"newaggregatename\"}}", MediaType.APPLICATION_JSON)).build(), + standardResponseBuilder(200).payload(payloadFromResource("/host_aggregate_details.json")).build()).getHostAggregateExtensionForZone("az-1.region-a.geo-1").get(); + + assertEquals(client.updateName("1", "newaggregatename"), exampleHostAggregate()); + } + + public void testUpdateAvailabilityZone() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates/1"); + HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).method("POST") + .payload(payloadFromStringWithContentType("{\"aggregate\":{\"availability_zone\":\"zone1\"}}", MediaType.APPLICATION_JSON)).build(), + standardResponseBuilder(200).payload(payloadFromResource("/host_aggregate_details.json")).build()).getHostAggregateExtensionForZone("az-1.region-a.geo-1").get(); + + assertEquals(client.updateAvailabilityZone("1", "zone1"), exampleHostAggregate()); + } + + public void testAddHost() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates/1/action"); + HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).method("POST") + .payload(payloadFromStringWithContentType("{\"add_host\":{\"host\":\"ubuntu\"}}", MediaType.APPLICATION_JSON)).build(), + standardResponseBuilder(200).payload(payloadFromResource("/host_aggregate_details.json")).build()).getHostAggregateExtensionForZone("az-1.region-a.geo-1").get(); + + assertEquals(client.addHost("1", "ubuntu"), exampleHostAggregate()); + } + + public void testRemoveHost() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates/1/action"); + HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).method("POST") + .payload(payloadFromStringWithContentType("{\"remove_host\":{\"host\":\"ubuntu\"}}", MediaType.APPLICATION_JSON)).build(), + standardResponseBuilder(200).payload(payloadFromResource("/host_aggregate_details.json")).build()).getHostAggregateExtensionForZone("az-1.region-a.geo-1").get(); + + assertEquals(client.removeHost("1", "ubuntu"), exampleHostAggregate()); + } + + + public void testSetMetadata() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-aggregates/1/action"); + HostAggregateClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).method("POST") + .payload(payloadFromStringWithContentType("{\"set_metadata\":{\"metadata\":{\"mykey\":\"some value or other\"}}}", MediaType.APPLICATION_JSON)).build(), + standardResponseBuilder(200).payload(payloadFromResource("/host_aggregate_details.json")).build()).getHostAggregateExtensionForZone("az-1.region-a.geo-1").get(); + + assertEquals(client.setMetadata("1", ImmutableMap.of("mykey", "some value or other")), exampleHostAggregate()); + } + + public HostAggregate exampleHostAggregate() { + return HostAggregate.builder().name("jclouds-test-a").availabilityZone("nova") + .created(dateService.iso8601SecondsDateParse("2012-05-11 11:40:17")) + .updated(dateService.iso8601SecondsDateParse("2012-05-11 11:46:44")) + .state("created").id("1").metadata(ImmutableMap.of("somekey", "somevalue", "anotherkey", "another val")).build(); + } + + public HostAggregate exampleHostAggregateWithHost() { + return exampleHostAggregate().toBuilder().hosts("ubuntu").build(); + } +} diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/HostAggregateClientLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/HostAggregateClientLiveTest.java new file mode 100644 index 0000000000..f81c7e6b1b --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/HostAggregateClientLiveTest.java @@ -0,0 +1,150 @@ +/** + * 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.extensions; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.util.Map; +import java.util.Set; + +import org.jclouds.openstack.nova.v1_1.domain.Host; +import org.jclouds.openstack.nova.v1_1.domain.HostAggregate; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientLiveTest; +import org.testng.annotations.AfterGroups; +import org.testng.annotations.BeforeGroups; +import org.testng.annotations.Test; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; + +/** + * Tests behavior of AggregateClient + * + * @author Adam Lowe + */ +@Test(groups = "live", testName = "AggregateClientLiveTest", singleThreaded = true) +public class HostAggregateClientLiveTest extends BaseNovaClientLiveTest { + private Optional clientOption; + private Optional hostAdminOption; + + private HostAggregate testAggregate; + + @BeforeGroups(groups = {"integration", "live"}) + @Override + public void setupContext() { + super.setupContext(); + String zone = Iterables.getLast(novaContext.getApi().getConfiguredZones(), "nova"); + clientOption = novaContext.getApi().getHostAggregateExtensionForZone(zone); + hostAdminOption = novaContext.getApi().getHostAdministrationExtensionForZone(zone); + } + + @Override + @AfterGroups(groups = {"integration", "live"}) + public void tearDown() { + if (testAggregate != null) { + assertTrue(clientOption.get().deleteAggregate(testAggregate.getId())); + } + super.tearDown(); + } + + public void testCreateAggregate() { + if (clientOption.isPresent()) { + // TODO assuming "nova" availability zone is present + testAggregate = clientOption.get().createAggregate("jclouds-test-a", "nova"); + } + } + + @Test(dependsOnMethods = "testCreateAggregate") + public void testListAndGetAggregate() { + if (clientOption.isPresent()) { + HostAggregateClient client = clientOption.get(); + Set aggregates = client.listAggregates(); + for (HostAggregate aggregate : aggregates) { + assertNotNull(aggregate.getId()); + assertNotNull(aggregate.getName()); + assertNotNull(aggregate.getAvailabilityZone()); + + HostAggregate details = client.getAggregate(aggregate.getId()); + assertEquals(details.getId(), aggregate.getId()); + assertEquals(details.getName(), aggregate.getName()); + assertEquals(details.getAvailabilityZone(), aggregate.getAvailabilityZone()); + assertEquals(details.getHosts(), aggregate.getHosts()); + } + } + } + + @Test(dependsOnMethods = "testCreateAggregate") + public void testModifyMetadata() { + if (clientOption.isPresent()) { + HostAggregateClient client = clientOption.get(); + for (Map theMetaData : ImmutableSet.of( + ImmutableMap.of("somekey", "somevalue"), + ImmutableMap.of("somekey", "some other value", "anotherkey", "another val") + )) { + // Apply changes + HostAggregate details = client.setMetadata(testAggregate.getId(), theMetaData); + + // bug in openstack - metadata values are never removed, so we just checking what we've set + for (String key : theMetaData.keySet()) { + assertEquals(details.getMetadata().get(key), theMetaData.get(key)); + } + + // Re-fetch to double-check + details = client.getAggregate(testAggregate.getId()); + for (String key : theMetaData.keySet()) { + assertEquals(details.getMetadata().get(key), theMetaData.get(key)); + } + } + } + } + + // Note the host will be added, but cannot remove it til + @Test(enabled = false, dependsOnMethods = "testCreateAggregate") + public void testModifyHosts() { + if (clientOption.isPresent() && hostAdminOption.isPresent()) { + HostAggregateClient client = clientOption.get(); + Host host = Iterables.getFirst(hostAdminOption.get().listHosts(), null); + assertNotNull(host); + + String host_id = host.getName(); + assertNotNull(host_id); + HostAggregate details; + + try { + details = client.addHost(testAggregate.getId(), host_id); + + assertEquals(details.getHosts(), ImmutableSet.of(host_id)); + + // re-fetch to double-check + details = client.getAggregate(testAggregate.getId()); + assertEquals(details.getHosts(), ImmutableSet.of(host_id)); + + // TODO wait until status of aggregate isn't CHANGING (hostAdministration.shutdown?) + } finally { + details = client.removeHost(testAggregate.getId(), host_id); + } + + assertEquals(details.getHosts(), ImmutableSet.of()); + } + } +} diff --git a/apis/openstack-nova/src/test/resources/host_aggregate_details.json b/apis/openstack-nova/src/test/resources/host_aggregate_details.json new file mode 100644 index 0000000000..5e939ddab0 --- /dev/null +++ b/apis/openstack-nova/src/test/resources/host_aggregate_details.json @@ -0,0 +1 @@ +{"aggregate": {"name": "jclouds-test-a", "availability_zone": "nova", "deleted": false, "created_at": "2012-05-11 11:40:17", "updated_at": "2012-05-11 11:46:44", "operational_state": "created", "hosts": [], "deleted_at": null, "id": 1, "metadata": {"somekey": "somevalue", "anotherkey": "another val"}} \ No newline at end of file diff --git a/apis/openstack-nova/src/test/resources/host_aggregate_list.json b/apis/openstack-nova/src/test/resources/host_aggregate_list.json new file mode 100644 index 0000000000..92dfd383f1 --- /dev/null +++ b/apis/openstack-nova/src/test/resources/host_aggregate_list.json @@ -0,0 +1 @@ +{"aggregates": [{"name": "jclouds-test-a", "availability_zone": "nova", "deleted": false, "created_at": "2012-05-11 11:40:17", "updated_at": "2012-05-11 11:46:44", "operational_state": "created", "hosts": [], "deleted_at": null, "id": 1, "metadata": {"somekey": "somevalue", "anotherkey": "another val"}}]} \ No newline at end of file diff --git a/apis/openstack-nova/src/test/resources/host_aggregate_with_host_details.json b/apis/openstack-nova/src/test/resources/host_aggregate_with_host_details.json new file mode 100644 index 0000000000..2d132f4698 --- /dev/null +++ b/apis/openstack-nova/src/test/resources/host_aggregate_with_host_details.json @@ -0,0 +1 @@ +{"aggregate": {"name": "jclouds-test-a", "availability_zone": "nova", "deleted": false, "created_at": "2012-05-11 11:40:17", "updated_at": "2012-05-11 11:46:44", "operational_state": "created", "hosts": ["ubuntu"], "deleted_at": null, "id": 1, "metadata": {"somekey": "somevalue", "anotherkey": "another val"}} \ No newline at end of file From 7f101267e661fe6e128e290b2cf2e3e69cce9547 Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Fri, 11 May 2012 15:18:11 +0100 Subject: [PATCH 086/148] openstack-nova: Adding Flavor Extra Specs extension --- .../openstack/nova/v1_1/NovaAsyncClient.java | 7 + .../openstack/nova/v1_1/NovaClient.java | 9 +- .../binders/BindExtraSpecsToJsonPayload.java | 39 +++++ .../v1_1/config/NovaRestClientModule.java | 1 + .../FlavorExtraSpecsAsyncClient.java | 115 ++++++++++++++ .../extensions/FlavorExtraSpecsClient.java | 83 ++++++++++ .../FlavorExtraSpecsClientExpectTest.java | 143 ++++++++++++++++++ .../FlavorExtraSpecsClientLiveTest.java | 125 +++++++++++++++ 8 files changed, 521 insertions(+), 1 deletion(-) create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindExtraSpecsToJsonPayload.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FlavorExtraSpecsAsyncClient.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FlavorExtraSpecsClient.java create mode 100644 apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FlavorExtraSpecsClientExpectTest.java create mode 100644 apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FlavorExtraSpecsClientLiveTest.java diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java index a3ea5134de..76081507fd 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java @@ -152,4 +152,11 @@ public interface NovaAsyncClient { Optional getHostAggregateExtensionForZone( @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + /** + * Provides asynchronous access to Flavor extra specs features. + */ + @Delegate + Optional getFlavorExtraSpecsExtensionForZone( + @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java index 546a3b4809..8177650af8 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java @@ -152,5 +152,12 @@ public interface NovaClient { @Delegate Optional getHostAggregateExtensionForZone( @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); - + + /** + * Provides synchronous access to Flavor extra specs features. + */ + @Delegate + Optional getFlavorExtraSpecsExtensionForZone( + @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindExtraSpecsToJsonPayload.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindExtraSpecsToJsonPayload.java new file mode 100644 index 0000000000..3eaac00c9e --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindExtraSpecsToJsonPayload.java @@ -0,0 +1,39 @@ +/** + * 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.binders; + +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.json.Json; + +import com.google.inject.TypeLiteral; + +/** + * @author Adam Lowe + */ +@Singleton +public class BindExtraSpecsToJsonPayload extends BindObjectToJsonPayload> { + @Inject + public BindExtraSpecsToJsonPayload(Json jsonBinder) { + super(jsonBinder, "extra_specs", new TypeLiteral>(){}); + } +} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java index 9e3d851a05..9f2cbcbc99 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java @@ -78,6 +78,7 @@ public class NovaRestClientModule extends RestClientModule + * @see org.jclouds.openstack.nova.v1_1.features.FlavorClient + * @see FlavorExtraSpecsClient + */ +@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.FLAVOR_EXTRA_SPECS) +@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS) +@RequestFilters(AuthenticateRequest.class) +@Consumes(MediaType.APPLICATION_JSON) +public interface FlavorExtraSpecsAsyncClient { + + /** + * @see FlavorExtraSpecsClient#getAllExtraSpecs(String) + */ + @GET + @SelectJson("extra_specs") + @Path("/flavors/{flavor_id}/os-extra_specs") + @ExceptionParser(ReturnEmptyMapOnNotFoundOr404.class) + ListenableFuture> getAllExtraSpecs(@PathParam("flavor_id") String flavorId); + + /** + * @see FlavorExtraSpecsClient#setExtraSpec(String, String, String) + */ + @POST + @Path("/flavors/{flavor_id}/os-extra_specs") + @Produces(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnFalseOnNotFoundOr404.class) + @MapBinder(BindExtraSpecsToJsonPayload.class) + ListenableFuture setAllExtraSpecs(@PathParam("flavor_id") String flavorId, Map specs); + + /** + * @see FlavorExtraSpecsClient#getExtraSpec(String, String) + */ + @GET + @Path("/flavors/{flavor_id}/os-extra_specs/{key}") + @Unwrap + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getExtraSpec(@PathParam("flavor_id") String flavorId, @PathParam("key") String key); + + /** + * @see FlavorExtraSpecsClient#setExtraSpec(String, String, String) + */ + @PUT + @Path("/flavors/{flavor_id}/os-extra_specs/{key}") + @Produces(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnFalseOnNotFoundOr404.class) + @Payload("%7B\"{key}\":\"{value}\"%7D") + ListenableFuture setExtraSpec(@PathParam("flavor_id") String flavorId, + @PathParam("key") @PayloadParam("key") String key, + @PayloadParam("value") String value); + + /** + * @see FlavorExtraSpecsClient#deleteExtraSpec(String, String) + */ + @DELETE + @Path("/flavors/{flavor_id}/os-extra_specs/{key}") + @ExceptionParser(ReturnFalseOnNotFoundOr404.class) + ListenableFuture deleteExtraSpec(@PathParam("flavor_id") String flavorId, + @PathParam("key") String key); + +} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FlavorExtraSpecsClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FlavorExtraSpecsClient.java new file mode 100644 index 0000000000..01f0fb0847 --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FlavorExtraSpecsClient.java @@ -0,0 +1,83 @@ +/** + * 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.extensions; + +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.openstack.filters.AuthenticateRequest; +import org.jclouds.openstack.services.Extension; +import org.jclouds.openstack.services.ServiceType; +import org.jclouds.rest.annotations.RequestFilters; + +/** + * Provide access to extra metadata for Nova flavors. + * + * @author Adam Lowe + * @see + * @see org.jclouds.openstack.nova.v1_1.features.FlavorClient + * @see org.jclouds.openstack.nova.v1_1.extensions.FlavorExtraSpecsAsyncClient + */ +@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.FLAVOR_EXTRA_SPECS) +@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS) +@RequestFilters(AuthenticateRequest.class) +public interface FlavorExtraSpecsClient { + + /** + * Retrieve all extra specs for a flavor + * + * @return the set of extra metadata for the flavor + */ + Map getAllExtraSpecs(String flavorId); + + /** + * Creates or updates the extra specs for a given flavor + * + * @param flavorId the id of the flavor to modify + * @param specs the extra specs to apply + */ + Boolean setAllExtraSpecs(String flavorId, Map specs); + + /** + * Return a single extra spec value + * + * @param flavorId the id of the flavor to modify + * @param key the extra spec key to retrieve + */ + String getExtraSpec(String flavorId, String key); + + /** + * Creates or updates a single extra spec value + * + * @param flavorId the id of the flavor to modify + * @param key the extra spec key (when creating ensure this does not include whitespace or other difficult characters) + * @param value the value to associate with the key + */ + Boolean setExtraSpec(String flavorId, String key, String value); + + /** + * Deletes an extra spec + * + * @param flavorId the id of the flavor to modify + * @param key the extra spec key to delete + */ + Boolean deleteExtraSpec(String flavorId, String key); + +} \ No newline at end of file diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FlavorExtraSpecsClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FlavorExtraSpecsClientExpectTest.java new file mode 100644 index 0000000000..104b222757 --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FlavorExtraSpecsClientExpectTest.java @@ -0,0 +1,143 @@ +/** + * 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.extensions; + +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 java.net.URI; + +import javax.ws.rs.core.MediaType; + +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientExpectTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; + +/** + * Tests guice wiring and parsing of FlavorExtraSpecsClient + * + * @author Adam Lowe + */ +@Test(groups = "unit", testName = "FlavorExtraSpecsClientExpectTest") +public class FlavorExtraSpecsClientExpectTest extends BaseNovaClientExpectTest { + + public void testGetAllExtraSpecs() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/flavors/9/os-extra_specs"); + FlavorExtraSpecsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(200).payload(payloadFromResource("/volume_type_extra_specs.json")).build() + ).getFlavorExtraSpecsExtensionForZone("az-1.region-a.geo-1").get(); + + assertEquals(client.getAllExtraSpecs("9"), ImmutableMap.of("test", "value1")); + } + + public void testGetAllExtraSpecsFailNotFound() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/flavors/9/os-extra_specs"); + FlavorExtraSpecsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(404).build() + ).getFlavorExtraSpecsExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.getAllExtraSpecs("9").isEmpty()); + } + + public void testSetAllExtraSpecs() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/flavors/9/os-extra_specs"); + FlavorExtraSpecsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint) + .method("POST") + .payload(payloadFromStringWithContentType("{\"extra_specs\":{\"test1\":\"somevalue\"}}", MediaType.APPLICATION_JSON)).build(), + standardResponseBuilder(200).build() + ).getFlavorExtraSpecsExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.setAllExtraSpecs("9", ImmutableMap.of("test1", "somevalue"))); + } + + public void testSetExtraSpec() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/flavors/5/os-extra_specs/test1"); + FlavorExtraSpecsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint) + .method("PUT") + .payload(payloadFromStringWithContentType("{\"test1\":\"somevalue\"}", MediaType.APPLICATION_JSON)).build(), + standardResponseBuilder(200).build() + ).getFlavorExtraSpecsExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.setExtraSpec("5", "test1", "somevalue")); + } + + public void testGetExtraSpec() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/flavors/5/os-extra_specs/test1"); + FlavorExtraSpecsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(200).payload(payloadFromStringWithContentType("{\"test1\":\"another value\"}", MediaType.APPLICATION_JSON)).build() + ).getFlavorExtraSpecsExtensionForZone("az-1.region-a.geo-1").get(); + + assertEquals(client.getExtraSpec("5", "test1"), "another value"); + } + + public void testGetExtraSpecFailNotFound() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/flavors/5/os-extra_specs/test1"); + FlavorExtraSpecsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(404).build() + ).getFlavorExtraSpecsExtensionForZone("az-1.region-a.geo-1").get(); + + assertNull(client.getExtraSpec("5", "test1")); + } + + public void testDeleteExtraSpec() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/flavors/5/os-extra_specs/test1"); + FlavorExtraSpecsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).method("DELETE").build(), + standardResponseBuilder(200).build() + ).getFlavorExtraSpecsExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.deleteExtraSpec("5", "test1")); + } + + public void testDeleteExtraSpecFailNotFound() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/flavors/5/os-extra_specs/test1"); + FlavorExtraSpecsClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).method("DELETE").build(), + standardResponseBuilder(404).build() + ).getFlavorExtraSpecsExtensionForZone("az-1.region-a.geo-1").get(); + + assertFalse(client.deleteExtraSpec("5", "test1")); + } + +} diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FlavorExtraSpecsClientLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FlavorExtraSpecsClientLiveTest.java new file mode 100644 index 0000000000..1408af82e8 --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FlavorExtraSpecsClientLiveTest.java @@ -0,0 +1,125 @@ +/** + * 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.extensions; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.util.Map; + +import org.jclouds.openstack.domain.Resource; +import org.jclouds.openstack.nova.v1_1.features.FlavorClient; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientLiveTest; +import org.testng.annotations.AfterGroups; +import org.testng.annotations.BeforeGroups; +import org.testng.annotations.Test; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; + +/** + * Tests behavior of FlavorExtraSpecsClient + * + * @author Adam Lowe + */ +@Test(groups = "live", testName = "FlavorExtraSpecsClientLiveTest", singleThreaded = true) +public class FlavorExtraSpecsClientLiveTest extends BaseNovaClientLiveTest { + private FlavorClient flavorClient; + private Optional clientOption; + private String zone; + + private Resource testFlavor; + private Map testSpecs = ImmutableMap.of("jclouds-test", "some data", "jclouds-test2", "more data!"); + + @BeforeGroups(groups = { "integration", "live" }) + @Override + public void setupContext() { + super.setupContext(); + zone = Iterables.getLast(novaContext.getApi().getConfiguredZones(), "nova"); + flavorClient = novaContext.getApi().getFlavorClientForZone(zone); + clientOption = novaContext.getApi().getFlavorExtraSpecsExtensionForZone(zone); + } + + @AfterGroups(groups = "live") + @Override + public void tearDown() { + if (clientOption.isPresent() && testFlavor != null) { + for(String key : testSpecs.keySet()) { + assertTrue(clientOption.get().deleteExtraSpec(testFlavor.getId(), key)); + } + } + super.tearDown(); + } + + public void testCreateExtraSpecs() { + if (clientOption.isPresent()) { + FlavorExtraSpecsClient client = clientOption.get(); + testFlavor = Iterables.getLast(flavorClient.listFlavors()); + Map before = client.getAllExtraSpecs(testFlavor.getId()); + assertNotNull(before); + Map specs = Maps.newHashMap(before); + specs.putAll(testSpecs); + assertTrue(client.setAllExtraSpecs(testFlavor.getId(), specs)); + assertEquals(client.getAllExtraSpecs(testFlavor.getId()), specs); + for (Map.Entry entry : specs.entrySet()) { + assertEquals(client.getExtraSpec(testFlavor.getId(), entry.getKey()), entry.getValue()); + } + } + } + + @Test(dependsOnMethods = "testCreateExtraSpecs") + public void testListExtraSpecs() { + if (clientOption.isPresent()) { + FlavorExtraSpecsClient client = clientOption.get(); + for (String key : testSpecs.keySet()) { + assertTrue(client.getAllExtraSpecs(testFlavor.getId()).containsKey(key)); + } + for (Resource flavor : flavorClient.listFlavors()) { + Map specs = client.getAllExtraSpecs(flavor.getId()); + assertNotNull(specs); + for (Map.Entry entry : specs.entrySet()) { + assertEquals(client.getExtraSpec(flavor.getId(), entry.getKey()), entry.getValue()); + } + } + } + } + + @Test(dependsOnMethods = "testCreateExtraSpecs") + public void testTwiddleIndividualSpecs() { + if (clientOption.isPresent()) { + FlavorExtraSpecsClient client = clientOption.get(); + for (String key : testSpecs.keySet()) { + assertTrue(client.setExtraSpec(testFlavor.getId(), key, "new value")); + } + for (String key : testSpecs.keySet()) { + assertEquals(client.getExtraSpec(testFlavor.getId(), key), "new value"); + } + for (Resource flavor : flavorClient.listFlavors()) { + Map specs = client.getAllExtraSpecs(flavor.getId()); + assertNotNull(specs); + for (Map.Entry entry : specs.entrySet()) { + assertEquals(client.getExtraSpec(flavor.getId(), entry.getKey()), entry.getValue()); + } + } + } + } +} From 987f2f93c6186eab4a07bc33dc9cd5f8b662dcb2 Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Fri, 11 May 2012 15:22:40 +0100 Subject: [PATCH 087/148] openstack-nova: Adding Quota and Quota Class extensions --- .../openstack/nova/v1_1/NovaAsyncClient.java | 14 + .../openstack/nova/v1_1/NovaClient.java | 14 + .../binders/BindQuotaClassToJsonPayload.java | 38 ++ .../v1_1/binders/BindQuotasToJsonPayload.java | 38 ++ .../v1_1/config/NovaRestClientModule.java | 2 + .../nova/v1_1/domain/QuotaClass.java | 68 ++++ .../openstack/nova/v1_1/domain/Quotas.java | 367 ++++++++++++++++++ .../v1_1/extensions/QuotaAsyncClient.java | 87 +++++ .../extensions/QuotaClassAsyncClient.java | 79 ++++ .../v1_1/extensions/QuotaClassClient.java | 58 +++ .../nova/v1_1/extensions/QuotaClient.java | 63 +++ .../QuotaClassClientExpectTest.java | 110 ++++++ .../extensions/QuotaClassClientLiveTest.java | 76 ++++ .../extensions/QuotaClientExpectTest.java | 129 ++++++ .../v1_1/extensions/QuotaClientLiveTest.java | 102 +++++ .../src/test/resources/quota_class.json | 15 + .../src/test/resources/quotas.json | 15 + 17 files changed, 1275 insertions(+) create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindQuotaClassToJsonPayload.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindQuotasToJsonPayload.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/QuotaClass.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Quotas.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaAsyncClient.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClassAsyncClient.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClassClient.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClient.java create mode 100644 apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClassClientExpectTest.java create mode 100644 apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClassClientLiveTest.java create mode 100644 apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClientExpectTest.java create mode 100644 apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClientLiveTest.java create mode 100644 apis/openstack-nova/src/test/resources/quota_class.json create mode 100644 apis/openstack-nova/src/test/resources/quotas.json diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java index 76081507fd..0f7df114ac 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java @@ -159,4 +159,18 @@ public interface NovaAsyncClient { Optional getFlavorExtraSpecsExtensionForZone( @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + /** + * Provides asynchronous access to Quota features. + */ + @Delegate + Optional getQuotaExtensionForZone( + @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + + /** + * Provides asynchronous access to Quota Classes features. + */ + @Delegate + Optional getQuotaClassExtensionForZone( + @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java index 8177650af8..34184cea25 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java @@ -160,4 +160,18 @@ public interface NovaClient { Optional getFlavorExtraSpecsExtensionForZone( @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + /** + * Provides synchronous access to Quota features. + */ + @Delegate + Optional getQuotaExtensionForZone( + @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + + /** + * Provides synchronous access to Quota Classes features. + */ + @Delegate + Optional getQuotaClassExtensionForZone( + @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindQuotaClassToJsonPayload.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindQuotaClassToJsonPayload.java new file mode 100644 index 0000000000..21818fb068 --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindQuotaClassToJsonPayload.java @@ -0,0 +1,38 @@ +/** + * 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.binders; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.json.Json; +import org.jclouds.openstack.nova.v1_1.domain.QuotaClass; + +import com.google.inject.TypeLiteral; + +/** + * @author Adam Lowe + */ +@Singleton +public class BindQuotaClassToJsonPayload extends BindObjectToJsonPayload { + @Inject + public BindQuotaClassToJsonPayload(Json jsonBinder) { + super(jsonBinder, "quota_class_set", new TypeLiteral(){}); + } +} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindQuotasToJsonPayload.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindQuotasToJsonPayload.java new file mode 100644 index 0000000000..e350b0a3a4 --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindQuotasToJsonPayload.java @@ -0,0 +1,38 @@ +/** + * 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.binders; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.json.Json; +import org.jclouds.openstack.nova.v1_1.domain.Quotas; + +import com.google.inject.TypeLiteral; + +/** + * @author Adam Lowe + */ +@Singleton +public class BindQuotasToJsonPayload extends BindObjectToJsonPayload { + @Inject + public BindQuotasToJsonPayload(Json jsonBinder) { + super(jsonBinder, "quota_set", new TypeLiteral(){}); + } +} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java index 9f2cbcbc99..cb49adff43 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java @@ -79,6 +79,8 @@ public class NovaRestClientModule extends RestClientModule builder() { + return new ConcreteBuilder(); + } + + public Builder toBuilder() { + return new ConcreteBuilder().fromQuotas(this); + } + + public static abstract class Builder> extends Quotas.Builder { + /** + * @see QuotaClass#getId() + */ + @Override + public T id(String id) { + return super.id(id); + } + public QuotaClass build() { + return new QuotaClass(this); + } + + } + + private static class ConcreteBuilder extends Builder { + @Override + protected ConcreteBuilder self() { + return this; + } + } + + protected QuotaClass(Builder builder) { + super(builder); + } + + /** + * The id of this Quota Class. + */ + @Override + public String getId() { + return super.getId(); + } +} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Quotas.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Quotas.java new file mode 100644 index 0000000000..cb44d57cca --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Quotas.java @@ -0,0 +1,367 @@ +/** + * 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 static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Objects; +import com.google.common.base.Objects.ToStringHelper; +import com.google.gson.annotations.SerializedName; + +/** + * Represents the set of limits (quotas) returned by the Quota Extension + * + * @see org.jclouds.openstack.nova.v1_1.extensions.QuotaClient + */ +public class Quotas { + + public static Builder builder() { + return new ConcreteBuilder(); + } + + public Builder toBuilder() { + return new ConcreteBuilder().fromQuotas(this); + } + + public static abstract class Builder> { + protected abstract T self(); + + private String id; + private int metadataItems; + private int injectedFileContentBytes; + private int volumes; + private int gigabytes; + private int ram; + private int floatingIps; + private int instances; + private int injectedFiles; + private int cores; + private int securityGroups; + private int securityGroupRules; + private int keyPairs; + + /** + * @see Quotas#getId() + */ + public T id(String id) { + this.id = id; + return self(); + } + + /** + * @see Quotas#getMetadataItems() + */ + public T metadataItems(int metadataItems) { + this.metadataItems = metadataItems; + return self(); + } + + /** + * @see Quotas#getInjectedFileContentBytes() + */ + public T injectedFileContentBytes(int injectedFileContentBytes) { + this.injectedFileContentBytes = injectedFileContentBytes; + return self(); + } + + /** + * @see Quotas#getVolumes() + */ + public T volumes(int volumes) { + this.volumes = volumes; + return self(); + } + + /** + * @see Quotas#getGigabytes() + */ + public T gigabytes(int gigabytes) { + this.gigabytes = gigabytes; + return self(); + } + + /** + * @see Quotas#getRam() + */ + public T ram(int ram) { + this.ram = ram; + return self(); + } + + /** + * @see Quotas#getFloatingIps() + */ + public T floatingIps(int floatingIps) { + this.floatingIps = floatingIps; + return self(); + } + + /** + * @see Quotas#getInstances() + */ + public T instances(int instances) { + this.instances = instances; + return self(); + } + + /** + * @see Quotas#getInjectedFiles() + */ + public T injectedFiles(int injectedFiles) { + this.injectedFiles = injectedFiles; + return self(); + } + + /** + * @see Quotas#getCores() + */ + public T cores(int cores) { + this.cores = cores; + return self(); + } + + /** + * @see Quotas#getSecurityGroups() + */ + public T securityGroups(int securityGroups) { + this.securityGroups = securityGroups; + return self(); + } + + /** + * @see Quotas#getSecurityGroupRules() + */ + public T securityGroupRules(int securityGroupRules) { + this.securityGroupRules = securityGroupRules; + return self(); + } + + /** + * @see Quotas#getKeyPairs() + */ + public T keyPairs(int keyPairs) { + this.keyPairs = keyPairs; + return self(); + } + + public Quotas build() { + return new Quotas(this); + } + + public T fromQuotas(Quotas in) { + return this.id(in.getId()) + .metadataItems(in.getMetadataItems()) + .injectedFileContentBytes(in.getInjectedFileContentBytes()) + .volumes(in.getVolumes()) + .gigabytes(in.getGigabytes()) + .ram(in.getRam()) + .floatingIps(in.getFloatingIps()) + .instances(in.getInstances()) + .injectedFiles(in.getInjectedFiles()) + .cores(in.getCores()) + .securityGroups(in.getSecurityGroups()) + .securityGroupRules(in.getSecurityGroupRules()) + .keyPairs(in.getKeyPairs()); + } + } + + private static class ConcreteBuilder extends Builder { + @Override + protected ConcreteBuilder self() { + return this; + } + } + + @SerializedName("id") + private final String id; + @SerializedName("metadata_items") + private final int metadataItems; + @SerializedName("injected_file_content_bytes") + private final int injectedFileContentBytes; + private final int volumes; + private final int gigabytes; + private final int ram; + @SerializedName("floating_ips") + private final int floatingIps; + private final int instances; + @SerializedName("injected_files") + private final int injectedFiles; + private final int cores; + @SerializedName("security_groups") + private final int securityGroups; + @SerializedName("security_group_rules") + private final int securityGroupRules; + @SerializedName("key_pairs") + private final int keyPairs; + + protected Quotas(Builder builder) { + this.id = checkNotNull(builder.id, "id"); + this.metadataItems = checkNotNull(builder.metadataItems, "metadataItems"); + this.injectedFileContentBytes = checkNotNull(builder.injectedFileContentBytes, "injectedFileContentBytes"); + this.volumes = checkNotNull(builder.volumes, "volumes"); + this.gigabytes = checkNotNull(builder.gigabytes, "gigabytes"); + this.ram = checkNotNull(builder.ram, "ram"); + this.floatingIps = checkNotNull(builder.floatingIps, "floatingIps"); + this.instances = checkNotNull(builder.instances, "instances"); + this.injectedFiles = checkNotNull(builder.injectedFiles, "injectedFiles"); + this.cores = checkNotNull(builder.cores, "cores"); + this.securityGroups = checkNotNull(builder.securityGroups, "securityGroups"); + this.securityGroupRules = checkNotNull(builder.securityGroupRules, "securityGroupRules"); + this.keyPairs = checkNotNull(builder.keyPairs, "keyPairs"); + } + + /** + * The id of the tenant this set of limits applies to + */ + public String getId() { + return this.id; + } + + /** + * The limit of the number of metadata items for the tenant + */ + public int getMetadataItems() { + return this.metadataItems; + } + + public int getInjectedFileContentBytes() { + return this.injectedFileContentBytes; + } + + /** + * The limit of the number of volumes that can be created for the tenant + */ + public int getVolumes() { + return this.volumes; + } + + /** + * The limit of the total size of all volumes for the tenant + */ + public int getGigabytes() { + return this.gigabytes; + } + + /** + * The limit of total ram available to the tenant + */ + public int getRam() { + return this.ram; + } + + /** + * The limit of the number of floating ips for the tenant + */ + public int getFloatingIps() { + return this.floatingIps; + } + + /** + * The limit of the number of instances that can be created for the tenant + */ + public int getInstances() { + return this.instances; + } + + public int getInjectedFiles() { + return this.injectedFiles; + } + + /** + * The limit of the number of cores that can be used by the tenant + */ + public int getCores() { + return this.cores; + } + + /** + * @return the limit of the number of security groups that can be created for the tenant + * + * @see org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupClient + */ + public int getSecurityGroups() { + return this.securityGroups; + } + + /** + * @return the limit of the number of security group rules that can be created for the tenant + * + * @see org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupClient + */ + public int getSecurityGroupRules() { + return this.securityGroupRules; + } + + /** + * @return the limit of the number of key pairs that can be created for the tenant + * + * @see org.jclouds.openstack.nova.v1_1.extensions.KeyPairClient + */ + public int getKeyPairs() { + return this.keyPairs; + } + + @Override + public int hashCode() { + return Objects.hashCode(id, metadataItems, injectedFileContentBytes, volumes, gigabytes, ram, floatingIps, instances, injectedFiles, cores, securityGroups, securityGroupRules, keyPairs); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + Quotas that = Quotas.class.cast(obj); + return Objects.equal(this.id, that.id) + && Objects.equal(this.metadataItems, that.metadataItems) + && Objects.equal(this.injectedFileContentBytes, that.injectedFileContentBytes) + && Objects.equal(this.volumes, that.volumes) + && Objects.equal(this.gigabytes, that.gigabytes) + && Objects.equal(this.ram, that.ram) + && Objects.equal(this.floatingIps, that.floatingIps) + && Objects.equal(this.instances, that.instances) + && Objects.equal(this.injectedFiles, that.injectedFiles) + && Objects.equal(this.cores, that.cores) + && Objects.equal(this.securityGroups, that.securityGroups) + && Objects.equal(this.securityGroupRules, that.securityGroupRules) + && Objects.equal(this.keyPairs, that.keyPairs); + } + + protected ToStringHelper string() { + return Objects.toStringHelper("") + .add("id", id) + .add("metadataItems", metadataItems) + .add("injectedFileContentBytes", injectedFileContentBytes) + .add("volumes", volumes) + .add("gigabytes", gigabytes) + .add("ram", ram) + .add("floatingIps", floatingIps) + .add("instances", instances) + .add("injectedFiles", injectedFiles) + .add("cores", cores) + .add("securityGroups", securityGroups) + .add("securityGroupRules", securityGroupRules) + .add("keyPairs", keyPairs); + } + + @Override + public String toString() { + return string().toString(); + } + +} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaAsyncClient.java new file mode 100644 index 0000000000..b85d14e0a9 --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaAsyncClient.java @@ -0,0 +1,87 @@ +/** + * 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.extensions; + +import java.util.concurrent.TimeUnit; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.openstack.filters.AuthenticateRequest; +import org.jclouds.openstack.nova.v1_1.binders.BindQuotasToJsonPayload; +import org.jclouds.openstack.nova.v1_1.domain.Quotas; +import org.jclouds.openstack.services.Extension; +import org.jclouds.openstack.services.ServiceType; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.MapBinder; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.SelectJson; +import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; + +import com.google.common.util.concurrent.ListenableFuture; + +/** + * Provide access to Quota information for Nova tenants. + * + * @author Adam Lowe + * @see QuotaClient + * @see + */ +@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.QUOTAS) +@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS) +@RequestFilters(AuthenticateRequest.class) +@Path("/os-quota-sets") +public interface QuotaAsyncClient { + + /** + * @see QuotaClient#getDefaultQuotasForTenant(String) + */ + @GET + @SelectJson("quota_set") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/{tenant_id}") + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getQuotasForTenant(@PathParam("tenant_id") String tenantId); + + /** + * @see QuotaClient#updateQuotasForTenant(String, org.jclouds.openstack.nova.v1_1.domain.Quotas) + */ + @PUT + @Path("/{tenant_id}") + @Produces(MediaType.APPLICATION_JSON) + @MapBinder(BindQuotasToJsonPayload.class) + ListenableFuture updateQuotasForTenant(@PathParam("tenant_id") String tenantId, Quotas quotas); + + /** + * @see QuotaClient#getDefaultQuotasForTenant(String) + */ + @GET + @SelectJson("quota_set") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/{tenant_id}/defaults") + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getDefaultQuotasForTenant(@PathParam("tenant_id") String tenantId); + +} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClassAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClassAsyncClient.java new file mode 100644 index 0000000000..da76b11814 --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClassAsyncClient.java @@ -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.openstack.nova.v1_1.extensions; + +import java.util.concurrent.TimeUnit; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.openstack.filters.AuthenticateRequest; +import org.jclouds.openstack.nova.v1_1.binders.BindQuotaClassToJsonPayload; +import org.jclouds.openstack.nova.v1_1.domain.QuotaClass; +import org.jclouds.openstack.nova.v1_1.domain.Quotas; +import org.jclouds.openstack.services.Extension; +import org.jclouds.openstack.services.ServiceType; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.MapBinder; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.SelectJson; +import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; + +import com.google.common.util.concurrent.ListenableFuture; + +/** + * Provides asynchronous access to Quota Classes via the REST API. + * + * @author Adam Lowe + * @see QuotaClassClient + * @see + * @see + */ +@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.QUOTA_CLASSES) +@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS) +@RequestFilters(AuthenticateRequest.class) +@Path("/os-quota-class-sets") +public interface QuotaClassAsyncClient { + + /** + * @see QuotaClassClient#getQuotaClass + */ + @GET + @SelectJson("quota_class_set") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/{id}") + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getQuotaClass(@PathParam("id") String id); + + /** + * @see QuotaClassClient#updateQuotaClass + */ + @PUT + @Path("/{id}") + @Produces(MediaType.APPLICATION_JSON) + @MapBinder(BindQuotaClassToJsonPayload.class) + ListenableFuture updateQuotaClass(@PathParam("id") String id, QuotaClass quotas); + +} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClassClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClassClient.java new file mode 100644 index 0000000000..b4c3c8666b --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClassClient.java @@ -0,0 +1,58 @@ +/** + * 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.extensions; + +import java.util.concurrent.TimeUnit; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.openstack.filters.AuthenticateRequest; +import org.jclouds.openstack.nova.v1_1.domain.QuotaClass; +import org.jclouds.openstack.nova.v1_1.domain.Quotas; +import org.jclouds.openstack.services.Extension; +import org.jclouds.openstack.services.ServiceType; +import org.jclouds.rest.annotations.RequestFilters; + +/** + * Provides synchronous access to Quota Classes via the REST API. + *

+ * To use this extension, you need to have administrative rights to the tenants upon which you are placing quotas. + * + * @author Adam Lowe + * @see QuotaClassAsyncClient + * @see + * @see + */ +@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.QUOTA_CLASSES) +@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS) +@RequestFilters(AuthenticateRequest.class) +public interface QuotaClassClient { + + /** + * @return the quota settings for the tenant + */ + QuotaClass getQuotaClass(String id); + + /** + * Update the quotas for a given tenant + * + * @return true if successful + */ + Boolean updateQuotaClass(String id, QuotaClass quotas); + +} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClient.java new file mode 100644 index 0000000000..e5ab08c73b --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClient.java @@ -0,0 +1,63 @@ +/** + * 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.extensions; + +import java.util.concurrent.TimeUnit; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.openstack.filters.AuthenticateRequest; +import org.jclouds.openstack.nova.v1_1.domain.Quotas; +import org.jclouds.openstack.services.Extension; +import org.jclouds.openstack.services.ServiceType; +import org.jclouds.rest.annotations.RequestFilters; + +/** + * The quotas extension enables limiters placed on the resources used per tenant (project) for virtual instances. It is + * used with the OpenStack Compute API 1.1 for administrators who need to control the amount of volumes, memory, floating + * IP addresses, instances, or cores allowed within a defined tenant or project. + *

+ * To use this extension, you need to have administrative rights to the tenants upon which you are placing quotas. + * + * @author Adam Lowe + * @see QuotaAsyncClient + * @see + */ +@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.QUOTAS) +@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS) +@RequestFilters(AuthenticateRequest.class) +public interface QuotaClient { + + /** + * @return the quota settings for the tenant + */ + Quotas getQuotasForTenant(String tenantId); + + /** + * Update the quotas for a given tenant + * + * @return true if successful + */ + Boolean updateQuotasForTenant(String tenantId, Quotas quotas); + + /** + * @return the set of default quotas for the tenant + */ + Quotas getDefaultQuotasForTenant(String tenantId); + +} \ No newline at end of file diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClassClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClassClientExpectTest.java new file mode 100644 index 0000000000..ff08699b0b --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClassClientExpectTest.java @@ -0,0 +1,110 @@ +/** + * 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.extensions; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +import java.net.URI; + +import javax.ws.rs.core.MediaType; + +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.openstack.nova.v1_1.domain.QuotaClass; +import org.jclouds.openstack.nova.v1_1.domain.Quotas; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientExpectTest; +import org.jclouds.rest.ResourceNotFoundException; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMultimap; + +/** + * Tests HostAdministrationClient guice wiring and parsing + * + * @author Adam Lowe + */ +@Test(groups = "unit", testName = "QuotaClassClientExpectTest") +public class QuotaClassClientExpectTest extends BaseNovaClientExpectTest { + + public void testGetQuotas() throws Exception { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-quota-class-sets/jcloudstestquotas"); + QuotaClassClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(200).payload(payloadFromResource("/quota_class.json")).build()).getQuotaClassExtensionForZone("az-1.region-a.geo-1").get(); + + assertEquals(client.getQuotaClass("jcloudstestquotas"), getTestQuotas()); + } + + public void testGetQuotasFailsTenantNotFound() throws Exception { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-quota-class-sets/jcloudstestquotas"); + QuotaClassClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(404).build()).getQuotaClassExtensionForZone("az-1.region-a.geo-1").get(); + assertNull(client.getQuotaClass("jcloudstestquotas")); + } + + public void testUpdateQuotas() throws Exception { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-quota-class-sets/myclass"); + QuotaClassClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + HttpRequest.builder().endpoint(endpoint).method("PUT") + .headers(ImmutableMultimap.of("X-Auth-Token", authToken)) + .payload(payloadFromResourceWithContentType("/quota_class.json", MediaType.APPLICATION_JSON)) + .build(), + HttpResponse.builder().statusCode(200).build()).getQuotaClassExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.updateQuotaClass("myclass", getTestQuotas())); + } + + @Test(expectedExceptions = ResourceNotFoundException.class) + public void testUpdateQuotasFailsNotFound() throws Exception { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-quota-class-sets/jcloudstestquotas"); + QuotaClassClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + HttpRequest.builder().endpoint(endpoint).method("PUT") + .headers(ImmutableMultimap.of("X-Auth-Token", authToken)) + .payload(payloadFromResourceWithContentType("/quota_class.json", MediaType.APPLICATION_JSON)) + .build(), + HttpResponse.builder().statusCode(404).build()).getQuotaClassExtensionForZone("az-1.region-a.geo-1").get(); + + client.updateQuotaClass("jcloudstestquotas", getTestQuotas()); + } + + public static QuotaClass getTestQuotas() { + return QuotaClass.builder() + .metadataItems(128) + .injectedFileContentBytes(10240) + .injectedFiles(5) + .gigabytes(1000) + .ram(4096) + .floatingIps(10) + .securityGroups(10) + .securityGroupRules(20) + .instances(5) + .keyPairs(100) + .volumes(5) + .cores(10) + .id("jcloudstestquotas").build(); + } + +} diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClassClientLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClassClientLiveTest.java new file mode 100644 index 0000000000..0d1df006ee --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClassClientLiveTest.java @@ -0,0 +1,76 @@ +/** + * 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.extensions; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jclouds.openstack.nova.v1_1.domain.QuotaClass; +import org.jclouds.openstack.nova.v1_1.domain.Quotas; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientLiveTest; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import com.google.common.base.Optional; +import com.google.common.collect.Iterables; + +/** + * Tests behavior of QuotaClient + * + * @author Adam Lowe + */ +@Test(groups = "live", testName = "QuotaClassClientLiveTest", singleThreaded = true) +public class QuotaClassClientLiveTest extends BaseNovaClientLiveTest { + private Optional clientOption; + private String zone; + + @BeforeClass(groups = {"integration", "live"}) + @Override + public void setupContext() { + super.setupContext(); + zone = Iterables.getLast(novaContext.getApi().getConfiguredZones(), "nova"); + clientOption = novaContext.getApi().getQuotaClassExtensionForZone(zone); + } + + public void testUpdateAndGetQuotaClass() { + if (clientOption.isPresent()) { + QuotaClassClient client = clientOption.get(); + + QuotaClass firstVersion = + QuotaClassClientExpectTest.getTestQuotas().toBuilder() + .id("jcloudstestquotas") + .cores(10) + .instances(5) + .ram(4096) + .volumes(5) + .build(); + + assertTrue(client.updateQuotaClass(firstVersion.getId(), firstVersion)); + + assertEquals(client.getQuotaClass(firstVersion.getId()), firstVersion); + + // Change it again (since we may have run this test before and we can't delete the QuotaClass) + QuotaClass secondVersion = firstVersion.toBuilder().ram(8192).build(); + + assertTrue(client.updateQuotaClass(secondVersion.getId(), secondVersion)); + + assertEquals(client.getQuotaClass(secondVersion.getId()), secondVersion); + } + } +} diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClientExpectTest.java new file mode 100644 index 0000000000..005a2ff4b6 --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClientExpectTest.java @@ -0,0 +1,129 @@ +/** + * 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.extensions; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +import java.net.URI; + +import javax.ws.rs.core.MediaType; + +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.openstack.nova.v1_1.domain.Quotas; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientExpectTest; +import org.jclouds.rest.ResourceNotFoundException; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMultimap; + +/** + * Tests HostAdministrationClient guice wiring and parsing + * + * @author Adam Lowe + */ +@Test(groups = "unit", testName = "QuotaClientExpectTest") +public class QuotaClientExpectTest extends BaseNovaClientExpectTest { + + public void testGetQuotas() throws Exception { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-quota-sets/demo"); + QuotaClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(200).payload(payloadFromResource("/quotas.json")).build()).getQuotaExtensionForZone("az-1.region-a.geo-1").get(); + + assertEquals(client.getQuotasForTenant("demo"), getTestQuotas()); + } + + public void testGetQuotasFailsTenantNotFound() throws Exception { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-quota-sets/demo"); + QuotaClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(404).build()).getQuotaExtensionForZone("az-1.region-a.geo-1").get(); + assertNull(client.getQuotasForTenant("demo")); + } + + public void testGetDefaultQuotas() throws Exception { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-quota-sets/demo/defaults"); + QuotaClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(200).payload(payloadFromResource("/quotas.json")).build()).getQuotaExtensionForZone("az-1.region-a.geo-1").get(); + + assertEquals(client.getDefaultQuotasForTenant("demo"), getTestQuotas()); + } + + public void testGetDefaultQuotasFailsTenantNotFound() throws Exception { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-quota-sets/demo/defaults"); + QuotaClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(404).build()).getQuotaExtensionForZone("az-1.region-a.geo-1").get(); + assertNull(client.getDefaultQuotasForTenant("demo")); + } + + + public void testUpdateQuotas() throws Exception { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-quota-sets/demo"); + QuotaClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + HttpRequest.builder().endpoint(endpoint).method("PUT") + .headers(ImmutableMultimap.of("X-Auth-Token", authToken)) + .payload(payloadFromResourceWithContentType("/quotas.json", MediaType.APPLICATION_JSON)) + .build(), + HttpResponse.builder().statusCode(200).build()).getQuotaExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.updateQuotasForTenant("demo", getTestQuotas())); + } + + @Test(expectedExceptions = ResourceNotFoundException.class) + public void testUpdateQuotasFailsNotFound() throws Exception { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-quota-sets/demo"); + QuotaClient client = requestsSendResponses(keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + HttpRequest.builder().endpoint(endpoint).method("PUT") + .headers(ImmutableMultimap.of("X-Auth-Token", authToken)) + .payload(payloadFromResourceWithContentType("/quotas.json", MediaType.APPLICATION_JSON)) + .build(), + HttpResponse.builder().statusCode(404).build()).getQuotaExtensionForZone("az-1.region-a.geo-1").get(); + + client.updateQuotasForTenant("demo", getTestQuotas()); + } + + public static Quotas getTestQuotas() { + return Quotas.builder() + .metadataItems(128) + .injectedFileContentBytes(10240) + .injectedFiles(5) + .gigabytes(1000) + .ram(51200) + .floatingIps(10) + .securityGroups(10) + .securityGroupRules(20) + .instances(10) + .keyPairs(100) + .volumes(10) + .cores(20) + .id("demo").build(); + } + +} diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClientLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClientLiveTest.java new file mode 100644 index 0000000000..b8c3584ae1 --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClientLiveTest.java @@ -0,0 +1,102 @@ +/** + * 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.extensions; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jclouds.openstack.nova.v1_1.domain.Quotas; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientLiveTest; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import com.google.common.base.Optional; +import com.google.common.collect.Iterables; + +/** + * Tests behavior of QuotaClient + * + * @author Adam Lowe + */ +@Test(groups = "live", testName = "QuotaClientLiveTest", singleThreaded = true) +public class QuotaClientLiveTest extends BaseNovaClientLiveTest { + private Optional clientOption; + private String tenant; + + @BeforeClass(groups = {"integration", "live"}) + @Override + public void setupContext() { + super.setupContext(); + tenant = identity.split(":")[0]; + String zone = Iterables.getLast(novaContext.getApi().getConfiguredZones(), "nova"); + clientOption = novaContext.getApi().getQuotaExtensionForZone(zone); + } + + public void testGetQuotasForCurrentTenant() { + if (clientOption.isPresent()) { + Quotas quota = clientOption.get().getQuotasForTenant(tenant); + assertQuotasIsValid(quota); + } + } + + public void testGetDefaultQuotasForCurrentTenant() { + if (clientOption.isPresent()) { + Quotas quota = clientOption.get().getDefaultQuotasForTenant(tenant); + assertQuotasIsValid(quota); + } + } + + public void testUpdateQuotasOfCurrentTenantThenReset() { + if (clientOption.isPresent()) { + QuotaClient client = clientOption.get(); + Quotas before = client.getQuotasForTenant(tenant); + assertQuotasIsValid(before); + + Quotas modified = before.toBuilder() + .cores(before.getCores() - 1) + .instances(before.getInstances() - 1) + .metadataItems(before.getMetadataItems() - 1) + .ram(before.getRam() - 1) + .volumes(before.getVolumes() - 1) + .build(); + + assertTrue(client.updateQuotasForTenant(tenant, modified)); + + assertEquals(client.getQuotasForTenant(tenant), modified); + + assertTrue(client.updateQuotasForTenant(tenant, before)); + + assertEquals(client.getQuotasForTenant(tenant), before); + } + } + + protected void assertQuotasIsValid(Quotas quota) { + assertTrue(quota.getCores() > 0); + assertTrue(quota.getFloatingIps() >= 0); + assertTrue(quota.getGigabytes() > 0); + assertTrue(quota.getInjectedFileContentBytes() >= 0); + assertTrue(quota.getInjectedFiles() >= 0); + assertTrue(quota.getInstances() > 0); + assertTrue(quota.getKeyPairs() > 0); + assertTrue(quota.getRam() > 0); + assertTrue(quota.getSecurityGroups() > 0); + assertTrue(quota.getSecurityGroupRules() > 0); + assertTrue(quota.getVolumes() > 0); + } +} diff --git a/apis/openstack-nova/src/test/resources/quota_class.json b/apis/openstack-nova/src/test/resources/quota_class.json new file mode 100644 index 0000000000..837b9cc967 --- /dev/null +++ b/apis/openstack-nova/src/test/resources/quota_class.json @@ -0,0 +1,15 @@ +{"quota_class_set": { + "metadata_items": 128, + "injected_file_content_bytes": 10240, + "injected_files": 5, + "gigabytes": 1000, + "ram": 4096, + "floating_ips": 10, + "security_group_rules": 20, + "instances": 5, + "key_pairs": 100, + "volumes": 5, + "cores": 10, + "id": "jcloudstestquotas", + "security_groups": 10 +}} \ No newline at end of file diff --git a/apis/openstack-nova/src/test/resources/quotas.json b/apis/openstack-nova/src/test/resources/quotas.json new file mode 100644 index 0000000000..dcf2869d8c --- /dev/null +++ b/apis/openstack-nova/src/test/resources/quotas.json @@ -0,0 +1,15 @@ +{"quota_set": { + "metadata_items": 128, + "injected_file_content_bytes": 10240, + "injected_files": 5, + "gigabytes": 1000, + "ram": 51200, + "floating_ips": 10, + "security_group_rules": 20, + "instances": 10, + "key_pairs": 100, + "volumes": 10, + "cores": 20, + "id": "demo", + "security_groups": 10 +}} \ No newline at end of file From 95399121bd2f050e6ab033a71c25dcfcc222d816 Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Fri, 11 May 2012 15:27:33 +0100 Subject: [PATCH 088/148] openstack-nova: Adding Volume Types extension --- .../openstack/nova/v1_1/NovaAsyncClient.java | 7 + .../openstack/nova/v1_1/NovaClient.java | 7 + .../v1_1/config/NovaRestClientModule.java | 1 + .../openstack/nova/v1_1/domain/Server.java | 23 +- .../nova/v1_1/domain/VolumeType.java | 194 +++++++++++++ .../extensions/VolumeTypeAsyncClient.java | 154 +++++++++++ .../v1_1/extensions/VolumeTypeClient.java | 107 +++++++ .../v1_1/options/CreateVolumeTypeOptions.java | 103 +++++++ .../VolumeTypeClientExpectTest.java | 260 ++++++++++++++++++ .../extensions/VolumeTypeClientLiveTest.java | 129 +++++++++ .../src/test/resources/volume_type.json | 9 + .../resources/volume_type_extra_specs.json | 1 + .../src/test/resources/volume_type_list.json | 1 + 13 files changed, 986 insertions(+), 10 deletions(-) create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/VolumeType.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeTypeAsyncClient.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeTypeClient.java create mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateVolumeTypeOptions.java create mode 100644 apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeTypeClientExpectTest.java create mode 100644 apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeTypeClientLiveTest.java create mode 100644 apis/openstack-nova/src/test/resources/volume_type.json create mode 100644 apis/openstack-nova/src/test/resources/volume_type_extra_specs.json create mode 100644 apis/openstack-nova/src/test/resources/volume_type_list.json diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java index 0f7df114ac..fc5ab0364d 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java @@ -173,4 +173,11 @@ public interface NovaAsyncClient { Optional getQuotaClassExtensionForZone( @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + /** + * Provides asynchronous access to Volume Type features. + */ + @Delegate + Optional getVolumeTypeExtensionForZone( + @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java index 34184cea25..d7ca6c2081 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java @@ -174,4 +174,11 @@ public interface NovaClient { Optional getQuotaClassExtensionForZone( @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + /** + * Provides synchronous access to Volume Type features. + */ + @Delegate + Optional getVolumeTypeExtensionForZone( + @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone); + } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java index cb49adff43..231b5be658 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/config/NovaRestClientModule.java @@ -81,6 +81,7 @@ public class NovaRestClientModule extends RestClientModule * NOTE: This field is only present if the Extended Status extension is installed. - * + * * @see org.jclouds.openstack.nova.v1_1.features.ExtensionClient#getExtensionByAlias - * @see org.jclouds.openstack.nova.v1_1.extensions.ExtensionNamespaces#EXTENDED_STATUS (extended status?) + * @see org.jclouds.openstack.nova.v1_1.extensions.ExtensionNamespaces#EXTENDED_STATUS */ public Optional getExtendedStatus() { return this.extendedStatus; @@ -480,16 +480,19 @@ public class Server extends Resource { * NOTE: This field is only present if the The Extended Server Attributes API extension is installed. * * @see org.jclouds.openstack.nova.v1_1.features.ExtensionClient#getExtensionByAlias - * @see org.jclouds.openstack.nova.v1_1.extensions.ExtensionNamespaces#EXTENDED_STATUS (extended status?) + * @see org.jclouds.openstack.nova.v1_1.extensions.ExtensionNamespaces#EXTENDED_STATUS */ public Optional getExtendedAttributes() { return this.extendedAttributes; } /** - * State of task running against this instance (e.g. "suspending") + * Disk config attribute from the Disk Config Extension (alias "OS-DCF") *

- * NOTE: This field is only present if the Disk Config extension is installed. + * NOTE: This field is only present if the Disk Config extension is installed + * + * @see org.jclouds.openstack.nova.v1_1.features.ExtensionClient#getExtensionByAlias + * @see org.jclouds.openstack.nova.v1_1.extensions.ExtensionNamespaces#DISK_CONFIG */ public Optional getDiskConfig() { return this.diskConfig; @@ -501,10 +504,10 @@ public class Server extends Resource { @Override protected ToStringHelper string() { return super.string().add("uuid", uuid).add("tenantId", tenantId).add( - "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("diskConfig", diskConfig) - .add("extendedStatus", extendedStatus).add("extendedAttributes", extendedAttributes); + "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("diskConfig", diskConfig) + .add("extendedStatus", extendedStatus).add("extendedAttributes", extendedAttributes); } } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/VolumeType.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/VolumeType.java new file mode 100644 index 0000000000..c8480cefe5 --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/VolumeType.java @@ -0,0 +1,194 @@ +/** + * 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 static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Collections; +import java.util.Date; +import java.util.Map; + +import com.google.common.base.Objects; +import com.google.common.base.Objects.ToStringHelper; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableMap; +import com.google.gson.annotations.SerializedName; + +/** + * Volume Type used in the Volume Type Extension for Nova + * + * @see org.jclouds.openstack.nova.v1_1.extensions.VolumeTypeClient + */ +public class VolumeType { + + public static Builder builder() { + return new ConcreteBuilder(); + } + + public Builder toBuilder() { + return new ConcreteBuilder().fromVolumeType(this); + } + + public static abstract class Builder> { + protected abstract T self(); + + private String id; + private String name; + private Date created = new Date(); + private Date updated; + private Map extraSpecs; + + /** + * @see VolumeType#getId() + */ + public T id(String id) { + this.id = id; + return self(); + } + + /** + * @see VolumeType#getName() + */ + public T name(String name) { + this.name = name; + return self(); + } + + /** + * @see VolumeType#getCreated() + */ + public T created(Date created) { + this.created = created; + return self(); + } + + /** + * @see VolumeType#getUpdated() + */ + public T updated(Date updated) { + this.updated = updated; + return self(); + } + + /** + * @see VolumeType#getExtraSpecs() + */ + public T extraSpecs(Map extraSpecs) { + this.extraSpecs = ImmutableMap.copyOf(extraSpecs); + return self(); + } + + public VolumeType build() { + return new VolumeType(this); + } + + public T fromVolumeType(VolumeType in) { + return this + .id(in.getId()) + .name(in.getName()) + .extraSpecs(in.getExtraSpecs()) + .created(in.getCreated()) + .updated(in.getUpdated().orNull()); + } + + } + + private static class ConcreteBuilder extends Builder { + @Override + protected ConcreteBuilder self() { + return this; + } + } + + private String id; + private String name; + @SerializedName("created_at") + private Date created; + @SerializedName("updated_at") + private final Optional updated; + @SerializedName(value = "extra_specs") + private final Map extraSpecs; + + protected VolumeType(Builder builder) { + this.id = checkNotNull(builder.id, "id"); + this.name = checkNotNull(builder.name, "name"); + this.extraSpecs = checkNotNull(builder.extraSpecs, "extraSpecs"); + this.created = checkNotNull(builder.created, "created"); + this.updated = Optional.fromNullable(builder.updated); + } + + protected VolumeType() { + this.updated = Optional.absent(); + this.extraSpecs = ImmutableMap.of(); + } + + public String getId() { + return this.id; + } + + public String getName() { + return this.name; + } + + /** The Date the VolumeType was created */ + public Date getCreated() { + return created; + } + + /** The Date the VolumeType as last updated - absent if no updates have taken place */ + public Optional getUpdated() { + return updated; + } + + public Map getExtraSpecs() { + return Collections.unmodifiableMap(this.extraSpecs); + } + + @Override + public int hashCode() { + return Objects.hashCode(id, name, created, updated, extraSpecs); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + VolumeType that = VolumeType.class.cast(obj); + return Objects.equal(this.id, that.id) + && Objects.equal(this.name, that.name) + && Objects.equal(this.created, that.created) + && Objects.equal(this.updated, that.updated) + && Objects.equal(this.extraSpecs, that.extraSpecs); + } + + protected ToStringHelper string() { + return Objects.toStringHelper("") + .add("id", id) + .add("name", name) + .add("created", created) + .add("updated", updated) + .add("extraSpecs", extraSpecs); + } + + @Override + public String toString() { + return string().toString(); + } + +} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeTypeAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeTypeAsyncClient.java new file mode 100644 index 0000000000..6a2ebb0ced --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeTypeAsyncClient.java @@ -0,0 +1,154 @@ +/** + * 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.extensions; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.openstack.filters.AuthenticateRequest; +import org.jclouds.openstack.nova.v1_1.binders.BindExtraSpecsToJsonPayload; +import org.jclouds.openstack.nova.v1_1.domain.VolumeType; +import org.jclouds.openstack.nova.v1_1.options.CreateVolumeTypeOptions; +import org.jclouds.openstack.services.Extension; +import org.jclouds.openstack.services.ServiceType; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.MapBinder; +import org.jclouds.rest.annotations.Payload; +import org.jclouds.rest.annotations.PayloadParam; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.SelectJson; +import org.jclouds.rest.annotations.SkipEncoding; +import org.jclouds.rest.annotations.Unwrap; +import org.jclouds.rest.functions.ReturnEmptyMapOnNotFoundOr404; +import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; +import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404; +import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; + +import com.google.common.util.concurrent.ListenableFuture; + +/** + * Provides asynchronous access to Volume Type features + * + * @author Adam Lowe + * @see VolumeTypeClient + */ +@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.VOLUME_TYPES) +@SkipEncoding({'/', '='}) +@RequestFilters(AuthenticateRequest.class) +@Path("/os-volume-types") +@Consumes(MediaType.APPLICATION_JSON) +public interface VolumeTypeAsyncClient { + + /** + * @see VolumeTypeClient#listVolumeTypes + */ + @GET + @SelectJson("volume_types") + @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) + ListenableFuture> listVolumeTypes(); + + + /** + * @see VolumeTypeClient#getVolumeType + */ + @GET + @Path("/{id}") + @SelectJson("volume_type") + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getVolumeType(@PathParam("id") String id); + + /** + * @see VolumeTypeClient#createVolumeType + */ + @POST + @SelectJson("volume_type") + @Produces(MediaType.APPLICATION_JSON) + @Payload("%7B\"volume_type\":%7B\"name\":\"{name}\"%7D%7D") + ListenableFuture createVolumeType(@PayloadParam("name") String name, CreateVolumeTypeOptions... options); + + /** + * @see VolumeTypeClient#deleteVolumeType + */ + @DELETE + @Path("/{id}") + @ExceptionParser(ReturnFalseOnNotFoundOr404.class) + ListenableFuture deleteVolumeType(@PathParam("id") String id); + + /** + * @see VolumeTypeClient#getAllExtraSpecs(String) + */ + @GET + @SelectJson("extra_specs") + @Path("/{id}/extra_specs") + @ExceptionParser(ReturnEmptyMapOnNotFoundOr404.class) + ListenableFuture> getAllExtraSpecs(@PathParam("id") String id); + + /** + * @see VolumeTypeClient#setAllExtraSpecs(String, java.util.Map) + */ + @POST + @Path("/{id}/extra_specs") + @Produces(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnFalseOnNotFoundOr404.class) + @MapBinder(BindExtraSpecsToJsonPayload.class) + ListenableFuture setAllExtraSpecs(@PathParam("id") String id, Map specs); + + /** + * @see VolumeTypeClient#getExtraSpec(String, String) + */ + @GET + @Path("/{id}/extra_specs/{key}") + @Unwrap + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getExtraSpec(@PathParam("id") String id, @PathParam("key") String key); + + /** + * @see VolumeTypeClient#setExtraSpec(String, String, String) + */ + @PUT + @Path("/{id}/extra_specs/{key}") + @Produces(MediaType.APPLICATION_JSON) + @Payload("%7B\"{key}\":\"{value}\"%7D") + @ExceptionParser(ReturnFalseOnNotFoundOr404.class) + ListenableFuture setExtraSpec(@PathParam("id") String id, + @PathParam("key") @PayloadParam("key") String key, + @PayloadParam("value") String value); + + /** + * @see VolumeTypeClient#deleteExtraSpec(String, String) + */ + @DELETE + @Path("/{id}/extra_specs/{key}") + @ExceptionParser(ReturnFalseOnNotFoundOr404.class) + ListenableFuture deleteExtraSpec(@PathParam("id") String id, + @PathParam("key") String key); + +} diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeTypeClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeTypeClient.java new file mode 100644 index 0000000000..39a1695bc3 --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeTypeClient.java @@ -0,0 +1,107 @@ +/** + * 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.extensions; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.openstack.filters.AuthenticateRequest; +import org.jclouds.openstack.nova.v1_1.domain.VolumeType; +import org.jclouds.openstack.nova.v1_1.options.CreateVolumeTypeOptions; +import org.jclouds.openstack.services.Extension; +import org.jclouds.openstack.services.ServiceType; +import org.jclouds.rest.annotations.RequestFilters; + +/** + * Provides synchronous access to Volume Type features + * + * @author Adam Lowe + * @see VolumeClient + * @see VolumeTypeAsyncClient + * @see + * @see + */ +@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.VOLUME_TYPES) +@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS) +@RequestFilters(AuthenticateRequest.class) +public interface VolumeTypeClient { + + /** + * @return set of all volume types + */ + Set listVolumeTypes(); + + /** + * @param id the id of the volume type to retrieve + * @return the requested volume type + */ + VolumeType getVolumeType(String id); + + /** + * Creates a new volume type + * + * @param name the name of the new volume type + * @param options optional settings for the new volume type + * @return the new volume type + */ + VolumeType createVolumeType(String name, CreateVolumeTypeOptions... options); + + /** + * Deletes a volume type + */ + Boolean deleteVolumeType(String id); + + /** + * @param id the id of the volume type + * @return the set of extra metadata for the flavor + */ + Map getAllExtraSpecs(String id); + + /** + * Creates or updates the extra metadata for a given flavor + */ + Boolean setAllExtraSpecs(String id, Map specs); + + /** + * Retrieve a single extra spec value + * + * @param id the id of the volume type + * @param key the key of the extra spec item to retrieve + */ + String getExtraSpec(String id, String key); + + /** + * Creates or updates a single extra spec value + * + * @param id the id of the volume type + * @param key the extra spec key (when creating ensure this does not include whitespace or other difficult characters) + * @param value the new value to store associate with the key + */ + Boolean setExtraSpec(String id, String key, String value); + + /** + * Deletes an existing extra spec + * + * @param id the id of the volume type + * @param key the key of the extra spec to delete + */ + Boolean deleteExtraSpec(String id, String key); +} diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateVolumeTypeOptions.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateVolumeTypeOptions.java new file mode 100644 index 0000000000..fc40855b9f --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateVolumeTypeOptions.java @@ -0,0 +1,103 @@ +/** + * 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.options; + +import static com.google.common.base.Objects.equal; +import static com.google.common.base.Objects.toStringHelper; + +import java.util.Map; + +import javax.inject.Inject; + +import org.jclouds.http.HttpRequest; +import org.jclouds.rest.MapBinder; +import org.jclouds.rest.binders.BindToJsonPayload; + +import com.google.common.base.Objects; +import com.google.common.base.Objects.ToStringHelper; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; + +/** + * @author Adam Lowe + */ +public class CreateVolumeTypeOptions implements MapBinder { + public static final CreateVolumeTypeOptions NONE = new CreateVolumeTypeOptions(); + + @Inject + protected BindToJsonPayload jsonBinder; + + protected Map specs = ImmutableMap.of(); + + @Override + public R bindToRequest(R request, Map postParams) { + Map data = Maps.newHashMap(); + data.putAll(postParams); + data.put("extra_specs", specs); + return jsonBinder.bindToRequest(request, ImmutableMap.of("volume_type", data)); + } + + @Override + public R bindToRequest(R request, Object toBind) { + throw new IllegalStateException("CreateWithExtraSpecs are POST operations"); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof CreateVolumeTypeOptions)) return false; + final CreateVolumeTypeOptions other = CreateVolumeTypeOptions.class.cast(object); + return equal(specs, other.specs); + } + + @Override + public int hashCode() { + return Objects.hashCode(specs); + } + + protected ToStringHelper string() { + return toStringHelper("").add("specs", specs); + } + + @Override + public String toString() { + return string().toString(); + } + + public CreateVolumeTypeOptions specs(Map specs) { + this.specs = specs; + return this; + } + + public Map getSpecs() { + return specs; + } + + public static class Builder { + /** + * @see CreateVolumeTypeOptions#getSpecs() + */ + public static CreateVolumeTypeOptions specs(Map specs) { + return new CreateVolumeTypeOptions().specs(specs); + } + } + +} diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeTypeClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeTypeClientExpectTest.java new file mode 100644 index 0000000000..1587cff8a2 --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeTypeClientExpectTest.java @@ -0,0 +1,260 @@ +/** + * 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.extensions; + +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 java.net.URI; +import java.util.Set; + +import javax.ws.rs.core.MediaType; + +import org.jclouds.date.DateService; +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.openstack.nova.v1_1.domain.VolumeType; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientExpectTest; +import org.jclouds.openstack.nova.v1_1.options.CreateVolumeTypeOptions; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +/** + * Tests guice wiring and parsing of VolumeTypeClient + * + * @author Adam Lowe + */ +@Test(groups = "unit", testName = "VolumeTypeClientExpectTest") +public class VolumeTypeClientExpectTest extends BaseNovaClientExpectTest { + private DateService dateService = new SimpleDateFormatDateService(); + + public void testListVolumeTypes() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volume-types"); + VolumeTypeClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(200).payload(payloadFromResource("/volume_type_list.json")).build() + ).getVolumeTypeExtensionForZone("az-1.region-a.geo-1").get(); + + Set types = client.listVolumeTypes(); + assertEquals(types, ImmutableSet.of(testVolumeType())); + } + + public void testGetVolumeType() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volume-types/8"); + VolumeTypeClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(200).payload(payloadFromResource("/volume_type.json")).build() + ).getVolumeTypeExtensionForZone("az-1.region-a.geo-1").get(); + + VolumeType type = client.getVolumeType("8"); + assertEquals(type, testVolumeType()); + } + + public void testGetVolumeTypeFailNotFound() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volume-types/8"); + VolumeTypeClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(404).build() + ).getVolumeTypeExtensionForZone("az-1.region-a.geo-1").get(); + + assertNull(client.getVolumeType("8")); + } + + public void testCreateVolumeType() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volume-types"); + VolumeTypeClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).method("POST") + .payload(payloadFromStringWithContentType("{\"volume_type\":{\"name\":\"jclouds-test-1\"}}", MediaType.APPLICATION_JSON)) + .build(), + standardResponseBuilder(200).payload(payloadFromResource("/volume_type.json")).build() + ).getVolumeTypeExtensionForZone("az-1.region-a.geo-1").get(); + + VolumeType type = client.createVolumeType("jclouds-test-1"); + assertEquals(type, testVolumeType()); + } + + public void testCreateVolumeTypeWithOptsNONE() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volume-types"); + VolumeTypeClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).method("POST") + .payload(payloadFromStringWithContentType("{\"volume_type\":{\"name\":\"jclouds-test-1\",\"extra_specs\":{}}}", MediaType.APPLICATION_JSON)) + .build(), + standardResponseBuilder(200).payload(payloadFromResource("/volume_type.json")).build() + ).getVolumeTypeExtensionForZone("az-1.region-a.geo-1").get(); + + VolumeType type = client.createVolumeType("jclouds-test-1", CreateVolumeTypeOptions.NONE); + assertEquals(type, testVolumeType()); + } + + public void testCreateVolumeTypeWithOptsSet() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volume-types"); + VolumeTypeClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).method("POST") + .payload(payloadFromStringWithContentType("{\"volume_type\":{\"name\":\"jclouds-test-1\",\"extra_specs\":{\"x\": \"y\"}}}", MediaType.APPLICATION_JSON)) + .build(), + standardResponseBuilder(200).payload(payloadFromResource("/volume_type.json")).build() + ).getVolumeTypeExtensionForZone("az-1.region-a.geo-1").get(); + + VolumeType type = client.createVolumeType("jclouds-test-1", CreateVolumeTypeOptions.Builder.specs(ImmutableMap.of("x", "y"))); + assertEquals(type, testVolumeType()); + } + + public void testDeleteVolumeType() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volume-types/8"); + VolumeTypeClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).method("DELETE").build(), + standardResponseBuilder(200).build() + ).getVolumeTypeExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.deleteVolumeType("8")); + } + + public void testDeleteVolumeTypeFailNotFound() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volume-types/8"); + VolumeTypeClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).method("DELETE").build(), + standardResponseBuilder(404).build() + ).getVolumeTypeExtensionForZone("az-1.region-a.geo-1").get(); + + assertFalse(client.deleteVolumeType("8")); + } + + public void testGetAllExtraSpecs() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volume-types/9/extra_specs"); + VolumeTypeClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(200).payload(payloadFromResource("/volume_type_extra_specs.json")).build() + ).getVolumeTypeExtensionForZone("az-1.region-a.geo-1").get(); + + assertEquals(client.getAllExtraSpecs("9"), ImmutableMap.of("test", "value1")); + } + + public void testGetAllExtraSpecsFailNotFound() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volume-types/9/extra_specs"); + VolumeTypeClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(404).build() + ).getVolumeTypeExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.getAllExtraSpecs("9").isEmpty()); + } + + public void testSetAllExtraSpecs() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volume-types/9/extra_specs"); + VolumeTypeClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint) + .method("POST") + .payload(payloadFromStringWithContentType("{\"extra_specs\":{\"test1\":\"somevalue\"}}", MediaType.APPLICATION_JSON)).build(), + standardResponseBuilder(200).build() + ).getVolumeTypeExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.setAllExtraSpecs("9", ImmutableMap.of("test1", "somevalue"))); + } + + public void testSetExtraSpec() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volume-types/5/extra_specs/test1"); + VolumeTypeClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint) + .method("PUT") + .payload(payloadFromStringWithContentType("{\"test1\":\"somevalue\"}", MediaType.APPLICATION_JSON)).build(), + standardResponseBuilder(200).build() + ).getVolumeTypeExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.setExtraSpec("5", "test1", "somevalue")); + } + + public void testGetExtraSpec() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volume-types/5/extra_specs/test1"); + VolumeTypeClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(200).payload(payloadFromStringWithContentType("{\"test1\":\"another value\"}", MediaType.APPLICATION_JSON)).build() + ).getVolumeTypeExtensionForZone("az-1.region-a.geo-1").get(); + + assertEquals(client.getExtraSpec("5", "test1"), "another value"); + } + + public void testGetExtraSpecFailNotFound() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volume-types/5/extra_specs/test1"); + VolumeTypeClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).build(), + standardResponseBuilder(404).build() + ).getVolumeTypeExtensionForZone("az-1.region-a.geo-1").get(); + + assertNull(client.getExtraSpec("5", "test1")); + } + + public void testDeleteExtraSpec() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volume-types/5/extra_specs/test1"); + VolumeTypeClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).method("DELETE").build(), + standardResponseBuilder(200).build() + ).getVolumeTypeExtensionForZone("az-1.region-a.geo-1").get(); + + assertTrue(client.deleteExtraSpec("5", "test1")); + } + + public void testDeleteExtraSpecFailNotFound() { + URI endpoint = URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-volume-types/5/extra_specs/test1"); + VolumeTypeClient client = requestsSendResponses( + keystoneAuthWithUsernameAndPassword, + responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, + standardRequestBuilder(endpoint).method("DELETE").build(), + standardResponseBuilder(404).build() + ).getVolumeTypeExtensionForZone("az-1.region-a.geo-1").get(); + + assertFalse(client.deleteExtraSpec("5", "test1")); + } + + public VolumeType testVolumeType() { + return VolumeType.builder().id("8").name("jclouds-test-1").created(dateService.iso8601SecondsDateParse("2012-05-10 12:33:06")).extraSpecs(ImmutableMap.of("test", "value1", "test1", "wibble")).build(); + } +} diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeTypeClientLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeTypeClientLiveTest.java new file mode 100644 index 0000000000..c9ca1563b8 --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeTypeClientLiveTest.java @@ -0,0 +1,129 @@ +/** + * 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.extensions; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.util.Set; + +import org.jclouds.openstack.nova.v1_1.domain.VolumeType; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientLiveTest; +import org.jclouds.openstack.nova.v1_1.options.CreateVolumeTypeOptions; +import org.jclouds.predicates.RetryablePredicate; +import org.testng.annotations.AfterGroups; +import org.testng.annotations.BeforeGroups; +import org.testng.annotations.Test; + +import com.google.common.base.Objects; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; + +/** + * Tests behavior of VolumeTypeClient + * + * @author Adam Lowe + */ +@Test(groups = "live", testName = "VolumeTypeClientLiveTest", singleThreaded = true) +public class VolumeTypeClientLiveTest extends BaseNovaClientLiveTest { + + private Optional volumeTypeOption; + private String zone; + + private VolumeType testVolumeType; + + @BeforeGroups(groups = {"integration", "live"}) + @Override + public void setupContext() { + super.setupContext(); + zone = Iterables.getLast(novaContext.getApi().getConfiguredZones(), "nova"); + volumeTypeOption = novaContext.getApi().getVolumeTypeExtensionForZone(zone); + } + + @AfterGroups(groups = "live") + @Override + protected void tearDown() { + if (volumeTypeOption.isPresent()) { + if (testVolumeType != null) { + final String id = testVolumeType.getId(); + assertTrue(volumeTypeOption.get().deleteVolumeType(id)); + assertTrue(new RetryablePredicate(new Predicate() { + @Override + public boolean apply(VolumeTypeClient volumeClient) { + return volumeClient.getVolumeType(id) == null; + } + }, 5 * 1000L).apply(volumeTypeOption.get())); + } + } + super.tearDown(); + } + + public void testCreateVolumeType() { + if (volumeTypeOption.isPresent()) { + testVolumeType = volumeTypeOption.get().createVolumeType( + "jclouds-test-1", CreateVolumeTypeOptions.Builder.specs(ImmutableMap.of("test", "value1"))); + assertTrue(new RetryablePredicate(new Predicate() { + @Override + public boolean apply(VolumeTypeClient volumeTypeClient) { + return volumeTypeClient.getVolumeType(testVolumeType.getId()) != null; + } + }, 180 * 1000L).apply(volumeTypeOption.get())); + + assertEquals(volumeTypeOption.get().getVolumeType(testVolumeType.getId()).getName(), "jclouds-test-1"); + assertEquals(volumeTypeOption.get().getVolumeType(testVolumeType.getId()).getExtraSpecs(), ImmutableMap.of("test", "value1")); + } + } + + @Test(dependsOnMethods = "testCreateVolumeType") + public void testListVolumeTypes() { + if (volumeTypeOption.isPresent()) { + Set volumeTypes = volumeTypeOption.get().listVolumeTypes(); + assertNotNull(volumeTypes); + boolean foundIt = false; + for (VolumeType vt : volumeTypes) { + VolumeType details = volumeTypeOption.get().getVolumeType(vt.getId()); + assertNotNull(details); + if (Objects.equal(details.getId(), testVolumeType.getId())) { + foundIt = true; + } + } + assertTrue(foundIt, "Failed to find the volume type we created in listVolumeTypes() response"); + } + } + + @Test(dependsOnMethods = "testCreateVolumeType") + public void testExtraSpecs() { + if (volumeTypeOption.isPresent()) { + assertEquals(volumeTypeOption.get().getAllExtraSpecs(testVolumeType.getId()), ImmutableMap.of("test", "value1")); + assertEquals(volumeTypeOption.get().getExtraSpec(testVolumeType.getId(), "test"), "value1"); + assertTrue(volumeTypeOption.get().setAllExtraSpecs(testVolumeType.getId(), ImmutableMap.of("test1", "wibble"))); + } + } + + @Test(dependsOnMethods = "testCreateVolumeType") + public void testUpdateIndividualSpec() { + if (volumeTypeOption.isPresent()) { + assertTrue(volumeTypeOption.get().setExtraSpec(testVolumeType.getId(), "test1", "freddy")); + assertEquals(volumeTypeOption.get().getExtraSpec(testVolumeType.getId(), "test1"), "freddy"); + } + } +} diff --git a/apis/openstack-nova/src/test/resources/volume_type.json b/apis/openstack-nova/src/test/resources/volume_type.json new file mode 100644 index 0000000000..8739d09cdc --- /dev/null +++ b/apis/openstack-nova/src/test/resources/volume_type.json @@ -0,0 +1,9 @@ +{"volume_type": { + "name": "jclouds-test-1", + "deleted": false, + "created_at": "2012-05-10 12:33:06", + "updated_at": null, + "extra_specs": {"test": "value1", "test1": "wibble"}, + "deleted_at": null, + "id": 8 +}} \ No newline at end of file diff --git a/apis/openstack-nova/src/test/resources/volume_type_extra_specs.json b/apis/openstack-nova/src/test/resources/volume_type_extra_specs.json new file mode 100644 index 0000000000..6e71d5d3fd --- /dev/null +++ b/apis/openstack-nova/src/test/resources/volume_type_extra_specs.json @@ -0,0 +1 @@ +{"extra_specs": {"test": "value1"}} \ No newline at end of file diff --git a/apis/openstack-nova/src/test/resources/volume_type_list.json b/apis/openstack-nova/src/test/resources/volume_type_list.json new file mode 100644 index 0000000000..3e613a30cd --- /dev/null +++ b/apis/openstack-nova/src/test/resources/volume_type_list.json @@ -0,0 +1 @@ +{"volume_types": [{"name": "jclouds-test-1", "deleted": false, "created_at": "2012-05-10 12:33:06", "updated_at": null, "extra_specs": {"test": "value1", "test1": "wibble"}, "deleted_at": null, "id": 8}]} \ No newline at end of file From 42b807168217b214c928d4d240eac8ec152247fb Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Mon, 14 May 2012 11:38:41 +0100 Subject: [PATCH 089/148] openstack-nova: Adding support for @WrapWith on methods --- .../java/org/jclouds/rest/annotations/WrapWith.java | 3 ++- .../rest/binders/BindToJsonPayloadWrappedWith.java | 12 ++++++++++-- .../rest/internal/RestAnnotationProcessor.java | 3 +++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/jclouds/rest/annotations/WrapWith.java b/core/src/main/java/org/jclouds/rest/annotations/WrapWith.java index dafc173496..c517aac4ec 100644 --- a/core/src/main/java/org/jclouds/rest/annotations/WrapWith.java +++ b/core/src/main/java/org/jclouds/rest/annotations/WrapWith.java @@ -18,6 +18,7 @@ */ package org.jclouds.rest.annotations; +import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; @@ -31,7 +32,7 @@ import java.lang.annotation.Target; * * @author Adrian Cole */ -@Target(PARAMETER) +@Target( { METHOD, PARAMETER }) @Retention(RUNTIME) public @interface WrapWith { diff --git a/core/src/main/java/org/jclouds/rest/binders/BindToJsonPayloadWrappedWith.java b/core/src/main/java/org/jclouds/rest/binders/BindToJsonPayloadWrappedWith.java index a65d14ee52..3909d07961 100644 --- a/core/src/main/java/org/jclouds/rest/binders/BindToJsonPayloadWrappedWith.java +++ b/core/src/main/java/org/jclouds/rest/binders/BindToJsonPayloadWrappedWith.java @@ -20,20 +20,24 @@ package org.jclouds.rest.binders; import static com.google.common.base.Preconditions.checkNotNull; +import java.util.Map; + import javax.inject.Inject; import org.jclouds.http.HttpRequest; import org.jclouds.rest.Binder; +import org.jclouds.rest.MapBinder; +import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import com.google.inject.assistedinject.Assisted; /** * Sometimes, cloud apis wrap requests inside an envelope. This addresses this. - * + * * @author Adrian Cole */ -public class BindToJsonPayloadWrappedWith implements Binder { +public class BindToJsonPayloadWrappedWith implements MapBinder { public static interface Factory { BindToJsonPayloadWrappedWith create(String envelope); @@ -53,4 +57,8 @@ public class BindToJsonPayloadWrappedWith implements Binder { return jsonBinder.bindToRequest(request, (Object) ImmutableMap.of(envelope, checkNotNull(payload, "payload"))); } + @Override + public R bindToRequest(R request, Map postParams) { + return this.bindToRequest(request, (Object) postParams); + } } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java b/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java index 7a25e874a5..aa439d4180 100644 --- a/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java +++ b/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java @@ -916,6 +916,9 @@ public class RestAnnotationProcessor { return injector.getInstance(method.getAnnotation(MapBinder.class).value()); } else if (method.isAnnotationPresent(org.jclouds.rest.annotations.Payload.class)) { return injector.getInstance(BindMapToStringPayload.class); + } else if (method.isAnnotationPresent(WrapWith.class)) { + return injector.getInstance(BindToJsonPayloadWrappedWith.Factory.class).create( + method.getAnnotation(WrapWith.class).value()); } return null; } From cc016d5dc80f449a870e7b1dba7868b40fe5aa0a Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Mon, 14 May 2012 12:42:17 +0100 Subject: [PATCH 090/148] Adjusting MapBinder to take Map so BindToJsonPayload can bind objects annotated with PayloadParam correctly --- .../BindBackupScheduleToJsonPayload.java | 4 +- .../options/CreateServerOptions.java | 10 +-- .../options/CreateSharedIpGroupOptions.java | 4 +- .../options/RebuildServerOptions.java | 2 +- .../options/CreateServerOptionsTest.java | 2 +- .../CreateSharedIpGroupOptionsTest.java | 2 +- .../options/RebuildServerOptionsTest.java | 2 +- ...indCloneDriveOptionsToPlainTextString.java | 14 ++- ...loneDriveOptionsToPlainTextStringTest.java | 6 +- .../openstack/nova/NovaAsyncClient.java | 2 +- .../nova/options/CreateServerOptions.java | 12 +-- .../nova/options/RebuildServerOptions.java | 2 +- .../nova/options/CreateServerOptionsTest.java | 2 +- .../options/RebuildServerOptionsTest.java | 2 +- .../BindAggregateMetadataToJsonPayload.java | 39 --------- .../binders/BindExtraSpecsToJsonPayload.java | 39 --------- .../v1_1/binders/BindObjectToJsonPayload.java | 86 ------------------- .../binders/BindQuotaClassToJsonPayload.java | 38 -------- .../v1_1/binders/BindQuotasToJsonPayload.java | 38 -------- .../BindSecurityGroupRuleToJsonPayload.java | 4 +- .../extensions/AdminActionsAsyncClient.java | 9 +- .../FlavorExtraSpecsAsyncClient.java | 6 +- .../extensions/HostAggregateAsyncClient.java | 23 +++-- .../v1_1/extensions/QuotaAsyncClient.java | 7 +- .../extensions/QuotaClassAsyncClient.java | 9 +- .../v1_1/extensions/VolumeAsyncClient.java | 5 +- .../extensions/VolumeTypeAsyncClient.java | 11 ++- .../options/CreateBackupOfServerOptions.java | 2 +- .../v1_1/options/CreateServerOptions.java | 8 +- .../v1_1/options/CreateVolumeOptions.java | 2 +- .../options/CreateVolumeSnapshotOptions.java | 4 +- .../v1_1/options/CreateVolumeTypeOptions.java | 2 +- .../v1_1/options/RebuildServerOptions.java | 2 +- .../AdminActionsClientExpectTest.java | 8 +- .../extensions/VolumeClientExpectTest.java | 4 +- .../BindCaptureVAppParamsToXmlPayload.java | 6 +- .../binders/BindCatalogItemToXmlPayload.java | 6 +- .../binders/BindCloneParamsToXmlPayload.java | 8 +- ...antiateVAppTemplateParamsToXmlPayload.java | 6 +- .../binders/BindParamsToXmlPayload.java | 8 +- .../BindCatalogItemToXmlPayloadTest.java | 2 +- .../BindCloneVAppParamsToXmlPayloadTest.java | 6 +- ...oneVAppTemplateParamsToXmlPayloadTest.java | 6 +- .../BindDeployVAppParamsToXmlPayloadTest.java | 4 +- ...ateVAppTemplateParamsToXmlPayloadTest.java | 10 +-- ...indUndeployVAppParamsToXmlPayloadTest.java | 4 +- .../binders/BindCredentialsToJsonPayload.java | 2 +- .../v2_0/binders/BindAuthToJsonPayload.java | 4 +- .../BindAddInternetServiceToXmlPayload.java | 16 ++-- .../BindAddNodeServiceToXmlPayload.java | 12 +-- .../BindCloneVAppParamsToXmlPayload.java | 6 +- .../binders/BindCreateKeyToXmlPayload.java | 6 +- ...antiateVAppTemplateParamsToXmlPayload.java | 6 +- .../BindNodeConfigurationToXmlPayload.java | 10 +-- .../BindVAppConfigurationToXmlPayload.java | 2 +- .../options/AddInternetServiceOptions.java | 4 +- .../vcloud_0_8/options/AddNodeOptions.java | 4 +- ...indAddInternetServiceToXmlPayloadTest.java | 6 +- .../BindAddNodeServiceToXmlPayloadTest.java | 2 +- .../BindCloneVAppParamsToXmlPayloadTest.java | 4 +- ...ateVAppTemplateParamsToXmlPayloadTest.java | 2 +- ...BindNodeConfigurationToXmlPayloadTest.java | 10 +-- ...BindVAppConfigurationToXmlPayloadTest.java | 10 +-- .../EnumTypeAdapterThatReturnsFromValue.java | 2 +- .../main/java/org/jclouds/rest/MapBinder.java | 2 +- .../rest/binders/BindMapToStringPayload.java | 2 +- .../rest/binders/BindToJsonPayload.java | 2 +- .../binders/BindToJsonPayloadWrappedWith.java | 4 +- .../internal/RestAnnotationProcessor.java | 14 +-- .../binders/BindMapToStringPayloadTest.java | 4 +- .../internal/RestAnnotationProcessorTest.java | 4 +- .../glesys/options/CreateServerOptions.java | 4 +- .../binders/BaseBindVMSpecToXmlPayload.java | 2 +- .../BindCaptureVAppTemplateToXmlPayload.java | 2 +- .../vpdc/binders/BindCloneVMToXmlPayload.java | 4 +- .../binders/BindFirewallRuleToXmlPayload.java | 2 +- ...AndPasswordAsBasicAuthorizationHeader.java | 2 +- .../miro/binder/CreateServerOptions.java | 8 +- .../miro/binder/RimuHostingJsonBinder.java | 8 +- .../binder/RimuHostingRebootJsonBinder.java | 2 +- .../binders/BindCreateBackupToXmlPayload.java | 6 +- .../binders/BindCreateSliceToXmlPayload.java | 8 +- 82 files changed, 217 insertions(+), 449 deletions(-) delete mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindAggregateMetadataToJsonPayload.java delete mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindExtraSpecsToJsonPayload.java delete mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindObjectToJsonPayload.java delete mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindQuotaClassToJsonPayload.java delete mode 100644 apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindQuotasToJsonPayload.java diff --git a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/binders/BindBackupScheduleToJsonPayload.java b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/binders/BindBackupScheduleToJsonPayload.java index 03b7ee2982..89c64ff620 100644 --- a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/binders/BindBackupScheduleToJsonPayload.java +++ b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/binders/BindBackupScheduleToJsonPayload.java @@ -45,13 +45,13 @@ public class BindBackupScheduleToJsonPayload extends BindToJsonPayload { } @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { throw new IllegalStateException("Replace Backup Schedule needs an BackupSchedule object, not a Map"); } @Override public R bindToRequest(R request, Object toBind) { checkArgument(toBind instanceof BackupSchedule, "this binder is only valid for BackupSchedules!"); - return super.bindToRequest(request, ImmutableMap.of("backupSchedule", toBind)); + return super.bindToRequest(request, (Object) ImmutableMap.of("backupSchedule", toBind)); } } diff --git a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/CreateServerOptions.java b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/CreateServerOptions.java index 63b947a9c7..180c42aec0 100644 --- a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/CreateServerOptions.java +++ b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/CreateServerOptions.java @@ -94,10 +94,10 @@ public class CreateServerOptions implements MapBinder { private String publicIp; @Override - public R bindToRequest(R request, Map postParams) { - ServerRequest server = new ServerRequest(checkNotNull(postParams.get("name"), "name parameter not present"), - Integer.parseInt(checkNotNull(postParams.get("imageId"), "imageId parameter not present")), Integer - .parseInt(checkNotNull(postParams.get("flavorId"), "flavorId parameter not present"))); + public R bindToRequest(R request, Map postParams) { + ServerRequest server = new ServerRequest(checkNotNull(postParams.get("name"), "name parameter not present").toString(), + Integer.parseInt(checkNotNull(postParams.get("imageId"), "imageId parameter not present").toString()), + Integer.parseInt(checkNotNull(postParams.get("flavorId"), "flavorId parameter not present").toString())); if (metadata.size() > 0) server.metadata = metadata; if (files.size() > 0) @@ -202,7 +202,7 @@ public class CreateServerOptions implements MapBinder { public static class Builder { /** - * @see CreateServerOptions#withFile(String,byte []) + * @see CreateServerOptions#withFile(String,byte[]) */ public static CreateServerOptions withFile(String path, byte[] contents) { CreateServerOptions options = new CreateServerOptions(); diff --git a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/CreateSharedIpGroupOptions.java b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/CreateSharedIpGroupOptions.java index 9091fb1e26..38ef57a8e0 100644 --- a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/CreateSharedIpGroupOptions.java +++ b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/CreateSharedIpGroupOptions.java @@ -57,8 +57,8 @@ public class CreateSharedIpGroupOptions implements MapBinder { } @Override - public R bindToRequest(R request, Map postParams) { - SharedIpGroupRequest createRequest = new SharedIpGroupRequest(checkNotNull(postParams.get("name")), serverId); + public R bindToRequest(R request, Map postParams) { + SharedIpGroupRequest createRequest = new SharedIpGroupRequest(checkNotNull(postParams.get("name")).toString(), serverId); return jsonBinder.bindToRequest(request, ImmutableMap.of("sharedIpGroup", createRequest)); } diff --git a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/RebuildServerOptions.java b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/RebuildServerOptions.java index 894176ca89..e9e20525b3 100644 --- a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/RebuildServerOptions.java +++ b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/options/RebuildServerOptions.java @@ -43,7 +43,7 @@ public class RebuildServerOptions implements MapBinder { Integer imageId; @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { Map image = Maps.newHashMap(); if (imageId != null) image.put("imageId", imageId); diff --git a/apis/cloudservers/src/test/java/org/jclouds/cloudservers/options/CreateServerOptionsTest.java b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/options/CreateServerOptionsTest.java index 8305566b7e..d0e07cc1f4 100644 --- a/apis/cloudservers/src/test/java/org/jclouds/cloudservers/options/CreateServerOptionsTest.java +++ b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/options/CreateServerOptionsTest.java @@ -55,7 +55,7 @@ public class CreateServerOptionsTest { private HttpRequest buildRequest(CreateServerOptions options) { injector.injectMembers(options); HttpRequest request = new HttpRequest(HttpMethod.POST, URI.create("http://localhost")); - options.bindToRequest(request, ImmutableMap.of("name", "foo", "imageId", "1", "flavorId", "2")); + options.bindToRequest(request, ImmutableMap.of("name", "foo", "imageId", "1", "flavorId", "2")); return request; } diff --git a/apis/cloudservers/src/test/java/org/jclouds/cloudservers/options/CreateSharedIpGroupOptionsTest.java b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/options/CreateSharedIpGroupOptionsTest.java index 40838ef4ac..5604526968 100644 --- a/apis/cloudservers/src/test/java/org/jclouds/cloudservers/options/CreateSharedIpGroupOptionsTest.java +++ b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/options/CreateSharedIpGroupOptionsTest.java @@ -53,7 +53,7 @@ public class CreateSharedIpGroupOptionsTest { private HttpRequest buildRequest(CreateSharedIpGroupOptions options) { injector.injectMembers(options); HttpRequest request = new HttpRequest(HttpMethod.POST, URI.create("http://localhost")); - options.bindToRequest(request, ImmutableMap.of("name", "foo")); + options.bindToRequest(request, ImmutableMap.of("name", "foo")); return request; } diff --git a/apis/cloudservers/src/test/java/org/jclouds/cloudservers/options/RebuildServerOptionsTest.java b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/options/RebuildServerOptionsTest.java index 42c58151a5..a2cbacc294 100644 --- a/apis/cloudservers/src/test/java/org/jclouds/cloudservers/options/RebuildServerOptionsTest.java +++ b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/options/RebuildServerOptionsTest.java @@ -53,7 +53,7 @@ public class RebuildServerOptionsTest { private HttpRequest buildRequest(RebuildServerOptions options) { injector.injectMembers(options); HttpRequest request = new HttpRequest(HttpMethod.POST, URI.create("http://localhost")); - options.bindToRequest(request, new HashMap()); + options.bindToRequest(request, new HashMap()); return request; } diff --git a/apis/cloudsigma/src/main/java/org/jclouds/cloudsigma/binders/BindCloneDriveOptionsToPlainTextString.java b/apis/cloudsigma/src/main/java/org/jclouds/cloudsigma/binders/BindCloneDriveOptionsToPlainTextString.java index 297a5f7ed2..e784ff24a4 100644 --- a/apis/cloudsigma/src/main/java/org/jclouds/cloudsigma/binders/BindCloneDriveOptionsToPlainTextString.java +++ b/apis/cloudsigma/src/main/java/org/jclouds/cloudsigma/binders/BindCloneDriveOptionsToPlainTextString.java @@ -24,6 +24,7 @@ import static com.google.common.base.Preconditions.checkState; import java.util.Map; +import javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Singleton; import javax.ws.rs.core.MediaType; @@ -35,8 +36,10 @@ import org.jclouds.http.HttpRequest; import org.jclouds.rest.MapBinder; import org.jclouds.rest.internal.GeneratedHttpRequest; +import com.google.common.base.Function; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; /** * @@ -53,7 +56,7 @@ public class BindCloneDriveOptionsToPlainTextString implements MapBinder { } @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, "this binder is only valid for GeneratedHttpRequests!"); @SuppressWarnings("unchecked") @@ -62,10 +65,15 @@ public class BindCloneDriveOptionsToPlainTextString implements MapBinder { CloneDriveOptions options = findOptionsInArgsOrNull(gRequest); if (options != null) { - postParams = ImmutableMap. builder().putAll(postParams).putAll(options.getOptions()).build(); + postParams = ImmutableMap. builder().putAll(postParams).putAll(options.getOptions()).build(); } - request.setPayload(listOfMapsToListOfKeyValuesDelimitedByBlankLines.apply(ImmutableSet.of(postParams))); + request.setPayload(listOfMapsToListOfKeyValuesDelimitedByBlankLines.apply(ImmutableSet.of(Maps.transformValues(postParams, new Function() { + @Override + public String apply(@Nullable Object input) { + return input == null ? null : input.toString(); + } + })))); request.getPayload().getContentMetadata().setContentType(MediaType.TEXT_PLAIN); return request; } diff --git a/apis/cloudsigma/src/test/java/org/jclouds/cloudsigma/binders/BindCloneDriveOptionsToPlainTextStringTest.java b/apis/cloudsigma/src/test/java/org/jclouds/cloudsigma/binders/BindCloneDriveOptionsToPlainTextStringTest.java index e040fee83c..a360e67605 100644 --- a/apis/cloudsigma/src/test/java/org/jclouds/cloudsigma/binders/BindCloneDriveOptionsToPlainTextStringTest.java +++ b/apis/cloudsigma/src/test/java/org/jclouds/cloudsigma/binders/BindCloneDriveOptionsToPlainTextStringTest.java @@ -47,16 +47,16 @@ public class BindCloneDriveOptionsToPlainTextStringTest { BindCloneDriveOptionsToPlainTextString.class); public void testDefault() throws IOException { - assertInputAndArgsCreatesPayload(ImmutableMap.of("name", "newdrive"), ImmutableList. of(), + assertInputAndArgsCreatesPayload(ImmutableMap.of("name", "newdrive"), ImmutableList. of(), "name newdrive"); } public void testWithSize() throws IOException { - assertInputAndArgsCreatesPayload(ImmutableMap.of("name", "newdrive"), + assertInputAndArgsCreatesPayload(ImmutableMap.of("name", "newdrive"), ImmutableList. of(new CloneDriveOptions().size(1024)), "name newdrive\nsize 1024"); } - protected void assertInputAndArgsCreatesPayload(ImmutableMap inputMap, List args, + protected void assertInputAndArgsCreatesPayload(ImmutableMap inputMap, List args, String expected) { GeneratedHttpRequest request = createMock(GeneratedHttpRequest.class); expect(request.getArgs()).andReturn(args).atLeastOnce(); diff --git a/apis/nova/src/main/java/org/jclouds/openstack/nova/NovaAsyncClient.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/NovaAsyncClient.java index 6096ec6ba8..3b05db1eb7 100644 --- a/apis/nova/src/main/java/org/jclouds/openstack/nova/NovaAsyncClient.java +++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/NovaAsyncClient.java @@ -310,7 +310,7 @@ public interface NovaAsyncClient { @PayloadParam("serverId") int serverId); /** - * @see NovaClient#listAddresses + * @see NovaClient#getAddresses */ @GET @Unwrap diff --git a/apis/nova/src/main/java/org/jclouds/openstack/nova/options/CreateServerOptions.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/options/CreateServerOptions.java index 902d074d0a..d75b22778f 100644 --- a/apis/nova/src/main/java/org/jclouds/openstack/nova/options/CreateServerOptions.java +++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/options/CreateServerOptions.java @@ -100,10 +100,10 @@ public class CreateServerOptions implements MapBinder { private String adminPass; @Override - public R bindToRequest(R request, Map postParams) { - ServerRequest server = new ServerRequest(checkNotNull(postParams.get("name"), "name parameter not present"), - checkNotNull(postParams.get("imageRef"), "imageRef parameter not present"), checkNotNull(postParams - .get("flavorRef"), "flavorRef parameter not present")); + public R bindToRequest(R request, Map postParams) { + ServerRequest server = new ServerRequest(checkNotNull(postParams.get("name"), "name parameter not present").toString(), + checkNotNull(postParams.get("imageRef"), "imageRef parameter not present").toString(), + checkNotNull(postParams.get("flavorRef"), "flavorRef parameter not present").toString()); if (metadata.size() > 0) server.metadata = metadata; if (files.size() > 0) @@ -202,7 +202,7 @@ public class CreateServerOptions implements MapBinder { public static class Builder { /** - * @see CreateServerOptions#withFile(String,byte []) + * @see CreateServerOptions#withFile(String,byte[]) */ public static CreateServerOptions withFile(String path, byte[] contents) { CreateServerOptions options = new CreateServerOptions(); @@ -231,7 +231,7 @@ public class CreateServerOptions implements MapBinder { } /** - * @see CreateServerOptions#withGroupName(String) + * @see CreateServerOptions#withSecurityGroup(String) */ public static CreateServerOptions withSecurityGroup(String name) { CreateServerOptions options = new CreateServerOptions(); diff --git a/apis/nova/src/main/java/org/jclouds/openstack/nova/options/RebuildServerOptions.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/options/RebuildServerOptions.java index 5debf75ec1..b6602529fb 100644 --- a/apis/nova/src/main/java/org/jclouds/openstack/nova/options/RebuildServerOptions.java +++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/options/RebuildServerOptions.java @@ -44,7 +44,7 @@ public class RebuildServerOptions implements MapBinder { String imageRef; @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { Map image = Maps.newHashMap(); if (imageRef != null) image.put("imageRef", imageRef); diff --git a/apis/nova/src/test/java/org/jclouds/openstack/nova/options/CreateServerOptionsTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/options/CreateServerOptionsTest.java index daa3688a71..87c7bb56c0 100644 --- a/apis/nova/src/test/java/org/jclouds/openstack/nova/options/CreateServerOptionsTest.java +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/options/CreateServerOptionsTest.java @@ -55,7 +55,7 @@ public class CreateServerOptionsTest { private HttpRequest buildRequest(CreateServerOptions options) { injector.injectMembers(options); HttpRequest request = new HttpRequest(HttpMethod.POST, URI.create("http://localhost")); - options.bindToRequest(request, ImmutableMap.of("name", "foo", "imageRef", "1", "flavorRef", "2")); + options.bindToRequest(request, ImmutableMap.of("name", "foo", "imageRef", "1", "flavorRef", "2")); return request; } diff --git a/apis/nova/src/test/java/org/jclouds/openstack/nova/options/RebuildServerOptionsTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/options/RebuildServerOptionsTest.java index 1988c220cd..7ef6215ac4 100644 --- a/apis/nova/src/test/java/org/jclouds/openstack/nova/options/RebuildServerOptionsTest.java +++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/options/RebuildServerOptionsTest.java @@ -53,7 +53,7 @@ public class RebuildServerOptionsTest { private HttpRequest buildRequest(RebuildServerOptions options) { injector.injectMembers(options); HttpRequest request = new HttpRequest(HttpMethod.POST, URI.create("http://localhost")); - options.bindToRequest(request, new HashMap()); + options.bindToRequest(request, new HashMap()); return request; } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindAggregateMetadataToJsonPayload.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindAggregateMetadataToJsonPayload.java deleted file mode 100644 index f3c867f7c5..0000000000 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindAggregateMetadataToJsonPayload.java +++ /dev/null @@ -1,39 +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.openstack.nova.v1_1.binders; - -import java.util.Map; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import org.jclouds.json.Json; - -import com.google.inject.TypeLiteral; - -/** - * @author Adam Lowe - */ -@Singleton -public class BindAggregateMetadataToJsonPayload extends BindObjectToJsonPayload> { - @Inject - public BindAggregateMetadataToJsonPayload(Json jsonBinder) { - super(jsonBinder, "metadata", new TypeLiteral>(){}, "set_metadata"); - } -} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindExtraSpecsToJsonPayload.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindExtraSpecsToJsonPayload.java deleted file mode 100644 index 3eaac00c9e..0000000000 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindExtraSpecsToJsonPayload.java +++ /dev/null @@ -1,39 +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.openstack.nova.v1_1.binders; - -import java.util.Map; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import org.jclouds.json.Json; - -import com.google.inject.TypeLiteral; - -/** - * @author Adam Lowe - */ -@Singleton -public class BindExtraSpecsToJsonPayload extends BindObjectToJsonPayload> { - @Inject - public BindExtraSpecsToJsonPayload(Json jsonBinder) { - super(jsonBinder, "extra_specs", new TypeLiteral>(){}); - } -} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindObjectToJsonPayload.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindObjectToJsonPayload.java deleted file mode 100644 index 4f4541b111..0000000000 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindObjectToJsonPayload.java +++ /dev/null @@ -1,86 +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.openstack.nova.v1_1.binders; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.Map; - -import org.jclouds.http.HttpRequest; -import org.jclouds.json.Json; -import org.jclouds.rest.MapBinder; -import org.jclouds.rest.binders.BindToJsonPayload; -import org.jclouds.rest.internal.GeneratedHttpRequest; - -import com.google.common.base.Predicates; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap.Builder; -import com.google.common.collect.Iterables; -import com.google.inject.TypeLiteral; - -/** - * @author Adam Lowe - */ -public abstract class BindObjectToJsonPayload extends BindToJsonPayload implements MapBinder { - private final String fieldName; - private final String wrapperName; - private final TypeLiteral type; - - /** Bind a specific argument to the json payload - * - * @param fieldName the name of the output json field - * @param fieldType the type of the object to select from the method arguments - * @param wrapperName the name of the json field wrapper (if any) - */ - public BindObjectToJsonPayload(Json jsonBinder, String fieldName, TypeLiteral fieldType, String wrapperName) { - super(jsonBinder); - this.fieldName = fieldName; - this.wrapperName = wrapperName; - this.type = fieldType; - } - - public BindObjectToJsonPayload(Json jsonBinder, String fieldName, TypeLiteral fieldType) { - this(jsonBinder, fieldName, fieldType, null); - } - - @Override - public R bindToRequest(R request, Object toBind) { - throw new IllegalStateException("BindMapToJsonPayload needs parameters"); - } - - @SuppressWarnings("unchecked") - @Override - public R bindToRequest(R request, Map postParams) { - Builder payload = ImmutableMap.builder(); - payload.putAll(postParams); - checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, - "this binder is only valid for GeneratedHttpRequests!"); - GeneratedHttpRequest gRequest = (GeneratedHttpRequest) request; - - T specs = (T) Iterables.find(gRequest.getArgs(), Predicates.instanceOf(type.getRawType())); - payload.put(fieldName, specs); - - if (wrapperName != null) { - return super.bindToRequest(request, ImmutableMap.of(wrapperName, payload.build())); - } - - return super.bindToRequest(request, payload.build()); - } -} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindQuotaClassToJsonPayload.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindQuotaClassToJsonPayload.java deleted file mode 100644 index 21818fb068..0000000000 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindQuotaClassToJsonPayload.java +++ /dev/null @@ -1,38 +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.openstack.nova.v1_1.binders; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import org.jclouds.json.Json; -import org.jclouds.openstack.nova.v1_1.domain.QuotaClass; - -import com.google.inject.TypeLiteral; - -/** - * @author Adam Lowe - */ -@Singleton -public class BindQuotaClassToJsonPayload extends BindObjectToJsonPayload { - @Inject - public BindQuotaClassToJsonPayload(Json jsonBinder) { - super(jsonBinder, "quota_class_set", new TypeLiteral(){}); - } -} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindQuotasToJsonPayload.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindQuotasToJsonPayload.java deleted file mode 100644 index e350b0a3a4..0000000000 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindQuotasToJsonPayload.java +++ /dev/null @@ -1,38 +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.openstack.nova.v1_1.binders; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import org.jclouds.json.Json; -import org.jclouds.openstack.nova.v1_1.domain.Quotas; - -import com.google.inject.TypeLiteral; - -/** - * @author Adam Lowe - */ -@Singleton -public class BindQuotasToJsonPayload extends BindObjectToJsonPayload { - @Inject - public BindQuotasToJsonPayload(Json jsonBinder) { - super(jsonBinder, "quota_set", new TypeLiteral(){}); - } -} \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindSecurityGroupRuleToJsonPayload.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindSecurityGroupRuleToJsonPayload.java index ee9fec20ec..cd88ec12c6 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindSecurityGroupRuleToJsonPayload.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/binders/BindSecurityGroupRuleToJsonPayload.java @@ -56,8 +56,8 @@ public class BindSecurityGroupRuleToJsonPayload extends BindToJsonPayload implem } @Override - public R bindToRequest(R request, Map postParams) { - Builder payload = ImmutableMap.builder(); + public R bindToRequest(R request, Map postParams) { + Builder payload = ImmutableMap.builder(); payload.putAll(postParams); checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, "this binder is only valid for GeneratedHttpRequests!"); diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsAsyncClient.java index 8c30451fb0..94cf140cf6 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsAsyncClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsAsyncClient.java @@ -37,6 +37,7 @@ import org.jclouds.rest.annotations.PayloadParam; import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.ResponseParser; import org.jclouds.rest.annotations.SkipEncoding; +import org.jclouds.rest.annotations.WrapWith; import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions; import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404; @@ -114,7 +115,7 @@ public interface AdminActionsAsyncClient { @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Payload("%7B\"createBackup\":%7B\"name\":\"{name}\",\"backup_type\":\"{backup_type}\",\"rotation\":{rotation}%7D%7D") + @WrapWith("createBackup") @ExceptionParser(MapHttp4xxCodesToExceptions.class) @ResponseParser(ParseImageIdFromLocationHeader.class) ListenableFuture createBackupOfServer(@PathParam("id") String id, @@ -156,9 +157,9 @@ public interface AdminActionsAsyncClient { @POST @Produces(MediaType.APPLICATION_JSON) @ExceptionParser(ReturnFalseOnNotFoundOr404.class) - @Payload("%7B\"os-migrateLive\":%7B\"host\":\"{host}\",\"block_migration\":\"{bm}\",\"disk_over_commit\":\"{doc}\"%7D%7D") + @WrapWith("os-migrateLive") ListenableFuture liveMigrateServer(@PathParam("id") String id, @PayloadParam("host") String host, - @PayloadParam("bm") boolean blockMigration, - @PayloadParam("doc") boolean diskOverCommit); + @PayloadParam("block_migration") boolean blockMigration, + @PayloadParam("disk_over_commit") boolean diskOverCommit); } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FlavorExtraSpecsAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FlavorExtraSpecsAsyncClient.java index 9ec31041d2..24ea199e4a 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FlavorExtraSpecsAsyncClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FlavorExtraSpecsAsyncClient.java @@ -33,7 +33,6 @@ import javax.ws.rs.core.MediaType; import org.jclouds.concurrent.Timeout; import org.jclouds.openstack.filters.AuthenticateRequest; -import org.jclouds.openstack.nova.v1_1.binders.BindExtraSpecsToJsonPayload; import org.jclouds.openstack.services.Extension; import org.jclouds.openstack.services.ServiceType; import org.jclouds.rest.annotations.ExceptionParser; @@ -43,6 +42,7 @@ import org.jclouds.rest.annotations.PayloadParam; import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.SelectJson; import org.jclouds.rest.annotations.Unwrap; +import org.jclouds.rest.binders.BindToJsonPayload; import org.jclouds.rest.functions.ReturnEmptyMapOnNotFoundOr404; import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; @@ -79,8 +79,8 @@ public interface FlavorExtraSpecsAsyncClient { @Path("/flavors/{flavor_id}/os-extra_specs") @Produces(MediaType.APPLICATION_JSON) @ExceptionParser(ReturnFalseOnNotFoundOr404.class) - @MapBinder(BindExtraSpecsToJsonPayload.class) - ListenableFuture setAllExtraSpecs(@PathParam("flavor_id") String flavorId, Map specs); + @MapBinder(BindToJsonPayload.class) + ListenableFuture setAllExtraSpecs(@PathParam("flavor_id") String flavorId, @PayloadParam("extra_specs") Map specs); /** * @see FlavorExtraSpecsClient#getExtraSpec(String, String) diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/HostAggregateAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/HostAggregateAsyncClient.java index 79cb209bba..d5b492e649 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/HostAggregateAsyncClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/HostAggregateAsyncClient.java @@ -33,16 +33,14 @@ import javax.ws.rs.core.MediaType; import org.jclouds.concurrent.Timeout; import org.jclouds.openstack.filters.AuthenticateRequest; -import org.jclouds.openstack.nova.v1_1.binders.BindAggregateMetadataToJsonPayload; import org.jclouds.openstack.nova.v1_1.domain.HostAggregate; import org.jclouds.openstack.services.Extension; import org.jclouds.openstack.services.ServiceType; import org.jclouds.rest.annotations.ExceptionParser; -import org.jclouds.rest.annotations.MapBinder; -import org.jclouds.rest.annotations.Payload; import org.jclouds.rest.annotations.PayloadParam; import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.SelectJson; +import org.jclouds.rest.annotations.WrapWith; import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; @@ -87,8 +85,9 @@ public interface HostAggregateAsyncClient { @SelectJson("aggregate") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Payload("%7B\"aggregate\":%7B\"name\":\"{name}\",\"availability_zone\":\"{zone}\"%7D%7D") - ListenableFuture createAggregate(@PayloadParam("name") String name, @PayloadParam("zone") String availablityZone); + @WrapWith("aggregate") + ListenableFuture createAggregate(@PayloadParam("name") String name, + @PayloadParam("availability_zone") String availablityZone); /** * @see HostAggregateClient#updateName @@ -97,7 +96,7 @@ public interface HostAggregateAsyncClient { @Path("/{id}") @SelectJson("aggregate") @Consumes(MediaType.APPLICATION_JSON) - @Payload("%7B\"aggregate\":%7B\"name\":\"{name}\"%7D%7D") + @WrapWith("aggregate") ListenableFuture updateName(@PathParam("id") String id, @PayloadParam("name") String name); /** @@ -107,8 +106,8 @@ public interface HostAggregateAsyncClient { @Path("/{id}") @SelectJson("aggregate") @Consumes(MediaType.APPLICATION_JSON) - @Payload("%7B\"aggregate\":%7B\"availability_zone\":\"{zone}\"%7D%7D") - ListenableFuture updateAvailabilityZone(@PathParam("id") String id, @PayloadParam("zone") String availabilityZone); + @WrapWith("aggregate") + ListenableFuture updateAvailabilityZone(@PathParam("id") String id, @PayloadParam("availability_zone") String availabilityZone); /** * @see HostAggregateClient#deleteAggregate(String) @@ -127,7 +126,7 @@ public interface HostAggregateAsyncClient { @SelectJson("aggregate") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Payload("%7B\"add_host\":%7B\"host\":\"{host}\"%7D%7D") + @WrapWith("add_host") ListenableFuture addHost(@PathParam("id") String id, @PayloadParam("host") String host); @@ -139,7 +138,7 @@ public interface HostAggregateAsyncClient { @SelectJson("aggregate") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Payload("%7B\"remove_host\":%7B\"host\":\"{host}\"%7D%7D") + @WrapWith("remove_host") ListenableFuture removeHost(@PathParam("id") String id, @PayloadParam("host") String host); /** @@ -150,6 +149,6 @@ public interface HostAggregateAsyncClient { @SelectJson("aggregate") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @MapBinder(BindAggregateMetadataToJsonPayload.class) - ListenableFuture setMetadata(@PathParam("id") String id, Map metadata); + @WrapWith("set_metadata") + ListenableFuture setMetadata(@PathParam("id") String id, @PayloadParam("metadata") Map metadata); } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaAsyncClient.java index b85d14e0a9..f1e042fc80 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaAsyncClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaAsyncClient.java @@ -30,14 +30,15 @@ import javax.ws.rs.core.MediaType; import org.jclouds.concurrent.Timeout; import org.jclouds.openstack.filters.AuthenticateRequest; -import org.jclouds.openstack.nova.v1_1.binders.BindQuotasToJsonPayload; import org.jclouds.openstack.nova.v1_1.domain.Quotas; import org.jclouds.openstack.services.Extension; import org.jclouds.openstack.services.ServiceType; import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.MapBinder; +import org.jclouds.rest.annotations.PayloadParam; import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.SelectJson; +import org.jclouds.rest.binders.BindToJsonPayload; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; import com.google.common.util.concurrent.ListenableFuture; @@ -71,8 +72,8 @@ public interface QuotaAsyncClient { @PUT @Path("/{tenant_id}") @Produces(MediaType.APPLICATION_JSON) - @MapBinder(BindQuotasToJsonPayload.class) - ListenableFuture updateQuotasForTenant(@PathParam("tenant_id") String tenantId, Quotas quotas); + @MapBinder(BindToJsonPayload.class) + ListenableFuture updateQuotasForTenant(@PathParam("tenant_id") String tenantId, @PayloadParam("quota_set") Quotas quotas); /** * @see QuotaClient#getDefaultQuotasForTenant(String) diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClassAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClassAsyncClient.java index da76b11814..d307044c53 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClassAsyncClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/QuotaClassAsyncClient.java @@ -30,15 +30,16 @@ import javax.ws.rs.core.MediaType; import org.jclouds.concurrent.Timeout; import org.jclouds.openstack.filters.AuthenticateRequest; -import org.jclouds.openstack.nova.v1_1.binders.BindQuotaClassToJsonPayload; import org.jclouds.openstack.nova.v1_1.domain.QuotaClass; -import org.jclouds.openstack.nova.v1_1.domain.Quotas; import org.jclouds.openstack.services.Extension; import org.jclouds.openstack.services.ServiceType; import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.MapBinder; +import org.jclouds.rest.annotations.PayloadParam; import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.SelectJson; +import org.jclouds.rest.annotations.WrapWith; +import org.jclouds.rest.binders.BindToJsonPayload; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; import com.google.common.util.concurrent.ListenableFuture; @@ -73,7 +74,7 @@ public interface QuotaClassAsyncClient { @PUT @Path("/{id}") @Produces(MediaType.APPLICATION_JSON) - @MapBinder(BindQuotaClassToJsonPayload.class) - ListenableFuture updateQuotaClass(@PathParam("id") String id, QuotaClass quotas); + @MapBinder(BindToJsonPayload.class) + ListenableFuture updateQuotaClass(@PathParam("id") String id, @PayloadParam("quota_class_set") QuotaClass quotas); } \ No newline at end of file diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeAsyncClient.java index b27af9b13e..33039a0a7b 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeAsyncClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeAsyncClient.java @@ -44,6 +44,7 @@ import org.jclouds.rest.annotations.PayloadParam; import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.SelectJson; import org.jclouds.rest.annotations.SkipEncoding; +import org.jclouds.rest.annotations.WrapWith; import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; @@ -156,8 +157,8 @@ public interface VolumeAsyncClient { @SelectJson("volumeAttachment") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @Payload("%7B\"volumeAttachment\":%7B\"volumeId\":\"{id}\",\"device\":\"{device}\"%7D%7D") - ListenableFuture attachVolumeToServerAsDevice(@PayloadParam("id") String volumeId, + @WrapWith("volumeAttachment") + ListenableFuture attachVolumeToServerAsDevice(@PayloadParam("volumeId") String volumeId, @PathParam("server_id") String serverId, @PayloadParam("device") String device); /** diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeTypeAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeTypeAsyncClient.java index 6a2ebb0ced..dd7caf5ef7 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeTypeAsyncClient.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeTypeAsyncClient.java @@ -20,7 +20,6 @@ package org.jclouds.openstack.nova.v1_1.extensions; import java.util.Map; import java.util.Set; -import java.util.concurrent.TimeUnit; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -32,9 +31,7 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; -import org.jclouds.concurrent.Timeout; import org.jclouds.openstack.filters.AuthenticateRequest; -import org.jclouds.openstack.nova.v1_1.binders.BindExtraSpecsToJsonPayload; import org.jclouds.openstack.nova.v1_1.domain.VolumeType; import org.jclouds.openstack.nova.v1_1.options.CreateVolumeTypeOptions; import org.jclouds.openstack.services.Extension; @@ -47,6 +44,8 @@ import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.SelectJson; import org.jclouds.rest.annotations.SkipEncoding; import org.jclouds.rest.annotations.Unwrap; +import org.jclouds.rest.annotations.WrapWith; +import org.jclouds.rest.binders.BindToJsonPayload; import org.jclouds.rest.functions.ReturnEmptyMapOnNotFoundOr404; import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404; @@ -91,7 +90,7 @@ public interface VolumeTypeAsyncClient { @POST @SelectJson("volume_type") @Produces(MediaType.APPLICATION_JSON) - @Payload("%7B\"volume_type\":%7B\"name\":\"{name}\"%7D%7D") + @WrapWith("volume_type") ListenableFuture createVolumeType(@PayloadParam("name") String name, CreateVolumeTypeOptions... options); /** @@ -118,8 +117,8 @@ public interface VolumeTypeAsyncClient { @Path("/{id}/extra_specs") @Produces(MediaType.APPLICATION_JSON) @ExceptionParser(ReturnFalseOnNotFoundOr404.class) - @MapBinder(BindExtraSpecsToJsonPayload.class) - ListenableFuture setAllExtraSpecs(@PathParam("id") String id, Map specs); + @MapBinder(BindToJsonPayload.class) + ListenableFuture setAllExtraSpecs(@PathParam("id") String id, @PayloadParam("extra_specs") Map specs); /** * @see VolumeTypeClient#getExtraSpec(String, String) diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateBackupOfServerOptions.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateBackupOfServerOptions.java index eede730e91..c2fbe5178a 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateBackupOfServerOptions.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateBackupOfServerOptions.java @@ -46,7 +46,7 @@ public class CreateBackupOfServerOptions implements MapBinder { private Map metadata = ImmutableMap.of(); @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { Map data = Maps.newHashMap(); data.putAll(postParams); data.put("metadata", metadata); diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateServerOptions.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateServerOptions.java index ae36ea1455..7dc9b377e8 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateServerOptions.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateServerOptions.java @@ -166,10 +166,10 @@ public class CreateServerOptions implements MapBinder { } @Override - public R bindToRequest(R request, Map postParams) { - ServerRequest server = new ServerRequest(checkNotNull(postParams.get("name"), "name parameter not present"), - checkNotNull(postParams.get("imageRef"), "imageRef parameter not present"), checkNotNull( - postParams.get("flavorRef"), "flavorRef parameter not present")); + public R bindToRequest(R request, Map postParams) { + ServerRequest server = new ServerRequest(checkNotNull(postParams.get("name"), "name parameter not present").toString(), + checkNotNull(postParams.get("imageRef"), "imageRef parameter not present").toString(), + checkNotNull(postParams.get("flavorRef"), "flavorRef parameter not present").toString()); if (metadata.size() > 0) server.metadata = metadata; if (personality.size() > 0) diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateVolumeOptions.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateVolumeOptions.java index 7ae912d8b8..3ab0367367 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateVolumeOptions.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateVolumeOptions.java @@ -54,7 +54,7 @@ public class CreateVolumeOptions implements MapBinder { private Map metadata = ImmutableMap.of(); @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { Map image = Maps.newHashMap(); image.putAll(postParams); if (name != null) diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateVolumeSnapshotOptions.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateVolumeSnapshotOptions.java index df9b628970..94a0d77c32 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateVolumeSnapshotOptions.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateVolumeSnapshotOptions.java @@ -48,8 +48,8 @@ public class CreateVolumeSnapshotOptions implements MapBinder { private boolean force = false; @Override - public R bindToRequest(R request, Map postParams) { - Map data = Maps.newHashMap(postParams); + public R bindToRequest(R request, Map postParams) { + Map data = Maps.newHashMap(postParams); if (name != null) data.put("display_name", name); if (description != null) diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateVolumeTypeOptions.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateVolumeTypeOptions.java index fc40855b9f..865eaf8427 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateVolumeTypeOptions.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/CreateVolumeTypeOptions.java @@ -46,7 +46,7 @@ public class CreateVolumeTypeOptions implements MapBinder { protected Map specs = ImmutableMap.of(); @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { Map data = Maps.newHashMap(); data.putAll(postParams); data.put("extra_specs", specs); diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/RebuildServerOptions.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/RebuildServerOptions.java index 42eee7df4b..a6d71f4824 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/RebuildServerOptions.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/options/RebuildServerOptions.java @@ -44,7 +44,7 @@ public class RebuildServerOptions implements MapBinder { String imageRef; @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { Map image = Maps.newHashMap(); if (imageRef != null) image.put("imageRef", imageRef); diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsClientExpectTest.java index 7a1cc628f4..18ac61f537 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsClientExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/AdminActionsClientExpectTest.java @@ -294,7 +294,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, standardRequestBuilder(endpoint).method("POST") - .payload(payloadFromStringWithContentType("{\"createBackup\":{\"backup_type\":\"weekly\",\"rotation\":\"3\",\"name\":\"mybackup\",\"metadata\":{\"some\":\"data or other\"}}}", MediaType.APPLICATION_JSON)).build(), + .payload(payloadFromStringWithContentType("{\"createBackup\":{\"backup_type\":\"weekly\",\"rotation\":3,\"name\":\"mybackup\",\"metadata\":{\"some\":\"data or other\"}}}", MediaType.APPLICATION_JSON)).build(), standardResponseBuilder(202).headers(ImmutableMultimap.of("Location", "http://172.16.89.149:8774/v2/images/1976b3b3-409a-468d-b16c-a9172c341b46")).build() ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); @@ -309,7 +309,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, standardRequestBuilder(endpoint).method("POST") - .payload(payloadFromStringWithContentType("{\"createBackup\":{\"backup_type\":\"weekly\",\"rotation\":\"3\",\"name\":\"mybackup\",\"metadata\":{\"some\":\"data or other\"}}}", MediaType.APPLICATION_JSON)).build(), + .payload(payloadFromStringWithContentType("{\"createBackup\":{\"backup_type\":\"weekly\",\"rotation\":3,\"name\":\"mybackup\",\"metadata\":{\"some\":\"data or other\"}}}", MediaType.APPLICATION_JSON)).build(), standardResponseBuilder(404).build() ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); @@ -322,7 +322,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, standardActionRequestBuilderVoidResponse(endpoint, "GONNAOVERWRITE") - .payload(payloadFromStringWithContentType("{\"os-migrateLive\":{\"host\":\"bighost\",\"block_migration\":\"true\",\"disk_over_commit\":\"false\"}}", MediaType.APPLICATION_JSON)).build(), + .payload(payloadFromStringWithContentType("{\"os-migrateLive\":{\"host\":\"bighost\",\"block_migration\":true,\"disk_over_commit\":false}}", MediaType.APPLICATION_JSON)).build(), standardResponseBuilder(202).build() ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); @@ -335,7 +335,7 @@ public class AdminActionsClientExpectTest extends BaseNovaClientExpectTest { keystoneAuthWithUsernameAndPassword, responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, standardActionRequestBuilderVoidResponse(endpoint, "GONNAOVERWRITE") - .payload(payloadFromStringWithContentType("{\"os-migrateLive\":{\"host\":\"bighost\",\"block_migration\":\"true\",\"disk_over_commit\":\"false\"}}", MediaType.APPLICATION_JSON)).build(), + .payload(payloadFromStringWithContentType("{\"os-migrateLive\":{\"host\":\"bighost\",\"block_migration\":true,\"disk_over_commit\":false}}", MediaType.APPLICATION_JSON)).build(), standardResponseBuilder(404).build() ).getAdminActionsExtensionForZone("az-1.region-a.geo-1").get(); diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeClientExpectTest.java index 3e67d534d9..8e24964117 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeClientExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/VolumeClientExpectTest.java @@ -111,7 +111,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { responseWithKeystoneAccess, extensionsOfNovaRequest, extensionsOfNovaResponse, standardRequestBuilder(endpoint) .method("POST") - .payload(payloadFromStringWithContentType("{\"volume\":{\"display_name\":\"jclouds-test-volume\",\"display_description\":\"description of test volume\",\"size\":\"1\"}}", MediaType.APPLICATION_JSON)) + .payload(payloadFromStringWithContentType("{\"volume\":{\"display_name\":\"jclouds-test-volume\",\"display_description\":\"description of test volume\",\"size\":1}}", MediaType.APPLICATION_JSON)) .build(), standardResponseBuilder(200).payload(payloadFromResource("/volume_details.json")).build() ).getVolumeExtensionForZone("az-1.region-a.geo-1").get(); @@ -129,7 +129,7 @@ public class VolumeClientExpectTest extends BaseNovaClientExpectTest { standardRequestBuilder(endpoint) .endpoint(endpoint) .method("POST") - .payload(payloadFromStringWithContentType("{\"volume\":{\"display_name\":\"jclouds-test-volume\",\"display_description\":\"description of test volume\",\"size\":\"1\"}}", MediaType.APPLICATION_JSON)) + .payload(payloadFromStringWithContentType("{\"volume\":{\"display_name\":\"jclouds-test-volume\",\"display_description\":\"description of test volume\",\"size\":1}}", MediaType.APPLICATION_JSON)) .build(), standardResponseBuilder(404).payload(payloadFromResource("/volume_details.json")).build() ).getVolumeExtensionForZone("az-1.region-a.geo-1").get(); diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindCaptureVAppParamsToXmlPayload.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindCaptureVAppParamsToXmlPayload.java index d29bb07798..f9246e9c3a 100644 --- a/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindCaptureVAppParamsToXmlPayload.java +++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindCaptureVAppParamsToXmlPayload.java @@ -64,13 +64,13 @@ public class BindCaptureVAppParamsToXmlPayload implements MapBinder { } @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, "this binder is only valid for GeneratedHttpRequests!"); GeneratedHttpRequest gRequest = (GeneratedHttpRequest) request; checkState(gRequest.getArgs() != null, "args should be initialized at this point"); - String templateName = checkNotNull(postParams.remove("templateName"), "templateName"); - String vApp = checkNotNull(postParams.remove("vApp"), "vApp"); + String templateName = checkNotNull(postParams.remove("templateName"), "templateName").toString(); + String vApp = checkNotNull(postParams.remove("vApp"), "vApp").toString(); CaptureVAppOptions options = findOptionsInArgsOrNull(gRequest); if (options == null) { diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindCatalogItemToXmlPayload.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindCatalogItemToXmlPayload.java index 998e0cc901..844b458fed 100644 --- a/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindCatalogItemToXmlPayload.java +++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindCatalogItemToXmlPayload.java @@ -65,13 +65,13 @@ public class BindCatalogItemToXmlPayload implements MapBinder { } @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, "this binder is only valid for GeneratedHttpRequests!"); GeneratedHttpRequest gRequest = (GeneratedHttpRequest) request; checkState(gRequest.getArgs() != null, "args should be initialized at this point"); - String name = checkNotNull(postParams.get("name"), "name"); - URI entity = URI.create(checkNotNull(postParams.get("Entity"), "Entity")); + String name = checkNotNull(postParams.get("name"), "name").toString(); + URI entity = URI.create(checkNotNull(postParams.get("Entity"), "Entity").toString()); CatalogItemOptions options = findOptionsInArgsOrNew(gRequest); try { diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindCloneParamsToXmlPayload.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindCloneParamsToXmlPayload.java index 9241d53e26..efeedb5bbe 100644 --- a/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindCloneParamsToXmlPayload.java +++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindCloneParamsToXmlPayload.java @@ -65,14 +65,14 @@ public abstract class BindCloneParamsToXmlPayload implem } @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, "this binder is only valid for GeneratedHttpRequests!"); GeneratedHttpRequest gRequest = (GeneratedHttpRequest) request; checkState(gRequest.getArgs() != null, "args should be initialized at this point"); - String name = checkNotNull(postParams.get("name"), "name"); - String source = checkNotNull(postParams.get("Source"), "Source"); - boolean isSourceDelete = Boolean.parseBoolean(postParams.get("IsSourceDelete")); + String name = checkNotNull(postParams.get("name"), "name").toString(); + String source = checkNotNull(postParams.get("Source"), "Source").toString(); + boolean isSourceDelete = Boolean.parseBoolean((String) postParams.get("IsSourceDelete")); O options = findOptionsInArgsOrNew(gRequest); return stringBinder.bindToRequest(request, generateXml(name, source, isSourceDelete, options)); diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindInstantiateVAppTemplateParamsToXmlPayload.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindInstantiateVAppTemplateParamsToXmlPayload.java index 5dc5f7e607..506dd726dc 100644 --- a/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindInstantiateVAppTemplateParamsToXmlPayload.java +++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindInstantiateVAppTemplateParamsToXmlPayload.java @@ -88,13 +88,13 @@ public class BindInstantiateVAppTemplateParamsToXmlPayload implements MapBinder } @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, "this binder is only valid for GeneratedHttpRequests!"); GeneratedHttpRequest gRequest = (GeneratedHttpRequest) request; checkState(gRequest.getArgs() != null, "args should be initialized at this point"); - String name = checkNotNull(postParams.remove("name"), "name"); - URI template = URI.create(checkNotNull(postParams.remove("template"), "template")); + String name = checkNotNull(postParams.remove("name"), "name").toString(); + URI template = URI.create(checkNotNull(postParams.remove("template"), "template").toString()); Set networkConfig = null; diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindParamsToXmlPayload.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindParamsToXmlPayload.java index 897bf3b771..fe356fa9fb 100644 --- a/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindParamsToXmlPayload.java +++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindParamsToXmlPayload.java @@ -57,7 +57,7 @@ public class BindParamsToXmlPayload implements MapBinder { this.stringBinder = stringBinder; } @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { try { return stringBinder.bindToRequest(request, generateXml(postParams)); } catch (Exception e) { @@ -65,11 +65,11 @@ public class BindParamsToXmlPayload implements MapBinder { } } - private String generateXml(Map postParams) throws ParserConfigurationException, + private String generateXml(Map postParams) throws ParserConfigurationException, FactoryConfigurationError, TransformerException { XMLBuilder rootBuilder = XMLBuilder.create(element); - for (Entry entry : postParams.entrySet()) - rootBuilder.a(entry.getKey(), entry.getValue()); + for (Entry entry : postParams.entrySet()) + rootBuilder.a(entry.getKey(), (String) entry.getValue()); rootBuilder.a("xmlns", ns); Properties outputProperties = new Properties(); outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); diff --git a/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindCatalogItemToXmlPayloadTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindCatalogItemToXmlPayloadTest.java index d59b818d32..110f04ac93 100644 --- a/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindCatalogItemToXmlPayloadTest.java +++ b/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindCatalogItemToXmlPayloadTest.java @@ -67,7 +67,7 @@ public class BindCatalogItemToXmlPayloadTest { BindCatalogItemToXmlPayload binder = injector.getInstance(BindCatalogItemToXmlPayload.class); - Map map = ImmutableMap.of("name", "myname", "Entity", "http://fooentity"); + Map map = ImmutableMap.of("name", "myname", "Entity", "http://fooentity"); binder.bindToRequest(request, map); verify(request); diff --git a/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindCloneVAppParamsToXmlPayloadTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindCloneVAppParamsToXmlPayloadTest.java index a2aac9fe2a..954a5ce2e3 100644 --- a/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindCloneVAppParamsToXmlPayloadTest.java +++ b/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindCloneVAppParamsToXmlPayloadTest.java @@ -72,7 +72,7 @@ public class BindCloneVAppParamsToXmlPayloadTest { BindCloneVAppParamsToXmlPayload binder = injector.getInstance(BindCloneVAppParamsToXmlPayload.class); - Builder map = ImmutableMap.builder(); + Builder map = ImmutableMap.builder(); map.put("name", "new-linux-server"); map.put("Source", "https://vcenterprise.bluelock.com/api/v1.0/vapp/201"); binder.bindToRequest(request, map.build()); @@ -92,7 +92,7 @@ public class BindCloneVAppParamsToXmlPayloadTest { BindCloneVAppParamsToXmlPayload binder = injector.getInstance(BindCloneVAppParamsToXmlPayload.class); - Builder map = ImmutableMap.builder(); + Builder map = ImmutableMap.builder(); map.put("name", "new-linux-server"); map.put("Source", "https://vcenterprise.bluelock.com/api/v1.0/vapp/201"); map.put("IsSourceDelete", "true"); @@ -111,7 +111,7 @@ public class BindCloneVAppParamsToXmlPayloadTest { BindCloneVAppParamsToXmlPayload binder = injector.getInstance(BindCloneVAppParamsToXmlPayload.class); - Builder map = ImmutableMap.builder(); + Builder map = ImmutableMap.builder(); map.put("name", "my-vapp"); map.put("Source", "https://vcenterprise.bluelock.com/api/v1.0/vapp/4181"); binder.bindToRequest(request, map.build()); diff --git a/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindCloneVAppTemplateParamsToXmlPayloadTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindCloneVAppTemplateParamsToXmlPayloadTest.java index 8212c52f23..997444ef0e 100644 --- a/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindCloneVAppTemplateParamsToXmlPayloadTest.java +++ b/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindCloneVAppTemplateParamsToXmlPayloadTest.java @@ -73,7 +73,7 @@ public class BindCloneVAppTemplateParamsToXmlPayloadTest { BindCloneVAppTemplateParamsToXmlPayload binder = injector .getInstance(BindCloneVAppTemplateParamsToXmlPayload.class); - Builder map = ImmutableMap.builder(); + Builder map = ImmutableMap.builder(); map.put("name", "new-linux-server"); map.put("Source", "https://vcenterprise.bluelock.com/api/v1.0/vAppTemplate/201"); binder.bindToRequest(request, map.build()); @@ -94,7 +94,7 @@ public class BindCloneVAppTemplateParamsToXmlPayloadTest { BindCloneVAppTemplateParamsToXmlPayload binder = injector .getInstance(BindCloneVAppTemplateParamsToXmlPayload.class); - Builder map = ImmutableMap.builder(); + Builder map = ImmutableMap.builder(); map.put("name", "new-linux-server"); map.put("Source", "https://vcenterprise.bluelock.com/api/v1.0/vAppTemplate/201"); map.put("IsSourceDelete", "true"); @@ -114,7 +114,7 @@ public class BindCloneVAppTemplateParamsToXmlPayloadTest { BindCloneVAppTemplateParamsToXmlPayload binder = injector .getInstance(BindCloneVAppTemplateParamsToXmlPayload.class); - Builder map = ImmutableMap.builder(); + Builder map = ImmutableMap.builder(); map.put("name", "my-vapptemplate"); map.put("Source", "https://vcenterprise.bluelock.com/api/v1.0/vAppTemplate/4181"); binder.bindToRequest(request, map.build()); diff --git a/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindDeployVAppParamsToXmlPayloadTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindDeployVAppParamsToXmlPayloadTest.java index 5c4dd3170c..459ac6c42b 100644 --- a/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindDeployVAppParamsToXmlPayloadTest.java +++ b/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindDeployVAppParamsToXmlPayloadTest.java @@ -62,7 +62,7 @@ public class BindDeployVAppParamsToXmlPayloadTest { BindDeployVAppParamsToXmlPayload binder = injector.getInstance(BindDeployVAppParamsToXmlPayload.class); - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); map.put("powerOn", "true"); binder.bindToRequest(request, map); verify(request); @@ -78,7 +78,7 @@ public class BindDeployVAppParamsToXmlPayloadTest { BindDeployVAppParamsToXmlPayload binder = injector.getInstance(BindDeployVAppParamsToXmlPayload.class); - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); binder.bindToRequest(request, map); verify(request); diff --git a/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindInstantiateVAppTemplateParamsToXmlPayloadTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindInstantiateVAppTemplateParamsToXmlPayloadTest.java index 8fbca524fd..614f81e690 100644 --- a/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindInstantiateVAppTemplateParamsToXmlPayloadTest.java +++ b/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindInstantiateVAppTemplateParamsToXmlPayloadTest.java @@ -126,7 +126,7 @@ public class BindInstantiateVAppTemplateParamsToXmlPayloadTest { BindInstantiateVAppTemplateParamsToXmlPayload binder = createInjector(templateUri, template).getInstance( BindInstantiateVAppTemplateParamsToXmlPayload.class); - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); map.put("name", "my-vapp"); map.put("template", templateUri.toASCIIString()); binder.bindToRequest(request, map); @@ -151,7 +151,7 @@ public class BindInstantiateVAppTemplateParamsToXmlPayloadTest { BindInstantiateVAppTemplateParamsToXmlPayload binder = createInjector(templateUri, template).getInstance( BindInstantiateVAppTemplateParamsToXmlPayload.class); - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); map.put("name", "my-vapp"); map.put("template", templateUri.toASCIIString()); binder.bindToRequest(request, map); @@ -174,7 +174,7 @@ public class BindInstantiateVAppTemplateParamsToXmlPayloadTest { BindInstantiateVAppTemplateParamsToXmlPayload binder = createInjector(templateUri, template).getInstance( BindInstantiateVAppTemplateParamsToXmlPayload.class); - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); map.put("name", "my-vapp"); map.put("template", templateUri.toASCIIString()); binder.bindToRequest(request, map); @@ -200,7 +200,7 @@ public class BindInstantiateVAppTemplateParamsToXmlPayloadTest { BindInstantiateVAppTemplateParamsToXmlPayload binder = createInjector(templateUri, template).getInstance( BindInstantiateVAppTemplateParamsToXmlPayload.class); - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); map.put("name", "my-vapp"); map.put("template", "https://vcenterprise.bluelock.com/api/v1.0/vAppTemplate/3"); @@ -227,7 +227,7 @@ public class BindInstantiateVAppTemplateParamsToXmlPayloadTest { BindInstantiateVAppTemplateParamsToXmlPayload binder = createInjector(templateUri, template).getInstance( BindInstantiateVAppTemplateParamsToXmlPayload.class); - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); map.put("name", "my-vapp"); map.put("template", "https://vcenterprise.bluelock.com/api/v1.0/vAppTemplate/3"); diff --git a/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindUndeployVAppParamsToXmlPayloadTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindUndeployVAppParamsToXmlPayloadTest.java index 790ee6c8da..6bc0b58b53 100644 --- a/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindUndeployVAppParamsToXmlPayloadTest.java +++ b/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindUndeployVAppParamsToXmlPayloadTest.java @@ -62,7 +62,7 @@ public class BindUndeployVAppParamsToXmlPayloadTest { BindUndeployVAppParamsToXmlPayload binder = injector.getInstance(BindUndeployVAppParamsToXmlPayload.class); - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); map.put("saveState", "true"); binder.bindToRequest(request, map); verify(request); @@ -78,7 +78,7 @@ public class BindUndeployVAppParamsToXmlPayloadTest { BindUndeployVAppParamsToXmlPayload binder = injector.getInstance(BindUndeployVAppParamsToXmlPayload.class); - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); binder.bindToRequest(request, map); verify(request); diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/binders/BindCredentialsToJsonPayload.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/binders/BindCredentialsToJsonPayload.java index 4480aa5215..d10066ab40 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/binders/BindCredentialsToJsonPayload.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/binders/BindCredentialsToJsonPayload.java @@ -48,7 +48,7 @@ public class BindCredentialsToJsonPayload extends BindToJsonPayload implements M } @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { return super.bindToRequest(request, ImmutableMap.of("credentials", postParams)); } diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/binders/BindAuthToJsonPayload.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/binders/BindAuthToJsonPayload.java index aa7e591238..6990aecfb4 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/binders/BindAuthToJsonPayload.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/binders/BindAuthToJsonPayload.java @@ -67,7 +67,7 @@ public class BindAuthToJsonPayload extends BindToJsonPayload implements MapBinde } @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, "this binder is only valid for GeneratedHttpRequests!"); GeneratedHttpRequest gRequest = (GeneratedHttpRequest) request; @@ -77,7 +77,7 @@ public class BindAuthToJsonPayload extends BindToJsonPayload implements MapBinde addCredentialsInArgsOrNull(gRequest, builder); // TODO: is tenantName permanent? or should we switch to tenantId at some point. seems most tools // still use tenantName - if (Strings.emptyToNull(postParams.get("tenantName")) != null) + if (!Strings.isNullOrEmpty((String) postParams.get("tenantName"))) builder.put("tenantName", postParams.get("tenantName")); return super.bindToRequest(request, ImmutableMap.of("auth", builder.build())); } diff --git a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindAddInternetServiceToXmlPayload.java b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindAddInternetServiceToXmlPayload.java index cb5c931cf6..9440663417 100644 --- a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindAddInternetServiceToXmlPayload.java +++ b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindAddInternetServiceToXmlPayload.java @@ -54,13 +54,13 @@ public class BindAddInternetServiceToXmlPayload implements MapBinder { private String ns; @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { - String name = checkNotNull(postParams.get("name"), "name parameter not present"); - String protocol = checkNotNull(postParams.get("protocol"), "protocol parameter not present"); - String port = checkNotNull(postParams.get("port"), "port parameter not present"); - String enabled = checkNotNull(postParams.get("enabled"), "enabled parameter not present"); - String description = postParams.get("description"); + String name = checkNotNull(postParams.get("name"), "name parameter not present").toString(); + String protocol = checkNotNull(postParams.get("protocol"), "protocol parameter not present").toString(); + String port = checkNotNull(postParams.get("port"), "port parameter not present").toString(); + String enabled = checkNotNull(postParams.get("enabled"), "enabled parameter not present").toString(); + String description = (String) postParams.get("description"); String payload = Strings2.replaceTokens(xmlTemplate, ImmutableMap.of("name", name, "protocol", protocol, "port", port, "enabled", enabled, "ns", ns)); try { @@ -73,10 +73,10 @@ public class BindAddInternetServiceToXmlPayload implements MapBinder { return stringBinder.bindToRequest(request, payload); } - private String getMonitorString(Map postParams) + private String getMonitorString(Map postParams) { // Sending no element to Terremark will result in default behavior, which is to create a monitor. - String monitor = postParams.get("monitor"); + String monitor = (String) postParams.get("monitor"); if (monitor == null || "true".equalsIgnoreCase(monitor)) { return ""; } diff --git a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindAddNodeServiceToXmlPayload.java b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindAddNodeServiceToXmlPayload.java index eeef9b59e4..872407d67a 100644 --- a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindAddNodeServiceToXmlPayload.java +++ b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindAddNodeServiceToXmlPayload.java @@ -55,12 +55,12 @@ public class BindAddNodeServiceToXmlPayload implements MapBinder { private String ns; @Override - public R bindToRequest(R request, Map postParams) { - String ipAddress = checkNotNull(postParams.get("ipAddress"), "ipAddress parameter not present"); - String name = checkNotNull(postParams.get("name"), "name parameter not present"); - String port = checkNotNull(postParams.get("port"), "port parameter not present"); - String enabled = checkNotNull(postParams.get("enabled"), "enabled parameter not present"); - String description = postParams.get("description"); + public R bindToRequest(R request, Map postParams) { + String ipAddress = checkNotNull(postParams.get("ipAddress"), "ipAddress parameter not present").toString(); + String name = checkNotNull(postParams.get("name"), "name parameter not present").toString(); + String port = checkNotNull(postParams.get("port"), "port parameter not present").toString(); + String enabled = checkNotNull(postParams.get("enabled"), "enabled parameter not present").toString(); + String description = (String) postParams.get("description"); String payload = Strings2.replaceTokens(xmlTemplate, ImmutableMap.of("name", name, "ipAddress", ipAddress, "port", port, "enabled", enabled, "ns", ns)); diff --git a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindCloneVAppParamsToXmlPayload.java b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindCloneVAppParamsToXmlPayload.java index 2a31bb1d6f..7a194ee0b5 100644 --- a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindCloneVAppParamsToXmlPayload.java +++ b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindCloneVAppParamsToXmlPayload.java @@ -64,13 +64,13 @@ public class BindCloneVAppParamsToXmlPayload implements MapBinder { } @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, "this binder is only valid for GeneratedHttpRequests!"); GeneratedHttpRequest gRequest = (GeneratedHttpRequest) request; checkState(gRequest.getArgs() != null, "args should be initialized at this point"); - String newName = checkNotNull(postParams.remove("newName"), "newName"); - String vApp = checkNotNull(postParams.remove("vApp"), "vApp"); + String newName = checkNotNull(postParams.remove("newName"), "newName").toString(); + String vApp = checkNotNull(postParams.remove("vApp"), "vApp").toString(); CloneVAppOptions options = findOptionsInArgsOrNull(gRequest); if (options == null) { diff --git a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindCreateKeyToXmlPayload.java b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindCreateKeyToXmlPayload.java index fa458265e9..0dfd7be2ab 100644 --- a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindCreateKeyToXmlPayload.java +++ b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindCreateKeyToXmlPayload.java @@ -55,9 +55,9 @@ public class BindCreateKeyToXmlPayload implements MapBinder { } @Override - public R bindToRequest(R request, Map postParams) { - String name = checkNotNull(postParams.get("name"), "name parameter not present"); - String isDefault = checkNotNull(postParams.get("isDefault"), "isDefault parameter not present"); + public R bindToRequest(R request, Map postParams) { + String name = checkNotNull(postParams.get("name"), "name parameter not present").toString(); + String isDefault = checkNotNull(postParams.get("isDefault"), "isDefault parameter not present").toString(); String payload = Strings2.replaceTokens(xmlTemplate, ImmutableMap.of("name", name, "isDefault", isDefault, "ns", ns)); diff --git a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindInstantiateVAppTemplateParamsToXmlPayload.java b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindInstantiateVAppTemplateParamsToXmlPayload.java index 86e7f11736..51814d274a 100644 --- a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindInstantiateVAppTemplateParamsToXmlPayload.java +++ b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindInstantiateVAppTemplateParamsToXmlPayload.java @@ -83,13 +83,13 @@ public class BindInstantiateVAppTemplateParamsToXmlPayload implements MapBinder } @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, "this binder is only valid for GeneratedHttpRequests!"); GeneratedHttpRequest gRequest = (GeneratedHttpRequest) request; checkState(gRequest.getArgs() != null, "args should be initialized at this point"); - String name = checkNotNull(postParams.remove("name"), "name"); - String template = checkNotNull(postParams.remove("template"), "template"); + String name = checkNotNull(postParams.remove("name"), "name").toString(); + String template = checkNotNull(postParams.remove("template"), "template").toString(); SortedMap virtualHardwareQuantity = Maps.newTreeMap(); diff --git a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindNodeConfigurationToXmlPayload.java b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindNodeConfigurationToXmlPayload.java index 3d79795526..4a4272e8fe 100644 --- a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindNodeConfigurationToXmlPayload.java +++ b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindNodeConfigurationToXmlPayload.java @@ -55,21 +55,21 @@ public class BindNodeConfigurationToXmlPayload implements MapBinder { this.stringBinder = stringBinder; } - protected String generateXml(Map postParams) throws ParserConfigurationException, + protected String generateXml(Map postParams) throws ParserConfigurationException, FactoryConfigurationError, TransformerException { XMLBuilder rootBuilder = XMLBuilder.create("NodeService").a("xmlns", ns).a("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance").a("xmlns:xsd", "http://www.w3.org/2001/XMLSchema"); - rootBuilder.e("Name").t(checkNotNull(postParams.get("name"), "name")); - rootBuilder.e("Enabled").t(checkNotNull(postParams.get("enabled"), "enabled")); + rootBuilder.e("Name").t(checkNotNull(postParams.get("name"), "name").toString()); + rootBuilder.e("Enabled").t(checkNotNull(postParams.get("enabled"), "enabled").toString()); if (postParams.containsKey("description") && postParams.get("description") != null) - rootBuilder.e("Description").t(postParams.get("description")); + rootBuilder.e("Description").t((String) postParams.get("description")); Properties outputProperties = new Properties(); outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); return rootBuilder.asString(outputProperties); } @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { try { return stringBinder.bindToRequest(request, generateXml(postParams)); } catch (Exception e) { diff --git a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindVAppConfigurationToXmlPayload.java b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindVAppConfigurationToXmlPayload.java index 5d7de94ad2..023918c516 100644 --- a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindVAppConfigurationToXmlPayload.java +++ b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindVAppConfigurationToXmlPayload.java @@ -78,7 +78,7 @@ public class BindVAppConfigurationToXmlPayload implements MapBinder, Function R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, "this binder is only valid for GeneratedHttpRequests!"); GeneratedHttpRequest gRequest = (GeneratedHttpRequest) request; diff --git a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/options/AddInternetServiceOptions.java b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/options/AddInternetServiceOptions.java index 232582615f..32af8b3379 100644 --- a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/options/AddInternetServiceOptions.java +++ b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/options/AddInternetServiceOptions.java @@ -41,8 +41,8 @@ public class AddInternetServiceOptions extends BindAddInternetServiceToXmlPayloa Boolean monitorEnabled = null; @Override - public R bindToRequest(R request, Map postParams) { - ImmutableMap.Builder copy = ImmutableMap.builder(); + public R bindToRequest(R request, Map postParams) { + ImmutableMap.Builder copy = ImmutableMap.builder(); copy.putAll(postParams); if (description != null) copy.put("description", description); diff --git a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/options/AddNodeOptions.java b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/options/AddNodeOptions.java index dfacbbeb20..c1f8dc41b0 100644 --- a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/options/AddNodeOptions.java +++ b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/options/AddNodeOptions.java @@ -38,8 +38,8 @@ public class AddNodeOptions extends BindAddNodeServiceToXmlPayload { @VisibleForTesting String enabled = "true"; @Override - public R bindToRequest(R request, Map postParams) { - Map copy = Maps.newHashMap(); + public R bindToRequest(R request, Map postParams) { + Map copy = Maps.newHashMap(); copy.putAll(postParams); copy.put("description", description); copy.put("enabled", enabled); diff --git a/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindAddInternetServiceToXmlPayloadTest.java b/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindAddInternetServiceToXmlPayloadTest.java index cb82639d70..fd0d5c3cc9 100644 --- a/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindAddInternetServiceToXmlPayloadTest.java +++ b/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindAddInternetServiceToXmlPayloadTest.java @@ -73,7 +73,7 @@ public class BindAddInternetServiceToXmlPayloadTest { BindAddInternetServiceToXmlPayload binder = injector .getInstance(BindAddInternetServiceToXmlPayload.class); - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); map.put("name", "name"); map.put("protocol", "TCP"); map.put("port", "22"); @@ -88,7 +88,7 @@ public class BindAddInternetServiceToXmlPayloadTest { BindAddInternetServiceToXmlPayload binder = injector .getInstance(BindAddInternetServiceToXmlPayload.class); - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); map.put("name", "name"); map.put("protocol", "TCP"); map.put("port", "22"); @@ -106,7 +106,7 @@ public class BindAddInternetServiceToXmlPayloadTest { BindAddInternetServiceToXmlPayload binder = injector .getInstance(BindAddInternetServiceToXmlPayload.class); - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); map.put("name", "name"); map.put("protocol", "TCP"); map.put("port", "22"); diff --git a/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindAddNodeServiceToXmlPayloadTest.java b/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindAddNodeServiceToXmlPayloadTest.java index 395098d27c..15fea854d5 100644 --- a/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindAddNodeServiceToXmlPayloadTest.java +++ b/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindAddNodeServiceToXmlPayloadTest.java @@ -72,7 +72,7 @@ public class BindAddNodeServiceToXmlPayloadTest { BindAddNodeServiceToXmlPayload binder = injector .getInstance(BindAddNodeServiceToXmlPayload.class); - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); map.put("name", "Node for Jim"); map.put("ipAddress", "172.16.20.3"); map.put("port", "80"); diff --git a/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindCloneVAppParamsToXmlPayloadTest.java b/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindCloneVAppParamsToXmlPayloadTest.java index 60e22fafa6..bdae79b028 100644 --- a/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindCloneVAppParamsToXmlPayloadTest.java +++ b/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindCloneVAppParamsToXmlPayloadTest.java @@ -72,7 +72,7 @@ public class BindCloneVAppParamsToXmlPayloadTest { BindCloneVAppParamsToXmlPayload binder = injector.getInstance(BindCloneVAppParamsToXmlPayload.class); - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); map.put("newName", "new-linux-server"); map.put("vApp", "https://vcloud.safesecureweb.com/api/v0.8/vapp/201"); binder.bindToRequest(request, map); @@ -90,7 +90,7 @@ public class BindCloneVAppParamsToXmlPayloadTest { BindCloneVAppParamsToXmlPayload binder = injector.getInstance(BindCloneVAppParamsToXmlPayload.class); - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); map.put("newName", "my-vapp"); map.put("vApp", "https://vcloud.safesecureweb.com/api/v0.8/vapp/4181"); binder.bindToRequest(request, map); diff --git a/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindInstantiateVAppTemplateParamsToXmlPayloadTest.java b/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindInstantiateVAppTemplateParamsToXmlPayloadTest.java index 3f50afd3f0..aa627f25c9 100644 --- a/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindInstantiateVAppTemplateParamsToXmlPayloadTest.java +++ b/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindInstantiateVAppTemplateParamsToXmlPayloadTest.java @@ -94,7 +94,7 @@ public class BindInstantiateVAppTemplateParamsToXmlPayloadTest { BindInstantiateVAppTemplateParamsToXmlPayload binder = injector .getInstance(BindInstantiateVAppTemplateParamsToXmlPayload.class); - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); map.put("name", "name"); map.put("template", "https://vcloud/vAppTemplate/3"); binder.bindToRequest(request, map); diff --git a/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindNodeConfigurationToXmlPayloadTest.java b/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindNodeConfigurationToXmlPayloadTest.java index 1c884502da..c6c8a3571a 100644 --- a/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindNodeConfigurationToXmlPayloadTest.java +++ b/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindNodeConfigurationToXmlPayloadTest.java @@ -64,27 +64,27 @@ public class BindNodeConfigurationToXmlPayloadTest { public void testChangeDescription() throws IOException { String expectedPayload = "willietruedescription"; - assertConfigMakesPayload(ImmutableMap. of("name", "willie", "enabled", "true", "description", + assertConfigMakesPayload(ImmutableMap. of("name", "willie", "enabled", "true", "description", "description"), expectedPayload); } public void testDisableTraffic() throws IOException { String expectedPayload = "williefalse"; - assertConfigMakesPayload(ImmutableMap. of("name", "willie", "enabled", "false"), expectedPayload); + assertConfigMakesPayload(ImmutableMap. of("name", "willie", "enabled", "false"), expectedPayload); } public void testTwoOptions() throws IOException { String expectedPayload = "willietrue"; - assertConfigMakesPayload(ImmutableMap. of("name", "willie", "enabled", "true"), expectedPayload); + assertConfigMakesPayload(ImmutableMap. of("name", "willie", "enabled", "true"), expectedPayload); } @Test(expectedExceptions = NullPointerException.class) public void testNoOptions() throws IOException { String expectedPayload = "williefalse"; - assertConfigMakesPayload(ImmutableMap. of(), expectedPayload); + assertConfigMakesPayload(ImmutableMap. of(), expectedPayload); } - private void assertConfigMakesPayload(Map config, String expectedPayload) { + private void assertConfigMakesPayload(Map config, String expectedPayload) { BindNodeConfigurationToXmlPayload binder = injector.getInstance(BindNodeConfigurationToXmlPayload.class); HttpRequest request = createMock(HttpRequest.class); expect(request.getEndpoint()).andReturn(URI.create("http://localhost/key")).anyTimes(); diff --git a/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindVAppConfigurationToXmlPayloadTest.java b/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindVAppConfigurationToXmlPayloadTest.java index 9b6f2b7386..bc7fac7c28 100644 --- a/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindVAppConfigurationToXmlPayloadTest.java +++ b/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/binders/BindVAppConfigurationToXmlPayloadTest.java @@ -88,7 +88,7 @@ public class BindVAppConfigurationToXmlPayloadTest { BindVAppConfigurationToXmlPayload binder = injector.getInstance(BindVAppConfigurationToXmlPayload.class); - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); binder.bindToRequest(request, map); verify(request); } @@ -124,7 +124,7 @@ public class BindVAppConfigurationToXmlPayloadTest { BindVAppConfigurationToXmlPayload binder = injector.getInstance(BindVAppConfigurationToXmlPayload.class); - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); binder.bindToRequest(request, map); verify(request); } @@ -153,7 +153,7 @@ public class BindVAppConfigurationToXmlPayloadTest { BindVAppConfigurationToXmlPayload binder = injector.getInstance(BindVAppConfigurationToXmlPayload.class); - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); binder.bindToRequest(request, map); verify(request); } @@ -180,7 +180,7 @@ public class BindVAppConfigurationToXmlPayloadTest { BindVAppConfigurationToXmlPayload binder = injector.getInstance(BindVAppConfigurationToXmlPayload.class); - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); binder.bindToRequest(request, map); verify(request); } @@ -209,7 +209,7 @@ public class BindVAppConfigurationToXmlPayloadTest { BindVAppConfigurationToXmlPayload binder = injector.getInstance(BindVAppConfigurationToXmlPayload.class); - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); binder.bindToRequest(request, map); verify(request); } diff --git a/core/src/main/java/org/jclouds/json/internal/EnumTypeAdapterThatReturnsFromValue.java b/core/src/main/java/org/jclouds/json/internal/EnumTypeAdapterThatReturnsFromValue.java index 9264d4d362..bef3694449 100644 --- a/core/src/main/java/org/jclouds/json/internal/EnumTypeAdapterThatReturnsFromValue.java +++ b/core/src/main/java/org/jclouds/json/internal/EnumTypeAdapterThatReturnsFromValue.java @@ -39,7 +39,7 @@ import com.google.gson.JsonSerializer; @SuppressWarnings("unchecked") public class EnumTypeAdapterThatReturnsFromValue> implements JsonSerializer, JsonDeserializer { public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context) { - return new JsonPrimitive(src.name()); + return new JsonPrimitive(src.toString()); } @SuppressWarnings("cast") diff --git a/core/src/main/java/org/jclouds/rest/MapBinder.java b/core/src/main/java/org/jclouds/rest/MapBinder.java index afd1b2c76a..e49b1de0a0 100644 --- a/core/src/main/java/org/jclouds/rest/MapBinder.java +++ b/core/src/main/java/org/jclouds/rest/MapBinder.java @@ -35,5 +35,5 @@ public interface MapBinder extends Binder { * * @see org.jclouds.rest.annotations.PayloadParam */ - R bindToRequest(R request, Map postParams); + R bindToRequest(R request, Map postParams); } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/rest/binders/BindMapToStringPayload.java b/core/src/main/java/org/jclouds/rest/binders/BindMapToStringPayload.java index 4ce7bb6c65..138f7aac95 100644 --- a/core/src/main/java/org/jclouds/rest/binders/BindMapToStringPayload.java +++ b/core/src/main/java/org/jclouds/rest/binders/BindMapToStringPayload.java @@ -50,7 +50,7 @@ public class BindMapToStringPayload implements MapBinder { @SuppressWarnings("unchecked") @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { checkNotNull(postParams, "postParams"); GeneratedHttpRequest r = GeneratedHttpRequest.class.cast(checkNotNull(request, "request")); checkArgument(r.getJavaMethod().isAnnotationPresent(Payload.class), diff --git a/core/src/main/java/org/jclouds/rest/binders/BindToJsonPayload.java b/core/src/main/java/org/jclouds/rest/binders/BindToJsonPayload.java index 37bbb430df..4b53743ba6 100644 --- a/core/src/main/java/org/jclouds/rest/binders/BindToJsonPayload.java +++ b/core/src/main/java/org/jclouds/rest/binders/BindToJsonPayload.java @@ -44,7 +44,7 @@ public class BindToJsonPayload implements MapBinder { } @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { return bindToRequest(request, (Object) postParams); } diff --git a/core/src/main/java/org/jclouds/rest/binders/BindToJsonPayloadWrappedWith.java b/core/src/main/java/org/jclouds/rest/binders/BindToJsonPayloadWrappedWith.java index 3909d07961..7223febddd 100644 --- a/core/src/main/java/org/jclouds/rest/binders/BindToJsonPayloadWrappedWith.java +++ b/core/src/main/java/org/jclouds/rest/binders/BindToJsonPayloadWrappedWith.java @@ -25,10 +25,8 @@ import java.util.Map; import javax.inject.Inject; import org.jclouds.http.HttpRequest; -import org.jclouds.rest.Binder; import org.jclouds.rest.MapBinder; -import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import com.google.inject.assistedinject.Assisted; @@ -58,7 +56,7 @@ public class BindToJsonPayloadWrappedWith implements MapBinder { } @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { return this.bindToRequest(request, (Object) postParams); } } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java b/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java index aa439d4180..0d4a878f78 100644 --- a/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java +++ b/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java @@ -535,7 +535,7 @@ public class RestAnnotationProcessor { org.jclouds.rest.MapBinder mapBinder = getMapPayloadBinderOrNull(method, args); if (mapBinder != null) { - Map mapParams = buildPostParams(method, args); + Map mapParams = buildPostParams(method, args); if (method.isAnnotationPresent(PayloadParams.class)) { PayloadParams params = method.getAnnotation(PayloadParams.class); addMapPayload(mapParams, params, headers.entries()); @@ -681,7 +681,7 @@ public class RestAnnotationProcessor { } } - private void addMapPayload(Map postParams, PayloadParams mapDefaults, + private void addMapPayload(Map postParams, PayloadParams mapDefaults, Collection> tokenValues) { for (int i = 0; i < mapDefaults.keys().length; i++) { if (mapDefaults.values()[i].equals(PayloadParams.NULL)) { @@ -1313,22 +1313,22 @@ public class RestAnnotationProcessor { return queryParamValues; } - //TODO: change to LoadingCache and move this logic to the CacheLoader. + //TODO: change to LoadingCache and move this logic to the CacheLoader. //take care to manage size of this cache - private Map buildPostParams(Method method, Object... args) throws ExecutionException { - Map postParams = newHashMap(); + private Map buildPostParams(Method method, Object... args) throws ExecutionException { + Map postParams = newHashMap(); LoadingCache> indexToPathParam = methodToIndexOfParamToPostParamAnnotations.get(method); LoadingCache> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method); for (Entry> entry : indexToPathParam.asMap().entrySet()) { for (Annotation key : entry.getValue()) { Set extractors = indexToParamExtractor.get(entry.getKey()); String paramKey = ((PayloadParam) key).value(); - String paramValue; + Object paramValue; if (extractors != null && extractors.size() > 0) { ParamParser extractor = (ParamParser) extractors.iterator().next(); paramValue = injector.getInstance(extractor.value()).apply(args[entry.getKey()]); } else { - paramValue = args[entry.getKey()] != null ? args[entry.getKey()].toString() : null; + paramValue = args[entry.getKey()] != null ? args[entry.getKey()] : null; } postParams.put(paramKey, paramValue); diff --git a/core/src/test/java/org/jclouds/rest/binders/BindMapToStringPayloadTest.java b/core/src/test/java/org/jclouds/rest/binders/BindMapToStringPayloadTest.java index c38fecfb71..8490a56667 100644 --- a/core/src/test/java/org/jclouds/rest/binders/BindMapToStringPayloadTest.java +++ b/core/src/test/java/org/jclouds/rest/binders/BindMapToStringPayloadTest.java @@ -59,7 +59,7 @@ public class BindMapToStringPayloadTest { .method(HttpMethod.POST).endpoint(URI.create("http://localhost")).build(); GeneratedHttpRequest newRequest = binder() - .bindToRequest(request, ImmutableMap.of("fooble", "robot")); + .bindToRequest(request, ImmutableMap.of("fooble", "robot")); assertEquals(newRequest.getRequestLine(), request.getRequestLine()); assertEquals(newRequest.getPayload().getRawContent(), "name robot"); @@ -71,7 +71,7 @@ public class BindMapToStringPayloadTest { GeneratedHttpRequest request = GeneratedHttpRequest.requestBuilder() .declaring(TestPayload.class).javaMethod(noPayload).args(ImmutableList. of("robot")) .method(HttpMethod.POST).endpoint(URI.create("http://localhost")).build(); - binder().bindToRequest(request, ImmutableMap.of("fooble", "robot")); + binder().bindToRequest(request, ImmutableMap.of("fooble", "robot")); } @Test(expectedExceptions = IllegalArgumentException.class) diff --git a/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java b/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java index 4d182e223b..9b18a027f3 100644 --- a/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java +++ b/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java @@ -741,8 +741,8 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { Method method = TestPost.class.getMethod("postWithPath", String.class, MapBinder.class); HttpRequest request = factory(TestPost.class).createRequest(method, "data", new org.jclouds.rest.MapBinder() { @Override - public R bindToRequest(R request, Map postParams) { - request.setPayload(postParams.get("fooble")); + public R bindToRequest(R request, Map postParams) { + request.setPayload((String) postParams.get("fooble")); return request; } diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/options/CreateServerOptions.java b/labs/glesys/src/main/java/org/jclouds/glesys/options/CreateServerOptions.java index 8b69892918..75c61d588a 100644 --- a/labs/glesys/src/main/java/org/jclouds/glesys/options/CreateServerOptions.java +++ b/labs/glesys/src/main/java/org/jclouds/glesys/options/CreateServerOptions.java @@ -47,14 +47,14 @@ public class CreateServerOptions implements MapBinder { private String description; @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, "this binder is only valid for GeneratedHttpRequests!"); GeneratedHttpRequest gRequest = (GeneratedHttpRequest) request; checkState(gRequest.getArgs() != null, "args should be initialized at this point"); ImmutableMultimap.Builder formParams = ImmutableMultimap.builder(); - formParams.putAll(forMap(postParams)); + for(String key : postParams.keySet()) formParams.put(key, (String) postParams.get(key)); ServerSpec serverSpec = ServerSpec.class.cast(find(gRequest.getArgs(), instanceOf(ServerSpec.class))); formParams.put("datacenter", serverSpec.getDatacenter()); formParams.put("platform", serverSpec.getPlatform()); diff --git a/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BaseBindVMSpecToXmlPayload.java b/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BaseBindVMSpecToXmlPayload.java index 5aa9d9d60e..a583121e7f 100644 --- a/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BaseBindVMSpecToXmlPayload.java +++ b/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BaseBindVMSpecToXmlPayload.java @@ -55,7 +55,7 @@ public abstract class BaseBindVMSpecToXmlPayload extends BindToStringPayload protected abstract T findSpecInArgsOrNull(GeneratedHttpRequest gRequest); @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, "this binder is only valid for GeneratedHttpRequests!"); GeneratedHttpRequest gRequest = (GeneratedHttpRequest) request; diff --git a/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindCaptureVAppTemplateToXmlPayload.java b/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindCaptureVAppTemplateToXmlPayload.java index 6387b0e4be..4a99e051be 100644 --- a/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindCaptureVAppTemplateToXmlPayload.java +++ b/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindCaptureVAppTemplateToXmlPayload.java @@ -65,7 +65,7 @@ public class BindCaptureVAppTemplateToXmlPayload extends BindToStringPayload imp } @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, "this binder is only valid for GeneratedHttpRequests!"); GeneratedHttpRequest gRequest = (GeneratedHttpRequest) request; diff --git a/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindCloneVMToXmlPayload.java b/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindCloneVMToXmlPayload.java index 69e5b5d912..4b5e87cb0e 100644 --- a/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindCloneVMToXmlPayload.java +++ b/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindCloneVMToXmlPayload.java @@ -65,14 +65,14 @@ public class BindCloneVMToXmlPayload extends BindToStringPayload implements MapB } @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, "this binder is only valid for GeneratedHttpRequests!"); GeneratedHttpRequest gRequest = (GeneratedHttpRequest) request; checkState(gRequest.getArgs() != null, "args should be initialized at this point"); request = super.bindToRequest(request, - generateXml(findVAppURIInArgsOrNull(gRequest), postParams.get("name"), postParams.get("networkTierName"))); + generateXml(findVAppURIInArgsOrNull(gRequest), (String) postParams.get("name"), (String) postParams.get("networkTierName"))); request.getPayload().getContentMetadata().setContentType(MediaType.APPLICATION_XML); return request; } diff --git a/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindFirewallRuleToXmlPayload.java b/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindFirewallRuleToXmlPayload.java index 803bcfe70b..aff0d2e077 100644 --- a/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindFirewallRuleToXmlPayload.java +++ b/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindFirewallRuleToXmlPayload.java @@ -64,7 +64,7 @@ public class BindFirewallRuleToXmlPayload extends BindToStringPayload implements } @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, "this binder is only valid for GeneratedHttpRequests!"); GeneratedHttpRequest gRequest = (GeneratedHttpRequest) request; diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/binders/BindUserOrgAndPasswordAsBasicAuthorizationHeader.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/binders/BindUserOrgAndPasswordAsBasicAuthorizationHeader.java index 95674a7222..1f0975aa0e 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/binders/BindUserOrgAndPasswordAsBasicAuthorizationHeader.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/binders/BindUserOrgAndPasswordAsBasicAuthorizationHeader.java @@ -44,7 +44,7 @@ import com.google.common.base.Throwables; public class BindUserOrgAndPasswordAsBasicAuthorizationHeader implements MapBinder { @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { try { String header = "Basic " + CryptoStreams.base64(String.format("%s@%s:%s", checkNotNull(postParams.get("user"), "user"), diff --git a/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/binder/CreateServerOptions.java b/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/binder/CreateServerOptions.java index ee21827761..ccd96ee5df 100644 --- a/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/binder/CreateServerOptions.java +++ b/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/binder/CreateServerOptions.java @@ -45,10 +45,10 @@ public class CreateServerOptions extends RimuHostingJsonBinder { private List metaData = new ArrayList(); @Override - public R bindToRequest(R request, Map postParams) { - String name = checkNotNull(postParams.get("name")); - String imageId = checkNotNull(postParams.get("imageId")); - String planId = checkNotNull(postParams.get("planId")); + public R bindToRequest(R request, Map postParams) { + String name = checkNotNull(postParams.get("name")).toString(); + String imageId = checkNotNull(postParams.get("imageId")).toString(); + String planId = checkNotNull(postParams.get("planId")).toString(); // There will be cases when the password is null. String password = this.password; NewServerData newServerData = new NewServerData(new CreateOptions(name, password, imageId), planId); diff --git a/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/binder/RimuHostingJsonBinder.java b/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/binder/RimuHostingJsonBinder.java index 65b1896d50..011861169b 100644 --- a/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/binder/RimuHostingJsonBinder.java +++ b/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/binder/RimuHostingJsonBinder.java @@ -27,6 +27,8 @@ import org.jclouds.http.HttpRequest; import org.jclouds.json.Json; import org.jclouds.rest.binders.BindToJsonPayload; +import com.google.common.collect.ImmutableMap; + /** * Generic binder for RimuHosting POSTS/PUTS. In the form of * @@ -41,14 +43,12 @@ public class RimuHostingJsonBinder extends BindToJsonPayload { } @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { return bindToRequest(request, (Object) postParams); } @Override public R bindToRequest(R request, Object toBind) { - Map test = new HashMap(); - test.put("request", toBind); - return super.bindToRequest(request, test); + return super.bindToRequest(request, (Object) ImmutableMap.of("request", toBind)); } } diff --git a/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/binder/RimuHostingRebootJsonBinder.java b/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/binder/RimuHostingRebootJsonBinder.java index b3dc79a13f..84ff474891 100644 --- a/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/binder/RimuHostingRebootJsonBinder.java +++ b/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/binder/RimuHostingRebootJsonBinder.java @@ -41,7 +41,7 @@ public class RimuHostingRebootJsonBinder extends RimuHostingJsonBinder { } @Override - public R bindToRequest(R request, Map postParams) { + public R bindToRequest(R request, Map postParams) { return super.bindToRequest(request, (Object) ImmutableMap.of("running_state", "RESTARTING")); } } diff --git a/providers/slicehost/src/main/java/org/jclouds/slicehost/binders/BindCreateBackupToXmlPayload.java b/providers/slicehost/src/main/java/org/jclouds/slicehost/binders/BindCreateBackupToXmlPayload.java index eb6e9469b8..3b3afe6d5b 100644 --- a/providers/slicehost/src/main/java/org/jclouds/slicehost/binders/BindCreateBackupToXmlPayload.java +++ b/providers/slicehost/src/main/java/org/jclouds/slicehost/binders/BindCreateBackupToXmlPayload.java @@ -45,9 +45,9 @@ public class BindCreateBackupToXmlPayload implements MapBinder { } @Override - public R bindToRequest(R request, Map postParams) { - String sliceId = checkNotNull(postParams.get("slice_id"), "slice_id"); - String name = checkNotNull(postParams.get("name"), "name"); + public R bindToRequest(R request, Map postParams) { + String sliceId = checkNotNull(postParams.get("slice_id"), "slice_id").toString(); + String name = checkNotNull(postParams.get("name"), "name").toString(); StringBuilder builder = new StringBuilder(); builder.append(""); builder.append("").append(sliceId).append(""); diff --git a/providers/slicehost/src/main/java/org/jclouds/slicehost/binders/BindCreateSliceToXmlPayload.java b/providers/slicehost/src/main/java/org/jclouds/slicehost/binders/BindCreateSliceToXmlPayload.java index 8c4b6aeed5..82f4ce9d08 100644 --- a/providers/slicehost/src/main/java/org/jclouds/slicehost/binders/BindCreateSliceToXmlPayload.java +++ b/providers/slicehost/src/main/java/org/jclouds/slicehost/binders/BindCreateSliceToXmlPayload.java @@ -44,10 +44,10 @@ public class BindCreateSliceToXmlPayload implements MapBinder { this.binder = binder; } @Override - public R bindToRequest(R request, Map postParams) { - String flavorId = checkNotNull(postParams.get("flavor_id"), "flavor_id"); - String imageId = checkNotNull(postParams.get("image_id"), "image_id"); - String name = checkNotNull(postParams.get("name"), "name"); + public R bindToRequest(R request, Map postParams) { + String flavorId = checkNotNull(postParams.get("flavor_id"), "flavor_id").toString(); + String imageId = checkNotNull(postParams.get("image_id"), "image_id").toString(); + String name = checkNotNull(postParams.get("name"), "name").toString(); StringBuilder builder = new StringBuilder(); builder.append(""); builder.append("").append(flavorId).append(""); From d45404a338e638035c636063a9144b6ad19ed09f Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Thu, 3 May 2012 15:32:33 -0700 Subject: [PATCH 091/148] Throw exceptions in static blocks This reports any errors instead of silently ignoring them. --- .../cloudsigma/binders/BindServerToPlainTextStringTest.java | 5 +++-- .../binders/BindServerToPlainTextStringTest.java | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/apis/cloudsigma/src/test/java/org/jclouds/cloudsigma/binders/BindServerToPlainTextStringTest.java b/apis/cloudsigma/src/test/java/org/jclouds/cloudsigma/binders/BindServerToPlainTextStringTest.java index 14b0a4e457..463e172ff1 100644 --- a/apis/cloudsigma/src/test/java/org/jclouds/cloudsigma/binders/BindServerToPlainTextStringTest.java +++ b/apis/cloudsigma/src/test/java/org/jclouds/cloudsigma/binders/BindServerToPlainTextStringTest.java @@ -39,6 +39,7 @@ import org.testng.annotations.Test; import com.google.common.base.Function; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.base.Throwables; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.TypeLiteral; @@ -50,13 +51,13 @@ import com.google.inject.TypeLiteral; @Test(groups = { "unit" }) public class BindServerToPlainTextStringTest { - public static String CREATED_SERVER; + public static final String CREATED_SERVER; static { try { CREATED_SERVER = Strings2.toStringAndClose(BindServerToPlainTextStringTest.class .getResourceAsStream("/create_server.txt")); } catch (IOException e) { - CREATED_SERVER = null; + throw Throwables.propagate(e); } } public static final Server SERVER = new Server.Builder() diff --git a/apis/elasticstack/src/test/java/org/jclouds/elasticstack/binders/BindServerToPlainTextStringTest.java b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/binders/BindServerToPlainTextStringTest.java index d4972ecf00..818eefd2e2 100644 --- a/apis/elasticstack/src/test/java/org/jclouds/elasticstack/binders/BindServerToPlainTextStringTest.java +++ b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/binders/BindServerToPlainTextStringTest.java @@ -40,6 +40,7 @@ import org.testng.annotations.Test; import com.google.common.base.Function; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.base.Throwables; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; @@ -52,13 +53,13 @@ import com.google.inject.TypeLiteral; @Test(groups = { "unit" }) public class BindServerToPlainTextStringTest { - public static String CREATED_SERVER; + public static final String CREATED_SERVER; static { try { CREATED_SERVER = Strings2.toStringAndClose(BindServerToPlainTextStringTest.class .getResourceAsStream("/create_server.txt")); } catch (IOException e) { - CREATED_SERVER = null; + throw Throwables.propagate(e); } } public static final Server SERVER = new Server.Builder() From a30aad05edc9885ff9c20c495a597cf7482b9936 Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Mon, 14 May 2012 10:13:15 -0700 Subject: [PATCH 092/148] Harmonize filesystem and transient getBlob This commit also simplifies HTTP range logic calculations, sets the correct Content-Length, and avoids an unneeded copy. --- .../filesystem/FilesystemAsyncBlobStore.java | 31 ++++++++++++------- .../blobstore/TransientAsyncBlobStore.java | 27 +++++++++------- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java index ec2d91f3f7..66b8dde35a 100644 --- a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java +++ b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java @@ -91,7 +91,9 @@ import org.jclouds.http.HttpCommand; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponseException; +import org.jclouds.http.HttpUtils; import org.jclouds.http.options.HttpRequestOptions; +import org.jclouds.io.ContentMetadata; import org.jclouds.io.Payloads; import org.jclouds.io.payloads.BaseMutableContentMetadata; import org.jclouds.javax.annotation.Nullable; @@ -557,25 +559,32 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { } ByteArrayOutputStream out = new ByteArrayOutputStream(); for (String s : options.getRanges()) { + // HTTP uses a closed interval while Java array indexing uses a + // half-open interval. + int offset = 0; + int last = data.length - 1; if (s.startsWith("-")) { - int length = Integer.parseInt(s.substring(1)); - out.write(data, data.length - length, length); + offset = last - Integer.parseInt(s.substring(1)) + 1; } else if (s.endsWith("-")) { - int offset = Integer.parseInt(s.substring(0, s.length() - 1)); - out.write(data, offset, data.length - offset); + offset = Integer.parseInt(s.substring(0, s.length() - 1)); } else if (s.contains("-")) { String[] firstLast = s.split("\\-"); - int offset = Integer.parseInt(firstLast[0]); - int last = Integer.parseInt(firstLast[1]); - int length = last - offset + 1; // the range end is included - out.write(data, offset, length); + offset = Integer.parseInt(firstLast[0]); + last = Integer.parseInt(firstLast[1]); } else { - return immediateFailedFuture(new IllegalArgumentException("first and last were null!")); + return immediateFailedFuture(new IllegalArgumentException("illegal range: " + s)); } + if (offset > last || last + 1 > data.length) { + return immediateFailedFuture(new IllegalArgumentException("illegal range: " + s)); + } + out.write(data, offset, last - offset + 1); } - blob.setPayload(out.toByteArray()); - blob.getMetadata().getContentMetadata().setContentLength(new Long(data.length)); + ContentMetadata cmd = blob.getPayload().getContentMetadata(); + byte[] byteArray = out.toByteArray(); + blob.setPayload(byteArray); + HttpUtils.copy(cmd, blob.getPayload().getContentMetadata()); + blob.getPayload().getContentMetadata().setContentLength(new Long(byteArray.length)); } } checkNotNull(blob.getPayload(), "payload " + blob); diff --git a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java index ca882c2661..182d015093 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java @@ -590,27 +590,32 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { } ByteArrayOutputStream out = new ByteArrayOutputStream(); for (String s : options.getRanges()) { + // HTTP uses a closed interval while Java array indexing uses a + // half-open interval. + int offset = 0; + int last = data.length - 1; if (s.startsWith("-")) { - int length = Integer.parseInt(s.substring(1)); - out.write(data, data.length - length, length); + offset = last - Integer.parseInt(s.substring(1)) + 1; } else if (s.endsWith("-")) { - int offset = Integer.parseInt(s.substring(0, s.length() - 1)); - out.write(data, offset, data.length - offset); + offset = Integer.parseInt(s.substring(0, s.length() - 1)); } else if (s.contains("-")) { String[] firstLast = s.split("\\-"); - int offset = Integer.parseInt(firstLast[0]); - int last = Integer.parseInt(firstLast[1]); - int length = (last < data.length) ? last + 1 : data.length - offset; - out.write(data, offset, length); + offset = Integer.parseInt(firstLast[0]); + last = Integer.parseInt(firstLast[1]); } else { - return immediateFailedFuture(new IllegalArgumentException("first and last were null!")); + return immediateFailedFuture(new IllegalArgumentException("illegal range: " + s)); } + if (offset > last || last + 1 > data.length) { + return immediateFailedFuture(new IllegalArgumentException("illegal range: " + s)); + } + out.write(data, offset, last - offset + 1); } ContentMetadata cmd = blob.getPayload().getContentMetadata(); - blob.setPayload(out.toByteArray()); + byte[] byteArray = out.toByteArray(); + blob.setPayload(byteArray); HttpUtils.copy(cmd, blob.getPayload().getContentMetadata()); - blob.getPayload().getContentMetadata().setContentLength(new Long(out.toByteArray().length)); + blob.getPayload().getContentMetadata().setContentLength(new Long(byteArray.length)); } } checkNotNull(blob.getPayload(), "payload " + blob); From 1199c54f670391639d1327c071097fbde63a4524 Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Mon, 14 May 2012 12:48:20 -0700 Subject: [PATCH 093/148] Harmonize filesystem and transient loadBlob Move getBlob into FilesystemStorageStrategyImpl, similar to TransientStorageStrategy. --- .../filesystem/FilesystemAsyncBlobStore.java | 26 +------------------ .../strategy/FilesystemStorageStrategy.java | 14 ++++++++++ .../FilesystemStorageStrategyImpl.java | 18 +++++++++++++ 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java index 66b8dde35a..d186ac196c 100644 --- a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java +++ b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java @@ -343,33 +343,9 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { return immediateFuture(result); } - /** - * Load the blob with the given key belonging to the container with the given - * name. There must exist a resource on the file system whose complete name - * is given concatenating the container name and the key - * - * @param container - * it's the name of the container the blob belongs to - * @param key - * it's the key of the blob - * - * @return the blob belonging to the given container with the given key - */ private Blob loadBlob(final String container, final String key) { logger.debug("Opening blob in container: %s - %s", container, key); - BlobBuilder builder = blobUtils.blobBuilder(); - builder.name(key); - File file = storageStrategy.getFileForBlobKey(container, key); - try { - builder.payload(file).calculateMD5(); - } catch (IOException e) { - logger.error("An error occurred calculating MD5 for blob %s from container ", key, container); - Throwables.propagateIfPossible(e); - } - Blob blob = builder.build(); - if (blob.getPayload().getContentMetadata().getContentMD5() != null) - blob.getMetadata().setETag(CryptoStreams.hex(blob.getPayload().getContentMetadata().getContentMD5())); - return blob; + return storageStrategy.getBlob(container, key); } protected static class DelimiterFilter implements Predicate { diff --git a/apis/filesystem/src/main/java/org/jclouds/filesystem/strategy/FilesystemStorageStrategy.java b/apis/filesystem/src/main/java/org/jclouds/filesystem/strategy/FilesystemStorageStrategy.java index 2e341c49d9..a0c8662b63 100644 --- a/apis/filesystem/src/main/java/org/jclouds/filesystem/strategy/FilesystemStorageStrategy.java +++ b/apis/filesystem/src/main/java/org/jclouds/filesystem/strategy/FilesystemStorageStrategy.java @@ -123,6 +123,20 @@ public interface FilesystemStorageStrategy { */ boolean blobExists(String container, String key); + /** + * Load the blob with the given key belonging to the container with the given + * name. There must exist a resource on the file system whose complete name + * is given concatenating the container name and the key + * + * @param container + * it's the name of the container the blob belongs to + * @param key + * it's the key of the blob + * + * @return the blob belonging to the given container with the given key + */ + Blob getBlob(final String containerName, final String blobName); + /** * Returns all the blobs key inside a container * @param container diff --git a/apis/filesystem/src/main/java/org/jclouds/filesystem/strategy/internal/FilesystemStorageStrategyImpl.java b/apis/filesystem/src/main/java/org/jclouds/filesystem/strategy/internal/FilesystemStorageStrategyImpl.java index 2818cdfa97..b3ea8bf275 100644 --- a/apis/filesystem/src/main/java/org/jclouds/filesystem/strategy/internal/FilesystemStorageStrategyImpl.java +++ b/apis/filesystem/src/main/java/org/jclouds/filesystem/strategy/internal/FilesystemStorageStrategyImpl.java @@ -38,6 +38,7 @@ import org.apache.commons.io.filefilter.DirectoryFileFilter; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobBuilder; import org.jclouds.blobstore.options.ListContainerOptions; +import org.jclouds.crypto.CryptoStreams; import org.jclouds.filesystem.predicates.validators.FilesystemBlobKeyValidator; import org.jclouds.filesystem.predicates.validators.FilesystemContainerNameValidator; import org.jclouds.filesystem.reference.FilesystemConstants; @@ -91,6 +92,23 @@ public class FilesystemStorageStrategyImpl implements FilesystemStorageStrategy return buildPathAndChecksIfFileExists(container, key); } + @Override + public Blob getBlob(final String container, final String key) { + BlobBuilder builder = blobBuilders.get(); + builder.name(key); + File file = getFileForBlobKey(container, key); + try { + builder.payload(file).calculateMD5(); + } catch (IOException e) { + logger.error("An error occurred calculating MD5 for blob %s from container ", key, container); + Throwables.propagateIfPossible(e); + } + Blob blob = builder.build(); + if (blob.getPayload().getContentMetadata().getContentMD5() != null) + blob.getMetadata().setETag(CryptoStreams.hex(blob.getPayload().getContentMetadata().getContentMD5())); + return blob; + } + @Override public boolean createContainer(String container) { logger.debug("Creating container %s", container); From 9b6cf5ea21eefa82924d089cd1b35efa236f1fa8 Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Mon, 14 May 2012 13:36:08 -0700 Subject: [PATCH 094/148] Hamonize filesystem and transient copyBlob --- .../filesystem/FilesystemAsyncBlobStore.java | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java index d186ac196c..d570e8be1d 100644 --- a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java +++ b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java @@ -63,6 +63,7 @@ import org.jclouds.blobstore.BlobStoreContext; import org.jclouds.blobstore.ContainerNotFoundException; import org.jclouds.blobstore.KeyNotFoundException; import org.jclouds.blobstore.domain.Blob; +import org.jclouds.blobstore.domain.Blob.Factory; import org.jclouds.blobstore.domain.BlobBuilder; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.MutableBlobMetadata; @@ -94,6 +95,7 @@ import org.jclouds.http.HttpResponseException; import org.jclouds.http.HttpUtils; import org.jclouds.http.options.HttpRequestOptions; import org.jclouds.io.ContentMetadata; +import org.jclouds.io.Payload; import org.jclouds.io.Payloads; import org.jclouds.io.payloads.BaseMutableContentMetadata; import org.jclouds.javax.annotation.Nullable; @@ -123,6 +125,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { protected final Crypto crypto; protected final HttpGetOptionsListToGetOptions httpGetOptionsConverter; protected final IfDirectoryReturnNameStrategy ifDirectoryReturnName; + protected final Factory blobFactory; protected final FilesystemStorageStrategy storageStrategy; @Inject @@ -134,8 +137,9 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { @Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, Supplier defaultLocation, @Memoized Supplier> locations, - FilesystemStorageStrategy storageStrategy) { + Factory blobFactory, FilesystemStorageStrategy storageStrategy) { super(context, blobUtils, service, defaultLocation, locations); + this.blobFactory = blobFactory; this.dateService = dateService; this.crypto = crypto; this.httpGetOptionsConverter = httpGetOptionsConverter; @@ -470,6 +474,10 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { return immediateFuture(eTag); } + private void copyPayloadHeadersToBlob(Payload payload, Blob blob) { + blob.getAllHeaders().putAll(HttpUtils.getContentHeadersFromMetadata(payload.getContentMetadata())); + } + /** * {@inheritDoc} */ @@ -525,6 +533,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { .getMetadata().getLastModified(), unmodifiedSince), null, response)); } } + blob = copyBlob(blob); if (options.getRanges() != null && options.getRanges().size() > 0) { byte[] data; @@ -574,7 +583,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { public ListenableFuture blobMetadata(final String container, final String key) { try { Blob blob = getBlob(container, key).get(); - return immediateFuture(blob != null ? (BlobMetadata) blob.getMetadata() : null); + return immediateFuture(blob != null ? (BlobMetadata) copy(blob.getMetadata()) : null); } catch (Exception e) { if (size(filter(getCausalChain(e), KeyNotFoundException.class)) >= 1) return immediateFuture(null); @@ -582,6 +591,13 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { } } + private Blob copyBlob(Blob blob) { + Blob returnVal = blobFactory.create(copy(blob.getMetadata())); + returnVal.setPayload(blob.getPayload()); + copyPayloadHeadersToBlob(blob.getPayload(), returnVal); + return returnVal; + } + /** * Calculates the object MD5 and returns it as eTag * From f55231d60779fb134463a736d9e5d8b8a253a4ab Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Mon, 14 May 2012 15:15:54 -0700 Subject: [PATCH 095/148] Tolerate oversized ranges in getBlob --- .../org/jclouds/filesystem/FilesystemAsyncBlobStore.java | 5 ++++- .../java/org/jclouds/blobstore/TransientAsyncBlobStore.java | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java index d570e8be1d..33bf078aad 100644 --- a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java +++ b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java @@ -560,9 +560,12 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { return immediateFailedFuture(new IllegalArgumentException("illegal range: " + s)); } - if (offset > last || last + 1 > data.length) { + if (offset > last) { return immediateFailedFuture(new IllegalArgumentException("illegal range: " + s)); } + if (last + 1 > data.length) { + last = data.length - 1; + } out.write(data, offset, last - offset + 1); } ContentMetadata cmd = blob.getPayload().getContentMetadata(); diff --git a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java index 182d015093..ed85a5ca0f 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java @@ -606,9 +606,12 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { return immediateFailedFuture(new IllegalArgumentException("illegal range: " + s)); } - if (offset > last || last + 1 > data.length) { + if (offset > last) { return immediateFailedFuture(new IllegalArgumentException("illegal range: " + s)); } + if (last + 1 > data.length) { + last = data.length - 1; + } out.write(data, offset, last - offset + 1); } ContentMetadata cmd = blob.getPayload().getContentMetadata(); From ef0bba6050bcc0a86dc3aa8717423da3c9346e26 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Mon, 14 May 2012 14:57:09 -0700 Subject: [PATCH 096/148] Issue 928:update jclouds-jsch default version to 0.1.48 --- project/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/pom.xml b/project/pom.xml index 6992103b12..26f74430eb 100644 --- a/project/pom.xml +++ b/project/pom.xml @@ -216,7 +216,7 @@ com.jcraft jsch - 0.1.46 + 0.1.48 From 2a10087df913b2f85568f28301906085fe30494e Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Mon, 14 May 2012 15:33:49 -0700 Subject: [PATCH 097/148] gogrid test glitches --- .../main/java/org/jclouds/gogrid/GoGridProviderMetadata.java | 2 +- .../jclouds/gogrid/compute/GoGridTemplateBuilderLiveTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/providers/gogrid/src/main/java/org/jclouds/gogrid/GoGridProviderMetadata.java b/providers/gogrid/src/main/java/org/jclouds/gogrid/GoGridProviderMetadata.java index 051b96eee6..25d92dee2a 100644 --- a/providers/gogrid/src/main/java/org/jclouds/gogrid/GoGridProviderMetadata.java +++ b/providers/gogrid/src/main/java/org/jclouds/gogrid/GoGridProviderMetadata.java @@ -75,7 +75,7 @@ public class GoGridProviderMetadata extends BaseProviderMetadata { .apiMetadata(new GoGridApiMetadata()) .homepage(URI.create("http://www.gogrid.com")) .console(URI.create("https://my.gogrid.com/gogrid")) - .iso3166Codes("US-CA", "US-VA", "BR-SP") + .iso3166Codes("US-CA", "US-VA", "NL-NH") .endpoint("https://api.gogrid.com/api") .defaultProperties(GoGridProviderMetadata.defaultProperties()); } diff --git a/providers/gogrid/src/test/java/org/jclouds/gogrid/compute/GoGridTemplateBuilderLiveTest.java b/providers/gogrid/src/test/java/org/jclouds/gogrid/compute/GoGridTemplateBuilderLiveTest.java index 6b3fc3f145..705abad4eb 100644 --- a/providers/gogrid/src/test/java/org/jclouds/gogrid/compute/GoGridTemplateBuilderLiveTest.java +++ b/providers/gogrid/src/test/java/org/jclouds/gogrid/compute/GoGridTemplateBuilderLiveTest.java @@ -59,7 +59,7 @@ public class GoGridTemplateBuilderLiveTest extends BaseTemplateBuilderLiveTest { case UBUNTU: return input.version.equals("") || input.version.equals("10.04"); case CENTOS: - return input.version.equals("") || input.version.matches("5.[56]") || input.version.equals("6.0"); + return input.version.equals("") || input.version.matches("5.[06]") || input.version.equals("6.0"); case WINDOWS: return input.version.equals("") || input.version.matches("200[38]"); default: From db40facb2d78d3ffbd569a7afdbc9783625f2c2e Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Mon, 14 May 2012 23:02:07 -0700 Subject: [PATCH 098/148] Issue 930:RetryingCacheLoaderDecorator --- .../jclouds/cache/ForwardingCacheLoader.java | 79 +++++++ .../cache/RetryingCacheLoaderDecorator.java | 196 ++++++++++++++++++ ...ntiallyAndRetryOnThrowableCacheLoader.java | 128 ++++++++++++ ...onentiallyAndRetryOnThrowableCallable.java | 99 +++++++++ .../cache/ForwardingCacheLoaderTest.java | 82 ++++++++ .../RetryingCacheLoaderDecoratorTest.java | 85 ++++++++ ...llyAndRetryOnThrowableCacheLoaderTest.java | 76 +++++++ ...tiallyAndRetryOnThrowableCallableTest.java | 75 +++++++ 8 files changed, 820 insertions(+) create mode 100644 core/src/main/java/org/jclouds/cache/ForwardingCacheLoader.java create mode 100644 core/src/main/java/org/jclouds/cache/RetryingCacheLoaderDecorator.java create mode 100644 core/src/main/java/org/jclouds/cache/internal/BackoffExponentiallyAndRetryOnThrowableCacheLoader.java create mode 100644 core/src/main/java/org/jclouds/cache/internal/BackoffExponentiallyAndRetryOnThrowableCallable.java create mode 100644 core/src/test/java/org/jclouds/cache/ForwardingCacheLoaderTest.java create mode 100644 core/src/test/java/org/jclouds/cache/RetryingCacheLoaderDecoratorTest.java create mode 100644 core/src/test/java/org/jclouds/cache/internal/BackoffExponentiallyAndRetryOnThrowableCacheLoaderTest.java create mode 100644 core/src/test/java/org/jclouds/cache/internal/BackoffExponentiallyAndRetryOnThrowableCallableTest.java diff --git a/core/src/main/java/org/jclouds/cache/ForwardingCacheLoader.java b/core/src/main/java/org/jclouds/cache/ForwardingCacheLoader.java new file mode 100644 index 0000000000..18c203274f --- /dev/null +++ b/core/src/main/java/org/jclouds/cache/ForwardingCacheLoader.java @@ -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.cache; + +import java.util.Map; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import com.google.common.cache.CacheLoader; +import com.google.common.util.concurrent.ListenableFuture; + +/** + * A {@link CacheLoader} which forwards all its method calls to another {@link CacheLoader}. + * Subclasses should override one or more methods to modify the behavior of the backing cache as + * desired per the decorator pattern. + * + * @author Adrian Cole + * @since 1.5 + */ +@Beta +public abstract class ForwardingCacheLoader extends CacheLoader { + + /** Constructor for use by subclasses. */ + protected ForwardingCacheLoader() { + } + + protected abstract CacheLoader delegate(); + + @Override + public V load(K key) throws Exception { + return delegate().load(key); + } + + @Override + public ListenableFuture reload(K key, V oldValue) throws Exception { + return delegate().reload(key, oldValue); + } + + @Override + public Map loadAll(Iterable keys) throws Exception { + return delegate().loadAll(keys); + } + + /** + * A simplified version of {@link ForwardingCacheLoader} where subclasses can pass in an already + * constructed {@link CacheLoader} as the delegete. + * + */ + @Beta + public static class SimpleForwardingCacheLoader extends ForwardingCacheLoader { + private final CacheLoader delegate; + + protected SimpleForwardingCacheLoader(CacheLoader delegate) { + this.delegate = Preconditions.checkNotNull(delegate); + } + + @Override + protected final CacheLoader delegate() { + return delegate; + } + + } +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/cache/RetryingCacheLoaderDecorator.java b/core/src/main/java/org/jclouds/cache/RetryingCacheLoaderDecorator.java new file mode 100644 index 0000000000..46a925c242 --- /dev/null +++ b/core/src/main/java/org/jclouds/cache/RetryingCacheLoaderDecorator.java @@ -0,0 +1,196 @@ +/** + * 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.cache; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import org.jclouds.cache.internal.BackoffExponentiallyAndRetryOnThrowableCacheLoader; + +import com.google.common.annotations.Beta; +import com.google.common.base.Objects; +import com.google.common.cache.CacheLoader; + +/** + *

+ * A decorator of {@link CacheLoader} instances having any combination of the following features: + * + *

    + *
  • exponential backoff based on a particular Throwable type + *
+ * + * These features are all optional; cache loaders can be created using all or none of them. By + * default, the input cache loader is returned unaffected. + * + *

+ * Usage example: + * + *

+ * @code
+ * 
+ *   CacheLoader loader = RetryingCacheLoaderDecorator.newDecorator()
+ *       .on(ResourceNotFoundException.class).exponentiallyBackoff()
+ *       .decorate(
+ *           new CacheLoader() {
+ *             public Graph load(Key key) throws AnyException {
+ *               return createOnFlakeyConnection(key);
+ *             }
+ *           });}
+ * 
+ * + * @param + * the base key type for all cache loaders created by this decorator + * @param + * the base value type for all cache loaders created by this decorator + * @author Adrian Cole + * @since 1.5 + */ +@Beta +public class RetryingCacheLoaderDecorator { + protected RetryingCacheLoaderDecorator() { + } + + /** + * Constructs a new {@code RetryingCacheLoaderDecorator} instance with default settings, and no + * retrying any kind. + */ + // we can resolve generic type during decorate, as opposed to here + public static RetryingCacheLoaderDecorator newDecorator() { + return new RetryingCacheLoaderDecorator(); + } + + /** + * Determines the action to carry out on a particular throwable. + * + */ + public OnThrowableBuilder on(Class retryableThrowable) { + return new OnThrowableBuilder(retryableThrowable); + } + + public static class OnThrowableBuilder { + Class retryableThrowable; + + protected OnThrowableBuilder(Class retryableThrowable) { + this.retryableThrowable = checkNotNull(retryableThrowable, "retryableThrowable"); + } + + /** + * For each attempt, exponentially backoff + */ + public BackoffExponentiallyAndRetryOnThrowableCacheLoaderDecorator exponentiallyBackoff() { + return new BackoffExponentiallyAndRetryOnThrowableCacheLoaderDecorator(retryableThrowable); + } + + } + + public static class BackoffExponentiallyAndRetryOnThrowableCacheLoaderDecorator extends + RetryingCacheLoaderDecorator { + private long periodMs = 100l; + private long maxPeriodMs = 200l; + private int maxTries = 5; + private final Class retryableThrowable; + + protected BackoffExponentiallyAndRetryOnThrowableCacheLoaderDecorator(Class retryableThrowable) { + this.retryableThrowable = checkNotNull(retryableThrowable, "retryableThrowable"); + } + + /** + * The initial period in milliseconds to delay between tries. with each try this period will + * increase exponentially. + *

+ * default: {@code 100} + * + */ + public BackoffExponentiallyAndRetryOnThrowableCacheLoaderDecorator periodMs(long periodMs) { + checkArgument(periodMs > 1, "maxTries must be positive: %d", periodMs); + this.periodMs = periodMs; + return this; + } + + /** + * The initial period in milliseconds to delay between tries. with each try this period will + * increase exponentially. + *

+ * default: {@code 200} + * + */ + public BackoffExponentiallyAndRetryOnThrowableCacheLoaderDecorator maxPeriodMs(long maxPeriodMs) { + checkArgument(maxPeriodMs > periodMs, "maxPeriodMs must be equal to or greater than periodMs: %d %d", + maxPeriodMs, periodMs); + this.maxPeriodMs = maxPeriodMs; + return this; + } + + /** + * The maximum attempts to try on the given exception type + *

+ * default: {@code 5} + * + */ + public BackoffExponentiallyAndRetryOnThrowableCacheLoaderDecorator maxTries(int maxTries) { + checkArgument(maxTries > 1, "maxTries must be more than one: %d", maxTries); + this.maxTries = maxTries; + return this; + } + + @Override + public CacheLoader decorate(CacheLoader loader) { + return new BackoffExponentiallyAndRetryOnThrowableCacheLoader(retryableThrowable, periodMs, + maxPeriodMs, maxTries, super.decorate(loader)); + } + + @Override + protected Objects.ToStringHelper string() { + return string().add("retryableThrowable", retryableThrowable).add("periodMs", periodMs).add("maxPeriodMs", + maxPeriodMs).add("maxTries", maxTries); + } + } + + /** + * Decorates a cacheloader, or returns the same value, if no retrying features were requested. + * + *

+ * This method does not alter the state of this {@code RetryingCacheLoaderDecorator} instance, so + * it can be invoked again to create multiple independent cache loaders. + * + * @param loader + * the cache loader used to obtain new values + * @return a cache loader having the requested features + */ + public CacheLoader decorate(CacheLoader loader) { + return (CacheLoader) loader; + } + + /** + * Returns a string representation for this RetryingCacheLoaderDecorator instance. The exact form + * of the returned string is not specified. + */ + @Override + public String toString() { + return string().toString(); + } + + /** + * append any state that should be considered in {@link #toString} here. + */ + protected Objects.ToStringHelper string() { + return Objects.toStringHelper(this); + } +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/cache/internal/BackoffExponentiallyAndRetryOnThrowableCacheLoader.java b/core/src/main/java/org/jclouds/cache/internal/BackoffExponentiallyAndRetryOnThrowableCacheLoader.java new file mode 100644 index 0000000000..967a046143 --- /dev/null +++ b/core/src/main/java/org/jclouds/cache/internal/BackoffExponentiallyAndRetryOnThrowableCacheLoader.java @@ -0,0 +1,128 @@ +/** + * 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.cache.internal; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Map; +import java.util.concurrent.Callable; + +import org.jclouds.cache.ForwardingCacheLoader; + +import com.google.common.annotations.Beta; +import com.google.common.cache.CacheLoader; +import com.google.common.util.concurrent.ListenableFuture; + +/** + * Exponentially backs off, if we encounter an exception of the given type during any of the + * following methods: + *

    + *
  • load + *
  • reload + *
  • loadAll + *
+ * + * @param + * the key type of the cache loader + * @param + * the value type of the cache loader + * @author Adrian Cole + * @since 1.5 + */ +@Beta +public class BackoffExponentiallyAndRetryOnThrowableCacheLoader extends ForwardingCacheLoader { + private final Class retryableThrowable; + private final long periodMs; + private final long maxPeriodMs; + private final int maxTries; + private final CacheLoader loader; + + /** + * + * @param retryableThrowable + * the exception which we can retry + * @param periodMs + * initial period, which exponentially increases with each try, specified in + * milliseconds + * @param maxPeriodMs + * maximum period duration, specified in milliseconds + * @param maxTries + * maximum amount of tries + * @param loader + * the loader we are able to retry + */ + public BackoffExponentiallyAndRetryOnThrowableCacheLoader(Class retryableThrowable, long periodMs, + long maxPeriodMs, int maxTries, CacheLoader loader) { + this.retryableThrowable = checkNotNull(retryableThrowable, "retryableThrowable"); + checkArgument(maxTries > 1, "maxTries must be more than one: %d", maxTries); + this.maxTries = maxTries; + checkArgument(periodMs > 0, "periodMs must be positive: %d", periodMs); + this.periodMs = periodMs; + checkArgument(maxPeriodMs > periodMs, "maxPeriodMs must be equal to or greater than periodMs: %d %d", + maxPeriodMs, periodMs); + this.maxPeriodMs = maxPeriodMs; + this.loader = checkNotNull(loader, "loader"); + } + + @Override + protected CacheLoader delegate() { + return loader; + } + + // TODO: refactor into a better closure in java pattern, if one exists + @Override + public V load(final K key) throws Exception { + return backoffExponentiallyAndRetryOnThrowable(new Callable() { + + @Override + public V call() throws Exception { + return BackoffExponentiallyAndRetryOnThrowableCacheLoader.super.load(key); + } + }); + } + + @Override + public ListenableFuture reload(final K key, final V oldValue) throws Exception { + return backoffExponentiallyAndRetryOnThrowable(new Callable>() { + + @Override + public ListenableFuture call() throws Exception { + return BackoffExponentiallyAndRetryOnThrowableCacheLoader.super.reload(key, oldValue); + } + }); + } + + @Override + public Map loadAll(final Iterable keys) throws Exception { + return backoffExponentiallyAndRetryOnThrowable(new Callable>() { + + @Override + public Map call() throws Exception { + return BackoffExponentiallyAndRetryOnThrowableCacheLoader.super.loadAll(keys); + } + }); + } + + private T backoffExponentiallyAndRetryOnThrowable(Callable callable) throws Exception { + return new BackoffExponentiallyAndRetryOnThrowableCallable(retryableThrowable, periodMs, maxPeriodMs, + maxTries, callable).call(); + } + +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/cache/internal/BackoffExponentiallyAndRetryOnThrowableCallable.java b/core/src/main/java/org/jclouds/cache/internal/BackoffExponentiallyAndRetryOnThrowableCallable.java new file mode 100644 index 0000000000..07709edf57 --- /dev/null +++ b/core/src/main/java/org/jclouds/cache/internal/BackoffExponentiallyAndRetryOnThrowableCallable.java @@ -0,0 +1,99 @@ +/** + * 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.cache.internal; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.concurrent.Callable; + +import org.jclouds.util.Throwables2; + +import com.google.common.annotations.Beta; +import com.google.common.base.Objects; +import com.google.common.base.Throwables; +import com.google.common.collect.ForwardingObject; + +/** + * Exponentially backs off, if we encounter an exception of the given type during + * {@link Callable#call} + * + * @author Adrian Cole + * @since 1.5 + */ +@Beta +class BackoffExponentiallyAndRetryOnThrowableCallable extends ForwardingObject implements Callable { + private final Class retryableThrowable; + private final long periodMs; + private final long maxPeriodMs; + private final int maxTries; + private final Callable callable; + + BackoffExponentiallyAndRetryOnThrowableCallable(Class retryableThrowable, long periodMs, + long maxPeriodMs, int maxTries, Callable callable) { + this.retryableThrowable = checkNotNull(retryableThrowable, "retryableThrowable"); + checkArgument(maxTries > 1, "maxTries must be more than one: %d", maxTries); + this.maxTries = maxTries; + checkArgument(periodMs > 0, "periodMs must be positive: %d", periodMs); + this.periodMs = periodMs; + checkArgument(maxPeriodMs > periodMs, "maxPeriodMs must be equal to or greater than periodMs: %d %d", + maxPeriodMs, periodMs); + this.maxPeriodMs = maxPeriodMs; + this.callable = checkNotNull(callable, "callable"); + } + + @Override + protected Callable delegate() { + return callable; + } + + @Override + public T call() throws Exception { + Exception currentException = null; + for (int currentTries = 0; currentTries < maxTries; currentTries++) { + try { + return delegate().call(); + } catch (Exception e) { + currentException = e; + if (Throwables2.getFirstThrowableOfType(e, retryableThrowable) != null) { + imposeBackoffExponentialDelay(periodMs, maxPeriodMs, 2, currentTries, maxTries); + } else { + throw e; + } + } + } + throw currentException; + } + + void imposeBackoffExponentialDelay(long period, long maxPeriod, int pow, int failureCount, int max) { + long delayMs = (long) (period * Math.pow(failureCount, pow)); + delayMs = delayMs > maxPeriod ? maxPeriod : delayMs; + try { + Thread.sleep(delayMs); + } catch (InterruptedException e) { + Throwables.propagate(e); + } + } + + @Override + public String toString() { + return Objects.toStringHelper("").add("retryableThrowable", retryableThrowable).add("periodMs", periodMs).add( + "maxPeriodMs", maxPeriodMs).add("maxTries", maxTries).add("callable", callable).toString(); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/jclouds/cache/ForwardingCacheLoaderTest.java b/core/src/test/java/org/jclouds/cache/ForwardingCacheLoaderTest.java new file mode 100644 index 0000000000..bd07911b12 --- /dev/null +++ b/core/src/test/java/org/jclouds/cache/ForwardingCacheLoaderTest.java @@ -0,0 +1,82 @@ +/** + * 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.cache; + +import static org.easymock.EasyMock.createMockBuilder; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.cache.CacheLoader; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; + +/** + * Unit test for {@link ForwardingCacheLoader}. + * + * @author Adrian Cole + */ +@Test(testName = "ForwardingCacheLoaderTest", singleThreaded = true) +public class ForwardingCacheLoaderTest { + private CacheLoader forward; + private CacheLoader mock; + + @SuppressWarnings("unchecked") + @BeforeMethod + public void setUp() { + // add mocked methods for default forwarded ones + mock = createMockBuilder(CacheLoader.class).addMockedMethods("loadAll", "reload").createMock(); + forward = new ForwardingCacheLoader() { + @Override + protected CacheLoader delegate() { + return mock; + } + }; + } + + public void testLoad() throws Exception { + expect(mock.load("key")).andReturn(Boolean.TRUE); + replay(mock); + assertSame(Boolean.TRUE, forward.load("key")); + verify(mock); + } + + public void testReload() throws Exception { + ListenableFuture trueF = Futures.immediateFuture(true); + expect(mock.reload("key", false)).andReturn(trueF); + replay(mock); + assertSame(forward.reload("key", false), trueF); + verify(mock); + } + + public void testLoadAll() throws Exception { + expect(mock.loadAll(ImmutableList.of("key"))).andReturn(ImmutableMap.of("key", Boolean.TRUE)); + replay(mock); + assertEquals(ImmutableMap.of("key", Boolean.TRUE), forward.loadAll(ImmutableList.of("key"))); + verify(mock); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/jclouds/cache/RetryingCacheLoaderDecoratorTest.java b/core/src/test/java/org/jclouds/cache/RetryingCacheLoaderDecoratorTest.java new file mode 100644 index 0000000000..38c52aa7a8 --- /dev/null +++ b/core/src/test/java/org/jclouds/cache/RetryingCacheLoaderDecoratorTest.java @@ -0,0 +1,85 @@ +/** + * 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.cache; + +import static org.easymock.EasyMock.createMockBuilder; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; + +import org.jclouds.rest.ResourceNotFoundException; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.cache.CacheLoader; + +/** + * Unit tests for RetryingCacheLoaderDecorator. + * + * @author Adrian Cole + */ +@Test(testName = "ForwardingCacheLoaderTest", singleThreaded = true) +public class RetryingCacheLoaderDecoratorTest { + private CacheLoader mock; + + @SuppressWarnings("unchecked") + @BeforeMethod + public void setUp() { + // add mocked methods for default forwarded ones + mock = createMockBuilder(CacheLoader.class).addMockedMethods("loadAll", "reload").createMock(); + } + + public void testNewDecoratorDecorateSameWhenNoParams() throws Exception { + assertSame(mock, RetryingCacheLoaderDecorator.newDecorator().decorate(mock)); + } + + @Test + void testDefaultMaxTriesIs5() throws Exception { + CacheLoader backoff = RetryingCacheLoaderDecorator.newDecorator().on( + ResourceNotFoundException.class).exponentiallyBackoff().decorate(mock); + + expect(mock.load("foo")).andThrow(new ResourceNotFoundException()).times(4); + expect(mock.load("foo")).andReturn(Boolean.TRUE); + + replay(mock); + assertSame(backoff.load("foo"), Boolean.TRUE); + verify(mock); + } + + @Test + void testMaxRetriesExceededThrowsException() throws Exception { + CacheLoader backoff = RetryingCacheLoaderDecorator.newDecorator() + .on(ResourceNotFoundException.class).exponentiallyBackoff() + .decorate(mock); + + expect(mock.load("foo")).andThrow(new ResourceNotFoundException()).times(5); + + replay(mock); + try { + backoff.load("foo"); + assertTrue(false); + } catch (ResourceNotFoundException e) { + + } + verify(mock); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/jclouds/cache/internal/BackoffExponentiallyAndRetryOnThrowableCacheLoaderTest.java b/core/src/test/java/org/jclouds/cache/internal/BackoffExponentiallyAndRetryOnThrowableCacheLoaderTest.java new file mode 100644 index 0000000000..f0ce93213d --- /dev/null +++ b/core/src/test/java/org/jclouds/cache/internal/BackoffExponentiallyAndRetryOnThrowableCacheLoaderTest.java @@ -0,0 +1,76 @@ +/** + * 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.cache.internal; + +import static org.easymock.EasyMock.createMockBuilder; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; + +import org.jclouds.rest.ResourceNotFoundException; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.cache.CacheLoader; + +@Test(groups = "unit", testName = "BackoffExponentiallyAndRetryOnThrowableCacheLoaderTest") +public class BackoffExponentiallyAndRetryOnThrowableCacheLoaderTest { + private CacheLoader mock; + + @SuppressWarnings("unchecked") + @BeforeMethod + public void setUp() { + // add mocked methods for default forwarded ones + mock = createMockBuilder(CacheLoader.class).addMockedMethods("loadAll", "reload").createMock(); + } + + @Test + void testMaxRetriesNotExceededReturnsValue() throws Exception { + int attempts = 3; + BackoffExponentiallyAndRetryOnThrowableCacheLoader backoff = new BackoffExponentiallyAndRetryOnThrowableCacheLoader( + ResourceNotFoundException.class, 50l, 500l, attempts, mock); + + expect(mock.load("foo")).andThrow(new ResourceNotFoundException()).times(attempts - 1); + expect(mock.load("foo")).andReturn(Boolean.TRUE); + + replay(mock); + assertSame(backoff.load("foo"), Boolean.TRUE); + verify(mock); + } + + @Test + void testMaxRetriesExceededThrowsException() throws Exception { + int attempts = 3; + BackoffExponentiallyAndRetryOnThrowableCacheLoader backoff = new BackoffExponentiallyAndRetryOnThrowableCacheLoader( + ResourceNotFoundException.class, 50l, 500l, attempts, mock); + + expect(mock.load("foo")).andThrow(new ResourceNotFoundException()).times(attempts); + + replay(mock); + try { + backoff.load("foo"); + assertTrue(false); + } catch (ResourceNotFoundException e) { + + } + verify(mock); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/jclouds/cache/internal/BackoffExponentiallyAndRetryOnThrowableCallableTest.java b/core/src/test/java/org/jclouds/cache/internal/BackoffExponentiallyAndRetryOnThrowableCallableTest.java new file mode 100644 index 0000000000..484cea707f --- /dev/null +++ b/core/src/test/java/org/jclouds/cache/internal/BackoffExponentiallyAndRetryOnThrowableCallableTest.java @@ -0,0 +1,75 @@ +/** + * 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.cache.internal; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.concurrent.Callable; + +import org.jclouds.rest.ResourceNotFoundException; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +@Test(groups = "unit", testName = "BackoffExponentiallyAndRetryOnThrowableCallableTest") +public class BackoffExponentiallyAndRetryOnThrowableCallableTest { + private Callable mock; + + @SuppressWarnings("unchecked") + @BeforeMethod + public void setUp() { + mock = createMock(Callable.class); + } + + @Test + void testMaxRetriesNotExceededReturnsValue() throws Exception { + int attempts = 3; + BackoffExponentiallyAndRetryOnThrowableCallable backoff = new BackoffExponentiallyAndRetryOnThrowableCallable( + ResourceNotFoundException.class, 50l, 500l, attempts, mock); + + expect(mock.call()).andThrow(new ResourceNotFoundException()).times(attempts - 1); + expect(mock.call()).andReturn("foo"); + + replay(mock); + assertEquals(backoff.call(), "foo"); + verify(mock); + } + + @Test + void testMaxRetriesExceededThrowsException() throws Exception { + int attempts = 3; + BackoffExponentiallyAndRetryOnThrowableCallable backoff = new BackoffExponentiallyAndRetryOnThrowableCallable( + ResourceNotFoundException.class, 50l, 500l, attempts, mock); + + expect(mock.call()).andThrow(new ResourceNotFoundException()).times(attempts); + + replay(mock); + try { + backoff.call(); + assertTrue(false); + } catch (ResourceNotFoundException e) { + + } + verify(mock); + } +} \ No newline at end of file From 1b2eb7460458b8874b443507470764aee4593db8 Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Fri, 11 May 2012 17:39:54 +0100 Subject: [PATCH 099/148] Issue 656: failing test, demonstrating failures when non-default aws-s3 location is specified --- .../BaseBlobStoreIntegrationTest.java | 19 +++++- .../config/AWSS3BlobStoreContextModule.java | 29 ++++++++- .../integration/AWSS3ContainerLiveTest.java | 60 +++++++++++++++++++ 3 files changed, 106 insertions(+), 2 deletions(-) diff --git a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobStoreIntegrationTest.java b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobStoreIntegrationTest.java index 9445f01835..c49dd1b6f8 100644 --- a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobStoreIntegrationTest.java +++ b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobStoreIntegrationTest.java @@ -24,8 +24,8 @@ import static org.testng.Assert.assertEquals; import java.io.IOException; import java.util.Map; -import java.util.Set; import java.util.Map.Entry; +import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CancellationException; @@ -43,6 +43,7 @@ import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.StorageMetadata; import org.jclouds.blobstore.domain.StorageType; +import org.jclouds.domain.Location; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; import org.testng.ITestContext; import org.testng.annotations.AfterClass; @@ -337,6 +338,22 @@ public class BaseBlobStoreIntegrationTest extends BaseViewLiveTest bucketAcls(final S3Client client) { + CacheLoader loader = RetryingCacheLoaderDecorator.newDecorator() + .on(ResourceNotFoundException.class).exponentiallyBackoff() + .decorate( + new CacheLoader() { + @Override + public AccessControlList load(String bucketName) { + return client.getBucketACL(bucketName); + } + + @Override + public String toString() { + return "getBucketAcl()"; + } + }); + return CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.SECONDS).build(loader); + } } diff --git a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3ContainerLiveTest.java b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3ContainerLiveTest.java index 27b952bd65..c84e1c8d45 100644 --- a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3ContainerLiveTest.java +++ b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3ContainerLiveTest.java @@ -18,9 +18,23 @@ */ package org.jclouds.aws.s3.blobstore.integration; +import static org.jclouds.blobstore.options.CreateContainerOptions.Builder.publicRead; +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.NoSuchElementException; +import java.util.Set; + +import org.jclouds.blobstore.BlobStore; +import org.jclouds.blobstore.domain.BlobMetadata; +import org.jclouds.domain.Location; import org.jclouds.s3.blobstore.integration.S3ContainerLiveTest; +import org.jclouds.util.Strings2; import org.testng.annotations.Test; +import com.google.common.base.Strings; + /** * @author Adrian Cole */ @@ -29,4 +43,50 @@ public class AWSS3ContainerLiveTest extends S3ContainerLiveTest { public AWSS3ContainerLiveTest() { provider = "aws-s3"; } + + @Test(groups = { "live" }) + public void testCreateBlobInLocation() throws InterruptedException, MalformedURLException, IOException { + String payload = "my data"; + runCreateContainerInLocation(payload); + } + + @Test(groups = { "live" }) + public void testCreateBigBlobInLocation() throws InterruptedException, MalformedURLException, IOException { + String payload = Strings.repeat("a", 1024*1024); // 1MB + runCreateContainerInLocation(payload); + } + + private void runCreateContainerInLocation(String payload) throws InterruptedException, MalformedURLException, IOException { + String blobName = "hello"; + BlobStore blobStore = view.getBlobStore(); + final String containerName = getScratchContainerName(); + try { + String locationId = "EU"; + Location location = findLocation(blobStore, locationId); + blobStore.createContainerInLocation(location, containerName, publicRead()); + blobStore.putBlob(containerName, blobStore.blobBuilder(blobName).payload(payload).build()); + + assertConsistencyAwareContainerSize(containerName, 1); + + BlobMetadata metadata = view.getBlobStore().blobMetadata(containerName, blobName); + assertEquals(Strings2.toStringAndClose(view.utils().http().get(metadata.getPublicUri())), payload); + + assertConsistencyAwareBlobInLocation(containerName, blobName, location); + + } finally { + // this container is now public, so we can't reuse it directly + recycleContainer(containerName); + } + } + + private Location findLocation(BlobStore blobStore, String id) { + Set locs = blobStore.listAssignableLocations(); + for (Location loc : locs) { + if (loc.getId().equals(id)) { + return loc; + } + } + throw new NoSuchElementException("No location found with id '"+id+"'; contenders were "+locs); + } + } From c259bce6236d4e22f64867c609fc0fc62cd25764 Mon Sep 17 00:00:00 2001 From: Adam Lowe Date: Mon, 14 May 2012 17:22:56 +0100 Subject: [PATCH 100/148] Issue 872: softlayer ProductItemToImage now categorizes unknown operating systems accordingly, instead of throwing exceptions --- .../compute/functions/ProductItemToImage.java | 170 ++++++++++-------- .../functions/ProductItemToImageTest.java | 108 +++++++++-- 2 files changed, 181 insertions(+), 97 deletions(-) diff --git a/providers/softlayer/src/main/java/org/jclouds/softlayer/compute/functions/ProductItemToImage.java b/providers/softlayer/src/main/java/org/jclouds/softlayer/compute/functions/ProductItemToImage.java index 20d863b876..928ec9eaab 100644 --- a/providers/softlayer/src/main/java/org/jclouds/softlayer/compute/functions/ProductItemToImage.java +++ b/providers/softlayer/src/main/java/org/jclouds/softlayer/compute/functions/ProductItemToImage.java @@ -20,7 +20,6 @@ package org.jclouds.softlayer.compute.functions; import static com.google.common.base.Preconditions.checkNotNull; -import java.util.NoSuchElementException; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -38,6 +37,7 @@ import org.jclouds.softlayer.domain.ProductItem; import org.jclouds.softlayer.domain.ProductItemPrice; import com.google.common.base.Function; +import com.google.common.base.Objects; /** * @author Jason King @@ -45,14 +45,16 @@ import com.google.common.base.Function; @Singleton public class ProductItemToImage implements Function { - /** Pattern to capture the number of bits e.g. "a (32 bit) os" */ + /** + * Pattern to capture the number of bits e.g. "a (32 bit) os" + */ private static final Pattern OS_BITS_PATTERN = Pattern.compile(".*\\((\\d+) ?bit\\).*"); - private static final String CENTOS = "CentOS"; - private static final String DEBIAN = "Debian GNU/Linux"; - private static final String FEDORA = "Fedora Release"; - private static final String RHEL = "Red Hat Enterprise Linux"; - private static final String UBUNTU = "Ubuntu Linux"; + private static final String CENTOS = "CentOS"; + private static final String DEBIAN = "Debian GNU/Linux"; + private static final String FEDORA = "Fedora Release"; + private static final String RHEL = "Red Hat Enterprise Linux"; + private static final String UBUNTU = "Ubuntu Linux"; private static final String WINDOWS = "Windows Server"; private static final String CLOUD_LINUX = "CloudLinux"; @@ -62,111 +64,123 @@ public class ProductItemToImage implements Function { @Override public Image apply(ProductItem productItem) { - checkNotNull(productItem,"productItem"); + checkNotNull(productItem, "productItem"); + String description = checkNotNull(productItem.getDescription(), "productItem.description"); - OsFamily family = osFamily().apply(productItem); - Integer bits = osBits().apply(productItem); + OsFamily osFamily = osFamily().apply(description); + if (osFamily == OsFamily.UNRECOGNIZED) { + logger.debug("Cannot determine os family for item: %s", productItem); + } + Integer bits = osBits().apply(description); + if (bits == null) { + logger.debug("Cannot determine os bits for item: %s", productItem); + } + String osVersion = osVersion().apply(description); + if (osVersion == null) { + logger.debug("Cannot determine os version for item: %s", productItem); + } OperatingSystem os = OperatingSystem.builder() - .description(productItem.getDescription()) - .family(family) - .version(osVersion().apply(productItem)) - .is64Bit(bits.equals(64)) - .build(); + .description(description) + .family(osFamily) + .version(osVersion) + .is64Bit(Objects.equal(bits, 64)) + .build(); return new ImageBuilder() .ids(imageId().apply(productItem)) - .description(productItem.getDescription()) + .description(description) .operatingSystem(os) .build(); } /** * Parses the item description to determine the OSFamily + * * @return the @see OsFamily or OsFamily.UNRECOGNIZED */ - public static Function osFamily() { - return new Function() { - @Override - public OsFamily apply(ProductItem productItem) { - checkNotNull(productItem,"productItem"); - - final String description = productItem.getDescription(); - if(description.startsWith(CENTOS)) return OsFamily.CENTOS; - else if(description.startsWith(DEBIAN)) return OsFamily.DEBIAN; - else if(description.startsWith(FEDORA)) return OsFamily.FEDORA; - else if(description.startsWith(RHEL)) return OsFamily.RHEL; - else if(description.startsWith(UBUNTU)) return OsFamily.UBUNTU; - else if(description.startsWith(WINDOWS)) return OsFamily.WINDOWS; - else if(description.startsWith(CLOUD_LINUX)) return OsFamily.CLOUD_LINUX; - return OsFamily.UNRECOGNIZED; + public static Function osFamily() { + return new Function() { + @Override + public OsFamily apply(final String description) { + if (description != null) { + if (description.startsWith(CENTOS)) return OsFamily.CENTOS; + else if (description.startsWith(DEBIAN)) return OsFamily.DEBIAN; + else if (description.startsWith(FEDORA)) return OsFamily.FEDORA; + else if (description.startsWith(RHEL)) return OsFamily.RHEL; + else if (description.startsWith(UBUNTU)) return OsFamily.UBUNTU; + else if (description.startsWith(WINDOWS)) return OsFamily.WINDOWS; + else if (description.startsWith(CLOUD_LINUX)) return OsFamily.CLOUD_LINUX; } - }; - } + + return OsFamily.UNRECOGNIZED; + } + }; + } /** * Parses the item description to determine the os version - * @return the version - * @throws java.util.NoSuchElementException if the version cannot be determined + * + * @return the version or null if the version cannot be determined */ - public static Function osVersion() { - return new Function() { - @Override - public String apply(ProductItem productItem) { - checkNotNull(productItem,"productItem"); + public static Function osVersion() { + return new Function() { + @Override + public String apply(final String description) { + OsFamily family = osFamily().apply(description); - final String description = productItem.getDescription(); - OsFamily family = osFamily().apply(productItem); - if (family.equals(OsFamily.CENTOS)) return parseVersion(description, CENTOS); - else if(family.equals(OsFamily.DEBIAN)) return parseVersion(description, DEBIAN); - else if(family.equals(OsFamily.FEDORA)) return parseVersion(description, FEDORA); - else if(family.equals(OsFamily.RHEL)) return parseVersion(description, RHEL); - else if(family.equals(OsFamily.UBUNTU)) return parseVersion(description, UBUNTU); - else if(family.equals(OsFamily.WINDOWS)) return parseVersion(description, WINDOWS); - else if(family.equals(OsFamily.CLOUD_LINUX)) return parseVersion(description, CLOUD_LINUX); - else throw new NoSuchElementException("No os version for item:"+productItem); - } - }; - } + if (Objects.equal(family, OsFamily.CENTOS)) return parseVersion(description, CENTOS); + else if (Objects.equal(family, OsFamily.DEBIAN)) return parseVersion(description, DEBIAN); + else if (Objects.equal(family, OsFamily.FEDORA)) return parseVersion(description, FEDORA); + else if (Objects.equal(family, OsFamily.RHEL)) return parseVersion(description, RHEL); + else if (Objects.equal(family, OsFamily.UBUNTU)) return parseVersion(description, UBUNTU); + else if (Objects.equal(family, OsFamily.WINDOWS)) return parseVersion(description, WINDOWS); + else if (Objects.equal(family, OsFamily.CLOUD_LINUX)) return parseVersion(description, CLOUD_LINUX); - private static String parseVersion(String description, String os) { - String noOsName = description.replaceFirst(os,"").trim(); - return noOsName.split(" ")[0]; - } + return null; + } + }; + } + + private static String parseVersion(String description, String os) { + String noOsName = description.replaceFirst(os, "").trim(); + return noOsName.split(" ")[0]; + } /** * Parses the item description to determine the number of OS bits * Expects the number to be in parenthesis and to contain the word "bit". * The following return 64: "A (64 bit) OS", "A (64bit) OS" - * @return the number of bits - * @throws java.util.NoSuchElementException if the number of bits cannot be determined + * + * @return the number of bits or null if the number of bits cannot be determined */ - public static Function osBits() { - return new Function() { - @Override - public Integer apply(ProductItem productItem) { - checkNotNull(productItem,"productItem"); - - Matcher m = OS_BITS_PATTERN.matcher(productItem.getDescription()); + public static Function osBits() { + return new Function() { + @Override + public Integer apply(String description) { + if (description != null) { + Matcher m = OS_BITS_PATTERN.matcher(description); if (m.matches()) { return Integer.parseInt(m.group(1)); - } else { - throw new NoSuchElementException("Cannot determine os-bits for item:"+productItem); } } - }; - } - /** - * Generates an id for an Image. - * @return the generated id - */ - public static Function imageId() { - return new Function() { + return null; + } + }; + } + + /** + * Generates an id for an Image. + * + * @return the generated id + */ + public static Function imageId() { + return new Function() { @Override public String apply(ProductItem productItem) { - checkNotNull(productItem,"productItem"); + checkNotNull(productItem, "productItem"); ProductItemPrice price = ProductItems.price().apply(productItem); - return ""+price.getId(); + return "" + price.getId(); } }; } diff --git a/providers/softlayer/src/test/java/org/jclouds/softlayer/compute/functions/ProductItemToImageTest.java b/providers/softlayer/src/test/java/org/jclouds/softlayer/compute/functions/ProductItemToImageTest.java index 94bd1f4550..39f8c9babc 100644 --- a/providers/softlayer/src/test/java/org/jclouds/softlayer/compute/functions/ProductItemToImageTest.java +++ b/providers/softlayer/src/test/java/org/jclouds/softlayer/compute/functions/ProductItemToImageTest.java @@ -25,6 +25,7 @@ import static org.jclouds.softlayer.compute.functions.ProductItemToImage.osVersi import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; import java.util.Arrays; @@ -131,6 +132,62 @@ public class ProductItemToImageTest { assertTrue(os.is64Bit()); } + @Test + public void testUbuntuNoBitCount() { + ProductItem item = ProductItem.builder() + .description("Ubuntu Linux 10.04 LTS Lucid Lynx - Minimal Install") + .price(ProductItemPrice.builder().id(1234).build()) + .build(); + Image i = new ProductItemToImage().apply(item); + OperatingSystem os = i.getOperatingSystem(); + assertNotNull(os); + assertEquals(OsFamily.UBUNTU, os.getFamily()); + assertEquals("10.04",os.getVersion()); + assertFalse(os.is64Bit()); + } + + + @Test + public void testCompletelyUnknown() { + ProductItem item = ProductItem.builder() + .description("This fails to match anything!!!") + .price(ProductItemPrice.builder().id(1234).build()) + .build(); + Image i = new ProductItemToImage().apply(item); + OperatingSystem os = i.getOperatingSystem(); + assertNotNull(os); + assertEquals(OsFamily.UNRECOGNIZED, os.getFamily()); + assertNull(os.getVersion()); + assertFalse(os.is64Bit()); + } + + @Test + public void test64BitUnknown() { + ProductItem item = ProductItem.builder() + .description("This only has the bit-count (64 bit)") + .price(ProductItemPrice.builder().id(1234).build()) + .build(); + Image i = new ProductItemToImage().apply(item); + OperatingSystem os = i.getOperatingSystem(); + assertNotNull(os); + assertEquals(OsFamily.UNRECOGNIZED, os.getFamily()); + assertNull(os.getVersion()); + assertTrue(os.is64Bit()); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testNull() { + new ProductItemToImage().apply(null); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testNoDescription() { + ProductItem item = ProductItem.builder() + .price(ProductItemPrice.builder().id(1234).build()) + .build(); + new ProductItemToImage().apply(item); + } + @Test public void testId() { ProductItemPrice price = ProductItemPrice.builder().id(1234).build(); @@ -151,46 +208,59 @@ public class ProductItemToImageTest { ProductItem item = ProductItem.builder().build(); imageId().apply(item); } + + @Test(expectedExceptions = NullPointerException.class) + public void testIdNull() { + imageId().apply(null); + } @Test public void testOsFamily() { - ProductItem item = ProductItem.builder().description("Ubuntu Linux os").build(); - assertEquals(OsFamily.UBUNTU,osFamily().apply(item)); + assertEquals(OsFamily.UBUNTU,osFamily().apply("Ubuntu Linux os")); } @Test public void testOsFamilyUnrecognized() { - ProductItem item = ProductItem.builder().description("not a known operating system").build(); - assertEquals(OsFamily.UNRECOGNIZED,osFamily().apply(item)); + assertEquals(OsFamily.UNRECOGNIZED,osFamily().apply("not a known operating system")); + } + + @Test + public void testOsFamilyNull() { + assertEquals(OsFamily.UNRECOGNIZED,osFamily().apply(null)); } @Test - public void testBitsWithSpace() { - ProductItem item = ProductItem.builder().description("a (32 bit) os").build(); - assertEquals(osBits().apply(item),new Integer(32)); + public void testOsBitsWithSpace() { + assertEquals(osBits().apply("a (32 bit) os"),new Integer(32)); } @Test - public void testBitsNoSpace() { - ProductItem item = ProductItem.builder().description("a (64bit) os").build(); - assertEquals(osBits().apply(item),new Integer(64)); + public void testOsBitsNoSpace() { + assertEquals(osBits().apply("a (64bit) os"),new Integer(64)); } - @Test(expectedExceptions = NoSuchElementException.class) - public void testBitsMissing() { - ProductItem item = ProductItem.builder().description("an os").build(); - osBits().apply(item); + @Test + public void testOsBitsMissing() { + assertNull(osBits().apply("an os")); + } + + @Test + public void testOsBitsNull() { + assertNull(osBits().apply(null)); } @Test public void testOsVersion() { - ProductItem item = ProductItem.builder().description("Windows Server 2099 (256 bit)").build(); - assertEquals("2099",osVersion().apply(item)); + assertEquals("2099",osVersion().apply("Windows Server 2099 (256 bit)")); } - @Test(expectedExceptions = NoSuchElementException.class) + @Test public void testOsVersionMissing() { - ProductItem item = ProductItem.builder().description("asd Server").build(); - osVersion().apply(item); + assertNull(osVersion().apply("asd Server")); + } + + @Test + public void testOsVersionNull() { + assertNull(osVersion().apply(null)); } } From dbb56e64da7278682d2b4038781144e8c43fb3f7 Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Tue, 15 May 2012 09:52:05 -0700 Subject: [PATCH 101/148] Remove quirky isSame method Use safer InputSupplier instead. --- .../FilesystemAsyncBlobStoreTest.java | 14 ++++-- .../FilesystemStorageStrategyImplTest.java | 15 ++++-- .../jclouds/filesystem/utils/TestUtils.java | 49 ------------------- 3 files changed, 22 insertions(+), 56 deletions(-) diff --git a/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemAsyncBlobStoreTest.java b/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemAsyncBlobStoreTest.java index 86860b1796..2d31ccf356 100644 --- a/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemAsyncBlobStoreTest.java +++ b/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemAsyncBlobStoreTest.java @@ -57,6 +57,7 @@ import org.jclouds.crypto.CryptoStreams; import org.jclouds.filesystem.reference.FilesystemConstants; import org.jclouds.filesystem.utils.TestUtils; import org.jclouds.http.HttpRequest; +import org.jclouds.io.InputSuppliers; import org.jclouds.io.payloads.PhantomPayload; import org.jclouds.io.payloads.StringPayload; import org.testng.annotations.AfterMethod; @@ -64,7 +65,10 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import com.google.common.io.ByteStreams; import com.google.common.io.Closeables; +import com.google.common.io.Files; +import com.google.common.io.InputSupplier; import com.google.inject.CreationException; /** @@ -620,9 +624,13 @@ public class FilesystemAsyncBlobStoreTest { assertNotNull(resultBlob, "Blob exists"); // checks file content - InputStream expectedFile = new FileInputStream(TARGET_CONTAINER_NAME + File.separator + blobKey); - InputStream currentFile = resultBlob.getPayload().getInput(); - assertTrue(TestUtils.isSame(expectedFile, currentFile), "Blob payload differs from file content"); + InputSupplier expectedFile = + Files.newInputStreamSupplier(new File( + TARGET_CONTAINER_NAME, blobKey)); + InputSupplier actualFile = + InputSuppliers.of(resultBlob.getPayload().getInput()); + assertTrue(ByteStreams.equal(expectedFile, actualFile), + "Blob payload differs from file content"); // metadata are verified in the test for blobMetadata, so no need to // perform a complete test here assertNotNull(resultBlob.getMetadata(), "Metadata null"); diff --git a/apis/filesystem/src/test/java/org/jclouds/filesystem/strategy/internal/FilesystemStorageStrategyImplTest.java b/apis/filesystem/src/test/java/org/jclouds/filesystem/strategy/internal/FilesystemStorageStrategyImplTest.java index 599d789075..779d262712 100644 --- a/apis/filesystem/src/test/java/org/jclouds/filesystem/strategy/internal/FilesystemStorageStrategyImplTest.java +++ b/apis/filesystem/src/test/java/org/jclouds/filesystem/strategy/internal/FilesystemStorageStrategyImplTest.java @@ -51,6 +51,10 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import com.google.common.io.ByteStreams; +import com.google.common.io.Files; +import com.google.common.io.InputSupplier; + /** * Test class for {@link FilesystemStorageStrategyImpl } class * @@ -371,10 +375,13 @@ public class FilesystemStorageStrategyImplTest { // write files storageStrategy.writePayloadOnFile(CONTAINER_NAME, blobKey, filePayload); // verify that the files is equal - String blobFullPath = TARGET_CONTAINER_NAME + FS + blobKey; - InputStream expectedInput = new FileInputStream(sourceFile); - InputStream currentInput = new FileInputStream(blobFullPath); - assertTrue(TestUtils.isSame(expectedInput, currentInput), "Files aren't equals"); + File blobFullPath = new File(TARGET_CONTAINER_NAME, blobKey); + InputSupplier expectedInput = + Files.newInputStreamSupplier(sourceFile); + InputSupplier actualInput = + Files.newInputStreamSupplier(blobFullPath); + assertTrue(ByteStreams.equal(expectedInput, actualInput), + "Files are not equal"); } public void testWritePayloadOnFile_SourceFileDoesntExist() { diff --git a/apis/filesystem/src/test/java/org/jclouds/filesystem/utils/TestUtils.java b/apis/filesystem/src/test/java/org/jclouds/filesystem/utils/TestUtils.java index d430a05c8b..e0680fe8ed 100644 --- a/apis/filesystem/src/test/java/org/jclouds/filesystem/utils/TestUtils.java +++ b/apis/filesystem/src/test/java/org/jclouds/filesystem/utils/TestUtils.java @@ -200,53 +200,4 @@ public class TestUtils { if (imageResourceIndex >= imageResource.length) imageResourceIndex = 0; return new File(fileName); } - - - /** - * Compare two input stream - * - * @param input1 the first stream - * @param input2 the second stream - * @return true if the streams contain the same content, or false otherwise - * @throws IOException - * @throws IllegalArgumentException if the stream is null - */ - public static boolean isSame(InputStream input1, InputStream input2 ) throws IOException { - boolean error = false; - try { - byte[] buffer1 = new byte[1024]; - byte[] buffer2 = new byte[1024]; - try { - int numRead1 = 0; - int numRead2 = 0; - while (true) { - numRead1 = input1.read(buffer1); - numRead2 = input2.read(buffer2); - if (numRead1 > -1) { - if (numRead2 != numRead1) return false; - // Otherwise same number of bytes read - if (!Arrays.equals(buffer1, buffer2)) return false; - // Otherwise same bytes read, so continue ... - } else { - // Nothing more in stream 1 ... - return numRead2 < 0; - } - } - } finally { - input1.close(); - } - } catch (IOException e) { - error = true; // this error should be thrown, even if there is an error closing stream 2 - throw e; - } catch (RuntimeException e) { - error = true; // this error should be thrown, even if there is an error closing stream 2 - throw e; - } finally { - try { - input2.close(); - } catch (IOException e) { - if (!error) throw e; - } - } - } } From ffb9b34cea9243d1842a1ac4a205f0068ff34826 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Tue, 15 May 2012 12:12:21 -0700 Subject: [PATCH 102/148] ensure mock tests are run single-threaded --- .../BackoffExponentiallyAndRetryOnThrowableCallableTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/test/java/org/jclouds/cache/internal/BackoffExponentiallyAndRetryOnThrowableCallableTest.java b/core/src/test/java/org/jclouds/cache/internal/BackoffExponentiallyAndRetryOnThrowableCallableTest.java index 484cea707f..4e370a6109 100644 --- a/core/src/test/java/org/jclouds/cache/internal/BackoffExponentiallyAndRetryOnThrowableCallableTest.java +++ b/core/src/test/java/org/jclouds/cache/internal/BackoffExponentiallyAndRetryOnThrowableCallableTest.java @@ -31,7 +31,7 @@ import org.jclouds.rest.ResourceNotFoundException; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -@Test(groups = "unit", testName = "BackoffExponentiallyAndRetryOnThrowableCallableTest") +@Test(groups = "unit", testName = "BackoffExponentiallyAndRetryOnThrowableCallableTest", singleThreaded = true) public class BackoffExponentiallyAndRetryOnThrowableCallableTest { private Callable mock; @@ -72,4 +72,4 @@ public class BackoffExponentiallyAndRetryOnThrowableCallableTest { } verify(mock); } -} \ No newline at end of file +} From c1d9a7c47c9e97a7d01a163231d16385deb4771d Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 9 May 2012 17:52:15 +0200 Subject: [PATCH 103/148] Joyent server and Dataset features --- .../joyent/sdc/v6_5/SDCAsyncClient.java | 20 ++ .../jclouds/joyent/sdc/v6_5/SDCClient.java | 20 ++ .../sdc/v6_5/config/SDCParserModule.java | 49 ++++ .../sdc/v6_5/config/SDCRestClientModule.java | 10 + .../joyent/sdc/v6_5/domain/Dataset.java | 162 +++++++++++ .../joyent/sdc/v6_5/domain/Machine.java | 271 ++++++++++++++++++ .../joyent/sdc/v6_5/domain/Package.java | 156 ++++++++++ .../jclouds/joyent/sdc/v6_5/domain/Type.java | 29 ++ .../sdc/v6_5/features/DatasetAsyncClient.java | 51 ++++ .../sdc/v6_5/features/DatasetClient.java | 34 +++ .../sdc/v6_5/features/MachineAsyncClient.java | 71 +++++ .../sdc/v6_5/features/MachineClient.java | 55 ++++ .../sdc/v6_5/features/PackageAsyncClient.java | 50 ++++ .../sdc/v6_5/features/PackageClient.java | 33 +++ .../functions/internal/SDCTypeAdapters.java | 59 ++++ .../features/DatasetClientExpectTest.java | 70 +++++ .../v6_5/features/DatasetClientLiveTest.java | 50 ++++ .../features/MachineClientExpectTest.java | 70 +++++ .../v6_5/features/MachineClientLiveTest.java | 50 ++++ .../features/PackageClientExpectTest.java | 70 +++++ .../v6_5/features/PackageClientLiveTest.java | 49 ++++ .../sdc/v6_5/parse/ParseDatasetListTest.java | 90 ++++++ .../sdc/v6_5/parse/ParseDatasetTest.java | 73 +++++ .../sdc/v6_5/parse/ParseMachineListTest.java | 120 ++++++++ .../sdc/v6_5/parse/ParseMachineTest.java | 78 +++++ .../sdc/v6_5/parse/ParsePackageListTest.java | 84 ++++++ .../sdc/v6_5/parse/ParsePackageTest.java | 68 +++++ .../src/test/resources/dataset.json | 1 + .../src/test/resources/dataset_list.json | 1 + .../src/test/resources/machine.json | 1 + .../src/test/resources/machine_list.json | 1 + .../src/test/resources/package.json | 1 + .../src/test/resources/package_list.json | 1 + 33 files changed, 1948 insertions(+) create mode 100644 labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCParserModule.java create mode 100644 labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Dataset.java create mode 100644 labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Machine.java create mode 100644 labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Package.java create mode 100644 labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Type.java create mode 100644 labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetAsyncClient.java create mode 100644 labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClient.java create mode 100644 labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineAsyncClient.java create mode 100644 labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineClient.java create mode 100644 labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageAsyncClient.java create mode 100644 labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageClient.java create mode 100644 labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/functions/internal/SDCTypeAdapters.java create mode 100644 labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientExpectTest.java create mode 100644 labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientLiveTest.java create mode 100644 labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientExpectTest.java create mode 100644 labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientLiveTest.java create mode 100644 labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientExpectTest.java create mode 100644 labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientLiveTest.java create mode 100644 labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseDatasetListTest.java create mode 100644 labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseDatasetTest.java create mode 100644 labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineListTest.java create mode 100644 labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineTest.java create mode 100644 labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParsePackageListTest.java create mode 100644 labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParsePackageTest.java create mode 100644 labs/joyent-sdc/src/test/resources/dataset.json create mode 100644 labs/joyent-sdc/src/test/resources/dataset_list.json create mode 100644 labs/joyent-sdc/src/test/resources/machine.json create mode 100644 labs/joyent-sdc/src/test/resources/machine_list.json create mode 100644 labs/joyent-sdc/src/test/resources/package.json create mode 100644 labs/joyent-sdc/src/test/resources/package_list.json diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCAsyncClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCAsyncClient.java index e4e42de97b..e96f21689e 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCAsyncClient.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCAsyncClient.java @@ -19,6 +19,9 @@ package org.jclouds.joyent.sdc.v6_5; import org.jclouds.joyent.sdc.v6_5.features.DatacenterAsyncClient; +import org.jclouds.joyent.sdc.v6_5.features.DatasetAsyncClient; +import org.jclouds.joyent.sdc.v6_5.features.MachineAsyncClient; +import org.jclouds.joyent.sdc.v6_5.features.PackageAsyncClient; import org.jclouds.rest.annotations.Delegate; /** @@ -36,5 +39,22 @@ public interface SDCAsyncClient { */ @Delegate DatacenterAsyncClient getDatacenterClient(); + + /** + * Provides asynchronous access to Machine features. + */ + @Delegate + MachineAsyncClient getMachineClient(); + /** + * Provides asynchronous access to Dataset features. + */ + @Delegate + DatasetAsyncClient getDatasetClient(); + + /** + * Provides asynchronous access to Package features. + */ + @Delegate + PackageAsyncClient getPackageClient(); } diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCClient.java index 47ebe9fa27..3f30caaaac 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCClient.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCClient.java @@ -22,6 +22,9 @@ import java.util.concurrent.TimeUnit; import org.jclouds.concurrent.Timeout; import org.jclouds.joyent.sdc.v6_5.features.DatacenterClient; +import org.jclouds.joyent.sdc.v6_5.features.DatasetClient; +import org.jclouds.joyent.sdc.v6_5.features.MachineClient; +import org.jclouds.joyent.sdc.v6_5.features.PackageClient; import org.jclouds.rest.annotations.Delegate; /** @@ -40,5 +43,22 @@ public interface SDCClient { */ @Delegate DatacenterClient getDatacenterClient(); + + /** + * Provides synchronous access to Machine features. + */ + @Delegate + MachineClient getMachineClient(); + /** + * Provides synchronous access to Dataset features. + */ + @Delegate + DatasetClient getDatasetClient(); + + /** + * Provides synchronous access to Package features. + */ + @Delegate + PackageClient getPackageClient(); } diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCParserModule.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCParserModule.java new file mode 100644 index 0000000000..534fc94646 --- /dev/null +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCParserModule.java @@ -0,0 +1,49 @@ +/** + * 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.joyent.sdc.v6_5.config; + +import java.lang.reflect.Type; +import java.util.Map; + +import javax.inject.Singleton; + +import org.jclouds.joyent.sdc.v6_5.domain.Machine; +import org.jclouds.joyent.sdc.v6_5.functions.internal.SDCTypeAdapters; + +import com.google.common.collect.ImmutableMap; +import com.google.inject.AbstractModule; +import com.google.inject.Provides; + +/** + * @author Adrian Cole + */ +public class SDCParserModule extends AbstractModule { + + @Provides + @Singleton + public Map provideCustomAdapterBindings() { + return ImmutableMap. of(Machine.State.class, new SDCTypeAdapters.ServerStateAdapter(), + Type.class, new SDCTypeAdapters.SDCTypeAdapter()); + } + + @Override + protected void configure() { + } + +} diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCRestClientModule.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCRestClientModule.java index 201cb77bd4..e102cc00d4 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCRestClientModule.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCRestClientModule.java @@ -28,6 +28,12 @@ import org.jclouds.joyent.sdc.v6_5.SDCAsyncClient; import org.jclouds.joyent.sdc.v6_5.SDCClient; import org.jclouds.joyent.sdc.v6_5.features.DatacenterAsyncClient; import org.jclouds.joyent.sdc.v6_5.features.DatacenterClient; +import org.jclouds.joyent.sdc.v6_5.features.DatasetAsyncClient; +import org.jclouds.joyent.sdc.v6_5.features.DatasetClient; +import org.jclouds.joyent.sdc.v6_5.features.MachineAsyncClient; +import org.jclouds.joyent.sdc.v6_5.features.MachineClient; +import org.jclouds.joyent.sdc.v6_5.features.PackageAsyncClient; +import org.jclouds.joyent.sdc.v6_5.features.PackageClient; import org.jclouds.joyent.sdc.v6_5.handlers.SDCErrorHandler; import org.jclouds.json.config.GsonModule.DateAdapter; import org.jclouds.json.config.GsonModule.Iso8601DateAdapter; @@ -45,6 +51,9 @@ import com.google.common.collect.ImmutableMap; public class SDCRestClientModule extends RestClientModule { public static final Map, Class> DELEGATE_MAP = ImmutableMap., Class> builder() .put(DatacenterClient.class, DatacenterAsyncClient.class) + .put(MachineClient.class, MachineAsyncClient.class) + .put(DatasetClient.class, DatasetAsyncClient.class) + .put(PackageClient.class, PackageAsyncClient.class) .build(); public SDCRestClientModule() { @@ -54,6 +63,7 @@ public class SDCRestClientModule extends RestClientModule + */ +public class Dataset implements Comparable { + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String id; + private String name; + private Type type; + private String version; + private String urn; + private boolean defaultDataset; + private Date created; + + public Builder id(String id) { + this.id = id; + return this; + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder type(Type type) { + this.type = type; + return this; + } + + public Builder version(String version) { + this.version = version; + return this; + } + + public Builder urn(String urn) { + this.urn = urn; + return this; + } + + public Builder defaultDataset(boolean defaultDataset) { + this.defaultDataset = defaultDataset; + return this; + } + + public Builder created(Date created) { + this.created = created; + return this; + } + + public Dataset build() { + return new Dataset(id, name, type, version, urn, defaultDataset, created); + } + + public Builder fromDataset(Dataset in) { + return id(in.getId()).name(in.getName()).type(in.getType()).version( + in.getVersion()).urn(in.getUrn()).defaultDataset( + in.isDefaultDataset()).created( + in.getCreated()); + } + } + + // The globally unique id for this dataset + protected final String id; + // The "friendly" name for this dataset + protected final String name; + // Whether this is a smartmachine or virtualmachine + protected final Type type; + // The version for this dataset + protected final String version; + // The full URN for this dataset + protected final String urn; + // Whether this is the default dataset in this datacenter + @SerializedName("default") + protected final boolean defaultDataset; + // Date (ISO8601) When this dataset was created + protected final Date created; + + public Dataset(String id, String name, Type type, String version, + String urn, boolean defaultDataset, Date created) { + super(); + this.id = id; + this.name = name; + this.type = type; + this.version = version; + this.urn = urn; + this.defaultDataset = defaultDataset; + this.created = created; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public Type getType() { + return type; + } + + public String getVersion() { + return version; + } + + public String getUrn() { + return urn; + } + + public boolean isDefaultDataset() { + return defaultDataset; + } + + public Date getCreated() { + return created; + } + + @Override + public int compareTo(Dataset other) { + return id.compareTo(other.getId()); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object instanceof Machine) { + return Objects.equal(id, ((Machine) object).id); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } + + @Override + public String toString() { + return String + .format( + "[id=%s, name=%s, type=%s, version=%s, urn=%s, default=%s, created=%s]", + id, name, type.name(), type.name(), version, urn, + defaultDataset, created); + } +} diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Machine.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Machine.java new file mode 100644 index 0000000000..11fd3491c2 --- /dev/null +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Machine.java @@ -0,0 +1,271 @@ +/** + * 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.joyent.sdc.v6_5.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Date; +import java.util.Map; +import java.util.Set; + +import com.google.common.base.CaseFormat; +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.gson.annotations.SerializedName; + +/** + * Listing of a machine. + * + * @author Gerald Pereira + * @see + */ +public class Machine implements Comparable { + + public static enum State { + PUBLISHING, RUNNING, STOPPED, UNRECOGNIZED; + + public static State fromValue(String state) { + try { + return valueOf(CaseFormat.UPPER_CAMEL.to( + CaseFormat.UPPER_UNDERSCORE, checkNotNull(state, + "state"))); + } catch (IllegalArgumentException e) { + return UNRECOGNIZED; + } + } + + public String value() { + return (CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, + name())); + } + + @Override + public String toString() { + return value(); + } + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String id; + private String name; + private Type type; + private State state; + private String dataset; + private int memorySizeMb; + private int diskSizeGb; + private Set ips; + private Date created; + private Date updated; + private Map metadata = ImmutableMap.of(); + + public Builder id(String id) { + this.id = id; + return this; + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder type(Type type) { + this.type = type; + return this; + } + + public Builder state(State state) { + this.state = state; + return this; + } + + public Builder dataset(String dataset) { + this.dataset = dataset; + return this; + } + + public Builder memorySizeMb(int memorySizeMb) { + this.memorySizeMb = memorySizeMb; + return this; + } + + public Builder diskSizeGb(int diskSizeGb) { + this.diskSizeGb = diskSizeGb; + return this; + } + + public Builder ips(Set ips) { + this.ips = ips; + return this; + } + + public Builder created(Date created) { + this.created = created; + return this; + } + + public Builder updated(Date updated) { + this.updated = updated; + return this; + } + + /** + * @see Machine#getMetadata() + */ + public Builder metadata(Map metadata) { + this.metadata = metadata; + return this; + } + + public Machine build() { + return new Machine(id, name, type, state, dataset, memorySizeMb, + diskSizeGb, ips, created, updated, metadata); + } + + public Builder fromMachine(Machine in) { + return id(in.getId()).name(in.getName()).type(in.getType()).state( + in.getState()).dataset(in.getDataset()).memorySizeMb( + in.getMemorySizeMb()).diskSizeGb(in.getDiskSizeGb()).ips( + in.getIps()).metadata(in.getMetadata()).created( + in.getCreated()).updated(in.getUpdated()); + } + } + + // The globally unique id for this machine + protected final String id; + // The "friendly" name for this machine + protected final String name; + // Whether this is a smartmachine or virtualmachine + protected final Type type; + // The current state of this machine + protected final State state; + // The dataset urn this machine was provisioned with + protected final String dataset; + // The amount of memory this machine has (Mb) + @SerializedName("memory") + protected final int memorySizeMb; + // The amount of disk this machine has (Gb) + @SerializedName("disk") + protected final int diskSizeGb; + // The IP addresses this machine has + protected final Set ips; + // Date (ISO8601) When this machine was created + protected final Date created; + // Date (ISO8601) When this machine was updated + protected final Date updated; + + // metadata Object[String => String] Any "extra" metadata this machine has + private final Map metadata; + + @Override + public int compareTo(Machine other) { + return id.compareTo(other.getId()); + } + + public Machine(String id, String name, Type type, State state, + String dataset, int memorySizeMb, int diskSizeGb, Set ips, + Date created, Date updated, final Map metadata) { + super(); + this.id = id; + this.name = name; + this.type = type; + this.state = state; + this.dataset = dataset; + this.memorySizeMb = memorySizeMb; + this.diskSizeGb = diskSizeGb; + this.ips = ImmutableSet. copyOf(ips); + this.created = created; + this.updated = updated; + this.metadata = metadata; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public Type getType() { + return type; + } + + public State getState() { + return state; + } + + public String getDataset() { + return dataset; + } + + public int getMemorySizeMb() { + return memorySizeMb; + } + + public int getDiskSizeGb() { + return diskSizeGb; + } + + public Set getIps() { + return ips; + } + + public Date getCreated() { + return created; + } + + public Date getUpdated() { + return updated; + } + + public Map getMetadata() { + return metadata; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object instanceof Machine) { + return Objects.equal(id, ((Machine) object).id); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } + + @Override + public String toString() { + return String + .format( + "[id=%s, name=%s, type=%s, state=%s, memory=%s, disk=%s, ips=%s, created=%s, updated=%s]", + id, name, type.name(), state.name(), memorySizeMb, + diskSizeGb, ips, created, updated); + } +} diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Package.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Package.java new file mode 100644 index 0000000000..46387ef792 --- /dev/null +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Package.java @@ -0,0 +1,156 @@ +/** + * 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.joyent.sdc.v6_5.domain; + +import com.google.common.base.Objects; +import com.google.gson.annotations.SerializedName; + +/** + * Listing of a package. + * + * @author Gerald Pereira + * @see + */ +public class Package implements Comparable { + + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String name; + private int memorySizeMb; + private int diskSizeGb; + private int swapSizeMb; + private boolean defaultPackage; + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder memorySizeMb(int memorySizeMb) { + this.memorySizeMb = memorySizeMb; + return this; + } + + public Builder diskSizeGb(int diskSizeGb) { + this.diskSizeGb = diskSizeGb; + return this; + } + + public Builder swapSizeMb(int swapSizeMb) { + this.swapSizeMb = swapSizeMb; + return this; + } + + public Builder isDefault(boolean defaultPackage) { + this.defaultPackage = defaultPackage; + return this; + } + + + public Package build() { + return new Package(name, memorySizeMb, + diskSizeGb, swapSizeMb, defaultPackage); + } + + public Builder fromPackage(Package in) { + return name(in.getName()).memorySizeMb( + in.getMemorySizeMb()).diskSizeGb(in.getDiskSizeGb()).swapSizeMb(in.getSwapSizeMb()).isDefault(in.isDefault()); + } + } + + // The "friendly" name for this machine + protected final String name; + // The amount of memory this package has (Mb) + @SerializedName("memory") + protected final int memorySizeMb; + // The amount of disk this package has (Gb) + @SerializedName("disk") + protected final int diskSizeGb; + // The amount of swap this package has (Gb) + @SerializedName("swap") + protected final int swapSizeMb; + // Whether this is the default package in this datacenter + @SerializedName("default") + protected final boolean defaultPackage; + + @Override + public int compareTo(Package other) { + return name.compareTo(other.getName()); + } + + public Package(String name, int memorySizeMb, int diskSizeGb, + int swapSizeMb, boolean defaultPackage) { + super(); + this.name = name; + this.memorySizeMb = memorySizeMb; + this.diskSizeGb = diskSizeGb; + this.swapSizeMb = swapSizeMb; + this.defaultPackage = defaultPackage; + } + + public String getName() { + return name; + } + + public int getMemorySizeMb() { + return memorySizeMb; + } + + public int getDiskSizeGb() { + return diskSizeGb; + } + + public int getSwapSizeMb() { + return swapSizeMb; + } + + public boolean isDefault() { + return defaultPackage; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object instanceof Package) { + return Objects.equal(name, ((Package) object).name); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hashCode(name); + } + + @Override + public String toString() { + return String + .format( + "[name=%s, memory=%s, disk=%s, swap=%s, default=%s]", + name, memorySizeMb, + diskSizeGb, swapSizeMb, defaultPackage); + } +} diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Type.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Type.java new file mode 100644 index 0000000000..b02c260a0d --- /dev/null +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Type.java @@ -0,0 +1,29 @@ +package org.jclouds.joyent.sdc.v6_5.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.CaseFormat; + +public enum Type { + VIRTUALMACHINE, SMARTMACHINE, UNRECOGNIZED; + + public static Type fromValue(String type) { + try { + return valueOf(CaseFormat.UPPER_CAMEL + .to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(type, + "type"))); + } catch (IllegalArgumentException e) { + return UNRECOGNIZED; + } + } + + public String value() { + return (CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, + name())); + } + + @Override + public String toString() { + return value(); + } +} diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetAsyncClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetAsyncClient.java new file mode 100644 index 0000000000..a2812b9e58 --- /dev/null +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetAsyncClient.java @@ -0,0 +1,51 @@ +package org.jclouds.joyent.sdc.v6_5.features; + +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.MediaType; + +import org.jclouds.http.filters.BasicAuthentication; +import org.jclouds.joyent.sdc.v6_5.domain.Dataset; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.Headers; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.SkipEncoding; +import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; +import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; + +import com.google.common.util.concurrent.ListenableFuture; + +/** + * Provides asynchronous access to Dataset via their REST API. + *

+ * + * @author Gerald Pereira + * @see DatasetClient + * @see api doc + */ +@SkipEncoding( { '/', '=' }) +@Headers(keys = "X-Api-Version", values = "{jclouds.api-version}") +@RequestFilters(BasicAuthentication.class) +public interface DatasetAsyncClient { + /** + * @see DatasetClient#listMachines + */ + @GET + @Path("/my/datasets") + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) + ListenableFuture> listDatasets(); + + /** + * @see DatasetClient#getMachineDetails + */ + @GET + @Path("/my/datasets/{id}") + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getDataset(@PathParam("id") String id); +} diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClient.java new file mode 100644 index 0000000000..c61de92e23 --- /dev/null +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClient.java @@ -0,0 +1,34 @@ +package org.jclouds.joyent.sdc.v6_5.features; + +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.joyent.sdc.v6_5.domain.Dataset; + +/** + * Provides synchronous access to Datasets. + *

+ * + * @author Gerald Pereira + * @see DatasetAsyncClient + * @see api doc + */ +@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS) +public interface DatasetClient { + + /** + * Provides a list of datasets available in this datacenter. + * @return + */ + Set listDatasets(); + + /** + * Gets an individual dataset by id. + * + * @param id + * the id of the dataset + * @return + */ + Dataset getDataset(String id); +} diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineAsyncClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineAsyncClient.java new file mode 100644 index 0000000000..39431a0439 --- /dev/null +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineAsyncClient.java @@ -0,0 +1,71 @@ +/** + * 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.joyent.sdc.v6_5.features; + +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.MediaType; + +import org.jclouds.http.filters.BasicAuthentication; +import org.jclouds.joyent.sdc.v6_5.domain.Machine; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.Headers; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.SkipEncoding; +import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; +import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; + +import com.google.common.util.concurrent.ListenableFuture; + +/** + * Provides asynchronous access to Machine via their REST API. + *

+ * + * @author Gerald Pereira + * @see MachineClient + * @see api doc + */ +@SkipEncoding( { '/', '=' }) +@Headers(keys = "X-Api-Version", values = "{jclouds.api-version}") +@RequestFilters(BasicAuthentication.class) +public interface MachineAsyncClient { + + /** + * @see MachineClient#listMachines + */ + @GET + @Path("/my/machines") + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) + ListenableFuture> listMachines(); + + /** + * @see MachineClient#getMachineDetails + */ + @GET + @Path("/my/machines/{id}") + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getMachine(@PathParam("id") String id); + +} diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineClient.java new file mode 100644 index 0000000000..94302321a7 --- /dev/null +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineClient.java @@ -0,0 +1,55 @@ +/** + * 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.joyent.sdc.v6_5.features; + +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.joyent.sdc.v6_5.domain.Machine; + + +/** + * Provides synchronous access to Machine. + *

+ * + * @author Gerald Pereira + * @see MachineAsyncClient + * @see api doc + */ +@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS) +public interface MachineClient { + + /** + * Lists all machines we have on record for your account. + * + * @return an account's associated machine objects. + */ + Set listMachines(); + + + /** + * Gets the details for an individual machine. + * + * @param id the id of the machine + * @return + */ + Machine getMachine(String id); + +} \ No newline at end of file diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageAsyncClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageAsyncClient.java new file mode 100644 index 0000000000..b65abbbba9 --- /dev/null +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageAsyncClient.java @@ -0,0 +1,50 @@ +package org.jclouds.joyent.sdc.v6_5.features; + +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.MediaType; + +import org.jclouds.http.filters.BasicAuthentication; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.Headers; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.SkipEncoding; +import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; +import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; + +import com.google.common.util.concurrent.ListenableFuture; + +/** + * Provides asynchronous access to Dataset via their REST API. + *

+ * + * @author Gerald Pereira + * @see PackageClient + * @see api doc + */ +@SkipEncoding( { '/', '=' }) +@Headers(keys = "X-Api-Version", values = "{jclouds.api-version}") +@RequestFilters(BasicAuthentication.class) +public interface PackageAsyncClient { + /** + * @see PackageClient#listPackages + */ + @GET + @Path("/my/packages") + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) + ListenableFuture> listPackages(); + + /** + * @see PackageClient#getPackageDetails + */ + @GET + @Path("/my/packages/{name}") + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getPackage(@PathParam("name") String name); +} diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageClient.java new file mode 100644 index 0000000000..7bfa6bad6a --- /dev/null +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageClient.java @@ -0,0 +1,33 @@ +package org.jclouds.joyent.sdc.v6_5.features; + +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.jclouds.concurrent.Timeout; + +/** + * Provides synchronous access to Packages. + *

+ * + * @author Gerald Pereira + * @see PackageAsyncClient + * @see api doc + */ +@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS) +public interface PackageClient { + + /** + * Provides a list of packages available in this datacenter. + * @return + */ + Set listPackages(); + + /** + * Gets an individual package by id. + * + * @param name + * the name of the package + * @return + */ + org.jclouds.joyent.sdc.v6_5.domain.Package getPackage(String name); +} diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/functions/internal/SDCTypeAdapters.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/functions/internal/SDCTypeAdapters.java new file mode 100644 index 0000000000..ecfe62e8f9 --- /dev/null +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/functions/internal/SDCTypeAdapters.java @@ -0,0 +1,59 @@ +/** + * 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.joyent.sdc.v6_5.functions.internal; + +import java.io.IOException; + +import org.jclouds.joyent.sdc.v6_5.domain.Machine; +import org.jclouds.joyent.sdc.v6_5.domain.Type; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +/** + * @author Adam Lowe + */ +public class SDCTypeAdapters { + + public static class ServerStateAdapter extends TypeAdapter { + @Override + public void write(JsonWriter writer, Machine.State value) throws IOException { + writer.value(value.value()); + } + + @Override + public Machine.State read(JsonReader reader) throws IOException { + return Machine.State.fromValue(reader.nextString()); + } + } + + public static class SDCTypeAdapter extends TypeAdapter { + @Override + public void write(JsonWriter writer, Type value) throws IOException { + writer.value(value.value()); + } + + @Override + public Type read(JsonReader reader) throws IOException { + return Type.fromValue(reader.nextString()); + } + } + +} \ No newline at end of file diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientExpectTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientExpectTest.java new file mode 100644 index 0000000000..6ae2cc9579 --- /dev/null +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientExpectTest.java @@ -0,0 +1,70 @@ +/** + * 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 + * + * Unles 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 expres or implied. See the License for the + * specific language governing permisions and limitations + * under the License. + */ +package org.jclouds.joyent.sdc.v6_5.features; + +import static org.testng.Assert.assertEquals; + +import java.net.URI; + +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.joyent.sdc.v6_5.SDCClient; +import org.jclouds.joyent.sdc.v6_5.internal.BaseSDCClientExpectTest; +import org.jclouds.joyent.sdc.v6_5.parse.ParseDatasetListTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; + +/** + * @author Gerald Pereira + */ +@Test(groups = "unit", testName = "DatasetClientExpectTest") +public class DatasetClientExpectTest extends BaseSDCClientExpectTest { + HttpRequest listDatasets = HttpRequest.builder().method("GET").endpoint( + URI.create("https://api.joyentcloud.com/my/datasets")).headers( + ImmutableMultimap. builder().put("X-Api-Version", + "~6.5").put("Accept", "application/json").put( + "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==") + .build()).build(); + + public void testListDatasetsWhenResponseIs2xx() { + HttpResponse listDatasetsResponse = HttpResponse.builder() + .statusCode(200).payload( + payloadFromResource("/dataset_list.json")).build(); + + SDCClient clientWhenDatasetsExists = requestSendsResponse(listDatasets, + listDatasetsResponse); + + assertEquals( + clientWhenDatasetsExists.getDatasetClient().listDatasets().toString(), + new ParseDatasetListTest().expected().toString()); + } + + public void testListDatasetsWhenResponseIs404() { + HttpResponse listDatasetsResponse = HttpResponse.builder().statusCode( + 404).build(); + + SDCClient listDatasetsWhenNone = requestSendsResponse(listDatasets, + listDatasetsResponse); + + assertEquals(listDatasetsWhenNone.getDatasetClient().listDatasets(), + ImmutableSet.of()); + } +} diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientLiveTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientLiveTest.java new file mode 100644 index 0000000000..0dec63cdfa --- /dev/null +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientLiveTest.java @@ -0,0 +1,50 @@ +/** + * 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.joyent.sdc.v6_5.features; + +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.util.Set; + +import org.jclouds.joyent.sdc.v6_5.domain.Dataset; +import org.jclouds.joyent.sdc.v6_5.internal.BaseSDCClientLiveTest; +import org.testng.annotations.Test; + +/** + * @author Gerald Pereira + */ +@Test(groups = "live", testName = "DatasetClientLiveTest") +public class DatasetClientLiveTest extends BaseSDCClientLiveTest { + + public void testListDatasets() { + Set datasets = sdcContext.getApi().getDatasetClient() + .listDatasets(); + assertNotNull(datasets); + assertTrue(datasets.size() > 0); + } + + public void testGetDataset() { + final String id = "e4cd7b9e-4330-11e1-81cf-3bb50a972bda"; + Dataset dataset = sdcContext.getApi().getDatasetClient().getDataset(id); + assertNotNull(dataset); + assertEquals(dataset.getId(), id); + } +} diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientExpectTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientExpectTest.java new file mode 100644 index 0000000000..b2c75a80e6 --- /dev/null +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientExpectTest.java @@ -0,0 +1,70 @@ +/** + * 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 + * + * Unles 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 expres or implied. See the License for the + * specific language governing permisions and limitations + * under the License. + */ +package org.jclouds.joyent.sdc.v6_5.features; + +import static org.testng.Assert.assertEquals; + +import java.net.URI; + +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.joyent.sdc.v6_5.SDCClient; +import org.jclouds.joyent.sdc.v6_5.internal.BaseSDCClientExpectTest; +import org.jclouds.joyent.sdc.v6_5.parse.ParseMachineListTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; + +/** + * @author Gerald Pereira + */ +@Test(groups = "unit", testName = "MachineClientExpectTest") +public class MachineClientExpectTest extends BaseSDCClientExpectTest { + HttpRequest listMachines = HttpRequest.builder().method("GET").endpoint( + URI.create("https://api.joyentcloud.com/my/machines")).headers( + ImmutableMultimap. builder().put("X-Api-Version", + "~6.5").put("Accept", "application/json").put( + "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==") + .build()).build(); + + public void testListMachinesWhenResponseIs2xx() { + HttpResponse listMachinesResponse = HttpResponse.builder() + .statusCode(200).payload( + payloadFromResource("/machine_list.json")).build(); + + SDCClient clientWhenMachinesExists = requestSendsResponse(listMachines, + listMachinesResponse); + + assertEquals( + clientWhenMachinesExists.getMachineClient().listMachines(), + new ParseMachineListTest().expected()); + } + + public void testListMachinesWhenResponseIs404() { + HttpResponse listMachinesResponse = HttpResponse.builder().statusCode( + 404).build(); + + SDCClient listMachinesWhenNone = requestSendsResponse(listMachines, + listMachinesResponse); + + assertEquals(listMachinesWhenNone.getMachineClient().listMachines(), + ImmutableSet.of()); + } +} diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientLiveTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientLiveTest.java new file mode 100644 index 0000000000..fa213abdcd --- /dev/null +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientLiveTest.java @@ -0,0 +1,50 @@ +/** + * 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.joyent.sdc.v6_5.features; + +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.Set; + +import org.jclouds.joyent.sdc.v6_5.domain.Machine; +import org.jclouds.joyent.sdc.v6_5.internal.BaseSDCClientLiveTest; +import org.testng.annotations.Test; + +/** + * @author Gerald Pereira + */ +@Test(groups = "live", testName = "MachineClientLiveTest") +public class MachineClientLiveTest extends BaseSDCClientLiveTest { + + public void testListMachines() { + Set machines = sdcContext.getApi().getMachineClient() + .listMachines(); + assertNotNull(machines); + assertTrue(machines.size() > 0); + } + + public void testGetMachine() { + final String id = "d73cb0b0-7d1f-44ef-8c40-e040eef0f726"; + Machine machine = sdcContext.getApi().getMachineClient().getMachine(id); + assertNotNull(machine); + assertEquals(machine.getId(), id); + } +} diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientExpectTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientExpectTest.java new file mode 100644 index 0000000000..d572c8ecdc --- /dev/null +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientExpectTest.java @@ -0,0 +1,70 @@ +/** + * 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 + * + * Unles 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 expres or implied. See the License for the + * specific language governing permisions and limitations + * under the License. + */ +package org.jclouds.joyent.sdc.v6_5.features; + +import static org.testng.Assert.assertEquals; + +import java.net.URI; + +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.joyent.sdc.v6_5.SDCClient; +import org.jclouds.joyent.sdc.v6_5.internal.BaseSDCClientExpectTest; +import org.jclouds.joyent.sdc.v6_5.parse.ParsePackageListTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; + +/** + * @author Gerald Pereira + */ +@Test(groups = "unit", testName = "PackageClientExpectTest") +public class PackageClientExpectTest extends BaseSDCClientExpectTest { + HttpRequest listPackages = HttpRequest.builder().method("GET").endpoint( + URI.create("https://api.joyentcloud.com/my/packages")).headers( + ImmutableMultimap. builder().put("X-Api-Version", + "~6.5").put("Accept", "application/json").put( + "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==") + .build()).build(); + + public void testListPackagesWhenResponseIs2xx() { + HttpResponse listPackagesResponse = HttpResponse.builder() + .statusCode(200).payload( + payloadFromResource("/package_list.json")).build(); + + SDCClient clientWhenPackagesExists = requestSendsResponse(listPackages, + listPackagesResponse); + + assertEquals( + clientWhenPackagesExists.getPackageClient().listPackages(), + new ParsePackageListTest().expected()); + } + + public void testListPackagesWhenResponseIs404() { + HttpResponse listPackagesResponse = HttpResponse.builder().statusCode( + 404).build(); + + SDCClient listPackagesWhenNone = requestSendsResponse(listPackages, + listPackagesResponse); + + assertEquals(listPackagesWhenNone.getPackageClient().listPackages(), + ImmutableSet.of()); + } +} diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientLiveTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientLiveTest.java new file mode 100644 index 0000000000..64ee954654 --- /dev/null +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientLiveTest.java @@ -0,0 +1,49 @@ +/** + * 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.joyent.sdc.v6_5.features; + +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.util.Set; + +import org.jclouds.joyent.sdc.v6_5.internal.BaseSDCClientLiveTest; +import org.testng.annotations.Test; + +/** + * @author Gerald Pereira + */ +@Test(groups = "live", testName = "PackageClientLiveTest") +public class PackageClientLiveTest extends BaseSDCClientLiveTest { + + public void testListPackages() { + Set packages = sdcContext.getApi().getPackageClient() + .listPackages(); + assertNotNull(packages); + assertTrue(packages.size() > 0); + } + + public void testGetPackage() { + final String name = "Small 1GB"; + org.jclouds.joyent.sdc.v6_5.domain.Package packageSDC = sdcContext.getApi().getPackageClient().getPackage(name); + assertNotNull(packageSDC); + assertEquals(packageSDC.getName(), name); + } +} diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseDatasetListTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseDatasetListTest.java new file mode 100644 index 0000000000..2ff9e620d3 --- /dev/null +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseDatasetListTest.java @@ -0,0 +1,90 @@ +/** + * 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.joyent.sdc.v6_5.parse; + +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.joyent.sdc.v6_5.config.SDCParserModule; +import org.jclouds.joyent.sdc.v6_5.domain.Dataset; +import org.jclouds.joyent.sdc.v6_5.domain.Type; +import org.jclouds.json.BaseSetParserTest; +import org.jclouds.json.config.GsonModule; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * @author Gerald Pereira + */ +@Test(groups = "unit", testName = "ParseDatasetListTest") +public class ParseDatasetListTest extends BaseSetParserTest { + + @Override + public String resource() { + return "/dataset_list.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Set expected() { + return ImmutableSet + .of( + Dataset + .builder() + .id("e4cd7b9e-4330-11e1-81cf-3bb50a972bda") + .name("centos-6") + .urn("sdc:sdc:centos-6:1.0.1") + .type(Type.VIRTUALMACHINE) + .version("1.0.1") + .defaultDataset(false) + .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-02-13T06:30:33+00:00")) + .build(), + + Dataset + .builder() + .id("e62c30b4-cdda-11e0-9dd4-af4d032032e3") + .name("nodejs") + .urn("sdc:sdc:nodejs:1.2.3") + .type(Type.SMARTMACHINE) + .version("1.2.3") + .defaultDataset(false) + .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-09-15T08:15:29+00:00")) + .build() + + ); + } + + protected Injector injector() { + return Guice.createInjector(new SDCParserModule(), new GsonModule() { + + @Override + protected void configure() { + bind(DateAdapter.class).to(Iso8601DateAdapter.class); + super.configure(); + } + + }); + } +} diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseDatasetTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseDatasetTest.java new file mode 100644 index 0000000000..d6bfef14de --- /dev/null +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseDatasetTest.java @@ -0,0 +1,73 @@ +/** + * 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.joyent.sdc.v6_5.parse; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.joyent.sdc.v6_5.config.SDCParserModule; +import org.jclouds.joyent.sdc.v6_5.domain.Dataset; +import org.jclouds.joyent.sdc.v6_5.domain.Type; +import org.jclouds.json.BaseItemParserTest; +import org.jclouds.json.config.GsonModule; +import org.testng.annotations.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * @author Gerald Pereira + */ +@Test(groups = "unit", testName = "ParseDatasetTest") +public class ParseDatasetTest extends BaseItemParserTest { + + @Override + public String resource() { + return "/dataset.json"; + } + + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Dataset expected() { + return Dataset + .builder() + .id("e4cd7b9e-4330-11e1-81cf-3bb50a972bda") + .name("centos-6") + .urn("sdc:sdc:centos-6:1.0.1") + .type(Type.VIRTUALMACHINE) + .version("1.0.1") + .defaultDataset(false) + .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-02-13T06:30:33+00:00")) + .build(); + } + + protected Injector injector() { + return Guice.createInjector(new SDCParserModule(), new GsonModule() { + + @Override + protected void configure() { + bind(DateAdapter.class).to(Iso8601DateAdapter.class); + super.configure(); + } + + }); + } +} diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineListTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineListTest.java new file mode 100644 index 0000000000..2a9e0428d1 --- /dev/null +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineListTest.java @@ -0,0 +1,120 @@ +/** + * 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.joyent.sdc.v6_5.parse; + +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.joyent.sdc.v6_5.config.SDCParserModule; +import org.jclouds.joyent.sdc.v6_5.domain.Machine; +import org.jclouds.joyent.sdc.v6_5.domain.Type; +import org.jclouds.json.BaseSetParserTest; +import org.jclouds.json.config.GsonModule; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * @author Gerald Pereira + */ +@Test(groups = "unit", testName = "ParseMachineListTest") +public class ParseMachineListTest extends BaseSetParserTest { + + @Override + public String resource() { + return "/machine_list.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Set expected() { + return ImmutableSet + .of( + Machine + .builder() + .id("94eba336-ecb7-49f5-8a27-52f5e4dd57a1") + .name("testJClouds") + .type(Type.VIRTUALMACHINE) + .state(Machine.State.RUNNING) + .dataset("sdc:sdc:centos-5.7:1.2.1") + .ips( + ImmutableSet. builder().add( + "37.153.96.62").add( + "10.224.0.63").build()) + .memorySizeMb(1024) + .diskSizeGb(61440) + .metadata( + ImmutableMap + . builder() + .put("root_authorized_keys", + "ssh-rsa XXXXXX== test@xxxx.ovh.net\n") + .build()) + .created( + new SimpleDateFormatDateService() + .iso8601SecondsDateParse("2012-05-09T13:32:46+00:00")) + .updated( + new SimpleDateFormatDateService() + .iso8601SecondsDateParse("2012-05-11T09:00:33+00:00")) + .build(), + + Machine + .builder() + .id("d73cb0b0-7d1f-44ef-8c40-e040eef0f726") + .name("testJClouds2") + .type(Type.SMARTMACHINE) + .state(Machine.State.RUNNING) + .dataset("sdc:sdc:smartosplus:3.1.0") + .ips( + ImmutableSet. builder().add( + "37.153.96.56").add( + "10.224.0.57").build()) + .memorySizeMb(1024) + .diskSizeGb(61440) + .metadata( + ImmutableMap + . of()) + .created( + new SimpleDateFormatDateService() + .iso8601SecondsDateParse("2012-05-09T13:39:43+00:00")) + .updated( + new SimpleDateFormatDateService() + .iso8601SecondsDateParse("2012-05-09T13:43:45+00:00")) + .build() + + ); + } + + protected Injector injector() { + return Guice.createInjector(new SDCParserModule(), new GsonModule() { + + @Override + protected void configure() { + bind(DateAdapter.class).to(Iso8601DateAdapter.class); + super.configure(); + } + + }); + } +} diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineTest.java new file mode 100644 index 0000000000..170f9912d2 --- /dev/null +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineTest.java @@ -0,0 +1,78 @@ +/** + * 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.joyent.sdc.v6_5.parse; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.joyent.sdc.v6_5.config.SDCParserModule; +import org.jclouds.joyent.sdc.v6_5.domain.Machine; +import org.jclouds.joyent.sdc.v6_5.domain.Type; +import org.jclouds.json.BaseItemParserTest; +import org.jclouds.json.config.GsonModule; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * @author Gerald Pereira + */ +@Test(groups = "unit", testName = "ParseMachineTest") +public class ParseMachineTest extends BaseItemParserTest { + + @Override + public String resource() { + return "/machine.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Machine expected() { + return Machine + .builder() + .id("94eba336-ecb7-49f5-8a27-52f5e4dd57a1") + .name("testJClouds") + .type(Type.VIRTUALMACHINE) + .state(Machine.State.STOPPED) + .dataset("sdc:sdc:centos-5.7:1.2.1") + .ips(ImmutableSet. builder().add("37.153.96.62").add("10.224.0.63").build()) + .memorySizeMb(1024) + .diskSizeGb(61440) + .metadata(ImmutableMap. builder().put("root_authorized_keys","ssh-rsa XXXXXX== test@xxxx.ovh.net\n").build()) + .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-09T13:32:46+00:00")) + .updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-11T08:44:53+00:00")) + .build(); + } + + protected Injector injector() { + return Guice.createInjector(new SDCParserModule(), new GsonModule() { + + @Override + protected void configure() { + bind(DateAdapter.class).to(Iso8601DateAdapter.class); + super.configure(); + } + + }); + } +} diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParsePackageListTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParsePackageListTest.java new file mode 100644 index 0000000000..9dbda6f042 --- /dev/null +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParsePackageListTest.java @@ -0,0 +1,84 @@ +/** + * 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.joyent.sdc.v6_5.parse; + +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.joyent.sdc.v6_5.config.SDCParserModule; +import org.jclouds.joyent.sdc.v6_5.domain.Package; +import org.jclouds.json.BaseSetParserTest; +import org.jclouds.json.config.GsonModule; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * @author Gerald Pereira + */ +@Test(groups = "unit", testName = "ParsePackageListTest") +public class ParsePackageListTest extends BaseSetParserTest { + + @Override + public String resource() { + return "/package_list.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Set expected() { + return ImmutableSet + .of( + org.jclouds.joyent.sdc.v6_5.domain.Package + .builder() + .name("Small 1GB") + .memorySizeMb(1024) + .diskSizeGb(30720) + .swapSizeMb(2048) + .isDefault(true) + .build(), + + org.jclouds.joyent.sdc.v6_5.domain.Package + .builder() + .name("Medium 2GB") + .memorySizeMb(2048) + .diskSizeGb(61440) + .swapSizeMb(4096) + .isDefault(false) + .build() + + ); + } + + protected Injector injector() { + return Guice.createInjector(new SDCParserModule(), new GsonModule() { + + @Override + protected void configure() { + bind(DateAdapter.class).to(Iso8601DateAdapter.class); + super.configure(); + } + + }); + } +} diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParsePackageTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParsePackageTest.java new file mode 100644 index 0000000000..000b89939d --- /dev/null +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParsePackageTest.java @@ -0,0 +1,68 @@ +/** + * 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.joyent.sdc.v6_5.parse; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.joyent.sdc.v6_5.config.SDCParserModule; +import org.jclouds.json.BaseItemParserTest; +import org.jclouds.json.config.GsonModule; +import org.testng.annotations.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * @author Gerald Pereira + */ +@Test(groups = "unit", testName = "ParsePackageTest") +public class ParsePackageTest extends BaseItemParserTest { + + @Override + public String resource() { + return "/package.json"; + } + + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public org.jclouds.joyent.sdc.v6_5.domain.Package expected() { + return org.jclouds.joyent.sdc.v6_5.domain.Package + .builder() + .name("Small 1GB") + .memorySizeMb(1024) + .diskSizeGb(30720) + .swapSizeMb(2048) + .isDefault(true) + .build(); + } + + protected Injector injector() { + return Guice.createInjector(new SDCParserModule(), new GsonModule() { + + @Override + protected void configure() { + bind(DateAdapter.class).to(Iso8601DateAdapter.class); + super.configure(); + } + + }); + } +} diff --git a/labs/joyent-sdc/src/test/resources/dataset.json b/labs/joyent-sdc/src/test/resources/dataset.json new file mode 100644 index 0000000000..e363f8f44d --- /dev/null +++ b/labs/joyent-sdc/src/test/resources/dataset.json @@ -0,0 +1 @@ +{"id":"e4cd7b9e-4330-11e1-81cf-3bb50a972bda","urn":"sdc:sdc:centos-6:1.0.1","name":"centos-6","os":"linux","type":"virtualmachine","description":"Centos 6 VM 1.0.1","default":false,"requirements":{},"version":"1.0.1","created":"2012-02-13T06:30:33+00:00"} \ No newline at end of file diff --git a/labs/joyent-sdc/src/test/resources/dataset_list.json b/labs/joyent-sdc/src/test/resources/dataset_list.json new file mode 100644 index 0000000000..3988363ef7 --- /dev/null +++ b/labs/joyent-sdc/src/test/resources/dataset_list.json @@ -0,0 +1 @@ +[{"id":"e4cd7b9e-4330-11e1-81cf-3bb50a972bda","urn":"sdc:sdc:centos-6:1.0.1","name":"centos-6","os":"linux","type":"virtualmachine","description":"Centos 6 VM 1.0.1","default":false,"requirements":{},"version":"1.0.1","created":"2012-02-13T06:30:33+00:00"},{"id":"e62c30b4-cdda-11e0-9dd4-af4d032032e3","urn":"sdc:sdc:nodejs:1.2.3","name":"nodejs","os":"smartos","type":"smartmachine","description":"Node.js git-deploy PaaS dataset","default":false,"requirements":{},"version":"1.2.3","created":"2011-09-15T08:15:29+00:00"}] \ No newline at end of file diff --git a/labs/joyent-sdc/src/test/resources/machine.json b/labs/joyent-sdc/src/test/resources/machine.json new file mode 100644 index 0000000000..520e72c7e3 --- /dev/null +++ b/labs/joyent-sdc/src/test/resources/machine.json @@ -0,0 +1 @@ +{"id":"94eba336-ecb7-49f5-8a27-52f5e4dd57a1","name":"testJClouds","type":"virtualmachine","state":"stopped","dataset":"sdc:sdc:centos-5.7:1.2.1","ips":["37.153.96.62","10.224.0.63"],"memory":1024,"disk":61440,"metadata":{"root_authorized_keys":"ssh-rsa XXXXXX== test@xxxx.ovh.net"},"created":"2012-05-09T13:32:46+00:00","updated":"2012-05-11T08:44:53+00:00"} \ No newline at end of file diff --git a/labs/joyent-sdc/src/test/resources/machine_list.json b/labs/joyent-sdc/src/test/resources/machine_list.json new file mode 100644 index 0000000000..29ca4ca7a9 --- /dev/null +++ b/labs/joyent-sdc/src/test/resources/machine_list.json @@ -0,0 +1 @@ +[{"id":"d73cb0b0-7d1f-44ef-8c40-e040eef0f726","name":"testJClouds2","type":"smartmachine","state":"running","dataset":"sdc:sdc:smartosplus:3.1.0","ips":["37.153.96.56","10.224.0.57"],"memory":1024,"disk":61440,"metadata":{},"created":"2012-05-09T13:39:43+00:00","updated":"2012-05-09T13:43:45+00:00"},{"id":"94eba336-ecb7-49f5-8a27-52f5e4dd57a1","name":"testJClouds","type":"virtualmachine","state":"running","dataset":"sdc:sdc:centos-5.7:1.2.1","ips":["37.153.96.62","10.224.0.63"],"memory":1024,"disk":61440,"metadata":{"root_authorized_keys":"ssh-rsa XXXXXX== test@xxxx.ovh.net\n"},"created":"2012-05-09T13:32:46+00:00","updated":"2012-05-11T09:00:33+00:00"}] \ No newline at end of file diff --git a/labs/joyent-sdc/src/test/resources/package.json b/labs/joyent-sdc/src/test/resources/package.json new file mode 100644 index 0000000000..757a6a663d --- /dev/null +++ b/labs/joyent-sdc/src/test/resources/package.json @@ -0,0 +1 @@ +{"name":"Small 1GB","memory":1024,"disk":30720,"vcpus":1,"swap":2048,"default":true} \ No newline at end of file diff --git a/labs/joyent-sdc/src/test/resources/package_list.json b/labs/joyent-sdc/src/test/resources/package_list.json new file mode 100644 index 0000000000..d83ebc9022 --- /dev/null +++ b/labs/joyent-sdc/src/test/resources/package_list.json @@ -0,0 +1 @@ +[{"name":"Small 1GB","memory":1024,"disk":30720,"vcpus":1,"swap":2048,"default":true},{"name":"Medium 2GB","memory":2048,"disk":61440,"vcpus":1,"swap":4096,"default":false}] \ No newline at end of file From c63f3119ca43380313cf3a0b9485cca1ed80cd82 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Tue, 15 May 2012 14:43:29 -0700 Subject: [PATCH 104/148] formatting and imports --- .../joyent/sdc/v6_5/SDCApiMetadata.java | 17 +- .../joyent/sdc/v6_5/SDCAsyncClient.java | 6 +- .../jclouds/joyent/sdc/v6_5/SDCClient.java | 4 +- .../sdc/v6_5/config/SDCParserModule.java | 4 +- .../joyent/sdc/v6_5/config/SDCProperties.java | 2 +- .../sdc/v6_5/config/SDCRestClientModule.java | 7 +- .../joyent/sdc/v6_5/domain/Dataset.java | 240 ++++++----- .../joyent/sdc/v6_5/domain/Machine.java | 378 +++++++++--------- .../joyent/sdc/v6_5/domain/Package.java | 203 +++++----- .../jclouds/joyent/sdc/v6_5/domain/Type.java | 33 +- .../v6_5/features/DatacenterAsyncClient.java | 6 +- .../sdc/v6_5/features/DatasetAsyncClient.java | 34 +- .../sdc/v6_5/features/DatasetClient.java | 27 +- .../sdc/v6_5/features/MachineAsyncClient.java | 34 +- .../sdc/v6_5/features/MachineClient.java | 5 +- .../sdc/v6_5/features/PackageAsyncClient.java | 34 +- .../sdc/v6_5/features/PackageClient.java | 27 +- .../sdc/v6_5/handlers/SDCErrorHandler.java | 26 +- .../features/DatacenterClientExpectTest.java | 35 +- .../features/DatacenterClientLiveTest.java | 2 +- .../features/DatasetClientExpectTest.java | 44 +- .../v6_5/features/DatasetClientLiveTest.java | 25 +- .../features/MachineClientExpectTest.java | 43 +- .../v6_5/features/MachineClientLiveTest.java | 25 +- .../features/PackageClientExpectTest.java | 43 +- .../v6_5/features/PackageClientLiveTest.java | 25 +- .../v6_5/handlers/SDCErrorHandlerTest.java | 8 +- .../v6_5/internal/BaseSDCClientLiveTest.java | 2 +- .../sdc/v6_5/internal/BaseSDCExpectTest.java | 2 +- .../sdc/v6_5/parse/ParseDatasetListTest.java | 69 ++-- .../sdc/v6_5/parse/ParseDatasetTest.java | 26 +- .../sdc/v6_5/parse/ParseMachineListTest.java | 113 ++---- .../sdc/v6_5/parse/ParseMachineTest.java | 21 +- .../sdc/v6_5/parse/ParsePackageListTest.java | 60 ++- .../sdc/v6_5/parse/ParsePackageTest.java | 23 +- 35 files changed, 766 insertions(+), 887 deletions(-) diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCApiMetadata.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCApiMetadata.java index c3042a9dfe..3182b60ff0 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCApiMetadata.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCApiMetadata.java @@ -36,7 +36,7 @@ import com.google.inject.Module; * @author Adrian Cole */ public class SDCApiMetadata extends BaseRestApiMetadata { - + /** The serialVersionUID */ private static final long serialVersionUID = 6725672099385580694L; @@ -66,17 +66,12 @@ public class SDCApiMetadata extends BaseRestApiMetadata { protected Builder() { super(SDCClient.class, SDCAsyncClient.class); - id("joyent-sdc") - .name("Joyent SDC API") - .identityName("username") - .credentialName("password") - .documentation(URI.create("http://sdc.joyent.org/sdcapi.html")) - .version("~6.5") - .defaultEndpoint("https://api.joyentcloud.com") - .defaultProperties(SDCApiMetadata.defaultProperties()) - .defaultModules(ImmutableSet.>of(SDCRestClientModule.class)); + id("joyent-sdc").name("Joyent SDC API").identityName("username").credentialName("password") + .documentation(URI.create("http://sdc.joyent.org/sdcapi.html")).version("~6.5") + .defaultEndpoint("https://api.joyentcloud.com").defaultProperties(SDCApiMetadata.defaultProperties()) + .defaultModules(ImmutableSet.> of(SDCRestClientModule.class)); } - + @Override public SDCApiMetadata build() { return new SDCApiMetadata(this); diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCAsyncClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCAsyncClient.java index e96f21689e..7a15079982 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCAsyncClient.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCAsyncClient.java @@ -33,13 +33,13 @@ import org.jclouds.rest.annotations.Delegate; * @author Adrian Cole */ public interface SDCAsyncClient { - + /** * Provides asynchronous access to Datacenter features. */ @Delegate DatacenterAsyncClient getDatacenterClient(); - + /** * Provides asynchronous access to Machine features. */ @@ -51,7 +51,7 @@ public interface SDCAsyncClient { */ @Delegate DatasetAsyncClient getDatasetClient(); - + /** * Provides asynchronous access to Package features. */ diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCClient.java index 3f30caaaac..e0488fd51c 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCClient.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCClient.java @@ -43,7 +43,7 @@ public interface SDCClient { */ @Delegate DatacenterClient getDatacenterClient(); - + /** * Provides synchronous access to Machine features. */ @@ -55,7 +55,7 @@ public interface SDCClient { */ @Delegate DatasetClient getDatasetClient(); - + /** * Provides synchronous access to Package features. */ diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCParserModule.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCParserModule.java index 534fc94646..cc2e3f180c 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCParserModule.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCParserModule.java @@ -38,8 +38,8 @@ public class SDCParserModule extends AbstractModule { @Provides @Singleton public Map provideCustomAdapterBindings() { - return ImmutableMap. of(Machine.State.class, new SDCTypeAdapters.ServerStateAdapter(), - Type.class, new SDCTypeAdapters.SDCTypeAdapter()); + return ImmutableMap. of(Machine.State.class, new SDCTypeAdapters.ServerStateAdapter(), Type.class, + new SDCTypeAdapters.SDCTypeAdapter()); } @Override diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCProperties.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCProperties.java index dd0190e258..71eaaea09a 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCProperties.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCProperties.java @@ -20,7 +20,7 @@ package org.jclouds.joyent.sdc.v6_5.config; /** * Configuration properties and constants used in joyent SDC connections. - * + * * @author Adrian Cole */ public class SDCProperties { diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCRestClientModule.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCRestClientModule.java index e102cc00d4..922dac64c5 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCRestClientModule.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCRestClientModule.java @@ -50,11 +50,8 @@ import com.google.common.collect.ImmutableMap; @ConfiguresRestClient public class SDCRestClientModule extends RestClientModule { public static final Map, Class> DELEGATE_MAP = ImmutableMap., Class> builder() - .put(DatacenterClient.class, DatacenterAsyncClient.class) - .put(MachineClient.class, MachineAsyncClient.class) - .put(DatasetClient.class, DatasetAsyncClient.class) - .put(PackageClient.class, PackageAsyncClient.class) - .build(); + .put(DatacenterClient.class, DatacenterAsyncClient.class).put(MachineClient.class, MachineAsyncClient.class) + .put(DatasetClient.class, DatasetAsyncClient.class).put(PackageClient.class, PackageAsyncClient.class).build(); public SDCRestClientModule() { super(DELEGATE_MAP); diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Dataset.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Dataset.java index 3cd8eec1b9..72d7b0858d 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Dataset.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Dataset.java @@ -13,150 +13,144 @@ import com.google.gson.annotations.SerializedName; */ public class Dataset implements Comparable { - public static Builder builder() { - return new Builder(); - } + public static Builder builder() { + return new Builder(); + } - public static class Builder { - private String id; - private String name; - private Type type; - private String version; - private String urn; - private boolean defaultDataset; - private Date created; + public static class Builder { + private String id; + private String name; + private Type type; + private String version; + private String urn; + private boolean isDefault; + private Date created; - public Builder id(String id) { - this.id = id; - return this; - } + public Builder id(String id) { + this.id = id; + return this; + } - public Builder name(String name) { - this.name = name; - return this; - } + public Builder name(String name) { + this.name = name; + return this; + } - public Builder type(Type type) { - this.type = type; - return this; - } + public Builder type(Type type) { + this.type = type; + return this; + } - public Builder version(String version) { - this.version = version; - return this; - } + public Builder version(String version) { + this.version = version; + return this; + } - public Builder urn(String urn) { - this.urn = urn; - return this; - } + public Builder urn(String urn) { + this.urn = urn; + return this; + } - public Builder defaultDataset(boolean defaultDataset) { - this.defaultDataset = defaultDataset; - return this; - } + public Builder isDefault(boolean isDefault) { + this.isDefault = isDefault; + return this; + } - public Builder created(Date created) { - this.created = created; - return this; - } + public Builder created(Date created) { + this.created = created; + return this; + } - public Dataset build() { - return new Dataset(id, name, type, version, urn, defaultDataset, created); - } + public Dataset build() { + return new Dataset(id, name, type, version, urn, isDefault, created); + } - public Builder fromDataset(Dataset in) { - return id(in.getId()).name(in.getName()).type(in.getType()).version( - in.getVersion()).urn(in.getUrn()).defaultDataset( - in.isDefaultDataset()).created( - in.getCreated()); - } - } + public Builder fromDataset(Dataset in) { + return id(in.getId()).name(in.getName()).type(in.getType()).version(in.getVersion()).urn(in.getUrn()) + .isDefault(in.isDefault()).created(in.getCreated()); + } + } - // The globally unique id for this dataset - protected final String id; - // The "friendly" name for this dataset - protected final String name; - // Whether this is a smartmachine or virtualmachine - protected final Type type; - // The version for this dataset - protected final String version; - // The full URN for this dataset - protected final String urn; - // Whether this is the default dataset in this datacenter - @SerializedName("default") - protected final boolean defaultDataset; - // Date (ISO8601) When this dataset was created - protected final Date created; + // The globally unique id for this dataset + protected final String id; + // The "friendly" name for this dataset + protected final String name; + // Whether this is a smartmachine or virtualmachine + protected final Type type; + // The version for this dataset + protected final String version; + // The full URN for this dataset + protected final String urn; + // Whether this is the default dataset in this datacenter + @SerializedName("default") + protected final boolean isDefault; + // Date (ISO8601) When this dataset was created + protected final Date created; - public Dataset(String id, String name, Type type, String version, - String urn, boolean defaultDataset, Date created) { - super(); - this.id = id; - this.name = name; - this.type = type; - this.version = version; - this.urn = urn; - this.defaultDataset = defaultDataset; - this.created = created; - } + public Dataset(String id, String name, Type type, String version, String urn, boolean isDefault, Date created) { + super(); + this.id = id; + this.name = name; + this.type = type; + this.version = version; + this.urn = urn; + this.isDefault = isDefault; + this.created = created; + } - public String getId() { - return id; - } + public String getId() { + return id; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public Type getType() { - return type; - } + public Type getType() { + return type; + } - public String getVersion() { - return version; - } + public String getVersion() { + return version; + } - public String getUrn() { - return urn; - } + public String getUrn() { + return urn; + } - public boolean isDefaultDataset() { - return defaultDataset; - } + public boolean isDefault() { + return isDefault; + } - public Date getCreated() { - return created; - } + public Date getCreated() { + return created; + } - @Override - public int compareTo(Dataset other) { - return id.compareTo(other.getId()); - } + @Override + public int compareTo(Dataset other) { + return id.compareTo(other.getId()); + } - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } - if (object instanceof Machine) { - return Objects.equal(id, ((Machine) object).id); - } else { - return false; - } - } + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object instanceof Machine) { + return Objects.equal(id, ((Machine) object).id); + } else { + return false; + } + } - @Override - public int hashCode() { - return Objects.hashCode(id); - } + @Override + public int hashCode() { + return Objects.hashCode(id); + } - @Override - public String toString() { - return String - .format( - "[id=%s, name=%s, type=%s, version=%s, urn=%s, default=%s, created=%s]", - id, name, type.name(), type.name(), version, urn, - defaultDataset, created); - } + @Override + public String toString() { + return String.format("[id=%s, name=%s, type=%s, version=%s, urn=%s, default=%s, created=%s]", id, name, + type.name(), type.name(), version, urn, isDefault, created); + } } diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Machine.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Machine.java index 11fd3491c2..7a4df0cf64 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Machine.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Machine.java @@ -38,234 +38,224 @@ import com.google.gson.annotations.SerializedName; */ public class Machine implements Comparable { - public static enum State { - PUBLISHING, RUNNING, STOPPED, UNRECOGNIZED; + public static enum State { + PUBLISHING, RUNNING, STOPPED, UNRECOGNIZED; - public static State fromValue(String state) { - try { - return valueOf(CaseFormat.UPPER_CAMEL.to( - CaseFormat.UPPER_UNDERSCORE, checkNotNull(state, - "state"))); - } catch (IllegalArgumentException e) { - return UNRECOGNIZED; - } - } + public static State fromValue(String state) { + try { + return valueOf(CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(state, "state"))); + } catch (IllegalArgumentException e) { + return UNRECOGNIZED; + } + } - public String value() { - return (CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, - name())); - } + public String value() { + return (CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name())); + } - @Override - public String toString() { - return value(); - } - } + @Override + public String toString() { + return value(); + } + } - public static Builder builder() { - return new Builder(); - } + public static Builder builder() { + return new Builder(); + } - public static class Builder { - private String id; - private String name; - private Type type; - private State state; - private String dataset; - private int memorySizeMb; - private int diskSizeGb; - private Set ips; - private Date created; - private Date updated; - private Map metadata = ImmutableMap.of(); + public static class Builder { + private String id; + private String name; + private Type type; + private State state; + private String dataset; + private int memorySizeMb; + private int diskSizeGb; + private Set ips; + private Date created; + private Date updated; + private Map metadata = ImmutableMap.of(); - public Builder id(String id) { - this.id = id; - return this; - } + public Builder id(String id) { + this.id = id; + return this; + } - public Builder name(String name) { - this.name = name; - return this; - } + public Builder name(String name) { + this.name = name; + return this; + } - public Builder type(Type type) { - this.type = type; - return this; - } + public Builder type(Type type) { + this.type = type; + return this; + } - public Builder state(State state) { - this.state = state; - return this; - } + public Builder state(State state) { + this.state = state; + return this; + } - public Builder dataset(String dataset) { - this.dataset = dataset; - return this; - } + public Builder dataset(String dataset) { + this.dataset = dataset; + return this; + } - public Builder memorySizeMb(int memorySizeMb) { - this.memorySizeMb = memorySizeMb; - return this; - } + public Builder memorySizeMb(int memorySizeMb) { + this.memorySizeMb = memorySizeMb; + return this; + } - public Builder diskSizeGb(int diskSizeGb) { - this.diskSizeGb = diskSizeGb; - return this; - } + public Builder diskSizeGb(int diskSizeGb) { + this.diskSizeGb = diskSizeGb; + return this; + } - public Builder ips(Set ips) { - this.ips = ips; - return this; - } + public Builder ips(Set ips) { + this.ips = ips; + return this; + } - public Builder created(Date created) { - this.created = created; - return this; - } + public Builder created(Date created) { + this.created = created; + return this; + } - public Builder updated(Date updated) { - this.updated = updated; - return this; - } + public Builder updated(Date updated) { + this.updated = updated; + return this; + } - /** - * @see Machine#getMetadata() - */ - public Builder metadata(Map metadata) { - this.metadata = metadata; - return this; - } + /** + * @see Machine#getMetadata() + */ + public Builder metadata(Map metadata) { + this.metadata = metadata; + return this; + } - public Machine build() { - return new Machine(id, name, type, state, dataset, memorySizeMb, - diskSizeGb, ips, created, updated, metadata); - } + public Machine build() { + return new Machine(id, name, type, state, dataset, memorySizeMb, diskSizeGb, ips, created, updated, metadata); + } - public Builder fromMachine(Machine in) { - return id(in.getId()).name(in.getName()).type(in.getType()).state( - in.getState()).dataset(in.getDataset()).memorySizeMb( - in.getMemorySizeMb()).diskSizeGb(in.getDiskSizeGb()).ips( - in.getIps()).metadata(in.getMetadata()).created( - in.getCreated()).updated(in.getUpdated()); - } - } + public Builder fromMachine(Machine in) { + return id(in.getId()).name(in.getName()).type(in.getType()).state(in.getState()).dataset(in.getDataset()) + .memorySizeMb(in.getMemorySizeMb()).diskSizeGb(in.getDiskSizeGb()).ips(in.getIps()) + .metadata(in.getMetadata()).created(in.getCreated()).updated(in.getUpdated()); + } + } - // The globally unique id for this machine - protected final String id; - // The "friendly" name for this machine - protected final String name; - // Whether this is a smartmachine or virtualmachine - protected final Type type; - // The current state of this machine - protected final State state; - // The dataset urn this machine was provisioned with - protected final String dataset; - // The amount of memory this machine has (Mb) - @SerializedName("memory") - protected final int memorySizeMb; - // The amount of disk this machine has (Gb) - @SerializedName("disk") - protected final int diskSizeGb; - // The IP addresses this machine has - protected final Set ips; - // Date (ISO8601) When this machine was created - protected final Date created; - // Date (ISO8601) When this machine was updated - protected final Date updated; + // The globally unique id for this machine + protected final String id; + // The "friendly" name for this machine + protected final String name; + // Whether this is a smartmachine or virtualmachine + protected final Type type; + // The current state of this machine + protected final State state; + // The dataset urn this machine was provisioned with + protected final String dataset; + // The amount of memory this machine has (Mb) + @SerializedName("memory") + protected final int memorySizeMb; + // The amount of disk this machine has (Gb) + @SerializedName("disk") + protected final int diskSizeGb; + // The IP addresses this machine has + protected final Set ips; + // Date (ISO8601) When this machine was created + protected final Date created; + // Date (ISO8601) When this machine was updated + protected final Date updated; - // metadata Object[String => String] Any "extra" metadata this machine has - private final Map metadata; + // metadata Object[String => String] Any "extra" metadata this machine has + private final Map metadata; - @Override - public int compareTo(Machine other) { - return id.compareTo(other.getId()); - } + @Override + public int compareTo(Machine other) { + return id.compareTo(other.getId()); + } - public Machine(String id, String name, Type type, State state, - String dataset, int memorySizeMb, int diskSizeGb, Set ips, - Date created, Date updated, final Map metadata) { - super(); - this.id = id; - this.name = name; - this.type = type; - this.state = state; - this.dataset = dataset; - this.memorySizeMb = memorySizeMb; - this.diskSizeGb = diskSizeGb; - this.ips = ImmutableSet. copyOf(ips); - this.created = created; - this.updated = updated; - this.metadata = metadata; - } + public Machine(String id, String name, Type type, State state, String dataset, int memorySizeMb, int diskSizeGb, + Set ips, Date created, Date updated, final Map metadata) { + super(); + this.id = id; + this.name = name; + this.type = type; + this.state = state; + this.dataset = dataset; + this.memorySizeMb = memorySizeMb; + this.diskSizeGb = diskSizeGb; + this.ips = ImmutableSet. copyOf(ips); + this.created = created; + this.updated = updated; + this.metadata = metadata; + } - public String getId() { - return id; - } + public String getId() { + return id; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public Type getType() { - return type; - } + public Type getType() { + return type; + } - public State getState() { - return state; - } + public State getState() { + return state; + } - public String getDataset() { - return dataset; - } + public String getDataset() { + return dataset; + } - public int getMemorySizeMb() { - return memorySizeMb; - } + public int getMemorySizeMb() { + return memorySizeMb; + } - public int getDiskSizeGb() { - return diskSizeGb; - } + public int getDiskSizeGb() { + return diskSizeGb; + } - public Set getIps() { - return ips; - } + public Set getIps() { + return ips; + } - public Date getCreated() { - return created; - } + public Date getCreated() { + return created; + } - public Date getUpdated() { - return updated; - } + public Date getUpdated() { + return updated; + } - public Map getMetadata() { - return metadata; - } + public Map getMetadata() { + return metadata; + } - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } - if (object instanceof Machine) { - return Objects.equal(id, ((Machine) object).id); - } else { - return false; - } - } + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object instanceof Machine) { + return Objects.equal(id, ((Machine) object).id); + } else { + return false; + } + } - @Override - public int hashCode() { - return Objects.hashCode(id); - } + @Override + public int hashCode() { + return Objects.hashCode(id); + } - @Override - public String toString() { - return String - .format( - "[id=%s, name=%s, type=%s, state=%s, memory=%s, disk=%s, ips=%s, created=%s, updated=%s]", - id, name, type.name(), state.name(), memorySizeMb, - diskSizeGb, ips, created, updated); - } + @Override + public String toString() { + return String.format("[id=%s, name=%s, type=%s, state=%s, memory=%s, disk=%s, ips=%s, created=%s, updated=%s]", + id, name, type.name(), state.name(), memorySizeMb, diskSizeGb, ips, created, updated); + } } diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Package.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Package.java index 46387ef792..a76ca6dc5d 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Package.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Package.java @@ -29,128 +29,121 @@ import com.google.gson.annotations.SerializedName; */ public class Package implements Comparable { + public static Builder builder() { + return new Builder(); + } - public static Builder builder() { - return new Builder(); - } + public static class Builder { + private String name; + private int memorySizeMb; + private int diskSizeGb; + private int swapSizeMb; + private boolean isDefault; - public static class Builder { - private String name; - private int memorySizeMb; - private int diskSizeGb; - private int swapSizeMb; - private boolean defaultPackage; + public Builder name(String name) { + this.name = name; + return this; + } - public Builder name(String name) { - this.name = name; - return this; - } + public Builder memorySizeMb(int memorySizeMb) { + this.memorySizeMb = memorySizeMb; + return this; + } - public Builder memorySizeMb(int memorySizeMb) { - this.memorySizeMb = memorySizeMb; - return this; - } + public Builder diskSizeGb(int diskSizeGb) { + this.diskSizeGb = diskSizeGb; + return this; + } - public Builder diskSizeGb(int diskSizeGb) { - this.diskSizeGb = diskSizeGb; - return this; - } + public Builder swapSizeMb(int swapSizeMb) { + this.swapSizeMb = swapSizeMb; + return this; + } - public Builder swapSizeMb(int swapSizeMb) { - this.swapSizeMb = swapSizeMb; - return this; - } + public Builder isDefault(boolean isDefault) { + this.isDefault = isDefault; + return this; + } - public Builder isDefault(boolean defaultPackage) { - this.defaultPackage = defaultPackage; - return this; - } + public Package build() { + return new Package(name, memorySizeMb, diskSizeGb, swapSizeMb, isDefault); + } + public Builder fromPackage(Package in) { + return name(in.getName()).memorySizeMb(in.getMemorySizeMb()).diskSizeGb(in.getDiskSizeGb()) + .swapSizeMb(in.getSwapSizeMb()).isDefault(in.isDefault()); + } + } - public Package build() { - return new Package(name, memorySizeMb, - diskSizeGb, swapSizeMb, defaultPackage); - } + // The "friendly" name for this machine + protected final String name; + // The amount of memory this package has (Mb) + @SerializedName("memory") + protected final int memorySizeMb; + // The amount of disk this package has (Gb) + @SerializedName("disk") + protected final int diskSizeGb; + // The amount of swap this package has (Gb) + @SerializedName("swap") + protected final int swapSizeMb; + // Whether this is the default package in this datacenter + @SerializedName("default") + protected final boolean isDefault; - public Builder fromPackage(Package in) { - return name(in.getName()).memorySizeMb( - in.getMemorySizeMb()).diskSizeGb(in.getDiskSizeGb()).swapSizeMb(in.getSwapSizeMb()).isDefault(in.isDefault()); - } - } + @Override + public int compareTo(Package other) { + return name.compareTo(other.getName()); + } - // The "friendly" name for this machine - protected final String name; - // The amount of memory this package has (Mb) - @SerializedName("memory") - protected final int memorySizeMb; - // The amount of disk this package has (Gb) - @SerializedName("disk") - protected final int diskSizeGb; - // The amount of swap this package has (Gb) - @SerializedName("swap") - protected final int swapSizeMb; - // Whether this is the default package in this datacenter - @SerializedName("default") - protected final boolean defaultPackage; + public Package(String name, int memorySizeMb, int diskSizeGb, int swapSizeMb, boolean isDefault) { + super(); + this.name = name; + this.memorySizeMb = memorySizeMb; + this.diskSizeGb = diskSizeGb; + this.swapSizeMb = swapSizeMb; + this.isDefault = isDefault; + } - @Override - public int compareTo(Package other) { - return name.compareTo(other.getName()); - } + public String getName() { + return name; + } - public Package(String name, int memorySizeMb, int diskSizeGb, - int swapSizeMb, boolean defaultPackage) { - super(); - this.name = name; - this.memorySizeMb = memorySizeMb; - this.diskSizeGb = diskSizeGb; - this.swapSizeMb = swapSizeMb; - this.defaultPackage = defaultPackage; - } + public int getMemorySizeMb() { + return memorySizeMb; + } - public String getName() { - return name; - } + public int getDiskSizeGb() { + return diskSizeGb; + } - public int getMemorySizeMb() { - return memorySizeMb; - } + public int getSwapSizeMb() { + return swapSizeMb; + } - public int getDiskSizeGb() { - return diskSizeGb; - } + public boolean isDefault() { + return isDefault; + } - public int getSwapSizeMb() { - return swapSizeMb; - } + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object instanceof Package) { + return Objects.equal(name, ((Package) object).name); + } else { + return false; + } + } - public boolean isDefault() { - return defaultPackage; - } + @Override + public int hashCode() { + return Objects.hashCode(name); + } - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } - if (object instanceof Package) { - return Objects.equal(name, ((Package) object).name); - } else { - return false; - } - } - - @Override - public int hashCode() { - return Objects.hashCode(name); - } - - @Override - public String toString() { - return String - .format( - "[name=%s, memory=%s, disk=%s, swap=%s, default=%s]", - name, memorySizeMb, - diskSizeGb, swapSizeMb, defaultPackage); - } + @Override + public String toString() { + return String.format("[name=%s, memory=%s, disk=%s, swap=%s, default=%s]", name, memorySizeMb, diskSizeGb, + swapSizeMb, isDefault); + } } diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Type.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Type.java index b02c260a0d..e9224103cd 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Type.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Type.java @@ -5,25 +5,22 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.base.CaseFormat; public enum Type { - VIRTUALMACHINE, SMARTMACHINE, UNRECOGNIZED; + VIRTUALMACHINE, SMARTMACHINE, UNRECOGNIZED; - public static Type fromValue(String type) { - try { - return valueOf(CaseFormat.UPPER_CAMEL - .to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(type, - "type"))); - } catch (IllegalArgumentException e) { - return UNRECOGNIZED; - } - } + public static Type fromValue(String type) { + try { + return valueOf(CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(type, "type"))); + } catch (IllegalArgumentException e) { + return UNRECOGNIZED; + } + } - public String value() { - return (CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, - name())); - } + public String value() { + return (CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name())); + } - @Override - public String toString() { - return value(); - } + @Override + public String toString() { + return value(); + } } diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatacenterAsyncClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatacenterAsyncClient.java index 0c549137eb..cb4949aa36 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatacenterAsyncClient.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatacenterAsyncClient.java @@ -42,11 +42,11 @@ import com.google.common.util.concurrent.ListenableFuture; * @author Adrian Cole * @see api doc */ -@SkipEncoding( { '/', '=' }) -@Headers(keys="X-Api-Version", values="{jclouds.api-version}") +@SkipEncoding({ '/', '=' }) +@Headers(keys = "X-Api-Version", values = "{jclouds.api-version}") @RequestFilters(BasicAuthentication.class) public interface DatacenterAsyncClient { - + /** * @see DatacenterClient#getDatacenters */ diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetAsyncClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetAsyncClient.java index a2812b9e58..80eff51218 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetAsyncClient.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetAsyncClient.java @@ -27,25 +27,25 @@ import com.google.common.util.concurrent.ListenableFuture; * @see DatasetClient * @see api doc */ -@SkipEncoding( { '/', '=' }) +@SkipEncoding({ '/', '=' }) @Headers(keys = "X-Api-Version", values = "{jclouds.api-version}") @RequestFilters(BasicAuthentication.class) public interface DatasetAsyncClient { - /** - * @see DatasetClient#listMachines - */ - @GET - @Path("/my/datasets") - @Consumes(MediaType.APPLICATION_JSON) - @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) - ListenableFuture> listDatasets(); + /** + * @see DatasetClient#listMachines + */ + @GET + @Path("/my/datasets") + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) + ListenableFuture> listDatasets(); - /** - * @see DatasetClient#getMachineDetails - */ - @GET - @Path("/my/datasets/{id}") - @Consumes(MediaType.APPLICATION_JSON) - @ExceptionParser(ReturnNullOnNotFoundOr404.class) - ListenableFuture getDataset(@PathParam("id") String id); + /** + * @see DatasetClient#getMachineDetails + */ + @GET + @Path("/my/datasets/{id}") + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getDataset(@PathParam("id") String id); } diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClient.java index c61de92e23..c33fd8021e 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClient.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClient.java @@ -17,18 +17,19 @@ import org.jclouds.joyent.sdc.v6_5.domain.Dataset; @Timeout(duration = 30, timeUnit = TimeUnit.SECONDS) public interface DatasetClient { - /** - * Provides a list of datasets available in this datacenter. - * @return - */ - Set listDatasets(); + /** + * Provides a list of datasets available in this datacenter. + * + * @return + */ + Set listDatasets(); - /** - * Gets an individual dataset by id. - * - * @param id - * the id of the dataset - * @return - */ - Dataset getDataset(String id); + /** + * Gets an individual dataset by id. + * + * @param id + * the id of the dataset + * @return + */ + Dataset getDataset(String id); } diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineAsyncClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineAsyncClient.java index 39431a0439..21f9578430 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineAsyncClient.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineAsyncClient.java @@ -45,27 +45,27 @@ import com.google.common.util.concurrent.ListenableFuture; * @see MachineClient * @see api doc */ -@SkipEncoding( { '/', '=' }) +@SkipEncoding({ '/', '=' }) @Headers(keys = "X-Api-Version", values = "{jclouds.api-version}") @RequestFilters(BasicAuthentication.class) public interface MachineAsyncClient { - /** - * @see MachineClient#listMachines - */ - @GET - @Path("/my/machines") - @Consumes(MediaType.APPLICATION_JSON) - @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) - ListenableFuture> listMachines(); + /** + * @see MachineClient#listMachines + */ + @GET + @Path("/my/machines") + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) + ListenableFuture> listMachines(); - /** - * @see MachineClient#getMachineDetails - */ - @GET - @Path("/my/machines/{id}") - @Consumes(MediaType.APPLICATION_JSON) - @ExceptionParser(ReturnNullOnNotFoundOr404.class) - ListenableFuture getMachine(@PathParam("id") String id); + /** + * @see MachineClient#getMachineDetails + */ + @GET + @Path("/my/machines/{id}") + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getMachine(@PathParam("id") String id); } diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineClient.java index 94302321a7..4c0a5a1599 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineClient.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineClient.java @@ -24,7 +24,6 @@ import java.util.concurrent.TimeUnit; import org.jclouds.concurrent.Timeout; import org.jclouds.joyent.sdc.v6_5.domain.Machine; - /** * Provides synchronous access to Machine. *

@@ -42,12 +41,12 @@ public interface MachineClient { * @return an account's associated machine objects. */ Set listMachines(); - /** * Gets the details for an individual machine. * - * @param id the id of the machine + * @param id + * the id of the machine * @return */ Machine getMachine(String id); diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageAsyncClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageAsyncClient.java index b65abbbba9..7a316da00c 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageAsyncClient.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageAsyncClient.java @@ -26,25 +26,25 @@ import com.google.common.util.concurrent.ListenableFuture; * @see PackageClient * @see api doc */ -@SkipEncoding( { '/', '=' }) +@SkipEncoding({ '/', '=' }) @Headers(keys = "X-Api-Version", values = "{jclouds.api-version}") @RequestFilters(BasicAuthentication.class) public interface PackageAsyncClient { - /** - * @see PackageClient#listPackages - */ - @GET - @Path("/my/packages") - @Consumes(MediaType.APPLICATION_JSON) - @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) - ListenableFuture> listPackages(); + /** + * @see PackageClient#listPackages + */ + @GET + @Path("/my/packages") + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) + ListenableFuture> listPackages(); - /** - * @see PackageClient#getPackageDetails - */ - @GET - @Path("/my/packages/{name}") - @Consumes(MediaType.APPLICATION_JSON) - @ExceptionParser(ReturnNullOnNotFoundOr404.class) - ListenableFuture getPackage(@PathParam("name") String name); + /** + * @see PackageClient#getPackageDetails + */ + @GET + @Path("/my/packages/{name}") + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getPackage(@PathParam("name") String name); } diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageClient.java index 7bfa6bad6a..4f58da8f52 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageClient.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageClient.java @@ -16,18 +16,19 @@ import org.jclouds.concurrent.Timeout; @Timeout(duration = 30, timeUnit = TimeUnit.SECONDS) public interface PackageClient { - /** - * Provides a list of packages available in this datacenter. - * @return - */ - Set listPackages(); + /** + * Provides a list of packages available in this datacenter. + * + * @return + */ + Set listPackages(); - /** - * Gets an individual package by id. - * - * @param name - * the name of the package - * @return - */ - org.jclouds.joyent.sdc.v6_5.domain.Package getPackage(String name); + /** + * Gets an individual package by id. + * + * @param name + * the name of the package + * @return + */ + org.jclouds.joyent.sdc.v6_5.domain.Package getPackage(String name); } diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/handlers/SDCErrorHandler.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/handlers/SDCErrorHandler.java index 856ee47b45..2f8437088f 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/handlers/SDCErrorHandler.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/handlers/SDCErrorHandler.java @@ -44,21 +44,21 @@ public class SDCErrorHandler implements HttpErrorHandler { String message = data != null ? new String(data) : null; Exception exception = message != null ? new HttpResponseException(command, response, message) - : new HttpResponseException(command, response); + : new HttpResponseException(command, response); message = message != null ? message : String.format("%s -> %s", command.getCurrentRequest().getRequestLine(), - response.getStatusLine()); + response.getStatusLine()); switch (response.getStatusCode()) { - case 400: - break; - case 401: - case 403: - exception = new AuthorizationException(message, exception); - break; - case 404: - if (!command.getCurrentRequest().getMethod().equals("DELETE")) { - exception = new ResourceNotFoundException(message, exception); - } - break; + case 400: + break; + case 401: + case 403: + exception = new AuthorizationException(message, exception); + break; + case 404: + if (!command.getCurrentRequest().getMethod().equals("DELETE")) { + exception = new ResourceNotFoundException(message, exception); + } + break; } command.setException(exception); } diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatacenterClientExpectTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatacenterClientExpectTest.java index fd4711e884..07a6623867 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatacenterClientExpectTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatacenterClientExpectTest.java @@ -36,25 +36,24 @@ import com.google.common.collect.ImmutableMultimap; */ @Test(groups = "unit", testName = "DatacenterClientExpectTest") public class DatacenterClientExpectTest extends BaseSDCClientExpectTest { - HttpRequest getDatacenters = HttpRequest.builder() - .method("GET") - .endpoint(URI.create("https://api.joyentcloud.com/my/datacenters")) - .headers(ImmutableMultimap. builder() - .put("X-Api-Version", "~6.5") - .put("Accept", "application/json") - .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()) - .build(); + HttpRequest getDatacenters = HttpRequest + .builder() + .method("GET") + .endpoint(URI.create("https://api.joyentcloud.com/my/datacenters")) + .headers( + ImmutableMultimap. builder().put("X-Api-Version", "~6.5") + .put("Accept", "application/json").put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==") + .build()).build(); public void testGetDatacentersWhenResponseIs2xx() { - HttpResponse getDatacentersResponse = HttpResponse.builder() - .statusCode(200) - .payload(payloadFromResource("/datacenters.json")).build(); - + HttpResponse getDatacentersResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/datacenters.json")).build(); + SDCClient clientWhenDatacentersExists = requestSendsResponse(getDatacenters, getDatacentersResponse); - - assertEquals(clientWhenDatacentersExists.getDatacenterClient().getDatacenters(), - ImmutableMap. builder() - .put("us-east-1", URI.create("https://us-east-1.api.joyentcloud.com")) + + assertEquals( + clientWhenDatacentersExists.getDatacenterClient().getDatacenters(), + ImmutableMap. builder().put("us-east-1", URI.create("https://us-east-1.api.joyentcloud.com")) .put("us-west-1", URI.create("https://us-west-1.api.joyentcloud.com")) .put("us-sw-1", URI.create("https://us-sw-1.api.joyentcloud.com")) .put("eu-ams-1", URI.create("https://eu-ams-1.api.joyentcloud.com")).build()); @@ -62,9 +61,9 @@ public class DatacenterClientExpectTest extends BaseSDCClientExpectTest { public void testGetDatacentersWhenResponseIs404() { HttpResponse getDatacentersResponse = HttpResponse.builder().statusCode(404).build(); - + SDCClient getDatacentersWhenNone = requestSendsResponse(getDatacenters, getDatacentersResponse); - + assertEquals(getDatacentersWhenNone.getDatacenterClient().getDatacenters(), ImmutableMap.of()); } } diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatacenterClientLiveTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatacenterClientLiveTest.java index 7d14711b3b..6858db294e 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatacenterClientLiveTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatacenterClientLiveTest.java @@ -33,7 +33,7 @@ import org.testng.annotations.Test; @Test(groups = "live", testName = "DatacenterClientLiveTest") public class DatacenterClientLiveTest extends BaseSDCClientLiveTest { - public void testGetDatacenters(){ + public void testGetDatacenters() { Map dcs = sdcContext.getApi().getDatacenterClient().getDatacenters(); assertNotNull(dcs); assertTrue(dcs.size() > 0); diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientExpectTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientExpectTest.java index 6ae2cc9579..589dcd90bb 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientExpectTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientExpectTest.java @@ -37,34 +37,30 @@ import com.google.common.collect.ImmutableSet; */ @Test(groups = "unit", testName = "DatasetClientExpectTest") public class DatasetClientExpectTest extends BaseSDCClientExpectTest { - HttpRequest listDatasets = HttpRequest.builder().method("GET").endpoint( - URI.create("https://api.joyentcloud.com/my/datasets")).headers( - ImmutableMultimap. builder().put("X-Api-Version", - "~6.5").put("Accept", "application/json").put( - "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==") - .build()).build(); + HttpRequest listDatasets = HttpRequest + .builder() + .method("GET") + .endpoint(URI.create("https://api.joyentcloud.com/my/datasets")) + .headers( + ImmutableMultimap. builder().put("X-Api-Version", "~6.5") + .put("Accept", "application/json").put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==") + .build()).build(); - public void testListDatasetsWhenResponseIs2xx() { - HttpResponse listDatasetsResponse = HttpResponse.builder() - .statusCode(200).payload( - payloadFromResource("/dataset_list.json")).build(); + public void testListDatasetsWhenResponseIs2xx() { + HttpResponse listDatasetsResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/dataset_list.json")).build(); - SDCClient clientWhenDatasetsExists = requestSendsResponse(listDatasets, - listDatasetsResponse); + SDCClient clientWhenDatasetsExists = requestSendsResponse(listDatasets, listDatasetsResponse); - assertEquals( - clientWhenDatasetsExists.getDatasetClient().listDatasets().toString(), - new ParseDatasetListTest().expected().toString()); - } + assertEquals(clientWhenDatasetsExists.getDatasetClient().listDatasets().toString(), new ParseDatasetListTest() + .expected().toString()); + } - public void testListDatasetsWhenResponseIs404() { - HttpResponse listDatasetsResponse = HttpResponse.builder().statusCode( - 404).build(); + public void testListDatasetsWhenResponseIs404() { + HttpResponse listDatasetsResponse = HttpResponse.builder().statusCode(404).build(); - SDCClient listDatasetsWhenNone = requestSendsResponse(listDatasets, - listDatasetsResponse); + SDCClient listDatasetsWhenNone = requestSendsResponse(listDatasets, listDatasetsResponse); - assertEquals(listDatasetsWhenNone.getDatasetClient().listDatasets(), - ImmutableSet.of()); - } + assertEquals(listDatasetsWhenNone.getDatasetClient().listDatasets(), ImmutableSet.of()); + } } diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientLiveTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientLiveTest.java index 0dec63cdfa..31bf750d9e 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientLiveTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientLiveTest.java @@ -18,9 +18,9 @@ */ package org.jclouds.joyent.sdc.v6_5.features; -import static org.testng.AssertJUnit.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; +import static org.testng.AssertJUnit.assertEquals; import java.util.Set; @@ -34,17 +34,16 @@ import org.testng.annotations.Test; @Test(groups = "live", testName = "DatasetClientLiveTest") public class DatasetClientLiveTest extends BaseSDCClientLiveTest { - public void testListDatasets() { - Set datasets = sdcContext.getApi().getDatasetClient() - .listDatasets(); - assertNotNull(datasets); - assertTrue(datasets.size() > 0); - } + public void testListDatasets() { + Set datasets = sdcContext.getApi().getDatasetClient().listDatasets(); + assertNotNull(datasets); + assertTrue(datasets.size() > 0); + } - public void testGetDataset() { - final String id = "e4cd7b9e-4330-11e1-81cf-3bb50a972bda"; - Dataset dataset = sdcContext.getApi().getDatasetClient().getDataset(id); - assertNotNull(dataset); - assertEquals(dataset.getId(), id); - } + public void testGetDataset() { + final String id = "e4cd7b9e-4330-11e1-81cf-3bb50a972bda"; + Dataset dataset = sdcContext.getApi().getDatasetClient().getDataset(id); + assertNotNull(dataset); + assertEquals(dataset.getId(), id); + } } diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientExpectTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientExpectTest.java index b2c75a80e6..42ad7528bf 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientExpectTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientExpectTest.java @@ -37,34 +37,29 @@ import com.google.common.collect.ImmutableSet; */ @Test(groups = "unit", testName = "MachineClientExpectTest") public class MachineClientExpectTest extends BaseSDCClientExpectTest { - HttpRequest listMachines = HttpRequest.builder().method("GET").endpoint( - URI.create("https://api.joyentcloud.com/my/machines")).headers( - ImmutableMultimap. builder().put("X-Api-Version", - "~6.5").put("Accept", "application/json").put( - "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==") - .build()).build(); + HttpRequest listMachines = HttpRequest + .builder() + .method("GET") + .endpoint(URI.create("https://api.joyentcloud.com/my/machines")) + .headers( + ImmutableMultimap. builder().put("X-Api-Version", "~6.5") + .put("Accept", "application/json").put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==") + .build()).build(); - public void testListMachinesWhenResponseIs2xx() { - HttpResponse listMachinesResponse = HttpResponse.builder() - .statusCode(200).payload( - payloadFromResource("/machine_list.json")).build(); + public void testListMachinesWhenResponseIs2xx() { + HttpResponse listMachinesResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/machine_list.json")).build(); - SDCClient clientWhenMachinesExists = requestSendsResponse(listMachines, - listMachinesResponse); + SDCClient clientWhenMachinesExists = requestSendsResponse(listMachines, listMachinesResponse); - assertEquals( - clientWhenMachinesExists.getMachineClient().listMachines(), - new ParseMachineListTest().expected()); - } + assertEquals(clientWhenMachinesExists.getMachineClient().listMachines(), new ParseMachineListTest().expected()); + } - public void testListMachinesWhenResponseIs404() { - HttpResponse listMachinesResponse = HttpResponse.builder().statusCode( - 404).build(); + public void testListMachinesWhenResponseIs404() { + HttpResponse listMachinesResponse = HttpResponse.builder().statusCode(404).build(); - SDCClient listMachinesWhenNone = requestSendsResponse(listMachines, - listMachinesResponse); + SDCClient listMachinesWhenNone = requestSendsResponse(listMachines, listMachinesResponse); - assertEquals(listMachinesWhenNone.getMachineClient().listMachines(), - ImmutableSet.of()); - } + assertEquals(listMachinesWhenNone.getMachineClient().listMachines(), ImmutableSet.of()); + } } diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientLiveTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientLiveTest.java index fa213abdcd..7011ce569f 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientLiveTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientLiveTest.java @@ -18,8 +18,8 @@ */ package org.jclouds.joyent.sdc.v6_5.features; -import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; import java.util.Set; @@ -34,17 +34,16 @@ import org.testng.annotations.Test; @Test(groups = "live", testName = "MachineClientLiveTest") public class MachineClientLiveTest extends BaseSDCClientLiveTest { - public void testListMachines() { - Set machines = sdcContext.getApi().getMachineClient() - .listMachines(); - assertNotNull(machines); - assertTrue(machines.size() > 0); - } + public void testListMachines() { + Set machines = sdcContext.getApi().getMachineClient().listMachines(); + assertNotNull(machines); + assertTrue(machines.size() > 0); + } - public void testGetMachine() { - final String id = "d73cb0b0-7d1f-44ef-8c40-e040eef0f726"; - Machine machine = sdcContext.getApi().getMachineClient().getMachine(id); - assertNotNull(machine); - assertEquals(machine.getId(), id); - } + public void testGetMachine() { + final String id = "d73cb0b0-7d1f-44ef-8c40-e040eef0f726"; + Machine machine = sdcContext.getApi().getMachineClient().getMachine(id); + assertNotNull(machine); + assertEquals(machine.getId(), id); + } } diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientExpectTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientExpectTest.java index d572c8ecdc..9ecc673b11 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientExpectTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientExpectTest.java @@ -37,34 +37,29 @@ import com.google.common.collect.ImmutableSet; */ @Test(groups = "unit", testName = "PackageClientExpectTest") public class PackageClientExpectTest extends BaseSDCClientExpectTest { - HttpRequest listPackages = HttpRequest.builder().method("GET").endpoint( - URI.create("https://api.joyentcloud.com/my/packages")).headers( - ImmutableMultimap. builder().put("X-Api-Version", - "~6.5").put("Accept", "application/json").put( - "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==") - .build()).build(); + HttpRequest listPackages = HttpRequest + .builder() + .method("GET") + .endpoint(URI.create("https://api.joyentcloud.com/my/packages")) + .headers( + ImmutableMultimap. builder().put("X-Api-Version", "~6.5") + .put("Accept", "application/json").put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==") + .build()).build(); - public void testListPackagesWhenResponseIs2xx() { - HttpResponse listPackagesResponse = HttpResponse.builder() - .statusCode(200).payload( - payloadFromResource("/package_list.json")).build(); + public void testListPackagesWhenResponseIs2xx() { + HttpResponse listPackagesResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/package_list.json")).build(); - SDCClient clientWhenPackagesExists = requestSendsResponse(listPackages, - listPackagesResponse); + SDCClient clientWhenPackagesExists = requestSendsResponse(listPackages, listPackagesResponse); - assertEquals( - clientWhenPackagesExists.getPackageClient().listPackages(), - new ParsePackageListTest().expected()); - } + assertEquals(clientWhenPackagesExists.getPackageClient().listPackages(), new ParsePackageListTest().expected()); + } - public void testListPackagesWhenResponseIs404() { - HttpResponse listPackagesResponse = HttpResponse.builder().statusCode( - 404).build(); + public void testListPackagesWhenResponseIs404() { + HttpResponse listPackagesResponse = HttpResponse.builder().statusCode(404).build(); - SDCClient listPackagesWhenNone = requestSendsResponse(listPackages, - listPackagesResponse); + SDCClient listPackagesWhenNone = requestSendsResponse(listPackages, listPackagesResponse); - assertEquals(listPackagesWhenNone.getPackageClient().listPackages(), - ImmutableSet.of()); - } + assertEquals(listPackagesWhenNone.getPackageClient().listPackages(), ImmutableSet.of()); + } } diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientLiveTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientLiveTest.java index 64ee954654..55e1aaa230 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientLiveTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientLiveTest.java @@ -18,9 +18,9 @@ */ package org.jclouds.joyent.sdc.v6_5.features; -import static org.testng.AssertJUnit.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; +import static org.testng.AssertJUnit.assertEquals; import java.util.Set; @@ -33,17 +33,16 @@ import org.testng.annotations.Test; @Test(groups = "live", testName = "PackageClientLiveTest") public class PackageClientLiveTest extends BaseSDCClientLiveTest { - public void testListPackages() { - Set packages = sdcContext.getApi().getPackageClient() - .listPackages(); - assertNotNull(packages); - assertTrue(packages.size() > 0); - } + public void testListPackages() { + Set packages = sdcContext.getApi().getPackageClient().listPackages(); + assertNotNull(packages); + assertTrue(packages.size() > 0); + } - public void testGetPackage() { - final String name = "Small 1GB"; - org.jclouds.joyent.sdc.v6_5.domain.Package packageSDC = sdcContext.getApi().getPackageClient().getPackage(name); - assertNotNull(packageSDC); - assertEquals(packageSDC.getName(), name); - } + public void testGetPackage() { + final String name = "Small 1GB"; + org.jclouds.joyent.sdc.v6_5.domain.Package packageSDC = sdcContext.getApi().getPackageClient().getPackage(name); + assertNotNull(packageSDC); + assertEquals(packageSDC.getName(), name); + } } diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/handlers/SDCErrorHandlerTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/handlers/SDCErrorHandlerTest.java index 8d5db2ad6a..bec62ba662 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/handlers/SDCErrorHandlerTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/handlers/SDCErrorHandlerTest.java @@ -41,21 +41,20 @@ import org.testng.annotations.Test; @Test(groups = "unit", testName = "SDCErrorHandlerTest") public class SDCErrorHandlerTest { - private void assertCodeMakes(String method, URI uri, int statusCode, String message, String content, - Class expected) { + Class expected) { assertCodeMakes(method, uri, statusCode, message, "text/plain", content, expected); } private void assertCodeMakes(String method, URI uri, int statusCode, String message, String contentType, - String content, Class expected) { + String content, Class expected) { SDCErrorHandler function = new SDCErrorHandler(); HttpCommand command = createMockBuilder(HttpCommand.class).createMock(); HttpRequest request = new HttpRequest(method, uri); HttpResponse response = new HttpResponse(statusCode, message, Payloads.newInputStreamPayload(Strings2 - .toInputStream(content))); + .toInputStream(content))); response.getPayload().getContentMetadata().setContentType(contentType); expect(command.getCurrentRequest()).andReturn(request).atLeastOnce(); @@ -87,5 +86,4 @@ public class SDCErrorHandlerTest { return null; } - } diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/internal/BaseSDCClientLiveTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/internal/BaseSDCClientLiveTest.java index 39ab68f913..cbac274144 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/internal/BaseSDCClientLiveTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/internal/BaseSDCClientLiveTest.java @@ -49,7 +49,7 @@ public class BaseSDCClientLiveTest extends BaseContextLiveTest extends BaseRestClientExpectTest { - + public BaseSDCExpectTest() { provider = "joyent-sdc"; } diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseDatasetListTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseDatasetListTest.java index 2ff9e620d3..c824402ec0 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseDatasetListTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseDatasetListTest.java @@ -41,50 +41,37 @@ import com.google.inject.Injector; @Test(groups = "unit", testName = "ParseDatasetListTest") public class ParseDatasetListTest extends BaseSetParserTest { - @Override - public String resource() { - return "/dataset_list.json"; - } + @Override + public String resource() { + return "/dataset_list.json"; + } - @Override - @Consumes(MediaType.APPLICATION_JSON) - public Set expected() { - return ImmutableSet - .of( - Dataset - .builder() - .id("e4cd7b9e-4330-11e1-81cf-3bb50a972bda") - .name("centos-6") - .urn("sdc:sdc:centos-6:1.0.1") - .type(Type.VIRTUALMACHINE) - .version("1.0.1") - .defaultDataset(false) - .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-02-13T06:30:33+00:00")) - .build(), - - Dataset - .builder() - .id("e62c30b4-cdda-11e0-9dd4-af4d032032e3") - .name("nodejs") - .urn("sdc:sdc:nodejs:1.2.3") - .type(Type.SMARTMACHINE) - .version("1.2.3") - .defaultDataset(false) - .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-09-15T08:15:29+00:00")) - .build() + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Set expected() { + return ImmutableSet.of( + Dataset.builder().id("e4cd7b9e-4330-11e1-81cf-3bb50a972bda").name("centos-6").urn("sdc:sdc:centos-6:1.0.1") + .type(Type.VIRTUALMACHINE).version("1.0.1").isDefault(false) + .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-02-13T06:30:33+00:00")) + .build(), - ); - } + Dataset.builder().id("e62c30b4-cdda-11e0-9dd4-af4d032032e3").name("nodejs").urn("sdc:sdc:nodejs:1.2.3") + .type(Type.SMARTMACHINE).version("1.2.3").isDefault(false) + .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-09-15T08:15:29+00:00")) + .build() - protected Injector injector() { - return Guice.createInjector(new SDCParserModule(), new GsonModule() { + ); + } - @Override - protected void configure() { - bind(DateAdapter.class).to(Iso8601DateAdapter.class); - super.configure(); - } + protected Injector injector() { + return Guice.createInjector(new SDCParserModule(), new GsonModule() { - }); - } + @Override + protected void configure() { + bind(DateAdapter.class).to(Iso8601DateAdapter.class); + super.configure(); + } + + }); + } } diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseDatasetTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseDatasetTest.java index d6bfef14de..5e01809882 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseDatasetTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseDatasetTest.java @@ -43,31 +43,23 @@ public class ParseDatasetTest extends BaseItemParserTest { return "/dataset.json"; } - @Override @Consumes(MediaType.APPLICATION_JSON) public Dataset expected() { - return Dataset - .builder() - .id("e4cd7b9e-4330-11e1-81cf-3bb50a972bda") - .name("centos-6") - .urn("sdc:sdc:centos-6:1.0.1") - .type(Type.VIRTUALMACHINE) - .version("1.0.1") - .defaultDataset(false) - .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-02-13T06:30:33+00:00")) - .build(); + return Dataset.builder().id("e4cd7b9e-4330-11e1-81cf-3bb50a972bda").name("centos-6") + .urn("sdc:sdc:centos-6:1.0.1").type(Type.VIRTUALMACHINE).version("1.0.1").isDefault(false) + .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-02-13T06:30:33+00:00")).build(); } protected Injector injector() { return Guice.createInjector(new SDCParserModule(), new GsonModule() { - @Override - protected void configure() { - bind(DateAdapter.class).to(Iso8601DateAdapter.class); - super.configure(); - } + @Override + protected void configure() { + bind(DateAdapter.class).to(Iso8601DateAdapter.class); + super.configure(); + } - }); + }); } } diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineListTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineListTest.java index 2a9e0428d1..60d70bfe73 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineListTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineListTest.java @@ -42,79 +42,52 @@ import com.google.inject.Injector; @Test(groups = "unit", testName = "ParseMachineListTest") public class ParseMachineListTest extends BaseSetParserTest { - @Override - public String resource() { - return "/machine_list.json"; - } + @Override + public String resource() { + return "/machine_list.json"; + } - @Override - @Consumes(MediaType.APPLICATION_JSON) - public Set expected() { - return ImmutableSet - .of( - Machine - .builder() - .id("94eba336-ecb7-49f5-8a27-52f5e4dd57a1") - .name("testJClouds") - .type(Type.VIRTUALMACHINE) - .state(Machine.State.RUNNING) - .dataset("sdc:sdc:centos-5.7:1.2.1") - .ips( - ImmutableSet. builder().add( - "37.153.96.62").add( - "10.224.0.63").build()) - .memorySizeMb(1024) - .diskSizeGb(61440) - .metadata( - ImmutableMap - . builder() - .put("root_authorized_keys", - "ssh-rsa XXXXXX== test@xxxx.ovh.net\n") - .build()) - .created( - new SimpleDateFormatDateService() - .iso8601SecondsDateParse("2012-05-09T13:32:46+00:00")) - .updated( - new SimpleDateFormatDateService() - .iso8601SecondsDateParse("2012-05-11T09:00:33+00:00")) - .build(), - - Machine - .builder() - .id("d73cb0b0-7d1f-44ef-8c40-e040eef0f726") - .name("testJClouds2") - .type(Type.SMARTMACHINE) - .state(Machine.State.RUNNING) - .dataset("sdc:sdc:smartosplus:3.1.0") - .ips( - ImmutableSet. builder().add( - "37.153.96.56").add( - "10.224.0.57").build()) - .memorySizeMb(1024) - .diskSizeGb(61440) - .metadata( - ImmutableMap - . of()) - .created( - new SimpleDateFormatDateService() - .iso8601SecondsDateParse("2012-05-09T13:39:43+00:00")) - .updated( - new SimpleDateFormatDateService() - .iso8601SecondsDateParse("2012-05-09T13:43:45+00:00")) - .build() + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Set expected() { + return ImmutableSet.of( + Machine + .builder() + .id("94eba336-ecb7-49f5-8a27-52f5e4dd57a1") + .name("testJClouds") + .type(Type.VIRTUALMACHINE) + .state(Machine.State.RUNNING) + .dataset("sdc:sdc:centos-5.7:1.2.1") + .ips(ImmutableSet. builder().add("37.153.96.62").add("10.224.0.63").build()) + .memorySizeMb(1024) + .diskSizeGb(61440) + .metadata( + ImmutableMap. builder() + .put("root_authorized_keys", "ssh-rsa XXXXXX== test@xxxx.ovh.net\n").build()) + .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-09T13:32:46+00:00")) + .updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-11T09:00:33+00:00")) + .build(), - ); - } + Machine.builder().id("d73cb0b0-7d1f-44ef-8c40-e040eef0f726").name("testJClouds2").type(Type.SMARTMACHINE) + .state(Machine.State.RUNNING).dataset("sdc:sdc:smartosplus:3.1.0") + .ips(ImmutableSet. builder().add("37.153.96.56").add("10.224.0.57").build()) + .memorySizeMb(1024).diskSizeGb(61440).metadata(ImmutableMap. of()) + .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-09T13:39:43+00:00")) + .updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-09T13:43:45+00:00")) + .build() - protected Injector injector() { - return Guice.createInjector(new SDCParserModule(), new GsonModule() { + ); + } - @Override - protected void configure() { - bind(DateAdapter.class).to(Iso8601DateAdapter.class); - super.configure(); - } + protected Injector injector() { + return Guice.createInjector(new SDCParserModule(), new GsonModule() { - }); - } + @Override + protected void configure() { + bind(DateAdapter.class).to(Iso8601DateAdapter.class); + super.configure(); + } + + }); + } } diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineTest.java index 170f9912d2..02829a41b5 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineTest.java @@ -55,24 +55,25 @@ public class ParseMachineTest extends BaseItemParserTest { .type(Type.VIRTUALMACHINE) .state(Machine.State.STOPPED) .dataset("sdc:sdc:centos-5.7:1.2.1") - .ips(ImmutableSet. builder().add("37.153.96.62").add("10.224.0.63").build()) + .ips(ImmutableSet. builder().add("37.153.96.62").add("10.224.0.63").build()) .memorySizeMb(1024) .diskSizeGb(61440) - .metadata(ImmutableMap. builder().put("root_authorized_keys","ssh-rsa XXXXXX== test@xxxx.ovh.net\n").build()) + .metadata( + ImmutableMap. builder() + .put("root_authorized_keys", "ssh-rsa XXXXXX== test@xxxx.ovh.net\n").build()) .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-09T13:32:46+00:00")) - .updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-11T08:44:53+00:00")) - .build(); + .updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-11T08:44:53+00:00")).build(); } protected Injector injector() { return Guice.createInjector(new SDCParserModule(), new GsonModule() { - @Override - protected void configure() { - bind(DateAdapter.class).to(Iso8601DateAdapter.class); - super.configure(); - } + @Override + protected void configure() { + bind(DateAdapter.class).to(Iso8601DateAdapter.class); + super.configure(); + } - }); + }); } } diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParsePackageListTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParsePackageListTest.java index 9dbda6f042..0790ca4466 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParsePackageListTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParsePackageListTest.java @@ -39,46 +39,32 @@ import com.google.inject.Injector; @Test(groups = "unit", testName = "ParsePackageListTest") public class ParsePackageListTest extends BaseSetParserTest { - @Override - public String resource() { - return "/package_list.json"; - } + @Override + public String resource() { + return "/package_list.json"; + } - @Override - @Consumes(MediaType.APPLICATION_JSON) - public Set expected() { - return ImmutableSet - .of( - org.jclouds.joyent.sdc.v6_5.domain.Package - .builder() - .name("Small 1GB") - .memorySizeMb(1024) - .diskSizeGb(30720) - .swapSizeMb(2048) - .isDefault(true) - .build(), - - org.jclouds.joyent.sdc.v6_5.domain.Package - .builder() - .name("Medium 2GB") - .memorySizeMb(2048) - .diskSizeGb(61440) - .swapSizeMb(4096) - .isDefault(false) - .build() + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Set expected() { + return ImmutableSet.of(org.jclouds.joyent.sdc.v6_5.domain.Package.builder().name("Small 1GB").memorySizeMb(1024) + .diskSizeGb(30720).swapSizeMb(2048).isDefault(true).build(), - ); - } + org.jclouds.joyent.sdc.v6_5.domain.Package.builder().name("Medium 2GB").memorySizeMb(2048).diskSizeGb(61440) + .swapSizeMb(4096).isDefault(false).build() - protected Injector injector() { - return Guice.createInjector(new SDCParserModule(), new GsonModule() { + ); + } - @Override - protected void configure() { - bind(DateAdapter.class).to(Iso8601DateAdapter.class); - super.configure(); - } + protected Injector injector() { + return Guice.createInjector(new SDCParserModule(), new GsonModule() { - }); - } + @Override + protected void configure() { + bind(DateAdapter.class).to(Iso8601DateAdapter.class); + super.configure(); + } + + }); + } } diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParsePackageTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParsePackageTest.java index 000b89939d..397a887307 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParsePackageTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParsePackageTest.java @@ -40,29 +40,22 @@ public class ParsePackageTest extends BaseItemParserTest Date: Tue, 15 May 2012 16:14:52 -0700 Subject: [PATCH 105/148] updated gae demo to work on hpcloud --- demos/googleappengine/pom.xml | 291 ++++++++++++++++-- .../src/main/appengine/appengine-web.xml | 1 + .../config/GuiceServletConfig.java | 35 ++- .../BlobStoreContextToStatusResult.java | 6 +- .../ComputeServiceContextToStatusResult.java | 6 +- .../functest/GoogleAppEngineLiveTest.java | 12 +- 6 files changed, 297 insertions(+), 54 deletions(-) diff --git a/demos/googleappengine/pom.xml b/demos/googleappengine/pom.xml index 376be16673..b78ce3452b 100644 --- a/demos/googleappengine/pom.xml +++ b/demos/googleappengine/pom.xml @@ -32,10 +32,16 @@ JClouds Sample for Google App Engine - jclouds-aws-demo - 1.4.2 + + jclouds-hpcloud-demo + 1.6.5 localhost 8088 + FIXME_IDENTITY + FIXME_CREDENTIAL @@ -51,13 +57,13 @@ org.jclouds.provider - aws-s3 + hpcloud-objectstorage ${project.version} runtime org.jclouds.provider - aws-ec2 + hpcloud-compute ${project.version} runtime @@ -71,6 +77,21 @@ guice-servlet 3.0 + + + + com.google.appengine + appengine-api-1.0-sdk + ${appengine.sdk.version} + + + com.google.appengine + appengine-tools-sdk + ${appengine.sdk.version} + test + + + displaytag displaytag @@ -90,14 +111,14 @@ runtime - standard - taglibs + jstl + javax.servlet 1.1.2 runtime - jstl - javax.servlet + standard + taglibs 1.1.2 runtime @@ -107,30 +128,226 @@ 2.5 provided - - - com.google.appengine - appengine-tools-sdk - ${appengine.sdk.version} - test - ${project.artifactId} - - maven-war-plugin - 2.1.1 + + maven-remote-resources-plugin + 1.2 - - WEB-INF/web.xml - - - src/main/appengine - WEB-INF/ - true - - + + false + + + + maven-war-plugin + 2.1.1 + + + WEB-INF/web.xml + + + src/main/appengine + WEB-INF + true + + .gitignore + + + + + + + com.ning.maven.plugins + maven-duplicate-finder-plugin + 1.0.3 + + + + + + + com.google.appengine + appengine-api-1.0-sdk + 1.6.5 + + + com.google.appengine + appengine-tools-sdk + 1.6.5 + test + + + + com.google + + + + + + + javax.annotation + jsr250-api + 1.0 + runtime + + + org.apache.tomcat + annotations-api + 6.0.32 + test + + + + javax.annotation + + + + + + + commons-beanutils + commons-beanutils + 1.7.0 + runtime + + + commons-collections + commons-collections + 3.1 + runtime + + + + org.apache.commons.collections + + + + + + + javax.servlet + servlet-api + 2.5 + provided + + + com.google.appengine + appengine-tools-sdk + 1.6.5 + test + + + org.apache.tomcat + servlet-api + 6.0.32 + test + + + org.mortbay.jetty + jetty-runner + 7.5.4.v20111024 + test + + + + javax.servlet + + + + javax/servlet/resources/XMLSchema.dtd + javax/servlet/resources/datatypes.dtd + javax/servlet/resources/j2ee_1_4.xsd + javax/servlet/resources/j2ee_web_services_client_1_1.xsd + javax/servlet/resources/javaee_5.xsd + javax/servlet/resources/javaee_web_services_client_1_2.xsd + javax/servlet/resources/jsp_2_0.xsd + javax/servlet/resources/jsp_2_1.xsd + javax/servlet/resources/web-app_2_2.dtd + javax/servlet/resources/web-app_2_3.dtd + javax/servlet/resources/web-app_2_4.xsd + javax/servlet/resources/web-app_2_5.xsd + javax/servlet/resources/xml.xsd + javax/servlet/LocalStrings.properties + javax/servlet/LocalStrings_fr.properties + javax/servlet/LocalStrings_ja.properties + javax/servlet/http/LocalStrings.properties + javax/servlet/http/LocalStrings_es.properties + javax/servlet/http/LocalStrings_fr.properties + javax/servlet/http/LocalStrings_ja.properties + + + + + + + org.apache.tomcat + catalina + 6.0.32 + test + + + org.apache.tomcat + jasper + 6.0.32 + test + + + + org.apache.AnnotationProcessor + org.apache.PeriodicEventListener + + + + + + + javax.servlet + jstl + 1.1.2 + runtime + + + taglibs + standard + 1.1.2 + runtime + + + org.mortbay.jetty + jetty-runner + 7.5.4.v20111024 + test + + + + javax.servlet.jsp.jstl + org.apache.taglibs + + + META-INF/c-1_0-rt.tld + META-INF/c-1_0.tld + META-INF/c.tld + META-INF/fmt-1_0-rt.tld + META-INF/fmt-1_0.tld + META-INF/fmt.tld + META-INF/fn.tld + META-INF/permittedTaglibs.tld + META-INF/scriptfree.tld + META-INF/sql-1_0-rt.tld + META-INF/sql-1_0.tld + META-INF/sql.tld + META-INF/x-1_0-rt.tld + META-INF/x-1_0.tld + META-INF/x.tld + org/apache/taglibs/standard/lang/jstl/Resources.properties + org/apache/taglibs/standard/lang/jstl/Resources_ja.properties + org/apache/taglibs/standard/resources/Resources.properties + org/apache/taglibs/standard/resources/Resources_ja.properties + + + @@ -152,8 +369,8 @@ - ${test.aws-s3.identity} - ${test.aws-s3.credential} + ${test.hpcloud.identity} + ${test.hpcloud.credential} ${appengine.sdk.root} ${devappserver.address} ${devappserver.port} @@ -166,5 +383,21 @@ + + deploy + + + + net.kindleit + maven-gae-plugin + 0.9.2 + + google-appengine + ${appengine.sdk.root} + + + + + diff --git a/demos/googleappengine/src/main/appengine/appengine-web.xml b/demos/googleappengine/src/main/appengine/appengine-web.xml index 32d6d68a44..662e689c2c 100644 --- a/demos/googleappengine/src/main/appengine/appengine-web.xml +++ b/demos/googleappengine/src/main/appengine/appengine-web.xml @@ -25,4 +25,5 @@ + true diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/config/GuiceServletConfig.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/config/GuiceServletConfig.java index 9684038a58..5b195a08a1 100644 --- a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/config/GuiceServletConfig.java +++ b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/config/GuiceServletConfig.java @@ -18,10 +18,10 @@ */ package org.jclouds.samples.googleappengine.config; -import static org.jclouds.compute.reference.ComputeServiceConstants.PROPERTY_TIMEOUT_NODE_RUNNING; -import static org.jclouds.compute.reference.ComputeServiceConstants.PROPERTY_TIMEOUT_NODE_TERMINATED; -import static org.jclouds.compute.reference.ComputeServiceConstants.PROPERTY_TIMEOUT_PORT_OPEN; -import static org.jclouds.compute.reference.ComputeServiceConstants.PROPERTY_TIMEOUT_SCRIPT_COMPLETE; +import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING; +import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED; +import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_PORT_OPEN; +import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_SCRIPT_COMPLETE; import java.io.IOException; import java.io.InputStream; @@ -29,10 +29,9 @@ import java.util.Properties; import javax.servlet.ServletContextEvent; +import org.jclouds.ContextBuilder; import org.jclouds.blobstore.BlobStoreContext; -import org.jclouds.blobstore.BlobStoreContextFactory; import org.jclouds.compute.ComputeServiceContext; -import org.jclouds.compute.ComputeServiceContextFactory; import org.jclouds.gae.config.AsyncGoogleAppEngineConfigurationModule; import org.jclouds.samples.googleappengine.GetAllStatusController; @@ -57,19 +56,25 @@ public class GuiceServletConfig extends GuiceServletContextListener { @Override public void contextInitialized(ServletContextEvent servletContextEvent) { - Properties props = loadJCloudsProperties(servletContextEvent); - props.setProperty(PROPERTY_TIMEOUT_NODE_TERMINATED, "25000"); - props.setProperty(PROPERTY_TIMEOUT_NODE_RUNNING, "25000"); - props.setProperty(PROPERTY_TIMEOUT_SCRIPT_COMPLETE, "25000"); - props.setProperty(PROPERTY_TIMEOUT_PORT_OPEN, "25000"); + Properties overrides = loadJCloudsProperties(servletContextEvent); + overrides.setProperty(TIMEOUT_NODE_TERMINATED, "25000"); + overrides.setProperty(TIMEOUT_NODE_RUNNING, "25000"); + overrides.setProperty(TIMEOUT_SCRIPT_COMPLETE, "25000"); + overrides.setProperty(TIMEOUT_PORT_OPEN, "25000"); // note that this module hooks into the async urlfetchservice ImmutableSet modules = ImmutableSet. of(new AsyncGoogleAppEngineConfigurationModule()); - blobsStoreContexts = ImmutableSet. of(new BlobStoreContextFactory().createContext("aws-s3", - modules, props)); - computeServiceContexts = ImmutableSet. of(new ComputeServiceContextFactory() - .createContext("aws-ec2", modules, props)); + blobsStoreContexts = ImmutableSet.of( + ContextBuilder.newBuilder("hpcloud-objectstorage") + .modules(modules) + .overrides(overrides) + .buildView(BlobStoreContext.class)); + computeServiceContexts = ImmutableSet.of( + ContextBuilder.newBuilder("hpcloud-compute") + .modules(modules) + .overrides(overrides) + .buildView(ComputeServiceContext.class)); super.contextInitialized(servletContextEvent); } diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/BlobStoreContextToStatusResult.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/BlobStoreContextToStatusResult.java index 5ea0e40d7d..a98f28ff91 100644 --- a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/BlobStoreContextToStatusResult.java +++ b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/BlobStoreContextToStatusResult.java @@ -18,6 +18,8 @@ */ package org.jclouds.samples.googleappengine.functions; +import java.net.URI; + import javax.annotation.Resource; import javax.inject.Singleton; @@ -38,7 +40,7 @@ public class BlobStoreContextToStatusResult implements Function Date: Tue, 15 May 2012 19:16:41 -0700 Subject: [PATCH 106/148] Issue 925: fixed cyclic dependency on GAE --- .../AsyncGoogleAppEngineConfigurationModule.java | 12 +++--------- .../config/GoogleAppEngineConfigurationModule.java | 10 +++------- ...GaeHttpCommandExecutorServiceIntegrationTest.java | 8 ++++---- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/drivers/gae/src/main/java/org/jclouds/gae/config/AsyncGoogleAppEngineConfigurationModule.java b/drivers/gae/src/main/java/org/jclouds/gae/config/AsyncGoogleAppEngineConfigurationModule.java index 6358cb18df..1731be8429 100644 --- a/drivers/gae/src/main/java/org/jclouds/gae/config/AsyncGoogleAppEngineConfigurationModule.java +++ b/drivers/gae/src/main/java/org/jclouds/gae/config/AsyncGoogleAppEngineConfigurationModule.java @@ -24,8 +24,6 @@ import org.jclouds.gae.AsyncGaeHttpCommandExecutorService; import org.jclouds.http.HttpCommandExecutorService; import org.jclouds.http.config.ConfiguresHttpCommandExecutorService; -import com.google.inject.Injector; - /** * Configures {@link AsyncGaeHttpCommandExecutorService}. * @@ -35,13 +33,9 @@ import com.google.inject.Injector; @ConfiguresExecutorService @SingleThreaded public class AsyncGoogleAppEngineConfigurationModule extends GoogleAppEngineConfigurationModule { - - public AsyncGoogleAppEngineConfigurationModule() { - super(); - } - - protected HttpCommandExecutorService providerHttpCommandExecutorService(Injector injector) { - return injector.getInstance(AsyncGaeHttpCommandExecutorService.class); + + protected void bindHttpCommandExecutorService() { + bind(HttpCommandExecutorService.class).to(AsyncGaeHttpCommandExecutorService.class); } } diff --git a/drivers/gae/src/main/java/org/jclouds/gae/config/GoogleAppEngineConfigurationModule.java b/drivers/gae/src/main/java/org/jclouds/gae/config/GoogleAppEngineConfigurationModule.java index 92ffef1dee..85ffc92c72 100644 --- a/drivers/gae/src/main/java/org/jclouds/gae/config/GoogleAppEngineConfigurationModule.java +++ b/drivers/gae/src/main/java/org/jclouds/gae/config/GoogleAppEngineConfigurationModule.java @@ -18,8 +18,6 @@ */ package org.jclouds.gae.config; -import javax.inject.Singleton; - import org.jclouds.concurrent.MoreExecutors; import org.jclouds.concurrent.SingleThreaded; import org.jclouds.concurrent.config.ConfiguresExecutorService; @@ -32,7 +30,6 @@ import org.jclouds.http.config.ConfiguresHttpCommandExecutorService; import com.google.appengine.api.urlfetch.URLFetchService; import com.google.appengine.api.urlfetch.URLFetchServiceFactory; -import com.google.inject.Injector; import com.google.inject.Provides; /** @@ -53,12 +50,11 @@ public class GoogleAppEngineConfigurationModule extends ExecutorServiceModule { protected void configure() { super.configure(); bind(TransformingHttpCommandExecutorService.class).to(TransformingHttpCommandExecutorServiceImpl.class); + bindHttpCommandExecutorService(); } - @Singleton - @Provides - protected HttpCommandExecutorService providerHttpCommandExecutorService(Injector injector) { - return injector.getInstance(GaeHttpCommandExecutorService.class); + protected void bindHttpCommandExecutorService() { + bind(HttpCommandExecutorService.class).to(GaeHttpCommandExecutorService.class); } @Provides diff --git a/drivers/gae/src/test/java/org/jclouds/gae/AsyncGaeHttpCommandExecutorServiceIntegrationTest.java b/drivers/gae/src/test/java/org/jclouds/gae/AsyncGaeHttpCommandExecutorServiceIntegrationTest.java index 175863962d..4d93056327 100644 --- a/drivers/gae/src/test/java/org/jclouds/gae/AsyncGaeHttpCommandExecutorServiceIntegrationTest.java +++ b/drivers/gae/src/test/java/org/jclouds/gae/AsyncGaeHttpCommandExecutorServiceIntegrationTest.java @@ -22,7 +22,6 @@ import static org.jclouds.concurrent.FutureIterables.awaitCompletion; import java.io.IOException; import java.io.InputStream; -import java.lang.reflect.UndeclaredThrowableException; import java.net.MalformedURLException; import java.net.URI; import java.util.Map; @@ -232,9 +231,9 @@ public class AsyncGaeHttpCommandExecutorServiceIntegrationTest extends BaseHttpC super.testGetStringSynch(path); } - // local env does not support snakeoil certs + // TODO: determine how to get redirects to operate @Override - @Test(enabled = true, expectedExceptions = UndeclaredThrowableException.class) + @Test(enabled = false) public void testGetStringRedirect() throws MalformedURLException, ExecutionException, InterruptedException, TimeoutException { setupApiProxy(); @@ -284,8 +283,9 @@ public class AsyncGaeHttpCommandExecutorServiceIntegrationTest extends BaseHttpC super.testGetStringViaRequest(); } + // TODO: determine how to get redirects to operate @Override - @Test(enabled = true, expectedExceptions = UndeclaredThrowableException.class) + @Test(enabled = false) public void testPutRedirect() throws MalformedURLException, ExecutionException, InterruptedException, TimeoutException { setupApiProxy(); From c4499d8c5e5fc52caf824a9533719251251f58ec Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 16 May 2012 16:36:53 +0200 Subject: [PATCH 107/148] merged --- .../sdc/v6_5/features/MachineAsyncClient.java | 68 ++++++++ .../sdc/v6_5/features/MachineClient.java | 59 ++++++- .../sdc/v6_5/options/CreateServerOptions.java | 151 ++++++++++++++++++ .../features/DatasetClientExpectTest.java | 29 ++-- .../features/MachineClientExpectTest.java | 40 +++-- .../v6_5/parse/ParseCreatedMachineTest.java | 78 +++++++++ .../src/test/resources/machine.json | 2 +- .../src/test/resources/new_machine.json | 1 + 8 files changed, 405 insertions(+), 23 deletions(-) create mode 100644 labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/options/CreateServerOptions.java create mode 100644 labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseCreatedMachineTest.java create mode 100644 labs/joyent-sdc/src/test/resources/new_machine.json diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineAsyncClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineAsyncClient.java index 21f9578430..b32ab43dc9 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineAsyncClient.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineAsyncClient.java @@ -21,15 +21,22 @@ package org.jclouds.joyent.sdc.v6_5.features; import java.util.Set; import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; +import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.jclouds.http.filters.BasicAuthentication; import org.jclouds.joyent.sdc.v6_5.domain.Machine; +import org.jclouds.joyent.sdc.v6_5.options.CreateServerOptions; import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.Headers; +import org.jclouds.rest.annotations.MapBinder; +import org.jclouds.rest.annotations.Payload; +import org.jclouds.rest.annotations.PayloadParam; import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.SkipEncoding; import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; @@ -68,4 +75,65 @@ public interface MachineAsyncClient { @ExceptionParser(ReturnNullOnNotFoundOr404.class) ListenableFuture getMachine(@PathParam("id") String id); + /** + * @see MachineClient#createMachine + */ + @POST + @Path("/my/machines") + @Consumes(MediaType.APPLICATION_JSON) + @MapBinder(CreateServerOptions.class) + ListenableFuture createMachine(@PayloadParam("name") String name, + @PayloadParam("package") String packageSDC, + @PayloadParam("dataset") String dataset,CreateServerOptions... options); + + /** + * @see MachineClient#stopMachine + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_FORM_URLENCODED) + @Path("/my/machines/{id}") + @Payload("action=stop") + ListenableFuture stopMachine(@PathParam("id") String id); + + /** + * @see MachineClient#startMachine + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_FORM_URLENCODED) + @Path("/my/machines/{id}") + @Payload("action=start") + ListenableFuture startMachine(@PathParam("id") String id); + + /** + * @see MachineClient#rebootMachine + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_FORM_URLENCODED) + @Path("/my/machines/{id}") + @Payload("action=reboot") + ListenableFuture rebootMachine(@PathParam("id") String id); + + /** + * @see MachineClient#resizeMachine + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_FORM_URLENCODED) + @Path("/my/machines/{id}") + @Payload("action=resize&package={package}") + ListenableFuture resizeMachine(@PathParam("id") String id,@PayloadParam("package") String packageSDC); + + /** + * @see MachineClient#deleteMachine + */ + @DELETE + @Consumes(MediaType.APPLICATION_JSON) + @Path("/my/machines/{id}") + ListenableFuture deleteMachine(@PathParam("id") String id); + + + } diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineClient.java index 4c0a5a1599..5cec1ed38c 100644 --- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineClient.java +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineClient.java @@ -23,6 +23,7 @@ import java.util.concurrent.TimeUnit; import org.jclouds.concurrent.Timeout; import org.jclouds.joyent.sdc.v6_5.domain.Machine; +import org.jclouds.joyent.sdc.v6_5.options.CreateServerOptions; /** * Provides synchronous access to Machine. @@ -38,7 +39,7 @@ public interface MachineClient { /** * Lists all machines we have on record for your account. * - * @return an account's associated machine objects. + * @return an account's associated server objects. */ Set listMachines(); @@ -51,4 +52,60 @@ public interface MachineClient { */ Machine getMachine(String id); + /** + * + * @param name + * friendly name for this machine; default is a randomly generated name + * @param packageSDC + * Name of the package to use on provisioning; default is indicated in ListPackages + * @param dataset + * dataset URN; default is indicated in ListDatasets + * @param options + * optional parameters to be passed into the machine creation request + * @return the newly created machine + */ + Machine createMachine(String name, String packageSDC, String dataset, CreateServerOptions... options); + + /** + * Allows you to shut down a machine. + * + * @param id + * the id of the machine to stop + */ + void stopMachine(String id); + + /** + * Allows you to boot up a machine. + * + * @param id + * the id of the machine to start + */ + void startMachine(String id); + + /** + * Allows you to reboot a machine. + * + * @param id + * the id of the machine to reboot + */ + void rebootMachine(String id); + + /** + * Allows you to resize a machine. (Works only for smart machines) + * + * @param id + * the id of the machine to resize + * @param packageSDC + * the package to use for the machine + */ + void resizeMachine(String id, String packageSDC); + + /** + * Allows you to delete a machine (the machine must be stopped before it can be deleted). + * + * @param id + * the id of the machine to delete + */ + void deleteMachine(String id); + } \ No newline at end of file diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/options/CreateServerOptions.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/options/CreateServerOptions.java new file mode 100644 index 0000000000..631b69fdbc --- /dev/null +++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/options/CreateServerOptions.java @@ -0,0 +1,151 @@ +/** + * 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.joyent.sdc.v6_5.options; + +import static com.google.common.base.Objects.equal; +import static com.google.common.base.Objects.toStringHelper; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Map; + +import javax.inject.Inject; + +import org.jclouds.http.HttpRequest; +import org.jclouds.rest.MapBinder; +import org.jclouds.rest.binders.BindToJsonPayload; + +import com.google.common.base.Objects; +import com.google.common.base.Objects.ToStringHelper; +import com.google.common.collect.ImmutableMap; +import com.google.gson.annotations.SerializedName; + +/** + * + * @author Gerald Pereira + * + */ +public class CreateServerOptions implements MapBinder { + @Inject + private BindToJsonPayload jsonBinder; + + private Map metadata = ImmutableMap.of(); + private Map tag = ImmutableMap.of(); + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object instanceof CreateServerOptions) { + final CreateServerOptions other = CreateServerOptions.class.cast(object); + return equal(tag, tag) && equal(metadata, other.metadata); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hashCode(tag, metadata); + } + + protected ToStringHelper string() { + return toStringHelper("").add("metadata", metadata).add("tag", tag); + } + + @Override + public String toString() { + return string().toString(); + } + + @Override + public R bindToRequest(R request, Object input) { + return jsonBinder.bindToRequest(request, input); + } + + @Override + public R bindToRequest(R request, Map postParams) { + MachineRequest machine = new MachineRequest(checkNotNull(postParams.get("name"), "name parameter not present") + .toString(), checkNotNull(postParams.get("package"), "package parameter not present").toString(), + checkNotNull(postParams.get("dataset"), "dataset parameter not present").toString()); + + if (metadata.size() > 0) + machine.metadata = metadata; + if (tag.size() > 0) + machine.tag = tag; + + return bindToRequest(request, machine); + } + + /** + * An arbitrary set of metadata key/value pairs can be set at provision time, but they must be + * prefixed with "metadata." + */ + public CreateServerOptions metadata(Map metadata) { + checkNotNull(metadata, "metadata"); + this.metadata = ImmutableMap.copyOf(metadata); + return this; + } + + /** + * An arbitrary set of tags can be set at provision time, but they must be prefixed with "tag." + */ + public CreateServerOptions tag(Map tag) { + checkNotNull(tag, "tag"); + this.tag = ImmutableMap.copyOf(tag); + return this; + } + + @SuppressWarnings("unused") + private class MachineRequest { + final String name; + @SerializedName("package") + final String packageSDC; + final String dataset; + Map metadata; + Map tag; + + private MachineRequest(String name, String packageSDC, String dataset) { + this.name = name; + this.packageSDC = packageSDC; + this.dataset = dataset; + } + + } + + public static class Builder { + + /** + * @see CreateServerOptions#metadata(Map) + */ + public static CreateServerOptions metadata(Map metadata) { + CreateServerOptions options = new CreateServerOptions(); + return options.metadata(metadata); + } + + /** + * @see CreateServerOptions#tag(Map) + */ + public static CreateServerOptions tag(Map tag) { + CreateServerOptions options = new CreateServerOptions(); + return options.tag(tag); + } + } + +} diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientExpectTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientExpectTest.java index 589dcd90bb..20575f1a32 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientExpectTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientExpectTest.java @@ -37,23 +37,19 @@ import com.google.common.collect.ImmutableSet; */ @Test(groups = "unit", testName = "DatasetClientExpectTest") public class DatasetClientExpectTest extends BaseSDCClientExpectTest { - HttpRequest listDatasets = HttpRequest - .builder() - .method("GET") - .endpoint(URI.create("https://api.joyentcloud.com/my/datasets")) - .headers( - ImmutableMultimap. builder().put("X-Api-Version", "~6.5") - .put("Accept", "application/json").put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==") - .build()).build(); + HttpRequest listDatasets = HttpRequest.builder().method("GET").endpoint( + URI.create("https://api.joyentcloud.com/my/datasets")).headers( + ImmutableMultimap. builder().put("X-Api-Version", "~6.5").put("Accept", "application/json") + .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()).build(); public void testListDatasetsWhenResponseIs2xx() { - HttpResponse listDatasetsResponse = HttpResponse.builder().statusCode(200) - .payload(payloadFromResource("/dataset_list.json")).build(); + HttpResponse listDatasetsResponse = HttpResponse.builder().statusCode(200).payload( + payloadFromResource("/dataset_list.json")).build(); SDCClient clientWhenDatasetsExists = requestSendsResponse(listDatasets, listDatasetsResponse); assertEquals(clientWhenDatasetsExists.getDatasetClient().listDatasets().toString(), new ParseDatasetListTest() - .expected().toString()); + .expected().toString()); } public void testListDatasetsWhenResponseIs404() { @@ -63,4 +59,15 @@ public class DatasetClientExpectTest extends BaseSDCClientExpectTest { assertEquals(listDatasetsWhenNone.getDatasetClient().listDatasets(), ImmutableSet.of()); } + + // [id=e4cd7b9e-4330-11e1-81cf-3bb50a972bda, name=centos-6, type=VIRTUALMACHINE, version=1.0.1, + // urn=sdc:sdc:centos-6:1.0.1, default=false, created=Mon Feb 13 07:30:33 CET 2012], + // [id=e4cd7b9e-4330-11e1-81cf-3bb50a972bda, name=centos-6, type=VIRTUALMACHINE, version=1.0.1, + // urn=sdc:sdc:centos-6:1.0.1, default=false, created=Mon Feb 13 07:30:33 CET 2012], + // + // [id=e62c30b4-cdda-11e0-9dd4-af4d032032e3, name=nodejs, type=SMARTMACHINE, version=1.2.3, + // urn=sdc:sdc:nodejs:1.2.3, default=false, created=Thu Sep 15 10:15:29 CEST 2011]] + // + // [id=e62c30b4-cdda-11e0-9dd4-af4d032032e3, name=nodejs, type=SMARTMACHINE, version=1.2.3, + // urn=sdc:sdc:nodejs:1.2.3, default=false, created=Thu Sep 15 10:15:29 CEST 2011]] but got } diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientExpectTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientExpectTest.java index 42ad7528bf..ef7d79f849 100644 --- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientExpectTest.java +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientExpectTest.java @@ -26,6 +26,7 @@ import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; import org.jclouds.joyent.sdc.v6_5.SDCClient; import org.jclouds.joyent.sdc.v6_5.internal.BaseSDCClientExpectTest; +import org.jclouds.joyent.sdc.v6_5.parse.ParseCreatedMachineTest; import org.jclouds.joyent.sdc.v6_5.parse.ParseMachineListTest; import org.testng.annotations.Test; @@ -37,18 +38,14 @@ import com.google.common.collect.ImmutableSet; */ @Test(groups = "unit", testName = "MachineClientExpectTest") public class MachineClientExpectTest extends BaseSDCClientExpectTest { - HttpRequest listMachines = HttpRequest - .builder() - .method("GET") - .endpoint(URI.create("https://api.joyentcloud.com/my/machines")) - .headers( - ImmutableMultimap. builder().put("X-Api-Version", "~6.5") - .put("Accept", "application/json").put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==") - .build()).build(); + HttpRequest listMachines = HttpRequest.builder().method("GET").endpoint( + URI.create("https://api.joyentcloud.com/my/machines")).headers( + ImmutableMultimap. builder().put("X-Api-Version", "~6.5").put("Accept", "application/json") + .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()).build(); public void testListMachinesWhenResponseIs2xx() { - HttpResponse listMachinesResponse = HttpResponse.builder().statusCode(200) - .payload(payloadFromResource("/machine_list.json")).build(); + HttpResponse listMachinesResponse = HttpResponse.builder().statusCode(200).payload( + payloadFromResource("/machine_list.json")).build(); SDCClient clientWhenMachinesExists = requestSendsResponse(listMachines, listMachinesResponse); @@ -62,4 +59,27 @@ public class MachineClientExpectTest extends BaseSDCClientExpectTest { assertEquals(listMachinesWhenNone.getMachineClient().listMachines(), ImmutableSet.of()); } + + public void testCreateMachineWhenResponseIs202() throws Exception { + HttpRequest createMachine = HttpRequest + .builder() + .method("POST") + .endpoint(URI.create("https://api.joyentcloud.com/my/machines")) + .headers( + ImmutableMultimap. builder().put("X-Api-Version", "~6.5").put("Accept", + "application/json").put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()) + .payload( + payloadFromStringWithContentType( + "{\"name\":\"testJClouds\",\"package\":\"Small 1GB\",\"dataset\":\"sdc:sdc:centos-5.7:1.2.1\"}", + "application/json")).build(); + + HttpResponse createMachineResponse = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted") + .payload(payloadFromResourceWithContentType("/new_machine.json", "application/json; charset=UTF-8")) + .build(); + + SDCClient clientWithNewMachine = requestSendsResponse(createMachine, createMachineResponse); + + assertEquals(clientWithNewMachine.getMachineClient().createMachine("testJClouds", "Small 1GB", + "sdc:sdc:centos-5.7:1.2.1").toString(), new ParseCreatedMachineTest().expected().toString()); + } } diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseCreatedMachineTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseCreatedMachineTest.java new file mode 100644 index 0000000000..8920680586 --- /dev/null +++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseCreatedMachineTest.java @@ -0,0 +1,78 @@ +/** + * 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.joyent.sdc.v6_5.parse; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.joyent.sdc.v6_5.config.SDCParserModule; +import org.jclouds.joyent.sdc.v6_5.domain.Machine; +import org.jclouds.joyent.sdc.v6_5.domain.Type; +import org.jclouds.json.BaseItemParserTest; +import org.jclouds.json.config.GsonModule; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * @author Gerald Pereira + */ +@Test(groups = "unit", testName = "ParseServerTest") +public class ParseCreatedMachineTest extends BaseItemParserTest { + + @Override + public String resource() { + return "/new_machine.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Machine expected() { + return Machine + .builder() + .id("94eba336-ecb7-49f5-8a27-52f5e4dd57a1") + .name("testJClouds") + .type(Type.VIRTUALMACHINE) + .state(Machine.State.STOPPED) + .dataset("sdc:sdc:centos-5.7:1.2.1") + .ips(ImmutableSet. builder().add("37.153.96.62").add("10.224.0.63").build()) + .memorySizeMb(1024) + .diskSizeGb(61440) + .metadata(ImmutableMap. builder().put("root_authorized_keys","ssh-rsa XXXXXX== test@xxxx.ovh.net\n").build()) + .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-09T13:32:46+00:00")) + .updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-11T08:44:53+00:00")) + .build(); + } + + protected Injector injector() { + return Guice.createInjector(new SDCParserModule(), new GsonModule() { + + @Override + protected void configure() { + bind(DateAdapter.class).to(Iso8601DateAdapter.class); + super.configure(); + } + + }); + } +} diff --git a/labs/joyent-sdc/src/test/resources/machine.json b/labs/joyent-sdc/src/test/resources/machine.json index 520e72c7e3..fd56f2f794 100644 --- a/labs/joyent-sdc/src/test/resources/machine.json +++ b/labs/joyent-sdc/src/test/resources/machine.json @@ -1 +1 @@ -{"id":"94eba336-ecb7-49f5-8a27-52f5e4dd57a1","name":"testJClouds","type":"virtualmachine","state":"stopped","dataset":"sdc:sdc:centos-5.7:1.2.1","ips":["37.153.96.62","10.224.0.63"],"memory":1024,"disk":61440,"metadata":{"root_authorized_keys":"ssh-rsa XXXXXX== test@xxxx.ovh.net"},"created":"2012-05-09T13:32:46+00:00","updated":"2012-05-11T08:44:53+00:00"} \ No newline at end of file +{"id":"94eba336-ecb7-49f5-8a27-52f5e4dd57a1","name":"testJClouds","type":"virtualmachine","state":"stopped","dataset":"sdc:sdc:centos-5.7:1.2.1","ips":["37.153.96.62","10.224.0.63"],"memory":1024,"disk":61440,"metadata":{"root_authorized_keys":"ssh-rsa XXXXXX== test@xxxx.ovh.net"},"created":"2012-05-09T13:32:46+00:00","updated":"2012-05-11T08:44:53+00:00"} diff --git a/labs/joyent-sdc/src/test/resources/new_machine.json b/labs/joyent-sdc/src/test/resources/new_machine.json new file mode 100644 index 0000000000..fd56f2f794 --- /dev/null +++ b/labs/joyent-sdc/src/test/resources/new_machine.json @@ -0,0 +1 @@ +{"id":"94eba336-ecb7-49f5-8a27-52f5e4dd57a1","name":"testJClouds","type":"virtualmachine","state":"stopped","dataset":"sdc:sdc:centos-5.7:1.2.1","ips":["37.153.96.62","10.224.0.63"],"memory":1024,"disk":61440,"metadata":{"root_authorized_keys":"ssh-rsa XXXXXX== test@xxxx.ovh.net"},"created":"2012-05-09T13:32:46+00:00","updated":"2012-05-11T08:44:53+00:00"} From 96e272a91dfe8fa5e4cfb796e00f525ee2d539d0 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Wed, 16 May 2012 14:51:16 -0700 Subject: [PATCH 108/148] Issue 925: added default ctors so that gson can work w/o using sun.misc.Unsafe --- .../openstack/nova/v1_1/domain/Address.java | 12 +++- .../openstack/nova/v1_1/domain/Extension.java | 16 +++-- .../openstack/nova/v1_1/domain/Flavor.java | 20 +++--- .../nova/v1_1/domain/FloatingIP.java | 8 ++- .../openstack/nova/v1_1/domain/Host.java | 12 +++- .../nova/v1_1/domain/HostAggregate.java | 36 ++++------ .../nova/v1_1/domain/HostResourceUsage.java | 18 +++-- .../openstack/nova/v1_1/domain/Image.java | 28 +++++--- .../openstack/nova/v1_1/domain/Ingress.java | 14 ++-- .../openstack/nova/v1_1/domain/KeyPair.java | 18 +++-- .../openstack/nova/v1_1/domain/Quotas.java | 34 ++++++---- .../nova/v1_1/domain/SecurityGroup.java | 18 +++-- .../nova/v1_1/domain/SecurityGroupRule.java | 20 +++--- .../openstack/nova/v1_1/domain/Server.java | 68 +++++++------------ .../nova/v1_1/domain/ServerCreated.java | 13 ++-- .../v1_1/domain/ServerExtendedAttributes.java | 12 +++- .../v1_1/domain/ServerExtendedStatus.java | 20 +++--- .../v1_1/domain/ServerWithSecurityGroups.java | 10 ++- .../nova/v1_1/domain/SimpleServerUsage.java | 30 ++++---- .../nova/v1_1/domain/SimpleTenantUsage.java | 24 ++++--- .../nova/v1_1/domain/TenantIdAndName.java | 10 ++- .../nova/v1_1/domain/VirtualInterface.java | 10 ++- .../openstack/nova/v1_1/domain/Volume.java | 28 +++++--- .../nova/v1_1/domain/VolumeAttachment.java | 14 ++-- .../nova/v1_1/domain/VolumeSnapshot.java | 20 ++++-- .../nova/v1_1/domain/VolumeType.java | 15 ++-- .../domain/AuthenticationResponse.java | 8 +-- .../org/jclouds/openstack/domain/Link.java | 12 +++- .../jclouds/openstack/domain/Resource.java | 20 +++--- .../openstack/keystone/v1_1/domain/Auth.java | 11 ++- .../keystone/v1_1/domain/Endpoint.java | 14 ++-- .../openstack/keystone/v1_1/domain/Token.java | 10 ++- .../keystone/v2_0/domain/Access.java | 13 +++- .../keystone/v2_0/domain/ApiMetadata.java | 12 +++- .../keystone/v2_0/domain/Endpoint.java | 19 ++++-- .../keystone/v2_0/domain/MediaType.java | 12 +++- .../openstack/keystone/v2_0/domain/Role.java | 16 +++-- .../keystone/v2_0/domain/Service.java | 14 ++-- .../keystone/v2_0/domain/Tenant.java | 14 ++-- .../openstack/keystone/v2_0/domain/Token.java | 14 ++-- .../openstack/keystone/v2_0/domain/User.java | 20 +++--- .../swift/v1/domain/AccountMetadata.java | 10 ++- .../swift/v1/domain/ContainerMetadata.java | 12 +++- .../domain/ContainerCDNMetadata.java | 9 ++- 44 files changed, 474 insertions(+), 294 deletions(-) diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Address.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Address.java index 7faa0b49e7..fb6b3000aa 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Address.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Address.java @@ -75,9 +75,15 @@ public class Address { return addr(from.getAddr()).version(from.getVersion()); } } - - protected final String addr; - protected final int version; + + protected Address() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + protected String addr; + protected int version; public Address(String addr, int version) { this.addr = addr; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Extension.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Extension.java index 6291c495c7..25d2a922d0 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Extension.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Extension.java @@ -105,11 +105,17 @@ public class Extension extends Resource { return this; } } - - private final URI namespace; - private final String alias; - private final Date updated; - private final String description; + + protected Extension() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + private URI namespace; + private String alias; + private Date updated; + private String description; protected Extension(Builder builder) { super(builder); diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Flavor.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Flavor.java index 6462539625..9aa94c14cc 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Flavor.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Flavor.java @@ -122,15 +122,21 @@ public class Flavor extends Resource { return this; } } - + + protected Flavor() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + private int ram; private int disk; private int vcpus; - private final Optional swap; + private Optional swap = Optional.absent(); @SerializedName("rxtx_factor") - private final Optional rxtxFactor; + private Optional rxtxFactor = Optional.absent(); @SerializedName("OS-FLV-EXT-DATA:ephemeral") - private final Optional ephemeral; + private Optional ephemeral = Optional.absent(); protected Flavor(Builder builder) { super(builder); @@ -142,12 +148,6 @@ public class Flavor extends Resource { this.ephemeral = Optional.fromNullable(builder.ephemeral); } - protected Flavor() { - this.swap = Optional.absent(); - this.rxtxFactor = Optional.absent(); - this.ephemeral = Optional.absent(); - } - public int getRam() { return this.ram; } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/FloatingIP.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/FloatingIP.java index fad8643619..c69bd6cf5c 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/FloatingIP.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/FloatingIP.java @@ -76,7 +76,13 @@ public class FloatingIP implements Comparable { } } - + + protected FloatingIP() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + private String id; private String ip; @SerializedName("fixed_ip") diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Host.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Host.java index 2a4fedbf7b..6ed99745b2 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Host.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Host.java @@ -72,10 +72,16 @@ public class Host { return this; } } - + + protected Host() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + @SerializedName(value="host_name") - private final String name; - private final String service; + private String name; + private String service; protected Host(Builder builder) { this.name = builder.name; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/HostAggregate.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/HostAggregate.java index 6017843fe3..b3e018ce52 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/HostAggregate.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/HostAggregate.java @@ -154,19 +154,25 @@ public class HostAggregate { return this; } } - - private final String id; - private final String name; + + protected HostAggregate() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + private String id; + private String name; @SerializedName(value = "availability_zone") - private final String availabilityZone; - private final Set hosts; + private String availabilityZone; + private Set hosts = ImmutableSet.of(); @SerializedName(value = "operational_state") - private final String state; + private String state; @SerializedName(value = "created_at") - private final Date created; + private Date created; @SerializedName(value = "updated_at") - private final Optional updated; - private final Map metadata; + private Optional updated = Optional.absent(); + private Map metadata = ImmutableMap.of(); protected HostAggregate(Builder builder) { this.id = checkNotNull(builder.id, "id"); @@ -179,18 +185,6 @@ public class HostAggregate { this.metadata = ImmutableMap.copyOf(checkNotNull(builder.metadata, "metadata")); } - // Ensure GSON parsed objects don't have null collections or optionals - protected HostAggregate() { - this.id = null; - this.name = null; - this.availabilityZone = null; - this.hosts = ImmutableSet.of(); - this.state = null; - this.created = null; - this.updated = Optional.absent(); - this.metadata = ImmutableMap.of(); - } - public String getId() { return this.id; } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/HostResourceUsage.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/HostResourceUsage.java index e8dbbd1301..649549fe54 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/HostResourceUsage.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/HostResourceUsage.java @@ -95,14 +95,20 @@ public class HostResourceUsage { return this; } } - - private final String host; - private final String project; + + protected HostResourceUsage() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + private String host; + private String project; @SerializedName(value="memory_mb") - private final int memoryMb; - private final int cpu; + private int memoryMb; + private int cpu; @SerializedName(value="disk_gb") - private final int diskGb; + private int diskGb; protected HostResourceUsage(Builder builder) { this.host = checkNotNull(builder.host, "host"); diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Image.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Image.java index 28196bf454..dc374b8b57 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Image.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Image.java @@ -191,19 +191,25 @@ public class Image extends Resource { return this; } } - - private final Date updated; - private final Date created; + + protected Image() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + private Date updated; + private Date created; @SerializedName("tenant_id") - private final String tenantId; + private String tenantId; @SerializedName("user_id") - private final String userId; - private final Status status; - private final int progress; - private final int minDisk; - private final int minRam; - private final Resource server; - private final Map metadata; + private String userId; + private Status status; + private int progress; + private int minDisk; + private int minRam; + private Resource server; + private Map metadata = ImmutableMap.of(); protected Image(Builder builder) { super(builder); diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Ingress.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Ingress.java index 1f76d0e817..1163124a1d 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Ingress.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Ingress.java @@ -73,13 +73,19 @@ public class Ingress { return new Ingress(ipProtocol, fromPort, toPort); } } - + + protected Ingress() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + @SerializedName(value = "ip_protocol") - protected final IpProtocol ipProtocol; + protected IpProtocol ipProtocol; @SerializedName(value = "from_port") - protected final int fromPort; + protected int fromPort; @SerializedName(value = "to_port") - protected final int toPort; + protected int toPort; protected Ingress(IpProtocol ipProtocol, int fromPort, int toPort) { this.fromPort = fromPort; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/KeyPair.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/KeyPair.java index 46fa9afc0f..b979b205ce 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/KeyPair.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/KeyPair.java @@ -76,15 +76,21 @@ public class KeyPair implements Comparable { } } - + + protected KeyPair() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + @SerializedName("public_key") - String publicKey; + private String publicKey; @SerializedName("private_key") - String privateKey; + private String privateKey; @SerializedName("user_id") - String userId; - String name; - String fingerprint; + private String userId; + private String name; + private String fingerprint; protected KeyPair(String publicKey, String privateKey, @Nullable String userId, String name, String fingerprint) { this.publicKey = publicKey; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Quotas.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Quotas.java index cb44d57cca..9228f4d896 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Quotas.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Quotas.java @@ -187,28 +187,34 @@ public class Quotas { return this; } } - + + protected Quotas() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + @SerializedName("id") - private final String id; + private String id; @SerializedName("metadata_items") - private final int metadataItems; + private int metadataItems; @SerializedName("injected_file_content_bytes") - private final int injectedFileContentBytes; - private final int volumes; - private final int gigabytes; - private final int ram; + private int injectedFileContentBytes; + private int volumes; + private int gigabytes; + private int ram; @SerializedName("floating_ips") - private final int floatingIps; - private final int instances; + private int floatingIps; + private int instances; @SerializedName("injected_files") - private final int injectedFiles; - private final int cores; + private int injectedFiles; + private int cores; @SerializedName("security_groups") - private final int securityGroups; + private int securityGroups; @SerializedName("security_group_rules") - private final int securityGroupRules; + private int securityGroupRules; @SerializedName("key_pairs") - private final int keyPairs; + private int keyPairs; protected Quotas(Builder builder) { this.id = checkNotNull(builder.id, "id"); diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/SecurityGroup.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/SecurityGroup.java index 1b1665bc4b..f1b635501b 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/SecurityGroup.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/SecurityGroup.java @@ -102,13 +102,19 @@ public class SecurityGroup { } } - - protected final String id; + + protected SecurityGroup() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + protected String id; @SerializedName("tenant_id") - protected final String tenantId; - protected final String name; - protected final String description; - protected final Set rules; + protected String tenantId; + protected String name; + protected String description; + protected Set rules = ImmutableSet.of(); protected SecurityGroup(String id, String tenantId, @Nullable String name, @Nullable String description, Set rules) { diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/SecurityGroupRule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/SecurityGroupRule.java index a312a01f4c..d1895fc79d 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/SecurityGroupRule.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/SecurityGroupRule.java @@ -95,13 +95,19 @@ public class SecurityGroupRule extends Ingress { } } - - protected final String id; - - protected final TenantIdAndName group; - + + protected SecurityGroupRule() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + protected String id; + protected TenantIdAndName group; @SerializedName(value = "parent_group_id") - protected final String parentGroupId; + protected String parentGroupId; + @SerializedName(value = "ip_range") + protected Cidr ipRange; // type to get around unnecessary structure private static class Cidr extends ForwardingObject { @@ -117,8 +123,6 @@ public class SecurityGroupRule extends Ingress { } } - @SerializedName(value = "ip_range") - protected final Cidr ipRange; protected SecurityGroupRule(IpProtocol ipProtocol, int fromPort, int toPort, String id, String parentGroupId, @Nullable TenantIdAndName group, @Nullable String ipRange) { diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Server.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Server.java index 9594e0619d..0cf4b8a6a2 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Server.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Server.java @@ -34,7 +34,6 @@ 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; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Maps; @@ -302,38 +301,41 @@ public class Server extends Resource { } } - private final String uuid; + protected Server() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + private String uuid; @SerializedName("tenant_id") - private final String tenantId; + private String tenantId; @SerializedName("user_id") - 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; + private String userId; + private Date updated; + private Date created; + private String hostId; + private String accessIPv4; + private String accessIPv6; + private Status status; + private Resource image; + private Resource flavor; @SerializedName("key_name") - private final String keyName; + private String keyName; @SerializedName("config_drive") - private final String configDrive; + private String configDrive; // TODO: get gson multimap adapter! - private final Map> addresses; - private final Map metadata; - + private Map> addresses = ImmutableMap.of(); + private Map metadata = ImmutableMap.of(); // Extended status extension // @Prefixed("OS-EXT-STS:") - private final Optional extendedStatus; - + private Optional extendedStatus = Optional.absent(); // Extended server attributes extension // @Prefixed("OS-EXT-SRV-ATTR:") - private final Optional extendedAttributes; - + private Optional extendedAttributes = Optional.absent(); // Disk Config extension @SerializedName("OS-DCF:diskConfig") - private final Optional diskConfig; + private Optional diskConfig = Optional.absent(); protected Server(Builder builder) { super(builder); @@ -357,28 +359,6 @@ public class Server extends Resource { this.diskConfig = builder.diskConfig == null ? Optional.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(); - } - /** * only present until the id is in uuid form * diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerCreated.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerCreated.java index 607b2f3ae4..6ca7505385 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerCreated.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerCreated.java @@ -18,7 +18,6 @@ */ 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; @@ -68,17 +67,19 @@ public class ServerCreated extends Resource { } } - private final String adminPass; + protected ServerCreated() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + private 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. */ diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedAttributes.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedAttributes.java index 9dcd471e8b..bf43cfeac0 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedAttributes.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedAttributes.java @@ -93,12 +93,18 @@ public class ServerExtendedAttributes { } } + protected ServerExtendedAttributes() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + @SerializedName(value=PREFIX + "instance_name") - private final String instanceName; + private String instanceName; @SerializedName(value=PREFIX + "host") - private final String hostName; + private String hostName; @SerializedName(value=PREFIX + "hypervisor_hostname") - private final String hypervisorHostName; + private String hypervisorHostName; protected ServerExtendedAttributes(Builder builder) { this.instanceName = builder.instanceName; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedStatus.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedStatus.java index 02908d4a66..1d64089d42 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedStatus.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerExtendedStatus.java @@ -94,25 +94,25 @@ public class ServerExtendedStatus { return this; } } - + + protected ServerExtendedStatus() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + @SerializedName(value=PREFIX + "task_state") - private final String taskState; + private String taskState; @SerializedName(value=PREFIX + "vm_state") - private final String vmState; + private String vmState; @SerializedName(value=PREFIX + "power_state") - private final int powerState; + private int powerState = Integer.MIN_VALUE; 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() { diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerWithSecurityGroups.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerWithSecurityGroups.java index bfe1d3ca83..fb294a4cfd 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerWithSecurityGroups.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/ServerWithSecurityGroups.java @@ -73,9 +73,15 @@ public class ServerWithSecurityGroups extends Server { return this; } } - + + protected ServerWithSecurityGroups() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + @SerializedName(value="security_groups") - private final Set securityGroupNames; + private Set securityGroupNames = ImmutableSet.of(); protected ServerWithSecurityGroups(Builder builder) { super(builder); diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/SimpleServerUsage.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/SimpleServerUsage.java index c199924b61..1fd7232fec 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/SimpleServerUsage.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/SimpleServerUsage.java @@ -159,27 +159,33 @@ public class SimpleServerUsage { return this; } } - + + protected SimpleServerUsage() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + @SerializedName("name") - private final String instanceName; - private final double hours; + private String instanceName; + private double hours; @SerializedName("memory_mb") - private final double flavorMemoryMb; + private double flavorMemoryMb; @SerializedName("local_gb") - private final double flavorLocalGb; + private double flavorLocalGb; @SerializedName("vcpus") - private final double flavorVcpus; + private double flavorVcpus; @SerializedName("tenant_id") - private final String tenantId; + private String tenantId; @SerializedName("flavor") - private final String flavorName; + private String flavorName; @SerializedName("started_at") - private final Date instanceCreated; + private Date instanceCreated; @SerializedName("ended_at") - private final Date instanceTerminiated; + private Date instanceTerminiated; @SerializedName("state") - private final Status instanceStatus; - private final long uptime; + private Status instanceStatus; + private long uptime; private SimpleServerUsage(Builder builder) { this.instanceName = checkNotNull(builder.instanceName, "instanceName"); diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/SimpleTenantUsage.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/SimpleTenantUsage.java index b4b20ddd2e..882177ae5d 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/SimpleTenantUsage.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/SimpleTenantUsage.java @@ -123,21 +123,27 @@ public class SimpleTenantUsage { return this; } } - + + protected SimpleTenantUsage() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + @SerializedName("tenant_id") - private final String tenantId; + private String tenantId; @SerializedName("total_local_gb_usage") - private final double totalLocalGbUsage; + private double totalLocalGbUsage; @SerializedName("total_vcpus_usage") - private final double totalVcpusUsage; + private double totalVcpusUsage; @SerializedName("total_memory_mb_usage") - private final double totalMemoryMbUsage; + private double totalMemoryMbUsage; @SerializedName("total_hours") - private final double totalHours; - private final Date start; - private final Date stop; + private double totalHours; + private Date start; + private Date stop; @SerializedName("server_usages") - private final Set serverUsages; + private Set serverUsages = ImmutableSet.of(); private SimpleTenantUsage(Builder builder) { this.tenantId = builder.tenantId; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/TenantIdAndName.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/TenantIdAndName.java index d203dfd34f..f2019269df 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/TenantIdAndName.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/TenantIdAndName.java @@ -31,9 +31,15 @@ import com.google.gson.annotations.SerializedName; */ public class TenantIdAndName { + protected TenantIdAndName() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + @SerializedName("tenant_id") - protected final String tenantId; - protected final String name; + protected String tenantId; + protected String name; public TenantIdAndName(String tenantId, String name) { this.tenantId = checkNotNull(tenantId, "tenantId"); diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/VirtualInterface.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/VirtualInterface.java index b35dbea7af..d333611ff4 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/VirtualInterface.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/VirtualInterface.java @@ -82,9 +82,15 @@ public class VirtualInterface { } } - private final String id; + protected VirtualInterface() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + private String id; @SerializedName(value="mac_address") - private final String macAddress; + private String macAddress; protected VirtualInterface(Builder builder) { this.id = checkNotNull(builder.id, "id"); diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Volume.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Volume.java index ad581a650c..5f299fb401 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Volume.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/Volume.java @@ -177,21 +177,27 @@ public class Volume { } } - private final String id; - private final Status status; - private final int size; + protected Volume() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + private String id; + private Status status; + private int size; @SerializedName(value="availabilityZone") - private final String zone; + private String zone; @SerializedName(value="createdAt") - private final Date created; - private final Set attachments; - private final String volumeType; - private final String snapshotId; + private Date created; + private Set attachments = ImmutableSet.of(); + private String volumeType; + private String snapshotId; @SerializedName(value="displayName") - private final String name; + private String name; @SerializedName(value="displayDescription") - private final String description; - private final Map metadata; + private String description; + private Map metadata = ImmutableMap.of(); protected Volume(Builder builder) { this.id = builder.id; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/VolumeAttachment.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/VolumeAttachment.java index bc33a0ba63..12b48c338d 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/VolumeAttachment.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/VolumeAttachment.java @@ -92,10 +92,16 @@ public class VolumeAttachment { } } - private final String id; - private final String volumeId; - private final String serverId; - private final String device; + protected VolumeAttachment() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + private String id; + private String volumeId; + private String serverId; + private String device; protected VolumeAttachment(Builder builder) { this.id = checkNotNull(builder.id, "id"); diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/VolumeSnapshot.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/VolumeSnapshot.java index d79d3d3231..8f0754da7d 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/VolumeSnapshot.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/VolumeSnapshot.java @@ -119,16 +119,22 @@ public class VolumeSnapshot { } } - private final String id; - private final String volumeId; - private final Volume.Status status; - private final int size; + protected VolumeSnapshot() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + private String id; + private String volumeId; + private Volume.Status status; + private int size; @SerializedName(value="createdAt") - private final Date created; + private Date created; @SerializedName(value="displayName") - private final String name; + private String name; @SerializedName(value="displayDescription") - private final String description; + private String description; protected VolumeSnapshot(Builder builder) { this.id = checkNotNull(builder.id, "id"); diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/VolumeType.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/VolumeType.java index c8480cefe5..9fe73ef01e 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/VolumeType.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/domain/VolumeType.java @@ -116,14 +116,20 @@ public class VolumeType { } } + protected VolumeType() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + private String id; private String name; @SerializedName("created_at") private Date created; @SerializedName("updated_at") - private final Optional updated; + private Optional updated = Optional.absent(); @SerializedName(value = "extra_specs") - private final Map extraSpecs; + private Map extraSpecs = ImmutableMap.of(); protected VolumeType(Builder builder) { this.id = checkNotNull(builder.id, "id"); @@ -133,11 +139,6 @@ public class VolumeType { this.updated = Optional.fromNullable(builder.updated); } - protected VolumeType() { - this.updated = Optional.absent(); - this.extraSpecs = ImmutableMap.of(); - } - public String getId() { return this.id; } diff --git a/common/openstack/src/main/java/org/jclouds/openstack/domain/AuthenticationResponse.java b/common/openstack/src/main/java/org/jclouds/openstack/domain/AuthenticationResponse.java index 6311c657f1..5b36ad63d0 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/domain/AuthenticationResponse.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/domain/AuthenticationResponse.java @@ -28,7 +28,7 @@ import com.google.common.collect.ImmutableMap; public class AuthenticationResponse { private final String authToken; - private Map services; + private final Map services; public AuthenticationResponse(String authToken, Map services) { this.authToken = checkNotNull(authToken, "authToken"); @@ -38,11 +38,7 @@ public class AuthenticationResponse { public Map getServices() { return services; } - - public void setEndpoints(Map services) { - this.services = services; - } - + public String getAuthToken() { return authToken; } diff --git a/common/openstack/src/main/java/org/jclouds/openstack/domain/Link.java b/common/openstack/src/main/java/org/jclouds/openstack/domain/Link.java index 1adf14abed..0a7ca38410 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/domain/Link.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/domain/Link.java @@ -133,11 +133,17 @@ public class Link { return relation(from.getRelation()).type(from.getType()).href(from.getHref()); } } + + protected Link() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } @SerializedName("rel") - protected final Relation relation; - protected final String type; - protected final URI href; + protected Relation relation; + protected String type; + protected URI href; protected Link(Relation relation, @Nullable String type, URI href) { this.relation = checkNotNull(relation, "relation"); diff --git a/common/openstack/src/main/java/org/jclouds/openstack/domain/Resource.java b/common/openstack/src/main/java/org/jclouds/openstack/domain/Resource.java index a05e4b55e5..60dceef752 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/domain/Resource.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/domain/Resource.java @@ -105,23 +105,23 @@ public class Resource implements Comparable { return this; } } + + protected Resource() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } - private final String id; - private final String name; - private final Set links; + private String id; + private String name; + private Set links = ImmutableSet.of(); protected Resource(Builder builder) { this.id = checkNotNull(builder.id, "id"); this.name = builder.name; 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 diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/domain/Auth.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/domain/Auth.java index e37ab75ded..396ce78ffa 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/domain/Auth.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/domain/Auth.java @@ -28,6 +28,7 @@ import java.util.Set; import org.jclouds.util.Multimaps2; import com.google.common.base.Objects; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; @@ -78,9 +79,15 @@ public class Auth implements Comparable { } } - protected final Token token; + protected Auth() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + protected Token token; // TODO: get gson multimap adapter! - protected final Map> serviceCatalog; + protected Map> serviceCatalog = ImmutableMap.of(); public Auth(Token token, Multimap serviceCatalog) { this.token = checkNotNull(token, "token"); diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/domain/Endpoint.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/domain/Endpoint.java index 7486b3198b..3ab554706d 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/domain/Endpoint.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/domain/Endpoint.java @@ -99,10 +99,16 @@ public class Endpoint implements Comparable { } } - protected final boolean v1Default; - protected final String region; - protected final URI publicURL; - protected final URI internalURL; + protected Endpoint() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + protected boolean v1Default; + protected String region; + protected URI publicURL; + protected URI internalURL; protected Endpoint(boolean v1Default, @Nullable String region, @Nullable URI publicURL, @Nullable URI internalURL) { this.v1Default = v1Default; diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/domain/Token.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/domain/Token.java index e8edb38680..4a48cce928 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/domain/Token.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v1_1/domain/Token.java @@ -80,8 +80,14 @@ public class Token implements Comparable { } } - protected final String id; - protected final Date expires; + protected Token() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + protected String id; + protected Date expires; public Token(String id, Date expires) { this.id = checkNotNull(id, "id"); diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Access.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Access.java index 65a93e508c..b6e9e57560 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Access.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Access.java @@ -89,9 +89,16 @@ public class Access implements Comparable { } } - protected final Token token; - protected final User user; - protected final Set serviceCatalog; + + protected Access() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + protected Token token; + protected User user; + protected Set serviceCatalog = ImmutableSet.of(); public Access(Token token, User user, Set serviceCatalog) { this.token = checkNotNull(token, "token"); diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/ApiMetadata.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/ApiMetadata.java index ef4f0bfe48..768ad8c8c5 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/ApiMetadata.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/ApiMetadata.java @@ -94,14 +94,20 @@ public class ApiMetadata extends Resource { return this; } } + + protected ApiMetadata() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } @Nullable - private final String status; + private String status; @Nullable - private final Date updated; + private Date updated; @SerializedName(value="media-types") @Nullable - private final Set mediaTypes; + private Set mediaTypes = ImmutableSet.of(); protected ApiMetadata(Builder builder) { super(builder); diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Endpoint.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Endpoint.java index 0b57843d22..fc3c58e440 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Endpoint.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Endpoint.java @@ -115,19 +115,26 @@ public class Endpoint implements Comparable { from.getInternalURL()).tenantId(from.getTenantId()); } } + + protected Endpoint() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + // renamed half-way through @Deprecated protected String id; - protected final String versionId; - protected final String region; - protected final URI publicURL; - protected final URI internalURL; - protected final URI adminURL; + protected String versionId; + protected String region; + protected URI publicURL; + protected URI internalURL; + protected URI adminURL; // renamed half-way through @Deprecated protected String tenantName; - protected final String tenantId; + protected String tenantId; protected Endpoint(String versionId, String region, @Nullable URI publicURL, @Nullable URI internalURL, @Nullable URI adminURL, @Nullable String tenantId) { diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/MediaType.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/MediaType.java index cb3cf6d071..015b69a358 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/MediaType.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/MediaType.java @@ -59,9 +59,15 @@ public class MediaType { return this.base(in.getBase()).type(in.getType()); } } - - private final String base; - private final String type; + + protected MediaType() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + private String base; + private String type; protected MediaType(Builder builder) { this.base = builder.base; diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Role.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Role.java index 714e143561..3e7342ee31 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Role.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Role.java @@ -96,14 +96,20 @@ public class Role implements Comparable { return id(from.getId()).name(from.getName()).serviceId(from.getServiceId()).tenantId(from.getTenantId()); } } - - protected final String id; - protected final String name; - protected final String serviceId; + + protected Role() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + protected String id; + protected String name; + protected String serviceId; // renamed half-way through @Deprecated protected String tenantName; - protected final String tenantId; + protected String tenantId; protected Role(String id, String name, @Nullable String serviceId, @Nullable String tenantId) { this.id = checkNotNull(id, "id"); diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Service.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Service.java index 6f8d579f74..2da7bab460 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Service.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Service.java @@ -90,10 +90,16 @@ public class Service implements Comparable { return type(from.getType()).name(from.getName()).endpoints(from.getEndpoints()); } } - - protected final String type; - protected final String name; - protected final Set endpoints; + + protected Service() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + protected String type; + protected String name; + protected Set endpoints = ImmutableSet.of(); public Service(String type, String name, Set endpoints) { this.type = checkNotNull(type, "type"); diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Tenant.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Tenant.java index b0efc5365c..d39735074f 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Tenant.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Tenant.java @@ -82,10 +82,16 @@ public class Tenant implements Comparable { return id(from.getId()).name(from.getName()); } } - - protected final String id; - protected final String name; - protected final String description; + + protected Tenant() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + protected String id; + protected String name; + protected String description; protected Tenant(String id, String name, String description) { this.id = checkNotNull(id, "id"); diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Token.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Token.java index ebdfa9ecef..171ed7db4f 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Token.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Token.java @@ -87,10 +87,16 @@ public class Token implements Comparable { return id(from.getId()).expires(from.getExpires()).tenant(from.getTenant()); } } - - protected final String id; - protected final Date expires; - protected final Tenant tenant; + + protected Token() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + protected String id; + protected Date expires; + protected Tenant tenant; public Token(String id, Date expires, Tenant tenant) { this.id = checkNotNull(id, "id"); diff --git a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/User.java b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/User.java index e8d5ff7dcb..b6b19cd859 100644 --- a/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/User.java +++ b/common/openstack/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/User.java @@ -93,22 +93,22 @@ public class User implements Comparable { return id(from.getId()).name(from.getName()).roles(from.getRoles()); } } - - protected final String id; - protected final String name; - protected final Set roles; + + protected User() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + protected String id; + protected String name; + protected Set roles = ImmutableSet.of(); protected User(String id, String name, Set roles) { this.id = checkNotNull(id, "id"); this.name = checkNotNull(name, "name"); this.roles = ImmutableSet.copyOf(checkNotNull(roles, "roles")); } - - protected User() { - id = null; - name = null; - roles = ImmutableSet.of(); - } /** * When providing an ID, it is assumed that the user exists in the current OpenStack deployment diff --git a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/AccountMetadata.java b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/AccountMetadata.java index 61a53d5c4a..18c1801ced 100644 --- a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/AccountMetadata.java +++ b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/AccountMetadata.java @@ -49,8 +49,14 @@ public class AccountMetadata { } } - protected final int containerCount; - protected final long bytesUsed; + protected AccountMetadata() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + protected int containerCount; + protected long bytesUsed; public AccountMetadata(int containerCount, long bytesUsed) { this.containerCount = containerCount; diff --git a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/ContainerMetadata.java b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/ContainerMetadata.java index 2852022535..18e0018ef8 100644 --- a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/ContainerMetadata.java +++ b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/ContainerMetadata.java @@ -65,9 +65,15 @@ public class ContainerMetadata implements Comparable { } } - protected final String name; - protected final int count; - protected final int bytes; + protected ContainerMetadata() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } + + protected String name; + protected int count; + protected int bytes; public ContainerMetadata(String name, int count, int bytes) { this.name = checkNotNull(name, "name"); diff --git a/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/domain/ContainerCDNMetadata.java b/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/domain/ContainerCDNMetadata.java index 14949d6a82..83155befa2 100644 --- a/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/domain/ContainerCDNMetadata.java +++ b/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/domain/ContainerCDNMetadata.java @@ -28,6 +28,12 @@ import com.google.gson.annotations.SerializedName; * */ public class ContainerCDNMetadata implements Comparable { + + protected ContainerCDNMetadata() { + // we want serializers like Gson to work w/o using sun.misc.Unsafe, + // prohibited in GAE. This also implies fields are not final. + // see http://code.google.com/p/jclouds/issues/detail?id=925 + } private String name; private boolean cdn_enabled; @@ -45,9 +51,6 @@ public class ContainerCDNMetadata implements Comparable { this.cdn_uri = cdnUri; } - public ContainerCDNMetadata() { - } - /** * Beware: The container name is not available from HEAD CDN responses and will be null. return * the name of the container to which these CDN settings apply. From 7854d85f13b782582accd99c8d576ed2c5963a69 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Thu, 17 May 2012 00:18:08 -0700 Subject: [PATCH 109/148] Issue 932:transition to multi-threaded google appengine --- .../concurrent/config/DescribedFuture.java | 130 ++++++++++ .../config/DescribingExecutorService.java | 123 ++++++++++ .../config/ExecutorServiceModule.java | 228 +----------------- .../ScheduledExecutorServiceModule.java | 2 - ...llyAndRetryOnThrowableCacheLoaderTest.java | 2 +- .../AsyncGaeHttpCommandExecutorService.java | 2 - ...yncGoogleAppEngineConfigurationModule.java | 22 +- .../CurrentRequestExecutorServiceModule.java | 118 +++++++++ .../GoogleAppEngineConfigurationModule.java | 33 ++- ...yncGoogleAppEngineConfigurationModule.java | 68 ++++++ ...CommandExecutorServiceIntegrationTest.java | 4 +- ...oogleAppEngineConfigurationModuleTest.java | 69 ------ 12 files changed, 506 insertions(+), 295 deletions(-) create mode 100644 core/src/main/java/org/jclouds/concurrent/config/DescribedFuture.java create mode 100644 core/src/main/java/org/jclouds/concurrent/config/DescribingExecutorService.java create mode 100644 drivers/gae/src/main/java/org/jclouds/gae/config/CurrentRequestExecutorServiceModule.java create mode 100644 drivers/gae/src/main/java/org/jclouds/gae/config/MultithreadedAsyncGoogleAppEngineConfigurationModule.java delete mode 100644 drivers/gae/src/test/java/org/jclouds/gae/config/GoogleAppEngineConfigurationModuleTest.java diff --git a/core/src/main/java/org/jclouds/concurrent/config/DescribedFuture.java b/core/src/main/java/org/jclouds/concurrent/config/DescribedFuture.java new file mode 100644 index 0000000000..0fec199d74 --- /dev/null +++ b/core/src/main/java/org/jclouds/concurrent/config/DescribedFuture.java @@ -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.concurrent.config; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class DescribedFuture implements Future { + protected final Future delegate; + private final String description; + private StackTraceElement[] submissionTrace; + + public DescribedFuture(Future delegate, String description, StackTraceElement[] submissionTrace) { + this.delegate = delegate; + this.description = description; + this.submissionTrace = submissionTrace; + } + + @Override + public boolean cancel(boolean arg0) { + return delegate.cancel(arg0); + } + + @Override + public T get() throws InterruptedException, ExecutionException { + try { + return delegate.get(); + } catch (ExecutionException e) { + throw ensureCauseHasSubmissionTrace(e); + } catch (InterruptedException e) { + throw ensureCauseHasSubmissionTrace(e); + } + } + + @Override + public T get(long arg0, TimeUnit arg1) throws InterruptedException, ExecutionException, TimeoutException { + try { + return delegate.get(arg0, arg1); + } catch (ExecutionException e) { + throw ensureCauseHasSubmissionTrace(e); + } catch (InterruptedException e) { + throw ensureCauseHasSubmissionTrace(e); + } catch (TimeoutException e) { + throw ensureCauseHasSubmissionTrace(e); + } + } + + /** This method does the work to ensure _if_ a submission stack trace was provided, + * it is included in the exception. most errors are thrown from the frame of the + * Future.get call, with a cause that took place in the executor's thread. + * We extend the stack trace of that cause with the submission stack trace. + * (An alternative would be to put the stack trace as a root cause, + * at the bottom of the stack, or appended to all traces, or inserted + * after the second cause, etc ... but since we can't change the "Caused by:" + * method in Throwable the compromise made here seems best.) + */ + private ET ensureCauseHasSubmissionTrace(ET e) { + if (submissionTrace==null) return e; + if (e.getCause()==null) { + ExecutionException ee = new ExecutionException("task submitted from the following trace", null); + e.initCause(ee); + return e; + } + Throwable cause = e.getCause(); + StackTraceElement[] causeTrace = cause.getStackTrace(); + boolean causeIncludesSubmissionTrace = submissionTrace.length >= causeTrace.length; + for (int i=0; causeIncludesSubmissionTrace && i List> invokeAll(Collection> tasks) throws InterruptedException { + return delegate.invokeAll(tasks); + } + + @Override + public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) + throws InterruptedException { + return delegate.invokeAll(tasks, timeout, unit); + } + + @Override + public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { + return delegate.invokeAny(tasks); + } + + @Override + public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return delegate.invokeAny(tasks, timeout, unit); + } + + @Override + public boolean isShutdown() { + return delegate.isShutdown(); + } + + @Override + public boolean isTerminated() { + return delegate.isTerminated(); + } + + @Override + public void shutdown() { + delegate.shutdown(); + } + + @Override + public List shutdownNow() { + return delegate.shutdownNow(); + } + + @Override + public Future submit(Callable task) { + return new DescribedFuture(delegate.submit(task), task.toString(), ExecutorServiceModule.getStackTraceHere()); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public Future submit(Runnable task) { + return new DescribedFuture(delegate.submit(task), task.toString(), ExecutorServiceModule.getStackTraceHere()); + } + + @Override + public Future submit(Runnable task, T result) { + return new DescribedFuture(delegate.submit(task, result), task.toString(), ExecutorServiceModule.getStackTraceHere()); + } + + @Override + public void execute(Runnable arg0) { + delegate.execute(arg0); + } + + @Override + public boolean equals(Object obj) { + return delegate.equals(obj); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public String toString() { + return delegate.toString(); + } + +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/concurrent/config/ExecutorServiceModule.java b/core/src/main/java/org/jclouds/concurrent/config/ExecutorServiceModule.java index f9400a121b..706320231a 100644 --- a/core/src/main/java/org/jclouds/concurrent/config/ExecutorServiceModule.java +++ b/core/src/main/java/org/jclouds/concurrent/config/ExecutorServiceModule.java @@ -18,20 +18,14 @@ */ package org.jclouds.concurrent.config; -import static com.google.common.base.Preconditions.checkNotNull; import static org.jclouds.concurrent.DynamicExecutors.newScalingThreadPool; import java.io.Closeable; import java.io.IOException; -import java.util.Collection; import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; +import java.util.concurrent.ThreadFactory; import javax.annotation.Resource; import javax.inject.Inject; @@ -95,14 +89,14 @@ public class ExecutorServiceModule extends AbstractModule { this.ioExecutorFromConstructor = addToStringOnSubmit(checkNotGuavaSameThreadExecutor(ioThreads)); } - static ExecutorService addToStringOnSubmit(ExecutorService executor) { + ExecutorService addToStringOnSubmit(ExecutorService executor) { if (executor != null) { return new DescribingExecutorService(executor); } return executor; } - static ExecutorService checkNotGuavaSameThreadExecutor(ExecutorService executor) { + ExecutorService checkNotGuavaSameThreadExecutor(ExecutorService executor) { // we detect behavior based on the class if (executor != null && !(executor.getClass().isAnnotationPresent(SingleThreaded.class)) && executor.getClass().getSimpleName().indexOf("SameThread") != -1) { @@ -122,205 +116,6 @@ public class ExecutorServiceModule extends AbstractModule { protected void configure() { } - static class DescribingExecutorService implements ExecutorService { - - protected final ExecutorService delegate; - - public DescribingExecutorService(ExecutorService delegate) { - this.delegate = checkNotNull(delegate, "delegate"); - } - - @Override - public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { - return delegate.awaitTermination(timeout, unit); - } - - @Override - public List> invokeAll(Collection> tasks) throws InterruptedException { - return delegate.invokeAll(tasks); - } - - @Override - public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) - throws InterruptedException { - return delegate.invokeAll(tasks, timeout, unit); - } - - @Override - public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { - return delegate.invokeAny(tasks); - } - - @Override - public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - return delegate.invokeAny(tasks, timeout, unit); - } - - @Override - public boolean isShutdown() { - return delegate.isShutdown(); - } - - @Override - public boolean isTerminated() { - return delegate.isTerminated(); - } - - @Override - public void shutdown() { - delegate.shutdown(); - } - - @Override - public List shutdownNow() { - return delegate.shutdownNow(); - } - - @Override - public Future submit(Callable task) { - return new DescribedFuture(delegate.submit(task), task.toString(), getStackTraceHere()); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public Future submit(Runnable task) { - return new DescribedFuture(delegate.submit(task), task.toString(), getStackTraceHere()); - } - - @Override - public Future submit(Runnable task, T result) { - return new DescribedFuture(delegate.submit(task, result), task.toString(), getStackTraceHere()); - } - - @Override - public void execute(Runnable arg0) { - delegate.execute(arg0); - } - - @Override - public boolean equals(Object obj) { - return delegate.equals(obj); - } - - @Override - public int hashCode() { - return delegate.hashCode(); - } - - @Override - public String toString() { - return delegate.toString(); - } - - } - - static class DescribedFuture implements Future { - protected final Future delegate; - private final String description; - private StackTraceElement[] submissionTrace; - - public DescribedFuture(Future delegate, String description, StackTraceElement[] submissionTrace) { - this.delegate = delegate; - this.description = description; - this.submissionTrace = submissionTrace; - } - - @Override - public boolean cancel(boolean arg0) { - return delegate.cancel(arg0); - } - - @Override - public T get() throws InterruptedException, ExecutionException { - try { - return delegate.get(); - } catch (ExecutionException e) { - throw ensureCauseHasSubmissionTrace(e); - } catch (InterruptedException e) { - throw ensureCauseHasSubmissionTrace(e); - } - } - - @Override - public T get(long arg0, TimeUnit arg1) throws InterruptedException, ExecutionException, TimeoutException { - try { - return delegate.get(arg0, arg1); - } catch (ExecutionException e) { - throw ensureCauseHasSubmissionTrace(e); - } catch (InterruptedException e) { - throw ensureCauseHasSubmissionTrace(e); - } catch (TimeoutException e) { - throw ensureCauseHasSubmissionTrace(e); - } - } - - /** This method does the work to ensure _if_ a submission stack trace was provided, - * it is included in the exception. most errors are thrown from the frame of the - * Future.get call, with a cause that took place in the executor's thread. - * We extend the stack trace of that cause with the submission stack trace. - * (An alternative would be to put the stack trace as a root cause, - * at the bottom of the stack, or appended to all traces, or inserted - * after the second cause, etc ... but since we can't change the "Caused by:" - * method in Throwable the compromise made here seems best.) - */ - private ET ensureCauseHasSubmissionTrace(ET e) { - if (submissionTrace==null) return e; - if (e.getCause()==null) { - ExecutionException ee = new ExecutionException("task submitted from the following trace", null); - e.initCause(ee); - return e; - } - Throwable cause = e.getCause(); - StackTraceElement[] causeTrace = cause.getStackTrace(); - boolean causeIncludesSubmissionTrace = submissionTrace.length >= causeTrace.length; - for (int i=0; causeIncludesSubmissionTrace && i mock; diff --git a/drivers/gae/src/main/java/org/jclouds/gae/AsyncGaeHttpCommandExecutorService.java b/drivers/gae/src/main/java/org/jclouds/gae/AsyncGaeHttpCommandExecutorService.java index 26573d9d05..62c2318c27 100644 --- a/drivers/gae/src/main/java/org/jclouds/gae/AsyncGaeHttpCommandExecutorService.java +++ b/drivers/gae/src/main/java/org/jclouds/gae/AsyncGaeHttpCommandExecutorService.java @@ -32,7 +32,6 @@ import javax.inject.Singleton; import org.jclouds.Constants; import org.jclouds.concurrent.Futures; -import org.jclouds.concurrent.SingleThreaded; import org.jclouds.http.HttpCommand; import org.jclouds.http.HttpCommandExecutorService; import org.jclouds.http.HttpRequest; @@ -57,7 +56,6 @@ import com.google.common.util.concurrent.ListenableFuture; * * @author Adrian Cole */ -@SingleThreaded @Singleton public class AsyncGaeHttpCommandExecutorService implements HttpCommandExecutorService { private final ExecutorService service; diff --git a/drivers/gae/src/main/java/org/jclouds/gae/config/AsyncGoogleAppEngineConfigurationModule.java b/drivers/gae/src/main/java/org/jclouds/gae/config/AsyncGoogleAppEngineConfigurationModule.java index 1731be8429..d30552fb11 100644 --- a/drivers/gae/src/main/java/org/jclouds/gae/config/AsyncGoogleAppEngineConfigurationModule.java +++ b/drivers/gae/src/main/java/org/jclouds/gae/config/AsyncGoogleAppEngineConfigurationModule.java @@ -24,6 +24,10 @@ import org.jclouds.gae.AsyncGaeHttpCommandExecutorService; import org.jclouds.http.HttpCommandExecutorService; import org.jclouds.http.config.ConfiguresHttpCommandExecutorService; +import com.google.common.base.Supplier; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.inject.Module; + /** * Configures {@link AsyncGaeHttpCommandExecutorService}. * @@ -33,7 +37,23 @@ import org.jclouds.http.config.ConfiguresHttpCommandExecutorService; @ConfiguresExecutorService @SingleThreaded public class AsyncGoogleAppEngineConfigurationModule extends GoogleAppEngineConfigurationModule { - + public AsyncGoogleAppEngineConfigurationModule() { + super(); + } + + public AsyncGoogleAppEngineConfigurationModule(Module executorServiceModule) { + super(executorServiceModule); + } + + /** + * Used when you are creating multiple contexts in the same app. + * @param memoizedCurrentRequestExecutorService + * @see CurrentRequestExecutorServiceModule#memoizedCurrentRequestExecutorService + */ + public AsyncGoogleAppEngineConfigurationModule(Supplier memoizedCurrentRequestExecutorService) { + super(memoizedCurrentRequestExecutorService); + } + protected void bindHttpCommandExecutorService() { bind(HttpCommandExecutorService.class).to(AsyncGaeHttpCommandExecutorService.class); } diff --git a/drivers/gae/src/main/java/org/jclouds/gae/config/CurrentRequestExecutorServiceModule.java b/drivers/gae/src/main/java/org/jclouds/gae/config/CurrentRequestExecutorServiceModule.java new file mode 100644 index 0000000000..53cad02af1 --- /dev/null +++ b/drivers/gae/src/main/java/org/jclouds/gae/config/CurrentRequestExecutorServiceModule.java @@ -0,0 +1,118 @@ +/** + * 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.gae.config; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.concurrent.DynamicExecutors.newScalingThreadPool; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadFactory; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.Constants; +import org.jclouds.concurrent.config.ConfiguresExecutorService; +import org.jclouds.concurrent.config.DescribingExecutorService; + +import com.google.appengine.api.ThreadManager; +import com.google.apphosting.api.ApiProxy; +import com.google.common.annotations.Beta; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.inject.AbstractModule; +import com.google.inject.Provides; + +/** + * + * @author Adrian Cole + */ +@Beta +@ConfiguresExecutorService +public class CurrentRequestExecutorServiceModule extends AbstractModule { + + private final Supplier memoizedCurrentRequestExecutorService; + + public CurrentRequestExecutorServiceModule() { + this(memoizedCurrentRequestExecutorService()); + } + + /** + * Used when you are creating multiple contexts in the same app. + * + * @param memoizedCurrentRequestExecutorService + * @see #memoizedCurrentRequestExecutorService + */ + public CurrentRequestExecutorServiceModule(Supplier memoizedCurrentRequestExecutorService) { + this.memoizedCurrentRequestExecutorService = memoizedCurrentRequestExecutorService; + } + + /** + * Used when you are creating multiple contexts in the same app. + * + * @param currentRequestExecutorService + * @see #currentRequestExecutorService + */ + public CurrentRequestExecutorServiceModule(ListeningExecutorService currentRequestExecutorService) { + this.memoizedCurrentRequestExecutorService = Suppliers.ofInstance(currentRequestExecutorService); + } + + @Override + protected void configure() { + } + + public static Supplier memoizedCurrentRequestExecutorService() { + return Suppliers.memoize(new Supplier() { + // important that these are lazy bound vs in configure, as GAE may not + // quite be initialized, yet! + @Override + public ListeningExecutorService get() { + return currentRequestExecutorService(); + } + + }); + + } + + public static ListeningExecutorService currentRequestExecutorService() { + ThreadFactory factory = checkNotNull(ThreadManager.currentRequestThreadFactory(), + "ThreadManager.currentRequestThreadFactory()"); + // GAE requests cannot exceed 10 threads per request + int maxThreads = 10; + long keepAlive = ApiProxy.getCurrentEnvironment().getRemainingMillis(); + ExecutorService pool = newScalingThreadPool(0, maxThreads, keepAlive, factory); + return MoreExecutors.listeningDecorator(new DescribingExecutorService(pool)); + } + + @Provides + @Singleton + @Named(Constants.PROPERTY_USER_THREADS) + protected ExecutorService userThreads() { + return memoizedCurrentRequestExecutorService.get(); + } + + @Provides + @Singleton + @Named(Constants.PROPERTY_IO_WORKER_THREADS) + protected ExecutorService ioThreads() { + return memoizedCurrentRequestExecutorService.get(); + } +} diff --git a/drivers/gae/src/main/java/org/jclouds/gae/config/GoogleAppEngineConfigurationModule.java b/drivers/gae/src/main/java/org/jclouds/gae/config/GoogleAppEngineConfigurationModule.java index 85ffc92c72..9374a87e2e 100644 --- a/drivers/gae/src/main/java/org/jclouds/gae/config/GoogleAppEngineConfigurationModule.java +++ b/drivers/gae/src/main/java/org/jclouds/gae/config/GoogleAppEngineConfigurationModule.java @@ -30,6 +30,10 @@ import org.jclouds.http.config.ConfiguresHttpCommandExecutorService; import com.google.appengine.api.urlfetch.URLFetchService; import com.google.appengine.api.urlfetch.URLFetchServiceFactory; +import com.google.common.base.Supplier; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.inject.AbstractModule; +import com.google.inject.Module; import com.google.inject.Provides; /** @@ -40,15 +44,36 @@ import com.google.inject.Provides; @ConfiguresHttpCommandExecutorService @ConfiguresExecutorService @SingleThreaded -public class GoogleAppEngineConfigurationModule extends ExecutorServiceModule { +public class GoogleAppEngineConfigurationModule extends AbstractModule { + private final Module executorServiceModule; public GoogleAppEngineConfigurationModule() { - super(MoreExecutors.sameThreadExecutor(), MoreExecutors.sameThreadExecutor()); + this(new ExecutorServiceModule(MoreExecutors.sameThreadExecutor(), MoreExecutors.sameThreadExecutor())); + } + + /** + * Used when you are creating multiple contexts in the same app. + * + * @param currentRequestExecutorService + * @see CurrentRequestExecutorServiceModule#currentRequestExecutorService + */ + public GoogleAppEngineConfigurationModule(Module executorServiceModule) { + this.executorServiceModule = executorServiceModule; + } + + /** + * Used when you are creating multiple contexts in the same app. + * + * @param memoizedCurrentRequestExecutorService + * @see CurrentRequestExecutorServiceModule#memoizedCurrentRequestExecutorService + */ + public GoogleAppEngineConfigurationModule(Supplier memoizedCurrentRequestExecutorService) { + this.executorServiceModule = new CurrentRequestExecutorServiceModule(memoizedCurrentRequestExecutorService); } @Override protected void configure() { - super.configure(); + install(executorServiceModule); bind(TransformingHttpCommandExecutorService.class).to(TransformingHttpCommandExecutorServiceImpl.class); bindHttpCommandExecutorService(); } @@ -58,7 +83,7 @@ public class GoogleAppEngineConfigurationModule extends ExecutorServiceModule { } @Provides - URLFetchService provideURLFetchService() { + protected URLFetchService provideURLFetchService() { return URLFetchServiceFactory.getURLFetchService(); } } diff --git a/drivers/gae/src/main/java/org/jclouds/gae/config/MultithreadedAsyncGoogleAppEngineConfigurationModule.java b/drivers/gae/src/main/java/org/jclouds/gae/config/MultithreadedAsyncGoogleAppEngineConfigurationModule.java new file mode 100644 index 0000000000..00475be15b --- /dev/null +++ b/drivers/gae/src/main/java/org/jclouds/gae/config/MultithreadedAsyncGoogleAppEngineConfigurationModule.java @@ -0,0 +1,68 @@ +/** + * 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.gae.config; + +import org.jclouds.concurrent.config.ConfiguresExecutorService; +import org.jclouds.gae.AsyncGaeHttpCommandExecutorService; +import org.jclouds.http.HttpCommandExecutorService; +import org.jclouds.http.config.ConfiguresHttpCommandExecutorService; + +import com.google.common.annotations.Beta; +import com.google.common.base.Supplier; +import com.google.common.util.concurrent.ListeningExecutorService; + +/** + * Configures {@link AsyncGaeHttpCommandExecutorService}. + * + * @author Adrian Cole + */ +@Beta +@ConfiguresHttpCommandExecutorService +@ConfiguresExecutorService +public class MultithreadedAsyncGoogleAppEngineConfigurationModule extends GoogleAppEngineConfigurationModule { + public MultithreadedAsyncGoogleAppEngineConfigurationModule() { + super(new CurrentRequestExecutorServiceModule()); + } + + /** + * Used when you are creating multiple contexts in the same app. + * + * @param currentRequestThreadFactory + * @see CurrentRequestExecutorServiceModule#currentRequestThreadFactory + */ + public MultithreadedAsyncGoogleAppEngineConfigurationModule(ListeningExecutorService currentRequestThreadFactory) { + super(new CurrentRequestExecutorServiceModule(currentRequestThreadFactory)); + } + + /** + * Used when you are creating multiple contexts in the same app. + * + * @param memoizedCurrentRequestExecutorService + * @see CurrentRequestExecutorServiceModule#memoizedCurrentRequestExecutorService + */ + public MultithreadedAsyncGoogleAppEngineConfigurationModule( + Supplier memoizedCurrentRequestExecutorService) { + super(memoizedCurrentRequestExecutorService); + } + + protected void bindHttpCommandExecutorService() { + bind(HttpCommandExecutorService.class).to(AsyncGaeHttpCommandExecutorService.class); + } + +} diff --git a/drivers/gae/src/test/java/org/jclouds/gae/AsyncGaeHttpCommandExecutorServiceIntegrationTest.java b/drivers/gae/src/test/java/org/jclouds/gae/AsyncGaeHttpCommandExecutorServiceIntegrationTest.java index 4d93056327..009910fa35 100644 --- a/drivers/gae/src/test/java/org/jclouds/gae/AsyncGaeHttpCommandExecutorServiceIntegrationTest.java +++ b/drivers/gae/src/test/java/org/jclouds/gae/AsyncGaeHttpCommandExecutorServiceIntegrationTest.java @@ -205,7 +205,8 @@ public class AsyncGaeHttpCommandExecutorServiceIntegrationTest extends BaseHttpC @BeforeMethod void setupApiProxy() { - new LocalServiceTestHelper(new LocalURLFetchServiceTestConfig()).setUp(); + LocalServiceTestHelper helper = new LocalServiceTestHelper(new LocalURLFetchServiceTestConfig()); + helper.setUp(); } @Override @@ -316,6 +317,7 @@ public class AsyncGaeHttpCommandExecutorServiceIntegrationTest extends BaseHttpC } protected Module createConnectionModule() { + setupApiProxy(); return new AsyncGoogleAppEngineConfigurationModule(); } diff --git a/drivers/gae/src/test/java/org/jclouds/gae/config/GoogleAppEngineConfigurationModuleTest.java b/drivers/gae/src/test/java/org/jclouds/gae/config/GoogleAppEngineConfigurationModuleTest.java deleted file mode 100644 index c3da90894b..0000000000 --- a/drivers/gae/src/test/java/org/jclouds/gae/config/GoogleAppEngineConfigurationModuleTest.java +++ /dev/null @@ -1,69 +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.gae.config; - -import java.util.Properties; -import java.util.concurrent.ExecutorService; - -import javax.ws.rs.core.UriBuilder; - -import org.jclouds.Constants; -import org.jclouds.gae.GaeHttpCommandExecutorService; -import org.jclouds.http.HttpCommandExecutorService; -import org.jclouds.logging.Logger; -import org.jclouds.logging.Logger.LoggerFactory; -import org.jclouds.rest.internal.BaseRestApiMetadata; -import org.testng.annotations.Test; - -import com.google.inject.Guice; -import com.google.inject.Injector; -import com.google.inject.Key; -import com.google.inject.name.Names; -import com.sun.jersey.api.uri.UriBuilderImpl; - -/** - * Tests the ability to configure a {@link GoogleAppEngineConfigurationModule} - * - * @author Adrian Cole - */ -@Test -public class GoogleAppEngineConfigurationModuleTest { - - public void testConfigureBindsClient() { - final Properties properties = BaseRestApiMetadata.defaultProperties(); - - Injector i = Guice.createInjector(new GoogleAppEngineConfigurationModule() { - @Override - protected void configure() { - Names.bindProperties(binder(), properties); - bind(Logger.LoggerFactory.class).toInstance(new LoggerFactory() { - public Logger getLogger(String category) { - return Logger.NULL; - } - }); - bind(UriBuilder.class).to(UriBuilderImpl.class); - super.configure(); - } - }); - HttpCommandExecutorService client = i.getInstance(HttpCommandExecutorService.class); - i.getInstance(Key.get(ExecutorService.class, Names.named(Constants.PROPERTY_USER_THREADS))); - // TODO check single threaded; - assert client instanceof GaeHttpCommandExecutorService; - } -} From a2c89935923ccae7413333c145916299fee2d94e Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Thu, 17 May 2012 00:42:08 -0700 Subject: [PATCH 110/148] updated gae demo to demonstrate use of views and GAE ThreadFactory --- demos/googleappengine/pom.xml | 14 +- .../GetAllResourcesController.java | 175 ++++++++++++++++++ .../GetAllStatusController.java | 93 ---------- .../config/GuiceServletConfig.java | 113 +++++++---- .../domain/ResourceResult.java | 128 +++++++++++++ .../googleappengine/domain/StatusResult.java | 105 ----------- ... => BlobStoreContextToAsyncResources.java} | 27 +-- ...ComputeServiceContextToAsyncResources.java | 69 +++++++ ... => ResourceMetadataToResourceResult.java} | 36 ++-- .../functions/ViewToAsyncResources.java | 59 ++++++ .../googleappengine/functions/ViewToId.java | 31 ++++ .../src/main/webapp/WEB-INF/jsp/resources.jsp | 104 +++++++++++ .../src/main/webapp/WEB-INF/jsp/status.jsp | 36 ---- .../googleappengine/src/main/webapp/index.jsp | 2 +- .../functest/GoogleAppEngineLiveTest.java | 56 ++++-- 15 files changed, 715 insertions(+), 333 deletions(-) create mode 100644 demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllResourcesController.java delete mode 100644 demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllStatusController.java create mode 100644 demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/domain/ResourceResult.java delete mode 100644 demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/domain/StatusResult.java rename demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/{BlobStoreContextToStatusResult.java => BlobStoreContextToAsyncResources.java} (59%) create mode 100644 demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ComputeServiceContextToAsyncResources.java rename demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/{ComputeServiceContextToStatusResult.java => ResourceMetadataToResourceResult.java} (56%) create mode 100644 demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ViewToAsyncResources.java create mode 100644 demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ViewToId.java create mode 100644 demos/googleappengine/src/main/webapp/WEB-INF/jsp/resources.jsp delete mode 100644 demos/googleappengine/src/main/webapp/WEB-INF/jsp/status.jsp diff --git a/demos/googleappengine/pom.xml b/demos/googleappengine/pom.xml index b78ce3452b..ce378227d5 100644 --- a/demos/googleappengine/pom.xml +++ b/demos/googleappengine/pom.xml @@ -40,8 +40,6 @@ 1.6.5 localhost 8088 - FIXME_IDENTITY - FIXME_CREDENTIAL @@ -57,13 +55,13 @@ org.jclouds.provider - hpcloud-objectstorage + aws-s3 ${project.version} runtime org.jclouds.provider - hpcloud-compute + hpcloud-objectstorage ${project.version} runtime @@ -369,8 +367,12 @@ - ${test.hpcloud.identity} - ${test.hpcloud.credential} + + ${test.aws-s3.identity} + ${test.aws-s3.credential} + ${test.hpcloud-objectstorage.identity} + ${test.hpcloud-objectstorage.credential} ${appengine.sdk.root} ${devappserver.address} ${devappserver.port} diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllResourcesController.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllResourcesController.java new file mode 100644 index 0000000000..1be0d9ca74 --- /dev/null +++ b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllResourcesController.java @@ -0,0 +1,175 @@ +/** + * 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.samples.googleappengine; + +import static com.google.common.collect.Iterables.concat; +import static com.google.common.collect.Iterables.size; +import static com.google.common.collect.Iterables.transform; + +import java.io.IOException; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.concurrent.TimeUnit; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Provider; +import javax.inject.Singleton; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.jclouds.View; +import org.jclouds.domain.ResourceMetadata; +import org.jclouds.logging.Logger; +import org.jclouds.samples.googleappengine.domain.ResourceResult; +import org.jclouds.samples.googleappengine.functions.ResourceMetadataToResourceResult; +import org.jclouds.samples.googleappengine.functions.ViewToAsyncResources; +import org.jclouds.samples.googleappengine.functions.ViewToId; + +import com.google.common.base.Stopwatch; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; +import com.google.common.collect.Lists; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; + +/** + * Shows an example of how to list all resources from all views! + * + * @author Adrian Cole + */ +@Singleton +public class GetAllResourcesController extends HttpServlet { + private static final long serialVersionUID = 1L; + + private final ListeningExecutorService currentRequestExecutorService; + private final Iterable views; + private final ViewToAsyncResources viewToAsyncResources; + private final ResourceMetadataToResourceResult resourceMetadataToStatusResult; + private final Provider remainingMillis; + + @Resource + protected Logger logger = Logger.NULL; + + @Inject + GetAllResourcesController(ListeningExecutorService currentRequestExecutorService, Iterable views, + ViewToAsyncResources viewToAsyncResources, ResourceMetadataToResourceResult resourceMetadataToStatusResult, + Provider remainingMillis) { + this.currentRequestExecutorService = currentRequestExecutorService; + this.views = views; + this.viewToAsyncResources = viewToAsyncResources; + this.resourceMetadataToStatusResult = resourceMetadataToStatusResult; + this.remainingMillis = remainingMillis; + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + try { + addResourcesToRequest(request); + RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/WEB-INF/jsp/resources.jsp"); + dispatcher.forward(request, response); + } catch (Exception e) { + logger.error(e, "Error listing resources"); + throw new ServletException(e); + } + } + + private void addResourcesToRequest(HttpServletRequest request) { + Stopwatch watch = new Stopwatch().start(); + logger.info("ready to list views: %s", transform(views, ViewToId.INSTANCE)); + Iterable>>> asyncResources = transform(views, + viewToAsyncResources); + logger.info("launched %s tasks with %sms remaining", size(asyncResources), remainingMillis.get()); + + Set>> done = allResourcesWithinDeadline(asyncResources); + logger.info("%s tasks completed in %sms with %sms remaining", size(done), watch.stop().elapsedMillis(), + remainingMillis.get()); + + Iterable> flattened = concat(done); + + Set results = FluentIterable.from(flattened).transform(resourceMetadataToStatusResult) + .toImmutableSet(); + + request.setAttribute("resources", results); + } + + private Set>> allResourcesWithinDeadline( + Iterable>>> asyncResources) { + Builder>> resourcesWeCanList = addToBuilderOnComplete(asyncResources); + + // only serve resources that made it by the timeout + blockUntilAllDoneOrCancelOnTimeout(asyncResources); + + return resourcesWeCanList.build(); + } + + private Builder>> addToBuilderOnComplete( + Iterable>>> asyncResources) { + + final Builder>> resourcesWeCanList = ImmutableSet + .>> builder(); + + for (final ListenableFuture>> asyncResource : asyncResources) { + Futures.addCallback(asyncResource, new FutureCallback>>() { + public void onSuccess(Iterable> result) { + if (result != null) + resourcesWeCanList.add(result); + } + + public void onFailure(Throwable t) { + if (!(t instanceof CancellationException)) + logger.info("exception getting resource %s: %s", asyncResource, t.getMessage()); + } + }, currentRequestExecutorService); + + } + return resourcesWeCanList; + } + + private void blockUntilAllDoneOrCancelOnTimeout( + Iterable>>> asyncResources) { + List>>> remaining = Lists + .newArrayList(asyncResources); + + while (remaining.size() > 0) { + ListenableFuture resource = remaining.remove(0); + if (remainingMillis.get() <= 0) { + if (!resource.isDone()) + resource.cancel(true); + continue; + } + + try { + resource.get(remainingMillis.get(), TimeUnit.MILLISECONDS); + } catch (Exception e) { + logger.info("exception getting resource %s: %s", resource, e.getMessage()); + if (!resource.isDone()) + resource.cancel(true); + } + } + } + +} \ No newline at end of file diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllStatusController.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllStatusController.java deleted file mode 100644 index 2f6e76089b..0000000000 --- a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllStatusController.java +++ /dev/null @@ -1,93 +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.samples.googleappengine; - -import java.io.IOException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; - -import javax.annotation.Resource; -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.servlet.RequestDispatcher; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.jclouds.blobstore.BlobStoreContext; -import org.jclouds.compute.ComputeServiceContext; -import org.jclouds.logging.Logger; -import org.jclouds.samples.googleappengine.functions.BlobStoreContextToStatusResult; -import org.jclouds.samples.googleappengine.functions.ComputeServiceContextToStatusResult; - -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; - -/** - * Shows an example of how to use {@link BlobStoreContext} and {@link ComputeServiceContext} - * injected with Guice. - * - * @author Adrian Cole - */ -@Singleton -public class GetAllStatusController extends HttpServlet { - private static final long serialVersionUID = 1L; - - private final Iterable blobsStoreContexts; - private final Iterable computeServiceContexts; - private final BlobStoreContextToStatusResult blobStoreContextToContainerResult; - private final ComputeServiceContextToStatusResult computeServiceContextToContainerResult; - - @Resource - protected Logger logger = Logger.NULL; - - @Inject - GetAllStatusController(Iterable blobsStoreContexts, - Iterable computeServiceContexts, - BlobStoreContextToStatusResult blobStoreContextToContainerResult, - ComputeServiceContextToStatusResult computeServiceContextToContainerResult) { - this.blobsStoreContexts = blobsStoreContexts; - this.computeServiceContexts = computeServiceContexts; - this.blobStoreContextToContainerResult = blobStoreContextToContainerResult; - this.computeServiceContextToContainerResult = computeServiceContextToContainerResult; - } - - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - try { - addStatusResultsToRequest(request); - RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/WEB-INF/jsp/status.jsp"); - dispatcher.forward(request, response); - } catch (Exception e) { - logger.error(e, "Error listing status"); - throw new ServletException(e); - } - } - - private void addStatusResultsToRequest(HttpServletRequest request) throws InterruptedException, ExecutionException, - TimeoutException { - request.setAttribute( - "status", - ImmutableSet.copyOf(Iterables.concat( - Iterables.transform(blobsStoreContexts, blobStoreContextToContainerResult), - Iterables.transform(computeServiceContexts, computeServiceContextToContainerResult)))); - } - -} \ No newline at end of file diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/config/GuiceServletConfig.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/config/GuiceServletConfig.java index 5b195a08a1..cdc671b8ac 100644 --- a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/config/GuiceServletConfig.java +++ b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/config/GuiceServletConfig.java @@ -18,6 +18,11 @@ */ package org.jclouds.samples.googleappengine.config; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.get; +import static com.google.common.collect.Iterables.transform; +import static com.google.common.io.Closeables.closeQuietly; import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING; import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED; import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_PORT_OPEN; @@ -26,59 +31,90 @@ import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_SCRIPT import java.io.IOException; import java.io.InputStream; import java.util.Properties; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import javax.inject.Singleton; import javax.servlet.ServletContextEvent; import org.jclouds.ContextBuilder; -import org.jclouds.blobstore.BlobStoreContext; -import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.View; import org.jclouds.gae.config.AsyncGoogleAppEngineConfigurationModule; -import org.jclouds.samples.googleappengine.GetAllStatusController; +import org.jclouds.logging.jdk.config.JDKLoggingModule; +import org.jclouds.providers.ProviderMetadata; +import org.jclouds.providers.Providers; +import org.jclouds.samples.googleappengine.GetAllResourcesController; +import com.google.appengine.api.ThreadManager; +import com.google.apphosting.api.ApiProxy; +import com.google.common.base.Function; +import com.google.common.base.Predicate; import com.google.common.collect.ImmutableSet; -import com.google.common.io.Closeables; +import com.google.common.reflect.TypeToken; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Module; +import com.google.inject.Provides; import com.google.inject.TypeLiteral; import com.google.inject.servlet.GuiceServletContextListener; import com.google.inject.servlet.ServletModule; /** - * Setup Logging and create {@link Injector} for use in testing Amazon EC2 and S3. + * Setup Logging and create {@link Injector} for use in testing Views * * @author Adrian Cole */ public class GuiceServletConfig extends GuiceServletContextListener { - private Iterable blobsStoreContexts; - private Iterable computeServiceContexts; + private Iterable views; @Override public void contextInitialized(ServletContextEvent servletContextEvent) { - Properties overrides = loadJCloudsProperties(servletContextEvent); + final Properties overrides = loadJCloudsProperties(servletContextEvent); + // until there's a global skip image parse option + overrides.setProperty("jclouds.ec2.ami-query", ""); + overrides.setProperty("jclouds.ec2.cc-ami-query", ""); + + // ensure requests don't take longer than GAE timeout overrides.setProperty(TIMEOUT_NODE_TERMINATED, "25000"); overrides.setProperty(TIMEOUT_NODE_RUNNING, "25000"); overrides.setProperty(TIMEOUT_SCRIPT_COMPLETE, "25000"); overrides.setProperty(TIMEOUT_PORT_OPEN, "25000"); - // note that this module hooks into the async urlfetchservice - ImmutableSet modules = ImmutableSet. of(new AsyncGoogleAppEngineConfigurationModule()); + // correct the classloader so that extensions can be found + Thread.currentThread().setContextClassLoader(Providers.class.getClassLoader()); - blobsStoreContexts = ImmutableSet.of( - ContextBuilder.newBuilder("hpcloud-objectstorage") - .modules(modules) - .overrides(overrides) - .buildView(BlobStoreContext.class)); - computeServiceContexts = ImmutableSet.of( - ContextBuilder.newBuilder("hpcloud-compute") - .modules(modules) - .overrides(overrides) - .buildView(ComputeServiceContext.class)); + Iterable identityInProperties = providersWeHaveIdentitiesFor(overrides); + + final ImmutableSet modules = ImmutableSet. of(new AsyncGoogleAppEngineConfigurationModule()); + views = transform(identityInProperties, new Function() { + + @Override + public View apply(ProviderMetadata input) { + TypeToken defaultView = get(input.getApiMetadata().getViews(), 0); + return ContextBuilder.newBuilder(input).modules(modules).overrides(overrides).buildView(defaultView); + } + + }); super.contextInitialized(servletContextEvent); } + protected Iterable providersWeHaveIdentitiesFor(final Properties overrides) { + // there's a chance serviceloader is being lazy, and we don't want + // ConcurrentModificationException, so copy into a set. + return ImmutableSet.copyOf(filter(Providers.all(), new Predicate() { + + @Override + public boolean apply(ProviderMetadata input) { + return overrides.containsKey(input.getId() + ".identity"); + } + + })); + } + private Properties loadJCloudsProperties(ServletContextEvent servletContextEvent) { InputStream input = servletContextEvent.getServletContext().getResourceAsStream("/WEB-INF/jclouds.properties"); Properties props = new Properties(); @@ -87,35 +123,44 @@ public class GuiceServletConfig extends GuiceServletContextListener { } catch (IOException e) { throw new RuntimeException(e); } finally { - Closeables.closeQuietly(input); + closeQuietly(input); } return props; } @Override protected Injector getInjector() { - return Guice.createInjector(new ServletModule() { + return Guice.createInjector(new JDKLoggingModule(), new ServletModule() { @Override protected void configureServlets() { - bind(new TypeLiteral>() { - }).toInstance(GuiceServletConfig.this.blobsStoreContexts); - bind(new TypeLiteral>() { - }).toInstance(GuiceServletConfig.this.computeServiceContexts); - serve("*.check").with(GetAllStatusController.class); + bind(new TypeLiteral>() { + }).toInstance(GuiceServletConfig.this.views); + serve("*.check").with(GetAllResourcesController.class); requestInjection(this); } - } - ); + @SuppressWarnings("unused") + @Provides + long remainingMillis() { + // leave 100ms for any post processing + return ApiProxy.getCurrentEnvironment().getRemainingMillis() - 100; + } + + @SuppressWarnings("unused") + @Provides + @Singleton + ListeningExecutorService currentRequestExecutorService() { + ThreadFactory factory = checkNotNull(ThreadManager.currentRequestThreadFactory(), + "ThreadManager.currentRequestThreadFactory()"); + return MoreExecutors.listeningDecorator(Executors.newCachedThreadPool(factory)); + } + }); } @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { - for (BlobStoreContext context : blobsStoreContexts) { - context.close(); - } - for (ComputeServiceContext context : computeServiceContexts) { - context.close(); + for (View view : views) { + view.unwrap().close(); } super.contextDestroyed(servletContextEvent); } diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/domain/ResourceResult.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/domain/ResourceResult.java new file mode 100644 index 0000000000..fe09faa27d --- /dev/null +++ b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/domain/ResourceResult.java @@ -0,0 +1,128 @@ +/** + * 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.samples.googleappengine.domain; + +import static com.google.common.base.Objects.equal; + +import com.google.common.base.Objects; + +/** + * + * @author Adrian Cole + */ +public class ResourceResult { + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + protected String provider; + protected String location; + protected String type; + protected String id; + protected String name; + + public Builder provider(String provider) { + this.provider = provider; + return this; + } + + public Builder location(String location) { + this.location = location; + return this; + } + + public Builder type(String type) { + this.type = type; + return this; + } + + public Builder id(String id) { + this.id = id; + return this; + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public ResourceResult build() { + return new ResourceResult(provider, location, type, id, name); + } + + } + + private final String provider; + private final String location; + private final String type; + private final String id; + private final String name; + + protected ResourceResult(String provider, String location, String type, String id, String name) { + this.provider = provider; + this.type = type; + this.location = location; + this.id = id; + this.name = name; + } + + public String getProvider() { + return provider; + } + + public String getLocation() { + return location; + } + + public String getType() { + return type; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + ResourceResult that = ResourceResult.class.cast(o); + return equal(this.provider, that.provider) && equal(this.location, that.location) && equal(this.type, that.type) + && equal(this.id, that.id) && equal(this.name, that.name); + } + + @Override + public int hashCode() { + return Objects.hashCode(provider, location, type, id, name); + } + + @Override + public String toString() { + return Objects.toStringHelper("").add("provider", provider).add("location", location).add("type", type) + .add("id", id).add("name", name).toString(); + } +} \ No newline at end of file diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/domain/StatusResult.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/domain/StatusResult.java deleted file mode 100644 index 26138971ce..0000000000 --- a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/domain/StatusResult.java +++ /dev/null @@ -1,105 +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.samples.googleappengine.domain; - -import java.io.Serializable; - -/** - * - * @author Adrian Cole - */ -public class StatusResult implements Comparable, Serializable { - /** The serialVersionUID */ - private static final long serialVersionUID = -3257496189689220018L; - private final String service; - private final String host; - private final String name; - private final String status; - - public StatusResult(String service, String host, String name, String status) { - this.service = service; - this.host = host; - this.name = name; - this.status = status; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((host == null) ? 0 : host.hashCode()); - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((service == null) ? 0 : service.hashCode()); - result = prime * result + ((status == null) ? 0 : status.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; - StatusResult other = (StatusResult) obj; - if (host == null) { - if (other.host != null) - return false; - } else if (!host.equals(other.host)) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (service == null) { - if (other.service != null) - return false; - } else if (!service.equals(other.service)) - return false; - if (status == null) { - if (other.status != null) - return false; - } else if (!status.equals(other.status)) - return false; - return true; - } - - public int compareTo(StatusResult o) { - return (this == o) ? 0 : getService().compareTo(o.getService()); - } - - public String getHost() { - return host; - } - - public String getName() { - return name; - } - - public String getStatus() { - return status; - } - - public String getService() { - return service; - } - -} diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/BlobStoreContextToStatusResult.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/BlobStoreContextToAsyncResources.java similarity index 59% rename from demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/BlobStoreContextToStatusResult.java rename to demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/BlobStoreContextToAsyncResources.java index a98f28ff91..518ea8b1ec 100644 --- a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/BlobStoreContextToStatusResult.java +++ b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/BlobStoreContextToAsyncResources.java @@ -18,41 +18,28 @@ */ package org.jclouds.samples.googleappengine.functions; -import java.net.URI; - import javax.annotation.Resource; import javax.inject.Singleton; import org.jclouds.blobstore.BlobStoreContext; +import org.jclouds.domain.ResourceMetadata; import org.jclouds.logging.Logger; -import org.jclouds.samples.googleappengine.domain.StatusResult; import com.google.common.base.Function; +import com.google.common.util.concurrent.ListenableFuture; /** * * @author Adrian Cole */ @Singleton -public class BlobStoreContextToStatusResult implements Function { - +public class BlobStoreContextToAsyncResources implements + Function>>> { @Resource protected Logger logger = Logger.NULL; - public StatusResult apply(BlobStoreContext in) { - String host = URI.create(in.unwrap().getProviderMetadata().getEndpoint()).getHost(); - String status; - String name = "not found"; - try { - long start = System.currentTimeMillis(); - - name = String.format("%d containers", in.getBlobStore().list().size()); - - status = ((System.currentTimeMillis() - start) + "ms"); - } catch (Exception e) { - logger.error(e, "Error listing context %s", in); - status = (e.getMessage()); - } - return new StatusResult(in.unwrap().getId(), host, name, status); + public ListenableFuture>> apply(BlobStoreContext in) { + logger.info("listing containers on %s: ", in.unwrap().getId()); + return in.getAsyncBlobStore().list(); } } \ No newline at end of file diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ComputeServiceContextToAsyncResources.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ComputeServiceContextToAsyncResources.java new file mode 100644 index 0000000000..fed3897fb1 --- /dev/null +++ b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ComputeServiceContextToAsyncResources.java @@ -0,0 +1,69 @@ +/** + * 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.samples.googleappengine.functions; + +import java.util.concurrent.Callable; + +import javax.annotation.Resource; +import javax.inject.Singleton; + +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.domain.ResourceMetadata; +import org.jclouds.logging.Logger; + +import com.google.common.base.Function; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.inject.Inject; + +/** + * ComputeService doesn't currently have an Async counterpart + * + * @author Adrian Cole + */ +@Singleton +public class ComputeServiceContextToAsyncResources implements + Function>>> { + + @Resource + protected Logger logger = Logger.NULL; + + private final ListeningExecutorService currentRequestExecutorService; + + @Inject + public ComputeServiceContextToAsyncResources(ListeningExecutorService currentRequestExecutorService) { + this.currentRequestExecutorService = currentRequestExecutorService; + } + + public ListenableFuture>> apply(final ComputeServiceContext in) { + return currentRequestExecutorService.submit(new Callable>>() { + + @Override + public Iterable> call() throws Exception { + logger.info("listing nodes on %s: ", in.unwrap().getId()); + return in.getComputeService().listNodes(); + } + + @Override + public String toString() { + return in.toString(); + } + }); + } +} \ No newline at end of file diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ComputeServiceContextToStatusResult.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ResourceMetadataToResourceResult.java similarity index 56% rename from demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ComputeServiceContextToStatusResult.java rename to demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ResourceMetadataToResourceResult.java index 08aaeb2b55..1ef82b85dc 100644 --- a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ComputeServiceContextToStatusResult.java +++ b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ResourceMetadataToResourceResult.java @@ -18,14 +18,14 @@ */ package org.jclouds.samples.googleappengine.functions; -import java.net.URI; - import javax.annotation.Resource; import javax.inject.Singleton; -import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.domain.Location; +import org.jclouds.domain.ResourceMetadata; import org.jclouds.logging.Logger; -import org.jclouds.samples.googleappengine.domain.StatusResult; +import org.jclouds.samples.googleappengine.domain.ResourceResult; +import org.jclouds.samples.googleappengine.domain.ResourceResult.Builder; import com.google.common.base.Function; @@ -34,25 +34,21 @@ import com.google.common.base.Function; * @author Adrian Cole */ @Singleton -public class ComputeServiceContextToStatusResult implements Function { +public class ResourceMetadataToResourceResult implements Function, ResourceResult> { @Resource protected Logger logger = Logger.NULL; - public StatusResult apply(ComputeServiceContext in) { - String host = URI.create(in.unwrap().getProviderMetadata().getEndpoint()).getHost(); - String status; - String name = "not found"; - try { - long start = System.currentTimeMillis(); - - name = String.format("%d nodes", in.getComputeService().listNodes().size()); - - status = ((System.currentTimeMillis() - start) + "ms"); - } catch (Exception e) { - logger.error(e, "Error listing context %s", in); - status = (e.getMessage()); - } - return new StatusResult(in.unwrap().getId(), host, name, status); + public ResourceResult apply(ResourceMetadata in) { + Builder builder = ResourceResult.builder(); + Location provider = in.getLocation(); + while (provider.getParent() != null) + provider = provider.getParent(); + builder.provider(provider.getId()); + builder.location(in.getLocation().getId()); + builder.type(in.getType().toString().toLowerCase()); + builder.id(in.getProviderId()); + builder.name(in.getName()); + return builder.build(); } } \ No newline at end of file diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ViewToAsyncResources.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ViewToAsyncResources.java new file mode 100644 index 0000000000..bd1030c213 --- /dev/null +++ b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ViewToAsyncResources.java @@ -0,0 +1,59 @@ +/** + * 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.samples.googleappengine.functions; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.View; +import org.jclouds.blobstore.BlobStoreContext; +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.domain.ResourceMetadata; + +import com.google.common.base.Function; +import com.google.common.util.concurrent.ListenableFuture; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class ViewToAsyncResources implements Function>>> { + private final BlobStoreContextToAsyncResources blobStoreContextToAsyncResources; + private final ComputeServiceContextToAsyncResources computeServiceContextToAsyncResources; + + @Inject + public ViewToAsyncResources(BlobStoreContextToAsyncResources blobStoreContextToAsyncResources, + ComputeServiceContextToAsyncResources computeServiceContextToAsyncResources) { + this.blobStoreContextToAsyncResources = blobStoreContextToAsyncResources; + this.computeServiceContextToAsyncResources = computeServiceContextToAsyncResources; + } + + + @Override + public ListenableFuture>> apply(View input) { + if (input instanceof BlobStoreContext) { + return blobStoreContextToAsyncResources.apply(BlobStoreContext.class.cast(input)); + } else if (input instanceof ComputeServiceContext) { + return computeServiceContextToAsyncResources.apply(ComputeServiceContext.class.cast(input)); + } + throw new UnsupportedOperationException("unknown view type: " + input); + } + +} \ No newline at end of file diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ViewToId.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ViewToId.java new file mode 100644 index 0000000000..ea9f2da92d --- /dev/null +++ b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/functions/ViewToId.java @@ -0,0 +1,31 @@ +/** + * 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.samples.googleappengine.functions; + +import org.jclouds.View; + +import com.google.common.base.Function; + +public enum ViewToId implements Function { + INSTANCE; + @Override + public String apply(View input) { + return input.unwrap().getId(); + } +} \ No newline at end of file diff --git a/demos/googleappengine/src/main/webapp/WEB-INF/jsp/resources.jsp b/demos/googleappengine/src/main/webapp/WEB-INF/jsp/resources.jsp new file mode 100644 index 0000000000..1a8a21bfd2 --- /dev/null +++ b/demos/googleappengine/src/main/webapp/WEB-INF/jsp/resources.jsp @@ -0,0 +1,104 @@ +<%-- + + 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. + +--%> +<%@ page buffer="100kb"%> +<%@ taglib uri="http://displaytag.sf.net" prefix="display"%> + + +jclouds: multi-cloud library + + + +

Resource List

+ + + + +
+
+ + + + + + + +
+
+ + diff --git a/demos/googleappengine/src/main/webapp/WEB-INF/jsp/status.jsp b/demos/googleappengine/src/main/webapp/WEB-INF/jsp/status.jsp deleted file mode 100644 index 8cb3960523..0000000000 --- a/demos/googleappengine/src/main/webapp/WEB-INF/jsp/status.jsp +++ /dev/null @@ -1,36 +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. - ---%> -<%@ page buffer="20kb"%> -<%@ taglib uri="http://displaytag.sf.net" prefix="display"%> - - -jclouds: multi-cloud library - - -

Status List

- - - - - - - - diff --git a/demos/googleappengine/src/main/webapp/index.jsp b/demos/googleappengine/src/main/webapp/index.jsp index b3db1e7d8f..38308a0f33 100644 --- a/demos/googleappengine/src/main/webapp/index.jsp +++ b/demos/googleappengine/src/main/webapp/index.jsp @@ -25,6 +25,6 @@

Welcome!

Click -here to get status of cloud services. +here to list all your cloud resources! diff --git a/demos/googleappengine/src/test/java/org/jclouds/samples/googleappengine/functest/GoogleAppEngineLiveTest.java b/demos/googleappengine/src/test/java/org/jclouds/samples/googleappengine/functest/GoogleAppEngineLiveTest.java index 93127e406e..c23e14d66a 100644 --- a/demos/googleappengine/src/test/java/org/jclouds/samples/googleappengine/functest/GoogleAppEngineLiveTest.java +++ b/demos/googleappengine/src/test/java/org/jclouds/samples/googleappengine/functest/GoogleAppEngineLiveTest.java @@ -18,21 +18,27 @@ */ package org.jclouds.samples.googleappengine.functest; -import static com.google.common.base.Preconditions.checkNotNull; - import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.util.Map; import java.util.Properties; +import org.jclouds.blobstore.BlobStore; +import org.jclouds.compute.ComputeService; +import org.jclouds.util.Maps2; import org.jclouds.util.Strings2; import org.testng.annotations.BeforeTest; import org.testng.annotations.Parameters; import org.testng.annotations.Test; +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.Maps; + /** * Starts up the Google App Engine for Java Development environment and deploys an application which - * tests Amazon EC2 and S3. + * tests {@link ComputeService} and {@link BlobStore}. * * @author Adrian Cole */ @@ -47,23 +53,37 @@ public class GoogleAppEngineLiveTest { public void startDevAppServer(final String warfile, final String address, final String port) throws Exception { url = new URL(String.format("http://%s:%s", address, port)); + Properties props = new Properties(); - String identity = checkNotNull(System.getProperty("test.hpcloud.identity"), - "test.hpcloud.identity"); - String credential = checkNotNull(System.getProperty("test.hpcloud.credential"), - "test.hpcloud.credential"); - - /** - * Since both objectstorage and compute use the same credentials, we can - * take a shortcut and specify both here: - */ - props.setProperty("jclouds.identity", identity); - props.setProperty("jclouds.credential", credential); - + props.putAll(stripTestPrefix(selectPropertiesForIdentityAndCredential())); server = new GoogleDevServer(); server.writePropertiesAndStartServer(address, port, warfile, props); } + Map stripTestPrefix(Map identityCrendential) { + return Maps2.transformKeys(identityCrendential, new Function() { + + @Override + public String apply(String arg0) { + return arg0.replace("test.", ""); + } + + }); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + Map selectPropertiesForIdentityAndCredential() { + return Maps.filterKeys((Map) System.getProperties(), new Predicate() { + + @Override + public boolean apply(String input) { + // TODO Auto-generated method stub + return input.matches("^test\\.[a-z0-9-]+\\.(identity|credential)$"); + } + + }); + } + @Test public void shouldPass() throws InterruptedException, IOException { InputStream i = url.openStream(); @@ -73,15 +93,15 @@ public class GoogleAppEngineLiveTest { @Test(invocationCount = 5, enabled = true) public void testGuiceJCloudsSerial() throws InterruptedException, IOException { - URL gurl = new URL(url, "/guice/status.check"); + URL gurl = new URL(url, "/guice/resources.check"); InputStream i = gurl.openStream(); String string = Strings2.toStringAndClose(i); assert string.indexOf("List") >= 0 : string; } - @Test(invocationCount = 10, enabled = true, threadPoolSize = 3) + @Test(invocationCount = 10, enabled = false, threadPoolSize = 3) public void testGuiceJCloudsParallel() throws InterruptedException, IOException { - URL gurl = new URL(url, "/guice/status.check"); + URL gurl = new URL(url, "/guice/resources.check"); InputStream i = gurl.openStream(); String string = Strings2.toStringAndClose(i); assert string.indexOf("List") >= 0 : string; From 6e1c354423573ea1711481d36ed7769a7d3c4fe2 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Thu, 17 May 2012 01:31:20 -0700 Subject: [PATCH 111/148] cleanup --- demos/googleappengine/pom.xml | 8 --- .../src/main/appengine/appengine-web.xml | 3 +- .../GetAllResourcesController.java | 59 +++++++++---------- 3 files changed, 29 insertions(+), 41 deletions(-) diff --git a/demos/googleappengine/pom.xml b/demos/googleappengine/pom.xml index ce378227d5..abcd66b18b 100644 --- a/demos/googleappengine/pom.xml +++ b/demos/googleappengine/pom.xml @@ -53,12 +53,6 @@ jclouds-compute ${project.version} - - org.jclouds.provider - aws-s3 - ${project.version} - runtime - org.jclouds.provider hpcloud-objectstorage @@ -369,8 +363,6 @@ - ${test.aws-s3.identity} - ${test.aws-s3.credential} ${test.hpcloud-objectstorage.identity} ${test.hpcloud-objectstorage.credential} ${appengine.sdk.root} diff --git a/demos/googleappengine/src/main/appengine/appengine-web.xml b/demos/googleappengine/src/main/appengine/appengine-web.xml index 662e689c2c..e2440d86da 100644 --- a/demos/googleappengine/src/main/appengine/appengine-web.xml +++ b/demos/googleappengine/src/main/appengine/appengine-web.xml @@ -25,5 +25,6 @@ - true + + false diff --git a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllResourcesController.java b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllResourcesController.java index 1be0d9ca74..1d9f450eaf 100644 --- a/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllResourcesController.java +++ b/demos/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllResourcesController.java @@ -23,7 +23,6 @@ import static com.google.common.collect.Iterables.size; import static com.google.common.collect.Iterables.transform; import java.io.IOException; -import java.util.List; import java.util.Set; import java.util.concurrent.CancellationException; import java.util.concurrent.TimeUnit; @@ -50,7 +49,6 @@ import com.google.common.base.Stopwatch; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet.Builder; -import com.google.common.collect.Lists; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -116,9 +114,8 @@ public class GetAllResourcesController extends HttpServlet { request.setAttribute("resources", results); } - private Set>> allResourcesWithinDeadline( - Iterable>>> asyncResources) { - Builder>> resourcesWeCanList = addToBuilderOnComplete(asyncResources); + private Set allResourcesWithinDeadline(Iterable> asyncResources) { + Builder resourcesWeCanList = addToBuilderOnComplete(asyncResources); // only serve resources that made it by the timeout blockUntilAllDoneOrCancelOnTimeout(asyncResources); @@ -126,22 +123,20 @@ public class GetAllResourcesController extends HttpServlet { return resourcesWeCanList.build(); } - private Builder>> addToBuilderOnComplete( - Iterable>>> asyncResources) { - - final Builder>> resourcesWeCanList = ImmutableSet - .>> builder(); + private Builder addToBuilderOnComplete(Iterable> asyncResources) { - for (final ListenableFuture>> asyncResource : asyncResources) { - Futures.addCallback(asyncResource, new FutureCallback>>() { - public void onSuccess(Iterable> result) { + final Builder resourcesWeCanList = ImmutableSet. builder(); + + for (final ListenableFuture asyncResource : asyncResources) { + Futures.addCallback(asyncResource, new FutureCallback() { + public void onSuccess(T result) { if (result != null) resourcesWeCanList.add(result); } public void onFailure(Throwable t) { if (!(t instanceof CancellationException)) - logger.info("exception getting resource %s: %s", asyncResource, t.getMessage()); + logger.error(t, "exception getting resource %s: %s", asyncResource, t.getMessage()); } }, currentRequestExecutorService); @@ -149,27 +144,27 @@ public class GetAllResourcesController extends HttpServlet { return resourcesWeCanList; } - private void blockUntilAllDoneOrCancelOnTimeout( - Iterable>>> asyncResources) { - List>>> remaining = Lists - .newArrayList(asyncResources); - - while (remaining.size() > 0) { - ListenableFuture resource = remaining.remove(0); - if (remainingMillis.get() <= 0) { - if (!resource.isDone()) - resource.cancel(true); - continue; + // ensure we don't violate our request timeouts. + private void blockUntilAllDoneOrCancelOnTimeout(Iterable> asyncResources) { + try { + for (ListenableFuture asyncResource : asyncResources) { + if (remainingMillis.get() > 0) { + try { + asyncResource.get(remainingMillis.get(), TimeUnit.MILLISECONDS); + } catch (Exception e) { + logger.info("exception getting resource %s: %s", asyncResource, e.getMessage()); + } + } } - - try { - resource.get(remainingMillis.get(), TimeUnit.MILLISECONDS); - } catch (Exception e) { - logger.info("exception getting resource %s: %s", resource, e.getMessage()); - if (!resource.isDone()) - resource.cancel(true); + } finally { + if (remainingMillis.get() < 0) { + for (ListenableFuture asyncResource : asyncResources) { + if (!asyncResource.isDone()) + asyncResource.cancel(true); + } } } + } } \ No newline at end of file From 9aedf7b6f6e66f292242a67b2ec0316b66440306 Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Fri, 11 May 2012 14:50:22 +0100 Subject: [PATCH 112/148] Issue 647: added "Expires" header for ContentMetadata --- .../DelegatingMutableContentMetadata.java | 10 +++++ .../blobstore/AtmosBlobRequestSignerTest.java | 3 +- .../internal/BucketListObjectMetadata.java | 2 +- .../s3/domain/internal/CopyObjectResult.java | 2 +- .../s3/blobstore/S3BlobRequestSignerTest.java | 4 +- .../blobstore/SwiftBlobRequestSignerTest.java | 3 +- .../jclouds/blobstore/domain/BlobBuilder.java | 2 + .../domain/internal/BlobBuilderImpl.java | 6 +++ .../TransientBlobRequestSignerTest.java | 11 +++-- .../main/java/org/jclouds/http/HttpUtils.java | 11 +++++ .../JavaUrlHttpCommandExecutorService.java | 2 + .../java/org/jclouds/io/ContentMetadata.java | 8 ++++ .../jclouds/io/ContentMetadataBuilder.java | 30 +++++++------ .../jclouds/io/MutableContentMetadata.java | 1 + .../BaseImmutableContentMetadata.java | 30 ++++++++----- .../payloads/BaseMutableContentMetadata.java | 18 +++++++- .../org/jclouds/logging/internal/Wire.java | 1 + .../rest/internal/BaseRestClientTest.java | 18 ++++++-- .../jclouds/http/apachehc/ApacheHCUtils.java | 2 + .../integration/AWSS3ContainerLiveTest.java | 43 ++++++++++++++++++- .../domain/internal/BlobPropertiesImpl.java | 5 ++- ...ontainerNameEnumerationResultsHandler.java | 10 ++++- .../blobstore/AzureBlobRequestSignerTest.java | 3 +- ...inerNameEnumerationResultsHandlerTest.java | 8 ++-- 24 files changed, 182 insertions(+), 51 deletions(-) diff --git a/apis/atmos/src/main/java/org/jclouds/atmos/domain/internal/DelegatingMutableContentMetadata.java b/apis/atmos/src/main/java/org/jclouds/atmos/domain/internal/DelegatingMutableContentMetadata.java index f81b1edb4b..15c19538de 100644 --- a/apis/atmos/src/main/java/org/jclouds/atmos/domain/internal/DelegatingMutableContentMetadata.java +++ b/apis/atmos/src/main/java/org/jclouds/atmos/domain/internal/DelegatingMutableContentMetadata.java @@ -158,6 +158,16 @@ public class DelegatingMutableContentMetadata implements MutableContentMetadata delegate.setPropertiesFromHttpHeaders(headers); } + @Override + public void setExpires(String expires) { + delegate.setExpires(expires); + } + + @Override + public String getExpires() { + return delegate.getExpires(); + } + @Override public ContentMetadataBuilder toBuilder() { return delegate.toBuilder(); diff --git a/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/AtmosBlobRequestSignerTest.java b/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/AtmosBlobRequestSignerTest.java index 79555eae05..bb1465f68e 100644 --- a/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/AtmosBlobRequestSignerTest.java +++ b/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/AtmosBlobRequestSignerTest.java @@ -89,6 +89,7 @@ public class AtmosBlobRequestSignerTest extends BaseAsyncClientTest + */ + @Nullable + String getExpires(); + ContentMetadataBuilder toBuilder(); } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/io/ContentMetadataBuilder.java b/core/src/main/java/org/jclouds/io/ContentMetadataBuilder.java index 11694871c1..647f6deda3 100644 --- a/core/src/main/java/org/jclouds/io/ContentMetadataBuilder.java +++ b/core/src/main/java/org/jclouds/io/ContentMetadataBuilder.java @@ -51,6 +51,7 @@ public class ContentMetadataBuilder implements Serializable { protected String contentDisposition; protected String contentLanguage; protected String contentEncoding; + protected String expires; public ContentMetadataBuilder fromHttpHeaders(Multimap headers) { boolean chunked = any(headers.entries(), new Predicate>() { @@ -72,6 +73,8 @@ public class ContentMetadataBuilder implements Serializable { contentEncoding(header.getValue()); } else if ("Content-Language".equalsIgnoreCase(header.getKey())) { contentLanguage(header.getValue()); + } else if ("Expires".equalsIgnoreCase(header.getKey())) { + expires(header.getValue()); } } return this; @@ -113,28 +116,26 @@ public class ContentMetadataBuilder implements Serializable { return this; } + public ContentMetadataBuilder expires(@Nullable String expires) { + this.expires = expires; + return this; + } + public ContentMetadata build() { return new BaseImmutableContentMetadata(contentType, contentLength, contentMD5, contentDisposition, - contentLanguage, contentEncoding); + contentLanguage, contentEncoding, expires); } public static ContentMetadataBuilder fromContentMetadata(ContentMetadata in) { return new ContentMetadataBuilder().contentType(in.getContentType()).contentLength(in.getContentLength()) .contentMD5(in.getContentMD5()).contentDisposition(in.getContentDisposition()).contentLanguage( - in.getContentLanguage()).contentEncoding(in.getContentEncoding()); + in.getContentLanguage()).contentEncoding(in.getContentEncoding()).expires(in.getExpires()); } @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((contentDisposition == null) ? 0 : contentDisposition.hashCode()); - result = prime * result + ((contentEncoding == null) ? 0 : contentEncoding.hashCode()); - result = prime * result + ((contentLanguage == null) ? 0 : contentLanguage.hashCode()); - result = prime * result + ((contentLength == null) ? 0 : contentLength.hashCode()); - result = prime * result + Arrays.hashCode(contentMD5); - result = prime * result + ((contentType == null) ? 0 : contentType.hashCode()); - return result; + return Objects.hashCode(contentDisposition, contentEncoding, contentLanguage, contentLength, + contentMD5, contentType, expires); } @Override @@ -151,13 +152,14 @@ public class ContentMetadataBuilder implements Serializable { Objects.equal(contentLanguage, other.contentLanguage) && Objects.equal(contentLength, other.contentLength) && Arrays.equals(contentMD5, other.contentMD5) && - Objects.equal(contentType, other.contentType); + Objects.equal(contentType, other.contentType) && + Objects.equal(expires, other.expires); } @Override public String toString() { return "[contentDisposition=" + contentDisposition + ", contentEncoding=" + contentEncoding + ", contentLanguage=" + contentLanguage + ", contentLength=" + contentLength + ", contentMD5=" - + Arrays.toString(contentMD5) + ", contentType=" + contentType + "]"; + + Arrays.toString(contentMD5) + ", contentType=" + contentType + ", expires=" + expires + "]"; } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jclouds/io/MutableContentMetadata.java b/core/src/main/java/org/jclouds/io/MutableContentMetadata.java index d2de785d01..e9e0a36901 100644 --- a/core/src/main/java/org/jclouds/io/MutableContentMetadata.java +++ b/core/src/main/java/org/jclouds/io/MutableContentMetadata.java @@ -66,4 +66,5 @@ public interface MutableContentMetadata extends ContentMetadata { */ void setContentEncoding(@Nullable String contentEncoding); + void setExpires(@Nullable String expires); } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/io/payloads/BaseImmutableContentMetadata.java b/core/src/main/java/org/jclouds/io/payloads/BaseImmutableContentMetadata.java index 6253b87a22..c781366e1f 100644 --- a/core/src/main/java/org/jclouds/io/payloads/BaseImmutableContentMetadata.java +++ b/core/src/main/java/org/jclouds/io/payloads/BaseImmutableContentMetadata.java @@ -24,6 +24,8 @@ import java.util.Arrays; import org.jclouds.io.ContentMetadata; import org.jclouds.io.ContentMetadataBuilder; +import com.google.common.base.Objects; + /** * @author Adrian Cole */ @@ -37,15 +39,17 @@ public class BaseImmutableContentMetadata implements ContentMetadata, Serializab protected String contentDisposition; protected String contentLanguage; protected String contentEncoding; + protected String expires; public BaseImmutableContentMetadata(String contentType, Long contentLength, byte[] contentMD5, - String contentDisposition, String contentLanguage, String contentEncoding) { + String contentDisposition, String contentLanguage, String contentEncoding, String expires) { this.contentType = contentType; this.contentLength = contentLength; this.contentMD5 = contentMD5; this.contentDisposition = contentDisposition; this.contentLanguage = contentLanguage; this.contentEncoding = contentEncoding; + this.expires = expires; } /** @@ -102,24 +106,25 @@ public class BaseImmutableContentMetadata implements ContentMetadata, Serializab return this.contentEncoding; } + /** + * {@inheritDoc} + */ + @Override + public String getExpires() { + return this.expires; + } + @Override public String toString() { return "[contentType=" + contentType + ", contentLength=" + contentLength + ", contentDisposition=" + contentDisposition + ", contentEncoding=" + contentEncoding + ", contentLanguage=" + contentLanguage - + ", contentMD5=" + Arrays.toString(contentMD5) + "]"; + + ", contentMD5=" + Arrays.toString(contentMD5) + ", expires = " + expires + "]"; } @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((contentDisposition == null) ? 0 : contentDisposition.hashCode()); - result = prime * result + ((contentEncoding == null) ? 0 : contentEncoding.hashCode()); - result = prime * result + ((contentLanguage == null) ? 0 : contentLanguage.hashCode()); - result = prime * result + ((contentLength == null) ? 0 : contentLength.hashCode()); - result = prime * result + Arrays.hashCode(contentMD5); - result = prime * result + ((contentType == null) ? 0 : contentType.hashCode()); - return result; + return Objects.hashCode(contentDisposition, contentEncoding, contentLanguage, contentLength, + contentMD5, contentType, expires); } @Override @@ -158,6 +163,9 @@ public class BaseImmutableContentMetadata implements ContentMetadata, Serializab return false; } else if (!contentType.equals(other.contentType)) return false; + if (!Objects.equal(expires, other.expires)) { + return false; + } return true; } diff --git a/core/src/main/java/org/jclouds/io/payloads/BaseMutableContentMetadata.java b/core/src/main/java/org/jclouds/io/payloads/BaseMutableContentMetadata.java index 6f3e3f3b19..50c32e878a 100644 --- a/core/src/main/java/org/jclouds/io/payloads/BaseMutableContentMetadata.java +++ b/core/src/main/java/org/jclouds/io/payloads/BaseMutableContentMetadata.java @@ -141,6 +141,22 @@ public class BaseMutableContentMetadata extends ContentMetadataBuilder implement return this.contentEncoding; } + /** + * {@inheritDoc} + */ + @Override + public void setExpires(@Nullable String expires) { + expires(expires); + } + + /** + * {@inheritDoc} + */ + @Override + public String getExpires() { + return this.expires; + } + @Override public BaseMutableContentMetadata toBuilder() { return BaseMutableContentMetadata.fromContentMetadata(this); @@ -150,6 +166,6 @@ public class BaseMutableContentMetadata extends ContentMetadataBuilder implement return (BaseMutableContentMetadata) new BaseMutableContentMetadata().contentType(in.getContentType()) .contentLength(in.getContentLength()).contentMD5(in.getContentMD5()).contentDisposition( in.getContentDisposition()).contentLanguage(in.getContentLanguage()).contentEncoding( - in.getContentEncoding()); + in.getContentEncoding()).expires(in.getExpires()); } } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/logging/internal/Wire.java b/core/src/main/java/org/jclouds/logging/internal/Wire.java index 74990e9119..ece0e6ac23 100644 --- a/core/src/main/java/org/jclouds/logging/internal/Wire.java +++ b/core/src/main/java/org/jclouds/logging/internal/Wire.java @@ -138,6 +138,7 @@ public abstract class Wire { wiredMd.setContentDisposition(oldMd.getContentDisposition()); wiredMd.setContentEncoding(oldMd.getContentEncoding()); wiredMd.setContentLanguage(oldMd.getContentLanguage()); + wiredMd.setExpires(oldMd.getExpires()); } @SuppressWarnings("unchecked") diff --git a/core/src/test/java/org/jclouds/rest/internal/BaseRestClientTest.java b/core/src/test/java/org/jclouds/rest/internal/BaseRestClientTest.java index cdee2c240b..ad6d5c5f1b 100644 --- a/core/src/test/java/org/jclouds/rest/internal/BaseRestClientTest.java +++ b/core/src/test/java/org/jclouds/rest/internal/BaseRestClientTest.java @@ -86,11 +86,22 @@ public abstract class BaseRestClientTest { } protected void assertPayloadEquals(HttpRequest request, String toMatch, String contentType, boolean contentMD5) { - assertPayloadEquals(request, toMatch, contentType, null, null, null, contentMD5); + assertPayloadEquals(request, toMatch, contentType, contentMD5, null); + } + + protected void assertPayloadEquals(HttpRequest request, String toMatch, String contentType, boolean contentMD5, String expires) { + assertPayloadEquals(request, toMatch, contentType, null, null, null, contentMD5, expires); } protected void assertPayloadEquals(HttpRequest request, String toMatch, String contentType, String contentDispositon, String contentEncoding, String contentLanguage, boolean contentMD5) { + assertPayloadEquals(request, toMatch, contentType, contentDispositon, contentEncoding, contentLanguage, + contentMD5, null); + } + + protected void assertPayloadEquals(HttpRequest request, String toMatch, String contentType, + String contentDispositon, String contentEncoding, String contentLanguage, boolean contentMD5, + String expires) { if (request.getPayload() == null) { assertNull(toMatch); } else { @@ -104,7 +115,7 @@ public abstract class BaseRestClientTest { Long length = new Long(payload.getBytes().length); try { assertContentHeadersEqual(request, contentType, contentDispositon, contentEncoding, contentLanguage, - length, contentMD5 ? CryptoStreams.md5(request.getPayload()) : null); + length, contentMD5 ? CryptoStreams.md5(request.getPayload()) : null, expires); } catch (IOException e) { propagate(e); } @@ -112,7 +123,7 @@ public abstract class BaseRestClientTest { } protected void assertContentHeadersEqual(HttpRequest request, String contentType, String contentDispositon, - String contentEncoding, String contentLanguage, Long length, byte[] contentMD5) { + String contentEncoding, String contentLanguage, Long length, byte[] contentMD5, String expires) { MutableContentMetadata md = request.getPayload().getContentMetadata(); if (request.getFirstHeaderOrNull(TRANSFER_ENCODING) == null) { assertEquals(md.getContentLength(), length); @@ -125,6 +136,7 @@ public abstract class BaseRestClientTest { assertEquals(md.getContentEncoding(), contentEncoding); assertEquals(md.getContentLanguage(), contentLanguage); assertEquals(md.getContentMD5(), contentMD5); + assertEquals(md.getExpires(), expires); } // FIXME Shouldn't be assertPayloadHeadersEqual? diff --git a/drivers/apachehc/src/main/java/org/jclouds/http/apachehc/ApacheHCUtils.java b/drivers/apachehc/src/main/java/org/jclouds/http/apachehc/ApacheHCUtils.java index 25e84bdfce..8c0a8375fc 100644 --- a/drivers/apachehc/src/main/java/org/jclouds/http/apachehc/ApacheHCUtils.java +++ b/drivers/apachehc/src/main/java/org/jclouds/http/apachehc/ApacheHCUtils.java @@ -152,6 +152,8 @@ public class ApacheHCUtils { apacheRequest.addHeader("Content-Encoding", payload.getContentMetadata().getContentEncoding()); if (payload.getContentMetadata().getContentLanguage() != null) apacheRequest.addHeader("Content-Language", payload.getContentMetadata().getContentLanguage()); + if (payload.getContentMetadata().getExpires() != null) + apacheRequest.addHeader("Expires", payload.getContentMetadata().getExpires()); assert (apacheRequest.getEntity() != null); } diff --git a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3ContainerLiveTest.java b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3ContainerLiveTest.java index c84e1c8d45..24a7d83a53 100644 --- a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3ContainerLiveTest.java +++ b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3ContainerLiveTest.java @@ -19,6 +19,7 @@ package org.jclouds.aws.s3.blobstore.integration; import static org.jclouds.blobstore.options.CreateContainerOptions.Builder.publicRead; +<<<<<<< HEAD import static org.testng.Assert.assertEquals; import java.io.IOException; @@ -29,11 +30,24 @@ import java.util.Set; import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.domain.Location; +======= + +import java.io.IOException; +import java.net.MalformedURLException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.jclouds.blobstore.BlobStore; +>>>>>>> Issue-647: added "Expires" header for ContentMetadata import org.jclouds.s3.blobstore.integration.S3ContainerLiveTest; import org.jclouds.util.Strings2; import org.testng.annotations.Test; +<<<<<<< HEAD import com.google.common.base.Strings; +======= +import com.google.common.base.Throwables; +>>>>>>> Issue-647: added "Expires" header for ContentMetadata /** * @author Adrian Cole @@ -44,6 +58,34 @@ public class AWSS3ContainerLiveTest extends S3ContainerLiveTest { provider = "aws-s3"; } + @Test(groups = { "live" }) + public void testCreateBlobWithExpiry() throws InterruptedException, MalformedURLException, IOException { + final String containerName = getScratchContainerName(); + BlobStore blobStore = view.getBlobStore(); + try { + final String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z"; + final String blobName = "hello"; + final String expires = new SimpleDateFormat(RFC1123_PATTERN).format(new Date(System.currentTimeMillis()+(60*1000))); + + blobStore.createContainerInLocation(null, containerName, publicRead()); + blobStore.putBlob(containerName, blobStore.blobBuilder(blobName).payload(TEST_STRING).expires(expires).build()); + + assertConsistencyAware(new Runnable() { + public void run() { + try { + String actualExpires = view.getBlobStore().getBlob(containerName, blobName).getPayload().getContentMetadata().getExpires(); + assert expires.equals(actualExpires) : "expires="+actualExpires+"; expected="+expires; + } catch (Exception e) { + Throwables.propagate(e); + } + } + }); + + } finally { + recycleContainer(containerName); + } + } + @Test(groups = { "live" }) public void testCreateBlobInLocation() throws InterruptedException, MalformedURLException, IOException { String payload = "my data"; @@ -88,5 +130,4 @@ public class AWSS3ContainerLiveTest extends S3ContainerLiveTest { } throw new NoSuchElementException("No location found with id '"+id+"'; contenders were "+locs); } - } diff --git a/providers/azureblob/src/main/java/org/jclouds/azureblob/domain/internal/BlobPropertiesImpl.java b/providers/azureblob/src/main/java/org/jclouds/azureblob/domain/internal/BlobPropertiesImpl.java index e824a01f03..cd770fbba8 100644 --- a/providers/azureblob/src/main/java/org/jclouds/azureblob/domain/internal/BlobPropertiesImpl.java +++ b/providers/azureblob/src/main/java/org/jclouds/azureblob/domain/internal/BlobPropertiesImpl.java @@ -55,7 +55,8 @@ public class BlobPropertiesImpl implements Serializable, BlobProperties { public BlobPropertiesImpl(BlobType type, String name, String container, URI url, Date lastModified, String eTag, long size, String contentType, @Nullable byte[] contentMD5, @Nullable String contentMetadata, - @Nullable String contentLanguage, LeaseStatus leaseStatus, Map metadata) { + @Nullable String contentLanguage, @Nullable String currentExpires, LeaseStatus leaseStatus, + Map metadata) { this.type = checkNotNull(type, "type"); this.leaseStatus = checkNotNull(leaseStatus, "leaseStatus"); this.name = checkNotNull(name, "name"); @@ -64,7 +65,7 @@ public class BlobPropertiesImpl implements Serializable, BlobProperties { this.lastModified = checkNotNull(lastModified, "lastModified"); this.eTag = checkNotNull(eTag, "eTag"); this.contentMetadata = new BaseImmutableContentMetadata(contentType, size, contentMD5, null, contentLanguage, - contentMetadata); + contentMetadata, currentExpires); this.metadata.putAll(checkNotNull(metadata, "metadata")); } diff --git a/providers/azureblob/src/main/java/org/jclouds/azureblob/xml/ContainerNameEnumerationResultsHandler.java b/providers/azureblob/src/main/java/org/jclouds/azureblob/xml/ContainerNameEnumerationResultsHandler.java index f307fd3445..c297847f35 100644 --- a/providers/azureblob/src/main/java/org/jclouds/azureblob/xml/ContainerNameEnumerationResultsHandler.java +++ b/providers/azureblob/src/main/java/org/jclouds/azureblob/xml/ContainerNameEnumerationResultsHandler.java @@ -70,6 +70,7 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith private String currentContentEncoding; private String currentContentLanguage; private BlobType currentBlobType; + private String currentExpires; private boolean inBlob; private boolean inBlobPrefix; private boolean inMetadata; @@ -131,8 +132,8 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith } else if (qName.equals("Blob")) { BlobProperties md = new BlobPropertiesImpl(currentBlobType, currentName, containerUrl.getPath().replace("/", ""), currentUrl, currentLastModified, currentETag, currentSize, currentContentType, - currentContentMD5, currentContentEncoding, currentContentLanguage, currentLeaseStatus, - currentMetadata); + currentContentMD5, currentContentEncoding, currentContentLanguage, currentExpires, + currentLeaseStatus, currentMetadata); blobMetadata.add(md); currentBlobType = null; currentName = null; @@ -145,6 +146,7 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith currentContentLanguage = null; currentContentMD5 = null; currentLeaseStatus = null; + currentExpires = null; currentMetadata = Maps.newHashMap(); } else if (qName.equals("Url")) { currentUrl = HttpUtils.createUri(currentText.toString().trim()); @@ -172,6 +174,10 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith currentContentLanguage = currentText.toString().trim(); if (currentContentLanguage.equals("")) currentContentLanguage = null; + } else if (qName.equals("Expires")) { + currentExpires = currentText.toString().trim(); + if (currentExpires.equals("")) + currentExpires= null; } currentText = new StringBuilder(); } diff --git a/providers/azureblob/src/test/java/org/jclouds/azureblob/blobstore/AzureBlobRequestSignerTest.java b/providers/azureblob/src/test/java/org/jclouds/azureblob/blobstore/AzureBlobRequestSignerTest.java index 03036714cc..4ee8a121b1 100644 --- a/providers/azureblob/src/test/java/org/jclouds/azureblob/blobstore/AzureBlobRequestSignerTest.java +++ b/providers/azureblob/src/test/java/org/jclouds/azureblob/blobstore/AzureBlobRequestSignerTest.java @@ -86,6 +86,7 @@ public class AzureBlobRequestSignerTest extends BaseAsyncClientTest of()), new BlobPropertiesImpl(BlobType.BLOCK_BLOB, "blob2.txt", "mycontainer", URI .create("http://myaccount.blob.core.windows.net/mycontainer/blob2.txt"), dateService .rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), "0x8CAE7D55CF6C339", 14, - "text/plain; charset=UTF-8", null, null, null, LeaseStatus.UNLOCKED, ImmutableMap + "text/plain; charset=UTF-8", null, null, null, null, LeaseStatus.UNLOCKED, ImmutableMap . of()), new BlobPropertiesImpl(BlobType.PAGE_BLOB, "newblob1.txt", "mycontainer", URI .create("http://myaccount.blob.core.windows.net/mycontainer/newblob1.txt"), dateService .rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), "0x8CAE7D55CF6C339", 25, - "text/plain; charset=UTF-8", null, null, null, LeaseStatus.UNLOCKED, ImmutableMap + "text/plain; charset=UTF-8", null, null, null, null, LeaseStatus.UNLOCKED, ImmutableMap . of())); ListBlobsResponse list = new HashSetListBlobsResponse(contents, @@ -91,7 +91,7 @@ public class ContainerNameEnumerationResultsHandlerTest extends BaseHandlerTest Set contents = ImmutableSet. of(new BlobPropertiesImpl(BlobType.BLOCK_BLOB, "a", "adriancole-blobstore3", URI.create("https://jclouds.blob.core.windows.net/adriancole-blobstore3/a"), dateService.rfc822DateParse("Sat, 30 Jan 2010 17:46:15 GMT"), "0x8CC6FEB41736428", 8, - "application/octet-stream", null, null, null, LeaseStatus.UNLOCKED, ImmutableMap. of())); + "application/octet-stream", null, null, null, null, LeaseStatus.UNLOCKED, ImmutableMap. of())); ListBlobsResponse list = new HashSetListBlobsResponse(contents, URI.create("https://jclouds.blob.core.windows.net/adriancole-blobstore3"), From cd9c830c5a73caea65da4debf75845c20eed4615 Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Mon, 14 May 2012 14:57:56 +0100 Subject: [PATCH 113/148] Issue 647: store Expires as Date; added ContentMetadataCodec for converting to/from HTTP headers --- .../DelegatingMutableContentMetadata.java | 10 +- .../blobstore/AtmosBlobRequestSignerTest.java | 8 +- .../s3/blobstore/S3BlobRequestSignerTest.java | 5 +- .../blobstore/SwiftBlobRequestSignerTest.java | 5 +- .../blobstore/TransientAsyncBlobStore.java | 8 +- .../blobstore/TransientBlobRequestSigner.java | 10 +- .../jclouds/blobstore/domain/BlobBuilder.java | 3 +- .../domain/internal/BlobBuilderImpl.java | 3 +- .../BaseBlobStoreIntegrationTest.java | 16 +++ .../main/java/org/jclouds/date/DateCodec.java | 12 ++ .../java/org/jclouds/date/DateCodecs.java | 47 +++++++ .../main/java/org/jclouds/http/HttpUtils.java | 22 ---- .../BaseHttpCommandExecutorService.java | 7 +- .../JavaUrlHttpCommandExecutorService.java | 23 ++-- .../java/org/jclouds/io/ContentMetadata.java | 14 ++- .../jclouds/io/ContentMetadataBuilder.java | 40 +----- .../org/jclouds/io/ContentMetadataCodec.java | 118 ++++++++++++++++++ .../jclouds/io/MutableContentMetadata.java | 10 +- .../BaseImmutableContentMetadata.java | 11 +- .../payloads/BaseMutableContentMetadata.java | 12 +- .../internal/RestAnnotationProcessor.java | 10 +- .../BackoffLimitedRetryHandlerTest.java | 9 +- ...kingJavaUrlHttpCommandExecutorService.java | 5 +- .../internal/BaseRestClientExpectTest.java | 11 +- .../rest/internal/BaseRestClientTest.java | 8 +- .../ApacheHCHttpCommandExecutorService.java | 14 ++- .../jclouds/http/apachehc/ApacheHCUtils.java | 39 ++++-- .../org/jclouds/gae/ConvertToGaeRequest.java | 10 +- .../jclouds/gae/ConvertToJcloudsResponse.java | 14 ++- .../gae/GaeHttpCommandExecutorService.java | 4 +- .../jclouds/gae/ConvertToGaeRequestTest.java | 3 +- .../gae/ConvertToJcloudsResponseTest.java | 3 +- .../integration/AWSS3ContainerLiveTest.java | 80 +++++++++--- .../domain/internal/BlobPropertiesImpl.java | 2 +- ...ontainerNameEnumerationResultsHandler.java | 16 ++- .../blobstore/AzureBlobRequestSignerTest.java | 5 +- 36 files changed, 431 insertions(+), 186 deletions(-) create mode 100644 core/src/main/java/org/jclouds/date/DateCodec.java create mode 100644 core/src/main/java/org/jclouds/date/DateCodecs.java create mode 100644 core/src/main/java/org/jclouds/io/ContentMetadataCodec.java diff --git a/apis/atmos/src/main/java/org/jclouds/atmos/domain/internal/DelegatingMutableContentMetadata.java b/apis/atmos/src/main/java/org/jclouds/atmos/domain/internal/DelegatingMutableContentMetadata.java index 15c19538de..041fc22fd7 100644 --- a/apis/atmos/src/main/java/org/jclouds/atmos/domain/internal/DelegatingMutableContentMetadata.java +++ b/apis/atmos/src/main/java/org/jclouds/atmos/domain/internal/DelegatingMutableContentMetadata.java @@ -19,6 +19,7 @@ package org.jclouds.atmos.domain.internal; import java.net.URI; +import java.util.Date; import org.jclouds.atmos.domain.MutableContentMetadata; import org.jclouds.io.ContentMetadataBuilder; @@ -154,17 +155,12 @@ public class DelegatingMutableContentMetadata implements MutableContentMetadata } @Override - public void setPropertiesFromHttpHeaders(Multimap headers) { - delegate.setPropertiesFromHttpHeaders(headers); - } - - @Override - public void setExpires(String expires) { + public void setExpires(Date expires) { delegate.setExpires(expires); } @Override - public String getExpires() { + public Date getExpires() { return delegate.getExpires(); } diff --git a/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/AtmosBlobRequestSignerTest.java b/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/AtmosBlobRequestSignerTest.java index bb1465f68e..7f262289ab 100644 --- a/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/AtmosBlobRequestSignerTest.java +++ b/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/AtmosBlobRequestSignerTest.java @@ -21,6 +21,9 @@ package org.jclouds.atmos.blobstore; import static org.testng.Assert.assertEquals; import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; import org.jclouds.apis.ApiMetadata; import org.jclouds.atmos.AtmosApiMetadata; @@ -32,6 +35,7 @@ import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.Blob.Factory; import org.jclouds.date.TimeStamp; import org.jclouds.http.HttpRequest; +import org.jclouds.io.ContentMetadata; import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.internal.BaseAsyncClientTest; import org.jclouds.rest.internal.RestAnnotationProcessor; @@ -89,7 +93,7 @@ public class AtmosBlobRequestSignerTest extends BaseAsyncClientTest defaultLocation, @Memoized Supplier> locations, - Factory blobFactory, Provider uriBuilders) { + Factory blobFactory, Provider uriBuilders, + ContentMetadataCodec contentMetadataCodec) { super(context, blobUtils, service, defaultLocation, locations); this.blobFactory = blobFactory; this.dateService = dateService; @@ -146,6 +149,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { this.httpGetOptionsConverter = httpGetOptionsConverter; this.ifDirectoryReturnName = ifDirectoryReturnName; this.storageStrategy = new TransientStorageStrategy(defaultLocation); + this.contentMetadataCodec = contentMetadataCodec; } /** @@ -521,7 +525,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { } private void copyPayloadHeadersToBlob(Payload payload, Blob blob) { - blob.getAllHeaders().putAll(HttpUtils.getContentHeadersFromMetadata(payload.getContentMetadata())); + blob.getAllHeaders().putAll(contentMetadataCodec.toHeaders(payload.getContentMetadata())); } /** diff --git a/blobstore/src/main/java/org/jclouds/blobstore/TransientBlobRequestSigner.java b/blobstore/src/main/java/org/jclouds/blobstore/TransientBlobRequestSigner.java index 3a6767b997..93424e3997 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/TransientBlobRequestSigner.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/TransientBlobRequestSigner.java @@ -31,6 +31,7 @@ import org.jclouds.blobstore.options.GetOptions; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpUtils; import org.jclouds.http.filters.BasicAuthentication; +import org.jclouds.io.ContentMetadataCodec; import org.jclouds.location.Provider; import com.google.common.base.Supplier; @@ -45,12 +46,15 @@ public class TransientBlobRequestSigner implements BlobRequestSigner { private final BasicAuthentication basicAuth; private final BlobToHttpGetOptions blob2HttpGetOptions; private final Supplier endpoint; - + private final ContentMetadataCodec contentMetadataCodec; + @Inject - public TransientBlobRequestSigner(BasicAuthentication basicAuth, BlobToHttpGetOptions blob2HttpGetOptions, @Provider Supplier endpoint) { + public TransientBlobRequestSigner(BasicAuthentication basicAuth, BlobToHttpGetOptions blob2HttpGetOptions, @Provider Supplier endpoint, + ContentMetadataCodec contentMetadataCodec) { this.basicAuth = checkNotNull(basicAuth, "basicAuth"); this.blob2HttpGetOptions = checkNotNull(blob2HttpGetOptions, "blob2HttpGetOptions"); this.endpoint = endpoint; + this.contentMetadataCodec = contentMetadataCodec; } @Override @@ -64,7 +68,7 @@ public class TransientBlobRequestSigner implements BlobRequestSigner { HttpRequest request = HttpRequest.builder().method("PUT").endpoint( URI.create(String.format("%s/%s/%s", endpoint.get(), container, blob.getMetadata().getName()))).payload( blob.getPayload()).headers( - HttpUtils.getContentHeadersFromMetadata(blob.getMetadata().getContentMetadata())).build(); + contentMetadataCodec.toHeaders(blob.getMetadata().getContentMetadata())).build(); return basicAuth.filter(request); } diff --git a/blobstore/src/main/java/org/jclouds/blobstore/domain/BlobBuilder.java b/blobstore/src/main/java/org/jclouds/blobstore/domain/BlobBuilder.java index 7f8777ce6a..49a6a903e4 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/domain/BlobBuilder.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/domain/BlobBuilder.java @@ -21,6 +21,7 @@ package org.jclouds.blobstore.domain; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.util.Date; import java.util.Map; import org.jclouds.blobstore.domain.internal.BlobBuilderImpl; @@ -117,7 +118,7 @@ public interface BlobBuilder { PayloadBlobBuilder contentEncoding(String contentEncoding); - PayloadBlobBuilder expires(String expires); + PayloadBlobBuilder expires(Date expires); /** * diff --git a/blobstore/src/main/java/org/jclouds/blobstore/domain/internal/BlobBuilderImpl.java b/blobstore/src/main/java/org/jclouds/blobstore/domain/internal/BlobBuilderImpl.java index bc7c255812..7943b5ef47 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/domain/internal/BlobBuilderImpl.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/domain/internal/BlobBuilderImpl.java @@ -24,6 +24,7 @@ import static org.jclouds.io.Payloads.newPayload; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.util.Date; import java.util.Map; import javax.inject.Inject; @@ -222,7 +223,7 @@ public class BlobBuilderImpl implements BlobBuilder { } @Override - public PayloadBlobBuilder expires(String expires) { + public PayloadBlobBuilder expires(Date expires) { payload.getContentMetadata().setExpires(expires); return this; } diff --git a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobStoreIntegrationTest.java b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobStoreIntegrationTest.java index c49dd1b6f8..5af7ef47fb 100644 --- a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobStoreIntegrationTest.java +++ b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobStoreIntegrationTest.java @@ -23,6 +23,7 @@ import static org.jclouds.blobstore.util.BlobStoreUtils.getContentAsStringOrNull import static org.testng.Assert.assertEquals; import java.io.IOException; +import java.util.Date; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -338,6 +339,21 @@ public class BaseBlobStoreIntegrationTest extends BaseViewLiveTest getContentHeadersFromMetadata(ContentMetadata md) { - Builder builder = ImmutableMultimap.builder(); - if (md.getContentType() != null) - builder.put(HttpHeaders.CONTENT_TYPE, md.getContentType()); - if (md.getContentDisposition() != null) - builder.put("Content-Disposition", md.getContentDisposition()); - if (md.getContentEncoding() != null) - builder.put(HttpHeaders.CONTENT_ENCODING, md.getContentEncoding()); - if (md.getContentLanguage() != null) - builder.put(HttpHeaders.CONTENT_LANGUAGE, md.getContentLanguage()); - if (md.getContentLength() != null) - builder.put(HttpHeaders.CONTENT_LENGTH, md.getContentLength() + ""); - if (md.getContentMD5() != null) - builder.put("Content-MD5", CryptoStreams.base64(md.getContentMD5())); - if (md.getExpires() != null) - builder.put(HttpHeaders.EXPIRES, md.getExpires()); - return builder.build(); - } - public static byte[] toByteArrayOrNull(PayloadEnclosing response) { if (response.getPayload() != null) { InputStream input = response.getPayload().getInput(); diff --git a/core/src/main/java/org/jclouds/http/internal/BaseHttpCommandExecutorService.java b/core/src/main/java/org/jclouds/http/internal/BaseHttpCommandExecutorService.java index dd23d06607..0e3b530708 100644 --- a/core/src/main/java/org/jclouds/http/internal/BaseHttpCommandExecutorService.java +++ b/core/src/main/java/org/jclouds/http/internal/BaseHttpCommandExecutorService.java @@ -45,6 +45,7 @@ import org.jclouds.http.HttpUtils; import org.jclouds.http.IOExceptionRetryHandler; import org.jclouds.http.handlers.DelegatingErrorHandler; import org.jclouds.http.handlers.DelegatingRetryHandler; +import org.jclouds.io.ContentMetadataCodec; import org.jclouds.logging.Logger; import org.jclouds.util.Throwables2; @@ -56,7 +57,8 @@ import com.google.common.io.NullOutputStream; */ public abstract class BaseHttpCommandExecutorService implements HttpCommandExecutorService { protected final HttpUtils utils; - + protected final ContentMetadataCodec contentMetadataCodec; + private final DelegatingRetryHandler retryHandler; private final IOExceptionRetryHandler ioRetryHandler; private final DelegatingErrorHandler errorHandler; @@ -71,11 +73,12 @@ public abstract class BaseHttpCommandExecutorService implements HttpCommandEx protected final HttpWire wire; @Inject - protected BaseHttpCommandExecutorService(HttpUtils utils, + protected BaseHttpCommandExecutorService(HttpUtils utils, ContentMetadataCodec contentMetadataCodec, @Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor, DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler, DelegatingErrorHandler errorHandler, HttpWire wire) { this.utils = checkNotNull(utils, "utils"); + this.contentMetadataCodec = checkNotNull(contentMetadataCodec, "contentMetadataCodec"); this.retryHandler = checkNotNull(retryHandler, "retryHandler"); this.ioRetryHandler = checkNotNull(ioRetryHandler, "ioRetryHandler"); this.errorHandler = checkNotNull(errorHandler, "errorHandler"); diff --git a/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java b/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java index c720d0505e..87a862419d 100644 --- a/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java +++ b/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java @@ -54,7 +54,6 @@ import javax.ws.rs.core.HttpHeaders; import org.jclouds.Constants; import org.jclouds.JcloudsVersion; -import org.jclouds.crypto.CryptoStreams; import org.jclouds.http.HttpCommandExecutorService; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; @@ -62,6 +61,7 @@ import org.jclouds.http.HttpUtils; import org.jclouds.http.IOExceptionRetryHandler; import org.jclouds.http.handlers.DelegatingErrorHandler; import org.jclouds.http.handlers.DelegatingRetryHandler; +import org.jclouds.io.ContentMetadataCodec; import org.jclouds.io.MutableContentMetadata; import org.jclouds.io.Payload; import org.jclouds.logging.Logger; @@ -90,13 +90,13 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe private final Field methodField; @Inject - public JavaUrlHttpCommandExecutorService(HttpUtils utils, + public JavaUrlHttpCommandExecutorService(HttpUtils utils, ContentMetadataCodec contentMetadataCodec, @Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor, DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler, DelegatingErrorHandler errorHandler, HttpWire wire, @Named("untrusted") HostnameVerifier verifier, @Named("untrusted") Supplier untrustedSSLContextProvider) throws SecurityException, NoSuchFieldException { - super(utils, ioWorkerExecutor, retryHandler, ioRetryHandler, errorHandler, wire); + super(utils, contentMetadataCodec, ioWorkerExecutor, retryHandler, ioRetryHandler, errorHandler, wire); if (utils.getMaxConnections() > 0) System.setProperty("http.maxConnections", String.valueOf(checkNotNull(utils, "utils").getMaxConnections())); this.untrustedSSLContextProvider = checkNotNull(untrustedSSLContextProvider, "untrustedSSLContextProvider"); @@ -136,7 +136,7 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe ImmutableMultimap headers = headerBuilder.build(); if (in != null) { Payload payload = newInputStreamPayload(in); - payload.getContentMetadata().setPropertiesFromHttpHeaders(headers); + contentMetadataCodec.fromHeaders(payload.getContentMetadata(), headers); builder.payload(payload); } builder.headers(RestAnnotationProcessor.filterOutContentHeaders(headers)); @@ -214,18 +214,9 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe if (request.getPayload() != null) { MutableContentMetadata md = request.getPayload().getContentMetadata(); - if (md.getContentMD5() != null) - connection.setRequestProperty("Content-MD5", CryptoStreams.base64(md.getContentMD5())); - if (md.getContentType() != null) - connection.setRequestProperty(HttpHeaders.CONTENT_TYPE, md.getContentType()); - if (md.getContentDisposition() != null) - connection.setRequestProperty("Content-Disposition", md.getContentDisposition()); - if (md.getContentEncoding() != null) - connection.setRequestProperty("Content-Encoding", md.getContentEncoding()); - if (md.getContentLanguage() != null) - connection.setRequestProperty("Content-Language", md.getContentLanguage()); - if (md.getExpires() != null) - connection.setRequestProperty("Expires", md.getExpires()); + for (Map.Entry entry : contentMetadataCodec.toHeaders(md).entries()) { + connection.setRequestProperty(entry.getKey(), entry.getValue()); + } if (chunked) { connection.setChunkedStreamingMode(8196); } else { diff --git a/core/src/main/java/org/jclouds/io/ContentMetadata.java b/core/src/main/java/org/jclouds/io/ContentMetadata.java index f60caea616..e9b9b25c8a 100644 --- a/core/src/main/java/org/jclouds/io/ContentMetadata.java +++ b/core/src/main/java/org/jclouds/io/ContentMetadata.java @@ -20,7 +20,9 @@ package org.jclouds.io; import static javax.ws.rs.core.HttpHeaders.CONTENT_LENGTH; import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE; +import static javax.ws.rs.core.HttpHeaders.EXPIRES; +import java.util.Date; import java.util.Set; import org.jclouds.javax.annotation.Nullable; @@ -32,8 +34,12 @@ import com.google.common.collect.ImmutableSet; */ public interface ContentMetadata { public static final Set HTTP_HEADERS = ImmutableSet.of(CONTENT_LENGTH, "Content-MD5", CONTENT_TYPE, - "Content-Disposition", "Content-Encoding", "Content-Language"); + "Content-Disposition", "Content-Encoding", "Content-Language", EXPIRES); + // See http://stackoverflow.com/questions/10584647/simpledateformat-parse-is-one-hour-out-using-rfc-1123-gmt-in-summer + // for why not using "zzz" + public final static String RFC1123_DATE_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss Z"; + /** * Returns the total size of the payload, or the chunk that's available. *

@@ -89,11 +95,13 @@ public interface ContentMetadata { /** * Gives the date/time after which the response is considered stale. * + * @throws IllegalStateException If the Expires header is non-null, and not a valid RFC 1123 date + * * @see */ @Nullable - String getExpires(); + Date getExpires(); - ContentMetadataBuilder toBuilder(); + ContentMetadataBuilder toBuilder(); } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/io/ContentMetadataBuilder.java b/core/src/main/java/org/jclouds/io/ContentMetadataBuilder.java index 647f6deda3..eee3531118 100644 --- a/core/src/main/java/org/jclouds/io/ContentMetadataBuilder.java +++ b/core/src/main/java/org/jclouds/io/ContentMetadataBuilder.java @@ -18,21 +18,14 @@ */ package org.jclouds.io; -import static com.google.common.collect.Iterables.any; -import static javax.ws.rs.core.HttpHeaders.CONTENT_LENGTH; -import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE; - import java.io.Serializable; import java.util.Arrays; -import java.util.Map.Entry; +import java.util.Date; -import org.jclouds.crypto.CryptoStreams; import org.jclouds.io.payloads.BaseImmutableContentMetadata; import org.jclouds.javax.annotation.Nullable; import com.google.common.base.Objects; -import com.google.common.base.Predicate; -import com.google.common.collect.Multimap; /** * @author Adrian Cole @@ -51,34 +44,7 @@ public class ContentMetadataBuilder implements Serializable { protected String contentDisposition; protected String contentLanguage; protected String contentEncoding; - protected String expires; - - public ContentMetadataBuilder fromHttpHeaders(Multimap headers) { - boolean chunked = any(headers.entries(), new Predicate>() { - @Override - public boolean apply(Entry input) { - return "Transfer-Encoding".equalsIgnoreCase(input.getKey()) && "chunked".equalsIgnoreCase(input.getValue()); - } - }); - for (Entry header : headers.entries()) { - if (!chunked && CONTENT_LENGTH.equalsIgnoreCase(header.getKey())) { - contentLength(new Long(header.getValue())); - } else if ("Content-MD5".equalsIgnoreCase(header.getKey())) { - contentMD5(CryptoStreams.base64(header.getValue())); - } else if (CONTENT_TYPE.equalsIgnoreCase(header.getKey())) { - contentType(header.getValue()); - } else if ("Content-Disposition".equalsIgnoreCase(header.getKey())) { - contentDisposition(header.getValue()); - } else if ("Content-Encoding".equalsIgnoreCase(header.getKey())) { - contentEncoding(header.getValue()); - } else if ("Content-Language".equalsIgnoreCase(header.getKey())) { - contentLanguage(header.getValue()); - } else if ("Expires".equalsIgnoreCase(header.getKey())) { - expires(header.getValue()); - } - } - return this; - } + protected Date expires; public ContentMetadataBuilder contentLength(@Nullable Long contentLength) { this.contentLength = contentLength; @@ -116,7 +82,7 @@ public class ContentMetadataBuilder implements Serializable { return this; } - public ContentMetadataBuilder expires(@Nullable String expires) { + public ContentMetadataBuilder expires(@Nullable Date expires) { this.expires = expires; return this; } diff --git a/core/src/main/java/org/jclouds/io/ContentMetadataCodec.java b/core/src/main/java/org/jclouds/io/ContentMetadataCodec.java new file mode 100644 index 0000000000..bd9298922a --- /dev/null +++ b/core/src/main/java/org/jclouds/io/ContentMetadataCodec.java @@ -0,0 +1,118 @@ +package org.jclouds.io; + +import static com.google.common.collect.Iterables.any; +import static javax.ws.rs.core.HttpHeaders.CONTENT_LENGTH; +import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE; +import static javax.ws.rs.core.HttpHeaders.EXPIRES; + +import java.text.ParseException; +import java.util.Date; +import java.util.Map.Entry; + +import javax.annotation.Resource; +import javax.ws.rs.core.HttpHeaders; + +import org.jclouds.crypto.CryptoStreams; +import org.jclouds.date.DateCodec; +import org.jclouds.date.DateCodecs; +import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec; +import org.jclouds.logging.Logger; + +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableMultimap.Builder; +import com.google.common.collect.Multimap; +import com.google.inject.ImplementedBy; + +@ImplementedBy(DefaultContentMetadataCodec.class) +public interface ContentMetadataCodec { + + /** + * Generates standard HTTP headers for the give metadata. + */ + public Multimap toHeaders(ContentMetadata md); + + /** + * Sets properties related to the http headers listed in {@link ContentMetadata#HTTP_HEADERS} + */ + public void fromHeaders(MutableContentMetadata contentMetadata, Multimap headers); + + /** + * Parses the 'Expires' header. + * If invalid, returns a date in the past (in accordance with HTTP 1.1 client spec). + */ + public Date parseExpires(String expires); + + /** + * Default implementation, in accordance with HTTP 1.1 spec. + * + * @author aled + */ + public static class DefaultContentMetadataCodec implements ContentMetadataCodec { + + @Resource + protected Logger logger = Logger.NULL; + + private final DateCodec httpExpiresDateCodec = DateCodecs.rfc1123(); + + protected DateCodec getExpiresDateCodec() { + return httpExpiresDateCodec; + } + + @Override + public Multimap toHeaders(ContentMetadata md) { + Builder builder = ImmutableMultimap.builder(); + if (md.getContentType() != null) + builder.put(HttpHeaders.CONTENT_TYPE, md.getContentType()); + if (md.getContentDisposition() != null) + builder.put("Content-Disposition", md.getContentDisposition()); + if (md.getContentEncoding() != null) + builder.put(HttpHeaders.CONTENT_ENCODING, md.getContentEncoding()); + if (md.getContentLanguage() != null) + builder.put(HttpHeaders.CONTENT_LANGUAGE, md.getContentLanguage()); + if (md.getContentLength() != null) + builder.put(HttpHeaders.CONTENT_LENGTH, md.getContentLength() + ""); + if (md.getContentMD5() != null) + builder.put("Content-MD5", CryptoStreams.base64(md.getContentMD5())); + if (md.getExpires() != null) + builder.put(HttpHeaders.EXPIRES, getExpiresDateCodec().toString(md.getExpires())); + return builder.build(); + } + + @Override + public void fromHeaders(MutableContentMetadata contentMetadata, Multimap headers) { + boolean chunked = any(headers.entries(), new Predicate>() { + @Override + public boolean apply(Entry input) { + return "Transfer-Encoding".equalsIgnoreCase(input.getKey()) && "chunked".equalsIgnoreCase(input.getValue()); + } + }); + for (Entry header : headers.entries()) { + if (!chunked && CONTENT_LENGTH.equalsIgnoreCase(header.getKey())) { + contentMetadata.setContentLength(new Long(header.getValue())); + } else if ("Content-MD5".equalsIgnoreCase(header.getKey())) { + contentMetadata.setContentMD5(CryptoStreams.base64(header.getValue())); + } else if (CONTENT_TYPE.equalsIgnoreCase(header.getKey())) { + contentMetadata.setContentType(header.getValue()); + } else if ("Content-Disposition".equalsIgnoreCase(header.getKey())) { + contentMetadata.setContentDisposition(header.getValue()); + } else if ("Content-Encoding".equalsIgnoreCase(header.getKey())) { + contentMetadata.setContentEncoding(header.getValue()); + } else if ("Content-Language".equalsIgnoreCase(header.getKey())) { + contentMetadata.setContentLanguage(header.getValue()); + } else if (EXPIRES.equalsIgnoreCase(header.getKey())) { + contentMetadata.setExpires(parseExpires(header.getValue())); + } + } + } + + public Date parseExpires(String expires) { + try { + return (expires != null) ? getExpiresDateCodec().toDate(expires) : null; + } catch (ParseException e) { + logger.warn(e, "Invalid Expires header (%s); should be in RFC-1123 format; treating as already expired", expires); + return new Date(0); + } + } + } +} diff --git a/core/src/main/java/org/jclouds/io/MutableContentMetadata.java b/core/src/main/java/org/jclouds/io/MutableContentMetadata.java index e9e0a36901..5e8d91782e 100644 --- a/core/src/main/java/org/jclouds/io/MutableContentMetadata.java +++ b/core/src/main/java/org/jclouds/io/MutableContentMetadata.java @@ -18,6 +18,8 @@ */ package org.jclouds.io; +import java.util.Date; + import org.jclouds.javax.annotation.Nullable; import com.google.common.collect.Multimap; @@ -26,12 +28,6 @@ import com.google.common.collect.Multimap; * @author Adrian Cole */ public interface MutableContentMetadata extends ContentMetadata { - /** - * sets properties related to the http headers listed in - * {@link ContentMetadata#HTTP_HEADERS} - * - */ - void setPropertiesFromHttpHeaders(Multimap headers); void setContentLength(@Nullable Long contentLength); @@ -66,5 +62,5 @@ public interface MutableContentMetadata extends ContentMetadata { */ void setContentEncoding(@Nullable String contentEncoding); - void setExpires(@Nullable String expires); + void setExpires(@Nullable Date expires); } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/io/payloads/BaseImmutableContentMetadata.java b/core/src/main/java/org/jclouds/io/payloads/BaseImmutableContentMetadata.java index c781366e1f..a54e966c7f 100644 --- a/core/src/main/java/org/jclouds/io/payloads/BaseImmutableContentMetadata.java +++ b/core/src/main/java/org/jclouds/io/payloads/BaseImmutableContentMetadata.java @@ -19,7 +19,10 @@ package org.jclouds.io.payloads; import java.io.Serializable; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.Arrays; +import java.util.Date; import org.jclouds.io.ContentMetadata; import org.jclouds.io.ContentMetadataBuilder; @@ -39,10 +42,10 @@ public class BaseImmutableContentMetadata implements ContentMetadata, Serializab protected String contentDisposition; protected String contentLanguage; protected String contentEncoding; - protected String expires; + protected Date expires; public BaseImmutableContentMetadata(String contentType, Long contentLength, byte[] contentMD5, - String contentDisposition, String contentLanguage, String contentEncoding, String expires) { + String contentDisposition, String contentLanguage, String contentEncoding, Date expires) { this.contentType = contentType; this.contentLength = contentLength; this.contentMD5 = contentMD5; @@ -110,8 +113,8 @@ public class BaseImmutableContentMetadata implements ContentMetadata, Serializab * {@inheritDoc} */ @Override - public String getExpires() { - return this.expires; + public Date getExpires() { + return expires; } @Override diff --git a/core/src/main/java/org/jclouds/io/payloads/BaseMutableContentMetadata.java b/core/src/main/java/org/jclouds/io/payloads/BaseMutableContentMetadata.java index 50c32e878a..ac2045e966 100644 --- a/core/src/main/java/org/jclouds/io/payloads/BaseMutableContentMetadata.java +++ b/core/src/main/java/org/jclouds/io/payloads/BaseMutableContentMetadata.java @@ -19,6 +19,7 @@ package org.jclouds.io.payloads; import java.io.Serializable; +import java.util.Date; import org.jclouds.io.ContentMetadata; import org.jclouds.io.ContentMetadataBuilder; @@ -34,11 +35,6 @@ public class BaseMutableContentMetadata extends ContentMetadataBuilder implement /** The serialVersionUID */ private static final long serialVersionUID = 8364286391963469370L; - @Override - public void setPropertiesFromHttpHeaders(Multimap headers) { - fromHttpHeaders(headers); - } - /** * {@inheritDoc} */ @@ -145,7 +141,7 @@ public class BaseMutableContentMetadata extends ContentMetadataBuilder implement * {@inheritDoc} */ @Override - public void setExpires(@Nullable String expires) { + public void setExpires(@Nullable Date expires) { expires(expires); } @@ -153,8 +149,8 @@ public class BaseMutableContentMetadata extends ContentMetadataBuilder implement * {@inheritDoc} */ @Override - public String getExpires() { - return this.expires; + public Date getExpires() { + return expires; } @Override diff --git a/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java b/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java index 0d4a878f78..30478ebd0a 100644 --- a/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java +++ b/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java @@ -89,6 +89,7 @@ import org.jclouds.http.options.HttpRequestOptions; import org.jclouds.http.utils.ModifyRequest; import org.jclouds.internal.ClassMethodArgs; import org.jclouds.io.ContentMetadata; +import org.jclouds.io.ContentMetadataCodec; import org.jclouds.io.Payload; import org.jclouds.io.PayloadEnclosing; import org.jclouds.io.Payloads; @@ -255,6 +256,7 @@ public class RestAnnotationProcessor { private final ParseSax.Factory parserFactory; private final HttpUtils utils; + private final ContentMetadataCodec contentMetadataCodec; private final Provider uriBuilderProvider; private final LoadingCache, Boolean> seedAnnotationCache; private final String apiVersion; @@ -321,11 +323,12 @@ public class RestAnnotationProcessor { @Inject public RestAnnotationProcessor(Injector injector, LoadingCache, Boolean> seedAnnotationCache, @ApiVersion String apiVersion, @BuildVersion String buildVersion, ParseSax.Factory parserFactory, - HttpUtils utils, TypeLiteral typeLiteral) throws ExecutionException { + HttpUtils utils, ContentMetadataCodec contentMetadataCodec, TypeLiteral typeLiteral) throws ExecutionException { this.declaring = (Class) typeLiteral.getRawType(); this.injector = injector; this.parserFactory = parserFactory; this.utils = utils; + this.contentMetadataCodec = contentMetadataCodec; this.uriBuilderProvider = injector.getProvider(UriBuilder.class); this.seedAnnotationCache = seedAnnotationCache; seedAnnotationCache.get(declaring); @@ -545,8 +548,9 @@ public class RestAnnotationProcessor { request = decorateRequest(request); } - if (request.getPayload() != null) - request.getPayload().getContentMetadata().setPropertiesFromHttpHeaders(headers); + if (request.getPayload() != null) { + contentMetadataCodec.fromHeaders(request.getPayload().getContentMetadata(), headers); + } utils.checkRequestHasRequiredProperties(request); return request; } catch (ExecutionException e) { diff --git a/core/src/test/java/org/jclouds/http/handlers/BackoffLimitedRetryHandlerTest.java b/core/src/test/java/org/jclouds/http/handlers/BackoffLimitedRetryHandlerTest.java index 2c9ac3d409..7f18803f93 100644 --- a/core/src/test/java/org/jclouds/http/handlers/BackoffLimitedRetryHandlerTest.java +++ b/core/src/test/java/org/jclouds/http/handlers/BackoffLimitedRetryHandlerTest.java @@ -44,7 +44,9 @@ import org.jclouds.http.TransformingHttpCommandImpl; import org.jclouds.http.functions.ReturnStringIf2xx; import org.jclouds.http.internal.HttpWire; import org.jclouds.http.internal.JavaUrlHttpCommandExecutorService; +import org.jclouds.io.ContentMetadataCodec; import org.jclouds.io.Payloads; +import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec; import org.jclouds.rest.internal.RestAnnotationProcessor; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; @@ -105,15 +107,16 @@ public class BackoffLimitedRetryHandlerTest { } }; - private HttpUtils utils; @BeforeTest void setupExecutorService() throws Exception { ExecutorService execService = Executors.newCachedThreadPool(); BackoffLimitedRetryHandler backoff = new BackoffLimitedRetryHandler(); - utils = new HttpUtils(0, 500, 1, 1); + HttpUtils utils = new HttpUtils(0, 500, 1, 1); + ContentMetadataCodec contentMetadataCodec = new DefaultContentMetadataCodec(); RedirectionRetryHandler retry = new RedirectionRetryHandler(uriBuilderProvider, backoff); - JavaUrlHttpCommandExecutorService httpService = new JavaUrlHttpCommandExecutorService(utils, execService, + JavaUrlHttpCommandExecutorService httpService = new JavaUrlHttpCommandExecutorService(utils, + contentMetadataCodec, execService, new DelegatingRetryHandler(backoff, retry), new BackoffLimitedRetryHandler(), new DelegatingErrorHandler(), new HttpWire(), new HostnameVerifier() { diff --git a/core/src/test/java/org/jclouds/http/internal/TrackingJavaUrlHttpCommandExecutorService.java b/core/src/test/java/org/jclouds/http/internal/TrackingJavaUrlHttpCommandExecutorService.java index db0dba6196..44c55b1f07 100644 --- a/core/src/test/java/org/jclouds/http/internal/TrackingJavaUrlHttpCommandExecutorService.java +++ b/core/src/test/java/org/jclouds/http/internal/TrackingJavaUrlHttpCommandExecutorService.java @@ -37,6 +37,7 @@ import org.jclouds.http.HttpUtils; import org.jclouds.http.IOExceptionRetryHandler; import org.jclouds.http.handlers.DelegatingErrorHandler; import org.jclouds.http.handlers.DelegatingRetryHandler; +import org.jclouds.io.ContentMetadataCodec; import org.jclouds.rest.internal.GeneratedHttpRequest; import com.google.common.base.Supplier; @@ -86,13 +87,13 @@ public class TrackingJavaUrlHttpCommandExecutorService extends JavaUrlHttpComman } @Inject - public TrackingJavaUrlHttpCommandExecutorService(HttpUtils utils, + public TrackingJavaUrlHttpCommandExecutorService(HttpUtils utils, ContentMetadataCodec contentMetadataCodec, @Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor, DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler, DelegatingErrorHandler errorHandler, HttpWire wire, @Named("untrusted") HostnameVerifier verifier, @Named("untrusted") Supplier untrustedSSLContextProvider, List commandsInvoked) throws SecurityException, NoSuchFieldException { - super(utils, ioWorkerExecutor, retryHandler, ioRetryHandler, errorHandler, wire, verifier, + super(utils, contentMetadataCodec, ioWorkerExecutor, retryHandler, ioRetryHandler, errorHandler, wire, verifier, untrustedSSLContextProvider); this.commandsInvoked = commandsInvoked; } diff --git a/core/src/test/java/org/jclouds/rest/internal/BaseRestClientExpectTest.java b/core/src/test/java/org/jclouds/rest/internal/BaseRestClientExpectTest.java index d90abf86fa..45f43a2fb4 100644 --- a/core/src/test/java/org/jclouds/rest/internal/BaseRestClientExpectTest.java +++ b/core/src/test/java/org/jclouds/rest/internal/BaseRestClientExpectTest.java @@ -25,9 +25,9 @@ import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.NoSuchElementException; import java.util.Properties; -import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicInteger; @@ -59,9 +59,11 @@ import org.jclouds.http.handlers.DelegatingErrorHandler; import org.jclouds.http.handlers.DelegatingRetryHandler; import org.jclouds.http.internal.BaseHttpCommandExecutorService; import org.jclouds.http.internal.HttpWire; +import org.jclouds.io.ContentMetadataCodec; import org.jclouds.io.CopyInputStreamInputSupplierMap; import org.jclouds.io.Payload; import org.jclouds.io.Payloads; +import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec; import org.jclouds.logging.config.NullLoggingModule; import org.jclouds.providers.ProviderMetadata; import org.jclouds.rest.RestApiMetadata; @@ -118,6 +120,8 @@ public abstract class BaseRestClientExpectTest { protected String provider = "mock"; + protected ContentMetadataCodec contentMetadataCodec = new DefaultContentMetadataCodec(); + /** * Override this to supply alternative bindings for use in the test. This is commonly used to * override suppliers of dates so that the test results are predicatable. @@ -188,10 +192,11 @@ public abstract class BaseRestClientExpectTest { @Inject public ExpectHttpCommandExecutorService(Function fn, HttpUtils utils, + ContentMetadataCodec contentMetadataCodec, @Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioExecutor, IOExceptionRetryHandler ioRetryHandler, DelegatingRetryHandler retryHandler, DelegatingErrorHandler errorHandler, HttpWire wire) { - super(utils, ioExecutor, retryHandler, ioRetryHandler, errorHandler, wire); + super(utils, contentMetadataCodec, ioExecutor, retryHandler, ioRetryHandler, errorHandler, wire); this.fn = checkNotNull(fn, "fn"); } @@ -471,7 +476,7 @@ public abstract class BaseRestClientExpectTest { builder.append(header.getKey()).append(": ").append(header.getValue()).append('\n'); } if (request.getPayload() != null) { - for (Entry header : HttpUtils.getContentHeadersFromMetadata( + for (Entry header : contentMetadataCodec.toHeaders( request.getPayload().getContentMetadata()).entries()) { builder.append(header.getKey()).append(": ").append(header.getValue()).append('\n'); } diff --git a/core/src/test/java/org/jclouds/rest/internal/BaseRestClientTest.java b/core/src/test/java/org/jclouds/rest/internal/BaseRestClientTest.java index ad6d5c5f1b..4cbca9ffc5 100644 --- a/core/src/test/java/org/jclouds/rest/internal/BaseRestClientTest.java +++ b/core/src/test/java/org/jclouds/rest/internal/BaseRestClientTest.java @@ -28,6 +28,7 @@ import static org.testng.Assert.assertNull; import java.io.IOException; import java.lang.reflect.Method; +import java.util.Date; import java.util.concurrent.ExecutorService; import org.jclouds.Constants; @@ -42,7 +43,6 @@ import org.jclouds.http.functions.ParseSax; import org.jclouds.io.MutableContentMetadata; import org.jclouds.javax.annotation.Nullable; import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions; -import org.jclouds.rest.internal.RestAnnotationProcessor; import org.jclouds.util.Strings2; import org.testng.annotations.Test; @@ -89,7 +89,7 @@ public abstract class BaseRestClientTest { assertPayloadEquals(request, toMatch, contentType, contentMD5, null); } - protected void assertPayloadEquals(HttpRequest request, String toMatch, String contentType, boolean contentMD5, String expires) { + protected void assertPayloadEquals(HttpRequest request, String toMatch, String contentType, boolean contentMD5, Date expires) { assertPayloadEquals(request, toMatch, contentType, null, null, null, contentMD5, expires); } @@ -101,7 +101,7 @@ public abstract class BaseRestClientTest { protected void assertPayloadEquals(HttpRequest request, String toMatch, String contentType, String contentDispositon, String contentEncoding, String contentLanguage, boolean contentMD5, - String expires) { + Date expires) { if (request.getPayload() == null) { assertNull(toMatch); } else { @@ -123,7 +123,7 @@ public abstract class BaseRestClientTest { } protected void assertContentHeadersEqual(HttpRequest request, String contentType, String contentDispositon, - String contentEncoding, String contentLanguage, Long length, byte[] contentMD5, String expires) { + String contentEncoding, String contentLanguage, Long length, byte[] contentMD5, Date expires) { MutableContentMetadata md = request.getPayload().getContentMetadata(); if (request.getFirstHeaderOrNull(TRANSFER_ENCODING) == null) { assertEquals(md.getContentLength(), length); diff --git a/drivers/apachehc/src/main/java/org/jclouds/http/apachehc/ApacheHCHttpCommandExecutorService.java b/drivers/apachehc/src/main/java/org/jclouds/http/apachehc/ApacheHCHttpCommandExecutorService.java index 6d1ba0c7e2..ac1b99a464 100644 --- a/drivers/apachehc/src/main/java/org/jclouds/http/apachehc/ApacheHCHttpCommandExecutorService.java +++ b/drivers/apachehc/src/main/java/org/jclouds/http/apachehc/ApacheHCHttpCommandExecutorService.java @@ -39,6 +39,7 @@ import org.jclouds.http.handlers.DelegatingErrorHandler; import org.jclouds.http.handlers.DelegatingRetryHandler; import org.jclouds.http.internal.BaseHttpCommandExecutorService; import org.jclouds.http.internal.HttpWire; +import org.jclouds.io.ContentMetadataCodec; import org.jclouds.io.Payload; import org.jclouds.io.Payloads; import org.jclouds.rest.internal.RestAnnotationProcessor; @@ -55,19 +56,21 @@ import com.google.inject.Inject; */ public class ApacheHCHttpCommandExecutorService extends BaseHttpCommandExecutorService { private final HttpClient client; + private final ApacheHCUtils apacheHCUtils; @Inject - ApacheHCHttpCommandExecutorService(HttpUtils utils, + ApacheHCHttpCommandExecutorService(HttpUtils utils, ContentMetadataCodec contentMetadataCodec, @Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor, DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler, DelegatingErrorHandler errorHandler, HttpWire wire, HttpClient client) { - super(utils, ioWorkerExecutor, retryHandler, ioRetryHandler, errorHandler, wire); + super(utils, contentMetadataCodec, ioWorkerExecutor, retryHandler, ioRetryHandler, errorHandler, wire); this.client = client; + this.apacheHCUtils = new ApacheHCUtils(contentMetadataCodec); } @Override protected HttpUriRequest convert(HttpRequest request) throws IOException { - HttpUriRequest returnVal = ApacheHCUtils.convertToApacheRequest(request); + HttpUriRequest returnVal = apacheHCUtils.convertToApacheRequest(request); if (request.getPayload() != null && request.getPayload().getContentMetadata().getContentMD5() != null) returnVal.addHeader("Content-MD5", CryptoStreams.md5Base64(request.getPayload())); return returnVal; @@ -93,8 +96,9 @@ public class ApacheHCHttpCommandExecutorService extends BaseHttpCommandExecutorS for (Header header : apacheResponse.getAllHeaders()) { headers.put(header.getName(), header.getValue()); } - if (payload != null) - payload.getContentMetadata().setPropertiesFromHttpHeaders(headers); + if (payload != null) { + contentMetadataCodec.fromHeaders(payload.getContentMetadata(), headers); + } return new HttpResponse(apacheResponse.getStatusLine().getStatusCode(), apacheResponse.getStatusLine() .getReasonPhrase(), payload, RestAnnotationProcessor.filterOutContentHeaders(headers)); } diff --git a/drivers/apachehc/src/main/java/org/jclouds/http/apachehc/ApacheHCUtils.java b/drivers/apachehc/src/main/java/org/jclouds/http/apachehc/ApacheHCUtils.java index 8c0a8375fc..a9d646e756 100644 --- a/drivers/apachehc/src/main/java/org/jclouds/http/apachehc/ApacheHCUtils.java +++ b/drivers/apachehc/src/main/java/org/jclouds/http/apachehc/ApacheHCUtils.java @@ -23,6 +23,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; +import java.util.Map; +import java.util.Set; import javax.inject.Singleton; import javax.ws.rs.HttpMethod; @@ -45,6 +47,8 @@ import org.apache.http.entity.StringEntity; import org.apache.http.params.CoreProtocolPNames; import org.jclouds.JcloudsVersion; import org.jclouds.http.HttpRequest; +import org.jclouds.io.ContentMetadataCodec; +import org.jclouds.io.MutableContentMetadata; import org.jclouds.io.Payload; import org.jclouds.io.payloads.BasePayload; import org.jclouds.io.payloads.ByteArrayPayload; @@ -53,6 +57,7 @@ import org.jclouds.io.payloads.FilePayload; import org.jclouds.io.payloads.StringPayload; import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableSet; /** * @@ -62,8 +67,14 @@ import com.google.common.base.Throwables; public class ApacheHCUtils { //TODO: look up httpclient version public static final String USER_AGENT = String.format("jclouds/%s httpclient/%s", JcloudsVersion.get(), "4.1.1"); + + private final ContentMetadataCodec contentMetadataCodec; - public static HttpUriRequest convertToApacheRequest(HttpRequest request) { + public ApacheHCUtils(ContentMetadataCodec contentMetadataCodec) { + this.contentMetadataCodec = contentMetadataCodec; + } + + public HttpUriRequest convertToApacheRequest(HttpRequest request) { HttpUriRequest apacheRequest; if (request.getMethod().equals(HttpMethod.HEAD)) { apacheRequest = new HttpHead(request.getEndpoint()); @@ -120,7 +131,7 @@ public class ApacheHCUtils { return apacheRequest; } - public static void addEntityForContent(HttpEntityEnclosingRequest apacheRequest, Payload payload) { + public void addEntityForContent(HttpEntityEnclosingRequest apacheRequest, Payload payload) { payload = payload instanceof DelegatingPayload ? DelegatingPayload.class.cast(payload).getDelegate() : payload; if (payload instanceof StringPayload) { StringEntity nStringEntity = null; @@ -142,18 +153,20 @@ public class ApacheHCUtils { InputStream inputStream = payload.getInput(); if (payload.getContentMetadata().getContentLength() == null) throw new IllegalArgumentException("you must specify size when content is an InputStream"); - InputStreamEntity Entity = new InputStreamEntity(inputStream, payload.getContentMetadata().getContentLength()); - Entity.setContentType(payload.getContentMetadata().getContentType()); - apacheRequest.setEntity(Entity); + InputStreamEntity entity = new InputStreamEntity(inputStream, payload.getContentMetadata().getContentLength()); + entity.setContentType(payload.getContentMetadata().getContentType()); + apacheRequest.setEntity(entity); } - if (payload.getContentMetadata().getContentDisposition() != null) - apacheRequest.addHeader("Content-Disposition", payload.getContentMetadata().getContentDisposition()); - if (payload.getContentMetadata().getContentEncoding() != null) - apacheRequest.addHeader("Content-Encoding", payload.getContentMetadata().getContentEncoding()); - if (payload.getContentMetadata().getContentLanguage() != null) - apacheRequest.addHeader("Content-Language", payload.getContentMetadata().getContentLanguage()); - if (payload.getContentMetadata().getExpires() != null) - apacheRequest.addHeader("Expires", payload.getContentMetadata().getExpires()); + + // TODO Reproducing old behaviour exactly; ignoring Content-Type, Content-Length and Content-MD5 + Set desiredHeaders = ImmutableSet.of("Content-Disposition", "Content-Encoding", "Content-Language", "Expires"); + MutableContentMetadata md = payload.getContentMetadata(); + for (Map.Entry entry : contentMetadataCodec.toHeaders(md).entries()) { + if (desiredHeaders.contains(entry.getKey())) { + apacheRequest.addHeader(entry.getKey(), entry.getValue()); + } + } + assert (apacheRequest.getEntity() != null); } diff --git a/drivers/gae/src/main/java/org/jclouds/gae/ConvertToGaeRequest.java b/drivers/gae/src/main/java/org/jclouds/gae/ConvertToGaeRequest.java index b681d76b2d..b6c6c608a3 100644 --- a/drivers/gae/src/main/java/org/jclouds/gae/ConvertToGaeRequest.java +++ b/drivers/gae/src/main/java/org/jclouds/gae/ConvertToGaeRequest.java @@ -34,6 +34,7 @@ import javax.ws.rs.core.HttpHeaders; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpUtils; +import org.jclouds.io.ContentMetadataCodec; import org.jclouds.io.Payload; import com.google.appengine.api.urlfetch.FetchOptions; @@ -52,14 +53,17 @@ import com.google.common.io.Closeables; @Singleton public class ConvertToGaeRequest implements Function { public static final String USER_AGENT = "jclouds/1.0 urlfetch/1.4.3"; - protected final HttpUtils utils; // http://code.google.com/appengine/docs/java/urlfetch/overview.html public final Set prohibitedHeaders = ImmutableSet.of("Accept-Encoding", "Content-Length", "Host", "Var", "X-Forwarded-For"); + protected final HttpUtils utils; + protected final ContentMetadataCodec contentMetadataCodec; + @Inject - ConvertToGaeRequest(HttpUtils utils) { + ConvertToGaeRequest(HttpUtils utils, ContentMetadataCodec contentMetadataCodec) { this.utils = utils; + this.contentMetadataCodec = contentMetadataCodec; } /** @@ -115,7 +119,7 @@ public class ConvertToGaeRequest implements Function { Closeables.closeQuietly(input); } - for (Entry header : HttpUtils.getContentHeadersFromMetadata( + for (Entry header : contentMetadataCodec.toHeaders( request.getPayload().getContentMetadata()).entries()) { if (!prohibitedHeaders.contains(header.getKey())) gaeRequest.setHeader(new HTTPHeader(header.getKey(), header.getValue())); diff --git a/drivers/gae/src/main/java/org/jclouds/gae/ConvertToJcloudsResponse.java b/drivers/gae/src/main/java/org/jclouds/gae/ConvertToJcloudsResponse.java index f30a3d7181..7ff83988e5 100644 --- a/drivers/gae/src/main/java/org/jclouds/gae/ConvertToJcloudsResponse.java +++ b/drivers/gae/src/main/java/org/jclouds/gae/ConvertToJcloudsResponse.java @@ -21,6 +21,7 @@ package org.jclouds.gae; import javax.inject.Singleton; import org.jclouds.http.HttpResponse; +import org.jclouds.io.ContentMetadataCodec; import org.jclouds.io.Payload; import org.jclouds.io.Payloads; import org.jclouds.rest.internal.RestAnnotationProcessor; @@ -30,6 +31,7 @@ import com.google.appengine.api.urlfetch.HTTPResponse; import com.google.common.base.Function; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; +import com.google.inject.Inject; /** * @@ -38,6 +40,13 @@ import com.google.common.collect.Multimap; @Singleton public class ConvertToJcloudsResponse implements Function { + private final ContentMetadataCodec contentMetadataCodec; + + @Inject + public ConvertToJcloudsResponse(ContentMetadataCodec contentMetadataCodec) { + this.contentMetadataCodec = contentMetadataCodec; + } + @Override public HttpResponse apply(HTTPResponse gaeResponse) { Payload payload = gaeResponse.getContent() != null ? Payloads.newByteArrayPayload(gaeResponse.getContent()) @@ -51,8 +60,9 @@ public class ConvertToJcloudsResponse implements Function>>>>>> Issue-647: added "Expires" header for ContentMetadata +import org.jclouds.blobstore.BlobStoreContext; +import org.jclouds.date.DateCodec; +import org.jclouds.date.DateCodecs; +import org.jclouds.http.HttpCommandExecutorService; +import org.jclouds.http.TransformingHttpCommandExecutorService; +import org.jclouds.http.TransformingHttpCommandExecutorServiceImpl; +import org.jclouds.http.config.SSLModule; +import org.jclouds.http.internal.JavaUrlHttpCommandExecutorService; +import org.jclouds.io.ContentMetadataCodec; +import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec; import org.jclouds.s3.blobstore.integration.S3ContainerLiveTest; import org.jclouds.util.Strings2; import org.testng.annotations.Test; -<<<<<<< HEAD import com.google.common.base.Strings; -======= import com.google.common.base.Throwables; ->>>>>>> Issue-647: added "Expires" header for ContentMetadata +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import com.google.inject.AbstractModule; +import com.google.inject.Module; +import com.google.inject.Scopes; /** * @author Adrian Cole @@ -63,23 +73,57 @@ public class AWSS3ContainerLiveTest extends S3ContainerLiveTest { final String containerName = getScratchContainerName(); BlobStore blobStore = view.getBlobStore(); try { - final String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z"; final String blobName = "hello"; - final String expires = new SimpleDateFormat(RFC1123_PATTERN).format(new Date(System.currentTimeMillis()+(60*1000))); + final Date expires = new Date( (System.currentTimeMillis() / 1000) * 1000 + 60*1000); blobStore.createContainerInLocation(null, containerName, publicRead()); blobStore.putBlob(containerName, blobStore.blobBuilder(blobName).payload(TEST_STRING).expires(expires).build()); - assertConsistencyAware(new Runnable() { - public void run() { - try { - String actualExpires = view.getBlobStore().getBlob(containerName, blobName).getPayload().getContentMetadata().getExpires(); - assert expires.equals(actualExpires) : "expires="+actualExpires+"; expected="+expires; - } catch (Exception e) { - Throwables.propagate(e); + assertConsistencyAwareBlobExpiryMetadata(containerName, blobName, expires); + + } finally { + recycleContainer(containerName); + } + } + + @Test(groups = { "live" }) + public void testCreateBlobWithMalformedExpiry() throws InterruptedException, MalformedURLException, IOException { + // Create a blob that has a malformed Expires value; requires overriding the ContentMetadataCodec in Guice... + final ContentMetadataCodec contentMetadataCodec = new DefaultContentMetadataCodec() { + @Override + protected DateCodec getExpiresDateCodec() { + return new DateCodec() { + @Override public Date toDate(String date) throws ParseException { + return DateCodecs.rfc1123().toDate(date); } - } - }); + @Override public String toString(Date date) { + return "wrong"; + } + }; + } + }; + + Module customModule = new AbstractModule() { + @Override + protected void configure() { + bind(ContentMetadataCodec.class).toInstance(contentMetadataCodec); + } + }; + + Iterable modules = Iterables.concat(setupModules(), ImmutableList.of(customModule)); + BlobStoreContext naughtyBlobStoreContext = createView(setupProperties(), modules); + BlobStore naughtyBlobStore = naughtyBlobStoreContext.getBlobStore(); + + final String containerName = getScratchContainerName(); + + try { + final String blobName = "hello"; + + naughtyBlobStore.createContainerInLocation(null, containerName, publicRead()); + naughtyBlobStore.putBlob(containerName, naughtyBlobStore.blobBuilder(blobName) + .payload(TEST_STRING).expires(new Date(System.currentTimeMillis() + 60*1000)).build()); + + assertConsistencyAwareBlobExpiryMetadata(containerName, blobName, new Date(0)); } finally { recycleContainer(containerName); diff --git a/providers/azureblob/src/main/java/org/jclouds/azureblob/domain/internal/BlobPropertiesImpl.java b/providers/azureblob/src/main/java/org/jclouds/azureblob/domain/internal/BlobPropertiesImpl.java index cd770fbba8..4b64ee5788 100644 --- a/providers/azureblob/src/main/java/org/jclouds/azureblob/domain/internal/BlobPropertiesImpl.java +++ b/providers/azureblob/src/main/java/org/jclouds/azureblob/domain/internal/BlobPropertiesImpl.java @@ -55,7 +55,7 @@ public class BlobPropertiesImpl implements Serializable, BlobProperties { public BlobPropertiesImpl(BlobType type, String name, String container, URI url, Date lastModified, String eTag, long size, String contentType, @Nullable byte[] contentMD5, @Nullable String contentMetadata, - @Nullable String contentLanguage, @Nullable String currentExpires, LeaseStatus leaseStatus, + @Nullable String contentLanguage, @Nullable Date currentExpires, LeaseStatus leaseStatus, Map metadata) { this.type = checkNotNull(type, "type"); this.leaseStatus = checkNotNull(leaseStatus, "leaseStatus"); diff --git a/providers/azureblob/src/main/java/org/jclouds/azureblob/xml/ContainerNameEnumerationResultsHandler.java b/providers/azureblob/src/main/java/org/jclouds/azureblob/xml/ContainerNameEnumerationResultsHandler.java index c297847f35..60816013c3 100644 --- a/providers/azureblob/src/main/java/org/jclouds/azureblob/xml/ContainerNameEnumerationResultsHandler.java +++ b/providers/azureblob/src/main/java/org/jclouds/azureblob/xml/ContainerNameEnumerationResultsHandler.java @@ -35,6 +35,7 @@ import org.jclouds.crypto.CryptoStreams; import org.jclouds.date.DateService; import org.jclouds.http.HttpUtils; import org.jclouds.http.functions.ParseSax; +import org.jclouds.io.ContentMetadataCodec; import org.xml.sax.Attributes; import org.xml.sax.SAXException; @@ -63,6 +64,7 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith private StringBuilder currentText = new StringBuilder(); private final DateService dateParser; + private final ContentMetadataCodec contentMetadataCodec; private String delimiter; private String currentName; private long currentSize; @@ -70,7 +72,7 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith private String currentContentEncoding; private String currentContentLanguage; private BlobType currentBlobType; - private String currentExpires; + private Date currentExpires; private boolean inBlob; private boolean inBlobPrefix; private boolean inMetadata; @@ -80,8 +82,9 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith private LeaseStatus currentLeaseStatus; @Inject - public ContainerNameEnumerationResultsHandler(DateService dateParser) { + public ContainerNameEnumerationResultsHandler(DateService dateParser, ContentMetadataCodec contentMetadataCodec) { this.dateParser = dateParser; + this.contentMetadataCodec = contentMetadataCodec; } public ListBlobsResponse getResult() { @@ -175,9 +178,12 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith if (currentContentLanguage.equals("")) currentContentLanguage = null; } else if (qName.equals("Expires")) { - currentExpires = currentText.toString().trim(); - if (currentExpires.equals("")) - currentExpires= null; + String trimmedCurrentText = currentText.toString().trim(); + if (trimmedCurrentText.equals("")) { + currentExpires = null; + } else { + currentExpires = contentMetadataCodec.parseExpires(trimmedCurrentText); + } } currentText = new StringBuilder(); } diff --git a/providers/azureblob/src/test/java/org/jclouds/azureblob/blobstore/AzureBlobRequestSignerTest.java b/providers/azureblob/src/test/java/org/jclouds/azureblob/blobstore/AzureBlobRequestSignerTest.java index 4ee8a121b1..947aacf25d 100644 --- a/providers/azureblob/src/test/java/org/jclouds/azureblob/blobstore/AzureBlobRequestSignerTest.java +++ b/providers/azureblob/src/test/java/org/jclouds/azureblob/blobstore/AzureBlobRequestSignerTest.java @@ -21,6 +21,7 @@ package org.jclouds.azureblob.blobstore; import static org.testng.Assert.assertEquals; import java.io.IOException; +import java.util.Date; import org.jclouds.azureblob.AzureBlobAsyncClient; import org.jclouds.azureblob.AzureBlobProviderMetadata; @@ -86,7 +87,7 @@ public class AzureBlobRequestSignerTest extends BaseAsyncClientTest Date: Thu, 17 May 2012 10:03:49 +0100 Subject: [PATCH 114/148] Issue 647: DateCodec impl delegates to DateService --- .../filesystem/FilesystemAsyncBlobStore.java | 6 ++- .../nova/ec2/config/NovaEC2ParserModule.java | 15 ++++++ .../org/jclouds/date/DateCodecFactory.java | 17 +++++++ .../java/org/jclouds/date/DateCodecs.java | 47 ------------------- .../java/org/jclouds/date/DateService.java | 6 +++ .../date/internal/SimpleDateCodecFactory.java | 41 ++++++++++++++++ .../internal/SimpleDateFormatDateService.java | 29 +++++++++++- .../org/jclouds/io/ContentMetadataCodec.java | 10 +++- .../BackoffLimitedRetryHandlerTest.java | 5 +- .../internal/BaseRestClientExpectTest.java | 7 ++- .../jclouds/gae/ConvertToGaeRequestTest.java | 6 ++- .../gae/ConvertToJcloudsResponseTest.java | 5 +- .../jclouds/date/joda/JodaDateService.java | 18 +++++++ .../integration/AWSS3ContainerLiveTest.java | 28 ++++------- 14 files changed, 164 insertions(+), 76 deletions(-) create mode 100644 core/src/main/java/org/jclouds/date/DateCodecFactory.java delete mode 100644 core/src/main/java/org/jclouds/date/DateCodecs.java create mode 100644 core/src/main/java/org/jclouds/date/internal/SimpleDateCodecFactory.java diff --git a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java index 33bf078aad..176b291f9a 100644 --- a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java +++ b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java @@ -95,6 +95,7 @@ import org.jclouds.http.HttpResponseException; import org.jclouds.http.HttpUtils; import org.jclouds.http.options.HttpRequestOptions; import org.jclouds.io.ContentMetadata; +import org.jclouds.io.ContentMetadataCodec; import org.jclouds.io.Payload; import org.jclouds.io.Payloads; import org.jclouds.io.payloads.BaseMutableContentMetadata; @@ -124,6 +125,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { protected final DateService dateService; protected final Crypto crypto; protected final HttpGetOptionsListToGetOptions httpGetOptionsConverter; + protected final ContentMetadataCodec contentMetadataCodec; protected final IfDirectoryReturnNameStrategy ifDirectoryReturnName; protected final Factory blobFactory; protected final FilesystemStorageStrategy storageStrategy; @@ -132,6 +134,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { protected FilesystemAsyncBlobStore(BlobStoreContext context, DateService dateService, Crypto crypto, HttpGetOptionsListToGetOptions httpGetOptionsConverter, + ContentMetadataCodec contentMetadataCodec, IfDirectoryReturnNameStrategy ifDirectoryReturnName, BlobUtils blobUtils, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, @@ -143,6 +146,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { this.dateService = dateService; this.crypto = crypto; this.httpGetOptionsConverter = httpGetOptionsConverter; + this.contentMetadataCodec = contentMetadataCodec; this.ifDirectoryReturnName = ifDirectoryReturnName; this.storageStrategy = checkNotNull(storageStrategy, "Storage strategy"); } @@ -475,7 +479,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { } private void copyPayloadHeadersToBlob(Payload payload, Blob blob) { - blob.getAllHeaders().putAll(HttpUtils.getContentHeadersFromMetadata(payload.getContentMetadata())); + blob.getAllHeaders().putAll(contentMetadataCodec.toHeaders(payload.getContentMetadata())); } /** diff --git a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2ParserModule.java b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2ParserModule.java index d8f7931c3f..cb4ddd82db 100644 --- a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2ParserModule.java +++ b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2ParserModule.java @@ -114,6 +114,21 @@ public class NovaEC2ParserModule extends AbstractModule { if (Objects.equal("-", toParse)) return null; return delegate.iso8601SecondsDateParse(toParse); } + + @Override + public String rfc1123DateFormat(Date date) { + return delegate.rfc1123DateFormat(date); + } + + @Override + public String rfc1123DateFormat() { + return delegate.rfc1123DateFormat(); + } + + @Override + public Date rfc1123DateParse(String toParse) { + return delegate.rfc1123DateParse(toParse); + } } } diff --git a/core/src/main/java/org/jclouds/date/DateCodecFactory.java b/core/src/main/java/org/jclouds/date/DateCodecFactory.java new file mode 100644 index 0000000000..4dd0b97a32 --- /dev/null +++ b/core/src/main/java/org/jclouds/date/DateCodecFactory.java @@ -0,0 +1,17 @@ +package org.jclouds.date; + +import org.jclouds.date.internal.SimpleDateCodecFactory; + +import com.google.inject.ImplementedBy; + + +/** + * Codecs for converting from Date->String and vice versa. + * + * @author aled + */ +@ImplementedBy(SimpleDateCodecFactory.class) +public interface DateCodecFactory { + + public DateCodec rfc1123(); +} diff --git a/core/src/main/java/org/jclouds/date/DateCodecs.java b/core/src/main/java/org/jclouds/date/DateCodecs.java deleted file mode 100644 index 440ef8b0d7..0000000000 --- a/core/src/main/java/org/jclouds/date/DateCodecs.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.jclouds.date; - -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; - -public class DateCodecs { - - // See http://stackoverflow.com/questions/10584647/simpledateformat-parse-is-one-hour-out-using-rfc-1123-gmt-in-summer - // for why not using "zzz" - public final static String RFC1123_DATE_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss Z"; - - /* - * Use default Java Date/SimpleDateFormat classes for date manipulation, but be *very* careful to - * guard against the lack of thread safety. - */ - // @GuardedBy("this") - private static final SimpleDateFormat rfc1123SimpleDateFormat = new SimpleDateFormat(RFC1123_DATE_PATTERN, Locale.US); - - public static DateCodec rfc1123() { - return new SimpleDateCodec(rfc1123SimpleDateFormat); - } - - private static class SimpleDateCodec implements DateCodec { - - private final SimpleDateFormat dateFormat; - - SimpleDateCodec(SimpleDateFormat dateFormat) { - this.dateFormat = dateFormat; - } - - @Override - public Date toDate(String date) throws ParseException { - synchronized (dateFormat) { - return dateFormat.parse(date); - } - } - - @Override - public String toString(Date date) { - synchronized (dateFormat) { - return dateFormat.format(date); - } - } - } -} diff --git a/core/src/main/java/org/jclouds/date/DateService.java b/core/src/main/java/org/jclouds/date/DateService.java index ca1a699012..a8c0e2d9ed 100644 --- a/core/src/main/java/org/jclouds/date/DateService.java +++ b/core/src/main/java/org/jclouds/date/DateService.java @@ -60,4 +60,10 @@ public interface DateService { Date iso8601SecondsDateParse(String toParse); + String rfc1123DateFormat(Date date); + + String rfc1123DateFormat(); + + Date rfc1123DateParse(String toParse); + } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/date/internal/SimpleDateCodecFactory.java b/core/src/main/java/org/jclouds/date/internal/SimpleDateCodecFactory.java new file mode 100644 index 0000000000..20a0b7868d --- /dev/null +++ b/core/src/main/java/org/jclouds/date/internal/SimpleDateCodecFactory.java @@ -0,0 +1,41 @@ +package org.jclouds.date.internal; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.text.ParseException; +import java.util.Date; + +import org.jclouds.date.DateCodec; +import org.jclouds.date.DateCodecFactory; +import org.jclouds.date.DateService; + +import com.google.inject.Inject; + +public class SimpleDateCodecFactory implements DateCodecFactory { + + private final DateService dateService; + + private volatile DateCodec rfc1123Codec; + + @Inject + public SimpleDateCodecFactory(final DateService dateService) { + this.dateService = checkNotNull(dateService, "dateService"); + } + + public DateCodec rfc1123() { + if (rfc1123Codec == null) { + rfc1123Codec = new DateCodec() { + @Override + public Date toDate(String date) throws ParseException { + return dateService.rfc1123DateParse(date); + } + + @Override + public String toString(Date date) { + return dateService.rfc1123DateFormat(date); + } + }; + } + return rfc1123Codec; + } +} diff --git a/core/src/main/java/org/jclouds/date/internal/SimpleDateFormatDateService.java b/core/src/main/java/org/jclouds/date/internal/SimpleDateFormatDateService.java index b6a0108dc8..bcff3f0253 100644 --- a/core/src/main/java/org/jclouds/date/internal/SimpleDateFormatDateService.java +++ b/core/src/main/java/org/jclouds/date/internal/SimpleDateFormatDateService.java @@ -51,6 +51,11 @@ public class SimpleDateFormatDateService implements DateService { // @GuardedBy("this") private static final SimpleDateFormat rfc822SimpleDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US); + // See http://stackoverflow.com/questions/10584647/simpledateformat-parse-is-one-hour-out-using-rfc-1123-gmt-in-summer + // for why not using "zzz" + // @GuardedBy("this") + private static final SimpleDateFormat rfc1123SimpleDateFormat = new SimpleDateFormat("EEE, dd MMM yyyyy HH:mm:ss Z", Locale.US); + // @GuardedBy("this") private static final SimpleDateFormat cSimpleDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy", Locale.US); @@ -180,4 +185,26 @@ public class SimpleDateFormatDateService implements DateService { } } -} \ No newline at end of file + @Override + public final String rfc1123DateFormat(Date date) { + synchronized (rfc1123SimpleDateFormat) { + return rfc1123SimpleDateFormat.format(date); + } + } + + @Override + public final String rfc1123DateFormat() { + return rfc1123DateFormat(new Date()); + } + + @Override + public final Date rfc1123DateParse(String toParse) { + synchronized (rfc1123SimpleDateFormat) { + try { + return rfc1123SimpleDateFormat.parse(toParse); + } catch (ParseException pe) { + throw new RuntimeException("Error parsing data at " + pe.getErrorOffset(), pe); + } + } + } +} diff --git a/core/src/main/java/org/jclouds/io/ContentMetadataCodec.java b/core/src/main/java/org/jclouds/io/ContentMetadataCodec.java index bd9298922a..e10a572870 100644 --- a/core/src/main/java/org/jclouds/io/ContentMetadataCodec.java +++ b/core/src/main/java/org/jclouds/io/ContentMetadataCodec.java @@ -14,7 +14,7 @@ import javax.ws.rs.core.HttpHeaders; import org.jclouds.crypto.CryptoStreams; import org.jclouds.date.DateCodec; -import org.jclouds.date.DateCodecs; +import org.jclouds.date.DateCodecFactory; import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec; import org.jclouds.logging.Logger; @@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap.Builder; import com.google.common.collect.Multimap; import com.google.inject.ImplementedBy; +import com.google.inject.Inject; @ImplementedBy(DefaultContentMetadataCodec.class) public interface ContentMetadataCodec { @@ -53,8 +54,13 @@ public interface ContentMetadataCodec { @Resource protected Logger logger = Logger.NULL; - private final DateCodec httpExpiresDateCodec = DateCodecs.rfc1123(); + private final DateCodec httpExpiresDateCodec; + @Inject + public DefaultContentMetadataCodec(DateCodecFactory dateCodecs) { + httpExpiresDateCodec = dateCodecs.rfc1123(); + } + protected DateCodec getExpiresDateCodec() { return httpExpiresDateCodec; } diff --git a/core/src/test/java/org/jclouds/http/handlers/BackoffLimitedRetryHandlerTest.java b/core/src/test/java/org/jclouds/http/handlers/BackoffLimitedRetryHandlerTest.java index 7f18803f93..1672e5a72f 100644 --- a/core/src/test/java/org/jclouds/http/handlers/BackoffLimitedRetryHandlerTest.java +++ b/core/src/test/java/org/jclouds/http/handlers/BackoffLimitedRetryHandlerTest.java @@ -34,6 +34,8 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.ws.rs.core.UriBuilder; +import org.jclouds.date.internal.SimpleDateCodecFactory; +import org.jclouds.date.internal.SimpleDateFormatDateService; import org.jclouds.http.BaseJettyTest; import org.jclouds.http.HttpCommand; import org.jclouds.http.HttpResponse; @@ -113,7 +115,8 @@ public class BackoffLimitedRetryHandlerTest { ExecutorService execService = Executors.newCachedThreadPool(); BackoffLimitedRetryHandler backoff = new BackoffLimitedRetryHandler(); HttpUtils utils = new HttpUtils(0, 500, 1, 1); - ContentMetadataCodec contentMetadataCodec = new DefaultContentMetadataCodec(); + ContentMetadataCodec contentMetadataCodec = new DefaultContentMetadataCodec( + new SimpleDateCodecFactory(new SimpleDateFormatDateService())); RedirectionRetryHandler retry = new RedirectionRetryHandler(uriBuilderProvider, backoff); JavaUrlHttpCommandExecutorService httpService = new JavaUrlHttpCommandExecutorService(utils, contentMetadataCodec, execService, diff --git a/core/src/test/java/org/jclouds/rest/internal/BaseRestClientExpectTest.java b/core/src/test/java/org/jclouds/rest/internal/BaseRestClientExpectTest.java index 45f43a2fb4..7987f33f48 100644 --- a/core/src/test/java/org/jclouds/rest/internal/BaseRestClientExpectTest.java +++ b/core/src/test/java/org/jclouds/rest/internal/BaseRestClientExpectTest.java @@ -49,6 +49,8 @@ import org.jclouds.apis.ApiMetadata; import org.jclouds.concurrent.MoreExecutors; import org.jclouds.concurrent.SingleThreaded; import org.jclouds.concurrent.config.ConfiguresExecutorService; +import org.jclouds.date.internal.SimpleDateCodecFactory; +import org.jclouds.date.internal.SimpleDateFormatDateService; import org.jclouds.http.HttpCommandExecutorService; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; @@ -60,10 +62,10 @@ import org.jclouds.http.handlers.DelegatingRetryHandler; import org.jclouds.http.internal.BaseHttpCommandExecutorService; import org.jclouds.http.internal.HttpWire; import org.jclouds.io.ContentMetadataCodec; +import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec; import org.jclouds.io.CopyInputStreamInputSupplierMap; import org.jclouds.io.Payload; import org.jclouds.io.Payloads; -import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec; import org.jclouds.logging.config.NullLoggingModule; import org.jclouds.providers.ProviderMetadata; import org.jclouds.rest.RestApiMetadata; @@ -120,7 +122,8 @@ public abstract class BaseRestClientExpectTest { protected String provider = "mock"; - protected ContentMetadataCodec contentMetadataCodec = new DefaultContentMetadataCodec(); + protected ContentMetadataCodec contentMetadataCodec = new DefaultContentMetadataCodec( + new SimpleDateCodecFactory(new SimpleDateFormatDateService())); /** * Override this to supply alternative bindings for use in the test. This is commonly used to diff --git a/drivers/gae/src/test/java/org/jclouds/gae/ConvertToGaeRequestTest.java b/drivers/gae/src/test/java/org/jclouds/gae/ConvertToGaeRequestTest.java index 5596a13d7c..ea10bb2d38 100644 --- a/drivers/gae/src/test/java/org/jclouds/gae/ConvertToGaeRequestTest.java +++ b/drivers/gae/src/test/java/org/jclouds/gae/ConvertToGaeRequestTest.java @@ -32,6 +32,8 @@ import javax.ws.rs.HttpMethod; import javax.ws.rs.core.HttpHeaders; import org.jclouds.crypto.Crypto; +import org.jclouds.date.internal.SimpleDateCodecFactory; +import org.jclouds.date.internal.SimpleDateFormatDateService; import org.jclouds.encryption.internal.JCECrypto; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpUtils; @@ -72,7 +74,9 @@ public class ConvertToGaeRequestTest { @BeforeTest void setupClient() throws MalformedURLException { endPoint = URI.create("http://localhost:80/foo"); - req = new ConvertToGaeRequest(new HttpUtils(0, 0, 0, 0), new DefaultContentMetadataCodec()); + req = new ConvertToGaeRequest(new HttpUtils(0, 0, 0, 0), new DefaultContentMetadataCodec( + new SimpleDateCodecFactory(new SimpleDateFormatDateService()))); + } @Test diff --git a/drivers/gae/src/test/java/org/jclouds/gae/ConvertToJcloudsResponseTest.java b/drivers/gae/src/test/java/org/jclouds/gae/ConvertToJcloudsResponseTest.java index 1298d81fa8..bce7f1d0b6 100644 --- a/drivers/gae/src/test/java/org/jclouds/gae/ConvertToJcloudsResponseTest.java +++ b/drivers/gae/src/test/java/org/jclouds/gae/ConvertToJcloudsResponseTest.java @@ -34,6 +34,8 @@ import java.util.List; import javax.ws.rs.core.HttpHeaders; import org.jclouds.crypto.Crypto; +import org.jclouds.date.internal.SimpleDateCodecFactory; +import org.jclouds.date.internal.SimpleDateFormatDateService; import org.jclouds.encryption.internal.JCECrypto; import org.jclouds.http.HttpResponse; import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec; @@ -68,7 +70,8 @@ public class ConvertToJcloudsResponseTest { @BeforeTest void setupClient() throws MalformedURLException { endPoint = URI.create("http://localhost:80/foo"); - req = new ConvertToJcloudsResponse(new DefaultContentMetadataCodec()); + req = new ConvertToJcloudsResponse(new DefaultContentMetadataCodec( + new SimpleDateCodecFactory(new SimpleDateFormatDateService()))); } @Test diff --git a/drivers/joda/src/main/java/org/jclouds/date/joda/JodaDateService.java b/drivers/joda/src/main/java/org/jclouds/date/joda/JodaDateService.java index 3878da429d..4382b4b293 100644 --- a/drivers/joda/src/main/java/org/jclouds/date/joda/JodaDateService.java +++ b/drivers/joda/src/main/java/org/jclouds/date/joda/JodaDateService.java @@ -53,6 +53,9 @@ public class JodaDateService implements DateService { private static final DateTimeFormatter iso8601DateFormatter = DateTimeFormat.forPattern( "yyyy-MM-dd'T'HH:mm:ss.SSSZ").withLocale(Locale.US).withZone(DateTimeZone.forID("GMT")); + private static final DateTimeFormatter rfc1123DateFormat = DateTimeFormat.forPattern( + "EEE, dd MMM yyyyy HH:mm:ss Z").withLocale(Locale.US).withZone(DateTimeZone.forID("GMT")); + public final Date fromSeconds(long seconds) { return new Date(seconds * 1000); } @@ -124,4 +127,19 @@ public class JodaDateService implements DateService { toParse += tz; return iso8601SecondsDateFormatter.parseDateTime(toParse).toDate(); } + + @Override + public final String rfc1123DateFormat(Date dateTime) { + return rfc1123DateFormat.print(new DateTime(dateTime)); + } + + @Override + public final String rfc1123DateFormat() { + return rfc1123DateFormat(new Date()); + } + + @Override + public final Date rfc1123DateParse(String toParse) { + return rfc1123DateFormat.parseDateTime(toParse).toDate(); + } } \ No newline at end of file diff --git a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3ContainerLiveTest.java b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3ContainerLiveTest.java index 5ab2d67b32..78788e3ab2 100644 --- a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3ContainerLiveTest.java +++ b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3ContainerLiveTest.java @@ -23,27 +23,18 @@ import static org.testng.Assert.assertEquals; import java.io.IOException; import java.net.MalformedURLException; +import java.text.ParseException; +import java.util.Date; import java.util.NoSuchElementException; import java.util.Set; -import org.jclouds.blobstore.BlobStore; -import org.jclouds.blobstore.domain.BlobMetadata; -import org.jclouds.domain.Location; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.text.ParseException; -import java.util.Date; - import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.BlobStoreContext; +import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.date.DateCodec; -import org.jclouds.date.DateCodecs; -import org.jclouds.http.HttpCommandExecutorService; -import org.jclouds.http.TransformingHttpCommandExecutorService; -import org.jclouds.http.TransformingHttpCommandExecutorServiceImpl; -import org.jclouds.http.config.SSLModule; -import org.jclouds.http.internal.JavaUrlHttpCommandExecutorService; +import org.jclouds.date.internal.SimpleDateCodecFactory; +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.domain.Location; import org.jclouds.io.ContentMetadataCodec; import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec; import org.jclouds.s3.blobstore.integration.S3ContainerLiveTest; @@ -51,13 +42,10 @@ import org.jclouds.util.Strings2; import org.testng.annotations.Test; import com.google.common.base.Strings; -import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.inject.AbstractModule; import com.google.inject.Module; -import com.google.inject.Scopes; /** * @author Adrian Cole @@ -89,12 +77,12 @@ public class AWSS3ContainerLiveTest extends S3ContainerLiveTest { @Test(groups = { "live" }) public void testCreateBlobWithMalformedExpiry() throws InterruptedException, MalformedURLException, IOException { // Create a blob that has a malformed Expires value; requires overriding the ContentMetadataCodec in Guice... - final ContentMetadataCodec contentMetadataCodec = new DefaultContentMetadataCodec() { + final ContentMetadataCodec contentMetadataCodec = new DefaultContentMetadataCodec(new SimpleDateCodecFactory(new SimpleDateFormatDateService())) { @Override protected DateCodec getExpiresDateCodec() { return new DateCodec() { @Override public Date toDate(String date) throws ParseException { - return DateCodecs.rfc1123().toDate(date); + return new Date(); } @Override public String toString(Date date) { return "wrong"; From 612f8f2bba84ea3214acc1cc491a5ec0e3448d13 Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Fri, 11 May 2012 10:09:55 +0100 Subject: [PATCH 115/148] Issue 858: fix timeout waiting for port connection, and abort when node!=running --- ...ateSshClientOncePortIsListeningOnNode.java | 25 ++++- ...dAddToGoodMapOrPutExceptionIntoBadMap.java | 12 ++- .../compute/util/ComputeServiceUtils.java | 80 +++++++++++--- ...ToGoodMapOrPutExceptionIntoBadMapTest.java | 6 +- .../compute/util/ComputeServiceUtilsTest.java | 101 ++++++++++++++++++ 5 files changed, 194 insertions(+), 30 deletions(-) diff --git a/compute/src/main/java/org/jclouds/compute/functions/CreateSshClientOncePortIsListeningOnNode.java b/compute/src/main/java/org/jclouds/compute/functions/CreateSshClientOncePortIsListeningOnNode.java index 90a5b23c8e..7dac74005b 100644 --- a/compute/src/main/java/org/jclouds/compute/functions/CreateSshClientOncePortIsListeningOnNode.java +++ b/compute/src/main/java/org/jclouds/compute/functions/CreateSshClientOncePortIsListeningOnNode.java @@ -21,11 +21,18 @@ package org.jclouds.compute.functions; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import java.util.concurrent.TimeUnit; + +import javax.annotation.Resource; +import javax.inject.Named; import javax.inject.Singleton; import org.jclouds.compute.domain.NodeMetadata; -import org.jclouds.compute.predicates.RetryIfSocketNotYetOpen; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts; import org.jclouds.compute.util.ComputeServiceUtils; +import org.jclouds.logging.Logger; +import org.jclouds.predicates.SocketOpen; import org.jclouds.ssh.SshClient; import com.google.common.base.Function; @@ -39,13 +46,20 @@ import com.google.inject.Inject; */ @Singleton public class CreateSshClientOncePortIsListeningOnNode implements Function { + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + @Inject(optional = true) SshClient.Factory sshFactory; - private final RetryIfSocketNotYetOpen socketTester; - + private final SocketOpen socketTester; + + private final long timeoutMs; + @Inject - public CreateSshClientOncePortIsListeningOnNode(RetryIfSocketNotYetOpen socketTester) { + public CreateSshClientOncePortIsListeningOnNode(SocketOpen socketTester, Timeouts timeouts) { this.socketTester = socketTester; + this.timeoutMs = timeouts.portOpen; } @Override @@ -55,7 +69,8 @@ public class CreateSshClientOncePortIsListeningOnNode implements Function> nodeRunning; private final InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory; - private final RetryIfSocketNotYetOpen socketTester; + private final SocketOpen socketTester; private final Timeouts timeouts; @Nullable @@ -87,7 +88,7 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap implements Cal @AssistedInject public CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap( @Named("NODE_RUNNING") Predicate> nodeRunning, - RetryIfSocketNotYetOpen socketTester, Timeouts timeouts, + SocketOpen socketTester, Timeouts timeouts, Function templateOptionsToStatement, InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, @Assisted TemplateOptions options, @Assisted AtomicReference node, @Assisted Set goodNodes, @@ -109,7 +110,7 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap implements Cal @AssistedInject public CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap( @Named("NODE_RUNNING") Predicate> nodeRunning, GetNodeMetadataStrategy getNode, - RetryIfSocketNotYetOpen socketTester, Timeouts timeouts, + SocketOpen socketTester, Timeouts timeouts, Function templateOptionsToStatement, InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, @Assisted TemplateOptions options, @Assisted Set goodNodes, @Assisted Map badNodes, @@ -153,7 +154,8 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap implements Cal } } if (options.getPort() > 0) { - findReachableSocketOnNode(socketTester.seconds(options.getSeconds()), node.get(), options.getPort()); + findReachableSocketOnNode(socketTester, nodeRunning, node.get(), options.getPort(), + options.getSeconds(), TimeUnit.SECONDS, logger); } } logger.debug("<< customized node(%s)", originalId); diff --git a/compute/src/main/java/org/jclouds/compute/util/ComputeServiceUtils.java b/compute/src/main/java/org/jclouds/compute/util/ComputeServiceUtils.java index ff149ab778..8b47b0bc71 100644 --- a/compute/src/main/java/org/jclouds/compute/util/ComputeServiceUtils.java +++ b/compute/src/main/java/org/jclouds/compute/util/ComputeServiceUtils.java @@ -22,7 +22,6 @@ import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Throwables.getStackTraceAsString; import static com.google.common.collect.Iterables.concat; import static com.google.common.collect.Iterables.filter; -import static com.google.common.collect.Iterables.find; import static com.google.common.collect.Iterables.size; import static com.google.common.collect.Iterables.transform; import static org.jclouds.scriptbuilder.domain.Statements.pipeHttpResponseToBash; @@ -32,8 +31,12 @@ import java.util.Formatter; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Pattern; +import javax.annotation.Nullable; + import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.domain.ComputeMetadata; import org.jclouds.compute.domain.Hardware; @@ -41,8 +44,10 @@ import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.OsFamily; import org.jclouds.compute.domain.Processor; import org.jclouds.compute.domain.Volume; -import org.jclouds.compute.predicates.RetryIfSocketNotYetOpen; import org.jclouds.http.HttpRequest; +import org.jclouds.logging.Logger; +import org.jclouds.predicates.RetryablePredicate; +import org.jclouds.predicates.SocketOpen; import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.Statements; @@ -165,24 +170,65 @@ public class ComputeServiceUtils { return org.jclouds.rest.Providers.getSupportedProvidersOfType(TypeToken.of(ComputeServiceContext.class)); } - public static HostAndPort findReachableSocketOnNode(RetryIfSocketNotYetOpen socketTester, final NodeMetadata node, - final int port) { + public static HostAndPort findReachableSocketOnNode(final SocketOpen socketTester, final NodeMetadata node, + final int port, long timeoutValue, TimeUnit timeUnits, Logger logger) { + return findReachableSocketOnNode(socketTester, null, node, port, timeoutValue, timeUnits, logger); + } + + public static HostAndPort findReachableSocketOnNode(final SocketOpen socketTester, + @Nullable final Predicate> nodeRunning, final NodeMetadata node, + final int port, long timeoutValue, TimeUnit timeUnits, Logger logger) { checkNodeHasIps(node); - HostAndPort socket = null; - try { - socket = find(transform(concat(node.getPublicAddresses(), node.getPrivateAddresses()), - new Function() { - @Override - public HostAndPort apply(String from) { - return HostAndPort.fromParts(from, port); - } - }), socketTester); - } catch (NoSuchElementException e) { - throw new NoSuchElementException(String.format("could not connect to any ip address port %d on node %s", port, - node)); + Iterable sockets = transform(concat(node.getPublicAddresses(), node.getPrivateAddresses()), + new Function() { + + @Override + public HostAndPort apply(String from) { + return HostAndPort.fromParts(from, port); + } + }); + + // Specify a retry period of 1s, expressed in the same time units. + long period = timeUnits.convert(1, TimeUnit.SECONDS); + + // For storing the result, as predicate will just tell us true/false + final AtomicReference result = new AtomicReference(); + + Predicate> multiIpSocketTester = new Predicate>() { + + @Override + public boolean apply(Iterable sockets) { + for (HostAndPort socket : sockets) { + if (socketTester.apply(socket)) { + result.set(socket); + return true; + } + } + if (nodeRunning != null && !nodeRunning.apply(new AtomicReference(node))) { + throw new IllegalStateException(String.format("Node %s is no longer running; aborting waiting for ip:port connection", node.getId())); + } + return false; + } + + }; + + RetryablePredicate> tester = new RetryablePredicate>( + multiIpSocketTester, timeoutValue, period, timeUnits); + + logger.debug(">> blocking on sockets %s for %d %s", sockets, timeoutValue, timeUnits); + + boolean passed = tester.apply(sockets); + + if (passed) { + logger.debug("<< socket %s opened", result); + assert result.get() != null; + return result.get(); + } else { + logger.warn("<< sockets %s didn't open after %d %s", sockets, timeoutValue, timeUnits); + throw new NoSuchElementException(String.format("could not connect to any ip address port %d on node %s", + port, node)); } - return socket; } public static void checkNodeHasIps(NodeMetadata node) { diff --git a/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java b/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java index 85ae78f04d..11585e053a 100644 --- a/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java +++ b/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java @@ -34,8 +34,8 @@ import org.jclouds.compute.domain.NodeState; import org.jclouds.compute.functions.TemplateOptionsToStatement; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.predicates.AtomicNodeRunning; -import org.jclouds.compute.predicates.RetryIfSocketNotYetOpen; import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts; +import org.jclouds.predicates.SocketOpen; import org.jclouds.scriptbuilder.domain.Statement; import org.testng.Assert; import org.testng.annotations.Test; @@ -55,7 +55,7 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest { public void testBreakWhenNodeStillPending() { InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory = createMock(InitializeRunScriptOnNodeOrPlaceInBadMap.Factory.class); - RetryIfSocketNotYetOpen socketTester = createMock(RetryIfSocketNotYetOpen.class); + SocketOpen socketTester = createMock(SocketOpen.class); Timeouts timeouts = new Timeouts(); Function templateOptionsToStatement = new TemplateOptionsToStatement(); @SuppressWarnings("unused") @@ -98,7 +98,7 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest { public void testBreakGraceFullyWhenNodeDied() { InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory = createMock(InitializeRunScriptOnNodeOrPlaceInBadMap.Factory.class); - RetryIfSocketNotYetOpen socketTester = createMock(RetryIfSocketNotYetOpen.class); + SocketOpen socketTester = createMock(SocketOpen.class); Timeouts timeouts = new Timeouts(); Function templateOptionsToStatement = new TemplateOptionsToStatement(); @SuppressWarnings("unused") diff --git a/compute/src/test/java/org/jclouds/compute/util/ComputeServiceUtilsTest.java b/compute/src/test/java/org/jclouds/compute/util/ComputeServiceUtilsTest.java index 6bca217d1c..b606e1f4f0 100644 --- a/compute/src/test/java/org/jclouds/compute/util/ComputeServiceUtilsTest.java +++ b/compute/src/test/java/org/jclouds/compute/util/ComputeServiceUtilsTest.java @@ -18,21 +18,39 @@ */ package org.jclouds.compute.util; +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; import static org.jclouds.compute.util.ComputeServiceUtils.parseVersionOrReturnEmptyString; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; import java.net.URI; import java.util.Map; +import java.util.NoSuchElementException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import org.easymock.EasyMock; import org.jclouds.compute.config.BaseComputeServiceContextModule; +import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.OsFamily; +import org.jclouds.compute.predicates.SocketOpenPredicates; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.http.HttpRequest; import org.jclouds.json.Json; import org.jclouds.json.config.GsonModule; +import org.jclouds.logging.Logger; +import org.jclouds.predicates.SocketOpen; import org.testng.annotations.Test; +import com.google.common.base.Predicate; +import com.google.common.base.Stopwatch; import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; +import com.google.common.net.HostAndPort; import com.google.inject.Guice; /** @@ -79,6 +97,89 @@ public class ComputeServiceUtilsTest { ComputeServiceUtils.extractTargzIntoDirectory(request, "/stage/").render( org.jclouds.scriptbuilder.domain.OsFamily.UNIX), "curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET -H \"Host: adriancolehappy.s3.amazonaws.com\" -H \"Date: Sun, 12 Sep 2010 08:25:19 GMT\" -H \"Authorization: AWS 0ASHDJAS82:JASHFDA=\" https://adriancolehappy.s3.amazonaws.com/java/install |(mkdir -p /stage/ &&cd /stage/ &&tar -xpzf -)\n"); + } + + @Test + public void testFindReachableSocketOnNodeTimesOut() throws Exception { + final long timeoutSecs = 2; + final long timeoutMs = timeoutSecs * 1000; + final long SLOW_GRACE = 500; + final long EARLY_GRACE = 10; + + SocketOpen socketTester = SocketOpenPredicates.alwaysFail; + NodeMetadata node = createMock(NodeMetadata.class); + expect(node.getPublicAddresses()).andReturn(ImmutableSet.of("1.2.3.4")).atLeastOnce(); + expect(node.getPrivateAddresses()).andReturn(ImmutableSet.of("1.2.3.5")).atLeastOnce(); + + replay(node); + + Stopwatch stopwatch = new Stopwatch(); + stopwatch.start(); + try { + ComputeServiceUtils.findReachableSocketOnNode(socketTester, node, 1234, timeoutMs, TimeUnit.MILLISECONDS, Logger.CONSOLE); + fail(); + } catch (NoSuchElementException success) { + // expected + } + long timetaken = stopwatch.elapsedMillis(); + + assertTrue(timetaken >= timeoutMs-EARLY_GRACE && timetaken <= timeoutMs+SLOW_GRACE, "timetaken="+timetaken); + verify(node); + } + + @Test + public void testFindReachableSocketOnNodeReturnsAvailable() throws Exception { + SocketOpen socketTester = createMock(SocketOpen.class); + expect(socketTester.apply(HostAndPort.fromParts("1.2.3.4", 22))).andReturn(false); + expect(socketTester.apply(HostAndPort.fromParts("1.2.3.5", 22))).andReturn(true); + + NodeMetadata node = createMock(NodeMetadata.class); + expect(node.getPublicAddresses()).andReturn(ImmutableSet.of("1.2.3.4")).atLeastOnce(); + expect(node.getPrivateAddresses()).andReturn(ImmutableSet.of("1.2.3.5")).atLeastOnce(); + + replay(socketTester); + replay(node); + + HostAndPort result = ComputeServiceUtils.findReachableSocketOnNode(socketTester, node, 22, 2000, TimeUnit.MILLISECONDS, Logger.CONSOLE); + assertEquals(result, HostAndPort.fromParts("1.2.3.5", 22)); + verify(socketTester); + verify(node); + } + + @Test + public void testFindReachableSocketOnNodeAbortsWhenNodeNotRunning() throws Exception { + final long SLOW_GRACE = 500; + + SocketOpen socketTester = SocketOpenPredicates.alwaysFail; + + NodeMetadata node = createMock(NodeMetadata.class); + expect(node.getPublicAddresses()).andReturn(ImmutableSet.of("1.2.3.4")).atLeastOnce(); + expect(node.getPrivateAddresses()).andReturn(ImmutableSet.of("1.2.3.5")).atLeastOnce(); + expect(node.getId()).andReturn("myid").atLeastOnce(); + + Predicate> nodeRunning = createMock(Predicate.class); + expect(nodeRunning.apply(EasyMock.>anyObject())).andReturn(false); + + replay(node); + replay(nodeRunning); + + Stopwatch stopwatch = new Stopwatch(); + stopwatch.start(); + try { + ComputeServiceUtils.findReachableSocketOnNode(socketTester, nodeRunning, + node, 22, 2000000, TimeUnit.MILLISECONDS, Logger.CONSOLE); + fail(); + } catch (RuntimeException e) { + if (!e.getMessage().contains("no longer running")) { + throw e; + } + } + long timetaken = stopwatch.elapsedMillis(); + + assertTrue(timetaken <= SLOW_GRACE, "timetaken="+timetaken); + + verify(node); + verify(nodeRunning); } } From 83c9ecc3d813dbf250481bc164f59dc7168d67c5 Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Mon, 14 May 2012 10:38:10 +0100 Subject: [PATCH 116/148] Issue 858: moved RetryIfSocketNotYetOpen from compute/ to labs/virtualbox/ --- .../org/jclouds/compute/predicates/SocketOpenPredicates.java | 2 +- .../functions/admin/StartVBoxIfNotAlreadyRunning.java | 2 +- .../jclouds/virtualbox}/predicates/RetryIfSocketNotYetOpen.java | 2 +- .../functions/admin/StartVBoxIfNotAlreadyRunningLiveTest.java | 2 +- .../virtualbox}/predicates/RetryIfSocketNotYetOpenTest.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename {compute/src/main/java/org/jclouds/compute => labs/virtualbox/src/main/java/org/jclouds/virtualbox}/predicates/RetryIfSocketNotYetOpen.java (98%) rename {compute/src/test/java/org/jclouds/compute => labs/virtualbox/src/test/java/org/jclouds/virtualbox}/predicates/RetryIfSocketNotYetOpenTest.java (98%) diff --git a/compute/src/test/java/org/jclouds/compute/predicates/SocketOpenPredicates.java b/compute/src/test/java/org/jclouds/compute/predicates/SocketOpenPredicates.java index 86ddbef963..eb8aab1ce7 100644 --- a/compute/src/test/java/org/jclouds/compute/predicates/SocketOpenPredicates.java +++ b/compute/src/test/java/org/jclouds/compute/predicates/SocketOpenPredicates.java @@ -24,7 +24,7 @@ import com.google.common.net.HostAndPort; /** - * For use in unit tests, e.g. {@link RetryIfSocketNotYetOpenTest}. + * For use in unit tests. */ public class SocketOpenPredicates { diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunning.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunning.java index 6c9d561c42..d75b83d927 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunning.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunning.java @@ -33,13 +33,13 @@ import javax.inject.Singleton; import org.jclouds.compute.callables.RunScriptOnNode.Factory; import org.jclouds.compute.domain.NodeMetadata; -import org.jclouds.compute.predicates.RetryIfSocketNotYetOpen; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.location.Provider; import org.jclouds.logging.Logger; import org.jclouds.rest.annotations.Credential; import org.jclouds.rest.annotations.Identity; import org.jclouds.scriptbuilder.domain.Statements; +import org.jclouds.virtualbox.predicates.RetryIfSocketNotYetOpen; import org.virtualbox_4_1.SessionState; import org.virtualbox_4_1.VirtualBoxManager; diff --git a/compute/src/main/java/org/jclouds/compute/predicates/RetryIfSocketNotYetOpen.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/RetryIfSocketNotYetOpen.java similarity index 98% rename from compute/src/main/java/org/jclouds/compute/predicates/RetryIfSocketNotYetOpen.java rename to labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/RetryIfSocketNotYetOpen.java index 50c0dea589..b1d506b88e 100644 --- a/compute/src/main/java/org/jclouds/compute/predicates/RetryIfSocketNotYetOpen.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/RetryIfSocketNotYetOpen.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.jclouds.compute.predicates; +package org.jclouds.virtualbox.predicates; import java.util.concurrent.TimeUnit; diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunningLiveTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunningLiveTest.java index 45958fe7f4..783081c01b 100644 --- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunningLiveTest.java +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunningLiveTest.java @@ -35,8 +35,8 @@ import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.NodeMetadataBuilder; import org.jclouds.compute.domain.NodeState; import org.jclouds.compute.domain.OperatingSystem; -import org.jclouds.compute.predicates.RetryIfSocketNotYetOpen; import org.jclouds.scriptbuilder.domain.Statements; +import org.jclouds.virtualbox.predicates.RetryIfSocketNotYetOpen; import org.testng.annotations.Test; import org.virtualbox_4_1.VirtualBoxManager; diff --git a/compute/src/test/java/org/jclouds/compute/predicates/RetryIfSocketNotYetOpenTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/predicates/RetryIfSocketNotYetOpenTest.java similarity index 98% rename from compute/src/test/java/org/jclouds/compute/predicates/RetryIfSocketNotYetOpenTest.java rename to labs/virtualbox/src/test/java/org/jclouds/virtualbox/predicates/RetryIfSocketNotYetOpenTest.java index c3a24721f0..782bc77968 100644 --- a/compute/src/test/java/org/jclouds/compute/predicates/RetryIfSocketNotYetOpenTest.java +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/predicates/RetryIfSocketNotYetOpenTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.jclouds.compute.predicates; +package org.jclouds.virtualbox.predicates; import static org.jclouds.compute.predicates.SocketOpenPredicates.alwaysFail; import static org.testng.Assert.assertEquals; From b3a027f06525b608d6d7763cad9991fe5fa2c89d Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Mon, 14 May 2012 12:43:20 +0100 Subject: [PATCH 117/148] Issue 858: extracted OpenSocketFinder from ComputeServiceUtils --- ...ateSshClientOncePortIsListeningOnNode.java | 14 +- ...dAddToGoodMapOrPutExceptionIntoBadMap.java | 17 +- .../compute/util/ComputeServiceUtils.java | 79 ------- .../util/ConcurrentOpenSocketFinder.java | 170 +++++++++++++++ .../compute/util/OpenSocketFinder.java | 30 +++ ...ToGoodMapOrPutExceptionIntoBadMapTest.java | 67 +++++- .../compute/util/ComputeServiceUtilsTest.java | 102 --------- .../util/ConcurrentOpenSocketFinderTest.java | 193 ++++++++++++++++++ 8 files changed, 466 insertions(+), 206 deletions(-) create mode 100644 compute/src/main/java/org/jclouds/compute/util/ConcurrentOpenSocketFinder.java create mode 100644 compute/src/main/java/org/jclouds/compute/util/OpenSocketFinder.java create mode 100644 compute/src/test/java/org/jclouds/compute/util/ConcurrentOpenSocketFinderTest.java diff --git a/compute/src/main/java/org/jclouds/compute/functions/CreateSshClientOncePortIsListeningOnNode.java b/compute/src/main/java/org/jclouds/compute/functions/CreateSshClientOncePortIsListeningOnNode.java index 7dac74005b..efa16b7016 100644 --- a/compute/src/main/java/org/jclouds/compute/functions/CreateSshClientOncePortIsListeningOnNode.java +++ b/compute/src/main/java/org/jclouds/compute/functions/CreateSshClientOncePortIsListeningOnNode.java @@ -30,9 +30,8 @@ import javax.inject.Singleton; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts; -import org.jclouds.compute.util.ComputeServiceUtils; +import org.jclouds.compute.util.OpenSocketFinder; import org.jclouds.logging.Logger; -import org.jclouds.predicates.SocketOpen; import org.jclouds.ssh.SshClient; import com.google.common.base.Function; @@ -52,13 +51,14 @@ public class CreateSshClientOncePortIsListeningOnNode implements Function> nodeRunning; private final InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory; - private final SocketOpen socketTester; private final Timeouts timeouts; + private final OpenSocketFinder openSocketFinder; @Nullable private final Statement statement; @@ -88,7 +87,7 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap implements Cal @AssistedInject public CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap( @Named("NODE_RUNNING") Predicate> nodeRunning, - SocketOpen socketTester, Timeouts timeouts, + OpenSocketFinder openSocketFinder, Timeouts timeouts, Function templateOptionsToStatement, InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, @Assisted TemplateOptions options, @Assisted AtomicReference node, @Assisted Set goodNodes, @@ -98,7 +97,7 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap implements Cal checkNotNull(options, "options")); this.nodeRunning = checkNotNull(nodeRunning, "nodeRunning"); this.initScriptRunnerFactory = checkNotNull(initScriptRunnerFactory, "initScriptRunnerFactory"); - this.socketTester = checkNotNull(socketTester, "socketTester"); + this.openSocketFinder = checkNotNull(openSocketFinder, "openSocketFinder"); this.timeouts = checkNotNull(timeouts, "timeouts"); this.node = node; this.options = checkNotNull(options, "options"); @@ -110,12 +109,12 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap implements Cal @AssistedInject public CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap( @Named("NODE_RUNNING") Predicate> nodeRunning, GetNodeMetadataStrategy getNode, - SocketOpen socketTester, Timeouts timeouts, + OpenSocketFinder openSocketFinder, Timeouts timeouts, Function templateOptionsToStatement, InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, @Assisted TemplateOptions options, @Assisted Set goodNodes, @Assisted Map badNodes, @Assisted Multimap customizationResponses) { - this(nodeRunning, socketTester, timeouts, templateOptionsToStatement, initScriptRunnerFactory, options, + this(nodeRunning, openSocketFinder, timeouts, templateOptionsToStatement, initScriptRunnerFactory, options, new AtomicReference(null), goodNodes, badNodes, customizationResponses); } @@ -154,8 +153,8 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap implements Cal } } if (options.getPort() > 0) { - findReachableSocketOnNode(socketTester, nodeRunning, node.get(), options.getPort(), - options.getSeconds(), TimeUnit.SECONDS, logger); + openSocketFinder.findOpenSocketOnNode(node.get(), options.getPort(), + options.getSeconds(), TimeUnit.SECONDS); } } logger.debug("<< customized node(%s)", originalId); diff --git a/compute/src/main/java/org/jclouds/compute/util/ComputeServiceUtils.java b/compute/src/main/java/org/jclouds/compute/util/ComputeServiceUtils.java index 8b47b0bc71..cf3e18f27d 100644 --- a/compute/src/main/java/org/jclouds/compute/util/ComputeServiceUtils.java +++ b/compute/src/main/java/org/jclouds/compute/util/ComputeServiceUtils.java @@ -18,12 +18,8 @@ */ package org.jclouds.compute.util; -import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Throwables.getStackTraceAsString; -import static com.google.common.collect.Iterables.concat; import static com.google.common.collect.Iterables.filter; -import static com.google.common.collect.Iterables.size; -import static com.google.common.collect.Iterables.transform; import static org.jclouds.scriptbuilder.domain.Statements.pipeHttpResponseToBash; import java.net.URI; @@ -31,12 +27,8 @@ import java.util.Formatter; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Pattern; -import javax.annotation.Nullable; - import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.domain.ComputeMetadata; import org.jclouds.compute.domain.Hardware; @@ -45,16 +37,11 @@ import org.jclouds.compute.domain.OsFamily; import org.jclouds.compute.domain.Processor; import org.jclouds.compute.domain.Volume; import org.jclouds.http.HttpRequest; -import org.jclouds.logging.Logger; -import org.jclouds.predicates.RetryablePredicate; -import org.jclouds.predicates.SocketOpen; import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.Statements; -import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; -import com.google.common.net.HostAndPort; import com.google.common.reflect.TypeToken; /** @@ -170,72 +157,6 @@ public class ComputeServiceUtils { return org.jclouds.rest.Providers.getSupportedProvidersOfType(TypeToken.of(ComputeServiceContext.class)); } - public static HostAndPort findReachableSocketOnNode(final SocketOpen socketTester, final NodeMetadata node, - final int port, long timeoutValue, TimeUnit timeUnits, Logger logger) { - return findReachableSocketOnNode(socketTester, null, node, port, timeoutValue, timeUnits, logger); - } - - public static HostAndPort findReachableSocketOnNode(final SocketOpen socketTester, - @Nullable final Predicate> nodeRunning, final NodeMetadata node, - final int port, long timeoutValue, TimeUnit timeUnits, Logger logger) { - checkNodeHasIps(node); - - Iterable sockets = transform(concat(node.getPublicAddresses(), node.getPrivateAddresses()), - new Function() { - - @Override - public HostAndPort apply(String from) { - return HostAndPort.fromParts(from, port); - } - }); - - // Specify a retry period of 1s, expressed in the same time units. - long period = timeUnits.convert(1, TimeUnit.SECONDS); - - // For storing the result, as predicate will just tell us true/false - final AtomicReference result = new AtomicReference(); - - Predicate> multiIpSocketTester = new Predicate>() { - - @Override - public boolean apply(Iterable sockets) { - for (HostAndPort socket : sockets) { - if (socketTester.apply(socket)) { - result.set(socket); - return true; - } - } - if (nodeRunning != null && !nodeRunning.apply(new AtomicReference(node))) { - throw new IllegalStateException(String.format("Node %s is no longer running; aborting waiting for ip:port connection", node.getId())); - } - return false; - } - - }; - - RetryablePredicate> tester = new RetryablePredicate>( - multiIpSocketTester, timeoutValue, period, timeUnits); - - logger.debug(">> blocking on sockets %s for %d %s", sockets, timeoutValue, timeUnits); - - boolean passed = tester.apply(sockets); - - if (passed) { - logger.debug("<< socket %s opened", result); - assert result.get() != null; - return result.get(); - } else { - logger.warn("<< sockets %s didn't open after %d %s", sockets, timeoutValue, timeUnits); - throw new NoSuchElementException(String.format("could not connect to any ip address port %d on node %s", - port, node)); - } - } - - public static void checkNodeHasIps(NodeMetadata node) { - checkState(size(concat(node.getPublicAddresses(), node.getPrivateAddresses())) > 0, - "node does not have IP addresses configured: " + node); - } - public static String parseVersionOrReturnEmptyString(org.jclouds.compute.domain.OsFamily family, String in, Map> osVersionMap) { if (osVersionMap.containsKey(family)) { diff --git a/compute/src/main/java/org/jclouds/compute/util/ConcurrentOpenSocketFinder.java b/compute/src/main/java/org/jclouds/compute/util/ConcurrentOpenSocketFinder.java new file mode 100644 index 0000000000..efe3b84bee --- /dev/null +++ b/compute/src/main/java/org/jclouds/compute/util/ConcurrentOpenSocketFinder.java @@ -0,0 +1,170 @@ +package org.jclouds.compute.util; + +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.Iterables.concat; +import static com.google.common.collect.Iterables.size; +import static com.google.common.collect.Iterables.transform; + +import java.util.Collection; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import javax.annotation.Resource; +import javax.inject.Named; + +import org.jclouds.Constants; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.logging.Logger; +import org.jclouds.predicates.RetryablePredicate; +import org.jclouds.predicates.SocketOpen; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableSet; +import com.google.common.net.HostAndPort; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.inject.Inject; + +public class ConcurrentOpenSocketFinder implements OpenSocketFinder { + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + private Logger logger = Logger.NULL; + + private final SocketOpen socketTester; + private final Predicate> nodeRunning; + private final ListeningExecutorService executor; + + @Inject + public ConcurrentOpenSocketFinder(SocketOpen socketTester, + @Named("NODE_RUNNING") final Predicate> nodeRunning, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads) { + this.socketTester = socketTester; + this.nodeRunning = nodeRunning; + this.executor = MoreExecutors.listeningDecorator(userThreads); + } + + public HostAndPort findOpenSocketOnNode(final NodeMetadata node, final int port, + long timeoutValue, TimeUnit timeUnits) { + Iterable hosts = checkNodeHasIps(node); + Set sockets = toHostAndPorts(hosts, port); + + // Specify a retry period of 1s, expressed in the same time units. + long period = timeUnits.convert(1, TimeUnit.SECONDS); + + // For storing the result; needed because predicate will just tell us true/false + final AtomicReference result = new AtomicReference(); + + Predicate> concurrentOpenSocketFinder = new Predicate>() { + + @Override + public boolean apply(Collection input) { + HostAndPort reachableSocket = findOpenSocket(input); + if (reachableSocket != null) { + result.set(reachableSocket); + return true; + } else { + if (nodeRunning != null && !nodeRunning.apply(new AtomicReference(node))) { + throw new IllegalStateException(String.format("Node %s is no longer running; aborting waiting for ip:port connection", node.getId())); + } + return false; + } + } + + }; + + RetryablePredicate> retryingOpenSocketFinder = new RetryablePredicate>( + concurrentOpenSocketFinder, timeoutValue, period, timeUnits); + + logger.debug(">> blocking on sockets %s for %d %s", sockets, timeoutValue, timeUnits); + + boolean passed = retryingOpenSocketFinder.apply(sockets); + + if (passed) { + logger.debug("<< socket %s opened", result); + assert result.get() != null; + return result.get(); + } else { + logger.warn("<< sockets %s didn't open after %d %s", sockets, timeoutValue, timeUnits); + throw new NoSuchElementException(String.format("could not connect to any ip address port %d on node %s", + port, node)); + } + + } + + /** + * Checks if any any of the given HostAndPorts are reachable. It checks them all concurrently, + * and returns the first one found or null if none are reachable. + * + * @return A reachable HostAndPort, or null. + * @throws InterruptedException + */ + private HostAndPort findOpenSocket(final Collection sockets) { + final AtomicReference result = new AtomicReference(); + final CountDownLatch latch = new CountDownLatch(1); + final AtomicInteger completeCount = new AtomicInteger(); + + for (final HostAndPort socket : sockets) { + final ListenableFuture future = executor.submit(new Runnable() { + + @Override + public void run() { + try { + if (socketTester.apply(socket)) { + result.compareAndSet(null, socket); + latch.countDown(); + } + } catch (RuntimeException e) { + logger.warn(e, "Error checking reachability of ip:port %s", socket); + } + } + + }); + + future.addListener(new Runnable() { + + @Override + public void run() { + if (completeCount.incrementAndGet() >= sockets.size()) { + latch.countDown(); // Tried all; mark as done + } + } + + }, MoreExecutors.sameThreadExecutor()); + } + + try { + latch.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw Throwables.propagate(e); + } + return result.get(); + } + + private Iterable checkNodeHasIps(NodeMetadata node) { + Iterable ips = concat(node.getPublicAddresses(), node.getPrivateAddresses()); + checkState(size(ips) > 0, "node does not have IP addresses configured: " + node); + return ips; + } + + private Set toHostAndPorts(Iterable hosts, final int port) { + return ImmutableSet.copyOf(transform(hosts, + new Function() { + + @Override + public HostAndPort apply(String from) { + return HostAndPort.fromParts(from, port); + } + })); + } +} diff --git a/compute/src/main/java/org/jclouds/compute/util/OpenSocketFinder.java b/compute/src/main/java/org/jclouds/compute/util/OpenSocketFinder.java new file mode 100644 index 0000000000..ce6d85c7e6 --- /dev/null +++ b/compute/src/main/java/org/jclouds/compute/util/OpenSocketFinder.java @@ -0,0 +1,30 @@ +package org.jclouds.compute.util; + +import java.util.concurrent.TimeUnit; + +import org.jclouds.compute.domain.NodeMetadata; + +import com.google.common.net.HostAndPort; +import com.google.inject.ImplementedBy; + +/** + * For finding an open/reachable ip:port for a node. + * + * @author aled + */ +@ImplementedBy(ConcurrentOpenSocketFinder.class) +public interface OpenSocketFinder { + + /** + * + * @param node The node (checking its public and private addresses) + * @param port The port to try to connect to + * @param timeoutValue Max time to try to connect to the ip:port + * @param timeUnits + * + * @return The reachable ip:port + * @throws NoSuchElementException If no ports accessible within the given time + * @throws IllegalStateException If the given node has no public or private addresses + */ + HostAndPort findOpenSocketOnNode(final NodeMetadata node, final int port, long timeoutValue, TimeUnit timeUnits); +} diff --git a/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java b/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java index 11585e053a..9e3db80062 100644 --- a/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java +++ b/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java @@ -19,12 +19,15 @@ package org.jclouds.compute.strategy; import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; import static org.testng.Assert.assertEquals; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import org.jclouds.compute.config.CustomizationResponse; @@ -35,7 +38,7 @@ import org.jclouds.compute.functions.TemplateOptionsToStatement; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.predicates.AtomicNodeRunning; import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts; -import org.jclouds.predicates.SocketOpen; +import org.jclouds.compute.util.OpenSocketFinder; import org.jclouds.scriptbuilder.domain.Statement; import org.testng.Assert; import org.testng.annotations.Test; @@ -55,7 +58,7 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest { public void testBreakWhenNodeStillPending() { InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory = createMock(InitializeRunScriptOnNodeOrPlaceInBadMap.Factory.class); - SocketOpen socketTester = createMock(SocketOpen.class); + OpenSocketFinder openSocketFinder = createMock(OpenSocketFinder.class); Timeouts timeouts = new Timeouts(); Function templateOptionsToStatement = new TemplateOptionsToStatement(); @SuppressWarnings("unused") @@ -79,10 +82,10 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest { }; // replay mocks - replay(initScriptRunnerFactory, socketTester); + replay(initScriptRunnerFactory, openSocketFinder); // run AtomicReference atomicNode = new AtomicReference(node); - new CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap( new AtomicNodeRunning(nodeRunning), socketTester, timeouts, + new CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap( new AtomicNodeRunning(nodeRunning), openSocketFinder, timeouts, templateOptionsToStatement, initScriptRunnerFactory, options, atomicNode, goodNodes, badNodes, customizationResponses).apply(atomicNode); @@ -93,12 +96,12 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest { assertEquals(customizationResponses.size(), 0); // verify mocks - verify(initScriptRunnerFactory, socketTester); + verify(initScriptRunnerFactory, openSocketFinder); } public void testBreakGraceFullyWhenNodeDied() { InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory = createMock(InitializeRunScriptOnNodeOrPlaceInBadMap.Factory.class); - SocketOpen socketTester = createMock(SocketOpen.class); + OpenSocketFinder openSocketFinder = createMock(OpenSocketFinder.class); Timeouts timeouts = new Timeouts(); Function templateOptionsToStatement = new TemplateOptionsToStatement(); @SuppressWarnings("unused") @@ -123,10 +126,10 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest { }; // replay mocks - replay(initScriptRunnerFactory, socketTester); + replay(initScriptRunnerFactory, openSocketFinder); // run AtomicReference atomicNode = new AtomicReference(node); - new CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap( new AtomicNodeRunning(nodeRunning), socketTester, timeouts, + new CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap( new AtomicNodeRunning(nodeRunning), openSocketFinder, timeouts, templateOptionsToStatement, initScriptRunnerFactory, options, atomicNode, goodNodes, badNodes, customizationResponses).apply(atomicNode); @@ -137,6 +140,52 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest { assertEquals(customizationResponses.size(), 0); // verify mocks - verify(initScriptRunnerFactory, socketTester); + verify(initScriptRunnerFactory, openSocketFinder); + } + + public void testBreakGraceWhenNodeSocketFailsToOpen() { + int portTimeoutSecs = 2; + InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory = createMock(InitializeRunScriptOnNodeOrPlaceInBadMap.Factory.class); + OpenSocketFinder openSocketFinder = createMock(OpenSocketFinder.class); + Timeouts timeouts = new Timeouts(); + Function templateOptionsToStatement = new TemplateOptionsToStatement(); + TemplateOptions options = new TemplateOptions().blockOnPort(22, portTimeoutSecs); + Set goodNodes = Sets.newLinkedHashSet(); + Map badNodes = Maps.newLinkedHashMap(); + Multimap customizationResponses = LinkedHashMultimap.create(); + + final NodeMetadata pendingNode = new NodeMetadataBuilder().ids("id").state(NodeState.PENDING).build(); + final NodeMetadata runningNode = new NodeMetadataBuilder().ids("id").state(NodeState.RUNNING).build(); + + expect(openSocketFinder.findOpenSocketOnNode(runningNode, 22, portTimeoutSecs, TimeUnit.SECONDS)) + .andThrow(new NoSuchElementException("could not connect to any ip address port")).once(); + + GetNodeMetadataStrategy nodeRunning = new GetNodeMetadataStrategy(){ + + @Override + public NodeMetadata getNode(String input) { + Assert.assertEquals(input, pendingNode.getId()); + return runningNode; + } + + }; + + // replay mocks + replay(initScriptRunnerFactory, openSocketFinder); + + // run + AtomicReference atomicNode = new AtomicReference(pendingNode); + new CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap( new AtomicNodeRunning(nodeRunning), openSocketFinder, timeouts, + templateOptionsToStatement, initScriptRunnerFactory, options, atomicNode, goodNodes, badNodes, + customizationResponses).apply(atomicNode); + + assertEquals(goodNodes.size(), 0); + assertEquals(badNodes.keySet(), ImmutableSet.of(pendingNode)); + badNodes.get(pendingNode).printStackTrace(); + assertEquals(badNodes.get(pendingNode).getMessage(), "could not connect to any ip address port"); + assertEquals(customizationResponses.size(), 0); + + // verify mocks + verify(initScriptRunnerFactory, openSocketFinder); } } diff --git a/compute/src/test/java/org/jclouds/compute/util/ComputeServiceUtilsTest.java b/compute/src/test/java/org/jclouds/compute/util/ComputeServiceUtilsTest.java index b606e1f4f0..6a5d101b07 100644 --- a/compute/src/test/java/org/jclouds/compute/util/ComputeServiceUtilsTest.java +++ b/compute/src/test/java/org/jclouds/compute/util/ComputeServiceUtilsTest.java @@ -18,39 +18,21 @@ */ package org.jclouds.compute.util; -import static org.easymock.EasyMock.createMock; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; -import static org.easymock.EasyMock.verify; import static org.jclouds.compute.util.ComputeServiceUtils.parseVersionOrReturnEmptyString; import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; import java.net.URI; import java.util.Map; -import java.util.NoSuchElementException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import org.easymock.EasyMock; import org.jclouds.compute.config.BaseComputeServiceContextModule; -import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.OsFamily; -import org.jclouds.compute.predicates.SocketOpenPredicates; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.http.HttpRequest; import org.jclouds.json.Json; import org.jclouds.json.config.GsonModule; -import org.jclouds.logging.Logger; -import org.jclouds.predicates.SocketOpen; import org.testng.annotations.Test; -import com.google.common.base.Predicate; -import com.google.common.base.Stopwatch; import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.ImmutableSet; -import com.google.common.net.HostAndPort; import com.google.inject.Guice; /** @@ -98,88 +80,4 @@ public class ComputeServiceUtilsTest { org.jclouds.scriptbuilder.domain.OsFamily.UNIX), "curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET -H \"Host: adriancolehappy.s3.amazonaws.com\" -H \"Date: Sun, 12 Sep 2010 08:25:19 GMT\" -H \"Authorization: AWS 0ASHDJAS82:JASHFDA=\" https://adriancolehappy.s3.amazonaws.com/java/install |(mkdir -p /stage/ &&cd /stage/ &&tar -xpzf -)\n"); } - - @Test - public void testFindReachableSocketOnNodeTimesOut() throws Exception { - final long timeoutSecs = 2; - final long timeoutMs = timeoutSecs * 1000; - final long SLOW_GRACE = 500; - final long EARLY_GRACE = 10; - - SocketOpen socketTester = SocketOpenPredicates.alwaysFail; - NodeMetadata node = createMock(NodeMetadata.class); - expect(node.getPublicAddresses()).andReturn(ImmutableSet.of("1.2.3.4")).atLeastOnce(); - expect(node.getPrivateAddresses()).andReturn(ImmutableSet.of("1.2.3.5")).atLeastOnce(); - - replay(node); - - Stopwatch stopwatch = new Stopwatch(); - stopwatch.start(); - try { - ComputeServiceUtils.findReachableSocketOnNode(socketTester, node, 1234, timeoutMs, TimeUnit.MILLISECONDS, Logger.CONSOLE); - fail(); - } catch (NoSuchElementException success) { - // expected - } - long timetaken = stopwatch.elapsedMillis(); - - assertTrue(timetaken >= timeoutMs-EARLY_GRACE && timetaken <= timeoutMs+SLOW_GRACE, "timetaken="+timetaken); - verify(node); - } - - @Test - public void testFindReachableSocketOnNodeReturnsAvailable() throws Exception { - SocketOpen socketTester = createMock(SocketOpen.class); - expect(socketTester.apply(HostAndPort.fromParts("1.2.3.4", 22))).andReturn(false); - expect(socketTester.apply(HostAndPort.fromParts("1.2.3.5", 22))).andReturn(true); - - NodeMetadata node = createMock(NodeMetadata.class); - expect(node.getPublicAddresses()).andReturn(ImmutableSet.of("1.2.3.4")).atLeastOnce(); - expect(node.getPrivateAddresses()).andReturn(ImmutableSet.of("1.2.3.5")).atLeastOnce(); - - replay(socketTester); - replay(node); - - HostAndPort result = ComputeServiceUtils.findReachableSocketOnNode(socketTester, node, 22, 2000, TimeUnit.MILLISECONDS, Logger.CONSOLE); - assertEquals(result, HostAndPort.fromParts("1.2.3.5", 22)); - - verify(socketTester); - verify(node); - } - - @Test - public void testFindReachableSocketOnNodeAbortsWhenNodeNotRunning() throws Exception { - final long SLOW_GRACE = 500; - - SocketOpen socketTester = SocketOpenPredicates.alwaysFail; - - NodeMetadata node = createMock(NodeMetadata.class); - expect(node.getPublicAddresses()).andReturn(ImmutableSet.of("1.2.3.4")).atLeastOnce(); - expect(node.getPrivateAddresses()).andReturn(ImmutableSet.of("1.2.3.5")).atLeastOnce(); - expect(node.getId()).andReturn("myid").atLeastOnce(); - - Predicate> nodeRunning = createMock(Predicate.class); - expect(nodeRunning.apply(EasyMock.>anyObject())).andReturn(false); - - replay(node); - replay(nodeRunning); - - Stopwatch stopwatch = new Stopwatch(); - stopwatch.start(); - try { - ComputeServiceUtils.findReachableSocketOnNode(socketTester, nodeRunning, - node, 22, 2000000, TimeUnit.MILLISECONDS, Logger.CONSOLE); - fail(); - } catch (RuntimeException e) { - if (!e.getMessage().contains("no longer running")) { - throw e; - } - } - long timetaken = stopwatch.elapsedMillis(); - - assertTrue(timetaken <= SLOW_GRACE, "timetaken="+timetaken); - - verify(node); - verify(nodeRunning); - } } diff --git a/compute/src/test/java/org/jclouds/compute/util/ConcurrentOpenSocketFinderTest.java b/compute/src/test/java/org/jclouds/compute/util/ConcurrentOpenSocketFinderTest.java new file mode 100644 index 0000000000..5421f4cf67 --- /dev/null +++ b/compute/src/test/java/org/jclouds/compute/util/ConcurrentOpenSocketFinderTest.java @@ -0,0 +1,193 @@ +package org.jclouds.compute.util; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import org.easymock.EasyMock; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.concurrent.MoreExecutors; +import org.jclouds.predicates.SocketOpen; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.base.Predicate; +import com.google.common.base.Stopwatch; +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.net.HostAndPort; + +@Test(singleThreaded=true) +public class ConcurrentOpenSocketFinderTest { + + private static final long SLOW_GRACE = 500; + private static final long EARLY_GRACE = 10; + + private NodeMetadata node; + private SocketOpen socketTester; + private Predicate> nodeRunning; + private ExecutorService threadPool; + + @SuppressWarnings("unchecked") + @BeforeMethod + public void setUp() { + node = createMock(NodeMetadata.class); + expect(node.getPublicAddresses()).andReturn(ImmutableSet.of("1.2.3.4")).atLeastOnce(); + expect(node.getPrivateAddresses()).andReturn(ImmutableSet.of("1.2.3.5")).atLeastOnce(); + expect(node.getId()).andReturn("myid").anyTimes(); + + socketTester = createMock(SocketOpen.class); + + nodeRunning = createMock(Predicate.class); + + replay(node); + + threadPool = Executors.newCachedThreadPool(); + } + + @AfterMethod(alwaysRun=true) + public void tearDown() { + if (threadPool != null) threadPool.shutdownNow(); + } + + @Test + public void testRespectsTimeout() throws Exception { + final long timeoutMs = 1000; + + expect(socketTester.apply(HostAndPort.fromParts("1.2.3.4", 22))).andReturn(false).times(2, Integer.MAX_VALUE); + expect(socketTester.apply(HostAndPort.fromParts("1.2.3.5", 22))).andReturn(false).times(2, Integer.MAX_VALUE); + expect(nodeRunning.apply(EasyMock.>anyObject())).andReturn(true); + replay(socketTester); + replay(nodeRunning); + + OpenSocketFinder finder = new ConcurrentOpenSocketFinder(socketTester, null, MoreExecutors.sameThreadExecutor()); + + Stopwatch stopwatch = new Stopwatch(); + stopwatch.start(); + try { + finder.findOpenSocketOnNode(node, 22, timeoutMs, TimeUnit.MILLISECONDS); + fail(); + } catch (NoSuchElementException success) { + // expected + } + long timetaken = stopwatch.elapsedMillis(); + + assertTrue(timetaken >= timeoutMs-EARLY_GRACE && timetaken <= timeoutMs+SLOW_GRACE, "timetaken="+timetaken); + + verify(node); + verify(socketTester); + } + + @Test + public void testReturnsReachable() throws Exception { + expect(socketTester.apply(HostAndPort.fromParts("1.2.3.4", 22))).andReturn(false).once(); + expect(socketTester.apply(HostAndPort.fromParts("1.2.3.5", 22))).andReturn(true).once(); + expect(nodeRunning.apply(EasyMock.>anyObject())).andReturn(true); + replay(socketTester); + replay(nodeRunning); + + OpenSocketFinder finder = new ConcurrentOpenSocketFinder(socketTester, null, MoreExecutors.sameThreadExecutor()); + + HostAndPort result = finder.findOpenSocketOnNode(node, 22, 2000, TimeUnit.MILLISECONDS); + assertEquals(result, HostAndPort.fromParts("1.2.3.5", 22)); + + verify(node); + verify(socketTester); + } + + @Test + public void testChecksSocketsConcurrently() throws Exception { + long delayForReachableMs = 25; + + expect(nodeRunning.apply(EasyMock.>anyObject())).andReturn(true); + replay(nodeRunning); + + // Can't use mock+answer for concurrency tests; EasyMock uses lock in ReplayState + ControllableSocketOpen socketTester = new ControllableSocketOpen(ImmutableMap.of( + HostAndPort.fromParts("1.2.3.4", 22), new SlowCallable(false, 1000), + HostAndPort.fromParts("1.2.3.5", 22), new SlowCallable(true, delayForReachableMs))); + + OpenSocketFinder finder = new ConcurrentOpenSocketFinder(socketTester, null, threadPool); + + Stopwatch stopwatch = new Stopwatch(); + stopwatch.start(); + HostAndPort result = finder.findOpenSocketOnNode(node, 22, 2000, TimeUnit.MILLISECONDS); + long timetaken = stopwatch.elapsedMillis(); + + assertEquals(result, HostAndPort.fromParts("1.2.3.5", 22)); + assertTrue(timetaken >= delayForReachableMs-EARLY_GRACE && timetaken <= delayForReachableMs+SLOW_GRACE, "timetaken="+timetaken); + verify(node); + } + + @Test + public void testAbortsWhenNodeNotRunning() throws Exception { + expect(socketTester.apply(EasyMock.anyObject())).andReturn(false); + expect(nodeRunning.apply(EasyMock.>anyObject())).andReturn(false); + replay(socketTester); + replay(nodeRunning); + + OpenSocketFinder finder = new ConcurrentOpenSocketFinder(socketTester, nodeRunning, MoreExecutors.sameThreadExecutor()); + + Stopwatch stopwatch = new Stopwatch(); + stopwatch.start(); + try { + finder.findOpenSocketOnNode(node, 22, 2000, TimeUnit.MILLISECONDS); + fail(); + } catch (NoSuchElementException e) { + // success + // Note: don't get the "no longer running" message, because logged+swallowed by RetryablePredicate + } + long timetaken = stopwatch.elapsedMillis(); + + assertTrue(timetaken <= SLOW_GRACE, "timetaken="+timetaken); + + verify(node); + verify(socketTester); + verify(nodeRunning); + } + + private static class SlowCallable implements Callable { + private final T result; + private final long delay; + + SlowCallable(T result, long delay) { + this.result = result; + this.delay = delay; + } + @Override + public T call() throws Exception { + Thread.sleep(delay); + return result; + } + }; + + private static class ControllableSocketOpen implements SocketOpen { + private final Map> answers; + + ControllableSocketOpen(Map> answers) { + this.answers = answers; + } + @Override + public boolean apply(HostAndPort input) { + try { + return answers.get(input).call(); + } catch (Exception e) { + throw Throwables.propagate(e); + } + } + }; +} From 4e02bd5caad976a9cdb22fa86c3ae66b74b1fbaf Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Tue, 15 May 2012 13:54:56 +0100 Subject: [PATCH 118/148] Added BaseComputeServiceLiveTest.testConcurrentUseOfComputeServiceToCreateNodes --- .../StubComputeServiceIntegrationTest.java | 29 ++++++++++++++ .../internal/BaseComputeServiceLiveTest.java | 40 ++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/compute/src/test/java/org/jclouds/compute/StubComputeServiceIntegrationTest.java b/compute/src/test/java/org/jclouds/compute/StubComputeServiceIntegrationTest.java index 0c94f6374e..6fab8fc46e 100644 --- a/compute/src/test/java/org/jclouds/compute/StubComputeServiceIntegrationTest.java +++ b/compute/src/test/java/org/jclouds/compute/StubComputeServiceIntegrationTest.java @@ -138,6 +138,8 @@ public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTes SshClient client3 = createMock(SshClient.class); SshClient client4 = createMock(SshClient.class); SshClient client5 = createMock(SshClient.class); + SshClient client6 = createMock(SshClient.class); + SshClient client7 = createMock(SshClient.class); expect( factory.create(HostAndPort.fromParts("144.175.1.1", 22), @@ -211,10 +213,20 @@ public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTes factory.create(HostAndPort.fromParts("144.175.1.5", 22), LoginCredentials.builder().user("root").password("password5").build())).andReturn(client5) .times(2); + expect( + factory.create(HostAndPort.fromParts("144.175.1.6", 22), + LoginCredentials.builder().user("root").password("password6").build())).andReturn(client6) + .times(2); + expect( + factory.create(HostAndPort.fromParts("144.175.1.7", 22), + LoginCredentials.builder().user("root").password("password7").build())).andReturn(client7) + .times(2); runScriptAndInstallSsh(client3, "bootstrap", 3); runScriptAndInstallSsh(client4, "bootstrap", 4); runScriptAndInstallSsh(client5, "bootstrap", 5); + runScriptAndInstallSsh(client6, "bootstrap", 6); + runScriptAndInstallSsh(client7, "bootstrap", 7); expect( factory.create(eq(HostAndPort.fromParts("144.175.1.1", 22)), @@ -236,11 +248,21 @@ public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTes factory.create(eq(HostAndPort.fromParts("144.175.1.5", 22)), eq(LoginCredentials.builder().user("defaultAdminUsername").privateKey(Pems.PRIVATE_PKCS1_MARKER).build()))) .andReturn(client5); + expect( + factory.create(eq(HostAndPort.fromParts("144.175.1.6", 22)), + eq(LoginCredentials.builder().user("defaultAdminUsername").privateKey(Pems.PRIVATE_PKCS1_MARKER).build()))) + .andReturn(client6); + expect( + factory.create(eq(HostAndPort.fromParts("144.175.1.7", 22)), + eq(LoginCredentials.builder().user("defaultAdminUsername").privateKey(Pems.PRIVATE_PKCS1_MARKER).build()))) + .andReturn(client7); helloAndJava(client2); helloAndJava(client3); helloAndJava(client4); helloAndJava(client5); + helloAndJava(client6); + helloAndJava(client7); replay(factory); replay(client1); @@ -251,6 +273,8 @@ public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTes replay(client3); replay(client4); replay(client5); + replay(client6); + replay(client7); bind(SshClient.Factory.class).toInstance(factory); } @@ -472,6 +496,11 @@ public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTes } @Test(enabled = true, dependsOnMethods = "testTemplateMatch") + public void testConcurrentUseOfComputeServiceToCreateNodes() throws Exception { + super.testConcurrentUseOfComputeServiceToCreateNodes(); + } + + @Test(enabled = true, dependsOnMethods = "testConcurrentUseOfComputeServiceToCreateNodes") public void testCreateTwoNodesWithRunScript() throws Exception { super.testCreateTwoNodesWithRunScript(); } diff --git a/compute/src/test/java/org/jclouds/compute/internal/BaseComputeServiceLiveTest.java b/compute/src/test/java/org/jclouds/compute/internal/BaseComputeServiceLiveTest.java index f5202b32f4..2ea50bd480 100644 --- a/compute/src/test/java/org/jclouds/compute/internal/BaseComputeServiceLiveTest.java +++ b/compute/src/test/java/org/jclouds/compute/internal/BaseComputeServiceLiveTest.java @@ -48,7 +48,9 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; @@ -105,7 +107,10 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.net.HostAndPort; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import com.google.inject.Module; /** @@ -323,7 +328,7 @@ public abstract class BaseComputeServiceLiveTest extends BaseComputeServiceConte ComputeTestUtils.checkHttpGet(view.utils().http(), node, 8080); } - @Test(enabled = true, dependsOnMethods = "testCompareSizes") + @Test(enabled = true, dependsOnMethods = "testConcurrentUseOfComputeServiceToCreateNodes") public void testCreateTwoNodesWithRunScript() throws Exception { try { client.destroyNodesMatching(inGroup(group)); @@ -412,6 +417,39 @@ public abstract class BaseComputeServiceLiveTest extends BaseComputeServiceConte this.nodes.add(node); } + @Test(enabled = true, dependsOnMethods = "testCompareSizes") + public void testConcurrentUseOfComputeServiceToCreateNodes() throws Exception { + final long timeoutMs = 20*60*1000; + List groups = new ArrayList(); + List> futures = new ArrayList>(); + ListeningExecutorService executor = MoreExecutors.listeningDecorator(context.utils().userExecutor()); + + try { + for (int i = 0; i < 2; i++) { + final int groupNum = i; + final String group = "groupconcurrent"+groupNum; + groups.add(group); + + ListenableFuture future = executor.submit(new Callable() { + public NodeMetadata call() throws Exception { + NodeMetadata node = getOnlyElement(client.createNodesInGroup(group, 1, + inboundPorts(22, 8080).blockOnPort(22, 300+groupNum))); + getAnonymousLogger().info("Started node "+node.getId()); + return node; + }}); + futures.add(future); + } + + ListenableFuture> compoundFuture = Futures.allAsList(futures); + compoundFuture.get(timeoutMs, TimeUnit.MILLISECONDS); + + } finally { + for (String group : groups) { + client.destroyNodesMatching(inGroup(group)); + } + } + } + @Test(enabled = true, dependsOnMethods = "testCreateAnotherNodeWithANewContextToEnsureSharedMemIsntRequired") public void testCredentialsCache() throws Exception { initializeContext(); From fffa3eb8aff59d82f5d1342bd758da50ed4d7cb1 Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Thu, 17 May 2012 23:37:06 +0100 Subject: [PATCH 119/148] Issue 647: fix handling of malformed 'Expires' header --- .../org/jclouds/io/ContentMetadataCodec.java | 12 +++-- .../internal/SimpleDateCodecFactoryTest.java | 44 +++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 core/src/test/java/org/jclouds/date/internal/SimpleDateCodecFactoryTest.java diff --git a/core/src/main/java/org/jclouds/io/ContentMetadataCodec.java b/core/src/main/java/org/jclouds/io/ContentMetadataCodec.java index e10a572870..4d2c801dbe 100644 --- a/core/src/main/java/org/jclouds/io/ContentMetadataCodec.java +++ b/core/src/main/java/org/jclouds/io/ContentMetadataCodec.java @@ -17,8 +17,10 @@ import org.jclouds.date.DateCodec; import org.jclouds.date.DateCodecFactory; import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec; import org.jclouds.logging.Logger; +import org.jclouds.util.Throwables2; import com.google.common.base.Predicate; +import com.google.common.base.Throwables; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap.Builder; import com.google.common.collect.Multimap; @@ -115,9 +117,13 @@ public interface ContentMetadataCodec { public Date parseExpires(String expires) { try { return (expires != null) ? getExpiresDateCodec().toDate(expires) : null; - } catch (ParseException e) { - logger.warn(e, "Invalid Expires header (%s); should be in RFC-1123 format; treating as already expired", expires); - return new Date(0); + } catch (Exception e) { + if (Throwables2.getFirstThrowableOfType(e, ParseException.class) != null) { + logger.debug("Invalid Expires header (%s); should be in RFC-1123 format; treating as already expired: %s", expires, e.getMessage()); + return new Date(0); + } else { + throw Throwables.propagate(e); + } } } } diff --git a/core/src/test/java/org/jclouds/date/internal/SimpleDateCodecFactoryTest.java b/core/src/test/java/org/jclouds/date/internal/SimpleDateCodecFactoryTest.java new file mode 100644 index 0000000000..7e03049840 --- /dev/null +++ b/core/src/test/java/org/jclouds/date/internal/SimpleDateCodecFactoryTest.java @@ -0,0 +1,44 @@ +package org.jclouds.date.internal; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +import java.text.ParseException; +import java.util.Date; + +import org.jclouds.date.DateCodec; +import org.jclouds.util.Throwables2; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +public class SimpleDateCodecFactoryTest { + + private SimpleDateCodecFactory simpleDateCodecFactory; + private DateCodec rfc1123Codec; + + @BeforeMethod + public void setUp() throws Exception { + simpleDateCodecFactory = new SimpleDateCodecFactory(new SimpleDateFormatDateService()); + rfc1123Codec = simpleDateCodecFactory.rfc1123(); + } + + @Test + public void testCodecForRfc1123() throws Exception { + Date date = new Date(1000); + assertEquals(rfc1123Codec.toDate(rfc1123Codec.toString(date)), date); + + assertEquals(rfc1123Codec.toDate("Thu, 01 Dec 1994 16:00:00 GMT"), new Date(786297600000L)); + } + + @Test + public void testCodecForRfc1123ThrowsParseExceptionWhenMalformed() throws Exception { + try { + rfc1123Codec.toDate("wrong"); + fail(); + } catch (Exception e) { + if (Throwables2.getFirstThrowableOfType(e, ParseException.class) == null) { + throw e; + } + } + } +} From 4a1e7e0005ad73a2ca26f5e547aba72e6ef85828 Mon Sep 17 00:00:00 2001 From: Andrew Bayer Date: Fri, 18 May 2012 11:39:09 -0700 Subject: [PATCH 120/148] Refactoring hashcodes/equals. --- .../jclouds/cloudstack/domain/Account.java | 23 +- .../org/jclouds/cloudstack/domain/Alert.java | 18 +- .../jclouds/cloudstack/domain/ApiKeyPair.java | 11 +- .../domain/AsyncCreateResponse.java | 17 +- .../jclouds/cloudstack/domain/AsyncJob.java | 81 ++---- .../cloudstack/domain/AsyncJobError.java | 20 +- .../cloudstack/domain/Capabilities.java | 32 +-- .../jclouds/cloudstack/domain/Capacity.java | 33 +-- .../jclouds/cloudstack/domain/Cluster.java | 36 +-- .../cloudstack/domain/ConfigurationEntry.java | 20 +- .../cloudstack/domain/DiskOffering.java | 60 +--- .../org/jclouds/cloudstack/domain/Domain.java | 29 +- .../EncryptedPasswordAndPrivateKey.java | 11 +- .../org/jclouds/cloudstack/domain/Event.java | 39 +-- .../cloudstack/domain/FirewallRule.java | 40 +-- .../org/jclouds/cloudstack/domain/Host.java | 138 ++++------ .../cloudstack/domain/IPForwardingRule.java | 93 ++----- .../org/jclouds/cloudstack/domain/ISO.java | 104 +++---- .../cloudstack/domain/ISOExtraction.java | 42 +-- .../cloudstack/domain/ISOPermissions.java | 39 +-- .../cloudstack/domain/IngressRule.java | 72 ++--- .../jclouds/cloudstack/domain/JobResult.java | 9 +- .../cloudstack/domain/LoadBalancerRule.java | 99 ++----- .../cloudstack/domain/LoginResponse.java | 108 ++------ .../org/jclouds/cloudstack/domain/NIC.java | 86 ++---- .../jclouds/cloudstack/domain/Network.java | 201 +++----------- .../cloudstack/domain/NetworkOffering.java | 82 ++---- .../cloudstack/domain/NetworkService.java | 77 ++---- .../org/jclouds/cloudstack/domain/OSType.java | 39 +-- .../org/jclouds/cloudstack/domain/Pod.java | 32 +-- .../cloudstack/domain/PortForwardingRule.java | 79 ++---- .../cloudstack/domain/PublicIPAddress.java | 142 +++------- .../cloudstack/domain/ResourceLimit.java | 17 +- .../cloudstack/domain/SecurityGroup.java | 44 +-- .../cloudstack/domain/ServiceOffering.java | 87 ++---- .../jclouds/cloudstack/domain/Snapshot.java | 18 +- .../cloudstack/domain/SnapshotPolicy.java | 21 +- .../jclouds/cloudstack/domain/SshKeyPair.java | 45 +-- .../cloudstack/domain/StoragePool.java | 59 ++-- .../jclouds/cloudstack/domain/Template.java | 181 ++++-------- .../cloudstack/domain/TemplateExtraction.java | 44 ++- .../cloudstack/domain/TemplatePermission.java | 15 +- .../cloudstack/domain/UsageRecord.java | 70 ++--- .../org/jclouds/cloudstack/domain/User.java | 45 ++- .../jclouds/cloudstack/domain/VMGroup.java | 23 +- .../cloudstack/domain/VirtualMachine.java | 259 ++++-------------- .../cloudstack/domain/VlanIPRange.java | 50 ++-- .../org/jclouds/cloudstack/domain/Volume.java | 112 +++----- .../org/jclouds/cloudstack/domain/Zone.java | 125 +++------ 49 files changed, 927 insertions(+), 2200 deletions(-) diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Account.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Account.java index d962601dd2..e73d97e3d3 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Account.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Account.java @@ -27,6 +27,7 @@ import javax.annotation.Nullable; import com.google.common.base.CaseFormat; import com.google.common.base.Function; +import com.google.common.base.Objects; import com.google.common.collect.ForwardingSet; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; @@ -637,12 +638,7 @@ public class Account extends ForwardingSet implements Comparable @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (int) (domainId ^ (domainId >>> 32)); - result = prime * result + (int) (id ^ (id >>> 32)); - result = prime * result + ((name == null) ? 0 : name.hashCode()); - return result; + return Objects.hashCode(domainId, id, name); } @Override @@ -653,16 +649,11 @@ public class Account extends ForwardingSet implements Comparable return false; if (getClass() != obj.getClass()) return false; - Account other = (Account) obj; - if (domainId != other.domainId) - return false; - if (id != other.id) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; + Account that = (Account) obj; + if (!Objects.equal(domainId, that.domainId)) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(name, that.name)) return false; + return true; } diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Alert.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Alert.java index e39ba9dce6..647dae11b8 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Alert.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Alert.java @@ -18,6 +18,8 @@ */ package org.jclouds.cloudstack.domain; +import com.google.common.base.Objects; + import java.util.Date; /** @@ -99,23 +101,19 @@ public class Alert implements Comparable { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - Alert alert = (Alert) o; + Alert that = (Alert) o; - if (id != alert.id) return false; - if (description != null ? !description.equals(alert.description) : alert.description != null) return false; - if (sent != null ? !sent.equals(alert.sent) : alert.sent != null) return false; - if (type != null ? !type.equals(alert.type) : alert.type != null) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(description, that.description)) return false; + if (!Objects.equal(sent, that.sent)) return false; + if (!Objects.equal(type, that.type)) return false; return true; } @Override public int hashCode() { - int result = (int) (id ^ (id >>> 32)); - result = 31 * result + (description != null ? description.hashCode() : 0); - result = 31 * result + (sent != null ? sent.hashCode() : 0); - result = 31 * result + (type != null ? type.hashCode() : 0); - return result; + return Objects.hashCode(id, description, sent, type); } diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ApiKeyPair.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ApiKeyPair.java index 4f8fec19c2..4851c133a3 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ApiKeyPair.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ApiKeyPair.java @@ -19,6 +19,7 @@ package org.jclouds.cloudstack.domain; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -81,19 +82,15 @@ public class ApiKeyPair implements Comparable { ApiKeyPair that = (ApiKeyPair) o; - if (apiKey != null ? !apiKey.equals(that.apiKey) : that.apiKey != null) - return false; - if (secretKey != null ? !secretKey.equals(that.secretKey) : that.secretKey != null) - return false; + if (!Objects.equal(apiKey, that.apiKey)) return false; + if (!Objects.equal(secretKey, that.secretKey)) return false; return true; } @Override public int hashCode() { - int result = apiKey != null ? apiKey.hashCode() : 0; - result = 31 * result + (secretKey != null ? secretKey.hashCode() : 0); - return result; + return Objects.hashCode(apiKey, secretKey); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncCreateResponse.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncCreateResponse.java index aa56420c17..48e63c5d94 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncCreateResponse.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncCreateResponse.java @@ -18,6 +18,7 @@ */ package org.jclouds.cloudstack.domain; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -60,11 +61,7 @@ public class AsyncCreateResponse { @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (int) (id ^ (id >>> 32)); - result = prime * result + (int) (jobId ^ (jobId >>> 32)); - return result; + return Objects.hashCode(id, jobId); } @Override @@ -75,11 +72,11 @@ public class AsyncCreateResponse { return false; if (getClass() != obj.getClass()) return false; - AsyncCreateResponse other = (AsyncCreateResponse) obj; - if (id != other.id) - return false; - if (jobId != other.jobId) - return false; + AsyncCreateResponse that = (AsyncCreateResponse) obj; + + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(jobId, that.jobId)) return false; + return true; } diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncJob.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncJob.java index cb4cfd25e8..a46cbe35d5 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncJob.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncJob.java @@ -20,6 +20,7 @@ package org.jclouds.cloudstack.domain; import java.util.Date; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -346,22 +347,8 @@ public class AsyncJob { @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (int) (accountId ^ (accountId >>> 32)); - result = prime * result + ((cmd == null) ? 0 : cmd.hashCode()); - result = prime * result + ((created == null) ? 0 : created.hashCode()); - result = prime * result + (int) (id ^ (id >>> 32)); - result = prime * result + (int) (instanceId ^ (instanceId >>> 32)); - result = prime * result + ((instanceType == null) ? 0 : instanceType.hashCode()); - result = prime * result + ((error == null) ? 0 : error.hashCode()); - result = prime * result + progress; - result = prime * result + ((this.result == null) ? 0 : this.result.hashCode()); - result = prime * result + resultCode.code(); - result = prime * result + ((resultType == null) ? 0 : resultType.hashCode()); - result = prime * result + status.code(); - result = prime * result + userId; - return result; + return Objects.hashCode(accountId, cmd, created, id, instanceId, instanceType, error, progress, + result, resultCode, resultType, status, userId); } @Override @@ -372,51 +359,23 @@ public class AsyncJob { return false; if (getClass() != obj.getClass()) return false; - AsyncJob other = (AsyncJob) obj; - if (accountId != other.accountId) - return false; - if (cmd == null) { - if (other.cmd != null) - return false; - } else if (!cmd.equals(other.cmd)) - return false; - if (created == null) { - if (other.created != null) - return false; - } else if (!created.equals(other.created)) - return false; - if (id != other.id) - return false; - if (instanceId != other.instanceId) - return false; - if (instanceType == null) { - if (other.instanceType != null) - return false; - } else if (!instanceType.equals(other.instanceType)) - return false; - if (error == null) { - if (other.error != null) - return false; - } else if (!error.equals(other.error)) - return false; - if (progress != other.progress) - return false; - if (result == null) { - if (other.result != null) - return false; - } else if (!result.equals(other.result)) - return false; - if (resultCode != other.resultCode) - return false; - if (resultType == null) { - if (other.resultType != null) - return false; - } else if (!resultType.equals(other.resultType)) - return false; - if (status != other.status) - return false; - if (userId != other.userId) - return false; + + AsyncJob that = (AsyncJob) obj; + + if (!Objects.equal(accountId, that.accountId)) return false; + if (!Objects.equal(cmd, that.cmd)) return false; + if (!Objects.equal(created, that.created)) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(instanceId, that.instanceId)) return false; + if (!Objects.equal(instanceType, that.instanceType)) return false; + if (!Objects.equal(error, that.error)) return false; + if (!Objects.equal(progress, that.progress)) return false; + if (!Objects.equal(result, that.result)) return false; + if (!Objects.equal(resultCode, that.resultCode)) return false; + if (!Objects.equal(resultType, that.resultType)) return false; + if (!Objects.equal(status, that.status)) return false; + if (!Objects.equal(userId, that.userId)) return false; + return true; } diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncJobError.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncJobError.java index a446dbf42f..f57fbd5e22 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncJobError.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncJobError.java @@ -18,6 +18,7 @@ */ package org.jclouds.cloudstack.domain; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -92,11 +93,7 @@ public class AsyncJobError { @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + errorCode.code(); - result = prime * result + ((errorText == null) ? 0 : errorText.hashCode()); - return result; + return Objects.hashCode(errorCode, errorText); } @Override @@ -107,14 +104,11 @@ public class AsyncJobError { return false; if (getClass() != obj.getClass()) return false; - AsyncJobError other = (AsyncJobError) obj; - if (errorCode != other.errorCode) - return false; - if (errorText == null) { - if (other.errorText != null) - return false; - } else if (!errorText.equals(other.errorText)) - return false; + AsyncJobError that = (AsyncJobError) obj; + + if (!Objects.equal(errorCode, that.errorCode)) return false; + if (!Objects.equal(errorText, that.errorText)) return false; + return true; } diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Capabilities.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Capabilities.java index 027cad05cf..4d83b8bca1 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Capabilities.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Capabilities.java @@ -18,6 +18,7 @@ */ package org.jclouds.cloudstack.domain; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -129,14 +130,7 @@ public class Capabilities { @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (canShareTemplates ? 1231 : 1237); - result = prime * result + ((cloudStackVersion == null) ? 0 : cloudStackVersion.hashCode()); - result = prime * result + (securityGroupsEnabled ? 1231 : 1237); - result = prime * result + (firewallRuleUiEnabled ? 1231 : 1237); - result = prime * result + (supportELB ? 1231 : 1237); - return result; + return Objects.hashCode(canShareTemplates, cloudStackVersion, securityGroupsEnabled, firewallRuleUiEnabled, supportELB); } @Override @@ -147,20 +141,14 @@ public class Capabilities { return false; if (getClass() != obj.getClass()) return false; - Capabilities other = (Capabilities) obj; - if (canShareTemplates != other.canShareTemplates) - return false; - if (cloudStackVersion == null) { - if (other.cloudStackVersion != null) - return false; - } else if (!cloudStackVersion.equals(other.cloudStackVersion)) - return false; - if (securityGroupsEnabled != other.securityGroupsEnabled) - return false; - if (firewallRuleUiEnabled != other.firewallRuleUiEnabled) - return false; - if (supportELB != other.supportELB) - return false; + Capabilities that = (Capabilities) obj; + + if (!Objects.equal(canShareTemplates, that.canShareTemplates)) return false; + if (!Objects.equal(cloudStackVersion, that.cloudStackVersion)) return false; + if (!Objects.equal(securityGroupsEnabled, that.securityGroupsEnabled)) return false; + if (!Objects.equal(firewallRuleUiEnabled, that.firewallRuleUiEnabled)) return false; + if (!Objects.equal(supportELB, that.supportELB)) return false; + return true; } diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Capacity.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Capacity.java index a674727131..a16a74c8ff 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Capacity.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Capacity.java @@ -23,6 +23,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.Map; import com.google.common.base.Function; +import com.google.common.base.Objects; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.gson.annotations.SerializedName; @@ -202,34 +203,24 @@ public class Capacity implements Comparable { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - Capacity capacity = (Capacity) o; + Capacity that = (Capacity) o; - if (capacityTotal != capacity.capacityTotal) return false; - if (capacityUsed != capacity.capacityUsed) return false; - if (Double.compare(capacity.percentUsed, percentUsed) != 0) return false; - if (podId != capacity.podId) return false; - if (zoneId != capacity.zoneId) return false; - if (podName != null ? !podName.equals(capacity.podName) : capacity.podName != null) return false; - if (type != capacity.type) return false; - if (zoneName != null ? !zoneName.equals(capacity.zoneName) : capacity.zoneName != null) return false; + if (!Objects.equal(capacityTotal, that.capacityTotal)) return false; + if (!Objects.equal(capacityUsed, that.capacityUsed)) return false; + if (!Objects.equal(percentUsed, that.percentUsed)) return false; + if (!Objects.equal(podId, that.podId)) return false; + if (!Objects.equal(zoneId, that.zoneId)) return false; + if (!Objects.equal(podName, that.podName)) return false; + if (!Objects.equal(type, that.type)) return false; + if (!Objects.equal(zoneName, that.zoneName)) return false; return true; } @Override public int hashCode() { - int result; - long temp; - result = (int) (capacityTotal ^ (capacityTotal >>> 32)); - result = 31 * result + (int) (capacityUsed ^ (capacityUsed >>> 32)); - temp = percentUsed != +0.0d ? Double.doubleToLongBits(percentUsed) : 0L; - result = 31 * result + (int) (temp ^ (temp >>> 32)); - result = 31 * result + (int) (podId ^ (podId >>> 32)); - result = 31 * result + (podName != null ? podName.hashCode() : 0); - result = 31 * result + (type != null ? type.hashCode() : 0); - result = 31 * result + (int) (zoneId ^ (zoneId >>> 32)); - result = 31 * result + (zoneName != null ? zoneName.hashCode() : 0); - return result; + return Objects.hashCode(capacityTotal, capacityUsed, percentUsed, podId, podName, + type, zoneId, zoneName); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Cluster.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Cluster.java index cd3714775c..4714939c4a 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Cluster.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Cluster.java @@ -21,6 +21,7 @@ package org.jclouds.cloudstack.domain; import static com.google.common.base.CaseFormat.UPPER_CAMEL; import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -194,35 +195,26 @@ public class Cluster implements Comparable { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - Cluster cluster = (Cluster) o; + Cluster that = (Cluster) o; - if (id != cluster.id) return false; - if (podId != cluster.podId) return false; - if (zoneId != cluster.zoneId) return false; - if (allocationState != cluster.allocationState) return false; - if (clusterType != cluster.clusterType) return false; - if (hypervisor != null ? !hypervisor.equals(cluster.hypervisor) : cluster.hypervisor != null) return false; - if (managedState != cluster.managedState) return false; - if (name != null ? !name.equals(cluster.name) : cluster.name != null) return false; - if (podName != null ? !podName.equals(cluster.podName) : cluster.podName != null) return false; - if (zoneName != null ? !zoneName.equals(cluster.zoneName) : cluster.zoneName != null) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(podId, that.podId)) return false; + if (!Objects.equal(zoneId, that.zoneId)) return false; + if (!Objects.equal(allocationState, that.allocationState)) return false; + if (!Objects.equal(clusterType, that.clusterType)) return false; + if (!Objects.equal(hypervisor, that.hypervisor)) return false; + if (!Objects.equal(managedState, that.managedState)) return false; + if (!Objects.equal(name, that.name)) return false; + if (!Objects.equal(podName, that.podName)) return false; + if (!Objects.equal(zoneName, that.zoneName)) return false; return true; } @Override public int hashCode() { - int result = (int) (id ^ (id >>> 32)); - result = 31 * result + (allocationState != null ? allocationState.hashCode() : 0); - result = 31 * result + (clusterType != null ? clusterType.hashCode() : 0); - result = 31 * result + (hypervisor != null ? hypervisor.hashCode() : 0); - result = 31 * result + (managedState != null ? managedState.hashCode() : 0); - result = 31 * result + (name != null ? name.hashCode() : 0); - result = 31 * result + (int) (podId ^ (podId >>> 32)); - result = 31 * result + (podName != null ? podName.hashCode() : 0); - result = 31 * result + (int) (zoneId ^ (zoneId >>> 32)); - result = 31 * result + (zoneName != null ? zoneName.hashCode() : 0); - return result; + return Objects.hashCode(id, allocationState, clusterType, hypervisor, managedState, name, podId, podName, + zoneId, zoneName); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ConfigurationEntry.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ConfigurationEntry.java index 475652134c..d3a655bf1b 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ConfigurationEntry.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ConfigurationEntry.java @@ -19,6 +19,8 @@ package org.jclouds.cloudstack.domain; +import com.google.common.base.Objects; + /** * Representation of the API configuration entry response * @@ -106,25 +108,17 @@ public class ConfigurationEntry implements Comparable { ConfigurationEntry that = (ConfigurationEntry) o; - if (category != null ? !category.equals(that.category) : that.category != null) - return false; - if (description != null ? !description.equals(that.description) : that.description != null) - return false; - if (name != null ? !name.equals(that.name) : that.name != null) - return false; - if (value != null ? !value.equals(that.value) : that.value != null) - return false; + if (!Objects.equal(category, that.category)) return false; + if (!Objects.equal(description, that.description)) return false; + if (!Objects.equal(name, that.name)) return false; + if (!Objects.equal(value, that.value)) return false; return true; } @Override public int hashCode() { - int result = category != null ? category.hashCode() : 0; - result = 31 * result + (description != null ? description.hashCode() : 0); - result = 31 * result + (name != null ? name.hashCode() : 0); - result = 31 * result + (value != null ? value.hashCode() : 0); - return result; + return Objects.hashCode(category, description, name, value); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/DiskOffering.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/DiskOffering.java index 6e2cd0be60..8ea21254b0 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/DiskOffering.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/DiskOffering.java @@ -24,6 +24,7 @@ import java.util.Date; import java.util.Set; import com.google.common.base.Joiner; +import com.google.common.base.Objects; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableSet; import com.google.gson.annotations.SerializedName; @@ -209,18 +210,7 @@ public class DiskOffering implements Comparable { @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((created == null) ? 0 : created.hashCode()); - result = prime * result + (customized ? 1231 : 1237); - result = prime * result + diskSize; - result = prime * result + ((displayText == null) ? 0 : displayText.hashCode()); - result = prime * result + ((domain == null) ? 0 : domain.hashCode()); - result = prime * result + (int) (domainId ^ (domainId >>> 32)); - result = prime * result + (int) (id ^ (id >>> 32)); - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((tags == null) ? 0 : tags.hashCode()); - return result; + return Objects.hashCode(created, customized, diskSize, displayText, domain, domainId, id, name, tags); } @Override @@ -231,40 +221,18 @@ public class DiskOffering implements Comparable { return false; if (getClass() != obj.getClass()) return false; - DiskOffering other = (DiskOffering) obj; - if (created == null) { - if (other.created != null) - return false; - } else if (!created.equals(other.created)) - return false; - if (customized != other.customized) - return false; - if (diskSize != other.diskSize) - return false; - if (displayText == null) { - if (other.displayText != null) - return false; - } else if (!displayText.equals(other.displayText)) - return false; - if (domain == null) { - if (other.domain != null) - return false; - } else if (!domain.equals(other.domain)) - return false; - if (domainId != other.domainId) - return false; - if (id != other.id) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (tags == null) { - if (other.tags != null) - return false; - } else if (!tags.equals(other.tags)) - return false; + DiskOffering that = (DiskOffering) obj; + + if (!Objects.equal(created, that.created)) return false; + if (!Objects.equal(customized, that.customized)) return false; + if (!Objects.equal(diskSize, that.diskSize)) return false; + if (!Objects.equal(displayText, that.displayText)) return false; + if (!Objects.equal(domain, that.domain)) return false; + if (!Objects.equal(domainId, that.domainId)) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(name, that.name)) return false; + if (!Objects.equal(tags, that.tags)) return false; + return true; } diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Domain.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Domain.java index 3e1fc7ab0c..577598f456 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Domain.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Domain.java @@ -19,6 +19,7 @@ package org.jclouds.cloudstack.domain; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -143,32 +144,22 @@ public class Domain implements Comparable { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - Domain domain = (Domain) o; + Domain that = (Domain) o; - if (hasChild != domain.hasChild) return false; - if (id != domain.id) return false; - if (level != domain.level) return false; - if (parentDomainId != domain.parentDomainId) return false; - if (name != null ? !name.equals(domain.name) : domain.name != null) - return false; - if (networkDomain != null ? !networkDomain.equals(domain.networkDomain) : domain.networkDomain != null) - return false; - if (parentDomainName != null ? !parentDomainName.equals(domain.parentDomainName) : domain.parentDomainName != null) - return false; + if (!Objects.equal(hasChild, that.hasChild)) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(level, that.level)) return false; + if (!Objects.equal(parentDomainId, that.parentDomainId)) return false; + if (!Objects.equal(name, that.name)) return false; + if (!Objects.equal(networkDomain, that.networkDomain)) return false; + if (!Objects.equal(parentDomainName, that.parentDomainName)) return false; return true; } @Override public int hashCode() { - int result = (int) (id ^ (id >>> 32)); - result = 31 * result + (hasChild ? 1 : 0); - result = 31 * result + (int) (level ^ (level >>> 32)); - result = 31 * result + (name != null ? name.hashCode() : 0); - result = 31 * result + (networkDomain != null ? networkDomain.hashCode() : 0); - result = 31 * result + (int) (parentDomainId ^ (parentDomainId >>> 32)); - result = 31 * result + (parentDomainName != null ? parentDomainName.hashCode() : 0); - return result; + return Objects.hashCode(id, hasChild, level, name, networkDomain, parentDomainId, parentDomainName); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/EncryptedPasswordAndPrivateKey.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/EncryptedPasswordAndPrivateKey.java index 40ecd2a476..fcec6d9174 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/EncryptedPasswordAndPrivateKey.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/EncryptedPasswordAndPrivateKey.java @@ -18,6 +18,8 @@ */ package org.jclouds.cloudstack.domain; +import com.google.common.base.Objects; + /** * @author Andrei Savu */ @@ -52,18 +54,15 @@ public class EncryptedPasswordAndPrivateKey { EncryptedPasswordAndPrivateKey that = (EncryptedPasswordAndPrivateKey) o; - if (encryptedPassword != null ? !encryptedPassword.equals(that.encryptedPassword) : that.encryptedPassword != null) - return false; - if (privateKey != null ? !privateKey.equals(that.privateKey) : that.privateKey != null) return false; + if (!Objects.equal(encryptedPassword, that.encryptedPassword)) return false; + if (!Objects.equal(privateKey, that.privateKey)) return false; return true; } @Override public int hashCode() { - int result = encryptedPassword != null ? encryptedPassword.hashCode() : 0; - result = 31 * result + (privateKey != null ? privateKey.hashCode() : 0); - return result; + return Objects.hashCode(encryptedPassword, privateKey); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Event.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Event.java index afeb56c04d..0666114c57 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Event.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Event.java @@ -18,6 +18,8 @@ */ package org.jclouds.cloudstack.domain; +import com.google.common.base.Objects; + import java.util.Date; /** @@ -230,37 +232,26 @@ public class Event implements Comparable { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - Event event = (Event) o; + Event that = (Event) o; - if (domainId != event.domainId) return false; - if (id != event.id) return false; - if (account != null ? !account.equals(event.account) : event.account != null) return false; - if (created != null ? !created.equals(event.created) : event.created != null) return false; - if (description != null ? !description.equals(event.description) : event.description != null) return false; - if (domain != null ? !domain.equals(event.domain) : event.domain != null) return false; - if (level != null ? !level.equals(event.level) : event.level != null) return false; - if (parentId != null ? !parentId.equals(event.parentId) : event.parentId != null) return false; - if (state != null ? !state.equals(event.state) : event.state != null) return false; - if (type != null ? !type.equals(event.type) : event.type != null) return false; - if (username != null ? !username.equals(event.username) : event.username != null) return false; + if (!Objects.equal(domainId, that.domainId)) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(account, that.account)) return false; + if (!Objects.equal(created, that.created)) return false; + if (!Objects.equal(description, that.description)) return false; + if (!Objects.equal(domain, that.domain)) return false; + if (!Objects.equal(level, that.level)) return false; + if (!Objects.equal(parentId, that.parentId)) return false; + if (!Objects.equal(state, that.state)) return false; + if (!Objects.equal(type, that.type)) return false; + if (!Objects.equal(username, that.username)) return false; return true; } @Override public int hashCode() { - int result = (int) (id ^ (id >>> 32)); - result = 31 * result + (account != null ? account.hashCode() : 0); - result = 31 * result + (description != null ? description.hashCode() : 0); - result = 31 * result + (created != null ? created.hashCode() : 0); - result = 31 * result + (domain != null ? domain.hashCode() : 0); - result = 31 * result + (int) (domainId ^ (domainId >>> 32)); - result = 31 * result + (level != null ? level.hashCode() : 0); - result = 31 * result + (parentId != null ? parentId.hashCode() : 0); - result = 31 * result + (state != null ? state.hashCode() : 0); - result = 31 * result + (type != null ? type.hashCode() : 0); - result = 31 * result + (username != null ? username.hashCode() : 0); - return result; + return Objects.hashCode(id, account, description, created, domain, domainId, level, parentId, state, type, username); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/FirewallRule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/FirewallRule.java index a8375b96b7..c53366a2b6 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/FirewallRule.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/FirewallRule.java @@ -21,6 +21,7 @@ package org.jclouds.cloudstack.domain; import java.util.Set; import com.google.common.base.CaseFormat; +import com.google.common.base.Objects; import com.google.common.collect.ImmutableSet; import com.google.gson.annotations.SerializedName; @@ -233,40 +234,23 @@ public class FirewallRule implements Comparable { FirewallRule that = (FirewallRule) o; - if (endPort != that.endPort) return false; - if (id != that.id) return false; - if (startPort != that.startPort) return false; - if (CIDRs != null ? !CIDRs.equals(that.CIDRs) : that.CIDRs != null) - return false; - if (icmpCode != null ? !icmpCode.equals(that.icmpCode) : that.icmpCode != null) - return false; - if (icmpType != null ? !icmpType.equals(that.icmpType) : that.icmpType != null) - return false; - if (ipAddress != null ? !ipAddress.equals(that.ipAddress) : that.ipAddress != null) - return false; - if (ipAddressId != that.ipAddressId) - return false; - if (protocol != null ? !protocol.equals(that.protocol) : that.protocol != null) - return false; - if (state != null ? !state.equals(that.state) : that.state != null) - return false; + if (!Objects.equal(endPort, that.endPort)) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(startPort, that.startPort)) return false; + if (!Objects.equal(CIDRs, that.CIDRs)) return false; + if (!Objects.equal(icmpCode, that.icmpCode)) return false; + if (!Objects.equal(icmpType, that.icmpType)) return false; + if (!Objects.equal(ipAddress, that.ipAddress)) return false; + if (!Objects.equal(ipAddressId, that.ipAddressId)) return false; + if (!Objects.equal(protocol, that.protocol)) return false; + if (!Objects.equal(state, that.state)) return false; return true; } @Override public int hashCode() { - int result = (int) (id ^ (id >>> 32)); - result = 31 * result + (CIDRs != null ? CIDRs.hashCode() : 0); - result = 31 * result + startPort; - result = 31 * result + endPort; - result = 31 * result + (icmpCode != null ? icmpCode.hashCode() : 0); - result = 31 * result + (icmpType != null ? icmpType.hashCode() : 0); - result = 31 * result + (ipAddress != null ? ipAddress.hashCode() : 0); - result = 31 * result + (int) (ipAddressId ^ (ipAddressId >>> 32)); - result = 31 * result + (protocol != null ? protocol.hashCode() : 0); - result = 31 * result + (state != null ? state.hashCode() : 0); - return result; + return Objects.hashCode(endPort, id, startPort, CIDRs, icmpCode, icmpType, ipAddress, ipAddressId, protocol, state); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Host.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Host.java index 18ae419ab7..2de0e4c58e 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Host.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Host.java @@ -23,6 +23,7 @@ import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE; import java.util.Date; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -693,101 +694,64 @@ public class Host implements Comparable { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - Host host = (Host) o; + Host that = (Host) o; - if (averageLoad != host.averageLoad) return false; - if (clusterId != host.clusterId) return false; - if (cpuNumber != host.cpuNumber) return false; - if (cpuSpeed != host.cpuSpeed) return false; - if (Float.compare(host.cpuWithOverProvisioning, cpuWithOverProvisioning) != 0) return false; - if (diskSizeAllocated != host.diskSizeAllocated) return false; - if (diskSizeTotal != host.diskSizeTotal) return false; - if (hasEnoughCapacity != host.hasEnoughCapacity) return false; - if (id != host.id) return false; - if (jobId != host.jobId) return false; - if (localStorageActive != host.localStorageActive) return false; - if (managementServerId != host.managementServerId) return false; - if (memoryAllocated != host.memoryAllocated) return false; - if (memoryTotal != host.memoryTotal) return false; - if (memoryUsed != host.memoryUsed) return false; - if (networkKbsRead != host.networkKbsRead) return false; - if (networkKbsWrite != host.networkKbsWrite) return false; - if (osCategoryId != host.osCategoryId) return false; - if (osCategoryName != host.osCategoryName) return false; - if (podId != host.podId) return false; - if (zoneId != host.zoneId) return false; - if (allocationState != host.allocationState) return false; - if (capabilities != null ? !capabilities.equals(host.capabilities) : host.capabilities != null) return false; - if (clusterName != null ? !clusterName.equals(host.clusterName) : host.clusterName != null) return false; - if (clusterType != host.clusterType) return false; - if (cpuAllocated != null ? !cpuAllocated.equals(host.cpuAllocated) : host.cpuAllocated != null) return false; - if (cpuUsed != null ? !cpuUsed.equals(host.cpuUsed) : host.cpuUsed != null) return false; - if (created != null ? !created.equals(host.created) : host.created != null) return false; - if (disconnected != null ? !disconnected.equals(host.disconnected) : host.disconnected != null) return false; - if (events != null ? !events.equals(host.events) : host.events != null) return false; - if (hostTags != null ? !hostTags.equals(host.hostTags) : host.hostTags != null) return false; - if (hypervisor != null ? !hypervisor.equals(host.hypervisor) : host.hypervisor != null) return false; - if (ipAddress != null ? !ipAddress.equals(host.ipAddress) : host.ipAddress != null) return false; - if (jobStatus != host.jobStatus) return false; - if (lastPinged != null ? !lastPinged.equals(host.lastPinged) : host.lastPinged != null) return false; - if (name != null ? !name.equals(host.name) : host.name != null) return false; - if (podName != null ? !podName.equals(host.podName) : host.podName != null) return false; - if (removed != null ? !removed.equals(host.removed) : host.removed != null) return false; - if (state != host.state) return false; - if (type != host.type) return false; - if (version != null ? !version.equals(host.version) : host.version != null) return false; - if (zoneName != null ? !zoneName.equals(host.zoneName) : host.zoneName != null) return false; + if (!Objects.equal(averageLoad, that.averageLoad)) return false; + if (!Objects.equal(clusterId, that.clusterId)) return false; + if (!Objects.equal(cpuNumber, that.cpuNumber)) return false; + if (!Objects.equal(cpuSpeed, that.cpuSpeed)) return false; + if (!Objects.equal(cpuWithOverProvisioning, that.cpuWithOverProvisioning)) return false; + if (!Objects.equal(diskSizeAllocated, that.diskSizeAllocated)) return false; + if (!Objects.equal(diskSizeTotal, that.diskSizeTotal)) return false; + if (!Objects.equal(hasEnoughCapacity, that.hasEnoughCapacity)) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(jobId, that.jobId)) return false; + if (!Objects.equal(localStorageActive, that.localStorageActive)) return false; + if (!Objects.equal(managementServerId, that.managementServerId)) return false; + if (!Objects.equal(memoryAllocated, that.memoryAllocated)) return false; + if (!Objects.equal(memoryTotal, that.memoryTotal)) return false; + if (!Objects.equal(memoryUsed, that.memoryUsed)) return false; + if (!Objects.equal(networkKbsRead, that.networkKbsRead)) return false; + if (!Objects.equal(networkKbsWrite, that.networkKbsWrite)) return false; + if (!Objects.equal(osCategoryId, that.osCategoryId)) return false; + if (!Objects.equal(osCategoryName, that.osCategoryName)) return false; + if (!Objects.equal(podId, that.podId)) return false; + if (!Objects.equal(zoneId, that.zoneId)) return false; + if (!Objects.equal(allocationState, that.allocationState)) return false; + if (!Objects.equal(capabilities, that.capabilities)) return false; + if (!Objects.equal(clusterName, that.clusterName)) return false; + if (!Objects.equal(clusterType, that.clusterType)) return false; + if (!Objects.equal(cpuAllocated, that.cpuAllocated)) return false; + if (!Objects.equal(cpuUsed, that.cpuUsed)) return false; + if (!Objects.equal(created, that.created)) return false; + if (!Objects.equal(disconnected, that.disconnected)) return false; + if (!Objects.equal(events, that.events)) return false; + if (!Objects.equal(hostTags, that.hostTags)) return false; + if (!Objects.equal(hypervisor, that.hypervisor)) return false; + if (!Objects.equal(ipAddress, that.ipAddress)) return false; + if (!Objects.equal(jobStatus, that.jobStatus)) return false; + if (!Objects.equal(lastPinged, that.lastPinged)) return false; + if (!Objects.equal(name, that.name)) return false; + if (!Objects.equal(podName, that.podName)) return false; + if (!Objects.equal(removed, that.removed)) return false; + if (!Objects.equal(state, that.state)) return false; + if (!Objects.equal(type, that.type)) return false; + if (!Objects.equal(version, that.version)) return false; + if (!Objects.equal(zoneName, that.zoneName)) return false; return true; } @Override public int hashCode() { - int result = (int) (id ^ (id >>> 32)); - result = 31 * result + (allocationState != null ? allocationState.hashCode() : 0); - result = 31 * result + averageLoad; - result = 31 * result + (capabilities != null ? capabilities.hashCode() : 0); - result = 31 * result + (int) (clusterId ^ (clusterId >>> 32)); - result = 31 * result + (clusterName != null ? clusterName.hashCode() : 0); - result = 31 * result + (clusterType != null ? clusterType.hashCode() : 0); - result = 31 * result + (cpuAllocated != null ? cpuAllocated.hashCode() : 0); - result = 31 * result + cpuNumber; - result = 31 * result + cpuSpeed; - result = 31 * result + (cpuUsed != null ? cpuUsed.hashCode() : 0); - result = 31 * result + (cpuWithOverProvisioning != +0.0f ? Float.floatToIntBits(cpuWithOverProvisioning) : 0); - result = 31 * result + (created != null ? created.hashCode() : 0); - result = 31 * result + (disconnected != null ? disconnected.hashCode() : 0); - result = 31 * result + (int) (diskSizeAllocated ^ (diskSizeAllocated >>> 32)); - result = 31 * result + (int) (diskSizeTotal ^ (diskSizeTotal >>> 32)); - result = 31 * result + (events != null ? events.hashCode() : 0); - result = 31 * result + (hasEnoughCapacity ? 1 : 0); - result = 31 * result + (hostTags != null ? hostTags.hashCode() : 0); - result = 31 * result + (hypervisor != null ? hypervisor.hashCode() : 0); - result = 31 * result + (ipAddress != null ? ipAddress.hashCode() : 0); - result = 31 * result + (localStorageActive ? 1 : 0); - result = 31 * result + (int) (jobId ^ (jobId >>> 32)); - result = 31 * result + (jobStatus != null ? jobStatus.hashCode() : 0); - result = 31 * result + (lastPinged != null ? lastPinged.hashCode() : 0); - result = 31 * result + (int) (managementServerId ^ (managementServerId >>> 32)); - result = 31 * result + (int) (memoryAllocated ^ (memoryAllocated >>> 32)); - result = 31 * result + (int) (memoryTotal ^ (memoryTotal >>> 32)); - result = 31 * result + (int) (memoryUsed ^ (memoryUsed >>> 32)); - result = 31 * result + (name != null ? name.hashCode() : 0); - result = 31 * result + (int) (networkKbsRead ^ (networkKbsRead >>> 32)); - result = 31 * result + (int) (networkKbsWrite ^ (networkKbsWrite >>> 32)); - result = 31 * result + (int) (osCategoryId ^ (osCategoryId >>> 32)); - result = 31 * result + (int) (osCategoryName ^ (osCategoryName >>> 32)); - result = 31 * result + (int) (podId ^ (podId >>> 32)); - result = 31 * result + (podName != null ? podName.hashCode() : 0); - result = 31 * result + (removed != null ? removed.hashCode() : 0); - result = 31 * result + (state != null ? state.hashCode() : 0); - result = 31 * result + (type != null ? type.hashCode() : 0); - result = 31 * result + (version != null ? version.hashCode() : 0); - result = 31 * result + (int) (zoneId ^ (zoneId >>> 32)); - result = 31 * result + (zoneName != null ? zoneName.hashCode() : 0); - return result; + return Objects.hashCode(averageLoad, clusterId, cpuNumber, cpuSpeed, cpuWithOverProvisioning, diskSizeAllocated, + diskSizeTotal, hasEnoughCapacity, id, jobId, localStorageActive, managementServerId, + memoryAllocated, memoryTotal, memoryUsed, networkKbsRead, networkKbsWrite, osCategoryId, + osCategoryName, podId, zoneId, allocationState, capabilities, clusterName, clusterType, + cpuAllocated, cpuUsed, created, disconnected, events, hostTags, hypervisor, ipAddress, + jobStatus, lastPinged, name, podName, removed, state, type, version, zoneName); } - + @Override public String toString() { return "Host{" + diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/IPForwardingRule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/IPForwardingRule.java index 4dca3a0c52..ac9aa5ac1d 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/IPForwardingRule.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/IPForwardingRule.java @@ -21,6 +21,7 @@ package org.jclouds.cloudstack.domain; import java.util.Collections; import java.util.Set; +import com.google.common.base.Objects; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.google.gson.annotations.SerializedName; @@ -280,76 +281,34 @@ public class IPForwardingRule implements Comparable { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((IPAddress == null) ? 0 : IPAddress.hashCode()); - result = prime * result + (int) (IPAddressId ^ (IPAddressId >>> 32)); - result = prime * result + endPort; - result = prime * result + (int) (id ^ (id >>> 32)); - result = prime * result + ((protocol == null) ? 0 : protocol.hashCode()); - result = prime * result + startPort; - result = prime * result + publicEndPort; - result = prime * result + privateEndPort; - result = prime * result + publicPort; - result = prime * result + ((state == null) ? 0 : state.hashCode()); - result = prime * result + ((virtualMachineDisplayName == null) ? 0 : virtualMachineDisplayName.hashCode()); - result = prime * result + (int) (virtualMachineId ^ (virtualMachineId >>> 32)); - result = prime * result + ((virtualMachineName == null) ? 0 : virtualMachineName.hashCode()); - return result; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + IPForwardingRule that = (IPForwardingRule) o; + + if (!Objects.equal(IPAddress, that.IPAddress)) return false; + if (!Objects.equal(IPAddressId, that.IPAddressId)) return false; + if (!Objects.equal(endPort, that.endPort)) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(protocol, that.protocol)) return false; + if (!Objects.equal(startPort, that.startPort)) return false; + if (!Objects.equal(publicEndPort, that.publicEndPort)) return false; + if (!Objects.equal(privateEndPort, that.privateEndPort)) return false; + if (!Objects.equal(publicPort, that.publicPort)) return false; + if (!Objects.equal(state, that.state)) return false; + if (!Objects.equal(virtualMachineDisplayName, that.virtualMachineDisplayName)) return false; + if (!Objects.equal(virtualMachineId, that.virtualMachineId)) return false; + if (!Objects.equal(virtualMachineName, that.virtualMachineName)) return false; + + return true; } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - IPForwardingRule other = (IPForwardingRule) obj; - if (IPAddress == null) { - if (other.IPAddress != null) - return false; - } else if (!IPAddress.equals(other.IPAddress)) - return false; - if (IPAddressId != other.IPAddressId) - return false; - if (endPort != other.endPort) - return false; - if (publicPort != other.publicPort) - return false; - if (publicEndPort != other.publicEndPort) - return false; - if (privateEndPort != other.privateEndPort) - return false; - if (id != other.id) - return false; - if (protocol == null) { - if (other.protocol != null) - return false; - } else if (!protocol.equals(other.protocol)) - return false; - if (startPort != other.startPort) - return false; - if (state == null) { - if (other.state != null) - return false; - } else if (!state.equals(other.state)) - return false; - if (virtualMachineDisplayName == null) { - if (other.virtualMachineDisplayName != null) - return false; - } else if (!virtualMachineDisplayName.equals(other.virtualMachineDisplayName)) - return false; - if (virtualMachineId != other.virtualMachineId) - return false; - if (virtualMachineName == null) { - if (other.virtualMachineName != null) - return false; - } else if (!virtualMachineName.equals(other.virtualMachineName)) - return false; - return true; + public int hashCode() { + return Objects.hashCode(IPAddress, IPAddressId, endPort, id, protocol, startPort, publicEndPort, + privateEndPort, publicPort, state, virtualMachineDisplayName, + virtualMachineId, virtualMachineName); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISO.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISO.java index b38b81509d..82462a4b51 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISO.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISO.java @@ -22,6 +22,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.Date; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -613,79 +614,50 @@ public class ISO implements Comparable { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - ISO iso = (ISO) o; + ISO that = (ISO) o; - if (accountId != iso.accountId) return false; - if (bootable != iso.bootable) return false; - if (crossZones != iso.crossZones) return false; - if (domainid != iso.domainid) return false; - if (hostId != iso.hostId) return false; - if (id != iso.id) return false; - if (isExtractable != iso.isExtractable) return false; - if (isFeatured != iso.isFeatured) return false; - if (isPublic != iso.isPublic) return false; - if (isReady != iso.isReady) return false; - if (jobId != iso.jobId) return false; - if (osTypeId != iso.osTypeId) return false; - if (passwordEnabled != iso.passwordEnabled) return false; - if (size != iso.size) return false; - if (sourceTemplateId != iso.sourceTemplateId) return false; - if (zoneId != iso.zoneId) return false; - if (account != null ? !account.equals(iso.account) : iso.account != null) return false; - if (checksum != null ? !checksum.equals(iso.checksum) : iso.checksum != null) return false; - if (created != null ? !created.equals(iso.created) : iso.created != null) return false; - if (displayText != null ? !displayText.equals(iso.displayText) : iso.displayText != null) return false; - if (domain != null ? !domain.equals(iso.domain) : iso.domain != null) return false; - if (format != null ? !format.equals(iso.format) : iso.format != null) return false; - if (hostName != null ? !hostName.equals(iso.hostName) : iso.hostName != null) return false; - if (hypervisor != null ? !hypervisor.equals(iso.hypervisor) : iso.hypervisor != null) return false; - if (jobStatus != null ? !jobStatus.equals(iso.jobStatus) : iso.jobStatus != null) return false; - if (name != null ? !name.equals(iso.name) : iso.name != null) return false; - if (osTypeName != null ? !osTypeName.equals(iso.osTypeName) : iso.osTypeName != null) return false; - if (removed != null ? !removed.equals(iso.removed) : iso.removed != null) return false; - if (status != null ? !status.equals(iso.status) : iso.status != null) return false; - if (templateTag != null ? !templateTag.equals(iso.templateTag) : iso.templateTag != null) return false; - if (templateType != null ? !templateType.equals(iso.templateType) : iso.templateType != null) return false; - if (zoneName != null ? !zoneName.equals(iso.zoneName) : iso.zoneName != null) return false; + if (!Objects.equal(accountId, that.accountId)) return false; + if (!Objects.equal(bootable, that.bootable)) return false; + if (!Objects.equal(crossZones, that.crossZones)) return false; + if (!Objects.equal(domainid, that.domainid)) return false; + if (!Objects.equal(hostId, that.hostId)) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(isExtractable, that.isExtractable)) return false; + if (!Objects.equal(isPublic, that.isPublic)) return false; + if (!Objects.equal(isReady, that.isReady)) return false; + if (!Objects.equal(jobId, that.jobId)) return false; + if (!Objects.equal(osTypeId, that.osTypeId)) return false; + if (!Objects.equal(passwordEnabled, that.passwordEnabled)) return false; + if (!Objects.equal(size, that.size)) return false; + if (!Objects.equal(sourceTemplateId, that.sourceTemplateId)) return false; + if (!Objects.equal(zoneId, that.zoneId)) return false; + if (!Objects.equal(account, that.account)) return false; + if (!Objects.equal(checksum, that.checksum)) return false; + if (!Objects.equal(created, that.created)) return false; + if (!Objects.equal(displayText, that.displayText)) return false; + if (!Objects.equal(domain, that.domain)) return false; + if (!Objects.equal(format, that.format)) return false; + if (!Objects.equal(hostName, that.hostName)) return false; + if (!Objects.equal(hypervisor, that.hypervisor)) return false; + if (!Objects.equal(jobStatus, that.jobStatus)) return false; + if (!Objects.equal(name, that.name)) return false; + if (!Objects.equal(osTypeName, that.osTypeName)) return false; + if (!Objects.equal(removed, that.removed)) return false; + if (!Objects.equal(status, that.status)) return false; + if (!Objects.equal(templateTag, that.templateTag)) return false; + if (!Objects.equal(templateType, that.templateType)) return false; + if (!Objects.equal(zoneName, that.zoneName)) return false; return true; } @Override public int hashCode() { - int result = (int) (id ^ (id >>> 32)); - result = 31 * result + (account != null ? account.hashCode() : 0); - result = 31 * result + (int) (accountId ^ (accountId >>> 32)); - result = 31 * result + (bootable ? 1 : 0); - result = 31 * result + (checksum != null ? checksum.hashCode() : 0); - result = 31 * result + (created != null ? created.hashCode() : 0); - result = 31 * result + (crossZones ? 1 : 0); - result = 31 * result + (displayText != null ? displayText.hashCode() : 0); - result = 31 * result + (domain != null ? domain.hashCode() : 0); - result = 31 * result + (int) (domainid ^ (domainid >>> 32)); - result = 31 * result + (format != null ? format.hashCode() : 0); - result = 31 * result + (int) (hostId ^ (hostId >>> 32)); - result = 31 * result + (hostName != null ? hostName.hashCode() : 0); - result = 31 * result + (hypervisor != null ? hypervisor.hashCode() : 0); - result = 31 * result + (isExtractable ? 1 : 0); - result = 31 * result + (isFeatured ? 1 : 0); - result = 31 * result + (isPublic ? 1 : 0); - result = 31 * result + (isReady ? 1 : 0); - result = 31 * result + (int) (jobId ^ (jobId >>> 32)); - result = 31 * result + (jobStatus != null ? jobStatus.hashCode() : 0); - result = 31 * result + (name != null ? name.hashCode() : 0); - result = 31 * result + (int) (osTypeId ^ (osTypeId >>> 32)); - result = 31 * result + (osTypeName != null ? osTypeName.hashCode() : 0); - result = 31 * result + (passwordEnabled ? 1 : 0); - result = 31 * result + (removed != null ? removed.hashCode() : 0); - result = 31 * result + (int) (size ^ (size >>> 32)); - result = 31 * result + (int) (sourceTemplateId ^ (sourceTemplateId >>> 32)); - result = 31 * result + (status != null ? status.hashCode() : 0); - result = 31 * result + (templateTag != null ? templateTag.hashCode() : 0); - result = 31 * result + (templateType != null ? templateType.hashCode() : 0); - result = 31 * result + (int) (zoneId ^ (zoneId >>> 32)); - result = 31 * result + (zoneName != null ? zoneName.hashCode() : 0); - return result; + return Objects.hashCode(accountId, bootable, crossZones, domainid, hostId, id, isExtractable, + isPublic, isReady, jobId, osTypeId, passwordEnabled, size, sourceTemplateId, + zoneId, account, checksum, created, displayText, domain, format, hostName, + hypervisor, jobStatus, name, osTypeName, removed, status, templateTag, + templateType, zoneName); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISOExtraction.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISOExtraction.java index 9fb91087c4..9961de23cd 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISOExtraction.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISOExtraction.java @@ -20,6 +20,7 @@ package org.jclouds.cloudstack.domain; import java.util.Date; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -276,39 +277,26 @@ public class ISOExtraction implements Comparable { ISOExtraction that = (ISOExtraction) o; - if (accountId != that.accountId) return false; - if (extractId != that.extractId) return false; - if (id != that.id) return false; - if (uploadPercentage != that.uploadPercentage) return false; - if (zoneId != that.zoneId) return false; - if (created != null ? !created.equals(that.created) : that.created != null) return false; - if (extractMode != that.extractMode) return false; - if (name != null ? !name.equals(that.name) : that.name != null) return false; - if (state != null ? !state.equals(that.state) : that.state != null) return false; - if (status != null ? !status.equals(that.status) : that.status != null) return false; - if (storageType != null ? !storageType.equals(that.storageType) : that.storageType != null) return false; - if (url != null ? !url.equals(that.url) : that.url != null) return false; - if (zoneName != null ? !zoneName.equals(that.zoneName) : that.zoneName != null) return false; + if (!Objects.equal(accountId, that.accountId)) return false; + if (!Objects.equal(extractId, that.extractId)) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(uploadPercentage, that.uploadPercentage)) return false; + if (!Objects.equal(zoneId, that.zoneId)) return false; + if (!Objects.equal(created, that.created)) return false; + if (!Objects.equal(extractMode, that.extractMode)) return false; + if (!Objects.equal(name, that.name)) return false; + if (!Objects.equal(state, that.state)) return false; + if (!Objects.equal(status, that.status)) return false; + if (!Objects.equal(storageType, that.storageType)) return false; + if (!Objects.equal(url, that.url)) return false; + if (!Objects.equal(zoneName, that.zoneName)) return false; return true; } @Override public int hashCode() { - int result = (int) (id ^ (id >>> 32)); - result = 31 * result + (int) (accountId ^ (accountId >>> 32)); - result = 31 * result + (created != null ? created.hashCode() : 0); - result = 31 * result + (int) (extractId ^ (extractId >>> 32)); - result = 31 * result + (extractMode != null ? extractMode.hashCode() : 0); - result = 31 * result + (name != null ? name.hashCode() : 0); - result = 31 * result + (state != null ? state.hashCode() : 0); - result = 31 * result + (status != null ? status.hashCode() : 0); - result = 31 * result + (storageType != null ? storageType.hashCode() : 0); - result = 31 * result + uploadPercentage; - result = 31 * result + (url != null ? url.hashCode() : 0); - result = 31 * result + (int) (zoneId ^ (zoneId >>> 32)); - result = 31 * result + (zoneName != null ? zoneName.hashCode() : 0); - return result; + return Objects.hashCode(accountId, extractId, id, uploadPercentage, zoneId, created, extractMode, name, state, status, storageType, url, zoneName); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISOPermissions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISOPermissions.java index ac0214507f..f03f3ac0ab 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISOPermissions.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISOPermissions.java @@ -20,6 +20,7 @@ package org.jclouds.cloudstack.domain; import java.util.Set; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -115,37 +116,23 @@ public class ISOPermissions implements Comparable { } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ISOPermissions other = (ISOPermissions) obj; - if (accounts == null) { - if (other.accounts != null) - return false; - } else if (!accounts.equals(other.accounts)) - return false; - if (domainId != other.domainId) - return false; - if (id != other.id) - return false; - if (isPublic != other.isPublic) - return false; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ISOPermissions that = (ISOPermissions) o; + + if (!Objects.equal(accounts, that.accounts)) return false; + if (!Objects.equal(domainId, that.domainId)) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(isPublic, that.isPublic)) return false; + return true; } @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((accounts == null) ? 0 : accounts.hashCode()); - result = prime * result + (int) (domainId ^ (domainId >>> 32)); - result = prime * result + (int) (id ^ (id >>> 32)); - result = prime * result + (isPublic ? 1231 : 1237); - return result; + return Objects.hashCode(accounts, domainId, id, isPublic); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/IngressRule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/IngressRule.java index 4012e48101..6224a32ad1 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/IngressRule.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/IngressRule.java @@ -20,6 +20,7 @@ package org.jclouds.cloudstack.domain; import static com.google.common.base.Preconditions.checkArgument; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -197,61 +198,28 @@ public class IngressRule implements Comparable { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((CIDR == null) ? 0 : CIDR.hashCode()); - result = prime * result + ICMPCode; - result = prime * result + ICMPType; - result = prime * result + ((account == null) ? 0 : account.hashCode()); - result = prime * result + endPort; - result = prime * result + (int) (id ^ (id >>> 32)); - result = prime * result + ((protocol == null) ? 0 : protocol.hashCode()); - result = prime * result + ((securityGroupName == null) ? 0 : securityGroupName.hashCode()); - result = prime * result + startPort; - return result; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + IngressRule that = (IngressRule) o; + + if (!Objects.equal(CIDR, that.CIDR)) return false; + if (!Objects.equal(ICMPCode, that.ICMPCode)) return false; + if (!Objects.equal(ICMPType, that.ICMPType)) return false; + if (!Objects.equal(account, that.account)) return false; + if (!Objects.equal(endPort, that.endPort)) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(protocol, that.protocol)) return false; + if (!Objects.equal(securityGroupName, that.securityGroupName)) return false; + if (!Objects.equal(startPort, that.startPort)) return false; + + return true; } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - IngressRule other = (IngressRule) obj; - if (CIDR == null) { - if (other.CIDR != null) - return false; - } else if (!CIDR.equals(other.CIDR)) - return false; - if (ICMPCode != other.ICMPCode) - return false; - if (ICMPType != other.ICMPType) - return false; - if (account == null) { - if (other.account != null) - return false; - } else if (!account.equals(other.account)) - return false; - if (endPort != other.endPort) - return false; - if (id != other.id) - return false; - if (protocol == null) { - if (other.protocol != null) - return false; - } else if (!protocol.equals(other.protocol)) - return false; - if (securityGroupName == null) { - if (other.securityGroupName != null) - return false; - } else if (!securityGroupName.equals(other.securityGroupName)) - return false; - if (startPort != other.startPort) - return false; - return true; + public int hashCode() { + return Objects.hashCode(CIDR, ICMPCode, ICMPType, account, endPort, id, protocol, securityGroupName, startPort); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/JobResult.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/JobResult.java index 823114a62a..f6b3bc469d 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/JobResult.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/JobResult.java @@ -18,6 +18,7 @@ */ package org.jclouds.cloudstack.domain; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -60,17 +61,15 @@ public class JobResult implements Comparable { JobResult that = (JobResult) o; - if (success != that.success) return false; - if (displayText != null ? !displayText.equals(that.displayText) : that.displayText != null) return false; + if (!Objects.equal(success, that.success)) return false; + if (!Objects.equal(displayText, that.displayText)) return false; return true; } @Override public int hashCode() { - int result = (success ? 1 : 0); - result = 31 * result + (displayText != null ? displayText.hashCode() : 0); - return result; + return Objects.hashCode(success, displayText); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/LoadBalancerRule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/LoadBalancerRule.java index 5feb52569c..1450a4127d 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/LoadBalancerRule.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/LoadBalancerRule.java @@ -23,6 +23,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.Set; import com.google.common.base.CaseFormat; +import com.google.common.base.Objects; import com.google.common.collect.ImmutableSet; import com.google.gson.annotations.SerializedName; @@ -301,7 +302,7 @@ public class LoadBalancerRule implements Comparable { } /** - * @return the id of the zone the rule belongs to + * @return the id of the zone the rule beStrings to */ public long getZoneId() { return zoneId; @@ -314,82 +315,32 @@ public class LoadBalancerRule implements Comparable { @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((account == null) ? 0 : account.hashCode()); - result = prime * result + ((algorithm == null) ? 0 : algorithm.hashCode()); - result = prime * result + ((description == null) ? 0 : description.hashCode()); - result = prime * result + ((domain == null) ? 0 : domain.hashCode()); - result = prime * result + (int) (domainId ^ (domainId >>> 32)); - result = prime * result + (int) (id ^ (id >>> 32)); - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + privatePort; - result = prime * result + ((publicIP == null) ? 0 : publicIP.hashCode()); - result = prime * result + (int) (publicIPId ^ (publicIPId >>> 32)); - result = prime * result + publicPort; - result = prime * result + (int) (zoneId ^ (zoneId >>> 32)); - result = prime * result + ((state == null) ? 0 : state.hashCode()); - return result; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + LoadBalancerRule that = (LoadBalancerRule) o; + + if (!Objects.equal(account, that.account)) return false; + if (!Objects.equal(algorithm, that.algorithm)) return false; + if (!Objects.equal(description, that.description)) return false; + if (!Objects.equal(domain, that.domain)) return false; + if (!Objects.equal(domainId, that.domainId)) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(name, that.name)) return false; + if (!Objects.equal(privatePort, that.privatePort)) return false; + if (!Objects.equal(publicIP, that.publicIP)) return false; + if (!Objects.equal(publicIPId, that.publicIPId)) return false; + if (!Objects.equal(publicPort, that.publicPort)) return false; + if (!Objects.equal(zoneId, that.zoneId)) return false; + if (!Objects.equal(state, that.state)) return false; + + return true; } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - LoadBalancerRule other = (LoadBalancerRule) obj; - if (account == null) { - if (other.account != null) - return false; - } else if (!account.equals(other.account)) - return false; - if (algorithm == null) { - if (other.algorithm != null) - return false; - } else if (!algorithm.equals(other.algorithm)) - return false; - if (description == null) { - if (other.description != null) - return false; - } else if (!description.equals(other.description)) - return false; - if (domain == null) { - if (other.domain != null) - return false; - } else if (!domain.equals(other.domain)) - return false; - if (domainId != other.domainId) - return false; - if (zoneId != other.zoneId) - return false; - if (id != other.id) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (privatePort != other.privatePort) - return false; - if (publicIP == null) { - if (other.publicIP != null) - return false; - } else if (!publicIP.equals(other.publicIP)) - return false; - if (publicIPId != other.publicIPId) - return false; - if (publicPort != other.publicPort) - return false; - if (state == null) { - if (other.state != null) - return false; - } else if (!state.equals(other.state)) - return false; - return true; + public int hashCode() { + return Objects.hashCode(account, algorithm, description, domain, domainId, id, name, privatePort, publicIP, publicIPId, publicPort, zoneId, state); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/LoginResponse.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/LoginResponse.java index 9fc2474a2f..3a97d99b81 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/LoginResponse.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/LoginResponse.java @@ -19,6 +19,7 @@ package org.jclouds.cloudstack.domain; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -242,94 +243,35 @@ public class LoginResponse implements Comparable { } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - LoginResponse other = (LoginResponse) obj; - if (accountName == null) { - if (other.accountName != null) - return false; - } else if (!accountName.equals(other.accountName)) - return false; - if (accountType == null) { - if (other.accountType != null) - return false; - } else if (!accountType.equals(other.accountType)) - return false; - if (domainId != other.domainId) - return false; - if (firstName == null) { - if (other.firstName != null) - return false; - } else if (!firstName.equals(other.firstName)) - return false; - if (jSessionId == null) { - if (other.jSessionId != null) - return false; - } else if (!jSessionId.equals(other.jSessionId)) - return false; - if (lastName == null) { - if (other.lastName != null) - return false; - } else if (!lastName.equals(other.lastName)) - return false; - if (password == null) { - if (other.password != null) - return false; - } else if (!password.equals(other.password)) - return false; - if (registered != other.registered) - return false; - if (sessionKey == null) { - if (other.sessionKey != null) - return false; - } else if (!sessionKey.equals(other.sessionKey)) - return false; - if (timeout != other.timeout) - return false; - if (timezone == null) { - if (other.timezone != null) - return false; - } else if (!timezone.equals(other.timezone)) - return false; - if (timezoneOffset == null) { - if (other.timezoneOffset != null) - return false; - } else if (!timezoneOffset.equals(other.timezoneOffset)) - return false; - if (userId != other.userId) - return false; - if (username == null) { - if (other.username != null) - return false; - } else if (!username.equals(other.username)) - return false; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + LoginResponse that = (LoginResponse) o; + + if (!Objects.equal(accountName, that.accountName)) return false; + if (!Objects.equal(accountType, that.accountType)) return false; + if (!Objects.equal(domainId, that.domainId)) return false; + if (!Objects.equal(firstName, that.firstName)) return false; + if (!Objects.equal(jSessionId, that.jSessionId)) return false; + if (!Objects.equal(lastName, that.lastName)) return false; + if (!Objects.equal(password, that.password)) return false; + if (!Objects.equal(registered, that.registered)) return false; + if (!Objects.equal(sessionKey, that.sessionKey)) return false; + if (!Objects.equal(timeout, that.timeout)) return false; + if (!Objects.equal(timezone, that.timezone)) return false; + if (!Objects.equal(timezoneOffset, that.timezoneOffset)) return false; + if (!Objects.equal(userId, that.userId)) return false; + if (!Objects.equal(username, that.username)) return false; + return true; } @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((accountName == null) ? 0 : accountName.hashCode()); - result = prime * result + ((accountType == null) ? 0 : accountType.hashCode()); - result = prime * result + (int) (domainId ^ (domainId >>> 32)); - result = prime * result + ((firstName == null) ? 0 : firstName.hashCode()); - result = prime * result + ((jSessionId == null) ? 0 : jSessionId.hashCode()); - result = prime * result + ((lastName == null) ? 0 : lastName.hashCode()); - result = prime * result + ((password == null) ? 0 : password.hashCode()); - result = prime * result + (registered ? 1231 : 1237); - result = prime * result + ((sessionKey == null) ? 0 : sessionKey.hashCode()); - result = prime * result + (int) (timeout ^ (timeout >>> 32)); - result = prime * result + ((timezone == null) ? 0 : timezone.hashCode()); - result = prime * result + ((timezoneOffset == null) ? 0 : timezoneOffset.hashCode()); - result = prime * result + (int) (userId ^ (userId >>> 32)); - result = prime * result + ((username == null) ? 0 : username.hashCode()); - return result; + return Objects.hashCode(accountName, accountType, domainId, firstName, jSessionId, lastName, + password, registered, sessionKey, timeout, timezone, timezoneOffset, + userId, username); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NIC.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NIC.java index 22133eb85c..d5b9f7cf74 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NIC.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NIC.java @@ -20,6 +20,7 @@ package org.jclouds.cloudstack.domain; import java.net.URI; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -226,73 +227,30 @@ public class NIC { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((IPAddress == null) ? 0 : IPAddress.hashCode()); - result = prime * result + ((broadcastURI == null) ? 0 : broadcastURI.hashCode()); - result = prime * result + ((gateway == null) ? 0 : gateway.hashCode()); - result = prime * result + ((guestIPType == null) ? 0 : guestIPType.hashCode()); - result = prime * result + (int) (id ^ (id >>> 32)); - result = prime * result + (isDefault ? 1231 : 1237); - result = prime * result + ((isolationURI == null) ? 0 : isolationURI.hashCode()); - result = prime * result + ((netmask == null) ? 0 : netmask.hashCode()); - result = prime * result + ((macAddress == null) ? 0 : macAddress.hashCode()); - result = prime * result + (int) (networkId ^ (networkId >>> 32)); - result = prime * result + ((trafficType == null) ? 0 : trafficType.hashCode()); - return result; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + NIC that = (NIC) o; + + if (!Objects.equal(IPAddress, that.IPAddress)) return false; + if (!Objects.equal(broadcastURI, that.broadcastURI)) return false; + if (!Objects.equal(gateway, that.gateway)) return false; + if (!Objects.equal(guestIPType, that.guestIPType)) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(isDefault, that.isDefault)) return false; + if (!Objects.equal(isolationURI, that.isolationURI)) return false; + if (!Objects.equal(netmask, that.netmask)) return false; + if (!Objects.equal(macAddress, that.macAddress)) return false; + if (!Objects.equal(networkId, that.networkId)) return false; + if (!Objects.equal(trafficType, that.trafficType)) return false; + + return true; } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - NIC other = (NIC) obj; - if (IPAddress == null) { - if (other.IPAddress != null) - return false; - } else if (!IPAddress.equals(other.IPAddress)) - return false; - if (broadcastURI == null) { - if (other.broadcastURI != null) - return false; - } else if (!broadcastURI.equals(other.broadcastURI)) - return false; - if (gateway == null) { - if (other.gateway != null) - return false; - } else if (!gateway.equals(other.gateway)) - return false; - if (guestIPType != other.guestIPType) - return false; - if (id != other.id) - return false; - if (isDefault != other.isDefault) - return false; - if (isolationURI == null) { - if (other.isolationURI != null) - return false; - } else if (!isolationURI.equals(other.isolationURI)) - return false; - if (netmask == null) { - if (other.netmask != null) - return false; - } else if (!netmask.equals(other.netmask)) - return false; - if (macAddress == null) { - if (other.macAddress != null) - return false; - } else if (!macAddress.equals(other.macAddress)) - return false; - if (networkId != other.networkId) - return false; - if (trafficType != other.trafficType) - return false; - return true; + public int hashCode() { + return Objects.hashCode(IPAddress, broadcastURI, gateway, guestIPType, id, isDefault, isolationURI, netmask, macAddress, networkId, trafficType); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Network.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Network.java index b5bf126503..5f5faeac43 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Network.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Network.java @@ -28,6 +28,7 @@ import java.util.SortedSet; import javax.annotation.Nullable; import com.google.common.base.Joiner; +import com.google.common.base.Objects; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -552,166 +553,52 @@ public class Network implements Comparable { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((DNS1 == null) ? 0 : DNS1.hashCode()); - result = prime * result + ((DNS2 == null) ? 0 : DNS2.hashCode()); - result = prime * result + ((VLAN == null) ? 0 : VLAN.hashCode()); - result = prime * result + ((broadcastDomainType == null) ? 0 : broadcastDomainType.hashCode()); - result = prime * result + ((broadcastURI == null) ? 0 : broadcastURI.hashCode()); - result = prime * result + ((displayText == null) ? 0 : displayText.hashCode()); - result = prime * result + ((domain == null) ? 0 : domain.hashCode()); - result = prime * result + ((endIP == null) ? 0 : endIP.hashCode()); - result = prime * result + ((gateway == null) ? 0 : gateway.hashCode()); - result = prime * result + ((guestIPType == null) ? 0 : guestIPType.hashCode()); - result = prime * result + (int) (id ^ (id >>> 32)); - result = prime * result + (isDefault ? 1231 : 1237); - result = prime * result + (isShared ? 1231 : 1237); - result = prime * result + (isSystem ? 1231 : 1237); - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((netmask == null) ? 0 : netmask.hashCode()); - result = prime * result + ((networkDomain == null) ? 0 : networkDomain.hashCode()); - result = prime * result + ((networkOfferingAvailability == null) ? 0 : networkOfferingAvailability.hashCode()); - result = prime * result + ((networkOfferingDisplayText == null) ? 0 : networkOfferingDisplayText.hashCode()); - result = prime * result + (int) (networkOfferingId ^ (networkOfferingId >>> 32)); - result = prime * result + ((networkOfferingName == null) ? 0 : networkOfferingName.hashCode()); - result = prime * result + (int) (related ^ (related >>> 32)); - result = prime * result + ((services == null) ? 0 : services.hashCode()); - result = prime * result + ((startIP == null) ? 0 : startIP.hashCode()); - result = prime * result + ((state == null) ? 0 : state.hashCode()); - result = prime * result + ((trafficType == null) ? 0 : trafficType.hashCode()); - result = prime * result + (int) (zoneId ^ (zoneId >>> 32)); - result = prime * result + ((tags == null) ? 0 : tags.hashCode()); - result = prime * result + (int) (domainId ^ (domainId >>> 32)); - return result; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Network that = (Network) o; + + if (!Objects.equal(DNS1, that.DNS1)) return false; + if (!Objects.equal(DNS2, that.DNS2)) return false; + if (!Objects.equal(VLAN, that.VLAN)) return false; + if (!Objects.equal(broadcastDomainType, that.broadcastDomainType)) return false; + if (!Objects.equal(broadcastURI, that.broadcastURI)) return false; + if (!Objects.equal(displayText, that.displayText)) return false; + if (!Objects.equal(domain, that.domain)) return false; + if (!Objects.equal(endIP, that.endIP)) return false; + if (!Objects.equal(gateway, that.gateway)) return false; + if (!Objects.equal(guestIPType, that.guestIPType)) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(isDefault, that.isDefault)) return false; + if (!Objects.equal(isShared, that.isShared)) return false; + if (!Objects.equal(isSystem, that.isSystem)) return false; + if (!Objects.equal(name, that.name)) return false; + if (!Objects.equal(netmask, that.netmask)) return false; + if (!Objects.equal(networkDomain, that.networkDomain)) return false; + if (!Objects.equal(networkOfferingAvailability, that.networkOfferingAvailability)) return false; + if (!Objects.equal(networkOfferingDisplayText, that.networkOfferingDisplayText)) return false; + if (!Objects.equal(networkOfferingId, that.networkOfferingId)) return false; + if (!Objects.equal(networkOfferingName, that.networkOfferingName)) return false; + if (!Objects.equal(related, that.related)) return false; + if (!Objects.equal(services, that.services)) return false; + if (!Objects.equal(startIP, that.startIP)) return false; + if (!Objects.equal(state, that.state)) return false; + if (!Objects.equal(trafficType, that.trafficType)) return false; + if (!Objects.equal(zoneId, that.zoneId)) return false; + if (!Objects.equal(tags, that.tags)) return false; + if (!Objects.equal(domainId, that.domainId)) return false; + + return true; } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Network other = (Network) obj; - if (DNS1 == null) { - if (other.DNS1 != null) - return false; - } else if (!DNS1.equals(other.DNS1)) - return false; - if (DNS2 == null) { - if (other.DNS2 != null) - return false; - } else if (!DNS2.equals(other.DNS2)) - return false; - if (VLAN == null) { - if (other.VLAN != null) - return false; - } else if (!VLAN.equals(other.VLAN)) - return false; - if (broadcastDomainType == null) { - if (other.broadcastDomainType != null) - return false; - } else if (!broadcastDomainType.equals(other.broadcastDomainType)) - return false; - if (broadcastURI == null) { - if (other.broadcastURI != null) - return false; - } else if (!broadcastURI.equals(other.broadcastURI)) - return false; - if (displayText == null) { - if (other.displayText != null) - return false; - } else if (!displayText.equals(other.displayText)) - return false; - if (domain == null) { - if (other.domain != null) - return false; - } else if (!domain.equals(other.domain)) - return false; - if (endIP == null) { - if (other.endIP != null) - return false; - } else if (!endIP.equals(other.endIP)) - return false; - if (gateway == null) { - if (other.gateway != null) - return false; - } else if (!gateway.equals(other.gateway)) - return false; - if (guestIPType != other.guestIPType) - return false; - if (id != other.id) - return false; - if (isDefault != other.isDefault) - return false; - if (isShared != other.isShared) - return false; - if (isSystem != other.isSystem) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (netmask == null) { - if (other.netmask != null) - return false; - } else if (!netmask.equals(other.netmask)) - return false; - if (networkDomain == null) { - if (other.networkDomain != null) - return false; - } else if (!networkDomain.equals(other.networkDomain)) - return false; - if (networkOfferingAvailability == null) { - if (other.networkOfferingAvailability != null) - return false; - } else if (!networkOfferingAvailability.equals(other.networkOfferingAvailability)) - return false; - if (networkOfferingDisplayText == null) { - if (other.networkOfferingDisplayText != null) - return false; - } else if (!networkOfferingDisplayText.equals(other.networkOfferingDisplayText)) - return false; - if (networkOfferingId != other.networkOfferingId) - return false; - if (networkOfferingName == null) { - if (other.networkOfferingName != null) - return false; - } else if (!networkOfferingName.equals(other.networkOfferingName)) - return false; - if (related != other.related) - return false; - if (services == null) { - if (other.services != null) - return false; - } else if (!services.equals(other.services)) - return false; - if (startIP == null) { - if (other.startIP != null) - return false; - } else if (!startIP.equals(other.startIP)) - return false; - if (state == null) { - if (other.state != null) - return false; - } else if (!state.equals(other.state)) - return false; - if (trafficType != other.trafficType) - return false; - if (zoneId != other.zoneId) - return false; - if (tags == null) { - if (other.tags != null) - return false; - } else if (!tags.equals(other.tags)) - return false; - if (domainId != other.domainId) - return false; - return true; + public int hashCode() { + return Objects.hashCode(DNS1, DNS2, VLAN, broadcastDomainType, broadcastURI, displayText, domain, + endIP, gateway, guestIPType, id, isDefault, isShared, isSystem, name, + netmask, networkDomain, networkOfferingAvailability, networkOfferingDisplayText, + networkOfferingId, networkOfferingName, related, services, startIP, state, + trafficType, zoneId, tags, domainId); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkOffering.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkOffering.java index d074bd7bb3..cff69666f0 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkOffering.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkOffering.java @@ -26,6 +26,7 @@ import java.util.Set; import javax.annotation.Nullable; import com.google.common.base.Joiner; +import com.google.common.base.Objects; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableSet; import com.google.gson.annotations.SerializedName; @@ -266,70 +267,29 @@ public class NetworkOffering implements Comparable { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((availability == null) ? 0 : availability.hashCode()); - result = prime * result + ((created == null) ? 0 : created.hashCode()); - result = prime * result + ((displayText == null) ? 0 : displayText.hashCode()); - result = prime * result + (int) (id ^ (id >>> 32)); - result = prime * result + (isDefault ? 1231 : 1237); - result = prime * result + ((maxConnections == null) ? 0 : maxConnections.hashCode()); - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + (supportsVLAN ? 1231 : 1237); - result = prime * result + ((tags == null) ? 0 : tags.hashCode()); - result = prime * result + ((trafficType == null) ? 0 : trafficType.hashCode()); - return result; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + NetworkOffering that = (NetworkOffering) o; + + if (!Objects.equal(availability, that.availability)) return false; + if (!Objects.equal(created, that.created)) return false; + if (!Objects.equal(displayText, that.displayText)) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(isDefault, that.isDefault)) return false; + if (!Objects.equal(maxConnections, that.maxConnections)) return false; + if (!Objects.equal(name, that.name)) return false; + if (!Objects.equal(supportsVLAN, that.supportsVLAN)) return false; + if (!Objects.equal(tags, that.tags)) return false; + if (!Objects.equal(trafficType, that.trafficType)) return false; + + return true; } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - NetworkOffering other = (NetworkOffering) obj; - if (availability == null) { - if (other.availability != null) - return false; - } else if (!availability.equals(other.availability)) - return false; - if (created == null) { - if (other.created != null) - return false; - } else if (!created.equals(other.created)) - return false; - if (displayText == null) { - if (other.displayText != null) - return false; - } else if (!displayText.equals(other.displayText)) - return false; - if (id != other.id) - return false; - if (isDefault != other.isDefault) - return false; - if (maxConnections == null) { - if (other.maxConnections != null) - return false; - } else if (!maxConnections.equals(other.maxConnections)) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (supportsVLAN != other.supportsVLAN) - return false; - if (tags == null) { - if (other.tags != null) - return false; - } else if (!tags.equals(other.tags)) - return false; - if (trafficType != other.trafficType) - return false; - return true; + public int hashCode() { + return Objects.hashCode(availability, created, displayText, id, isDefault, maxConnections, name, supportsVLAN, tags, trafficType); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkService.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkService.java index 39c910507b..71a1db51d0 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkService.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkService.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.SortedSet; import java.util.Map.Entry; +import com.google.common.base.Objects; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedSet; @@ -51,34 +52,21 @@ public class NetworkService implements Comparable { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((value == null) ? 0 : value.hashCode()); - return result; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + NetworkService.Capability that = (NetworkService.Capability) o; + + if (!Objects.equal(name, that.name)) return false; + if (!Objects.equal(value, that.value)) return false; + + return true; } - + @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - NetworkService.Capability other = (NetworkService.Capability) obj; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (value == null) { - if (other.value != null) - return false; - } else if (!value.equals(other.value)) - return false; - return true; + public int hashCode() { + return Objects.hashCode(name, value); } @Override @@ -128,34 +116,21 @@ public class NetworkService implements Comparable { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((capabilities == null) ? 0 : capabilities.hashCode()); - result = prime * result + ((name == null) ? 0 : name.hashCode()); - return result; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + NetworkService that = (NetworkService) o; + + if (!Objects.equal(capabilities, that.capabilities)) return false; + if (!Objects.equal(name, that.name)) return false; + + return true; } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - NetworkService other = (NetworkService) obj; - if (capabilities == null) { - if (other.capabilities != null) - return false; - } else if (!capabilities.equals(other.capabilities)) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - return true; + public int hashCode() { + return Objects.hashCode(capabilities, name); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/OSType.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/OSType.java index 314e4d45c4..c5ae4399ee 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/OSType.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/OSType.java @@ -18,6 +18,7 @@ */ package org.jclouds.cloudstack.domain; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -93,34 +94,22 @@ public class OSType implements Comparable { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (int) (OSCategoryId ^ (OSCategoryId >>> 32)); - result = prime * result + ((description == null) ? 0 : description.hashCode()); - result = prime * result + (int) (id ^ (id >>> 32)); - return result; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + OSType that = (OSType) o; + + if (!Objects.equal(OSCategoryId, that.OSCategoryId)) return false; + if (!Objects.equal(description, that.description)) return false; + if (!Objects.equal(id, that.id)) return false; + + return true; } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - OSType other = (OSType) obj; - if (OSCategoryId != other.OSCategoryId) - return false; - if (description == null) { - if (other.description != null) - return false; - } else if (!description.equals(other.description)) - return false; - if (id != other.id) - return false; - return true; + public int hashCode() { + return Objects.hashCode(OSCategoryId, description, id); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Pod.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Pod.java index 2b46bbc8ac..67e76e1530 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Pod.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Pod.java @@ -18,6 +18,7 @@ */ package org.jclouds.cloudstack.domain; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -219,33 +220,24 @@ public class Pod implements Comparable { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - Pod pod = (Pod) o; + Pod that = (Pod) o; - if (id != pod.id) return false; - if (zoneId != pod.zoneId) return false; - if (allocationState != pod.allocationState) return false; - if (endIp != null ? !endIp.equals(pod.endIp) : pod.endIp != null) return false; - if (gateway != null ? !gateway.equals(pod.gateway) : pod.gateway != null) return false; - if (name != null ? !name.equals(pod.name) : pod.name != null) return false; - if (netmask != null ? !netmask.equals(pod.netmask) : pod.netmask != null) return false; - if (startIp != null ? !startIp.equals(pod.startIp) : pod.startIp != null) return false; - if (zoneName != null ? !zoneName.equals(pod.zoneName) : pod.zoneName != null) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(zoneId, that.zoneId)) return false; + if (!Objects.equal(allocationState, that.allocationState)) return false; + if (!Objects.equal(endIp, that.endIp)) return false; + if (!Objects.equal(gateway, that.gateway)) return false; + if (!Objects.equal(name, that.name)) return false; + if (!Objects.equal(netmask, that.netmask)) return false; + if (!Objects.equal(startIp, that.startIp)) return false; + if (!Objects.equal(zoneName, that.zoneName)) return false; return true; } @Override public int hashCode() { - int result = (int) (id ^ (id >>> 32)); - result = 31 * result + (name != null ? name.hashCode() : 0); - result = 31 * result + (int) (zoneId ^ (zoneId >>> 32)); - result = 31 * result + (zoneName != null ? zoneName.hashCode() : 0); - result = 31 * result + (gateway != null ? gateway.hashCode() : 0); - result = 31 * result + (netmask != null ? netmask.hashCode() : 0); - result = 31 * result + (startIp != null ? startIp.hashCode() : 0); - result = 31 * result + (endIp != null ? endIp.hashCode() : 0); - result = 31 * result + (allocationState != null ? allocationState.hashCode() : 0); - return result; + return Objects.hashCode(id, zoneId, allocationState, endIp, gateway, name, netmask, startIp, zoneName); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/PortForwardingRule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/PortForwardingRule.java index 67c8ddb8ce..644037d4ac 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/PortForwardingRule.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/PortForwardingRule.java @@ -21,6 +21,7 @@ package org.jclouds.cloudstack.domain; import java.util.Set; import com.google.common.base.CaseFormat; +import com.google.common.base.Objects; import com.google.common.collect.ImmutableSet; import com.google.gson.annotations.SerializedName; @@ -301,67 +302,29 @@ public class PortForwardingRule implements Comparable { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((IPAddress == null) ? 0 : IPAddress.hashCode()); - result = prime * result + (int) (IPAddressId ^ (IPAddressId >>> 32)); - result = prime * result + (int) (id ^ (id >>> 32)); - result = prime * result + privatePort; - result = prime * result + ((protocol == null) ? 0 : protocol.hashCode()); - result = prime * result + publicPort; - result = prime * result + ((state == null) ? 0 : state.hashCode()); - result = prime * result + ((virtualMachineDisplayName == null) ? 0 : virtualMachineDisplayName.hashCode()); - result = prime * result + (int) (virtualMachineId ^ (virtualMachineId >>> 32)); - result = prime * result + ((virtualMachineName == null) ? 0 : virtualMachineName.hashCode()); - return result; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PortForwardingRule that = (PortForwardingRule) o; + + if (!Objects.equal(IPAddress, that.IPAddress)) return false; + if (!Objects.equal(IPAddressId, that.IPAddressId)) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(privatePort, that.privatePort)) return false; + if (!Objects.equal(protocol, that.protocol)) return false; + if (!Objects.equal(publicPort, that.publicPort)) return false; + if (!Objects.equal(state, that.state)) return false; + if (!Objects.equal(virtualMachineDisplayName, that.virtualMachineDisplayName)) return false; + if (!Objects.equal(virtualMachineId, that.virtualMachineId)) return false; + if (!Objects.equal(virtualMachineName, that.virtualMachineName)) return false; + + return true; } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - PortForwardingRule other = (PortForwardingRule) obj; - if (IPAddress == null) { - if (other.IPAddress != null) - return false; - } else if (!IPAddress.equals(other.IPAddress)) - return false; - if (IPAddressId != other.IPAddressId) - return false; - if (id != other.id) - return false; - if (privatePort != other.privatePort) - return false; - if (protocol == null) { - if (other.protocol != null) - return false; - } else if (!protocol.equals(other.protocol)) - return false; - if (privatePort != other.privatePort) - return false; - if (state == null) { - if (other.state != null) - return false; - } else if (!state.equals(other.state)) - return false; - if (virtualMachineDisplayName == null) { - if (other.virtualMachineDisplayName != null) - return false; - } else if (!virtualMachineDisplayName.equals(other.virtualMachineDisplayName)) - return false; - if (virtualMachineId != other.virtualMachineId) - return false; - if (virtualMachineName == null) { - if (other.virtualMachineName != null) - return false; - } else if (!virtualMachineName.equals(other.virtualMachineName)) - return false; - return true; + public int hashCode() { + return Objects.hashCode(IPAddress, IPAddressId, id, privatePort, protocol, publicPort, state, virtualMachineDisplayName, virtualMachineId, virtualMachineName); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/PublicIPAddress.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/PublicIPAddress.java index 18d865a01c..55f29049be 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/PublicIPAddress.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/PublicIPAddress.java @@ -25,6 +25,7 @@ import java.util.Date; import javax.annotation.Nullable; import com.google.common.base.CaseFormat; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -425,117 +426,42 @@ public class PublicIPAddress implements Comparable { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((IPAddress == null) ? 0 : IPAddress.hashCode()); - result = prime * result + (int) (VLANId ^ (VLANId >>> 32)); - result = prime * result + ((VLANName == null) ? 0 : VLANName.hashCode()); - result = prime * result + ((account == null) ? 0 : account.hashCode()); - result = prime * result + ((allocated == null) ? 0 : allocated.hashCode()); - result = prime * result + (int) (associatedNetworkId ^ (associatedNetworkId >>> 32)); - result = prime * result + ((domain == null) ? 0 : domain.hashCode()); - result = prime * result + (int) (domainId ^ (domainId >>> 32)); - result = prime * result + (int) (id ^ (id >>> 32)); - result = prime * result + (isSourceNAT ? 1231 : 1237); - result = prime * result + (isStaticNAT ? 1231 : 1237); - result = prime * result + (int) (networkId ^ (networkId >>> 32)); - result = prime * result + ((state == null) ? 0 : state.hashCode()); - result = prime * result + (usesVirtualNetwork ? 1231 : 1237); - result = prime * result + ((virtualMachineDisplayName == null) ? 0 : virtualMachineDisplayName.hashCode()); - result = prime * result + (int) (virtualMachineId ^ (virtualMachineId >>> 32)); - result = prime * result + ((virtualMachineName == null) ? 0 : virtualMachineName.hashCode()); - result = prime * result + (int) (zoneId ^ (zoneId >>> 32)); - result = prime * result + ((zoneName == null) ? 0 : zoneName.hashCode()); - result = prime * result + ((jobStatus == null) ? 0 : jobStatus.hashCode()); - return result; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PublicIPAddress that = (PublicIPAddress) o; + + if (!Objects.equal(IPAddress, that.IPAddress)) return false; + if (!Objects.equal(VLANId, that.VLANId)) return false; + if (!Objects.equal(VLANName, that.VLANName)) return false; + if (!Objects.equal(account, that.account)) return false; + if (!Objects.equal(allocated, that.allocated)) return false; + if (!Objects.equal(associatedNetworkId, that.associatedNetworkId)) return false; + if (!Objects.equal(domain, that.domain)) return false; + if (!Objects.equal(domainId, that.domainId)) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(isSourceNAT, that.isSourceNAT)) return false; + if (!Objects.equal(isStaticNAT, that.isStaticNAT)) return false; + if (!Objects.equal(networkId, that.networkId)) return false; + if (!Objects.equal(state, that.state)) return false; + if (!Objects.equal(usesVirtualNetwork, that.usesVirtualNetwork)) return false; + if (!Objects.equal(virtualMachineDisplayName, that.virtualMachineDisplayName)) return false; + if (!Objects.equal(virtualMachineId, that.virtualMachineId)) return false; + if (!Objects.equal(virtualMachineName, that.virtualMachineName)) return false; + if (!Objects.equal(zoneId, that.zoneId)) return false; + if (!Objects.equal(zoneName, that.zoneName)) return false; + if (!Objects.equal(jobStatus, that.jobStatus)) return false; + + return true; } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - PublicIPAddress other = (PublicIPAddress) obj; - if (IPAddress == null) { - if (other.IPAddress != null) - return false; - } else if (!IPAddress.equals(other.IPAddress)) - return false; - if (VLANId != other.VLANId) - return false; - if (VLANName == null) { - if (other.VLANName != null) - return false; - } else if (!VLANName.equals(other.VLANName)) - return false; - if (account == null) { - if (other.account != null) - return false; - } else if (!account.equals(other.account)) - return false; - if (allocated == null) { - if (other.allocated != null) - return false; - } else if (!allocated.equals(other.allocated)) - return false; - if (associatedNetworkId != other.associatedNetworkId) - return false; - if (domain == null) { - if (other.domain != null) - return false; - } else if (!domain.equals(other.domain)) - return false; - if (domainId != other.domainId) - return false; - if (id != other.id) - return false; - if (isSourceNAT != other.isSourceNAT) - return false; - if (isStaticNAT != other.isStaticNAT) - return false; - if (networkId != other.networkId) - return false; - if (state == null) { - if (other.state != null) - return false; - } else if (!state.equals(other.state)) - return false; - if (usesVirtualNetwork != other.usesVirtualNetwork) - return false; - if (virtualMachineDisplayName == null) { - if (other.virtualMachineDisplayName != null) - return false; - } else if (!virtualMachineDisplayName.equals(other.virtualMachineDisplayName)) - return false; - if (virtualMachineId != other.virtualMachineId) - return false; - if (virtualMachineName == null) { - if (other.virtualMachineName != null) - return false; - } else if (!virtualMachineName.equals(other.virtualMachineName)) - return false; - if (zoneId != other.zoneId) - return false; - if (zoneName == null) { - if (other.zoneName != null) - return false; - } else if (!zoneName.equals(other.zoneName)) - return false; - if (jobId == null) { - if (other.jobId != null) - return false; - } else if (!jobId.equals(other.jobId)) - return false; - if (jobStatus == null) { - if (other.jobStatus != null) - return false; - } else if (!jobStatus.equals(other.jobStatus)) - return false; - return true; + public int hashCode() { + return Objects.hashCode(IPAddress, VLANId, VLANName, account, allocated, associatedNetworkId, + domain, domainId, id, isSourceNAT, isStaticNAT, networkId, state, + usesVirtualNetwork, virtualMachineDisplayName, virtualMachineId, + virtualMachineName, zoneId, zoneName, jobStatus); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ResourceLimit.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ResourceLimit.java index e10c840c3e..50aaa015a4 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ResourceLimit.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ResourceLimit.java @@ -24,6 +24,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.Map; import com.google.common.base.Function; +import com.google.common.base.Objects; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.gson.annotations.SerializedName; @@ -123,22 +124,18 @@ public class ResourceLimit implements Comparable { ResourceLimit that = (ResourceLimit) o; - if (domainId != that.domainId) return false; - if (max != that.max) return false; - if (resourceType != that.resourceType) return false; - if (!account.equals(that.account)) return false; - if (!domain.equals(that.domain)) return false; + if (!Objects.equal(domainId, that.domainId)) return false; + if (!Objects.equal(max, that.max)) return false; + if (!Objects.equal(resourceType, that.resourceType)) return false; + if (!Objects.equal(account, that.account)) return false; + if (!Objects.equal(domain, that.domain)) return false; return true; } @Override public int hashCode() { - int result = account.hashCode(); - result = 31 * result + domain.hashCode(); - result = 31 * result + (int) (domainId ^ (domainId >>> 32)); - result = 31 * result + max; - return result; + return Objects.hashCode(domainId, max, resourceType, account, domain); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SecurityGroup.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SecurityGroup.java index 4301f172ea..09a44d22d8 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SecurityGroup.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SecurityGroup.java @@ -25,6 +25,7 @@ import java.util.SortedSet; import javax.annotation.Nullable; +import com.google.common.base.Objects; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import com.google.gson.annotations.SerializedName; @@ -205,39 +206,22 @@ public class SecurityGroup implements Comparable { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (int) (domainId ^ (domainId >>> 32)); - result = prime * result + (int) (id ^ (id >>> 32)); - result = prime * result + ((jobStatus == null) ? 0 : jobStatus.hashCode()); - return result; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + SecurityGroup that = (SecurityGroup) o; + + if (!Objects.equal(domainId, that.domainId)) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(jobStatus, that.jobStatus)) return false; + + return true; } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - SecurityGroup other = (SecurityGroup) obj; - if (domainId != other.domainId) - return false; - if (id != other.id) - return false; - if (jobId == null) { - if (other.jobId != null) - return false; - } else if (!jobId.equals(other.jobId)) - return false; - if (jobStatus == null) { - if (other.jobStatus != null) - return false; - } else if (!jobStatus.equals(other.jobStatus)) - return false; - return true; + public int hashCode() { + return Objects.hashCode(domainId, id, jobStatus); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ServiceOffering.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ServiceOffering.java index 44f5f26462..06d1ebf459 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ServiceOffering.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ServiceOffering.java @@ -24,6 +24,7 @@ import java.util.Date; import java.util.Set; import com.google.common.base.Joiner; +import com.google.common.base.Objects; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableSet; import com.google.gson.annotations.SerializedName; @@ -341,73 +342,31 @@ public class ServiceOffering implements Comparable { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + cpuNumber; - result = prime * result + cpuSpeed; - result = prime * result + ((created == null) ? 0 : created.hashCode()); - result = prime * result + ((displayText == null) ? 0 : displayText.hashCode()); - result = prime * result + ((domain == null) ? 0 : domain.hashCode()); - result = prime * result + (int) (domainId ^ (domainId >>> 32)); - result = prime * result + (haSupport ? 1231 : 1237); - result = prime * result + (int) (id ^ (id >>> 32)); - result = prime * result + memory; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((storageType == null) ? 0 : storageType.hashCode()); - result = prime * result + ((tags == null) ? 0 : tags.hashCode()); - return result; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ServiceOffering that = (ServiceOffering) o; + + if (!Objects.equal(cpuNumber, that.cpuNumber)) return false; + if (!Objects.equal(cpuSpeed, that.cpuSpeed)) return false; + if (!Objects.equal(created, that.created)) return false; + if (!Objects.equal(displayText, that.displayText)) return false; + if (!Objects.equal(domain, that.domain)) return false; + if (!Objects.equal(domainId, that.domainId)) return false; + if (!Objects.equal(haSupport, that.haSupport)) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(memory, that.memory)) return false; + if (!Objects.equal(name, that.name)) return false; + if (!Objects.equal(storageType, that.storageType)) return false; + if (!Objects.equal(tags, that.tags)) return false; + + return true; } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ServiceOffering other = (ServiceOffering) obj; - if (cpuNumber != other.cpuNumber) - return false; - if (cpuSpeed != other.cpuSpeed) - return false; - if (created == null) { - if (other.created != null) - return false; - } else if (!created.equals(other.created)) - return false; - if (displayText == null) { - if (other.displayText != null) - return false; - } else if (!displayText.equals(other.displayText)) - return false; - if (domain == null) { - if (other.domain != null) - return false; - } else if (!domain.equals(other.domain)) - return false; - if (domainId != other.domainId) - return false; - if (haSupport != other.haSupport) - return false; - if (id != other.id) - return false; - if (memory != other.memory) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (storageType != other.storageType) - return false; - if (tags == null) { - if (other.tags != null) - return false; - } else if (!tags.equals(other.tags)) - return false; - return true; + public int hashCode() { + return Objects.hashCode(cpuNumber, cpuSpeed, created, displayText, domain, domainId, haSupport, id, memory, name, storageType, tags); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Snapshot.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Snapshot.java index feafd8f686..fbbb3fcfe1 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Snapshot.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Snapshot.java @@ -387,21 +387,9 @@ public class Snapshot implements Comparable { @Override public int hashCode() { - int result = (int) (id ^ (id >>> 32)); - result = 31 * result + (account != null ? account.hashCode() : 0); - result = 31 * result + (created != null ? created.hashCode() : 0); - result = 31 * result + (domain != null ? domain.hashCode() : 0); - result = 31 * result + (int) (domainId ^ (domainId >>> 32)); - result = 31 * result + (interval != null ? interval.hashCode() : 0); - result = 31 * result + (int) (jobId ^ (jobId >>> 32)); - result = 31 * result + (jobStatus != null ? jobStatus.hashCode() : 0); - result = 31 * result + (name != null ? name.hashCode() : 0); - result = 31 * result + (snapshotType != null ? snapshotType.hashCode() : 0); - result = 31 * result + (state != null ? state.hashCode() : 0); - result = 31 * result + (int) (volumeId ^ (volumeId >>> 32)); - result = 31 * result + (volumeName != null ? volumeName.hashCode() : 0); - result = 31 * result + (volumeType != null ? volumeType.hashCode() : 0); - return result; + return Objects.hashCode(domainId, id, jobId, volumeId, account, created, domain, + interval, jobStatus, name, snapshotType, state, volumeName, + volumeType); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SnapshotPolicy.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SnapshotPolicy.java index 96d645c3c2..44da162d04 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SnapshotPolicy.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SnapshotPolicy.java @@ -18,6 +18,7 @@ */ package org.jclouds.cloudstack.domain; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -165,25 +166,19 @@ public class SnapshotPolicy implements Comparable { SnapshotPolicy that = (SnapshotPolicy) o; - if (id != that.id) return false; - if (numberToRetain != that.numberToRetain) return false; - if (volumeId != that.volumeId) return false; - if (interval != that.interval) return false; - if (schedule != null ? !schedule.equals(that.schedule) : that.schedule != null) return false; - if (timezone != null ? !timezone.equals(that.timezone) : that.timezone != null) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(numberToRetain, that.numberToRetain)) return false; + if (!Objects.equal(volumeId, that.volumeId)) return false; + if (!Objects.equal(interval, that.interval)) return false; + if (!Objects.equal(schedule, that.schedule)) return false; + if (!Objects.equal(timezone, that.timezone)) return false; return true; } @Override public int hashCode() { - int result = (int) (id ^ (id >>> 32)); - result = 31 * result + (interval != null ? interval.hashCode() : 0); - result = 31 * result + (int) (numberToRetain ^ (numberToRetain >>> 32)); - result = 31 * result + (schedule != null ? schedule.hashCode() : 0); - result = 31 * result + (timezone != null ? timezone.hashCode() : 0); - result = 31 * result + (int) (volumeId ^ (volumeId >>> 32)); - return result; + return Objects.hashCode(id, numberToRetain, volumeId, interval, schedule, timezone); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SshKeyPair.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SshKeyPair.java index 134ab5468b..ef36ea3b77 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SshKeyPair.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SshKeyPair.java @@ -19,6 +19,7 @@ package org.jclouds.cloudstack.domain; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -85,42 +86,22 @@ public class SshKeyPair implements Comparable { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((fingerprint == null) ? 0 : fingerprint.hashCode()); - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((privateKey == null) ? 0 : privateKey.hashCode()); + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; - return result; + SshKeyPair that = (SshKeyPair) o; + + if (!Objects.equal(fingerprint, that.fingerprint)) return false; + if (!Objects.equal(name, that.name)) return false; + if (!Objects.equal(privateKey, that.privateKey)) return false; + + return true; } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - SshKeyPair other = (SshKeyPair) obj; - if (fingerprint == null) { - if (other.fingerprint != null) - return false; - } else if (!fingerprint.equals(other.fingerprint)) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (privateKey == null) { - if (other.privateKey != null) - return false; - } else if (!privateKey.equals(other.privateKey)) - return false; - - return true; + public int hashCode() { + return Objects.hashCode(fingerprint, name, privateKey); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/StoragePool.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/StoragePool.java index 70dbf8e68c..e8a05fdf59 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/StoragePool.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/StoragePool.java @@ -23,6 +23,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.Date; import com.google.common.base.CaseFormat; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -330,49 +331,33 @@ public class StoragePool implements Comparable { StoragePool that = (StoragePool) o; - if (clusterId != that.clusterId) return false; - if (diskSizeAllocated != that.diskSizeAllocated) return false; - if (diskSizeTotal != that.diskSizeTotal) return false; - if (id != that.id) return false; - if (podId != that.podId) return false; - if (zoneId != that.zoneId) return false; - if (clusterName != null ? !clusterName.equals(that.clusterName) : that.clusterName != null) return false; - if (created != null ? !created.equals(that.created) : that.created != null) return false; - if (ipAddress != null ? !ipAddress.equals(that.ipAddress) : that.ipAddress != null) return false; - if (jobId != null ? !jobId.equals(that.jobId) : that.jobId != null) return false; - if (jobStatus != null ? !jobStatus.equals(that.jobStatus) : that.jobStatus != null) return false; - if (name != null ? !name.equals(that.name) : that.name != null) return false; - if (path != null ? !path.equals(that.path) : that.path != null) return false; - if (podName != null ? !podName.equals(that.podName) : that.podName != null) return false; - if (state != that.state) return false; - if (tags != null ? !tags.equals(that.tags) : that.tags != null) return false; - if (type != that.type) return false; - if (zoneName != null ? !zoneName.equals(that.zoneName) : that.zoneName != null) return false; + if (!Objects.equal(clusterId, that.clusterId)) return false; + if (!Objects.equal(diskSizeAllocated, that.diskSizeAllocated)) return false; + if (!Objects.equal(diskSizeTotal, that.diskSizeTotal)) return false; + if (!Objects.equal(id, that.id)) return false; + if (!Objects.equal(podId, that.podId)) return false; + if (!Objects.equal(zoneId, that.zoneId)) return false; + if (!Objects.equal(clusterName, that.clusterName)) return false; + if (!Objects.equal(created, that.created)) return false; + if (!Objects.equal(ipAddress, that.ipAddress)) return false; + if (!Objects.equal(jobId, that.jobId)) return false; + if (!Objects.equal(jobStatus, that.jobStatus)) return false; + if (!Objects.equal(name, that.name)) return false; + if (!Objects.equal(path, that.path)) return false; + if (!Objects.equal(podName, that.podName)) return false; + if (!Objects.equal(state, that.state)) return false; + if (!Objects.equal(tags, that.tags)) return false; + if (!Objects.equal(type, that.type)) return false; + if (!Objects.equal(zoneName, that.zoneName)) return false; return true; } @Override public int hashCode() { - int result = (int) (id ^ (id >>> 32)); - result = 31 * result + (name != null ? name.hashCode() : 0); - result = 31 * result + (path != null ? path.hashCode() : 0); - result = 31 * result + (tags != null ? tags.hashCode() : 0); - result = 31 * result + (state != null ? state.hashCode() : 0); - result = 31 * result + (type != null ? type.hashCode() : 0); - result = 31 * result + (int) (zoneId ^ (zoneId >>> 32)); - result = 31 * result + (zoneName != null ? zoneName.hashCode() : 0); - result = 31 * result + (int) (podId ^ (podId >>> 32)); - result = 31 * result + (podName != null ? podName.hashCode() : 0); - result = 31 * result + (int) (clusterId ^ (clusterId >>> 32)); - result = 31 * result + (clusterName != null ? clusterName.hashCode() : 0); - result = 31 * result + (created != null ? created.hashCode() : 0); - result = 31 * result + (int) (diskSizeAllocated ^ (diskSizeAllocated >>> 32)); - result = 31 * result + (int) (diskSizeTotal ^ (diskSizeTotal >>> 32)); - result = 31 * result + (ipAddress != null ? ipAddress.hashCode() : 0); - result = 31 * result + (jobId != null ? jobId.hashCode() : 0); - result = 31 * result + (jobStatus != null ? jobStatus.hashCode() : 0); - return result; + return Objects.hashCode(clusterId, diskSizeAllocated, diskSizeTotal, id, podId, zoneId, + clusterName, created, ipAddress, jobId, jobStatus, name, path, + podName, state, tags, type, zoneName); } @Override diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Template.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Template.java index 68a9d84f03..a3b4ea9eeb 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Template.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Template.java @@ -24,6 +24,7 @@ import java.util.Date; import javax.annotation.Nullable; +import com.google.common.base.Objects; import com.google.gson.annotations.SerializedName; /** @@ -599,142 +600,56 @@ public class Template implements Comparable