From cea0e7d7ad0581cb404098b1480ce1473ecf695f Mon Sep 17 00:00:00 2001 From: David Santiago Date: Thu, 21 Apr 2011 04:03:58 -0500 Subject: [PATCH 01/12] Update blobstore2 to have a convenience protocol for building blob payloads. * Added PayloadSource protocol with a number of implementations, including all currently covered classes. * Added implementation for clojure.lang.IFn, so that you can pass in a closure. * Updated blob function to pass the :payload argument through the payload protocol function. * Added some tests for the payload protocol. --- .../main/clojure/org/jclouds/blobstore2.clj | 43 +++++++++++++++++-- .../clojure/org/jclouds/blobstore2_test.clj | 36 +++++++++++++++- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/blobstore/src/main/clojure/org/jclouds/blobstore2.clj b/blobstore/src/main/clojure/org/jclouds/blobstore2.clj index 7621f98334..f3190da05a 100644 --- a/blobstore/src/main/clojure/org/jclouds/blobstore2.clj +++ b/blobstore/src/main/clojure/org/jclouds/blobstore2.clj @@ -50,7 +50,7 @@ See http://code.google.com/p/jclouds for details." domain.Blob domain.internal.BlobBuilderImpl options.PutOptions options.PutOptions$Builder options.CreateContainerOptions options.ListContainerOptions] - org.jclouds.io.Payloads + [org.jclouds.io Payload Payloads payloads.StreamingPayload] java.util.Arrays [java.security DigestOutputStream MessageDigest] com.google.common.collect.ImmutableSet @@ -73,6 +73,39 @@ See http://code.google.com/p/jclouds for details." (catch Exception e (JCECrypto.)))) +;; +;; Payload support for creating Blobs. +;; + +(def ^{:doc "Type object for a Java primitive byte array, for use in the + PayloadSource protocol." + :private true} + byte-array-type (class (make-array Byte/TYPE 0))) + +(defprotocol PayloadSource + "Various types can have PayloadSource extended onto them so that they are + easily coerced into a Payload." + (^Payload payload [arg] "Coerce arg into a Payload.")) + +(extend-protocol PayloadSource + Payload + (payload [p] p) + java.io.InputStream + (payload [is] (Payloads/newInputStreamPayload is)) + byte-array-type + (payload [ba] (Payloads/newByteArrayPayload ba)) + String + (payload [s] (Payloads/newStringPayload s)) + java.io.File + (payload [f] (Payloads/newFilePayload f)) + clojure.lang.IFn + ;; This will let you pass a closure to payload that takes an OutputStream + ;; as argument and writes to it when called from a StreamingPayload. + (payload [func] + (StreamingPayload. (reify org.jclouds.io.WriteTo + (writeTo [this output-stream] + (func output-stream)))))) + (defn blobstore "Create a logged in context. Options for communication style @@ -278,7 +311,10 @@ Options can also be specified for extension modules (.countBlobs blobstore container-name)) (defn blob - "Create a new blob with the specified payload and options." + "Create a new blob with the specified payload and options. + + The payload argument can be anything accepted by org.jclouds.io.Payloads, or + you can make one manually (recommended) with the payload protocol function." ([^String name & {:keys [payload content-type content-length content-md5 calculate-md5 content-disposition content-encoding content-language metadata]}] @@ -286,7 +322,8 @@ Options can also be specified for extension modules (not (and (nil? payload) calculate-md5))]} (let [blob-builder (.name (BlobBuilderImpl. crypto-impl) name) blob-builder (if payload - (.payload blob-builder payload) + (.payload blob-builder + (org.jclouds.blobstore2/payload payload)) (.forSigning blob-builder)) blob-builder (if content-length ;; Special case, arg is prim. (.contentLength blob-builder content-length) diff --git a/blobstore/src/test/clojure/org/jclouds/blobstore2_test.clj b/blobstore/src/test/clojure/org/jclouds/blobstore2_test.clj index 02673198e7..1afa747e39 100644 --- a/blobstore/src/test/clojure/org/jclouds/blobstore2_test.clj +++ b/blobstore/src/test/clojure/org/jclouds/blobstore2_test.clj @@ -22,7 +22,8 @@ (:use [clojure.test]) (:import [org.jclouds.blobstore BlobStoreContextFactory] [org.jclouds.crypto CryptoStreams] - [java.io ByteArrayOutputStream] + [java.io ByteArrayInputStream ByteArrayOutputStream + StringBufferInputStream] [org.jclouds.util Strings2])) (defn clean-stub-fixture @@ -157,4 +158,37 @@ (is (= (seq (.. a-blob (getPayload) (getContentMetadata) (getContentMD5))) (seq (CryptoStreams/md5 (.getBytes "test-payload"))))))) +(deftest payload-protocol-test + (is (instance? org.jclouds.io.Payload (payload "test"))) + (is (blob "blob1" :payload (payload "blob1"))) + (is (create-container *blobstore* "container")) + (is (= "blob1" + (do + (put-blob *blobstore* "container" + (blob "blob1" + :payload "blob1")) + (Strings2/toStringAndClose (get-blob-stream *blobstore* + "container" "blob1"))))) + (is (= "blob1" + (do + (put-blob *blobstore* "container" + (blob "blob1" + :payload (StringBufferInputStream. "blob1"))) + (Strings2/toStringAndClose (get-blob-stream *blobstore* + "container" "blob1"))))) + (is (= "blob1" + (do + (put-blob *blobstore* "container" + (blob "blob1" + :payload (.getBytes "blob1"))) + (Strings2/toStringAndClose (get-blob-stream *blobstore* + "container" "blob1"))))) + (is (= "blob1" + (do + (put-blob *blobstore* "container" + (blob "blob1" + :payload #(.write % (.getBytes "blob1")))) + (Strings2/toStringAndClose (get-blob-stream *blobstore* + "container" "blob1")))))) + ;; TODO: more tests involving blob-specific functions From 6152de91c5b17dc866a14b389d3ad4440bbca3f9 Mon Sep 17 00:00:00 2001 From: David Santiago Date: Thu, 21 Apr 2011 11:32:42 -0500 Subject: [PATCH 02/12] Small enhancements to blobstore2_test. Made each test clause insert a uniquely named blob, to reduce risk that an earlier correct result masquerades as a success in a later clause. --- .../clojure/org/jclouds/blobstore2_test.clj | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/blobstore/src/test/clojure/org/jclouds/blobstore2_test.clj b/blobstore/src/test/clojure/org/jclouds/blobstore2_test.clj index 1afa747e39..b520995709 100644 --- a/blobstore/src/test/clojure/org/jclouds/blobstore2_test.clj +++ b/blobstore/src/test/clojure/org/jclouds/blobstore2_test.clj @@ -169,26 +169,26 @@ :payload "blob1")) (Strings2/toStringAndClose (get-blob-stream *blobstore* "container" "blob1"))))) - (is (= "blob1" + (is (= "blob2" (do (put-blob *blobstore* "container" - (blob "blob1" - :payload (StringBufferInputStream. "blob1"))) + (blob "blob2" + :payload (StringBufferInputStream. "blob2"))) (Strings2/toStringAndClose (get-blob-stream *blobstore* - "container" "blob1"))))) - (is (= "blob1" + "container" "blob2"))))) + (is (= "blob3" (do (put-blob *blobstore* "container" - (blob "blob1" - :payload (.getBytes "blob1"))) + (blob "blob3" + :payload (.getBytes "blob3"))) (Strings2/toStringAndClose (get-blob-stream *blobstore* - "container" "blob1"))))) - (is (= "blob1" + "container" "blob3"))))) + (is (= "blob4" (do (put-blob *blobstore* "container" - (blob "blob1" - :payload #(.write % (.getBytes "blob1")))) + (blob "blob4" + :payload #(.write % (.getBytes "blob4")))) (Strings2/toStringAndClose (get-blob-stream *blobstore* - "container" "blob1")))))) + "container" "blob4")))))) ;; TODO: more tests involving blob-specific functions From 29744fdddadd1fcb708584be449c20bd7b1db698 Mon Sep 17 00:00:00 2001 From: David Santiago Date: Thu, 21 Apr 2011 11:36:47 -0500 Subject: [PATCH 03/12] Clean up a comment on the blobstore2/blob function. --- blobstore/src/main/clojure/org/jclouds/blobstore2.clj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/blobstore/src/main/clojure/org/jclouds/blobstore2.clj b/blobstore/src/main/clojure/org/jclouds/blobstore2.clj index f3190da05a..af244898bf 100644 --- a/blobstore/src/main/clojure/org/jclouds/blobstore2.clj +++ b/blobstore/src/main/clojure/org/jclouds/blobstore2.clj @@ -313,8 +313,7 @@ Options can also be specified for extension modules (defn blob "Create a new blob with the specified payload and options. - The payload argument can be anything accepted by org.jclouds.io.Payloads, or - you can make one manually (recommended) with the payload protocol function." + The payload argument can be anything accepted by org.jclouds.io.Payloads." ([^String name & {:keys [payload content-type content-length content-md5 calculate-md5 content-disposition content-encoding content-language metadata]}] From 6a3ce93d26873da89b9547f1eeb4bf8a4fe60ef5 Mon Sep 17 00:00:00 2001 From: David Santiago Date: Thu, 21 Apr 2011 14:05:51 -0500 Subject: [PATCH 04/12] Clean up a comment on the blobstore2/blob function. For realsies. --- blobstore/src/main/clojure/org/jclouds/blobstore2.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blobstore/src/main/clojure/org/jclouds/blobstore2.clj b/blobstore/src/main/clojure/org/jclouds/blobstore2.clj index af244898bf..d98dc74ad8 100644 --- a/blobstore/src/main/clojure/org/jclouds/blobstore2.clj +++ b/blobstore/src/main/clojure/org/jclouds/blobstore2.clj @@ -313,7 +313,7 @@ Options can also be specified for extension modules (defn blob "Create a new blob with the specified payload and options. - The payload argument can be anything accepted by org.jclouds.io.Payloads." + The payload argument can be anything accepted by the PayloadSource protocol." ([^String name & {:keys [payload content-type content-length content-md5 calculate-md5 content-disposition content-encoding content-language metadata]}] From f0629cd72639586c53688923ac862508157949ef Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Sat, 23 Apr 2011 17:21:08 +0100 Subject: [PATCH 05/12] Merge branch 'master', remote branch 'origin' From 10aec3b6038b14b589355c2dc54de3be929620ef Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Sat, 23 Apr 2011 17:21:26 +0100 Subject: [PATCH 06/12] Changed project description to match extension -> driver change --- drivers/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pom.xml b/drivers/pom.xml index 977babb3a0..a624a8ceb6 100644 --- a/drivers/pom.xml +++ b/drivers/pom.xml @@ -30,7 +30,7 @@ jclouds-drivers-project pom - jclouds extensions project + jclouds drivers project gae apachehc From 4bcc6a753bb42e185e37d1676b1f435dcca1cd9b Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Sat, 23 Apr 2011 18:33:38 +0100 Subject: [PATCH 07/12] Changed Maven site URL to the jclouds Google Code site. Also using a fork of the WebDAV wagon to work around http://code.google.com/p/support/issues/detail?id=4786 --- project/pom.xml | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/project/pom.xml b/project/pom.xml index 3892467151..3a659cdbcc 100644 --- a/project/pom.xml +++ b/project/pom.xml @@ -100,9 +100,8 @@ https://oss.sonatype.org/content/repositories/snapshots - website - website - file://${basedir}/target/dist/site/jclouds-testing/ + jclouds-googlecode-site + dav+https://jclouds.googlecode.com/svn/maven-sites/${project.version} @@ -501,7 +500,17 @@ pageTracker._trackPageview(); maven-site-plugin - 2.2 + + ${project.distributionManagement.site.id} + ${project.distributionManagement.site.url} + + + + org.apache.maven.wagon + wagon-webdav-jackrabbit + 1.0-beta-8-WAGON-319 + + maven-deploy-plugin @@ -681,6 +690,16 @@ pageTracker._trackPageview(); ${basedir} + + + + + maven-site-plugin + 2.2 + + + + @@ -766,19 +785,6 @@ pageTracker._trackPageview(); - - - maven-site-plugin - - - attach-descriptor - - attach-descriptor - - - - - From 4e114e9b2b4a9cf91d18ac0c5fd18f36a2602ef8 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Sat, 23 Apr 2011 11:45:10 -0700 Subject: [PATCH 08/12] Issue 537: changed to bounded thread pool executor --- core/src/test/java/org/jclouds/PerformanceTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/test/java/org/jclouds/PerformanceTest.java b/core/src/test/java/org/jclouds/PerformanceTest.java index 067a7a4256..f9e8c506cf 100644 --- a/core/src/test/java/org/jclouds/PerformanceTest.java +++ b/core/src/test/java/org/jclouds/PerformanceTest.java @@ -25,8 +25,8 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import org.jclouds.concurrent.DynamicExecutors; import org.jclouds.date.DateService; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; @@ -44,7 +44,7 @@ public abstract class PerformanceTest { @BeforeTest public void setupExecutorService() { - exec = Executors.newCachedThreadPool(); + exec = DynamicExecutors.newScalingThreadPool(1, THREAD_COUNT, 1000); } @AfterTest From 5752cf5426679b9f55101cbe2ad615795afea366 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Sat, 23 Apr 2011 11:45:32 -0700 Subject: [PATCH 09/12] loosened up thresholds on syncproxytest --- .../concurrent/internal/SyncProxyTest.java | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java b/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java index c8771ab749..2e1f68171c 100644 --- a/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java +++ b/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java @@ -46,15 +46,15 @@ import com.google.inject.Provides; * * @author Adrian Cole */ -@Test(groups = "unit", sequential = true) +@Test(groups = "unit", singleThreaded = true) public class SyncProxyTest { @Test void testConvertNanos() { - assertEquals(SyncProxy.convertToNanos(Sync.class.getAnnotation(Timeout.class)), 30000000); + assertEquals(SyncProxy.convertToNanos(Sync.class.getAnnotation(Timeout.class)), 40000000); } - @Timeout(duration = 30, timeUnit = TimeUnit.MILLISECONDS) + @Timeout(duration = 40, timeUnit = TimeUnit.MILLISECONDS) private static interface Sync { String getString(); @@ -69,10 +69,10 @@ public class SyncProxyTest { String take20Milliseconds(); - String take100MillisecondsAndTimeout(); + String take200MillisecondsAndTimeout(); @Timeout(duration = 300, timeUnit = TimeUnit.MILLISECONDS) - String take100MillisecondsAndOverride(); + String take200MillisecondsAndOverride(); } @@ -137,12 +137,12 @@ public class SyncProxyTest { }), executorService); } - public ListenableFuture take100MillisecondsAndTimeout() { + public ListenableFuture take200MillisecondsAndTimeout() { return Futures.makeListenable(executorService.submit(new Callable() { public String call() { try { - Thread.sleep(100); + Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } @@ -152,8 +152,8 @@ public class SyncProxyTest { }), executorService); } - public ListenableFuture take100MillisecondsAndOverride() { - return take100MillisecondsAndTimeout(); + public ListenableFuture take200MillisecondsAndOverride() { + return take200MillisecondsAndTimeout(); } } @@ -184,14 +184,13 @@ public class SyncProxyTest { } @Test(expectedExceptions = RuntimeException.class) - public void testTake100MillisecondsAndTimeout() { - assertEquals(sync.take100MillisecondsAndTimeout(), "foo"); - + public void testTake200MillisecondsAndTimeout() { + assertEquals(sync.take200MillisecondsAndTimeout(), "foo"); } @Test - public void testTake100MillisecondsAndOverride() { - assertEquals(sync.take100MillisecondsAndOverride(), "foo"); + public void testTake200MillisecondsAndOverride() { + assertEquals(sync.take200MillisecondsAndOverride(), "foo"); } @Test From b37e00fffe3720569dfe45806b075d5591e23f75 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Sat, 23 Apr 2011 16:56:09 -0700 Subject: [PATCH 10/12] Issue 538: removed redundant http tests which are not working due to test classpath issues --- .../EnterpriseConfigurationModuleTest.java | 51 ------------------ .../enterprise/src/test/resources/test.jks | Bin 1365 -> 0 bytes 2 files changed, 51 deletions(-) delete mode 100644 drivers/enterprise/src/test/java/org/jclouds/enterprise/config/EnterpriseConfigurationModuleTest.java delete mode 100644 drivers/enterprise/src/test/resources/test.jks diff --git a/drivers/enterprise/src/test/java/org/jclouds/enterprise/config/EnterpriseConfigurationModuleTest.java b/drivers/enterprise/src/test/java/org/jclouds/enterprise/config/EnterpriseConfigurationModuleTest.java deleted file mode 100644 index 9711a76aa5..0000000000 --- a/drivers/enterprise/src/test/java/org/jclouds/enterprise/config/EnterpriseConfigurationModuleTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * - * Copyright (C) 2011 Cloud Conscious, LLC. - * - * ==================================================================== - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ==================================================================== - */ -package org.jclouds.enterprise.config; - -import static org.jclouds.Constants.PROPERTY_IO_WORKER_THREADS; -import static org.jclouds.Constants.PROPERTY_MAX_CONNECTIONS_PER_CONTEXT; -import static org.jclouds.Constants.PROPERTY_MAX_CONNECTIONS_PER_HOST; -import static org.jclouds.Constants.PROPERTY_USER_THREADS; - -import java.util.Properties; - -import org.jclouds.http.BaseHttpCommandExecutorServiceIntegrationTest; - -import com.google.inject.Module; - -/** - * Tests the functionality of the {@link EnterpriseConfigurationModule} - * - * @author Adrian Cole - */ -public class EnterpriseConfigurationModuleTest extends BaseHttpCommandExecutorServiceIntegrationTest { - - protected Module createConnectionModule() { - return new EnterpriseConfigurationModule(); - } - - protected void addConnectionProperties(Properties props) { - props.setProperty(PROPERTY_MAX_CONNECTIONS_PER_CONTEXT, 50 + ""); - props.setProperty(PROPERTY_MAX_CONNECTIONS_PER_HOST, 50 + ""); - // IO workers not used in this executor - props.setProperty(PROPERTY_IO_WORKER_THREADS, 0 + ""); - props.setProperty(PROPERTY_USER_THREADS, 5 + ""); - } - -} \ No newline at end of file diff --git a/drivers/enterprise/src/test/resources/test.jks b/drivers/enterprise/src/test/resources/test.jks deleted file mode 100644 index e641fb5470345f3ca35830613ed9b69262d216ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1365 zcmezO_TO6u1_mY|W&~sQtmK^h(v)H#U+ewrt4%<;T?S1|+YI>FxU|_ASs1mL1Q{7w z8CaT_7X4Ljus&bM7w`G3R_(3u29fEzncQv)@!bD*c;iN|OyTaCv^yWbc>Mtj+(M{S-Vu20q*N)zqn_q`-X2 z8hMY*DUKKKZLNRXqHy-o*+{QNUpNXEurK%ZdFeV;*;k{=(Y{epu43Yo>AdUzt`Yk? zXWzDY78{gIO1x&Us2P_5b0D|`Qf~~ z56`1Z+rKVJQfT4&_ObWmKFt{p2U|5~->!Pel3)0dTXjiMN}|#;Cl)piCOP2dj)x3phObz8#Z{0-6N8|8mZvNl!Z;0Jv1*nFTXqw zCc=Rs!j2)rg(1R?A;Jt7F_07IH8e1=G_Wu*H8nRei2`zs3@nT+p*%u-MYn|K*vQGYLixsM| z56?Rk;-2c;(0P8(SLZqLiyj1Nsm3XYJUO*wd&7(w)4sUeYm}a1$i&RZz=-TTU}P}^ z-6ayRd0+pHZ##sz^PHWVg{8N?YfD*p@=y6r^9{%Ex+Dc~UUQ0zvukGjy7Sfd#6#iI zw?k@n)x4ERs&1;<{#*P+>;39$0aNG2@=iMNX5GbaetBlU`EGu-Haf&mV=2AJ;OAY9 zE;II>@59yw-#@nW_=iOu3#_hod~mte^}Wok#o Date: Sat, 23 Apr 2011 16:56:34 -0700 Subject: [PATCH 11/12] added more tests to Throwables --- .../org/jclouds/concurrent/internal/SyncProxyTest.java | 2 ++ core/src/test/java/org/jclouds/util/Throwables2Test.java | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java b/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java index 2e1f68171c..ebdefbc122 100644 --- a/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java +++ b/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java @@ -164,6 +164,8 @@ public class SyncProxyTest { public void setUp() throws IllegalArgumentException, SecurityException, NoSuchMethodException { sync = SyncProxy.proxy(Sync.class, new SyncProxy(Sync.class, new Async(), new ConcurrentHashMap(), ImmutableMap., Class> of())); + // just to warm up + sync.string(); } @Test diff --git a/core/src/test/java/org/jclouds/util/Throwables2Test.java b/core/src/test/java/org/jclouds/util/Throwables2Test.java index 8e06f6ec03..4acd527ae2 100644 --- a/core/src/test/java/org/jclouds/util/Throwables2Test.java +++ b/core/src/test/java/org/jclouds/util/Throwables2Test.java @@ -24,6 +24,8 @@ import static org.jclouds.util.Throwables2.getFirstThrowableOfType; import static org.jclouds.util.Throwables2.returnFirstExceptionIfInListOrThrowStandardExceptionOrCause; import static org.testng.Assert.assertEquals; +import java.io.IOException; +import java.net.SocketException; import java.util.concurrent.TimeoutException; import org.jclouds.http.HttpCommand; @@ -51,6 +53,11 @@ public class Throwables2Test { assertEquals(getFirstThrowableOfType(pex, AuthorizationException.class), aex); } + public void testGetFirstThrowableOfTypeSubclass() { + SocketException aex = createMock(SocketException.class); + assertEquals(getFirstThrowableOfType(aex, IOException.class), aex); + } + public void testGetFirstThrowableOfTypeOuter() { AuthorizationException aex = createMock(AuthorizationException.class); assertEquals(getFirstThrowableOfType(aex, AuthorizationException.class), aex); From bd5da6104d270ed6a9fafc36bcfe6f2ef2f1e10c Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Sun, 24 Apr 2011 01:02:20 -0700 Subject: [PATCH 12/12] added account features to cloudstack --- .../cloudstack/CloudStackAsyncClient.java | 7 + .../jclouds/cloudstack/CloudStackClient.java | 7 + .../config/CloudStackRestClientModule.java | 105 +++ .../jclouds/cloudstack/domain/Account.java | 643 ++++++++++++++++++ .../jclouds/cloudstack/domain/Template.java | 18 - .../org/jclouds/cloudstack/domain/User.java | 336 +++++++++ .../features/AccountAsyncClient.java | 71 ++ .../cloudstack/features/AccountClient.java | 54 ++ .../options/ListAccountsOptions.java | 153 +++++ .../cloudstack/predicates/UserPredicates.java | 82 +++ .../cloudstack/CloudStackAsyncClientTest.java | 14 +- .../cloudstack/CloudStackClientLiveTest.java | 2 +- .../features/AccountAsyncClientTest.java | 101 +++ .../features/AccountClientLiveTest.java | 84 +++ .../features/AddressClientLiveTest.java | 5 +- .../features/AsyncJobClientLiveTest.java | 2 +- .../BaseCloudStackClientLiveTest.java | 15 +- .../features/ConfigurationClientLiveTest.java | 2 +- .../features/FirewallClientLiveTest.java | 2 +- .../features/GuestOSClientLiveTest.java | 2 +- .../features/HypervisorClientLiveTest.java | 2 +- .../features/LoadBalancerClientLiveTest.java | 2 +- .../features/NATClientLiveTest.java | 2 +- .../features/NetworkClientLiveTest.java | 2 +- .../features/OfferingClientLiveTest.java | 2 +- .../features/SecurityGroupClientLiveTest.java | 4 +- .../features/TemplateClientLiveTest.java | 2 +- .../VirtualMachineClientLiveTest.java | 34 +- .../features/ZoneClientLiveTest.java | 2 +- .../options/ListAccountsOptionsTest.java | 112 +++ 30 files changed, 1807 insertions(+), 62 deletions(-) create mode 100644 sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Account.java create mode 100644 sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/User.java create mode 100644 sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/AccountAsyncClient.java create mode 100644 sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/AccountClient.java create mode 100644 sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListAccountsOptions.java create mode 100644 sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/predicates/UserPredicates.java create mode 100644 sandbox-apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AccountAsyncClientTest.java create mode 100644 sandbox-apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AccountClientLiveTest.java create mode 100644 sandbox-apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/ListAccountsOptionsTest.java diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackAsyncClient.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackAsyncClient.java index 8c37b6a3e6..efd5c11d28 100644 --- a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackAsyncClient.java +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackAsyncClient.java @@ -18,6 +18,7 @@ */ package org.jclouds.cloudstack; +import org.jclouds.cloudstack.features.AccountAsyncClient; import org.jclouds.cloudstack.features.AddressAsyncClient; import org.jclouds.cloudstack.features.AsyncJobAsyncClient; import org.jclouds.cloudstack.features.ConfigurationAsyncClient; @@ -127,4 +128,10 @@ public interface CloudStackAsyncClient { */ @Delegate ConfigurationAsyncClient getConfigurationClient(); + + /** + * Provides asynchronous access to Account features. + */ + @Delegate + AccountAsyncClient getAccountClient(); } diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackClient.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackClient.java index 6d6e91fefa..51fe909a5e 100644 --- a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackClient.java +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackClient.java @@ -20,6 +20,7 @@ package org.jclouds.cloudstack; import java.util.concurrent.TimeUnit; +import org.jclouds.cloudstack.features.AccountClient; import org.jclouds.cloudstack.features.AddressClient; import org.jclouds.cloudstack.features.AsyncJobClient; import org.jclouds.cloudstack.features.ConfigurationClient; @@ -130,4 +131,10 @@ public interface CloudStackClient { */ @Delegate ConfigurationClient getConfigurationClient(); + + /** + * Provides synchronous access to Account features. + */ + @Delegate + AccountClient getAccountClient(); } diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java index 33f7d30657..1d0f993691 100644 --- a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java @@ -18,10 +18,19 @@ */ package org.jclouds.cloudstack.config; +import java.lang.reflect.Type; import java.util.Map; +import java.util.Set; + +import javax.inject.Singleton; import org.jclouds.cloudstack.CloudStackAsyncClient; import org.jclouds.cloudstack.CloudStackClient; +import org.jclouds.cloudstack.domain.Account; +import org.jclouds.cloudstack.domain.User; +import org.jclouds.cloudstack.domain.Account.State; +import org.jclouds.cloudstack.features.AccountAsyncClient; +import org.jclouds.cloudstack.features.AccountClient; import org.jclouds.cloudstack.features.AddressAsyncClient; import org.jclouds.cloudstack.features.AddressClient; import org.jclouds.cloudstack.features.AsyncJobAsyncClient; @@ -64,6 +73,14 @@ import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.config.RestClientModule; import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.google.gson.annotations.SerializedName; +import com.google.inject.TypeLiteral; /** * Configures the cloudstack connection. @@ -89,12 +106,98 @@ public class CloudStackRestClientModule extends RestClientModule, JsonDeserializer { + + public JsonElement serialize(Account src, Type typeOfSrc, JsonSerializationContext context) { + return context.serialize(src); + } + + public Account deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + return apply(context. deserialize(json, AccountInternal.class)); + } + + public Account apply(AccountInternal in) { + return Account.builder().id(in.id).type(in.type).domain(in.domain).domainId(in.domainId).IPsAvailable( + nullIfUnlimited(in.IPsAvailable)).IPLimit(nullIfUnlimited(in.IPLimit)).IPs(in.IPs).cleanupRequired( + in.cleanupRequired).name(in.name).receivedBytes(in.receivedBytes).sentBytes(in.sentBytes) + .snapshotsAvailable(nullIfUnlimited(in.snapshotsAvailable)).snapshotLimit( + nullIfUnlimited(in.snapshotLimit)).snapshots(in.snapshots).state(in.state) + .templatesAvailable(nullIfUnlimited(in.templatesAvailable)).templateLimit( + nullIfUnlimited(in.templateLimit)).templates(in.templates).VMsAvailable( + nullIfUnlimited(in.VMsAvailable)).VMLimit(nullIfUnlimited(in.VMLimit)).VMsRunning( + in.VMsRunning).VMsStopped(in.VMsStopped).VMs(in.VMs).volumesAvailable( + nullIfUnlimited(in.volumesAvailable)).volumeLimit(nullIfUnlimited(in.volumeLimit)).volumes( + in.volumes).users(in.users).build(); + } + + static final class AccountInternal { + private long id; + @SerializedName("accounttype") + private Account.Type type; + private String domain; + @SerializedName("domainid") + private long domainId; + @SerializedName("ipavailable") + private String IPsAvailable; + @SerializedName("iplimit") + private String IPLimit; + @SerializedName("iptotal") + private long IPs; + @SerializedName("iscleanuprequired") + private boolean cleanupRequired; + private String name; + @SerializedName("receivedbytes") + private long receivedBytes; + @SerializedName("sentBytes") + private long sentBytes; + @SerializedName("snapshotavailable") + private String snapshotsAvailable; + @SerializedName("snapshotlimit") + private String snapshotLimit; + @SerializedName("snapshottotal") + private long snapshots; + @SerializedName("state") + private State state; + @SerializedName("templateavailable") + private String templatesAvailable; + @SerializedName("templatelimit") + private String templateLimit; + @SerializedName("templatetotal") + private long templates; + @SerializedName("vmavailable") + private String VMsAvailable; + @SerializedName("vmlimit") + private String VMLimit; + @SerializedName("vmrunning") + private long VMsRunning; + @SerializedName("vmstopped") + private long VMsStopped; + @SerializedName("vmtotal") + private long VMs; + @SerializedName("volumeavailable") + private String volumesAvailable; + @SerializedName("volumelimit") + private String volumeLimit; + @SerializedName("volumetotal") + private long volumes; + @SerializedName("user") + private Set users; + } + + private static Long nullIfUnlimited(String in) { + return in == null || "Unlimited".equals(in) ? null : new Long(in); + } + } + @Override protected void configure() { bind(DateAdapter.class).to(Iso8601DateAdapter.class); @@ -106,6 +209,8 @@ public class CloudStackRestClientModule extends RestClientModule>() { + }).toInstance(ImmutableMap. of(Account.class, new BreakGenericSetAdapter())); super.configure(); } diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Account.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Account.java new file mode 100644 index 0000000000..5e3ab1aa29 --- /dev/null +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Account.java @@ -0,0 +1,643 @@ +/** + * + * Copyright (C) 2011 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ +package org.jclouds.cloudstack.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.base.CaseFormat; +import com.google.common.base.Function; +import com.google.common.collect.ForwardingSet; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; + +/** + * + * @author Adrian Cole + */ +public class Account extends ForwardingSet implements Comparable { + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private long id; + private Type type; + private String domain; + private long domainId; + private Long IPsAvailable; + private Long IPLimit; + private long IPs; + private boolean cleanupRequired; + private String name; + private long receivedBytes; + private long sentBytes; + private Long snapshotsAvailable; + private Long snapshotLimit; + private long snapshots; + private State state; + private Long templatesAvailable; + private Long templateLimit; + private long templates; + private Long VMsAvailable; + private Long VMLimit; + private long VMsRunning; + private long VMsStopped; + private long VMs; + private Long volumesAvailable; + private Long volumeLimit; + private long volumes; + private Set users = ImmutableSet.of(); + + public Builder id(long id) { + this.id = id; + return this; + } + + public Builder type(Type type) { + this.type = type; + return this; + } + + public Builder domain(String domain) { + this.domain = domain; + return this; + } + + public Builder domainId(long domainId) { + this.domainId = domainId; + return this; + } + + public Builder IPsAvailable(Long IPsAvailable) { + this.IPsAvailable = IPsAvailable; + return this; + } + + public Builder IPLimit(Long IPLimit) { + this.IPLimit = IPLimit; + return this; + } + + public Builder IPs(long IPs) { + this.IPs = IPs; + return this; + } + + public Builder cleanupRequired(boolean cleanupRequired) { + this.cleanupRequired = cleanupRequired; + return this; + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder receivedBytes(long receivedBytes) { + this.receivedBytes = receivedBytes; + return this; + } + + public Builder sentBytes(long sentBytes) { + this.sentBytes = sentBytes; + return this; + } + + public Builder snapshotsAvailable(Long snapshotsAvailable) { + this.snapshotsAvailable = snapshotsAvailable; + return this; + } + + public Builder snapshotLimit(Long snapshotLimit) { + this.snapshotLimit = snapshotLimit; + return this; + } + + public Builder snapshots(long snapshots) { + this.snapshots = snapshots; + return this; + } + + public Builder state(State state) { + this.state = state; + return this; + } + + public Builder templatesAvailable(Long templatesAvailable) { + this.templatesAvailable = templatesAvailable; + return this; + } + + public Builder templateLimit(Long templateLimit) { + this.templateLimit = templateLimit; + return this; + } + + public Builder templates(long templates) { + this.templates = templates; + return this; + } + + public Builder VMsAvailable(Long VMsAvailable) { + this.VMsAvailable = VMsAvailable; + return this; + } + + public Builder VMLimit(Long VMLimit) { + this.VMLimit = VMLimit; + return this; + } + + public Builder VMsRunning(long VMsRunning) { + this.VMsRunning = VMsRunning; + return this; + } + + public Builder VMsStopped(long VMsStopped) { + this.VMsStopped = VMsStopped; + return this; + } + + public Builder VMs(long VMs) { + this.VMs = VMs; + return this; + } + + public Builder volumesAvailable(Long volumesAvailable) { + this.volumesAvailable = volumesAvailable; + return this; + } + + public Builder volumeLimit(Long volumeLimit) { + this.volumeLimit = volumeLimit; + return this; + } + + public Builder volumes(long volumes) { + this.volumes = volumes; + return this; + } + + public Builder users(Set users) { + this.users = ImmutableSet.copyOf(checkNotNull(users, "users")); + return this; + } + + public Account build() { + return new Account(id, type, domain, domainId, IPsAvailable, IPLimit, IPs, cleanupRequired, name, + receivedBytes, sentBytes, snapshotsAvailable, snapshotLimit, snapshots, state, templatesAvailable, + templateLimit, templates, VMsAvailable, VMLimit, VMsRunning, VMsStopped, VMs, volumesAvailable, + volumeLimit, volumes, users); + } + + } + + public static enum State { + ENABLED, DISABLED, LOCKED, UNRECOGNIZED; + @Override + public String toString() { + return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, name()); + } + + public static State fromValue(String state) { + try { + return valueOf(CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(state, "state"))); + } catch (IllegalArgumentException e) { + return UNRECOGNIZED; + } + } + + } + + public static enum Type { + + /** + * full API access. This is typically a service administrator or code that executes with + * complete trust in the service operator's environment. + */ + ADMIN(1), + /** + * full API access within a domain. This is the most privileged user that a given customer + * has. This may be a reseller for the service provider. + */ + DOMAIN_ADMIN(2), + // TODO get code for read-only user. + // /** + // * API access limited to viewing most entities. No access is given to create or update those + // * entities. This may be useful for monitoring programs in the service operator's + // environment. + // */ + // READ_ONLY_ADMIN(?) + /** + * API access for all the resources associated with their account. There may be many users in + * a domain, many domains in a deployment, and many users in a deployment. This is typically + * the end user + */ + USER(0), UNRECOGNIZED(Integer.MAX_VALUE); + + private int code; + + private static final Map INDEX = Maps.uniqueIndex(ImmutableSet.copyOf(Type.values()), + new Function() { + + @Override + public Integer apply(Type input) { + return input.code; + } + + }); + + Type(int code) { + this.code = code; + } + + @Override + public String toString() { + return name(); + } + + public static Type fromValue(String type) { + Integer code = new Integer(checkNotNull(type, "type")); + return INDEX.containsKey(code) ? INDEX.get(code) : UNRECOGNIZED; + } + + } + + private long id; + private Type type; + private String domain; + private long domainId; + private Long IPsAvailable; + private Long IPLimit; + private long IPs; + private boolean cleanupRequired; + private String name; + private long receivedBytes; + private long sentBytes; + private Long snapshotsAvailable; + private Long snapshotLimit; + private long snapshots; + private State state; + private Long templatesAvailable; + private Long templateLimit; + private long templates; + private Long VMsAvailable; + private Long VMLimit; + private long VMsRunning; + private long VMsStopped; + private long VMs; + private Long volumesAvailable; + private Long volumeLimit; + private long volumes; + private Set users; + + public Account(long id, Type type, String domain, long domainId, Long IPsAvailable, Long IPLimit, long iPs, + boolean cleanupRequired, String name, long receivedBytes, long sentBytes, Long snapshotsAvailable, + Long snapshotLimit, long snapshots, org.jclouds.cloudstack.domain.Account.State state, + Long templatesAvailable, Long templateLimit, long templates, Long VMsAvailable, Long VMLimit, + long vMsRunning, long vMsStopped, long vMs, Long volumesAvailable, Long volumeLimit, long volumes, + Set users) { + this.id = id; + this.type = type; + this.domain = domain; + this.domainId = domainId; + this.IPsAvailable = IPsAvailable; + this.IPLimit = IPLimit; + this.IPs = iPs; + this.cleanupRequired = cleanupRequired; + this.name = name; + this.receivedBytes = receivedBytes; + this.sentBytes = sentBytes; + this.snapshotsAvailable = snapshotsAvailable; + this.snapshotLimit = snapshotLimit; + this.snapshots = snapshots; + this.state = state; + this.templatesAvailable = templatesAvailable; + this.templateLimit = templateLimit; + this.templates = templates; + this.VMsAvailable = VMsAvailable; + this.VMLimit = VMLimit; + this.VMsRunning = vMsRunning; + this.VMsStopped = vMsStopped; + this.VMs = vMs; + this.volumesAvailable = volumesAvailable; + this.volumeLimit = volumeLimit; + this.volumes = volumes; + this.users = ImmutableSet.copyOf(checkNotNull(users, "users")); + } + + /** + * present only for serializer + * + */ + Account() { + + } + + /** + * + * @return the id of the account + */ + public long getId() { + return id; + } + + /** + * + * @return the name of the account + */ + + public String getName() { + return name; + } + + /** + * + * @return account type (admin, domain-admin, user) + */ + public Type getType() { + return type; + } + + /** + * + * @return name of the Domain the account belongs to + */ + public String getDomain() { + return domain; + } + + /** + * + * @return id of the Domain the account belongs to + */ + public long getDomainId() { + return domainId; + } + + /** + * + * @return true if the account requires cleanup + */ + public boolean isCleanupRequired() { + return cleanupRequired; + } + + /** + * + * @return the list of users associated with account + */ + public Set getUsers() { + return users; + } + + /** + * + * @return the total number of public ip addresses available for this account to acquire, or null + * if unlimited + */ + @Nullable + public Long getIPsAvailable() { + return IPsAvailable; + } + + /** + * + * @return the total number of public ip addresses this account can acquire, or null if unlimited + */ + @Nullable + public Long getIPLimit() { + return IPLimit; + } + + /** + * + * @return the total number of public ip addresses allocated for this account + */ + public long getIPs() { + return IPs; + } + + /** + * + * @return the total number of network traffic bytes received + */ + public long getReceivedBytes() { + return receivedBytes; + } + + /** + * + * @return the total number of network traffic bytes sent + */ + public long getSentBytes() { + return sentBytes; + } + + /** + * + * @return the total number of snapshots available for this account, or null if unlimited + */ + @Nullable + public Long getSnapshotsAvailable() { + return snapshotsAvailable; + } + + /** + * + * @return the total number of snapshots which can be stored by this account, or null if + * unlimited + */ + @Nullable + public Long getSnapshotLimit() { + return snapshotLimit; + } + + /** + * + * @return the total number of snapshots stored by this account + */ + public long getSnapshots() { + return snapshots; + } + + /** + * + * @return the state of the account + */ + public State getState() { + return state; + } + + /** + * + * @return the total number of templates available to be created by this account, or null if + * unlimited + */ + @Nullable + public Long getTemplatesAvailable() { + return templatesAvailable; + } + + /** + * + * @return the total number of templates which can be created by this account, or null if + * unlimited + */ + @Nullable + public Long getTemplateLimit() { + return templateLimit; + } + + /** + * + * @return the total number of templates which have been created by this account + */ + public long getTemplates() { + return templates; + } + + /** + * + * @return the total number of virtual machines available for this account to acquire, or null if + * unlimited + */ + @Nullable + public Long getVMsAvailable() { + return VMsAvailable; + } + + /** + * + * @return the total number of virtual machines that can be deployed by this account, or null if + * unlimited + */ + @Nullable + public Long getVMLimit() { + return VMLimit; + } + + /** + * + * @return the total number of virtual machines running for this account + */ + public long getVMsRunning() { + return VMsRunning; + } + + /** + * + * @return the total number of virtual machines stopped for this account + */ + public long getVMsStopped() { + return VMsStopped; + } + + /** + * + * @return the total number of virtual machines deployed by this account + */ + public long getVMs() { + return VMs; + } + + /** + * + * @return the total volume available for this account, or null if unlimited + */ + @Nullable + public Long getVolumesAvailable() { + return volumesAvailable; + } + + /** + * + * @return the total volume which can be used by this account, or null if unlimited + */ + @Nullable + public Long getVolumeLimit() { + return volumeLimit; + } + + /** + * + * @return the total volume being used by this account + */ + public long getVolumes() { + return volumes; + } + + @Override + public int compareTo(Account arg0) { + return new Long(id).compareTo(arg0.getId()); + } + + @Override + public String toString() { + return String + .format( + "[id=%s, name=%s, type=%s, state=%s, domain=%s, domainId=%s, cleanupRequired=%s, sentBytes=%s, receivedBytes=%s, IPs=%s, IPsAvailable=%s, IPLimit=%s, VMs=%s, VMsAvailable=%s, VMsRunning=%s, VMsStopped=%s, VMLimit=%s, snapshots=%s, snapshotLimit=%s, snapshotsAvailable=%s, templateLimit=%s, templates=%s, templatesAvailable=%s, volumes=%s, volumeLimit=%s, volumesAvailable=%s, users=%s]", + id, name, type, state, domain, domainId, cleanupRequired, sentBytes, receivedBytes, IPs, + IPsAvailable, IPLimit, VMs, VMsAvailable, VMsRunning, VMsStopped, VMLimit, snapshots, + snapshotLimit, snapshotsAvailable, templateLimit, templates, templatesAvailable, volumes, + volumeLimit, volumesAvailable, users); + } + + @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; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + 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; + return true; + } + + @Override + protected Set delegate() { + return users; + } +} diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Template.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Template.java index 834294f948..719052852a 100644 --- a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Template.java +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Template.java @@ -37,24 +37,16 @@ public class Template implements Comparable