You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by na...@apache.org on 2016/01/19 12:04:57 UTC
[1/2] jclouds git commit: JCLOUDS-512: Implement the ImageCache
Repository: jclouds
Updated Branches:
refs/heads/master 0f6ab3944 -> 40f31786c
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/core/src/test/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest.java b/core/src/test/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest.java
index 868e726..8b5a954 100644
--- a/core/src/test/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest.java
+++ b/core/src/test/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest.java
@@ -21,22 +21,26 @@ import static com.google.common.util.concurrent.Atomics.newReference;
import static org.testng.Assert.assertEquals;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.SetAndThrowAuthorizationExceptionSupplierBackedLoader;
import org.testng.annotations.Test;
+import com.google.common.base.Optional;
import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
import com.google.common.util.concurrent.UncheckedExecutionException;
+import com.google.common.util.concurrent.Uninterruptibles;
@Test(groups = "unit", testName = "MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest")
public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest {
@Test
public void testLoaderNormal() {
AtomicReference<AuthorizationException> authException = newReference();
- assertEquals(new SetAndThrowAuthorizationExceptionSupplierBackedLoader<String>(ofInstance("foo"),
- authException).load("KEY").get(), "foo");
+ assertEquals(new SetAndThrowAuthorizationExceptionSupplierBackedLoader<String>(ofInstance("foo"), authException, new ValueLoadedCallback.NoOpCallback<String>()).load("KEY").get(), "foo");
assertEquals(authException.get(), null);
}
@@ -48,7 +52,7 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest {
public String get() {
throw new AuthorizationException();
}
- }, authException).load("KEY");
+ }, authException, new ValueLoadedCallback.NoOpCallback<String>()).load("KEY");
} finally {
assertEquals(authException.get().getClass(), AuthorizationException.class);
}
@@ -62,7 +66,7 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest {
public String get() {
throw new RuntimeException(new ExecutionException(new AuthorizationException()));
}
- }, authException).load("KEY");
+ }, authException, new ValueLoadedCallback.NoOpCallback<String>()).load("KEY");
} finally {
assertEquals(authException.get().getClass(), AuthorizationException.class);
}
@@ -76,7 +80,7 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest {
public String get() {
throw new UncheckedExecutionException(new AuthorizationException());
}
- }, authException).load("KEY");
+ }, authException, new ValueLoadedCallback.NoOpCallback<String>()).load("KEY");
} finally {
assertEquals(authException.get().getClass(), AuthorizationException.class);
}
@@ -90,9 +94,43 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest {
public String get() {
throw new RuntimeException(new IllegalArgumentException("foo"));
}
- }, authException).load("KEY");
+ }, authException, new ValueLoadedCallback.NoOpCallback<String>()).load("KEY");
} finally {
assertEquals(authException.get().getClass(), RuntimeException.class);
}
}
+
+ @Test
+ public void testLoaderNotifiesAfterReloading() {
+ AtomicReference<AuthorizationException> authException = newReference();
+ ValueLoadedEventHandler handler = new ValueLoadedEventHandler();
+
+ Supplier<String> supplier = MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException,
+ Suppliers.<String> ofInstance("foo"), 3, TimeUnit.SECONDS, handler);
+
+ // The supplier loads the value initially and returns the cached values
+ assertEquals(handler.count.get(), 0);
+ supplier.get();
+ assertEquals(handler.count.get(), 1);
+ supplier.get();
+ assertEquals(handler.count.get(), 1);
+
+ // Once expired, it reloads the values, notified the event, and updated the cache
+ Uninterruptibles.sleepUninterruptibly(4, TimeUnit.SECONDS);
+ supplier.get();
+ assertEquals(handler.count.get(), 2);
+ supplier.get();
+ assertEquals(handler.count.get(), 2);
+ supplier.get();
+ assertEquals(handler.count.get(), 2);
+ }
+
+ static class ValueLoadedEventHandler implements ValueLoadedCallback<String>{
+ AtomicInteger count = new AtomicInteger(0);
+
+ @Override
+ public void valueLoaded(Optional<String> value) {
+ count.incrementAndGet();
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderImpl.java
----------------------------------------------------------------------
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderImpl.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderImpl.java
index 160c37f..aca7141 100644
--- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderImpl.java
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderImpl.java
@@ -27,8 +27,6 @@ import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.options.TemplateOptions;
-import org.jclouds.compute.strategy.GetImageStrategy;
-import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.domain.Location;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl;
@@ -40,11 +38,11 @@ public class AWSEC2TemplateBuilderImpl extends EC2TemplateBuilderImpl {
@Inject
protected AWSEC2TemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations,
- ImageCacheSupplier images, @Memoized Supplier<Set<? extends Hardware>> sizes,
+ @Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> sizes,
Supplier<Location> defaultLocation, @Named("DEFAULT") Provider<TemplateOptions> optionsProvider,
- @Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider, GetImageStrategy getImageStrategy,
+ @Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider,
Supplier<LoadingCache<RegionAndName, ? extends Image>> imageMap) {
- super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider, getImageStrategy, imageMap);
+ super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider, imageMap);
}
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java
----------------------------------------------------------------------
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 4d3d6c9..e1a184f 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
@@ -16,14 +16,11 @@
*/
package org.jclouds.aws.ec2.compute.config;
-import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
-
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicReference;
-import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.aws.ec2.compute.AWSEC2TemplateBuilderImpl;
@@ -38,8 +35,6 @@ import org.jclouds.aws.ec2.compute.strategy.CreateKeyPairPlacementAndSecurityGro
import org.jclouds.aws.ec2.compute.suppliers.AWSEC2HardwareSupplier;
import org.jclouds.compute.config.BaseComputeServiceContextModule;
import org.jclouds.compute.domain.Image;
-import org.jclouds.compute.extensions.ImageExtension;
-import org.jclouds.compute.extensions.SecurityGroupExtension;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.ec2.compute.config.EC2BindComputeStrategiesByClass;
import org.jclouds.ec2.compute.domain.RegionAndName;
@@ -59,7 +54,6 @@ 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;
@@ -107,9 +101,8 @@ public class AWSEC2ComputeServiceContextModule extends BaseComputeServiceContext
// duplicates EC2ComputeServiceContextModule; but that's easiest thing to do with guice; could extract to common util
// TODO: have a another look at this (Adrian)
@Override
- protected Supplier<Set<? extends Image>> supplyNonParsingImageCache(
- AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
- final Supplier<Set<? extends Image>> imageSupplier, Injector injector) {
+ protected Supplier<Set<? extends Image>> supplyNonParsingImages(final Supplier<Set<? extends Image>> imageSupplier,
+ Injector injector) {
final Supplier<LoadingCache<RegionAndName, ? extends Image>> cache = injector.getInstance(Key
.get(new TypeLiteral<Supplier<LoadingCache<RegionAndName, ? extends Image>>>() {
}));
@@ -170,14 +163,4 @@ 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<ImageExtension> provideImageExtension(Injector i) {
- return Optional.of(i.getInstance(ImageExtension.class));
- }
-
- @Override
- protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) {
- return Optional.of(i.getInstance(SecurityGroupExtension.class));
- }
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2ImageExtensionLiveTest.java
----------------------------------------------------------------------
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2ImageExtensionLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2ImageExtensionLiveTest.java
index ef0d539..2f6b962 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2ImageExtensionLiveTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/extensions/AWSEC2ImageExtensionLiveTest.java
@@ -21,7 +21,8 @@ import static com.google.common.collect.Iterables.transform;
import org.jclouds.aws.ec2.AWSEC2Api;
import org.jclouds.aws.util.AWSUtils;
import org.jclouds.compute.domain.Image;
-import org.jclouds.compute.extensions.internal.BaseImageExtensionLiveTest;
+import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.ec2.compute.extensions.EC2ImageExtensionLiveTest;
import org.jclouds.ec2.compute.functions.EC2ImageParser;
import org.jclouds.ec2.options.DescribeImagesOptions;
import org.jclouds.sshj.config.SshjSshClientModule;
@@ -33,7 +34,7 @@ import com.google.inject.Module;
* Live test for aws-ec2 {@link ImageExtension} implementation
*/
@Test(groups = "live", singleThreaded = true, testName = "AWSEC2ImageExtensionLiveTest")
-public class AWSEC2ImageExtensionLiveTest extends BaseImageExtensionLiveTest {
+public class AWSEC2ImageExtensionLiveTest extends EC2ImageExtensionLiveTest {
public AWSEC2ImageExtensionLiveTest() {
provider = "aws-ec2";
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java
----------------------------------------------------------------------
diff --git a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java
index 4951b05..baaec3f 100644
--- a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java
+++ b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java
@@ -25,26 +25,14 @@ import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineProperti
import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
import static org.jclouds.util.Predicates2.retry;
-import javax.inject.Named;
-import javax.inject.Singleton;
import java.net.URI;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
-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.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-import com.google.common.collect.ImmutableMap;
-import com.google.inject.Injector;
-import com.google.inject.Provides;
-import com.google.inject.Scopes;
-import com.google.inject.TypeLiteral;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
import org.jclouds.collect.Memoized;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceAdapter;
@@ -53,8 +41,6 @@ import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.OperatingSystem;
import org.jclouds.compute.domain.OsFamily;
-import org.jclouds.compute.extensions.ImageExtension;
-import org.jclouds.compute.extensions.SecurityGroupExtension;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.domain.Location;
import org.jclouds.domain.LoginCredentials;
@@ -80,6 +66,19 @@ import org.jclouds.googlecomputeengine.domain.Operation;
import org.jclouds.location.suppliers.ImplicitLocationSupplier;
import org.jclouds.location.suppliers.implicit.FirstZone;
+import com.google.common.base.Function;
+import com.google.common.base.Functions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Injector;
+import com.google.inject.Provides;
+import com.google.inject.Scopes;
+import com.google.inject.TypeLiteral;
+
public final class GoogleComputeEngineServiceContextModule
extends ComputeServiceAdapterContextModule<Instance, MachineType, Image, Location> {
@@ -197,15 +196,6 @@ public final class GoogleComputeEngineServiceContextModule
return CacheBuilder.newBuilder().build(in);
}
-
- @Override protected Optional<ImageExtension> provideImageExtension(Injector i) {
- return Optional.absent();
- }
-
- @Override protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) {
- return Optional.absent();
- }
-
private static final Map<Instance.Status, NodeMetadata.Status> toPortableNodeStatus =
ImmutableMap.<Instance.Status, NodeMetadata.Status>builder()
.put(Instance.Status.PROVISIONING, NodeMetadata.Status.PENDING)
[2/2] jclouds git commit: JCLOUDS-512: Implement the ImageCache
Posted by na...@apache.org.
JCLOUDS-512: Implement the ImageCache
This commit refactors the ImageCacheSupplier to act as a
proper cache. It is used by the ImageExtesion and all operations
on the images are propagated to the cache.
A method has also been added to the TemplateBuilder to let users
force a cache refresh. There have been several requests to provide a way
to disable image caching in the compute abstraction, and this new method
should fix that.
Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/40f31786
Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/40f31786
Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/40f31786
Branch: refs/heads/master
Commit: 40f31786c59e10f8ea74c2bf4463954361d5f68e
Parents: 0f6ab39
Author: Ignasi Barrera <na...@apache.org>
Authored: Thu Oct 22 09:38:14 2015 +0200
Committer: Ignasi Barrera <na...@apache.org>
Committed: Tue Jan 19 11:25:09 2016 +0100
----------------------------------------------------------------------
.../CloudStackComputeServiceContextModule.java | 11 --
.../config/EC2ComputeServiceContextModule.java | 19 +-
.../internal/EC2TemplateBuilderImpl.java | 8 +-
.../ec2/compute/EC2TemplateBuilderTest.java | 8 +-
.../extensions/EC2ImageExtensionLiveTest.java | 48 ++++-
.../internal/EC2TemplateBuilderImplTest.java | 9 +-
.../config/NovaComputeServiceContextModule.java | 11 --
.../config/BaseComputeServiceContextModule.java | 50 ++----
.../jclouds/compute/domain/TemplateBuilder.java | 16 +-
.../compute/domain/TemplateBuilderSpec.java | 25 ++-
.../domain/internal/TemplateBuilderImpl.java | 81 +++------
.../internal/DelegatingImageExtension.java | 74 ++++++++
.../compute/internal/BaseComputeService.java | 9 +-
.../config/StubComputeServiceContextModule.java | 9 -
.../compute/suppliers/ImageCacheSupplier.java | 175 +++++++++++++++----
.../compute/domain/TemplateBuilderSpecTest.java | 51 +++++-
.../internal/TemplateBuilderImplTest.java | 9 +-
.../internal/BaseImageExtensionLiveTest.java | 45 +++--
.../suppliers/ImageCacheSupplierTest.java | 79 ++++++++-
...tButNotOnAuthorizationExceptionSupplier.java | 45 ++++-
.../rest/suppliers/ValueLoadedCallback.java | 37 ++++
...NotOnAuthorizationExceptionSupplierTest.java | 50 +++++-
.../ec2/compute/AWSEC2TemplateBuilderImpl.java | 8 +-
.../AWSEC2ComputeServiceContextModule.java | 21 +--
.../AWSEC2ImageExtensionLiveTest.java | 5 +-
...GoogleComputeEngineServiceContextModule.java | 42 ++---
26 files changed, 677 insertions(+), 268 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java
index 7ef7805..f46937d 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java
@@ -89,7 +89,6 @@ import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExc
import com.google.common.base.Function;
import com.google.common.base.Objects;
-import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.cache.CacheBuilder;
@@ -277,14 +276,4 @@ public class CloudStackComputeServiceContextModule extends
NetworkType.ADVANCED, new AdvancedNetworkOptionsConverter(),
NetworkType.BASIC, new BasicNetworkOptionsConverter());
}
-
- @Override
- protected Optional<ImageExtension> provideImageExtension(Injector i) {
- return Optional.of(i.getInstance(ImageExtension.class));
- }
-
- @Override
- protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) {
- return Optional.of(i.getInstance(SecurityGroupExtension.class));
- }
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java
----------------------------------------------------------------------
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 97eca73..ce889a3 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
@@ -17,7 +17,6 @@
package org.jclouds.ec2.compute.config;
import static com.google.common.collect.Iterables.toArray;
-import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS;
import java.util.Set;
@@ -30,8 +29,6 @@ import javax.inject.Singleton;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.config.BaseComputeServiceContextModule;
import org.jclouds.compute.domain.Image;
-import org.jclouds.compute.extensions.ImageExtension;
-import org.jclouds.compute.extensions.SecurityGroupExtension;
import org.jclouds.ec2.compute.EC2ComputeService;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.loaders.RegionAndIdToImage;
@@ -39,7 +36,6 @@ 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;
@@ -78,9 +74,8 @@ public class EC2ComputeServiceContextModule extends BaseComputeServiceContextMod
}
@Override
- protected Supplier<Set<? extends Image>> supplyNonParsingImageCache(
- AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
- final Supplier<Set<? extends Image>> imageSupplier, Injector injector) {
+ protected Supplier<Set<? extends Image>> supplyNonParsingImages(final Supplier<Set<? extends Image>> imageSupplier,
+ Injector injector) {
final Supplier<LoadingCache<RegionAndName, ? extends Image>> cache = injector.getInstance(Key.get(new TypeLiteral<Supplier<LoadingCache<RegionAndName, ? extends Image>>>() {}));
return new Supplier<Set<? extends Image>>() {
@Override
@@ -135,15 +130,5 @@ public class EC2ComputeServiceContextModule extends BaseComputeServiceContextMod
return new String[] {};
return toArray(Splitter.on(',').split(amiOwners), String.class);
}
-
- @Override
- protected Optional<ImageExtension> provideImageExtension(Injector i) {
- return Optional.of(i.getInstance(ImageExtension.class));
- }
-
- @Override
- protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) {
- return Optional.of(i.getInstance(SecurityGroupExtension.class));
- }
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/apis/ec2/src/main/java/org/jclouds/ec2/compute/internal/EC2TemplateBuilderImpl.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/internal/EC2TemplateBuilderImpl.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/internal/EC2TemplateBuilderImpl.java
index 180bd62..ea76c03 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/internal/EC2TemplateBuilderImpl.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/internal/EC2TemplateBuilderImpl.java
@@ -32,8 +32,6 @@ import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.domain.internal.TemplateBuilderImpl;
import org.jclouds.compute.options.TemplateOptions;
-import org.jclouds.compute.strategy.GetImageStrategy;
-import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.domain.Location;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.util.Throwables2;
@@ -50,11 +48,11 @@ public class EC2TemplateBuilderImpl extends TemplateBuilderImpl {
@Inject
protected EC2TemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations,
- ImageCacheSupplier images, @Memoized Supplier<Set<? extends Hardware>> sizes,
+ @Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> sizes,
Supplier<Location> defaultLocation, @Named("DEFAULT") Provider<TemplateOptions> optionsProvider,
- @Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider, GetImageStrategy getImageStrategy,
+ @Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider,
Supplier<LoadingCache<RegionAndName, ? extends Image>> imageMap) {
- super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider, getImageStrategy);
+ super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider);
this.lazyImageCache = imageMap;
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2TemplateBuilderTest.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2TemplateBuilderTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2TemplateBuilderTest.java
index 2522a1f..8f752eb 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2TemplateBuilderTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2TemplateBuilderTest.java
@@ -58,6 +58,7 @@ import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.functions.ImagesToRegionAndIdMap;
import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl;
import org.jclouds.ec2.domain.VirtualizationType;
+import org.jclouds.rest.AuthorizationException;
import org.testng.annotations.Test;
import com.google.common.base.Function;
@@ -69,6 +70,8 @@ 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.common.util.concurrent.Atomics;
+import com.google.inject.util.Providers;
/**
* Tests compute service specifically to EC2.
@@ -236,8 +239,9 @@ public class EC2TemplateBuilderTest {
m1_small().build(), m1_xlarge().build(), m2_xlarge().build(), m2_2xlarge().build(),
m2_4xlarge().build(), g2_2xlarge().build(), HARDWARE_SUPPORTING_BOGUS));
- return new EC2TemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60), sizes, Suppliers.ofInstance(location), optionsProvider,
- templateBuilderProvider, getImageStrategy, imageCache) {
+ return new EC2TemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60,
+ Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy)), sizes,
+ Suppliers.ofInstance(location), optionsProvider, templateBuilderProvider, imageCache) {
};
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/apis/ec2/src/test/java/org/jclouds/ec2/compute/extensions/EC2ImageExtensionLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/extensions/EC2ImageExtensionLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/extensions/EC2ImageExtensionLiveTest.java
index 3be04db..be8abb6 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/extensions/EC2ImageExtensionLiveTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/extensions/EC2ImageExtensionLiveTest.java
@@ -17,16 +17,31 @@
package org.jclouds.ec2.compute.extensions;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import java.lang.reflect.Field;
import java.util.Properties;
+import java.util.Set;
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.TemplateBuilderSpec;
+import org.jclouds.compute.extensions.ImageExtension;
import org.jclouds.compute.extensions.internal.BaseImageExtensionLiveTest;
+import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.sshj.config.SshjSshClientModule;
import org.testng.annotations.Test;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.base.Throwables;
+import com.google.common.cache.LoadingCache;
+import com.google.inject.Key;
import com.google.inject.Module;
+import com.google.inject.TypeLiteral;
/**
* Live test for ec2 {@link ImageExtension} implementation
@@ -43,11 +58,42 @@ public class EC2ImageExtensionLiveTest extends BaseImageExtensionLiveTest {
protected Properties setupProperties() {
Properties overrides = super.setupProperties();
String ebsSpec = checkNotNull(setIfTestSystemPropertyPresent(overrides, provider + ".ebs-template"), provider
- + ".ebs-template");
+ + ".ebs-template");
ebsTemplate = TemplateBuilderSpec.parse(ebsSpec);
return overrides;
}
+ // Getting an image from the cache is tricky in EC2, as images are filtered
+ // by default by owner, and the image being created will be owned by the
+ // current user. If the cache needs to refresh the list, the just added image
+ // will be removed as the image list will be refreshed (potentially) without
+ // taking into account the current user owner id. Instead of using the
+ // TempalteBuilder, just inspect the ImageCacheSupplier directly
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Optional<Image> findImageWithNameInCache(String name) {
+ ImageCacheSupplier imageCache = (ImageCacheSupplier) context.utils().injector()
+ .getInstance(Key.get(new TypeLiteral<Supplier<Set<? extends Image>>>() {
+ }, Memoized.class));
+
+ try {
+ Field field = imageCache.getClass().getDeclaredField("imageCache");
+ field.setAccessible(true);
+ LoadingCache<String, Image> cache = (LoadingCache<String, Image>) field.get(imageCache);
+
+ return Optional.fromNullable(getOnlyElement(filter(cache.asMap().values(), new Predicate<Image>() {
+ @Override
+ public boolean apply(Image input) {
+ return imageGroup.equals(input.getName());
+ }
+ }), null));
+ } catch (NoSuchFieldException ex) {
+ throw Throwables.propagate(ex);
+ } catch (IllegalAccessException ex) {
+ throw Throwables.propagate(ex);
+ }
+ }
+
@Override
public Template getNodeTemplate() {
return view.getComputeService().templateBuilder().from(ebsTemplate).build();
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/apis/ec2/src/test/java/org/jclouds/ec2/compute/internal/EC2TemplateBuilderImplTest.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/internal/EC2TemplateBuilderImplTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/internal/EC2TemplateBuilderImplTest.java
index 0502175..7086978 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/internal/EC2TemplateBuilderImplTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/internal/EC2TemplateBuilderImplTest.java
@@ -44,6 +44,7 @@ import org.jclouds.domain.Location;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.functions.ImagesToRegionAndIdMap;
import org.jclouds.ec2.compute.options.EC2TemplateOptions;
+import org.jclouds.rest.AuthorizationException;
import org.testng.annotations.Test;
import com.google.common.base.Functions;
@@ -54,6 +55,8 @@ import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.Atomics;
+import com.google.inject.util.Providers;
@Test(groups = "unit", singleThreaded = true)
public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest {
@@ -86,8 +89,10 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest {
ImagesToRegionAndIdMap.imagesToMap(images.get()))));
}
- return new EC2TemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60), sizes, Suppliers.ofInstance(defaultLocation),
- optionsProvider, templateBuilderProvider, getImageStrategy, Suppliers.<LoadingCache<RegionAndName, ? extends Image>>ofInstance(imageMap));
+ return new EC2TemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60,
+ Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy)), sizes,
+ Suppliers.ofInstance(defaultLocation), optionsProvider, templateBuilderProvider,
+ Suppliers.<LoadingCache<RegionAndName, ? extends Image>> ofInstance(imageMap));
}
@Override
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java
index f03c456..338dee7 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java
@@ -80,7 +80,6 @@ import org.jclouds.openstack.nova.v2_0.predicates.FindSecurityGroupWithNameAndRe
import com.google.common.annotations.VisibleForTesting;
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;
@@ -280,14 +279,4 @@ public class NovaComputeServiceContextModule extends
protected final Map<org.jclouds.openstack.nova.v2_0.domain.Image.Status, Image.Status> toPortableImageStatus() {
return toPortableImageStatus;
}
-
- @Override
- protected Optional<ImageExtension> provideImageExtension(Injector i) {
- return Optional.of(i.getInstance(ImageExtension.class));
- }
-
- @Override
- protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) {
- return Optional.of(i.getInstance(SecurityGroupExtension.class));
- }
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java b/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java
index c1bdf38..ab5fb0f 100644
--- a/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java
+++ b/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java
@@ -52,6 +52,7 @@ import org.jclouds.compute.options.RunScriptOptions;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.compute.strategy.CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap;
+import org.jclouds.compute.strategy.GetImageStrategy;
import org.jclouds.compute.strategy.InitializeRunScriptOnNodeOrPlaceInBadMap;
import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.config.ValueOfConfigurationKeyOrNull;
@@ -71,8 +72,10 @@ import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.inject.AbstractModule;
+import com.google.inject.Binding;
import com.google.inject.Inject;
import com.google.inject.Injector;
+import com.google.inject.Key;
import com.google.inject.Provides;
import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.FactoryModuleBuilder;
@@ -112,9 +115,6 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule {
}, InitializeRunScriptOnNodeOrPlaceInBadMap.class).build(InitializeRunScriptOnNodeOrPlaceInBadMap.Factory.class));
install(new FactoryModuleBuilder().build(BlockUntilInitScriptStatusIsZeroThenReturnOutput.Factory.class));
-
- bind(new TypeLiteral<Supplier<Set<? extends Image>>>() {
- }).annotatedWith(Memoized.class).to(ImageCacheSupplier.class);
}
protected void bindCredentialsOverriderFunction() {
@@ -238,35 +238,27 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule {
}, images);
}
-
+
@Provides
@Singleton
- @Named("imageCache")
- protected final Supplier<Set<? extends Image>> supplyImageCache(AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
- final Supplier<Set<? extends Image>> imageSupplier, Injector injector) {
- if (shouldEagerlyParseImages(injector)) {
- return supplyImageCache(authException, seconds, imageSupplier);
- } else {
- return supplyNonParsingImageCache(authException, seconds, imageSupplier, injector);
- }
+ @Memoized
+ protected final Supplier<Set<? extends Image>> supplyImageCache(
+ AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
+ final Supplier<Set<? extends Image>> imageSupplier, com.google.inject.Provider<GetImageStrategy> imageLoader, Injector injector) {
+ Supplier<Set<? extends Image>> parsingImageSupplier = shouldEagerlyParseImages(injector) ? imageSupplier
+ : supplyNonParsingImages(imageSupplier, injector);
+ return new ImageCacheSupplier(parsingImageSupplier, seconds, authException, imageLoader);
}
protected boolean shouldEagerlyParseImages(Injector injector) {
return true;
}
- protected Supplier<Set<? extends Image>> supplyImageCache(AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
- final Supplier<Set<? extends Image>> imageSupplier) {
- return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, imageSupplier, seconds,
- TimeUnit.SECONDS);
- }
-
/**
- * For overriding; default impl is same as {@link supplyImageCache(seconds, imageSupplier)}
+ * For overriding; default impl just returns the image supplier.
*/
- protected Supplier<Set<? extends Image>> supplyNonParsingImageCache(AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
- final Supplier<Set<? extends Image>> imageSupplier, Injector injector) {
- return supplyImageCache(authException, seconds, imageSupplier);
+ protected Supplier<Set<? extends Image>> supplyNonParsingImages(final Supplier<Set<? extends Image>> imageSupplier, Injector injector) {
+ return imageSupplier;
}
@Provides
@@ -312,21 +304,15 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule {
@Provides
@Singleton
public final Optional<ImageExtension> guiceProvideImageExtension(Injector i) {
- return provideImageExtension(i);
- }
-
- protected Optional<ImageExtension> provideImageExtension(Injector i) {
- return Optional.absent();
+ Binding<ImageExtension> binding = i.getExistingBinding(Key.get(ImageExtension.class));
+ return binding == null ? Optional.<ImageExtension> absent() : Optional.of(binding.getProvider().get());
}
@Provides
@Singleton
protected final Optional<SecurityGroupExtension> guiceProvideSecurityGroupExtension(Injector i) {
- return provideSecurityGroupExtension(i);
- }
-
- protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) {
- return Optional.absent();
+ Binding<SecurityGroupExtension> binding = i.getExistingBinding(Key.get(SecurityGroupExtension.class));
+ return binding == null ? Optional.<SecurityGroupExtension> absent() : Optional.of(binding.getProvider().get());
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilder.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilder.java b/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilder.java
index cea193d..e2adadc 100644
--- a/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilder.java
+++ b/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilder.java
@@ -56,7 +56,6 @@ public interface TemplateBuilder {
*
* @since 1.5
*/
- @Beta
TemplateBuilder from(TemplateBuilderSpec spec);
/**
@@ -66,7 +65,6 @@ public interface TemplateBuilder {
* @param spec a String in the format specified by {@link TemplateBuilderSpec}
* @since 1.5
*/
- @Beta
TemplateBuilder from(String spec);
/**
@@ -202,5 +200,19 @@ public interface TemplateBuilder {
* Normal usage is to build up all options and pass them to the builder with a single call to this method.
*/
TemplateBuilder options(TemplateOptions options);
+
+ /**
+ * Forces an image lookup against the provider to reload the image cache.
+ * <p>
+ * Use with caution. In some providers getting the list of images is an
+ * expensive operation, and the use of the image cache is recommended. If
+ * there is a need to minimize the amount of time the images are cached,
+ * consider configuring the cache expiration time by setting the
+ * {@link org.jclouds.Constants#PROPERTY_SESSION_INTERVAL} property.
+ *
+ * @since 2.0
+ */
+ @Beta
+ TemplateBuilder forceCacheReload();
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilderSpec.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilderSpec.java b/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilderSpec.java
index 5a073e0..d00cb3b 100644
--- a/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilderSpec.java
+++ b/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilderSpec.java
@@ -145,6 +145,7 @@ public class TemplateBuilderSpec {
.put("loginUser", new LoginUserParser())
.put("authenticateSudo", new AuthenticateSudoParser())
.put("locationId", new LocationIdParser())
+ .put("forceCacheReload", new ForceCacheReloadParser())
.build();
@VisibleForTesting
@@ -177,6 +178,8 @@ public class TemplateBuilderSpec {
Boolean authenticateSudo;
@VisibleForTesting
String locationId;
+ @VisibleForTesting
+ Boolean forceCacheReload;
/** Specification; used for toParseableString(). */
// transient in case people using serializers don't want this to show up
@@ -279,6 +282,9 @@ public class TemplateBuilderSpec {
if (locationId != null) {
builder.locationId(locationId);
}
+ if (forceCacheReload != null && forceCacheReload) {
+ builder.forceCacheReload();
+ }
return builder;
}
@@ -304,7 +310,8 @@ public class TemplateBuilderSpec {
@Override
public int hashCode() {
return Objects.hashCode(hardwareId, minCores, minRam, hypervisorMatches, imageId, imageNameMatches, osFamily,
- osVersionMatches, os64Bit, osArchMatches, osDescriptionMatches, loginUser, authenticateSudo, locationId);
+ osVersionMatches, os64Bit, osArchMatches, osDescriptionMatches, loginUser, authenticateSudo, locationId,
+ forceCacheReload);
}
@Override
@@ -322,7 +329,7 @@ public class TemplateBuilderSpec {
&& equal(osVersionMatches, that.osVersionMatches) && equal(os64Bit, that.os64Bit)
&& equal(osArchMatches, that.osArchMatches) && equal(osDescriptionMatches, that.osDescriptionMatches)
&& equal(loginUser, that.loginUser) && equal(authenticateSudo, that.authenticateSudo)
- && equal(locationId, that.locationId);
+ && equal(locationId, that.locationId) && equal(forceCacheReload, that.forceCacheReload);
}
/** Base class for parsing doubles. */
@@ -563,6 +570,15 @@ public class TemplateBuilderSpec {
}
}
+ /** Parse forceCacheReload */
+ static class ForceCacheReloadParser extends BooleanParser {
+ @Override
+ protected void parseBoolean(TemplateBuilderSpec spec, boolean value) {
+ checkArgument(spec.forceCacheReload == null, "forceCacheReload was already set to ", spec.forceCacheReload);
+ spec.forceCacheReload = value;
+ }
+ }
+
public String getHardwareId() {
return hardwareId;
}
@@ -622,4 +638,9 @@ public class TemplateBuilderSpec {
public String getSpecification() {
return specification;
}
+
+ public Boolean getForceCacheReload() {
+ return forceCacheReload;
+ }
+
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateBuilderImpl.java
----------------------------------------------------------------------
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 72a4b47..2865409 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
@@ -16,6 +16,7 @@
*/
package org.jclouds.compute.domain.internal;
+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.Predicates.and;
@@ -52,8 +53,8 @@ import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.domain.TemplateBuilderSpec;
import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.compute.predicates.ImagePredicates;
import org.jclouds.compute.reference.ComputeServiceConstants;
-import org.jclouds.compute.strategy.GetImageStrategy;
import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.domain.Location;
import org.jclouds.logging.Logger;
@@ -84,7 +85,6 @@ public class TemplateBuilderImpl implements TemplateBuilder {
protected final Supplier<Location> defaultLocation;
protected final Provider<TemplateOptions> optionsProvider;
protected final Provider<TemplateBuilder> defaultTemplateProvider;
- protected final GetImageStrategy getImageStrategy;
@VisibleForTesting
protected Location location;
@@ -128,19 +128,21 @@ public class TemplateBuilderImpl implements TemplateBuilder {
protected boolean fastest;
@VisibleForTesting
protected TemplateOptions options;
+ @VisibleForTesting
+ protected Boolean forceCacheReload;
@Inject
protected TemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations,
- ImageCacheSupplier images, @Memoized Supplier<Set<? extends Hardware>> hardwares,
+ @Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> hardwares,
Supplier<Location> defaultLocation, @Named("DEFAULT") Provider<TemplateOptions> optionsProvider,
- @Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider, GetImageStrategy getImageStrategy) {
+ @Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider) {
this.locations = checkNotNull(locations, "locations");
- this.images = checkNotNull(images, "images");
+ checkArgument(images instanceof ImageCacheSupplier, "an instance of the ImageCacheSupplier is needed");
+ this.images = ImageCacheSupplier.class.cast(images);
this.hardwares = checkNotNull(hardwares, "hardwares");
this.defaultLocation = checkNotNull(defaultLocation, "defaultLocation");
this.optionsProvider = checkNotNull(optionsProvider, "optionsProvider");
this.defaultTemplateProvider = checkNotNull(defaultTemplateProvider, "defaultTemplateProvider");
- this.getImageStrategy = checkNotNull(getImageStrategy, "getImageStrategy");
}
static Predicate<Hardware> supportsImagesPredicate(final Iterable<? extends Image> images) {
@@ -175,26 +177,6 @@ public class TemplateBuilderImpl implements TemplateBuilder {
});
- private final Predicate<Image> idPredicate = new Predicate<Image>() {
- @Override
- public boolean apply(Image input) {
- boolean returnVal = true;
- if (imageId != null) {
- returnVal = imageId.equals(input.getId());
- // match our input params so that the later predicates pass.
- if (returnVal) {
- fromImage(input);
- }
- }
- return returnVal;
- }
-
- @Override
- public String toString() {
- return "imageId(" + imageId + ")";
- }
- };
-
private final Predicate<OperatingSystem> osFamilyPredicate = new Predicate<OperatingSystem>() {
@Override
@@ -691,7 +673,7 @@ public class TemplateBuilderImpl implements TemplateBuilder {
Image image = null;
if (imageId != null) {
- image = findImageWithId(images);
+ image = loadImageWithId(images);
if (currentLocationWiderThan(image.getLocation()))
this.location = image.getLocation();
}
@@ -733,27 +715,16 @@ public class TemplateBuilderImpl implements TemplateBuilder {
return supportedImages;
}
- private Image findImageWithId(Set<? extends Image> images) {
- // Try to find the image in the cache and fallback to the GetImageStrategy
- // see https://issues.apache.org/jira/browse/JCLOUDS-570
- Optional<? extends Image> image = tryFind(images, idPredicate);
- if (image.isPresent()) {
- return image.get();
- }
-
- logger.info("Image %s not found in the image cache. Trying to get it from the provider...", imageId);
- // Note that this will generate make a call to the provider instead of using a cache, but
- // this will be executed rarely, only when an image is not present in the image list but
- // it actually exists in the provider. It shouldn't be an expensive call so using a cache just for
- // this corner case is overkill.
- Image imageFromProvider = getImageStrategy.getImage(imageId);
- if (imageFromProvider == null) {
- throwNoSuchElementExceptionAfterLoggingImageIds(format("%s not found", idPredicate), images);
+ private Image loadImageWithId(Iterable<? extends Image> images) {
+ Optional<? extends Image> image = tryFind(images, ImagePredicates.idEquals(imageId));
+ if (!image.isPresent()) {
+ image = this.images.get(imageId); // Load the image from the cache, and refresh if missing
+ if (!image.isPresent()) {
+ throw throwNoSuchElementExceptionAfterLoggingImageIds(format("imageId(%s) not found", imageId), images);
+ }
}
- // Register the just found image in the image cache, so subsequent uses of the TemplateBuilder and
- // the ComptueService find it.
- this.images.registerImage(imageFromProvider);
- return imageFromProvider;
+ fromImage(image.get());
+ return image.get();
}
private Hardware findHardwareWithId(Set<? extends Hardware> hardwaresToSearch) {
@@ -856,10 +827,8 @@ public class TemplateBuilderImpl implements TemplateBuilder {
logger.trace("<< matched images(%s)", transform(matchingImages, imageToId));
return imageChooser().apply(matchingImages);
} catch (NoSuchElementException exception) {
- throwNoSuchElementExceptionAfterLoggingImageIds(format("no image matched params: %s", toString()),
- supportedImages);
- assert false;
- return null;
+ throw throwNoSuchElementExceptionAfterLoggingImageIds(format("no image matched params: %s", toString()),
+ supportedImages);
}
}
@@ -885,7 +854,7 @@ public class TemplateBuilderImpl implements TemplateBuilder {
return maxes;
}
protected Set<? extends Image> getImages() {
- return images.get();
+ return forceCacheReload != null && forceCacheReload ? images.rebuildCache() : images.get();
}
private Predicate<Image> buildImagePredicate() {
@@ -1157,6 +1126,7 @@ public class TemplateBuilderImpl implements TemplateBuilder {
toString.add("os64Bit", os64Bit);
toString.add("hardwareId", hardwareId);
toString.add("hypervisor", hypervisor);
+ toString.add("forceCacheReload", forceCacheReload);
return toString;
}
@@ -1176,4 +1146,11 @@ public class TemplateBuilderImpl implements TemplateBuilder {
return from(TemplateBuilderSpec.parse(spec));
}
+ @Override
+ public TemplateBuilder forceCacheReload() {
+ this.forceCacheReload = true;
+ return this;
+ }
+
+
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/compute/src/main/java/org/jclouds/compute/extensions/internal/DelegatingImageExtension.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/extensions/internal/DelegatingImageExtension.java b/compute/src/main/java/org/jclouds/compute/extensions/internal/DelegatingImageExtension.java
new file mode 100644
index 0000000..e0ecf8b
--- /dev/null
+++ b/compute/src/main/java/org/jclouds/compute/extensions/internal/DelegatingImageExtension.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.compute.extensions.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.ImageTemplate;
+import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.suppliers.ImageCacheSupplier;
+
+import com.google.common.annotations.Beta;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Delegates to the provider specific {@link ImageExtension} and takes care of
+ * propagating the changes made to the images to the image cache.
+ */
+@Beta
+public class DelegatingImageExtension implements ImageExtension {
+
+ private final ImageCacheSupplier imageCache;
+ private final ImageExtension delegate;
+
+ public DelegatingImageExtension(ImageCacheSupplier imageCache, ImageExtension delegate) {
+ this.imageCache = checkNotNull(imageCache, "imageCache");
+ this.delegate = checkNotNull(delegate, "delegate");
+ }
+
+ public ImageTemplate buildImageTemplateFromNode(String name, String id) {
+ return delegate.buildImageTemplateFromNode(name, id);
+ }
+
+ public ListenableFuture<Image> createImage(ImageTemplate template) {
+ ListenableFuture<Image> future = delegate.createImage(template);
+ Futures.addCallback(future, new FutureCallback<Image>() {
+ @Override
+ public void onSuccess(Image result) {
+ imageCache.registerImage(result);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+
+ }
+ });
+ return future;
+ }
+
+ public boolean deleteImage(String id) {
+ boolean success = delegate.deleteImage(id);
+ if (success) {
+ imageCache.removeImage(id);
+ }
+ return success;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java b/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java
index 3ba198c..6cde18d 100644
--- a/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java
+++ b/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java
@@ -66,6 +66,7 @@ import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.extensions.ImageExtension;
import org.jclouds.compute.extensions.SecurityGroupExtension;
+import org.jclouds.compute.extensions.internal.DelegatingImageExtension;
import org.jclouds.compute.options.RunScriptOptions;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.reference.ComputeServiceConstants;
@@ -80,6 +81,7 @@ import org.jclouds.compute.strategy.RebootNodeStrategy;
import org.jclouds.compute.strategy.ResumeNodeStrategy;
import org.jclouds.compute.strategy.RunScriptOnNodeAndAddToGoodMapOrPutExceptionIntoBadMap;
import org.jclouds.compute.strategy.SuspendNodeStrategy;
+import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.domain.Credentials;
import org.jclouds.domain.Location;
import org.jclouds.domain.LoginCredentials;
@@ -180,8 +182,13 @@ public class BaseComputeService implements ComputeService {
this.runScriptOnNodeFactory = checkNotNull(runScriptOnNodeFactory, "runScriptOnNodeFactory");
this.persistNodeCredentials = checkNotNull(persistNodeCredentials, "persistNodeCredentials");
this.userExecutor = checkNotNull(userExecutor, "userExecutor");
- this.imageExtension = checkNotNull(imageExtension, "imageExtension");
this.securityGroupExtension = checkNotNull(securityGroupExtension, "securityGroupExtension");
+ if (imageExtension.isPresent() && images instanceof ImageCacheSupplier) {
+ this.imageExtension = Optional.<ImageExtension> of(new DelegatingImageExtension(ImageCacheSupplier.class
+ .cast(images), imageExtension.get()));
+ } else {
+ this.imageExtension = checkNotNull(imageExtension, "imageExtension");
+ }
}
/**
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceContextModule.java b/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceContextModule.java
index 6ddc436..be419d5 100644
--- a/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceContextModule.java
+++ b/compute/src/main/java/org/jclouds/compute/stub/config/StubComputeServiceContextModule.java
@@ -17,12 +17,8 @@
package org.jclouds.compute.stub.config;
import org.jclouds.compute.config.JCloudsNativeComputeServiceAdapterContextModule;
-import org.jclouds.compute.extensions.SecurityGroupExtension;
import org.jclouds.concurrent.SingleThreaded;
-import com.google.common.base.Optional;
-import com.google.inject.Injector;
-
@SingleThreaded
public class StubComputeServiceContextModule extends JCloudsNativeComputeServiceAdapterContextModule {
@@ -36,9 +32,4 @@ public class StubComputeServiceContextModule extends JCloudsNativeComputeService
super.configure();
}
- @Override
- protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) {
- return Optional.of(i.getInstance(SecurityGroupExtension.class));
- }
-
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/compute/src/main/java/org/jclouds/compute/suppliers/ImageCacheSupplier.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/suppliers/ImageCacheSupplier.java b/compute/src/main/java/org/jclouds/compute/suppliers/ImageCacheSupplier.java
index 400525b..d2d111d 100644
--- a/compute/src/main/java/org/jclouds/compute/suppliers/ImageCacheSupplier.java
+++ b/compute/src/main/java/org/jclouds/compute/suppliers/ImageCacheSupplier.java
@@ -17,68 +17,181 @@
package org.jclouds.compute.suppliers;
import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.collect.Iterables.concat;
-import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
import java.util.Set;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.annotation.Resource;
import javax.inject.Named;
-import javax.inject.Singleton;
import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.compute.strategy.GetImageStrategy;
+import org.jclouds.logging.Logger;
+import org.jclouds.rest.AuthorizationException;
+import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
+import org.jclouds.rest.suppliers.ValueLoadedCallback;
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
import com.google.common.base.Supplier;
-import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
-import com.google.inject.Inject;
+import com.google.common.collect.Maps;
+import com.google.inject.Provider;
/**
- * Image supplier that allows new images to be registered.
+ * Memoized image supplier that allows new images to be registered at runtime.
* <p>
- * This is a wrapper for the memoized image supplier (the actual image cache), to provide a way to register new images as
- * needed. Once a new image is created by the {@link org.jclouds.compute.extensions.ImageExtension}, or discovered by
- * other means (see https://issues.apache.org/jira/browse/JCLOUDS-570) this supplier will allow the image to be appended
- * to the cached list, so it can be properly used normally.
+ * The memoized <code>Supplier<Set<? extends Image>></code> is a static data
+ * structure that can't be properly modified at runtime. This class is a wrapper
+ * for the image supplier to provide a way to register new images as needed.
+ * Once a new image is created by the
+ * {@link org.jclouds.compute.extensions.ImageExtension}, or discovered by other
+ * means (see https://issues.apache.org/jira/browse/JCLOUDS-570) this supplier
+ * will allow the image to be appended to the cached list.
*/
-@Singleton
-public class ImageCacheSupplier implements Supplier<Set<? extends Image>> {
+@Beta
+public class ImageCacheSupplier implements Supplier<Set<? extends Image>>, ValueLoadedCallback<Set<? extends Image>> {
- private final Supplier<Set<? extends Image>> imageCache;
-
- private final Cache<String, Image> uncachedImages;
+ /**
+ * The image supplier that fetches the images from the provider.
+ */
+ private final Supplier<Set<? extends Image>> liveImageSupplier;
+
+ /**
+ * The image supplier that loads the images and caches them for the duration
+ * of the session. Delegates to the {@link #liveImageSupplier}.
+ */
+ private final Supplier<Set<? extends Image>> memoizedImageSupplier;
+
+ /**
+ * The actual image cache. It acts as a view over the memoized image supplier
+ * and allows to add and remove images at runtime.
+ */
+ private final LoadingCache<String, Image> imageCache;
+
+ @Resource
+ @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+ protected Logger logger = Logger.NULL;
- @Inject
- public ImageCacheSupplier(@Named("imageCache") Supplier<Set<? extends Image>> imageCache,
- @Named(PROPERTY_SESSION_INTERVAL) long sessionIntervalSeconds) {
- this.imageCache = checkNotNull(imageCache, "imageCache");
- // We use a cache to let the entries in the "uncached" set expire as soon as the image cache expires. We want the
- // uncached set to be regenerated when the original cache is also regenerated.
- this.uncachedImages = CacheBuilder.newBuilder().expireAfterWrite(sessionIntervalSeconds, TimeUnit.SECONDS)
- .build();
+ public ImageCacheSupplier(Supplier<Set<? extends Image>> imageSupplier, long sessionIntervalSeconds,
+ AtomicReference<AuthorizationException> authException, final Provider<GetImageStrategy> imageLoader) {
+ liveImageSupplier = imageSupplier;
+ memoizedImageSupplier = MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException,
+ imageSupplier, sessionIntervalSeconds, TimeUnit.SECONDS, this);
+ imageCache = CacheBuilder.newBuilder().expireAfterWrite(sessionIntervalSeconds, TimeUnit.SECONDS)
+ .build(new CacheLoader<String, Image>() {
+ @Override
+ public Image load(String key) throws Exception {
+ return imageLoader.get().getImage(key);
+ }
+ });
}
-
+
@Override
public Set<? extends Image> get() {
- return ImmutableSet.copyOf(concat(imageCache.get(), uncachedImages.asMap().values()));
+ // Call the memoized supplier. The "imageCache" is subscribed to the
+ // reloads of the supplier once it expires. For this reason we ignore the
+ // value returned by the supplier: every time it is reloaded, the cache
+ // will be notified and re-populated with the fresh values. Any other call
+ // to the supplier that returns a cached value will be ignored and the
+ // values in the cache will be returned, as the cache properly handles
+ // individual image additions and deletions (introduced, for example, by
+ // the usage of the ImageExtension).
+ memoizedImageSupplier.get();
+ return ImmutableSet.copyOf(imageCache.asMap().values());
+ }
+
+ /**
+ * The cache is subscribed to value loading events generated by the
+ * {@link MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier}.
+ * <p>
+ * Every time the memoized supplier reloads a value, an event will be
+ * populated and this method will handle it. This makes it possible to
+ * refresh the cache with the last values everytime they are reloaded.
+ */
+ @Override
+ public void valueLoaded(Optional<Set<? extends Image>> value) {
+ if (value.isPresent()) {
+ reset(value.get());
+ }
+ }
+
+ /**
+ * Resets the cache to the given set of images.
+ * <p>
+ * This method is called when the memoized image supplier is reloaded, or
+ * when the cache needs to be refreshed (for example when the TempalteBuilder
+ * is invoked forcing a fresh image lookup.
+ */
+ public void reset(Set<? extends Image> images) {
+ imageCache.invalidateAll();
+ imageCache.putAll(Maps.uniqueIndex(images, new Function<Image, String>() {
+ @Override
+ public String apply(Image input) {
+ return input.getId();
+ }
+ }));
+ }
+
+ /**
+ * Calls the {@link #liveImageSupplier} to get the current images and
+ * rebuilds the cache with them.
+ */
+ public Set<? extends Image> rebuildCache() {
+ Set<? extends Image> images = liveImageSupplier.get();
+ reset(images);
+ return images;
+ }
+
+ /**
+ * Loads an image by id.
+ * <p>
+ * This methods returns the cached image, or performs a call to retrieve it
+ * if the image is still not cached.
+ */
+ public Optional<? extends Image> get(String id) {
+ try {
+ return Optional.fromNullable(imageCache.getUnchecked(id));
+ } catch (Exception ex) {
+ logger.error(ex, "Unexpected error loading image %s", id);
+ return Optional.absent();
+ }
}
/**
* Registers a new image in the image cache.
* <p>
- * This method should be called to register new images into the image cache, when some image that is known to exist
- * in the provider is still not cached. For example, this can happen when an image is created after the image cache
- * has been populated for the first time.
+ * This method should be called to register new images into the image cache
+ * when some image that is known to exist in the provider is still not
+ * cached. For example, this can happen when an image is created after the
+ * image cache has been populated for the first time.
* <p>
- * Note that this method does not check if the image is already cached, to avoid loading all images if the image
- * cache is still not populated.
+ * Note that this method does not check if the image is already cached, to
+ * avoid loading all images if the image cache is still not populated.
*
* @param image The image to be registered to the cache.
*/
public void registerImage(Image image) {
checkNotNull(image, "image");
- uncachedImages.put(image.getId(), image);
+ imageCache.put(image.getId(), image);
+ }
+
+ /**
+ * Removes an image from the image cache.
+ * <p>
+ * This method should be called to invalidate an already cached image, when
+ * some image known to not exist in the provider is still cached.
+ *
+ * @param imageId The id of the image to invalidate.
+ */
+ public void removeImage(String imageId) {
+ imageCache.invalidate(checkNotNull(imageId, "imageId"));
}
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/compute/src/test/java/org/jclouds/compute/domain/TemplateBuilderSpecTest.java
----------------------------------------------------------------------
diff --git a/compute/src/test/java/org/jclouds/compute/domain/TemplateBuilderSpecTest.java b/compute/src/test/java/org/jclouds/compute/domain/TemplateBuilderSpecTest.java
index cd51b33..8c11751 100644
--- a/compute/src/test/java/org/jclouds/compute/domain/TemplateBuilderSpecTest.java
+++ b/compute/src/test/java/org/jclouds/compute/domain/TemplateBuilderSpecTest.java
@@ -61,6 +61,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
+ assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get(), templateBuilders.get().from(spec));
}
@@ -81,6 +82,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
+ assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().hardwareId("m1.small"),
templateBuilders.get().from(spec));
}
@@ -132,6 +134,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
+ assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().minCores(32), templateBuilders.get().from(spec));
}
@@ -160,6 +163,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
+ assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().minRam(10), templateBuilders.get().from(spec));
}
@@ -180,6 +184,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
+ assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().minRam(10), templateBuilders.get().from(spec));
}
@@ -216,6 +221,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
+ assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().hypervisorMatches("OpenVZ"),
templateBuilders.get().from(spec));
}
@@ -246,6 +252,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
+ assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().imageId("us-east-1/ami-fffffff"),
templateBuilders.get().from(spec));
}
@@ -315,6 +322,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
+ assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().imageNameMatches(".*w/ None.*"),
templateBuilders.get().from(spec));
}
@@ -345,6 +353,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
+ assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().osFamily(OsFamily.UBUNTU),
templateBuilders.get().from(spec));
}
@@ -374,6 +383,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
+ assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().osVersionMatches(".*[Aa]utomated SSH Access.*"),
templateBuilders.get().from(spec));
}
@@ -403,6 +413,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
+ assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().os64Bit(true),
templateBuilders.get().from(spec));
}
@@ -432,6 +443,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
+ assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().osArchMatches("x86"),
templateBuilders.get().from(spec));
}
@@ -461,6 +473,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
+ assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().osDescriptionMatches("^((?!MGC).)*$"),
templateBuilders.get().from(spec));
}
@@ -491,6 +504,7 @@ public class TemplateBuilderSpecTest {
assertEquals(spec.loginUser, "ubuntu");
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
+ assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(
templateBuilders.get().options(overrideLoginUser("ubuntu")), templateBuilders
.get().from(spec));
@@ -522,6 +536,7 @@ public class TemplateBuilderSpecTest {
assertEquals(spec.loginUser, "root:toor");
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
+ assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(
templateBuilders.get().options(
overrideLoginCredentials(LoginCredentials.builder().user("root").password("toor").build())),
@@ -554,12 +569,13 @@ public class TemplateBuilderSpecTest {
assertEquals(spec.loginUser, "root:toor");
assertEquals(spec.authenticateSudo.booleanValue(), true);
assertNull(spec.locationId);
+ assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(
templateBuilders.get().options(
overrideLoginCredentials(LoginCredentials.builder().user("root").password("toor")
.authenticateSudo(true).build())), templateBuilders.get().from(spec));
}
-
+
public void testParse_authenticateSudoRepeated() {
try {
parse("loginUser=root:toor,authenticateSudo=true,authenticateSudo=false");
@@ -568,6 +584,36 @@ public class TemplateBuilderSpecTest {
// expected
}
}
+
+ public void testParse_forceCacheReload() {
+ TemplateBuilderSpec spec = parse("forceCacheReload=true");
+ assertNull(spec.hardwareId);
+ assertNull(spec.minCores);
+ assertNull(spec.minRam);
+ assertNull(spec.minDisk);
+ assertNull(spec.imageId);
+ assertNull(spec.imageNameMatches);
+ assertNull(spec.hypervisorMatches);
+ assertNull(spec.osFamily);
+ assertNull(spec.osVersionMatches);
+ assertNull(spec.os64Bit);
+ assertNull(spec.osArchMatches);
+ assertNull(spec.osDescriptionMatches);
+ assertNull(spec.loginUser);
+ assertNull(spec.authenticateSudo);
+ assertNull(spec.locationId);
+ assertEquals(spec.forceCacheReload.booleanValue(), true);
+ assertTemplateBuilderEquivalence(templateBuilders.get().forceCacheReload(), templateBuilders.get().from(spec));
+ }
+
+ public void testParse_forceCacheReloadRepeated() {
+ try {
+ parse("forceCacheReload=true,forceCacheReload=false");
+ fail("Expected exception");
+ } catch (IllegalArgumentException expected) {
+ // expected
+ }
+ }
public void testParse_locationId() {
TemplateBuilderSpec spec = parse("locationId=stub");
@@ -586,6 +632,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertEquals(spec.locationId, "stub");
+ assertNull(spec.forceCacheReload);
assertTemplateBuilderEquivalence(templateBuilders.get().locationId("stub"),
templateBuilders.get().from(spec));
}
@@ -616,6 +663,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
+ assertNull(spec.forceCacheReload);
TemplateBuilder expected = templateBuilders.get().osVersionMatches("1[012].[01][04]").imageNameMatches(".*w/ None.*").osFamily(OsFamily.UBUNTU);
assertTemplateBuilderEquivalence(expected, templateBuilders.get().from(spec));
}
@@ -637,6 +685,7 @@ public class TemplateBuilderSpecTest {
assertNull(spec.loginUser);
assertNull(spec.authenticateSudo);
assertNull(spec.locationId);
+ assertNull(spec.forceCacheReload);
TemplateBuilder expected = templateBuilders.get().minRam(10).osFamily(OsFamily.UBUNTU);
assertTemplateBuilderEquivalence(expected, templateBuilders.get().from(spec));
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/compute/src/test/java/org/jclouds/compute/domain/internal/TemplateBuilderImplTest.java
----------------------------------------------------------------------
diff --git a/compute/src/test/java/org/jclouds/compute/domain/internal/TemplateBuilderImplTest.java b/compute/src/test/java/org/jclouds/compute/domain/internal/TemplateBuilderImplTest.java
index eaaf17e..cc2708c 100644
--- a/compute/src/test/java/org/jclouds/compute/domain/internal/TemplateBuilderImplTest.java
+++ b/compute/src/test/java/org/jclouds/compute/domain/internal/TemplateBuilderImplTest.java
@@ -50,6 +50,7 @@ import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.domain.Location;
import org.jclouds.domain.LocationBuilder;
import org.jclouds.domain.LocationScope;
+import org.jclouds.rest.AuthorizationException;
import org.testng.annotations.Test;
import com.google.common.base.Function;
@@ -62,6 +63,8 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering;
+import com.google.common.util.concurrent.Atomics;
+import com.google.inject.util.Providers;
@Test(groups = "unit", singleThreaded = true, testName = "TemplateBuilderImplTest")
public class TemplateBuilderImplTest {
@@ -468,8 +471,10 @@ public class TemplateBuilderImplTest {
Supplier<Set<? extends Image>> images, Supplier<Set<? extends Hardware>> hardwares,
Location defaultLocation, Provider<TemplateOptions> optionsProvider,
Provider<TemplateBuilder> templateBuilderProvider, GetImageStrategy getImageStrategy) {
- TemplateBuilderImpl template = new TemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60), hardwares, Suppliers
- .ofInstance(defaultLocation), optionsProvider, templateBuilderProvider, getImageStrategy);
+ TemplateBuilderImpl template = new TemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60,
+ Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy)), hardwares,
+ Suppliers.ofInstance(defaultLocation),
+ optionsProvider, templateBuilderProvider);
return template;
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/compute/src/test/java/org/jclouds/compute/extensions/internal/BaseImageExtensionLiveTest.java
----------------------------------------------------------------------
diff --git a/compute/src/test/java/org/jclouds/compute/extensions/internal/BaseImageExtensionLiveTest.java b/compute/src/test/java/org/jclouds/compute/extensions/internal/BaseImageExtensionLiveTest.java
index 8c0476e..878768e 100644
--- a/compute/src/test/java/org/jclouds/compute/extensions/internal/BaseImageExtensionLiveTest.java
+++ b/compute/src/test/java/org/jclouds/compute/extensions/internal/BaseImageExtensionLiveTest.java
@@ -20,8 +20,10 @@ import static java.util.concurrent.TimeUnit.SECONDS;
import static org.jclouds.compute.predicates.NodePredicates.inGroup;
import static org.jclouds.util.Predicates2.retry;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
+import java.util.NoSuchElementException;
import java.util.concurrent.ExecutionException;
import javax.annotation.Resource;
@@ -90,46 +92,40 @@ public abstract class BaseImageExtensionLiveTest extends BaseComputeServiceConte
@Test(groups = { "integration", "live" }, singleThreaded = true)
public void testCreateImage() throws RunNodesException, InterruptedException, ExecutionException {
-
ComputeService computeService = view.getComputeService();
-
Optional<ImageExtension> imageExtension = computeService.getImageExtension();
-
assertTrue(imageExtension.isPresent(), "image extension was not present");
Template template = getNodeTemplate();
-
NodeMetadata node = Iterables.getOnlyElement(computeService.createNodesInGroup(imageGroup, 1, template));
-
checkReachable(node);
logger.info("Creating image from node %s, started with template: %s", node, template);
-
ImageTemplate newImageTemplate = imageExtension.get().buildImageTemplateFromNode(imageGroup,
node.getId());
-
Image image = imageExtension.get().createImage(newImageTemplate).get();
-
logger.info("Image created: %s", image);
assertEquals(imageGroup, image.getName());
imageId = image.getId();
-
computeService.destroyNode(node.getId());
Optional<? extends Image> optImage = getImage();
-
assertTrue(optImage.isPresent());
}
+
+ @Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = "testCreateImage")
+ public void testImageIsCachedAfterBeingCreated() {
+ Optional<Image> imageInCache = findImageWithNameInCache(imageGroup);
+ assertTrue(imageInCache.isPresent());
+ assertEquals(imageInCache.get().getId(), imageId);
+ }
@Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = "testCreateImage")
public void testSpawnNodeFromImage() throws RunNodesException {
-
ComputeService computeService = view.getComputeService();
-
Optional<? extends Image> optImage = getImage();
-
assertTrue(optImage.isPresent());
NodeMetadata node = Iterables.getOnlyElement(computeService.createNodesInGroup(imageGroup, 1, view
@@ -138,28 +134,30 @@ public abstract class BaseImageExtensionLiveTest extends BaseComputeServiceConte
.templateBuilder().imageId(optImage.get().getId()).fromImage(optImage.get()).build()));
checkReachable(node);
-
view.getComputeService().destroyNode(node.getId());
-
}
@Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = { "testCreateImage",
- "testSpawnNodeFromImage" })
+ "testSpawnNodeFromImage", "testImageIsCachedAfterBeingCreated" })
public void testDeleteImage() {
-
ComputeService computeService = view.getComputeService();
Optional<ImageExtension> imageExtension = computeService.getImageExtension();
assertTrue(imageExtension.isPresent(), "image extension was not present");
Optional<? extends Image> optImage = getImage();
-
assertTrue(optImage.isPresent());
Image image = optImage.get();
-
assertTrue(imageExtension.get().deleteImage(image.getId()));
}
+
+ @Test(groups = { "integration", "live" }, singleThreaded = true, dependsOnMethods = "testDeleteImage")
+ public void testImageIsRemovedFromCacheAfterDeletion() {
+ Optional<Image> imageInCache = findImageWithNameInCache(imageGroup);
+ assertFalse(imageInCache.isPresent());
+ assertFalse(getImage().isPresent());
+ }
private Optional<? extends Image> getImage() {
return Optional.fromNullable(view.getComputeService().getImage(imageId));
@@ -179,6 +177,15 @@ public abstract class BaseImageExtensionLiveTest extends BaseComputeServiceConte
}, getSpawnNodeMaxWait(), 1l, SECONDS).apply(client));
}
+ protected Optional<Image> findImageWithNameInCache(String name) {
+ try {
+ Template template = view.getComputeService().templateBuilder().imageNameMatches(name).build();
+ return Optional.of(template.getImage());
+ } catch (NoSuchElementException ex) {
+ return Optional.absent();
+ }
+ }
+
@AfterClass(groups = { "integration", "live" })
@Override
protected void tearDownContext() {
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/compute/src/test/java/org/jclouds/compute/suppliers/ImageCacheSupplierTest.java
----------------------------------------------------------------------
diff --git a/compute/src/test/java/org/jclouds/compute/suppliers/ImageCacheSupplierTest.java b/compute/src/test/java/org/jclouds/compute/suppliers/ImageCacheSupplierTest.java
index 29a7caf..1e0befc 100644
--- a/compute/src/test/java/org/jclouds/compute/suppliers/ImageCacheSupplierTest.java
+++ b/compute/src/test/java/org/jclouds/compute/suppliers/ImageCacheSupplierTest.java
@@ -16,20 +16,31 @@
*/
package org.jclouds.compute.suppliers;
+import static com.google.common.collect.Iterables.any;
+import static org.jclouds.compute.predicates.ImagePredicates.idEquals;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.ImageBuilder;
import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.strategy.GetImageStrategy;
import org.jclouds.domain.Location;
import org.jclouds.domain.LocationBuilder;
import org.jclouds.domain.LocationScope;
+import org.jclouds.rest.AuthorizationException;
import org.testng.annotations.Test;
+import com.google.common.base.Optional;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.Atomics;
+import com.google.common.util.concurrent.Uninterruptibles;
+import com.google.inject.util.Providers;
/**
* Unit tests for the {@link ImageCacheSupplier} class.
@@ -49,15 +60,25 @@ public class ImageCacheSupplierTest {
private Set<? extends Image> images = ImmutableSet.of(image);
+ private GetImageStrategy getImageStrategy = new GetImageStrategy() {
+ @Override
+ public Image getImage(String id) {
+ return new ImageBuilder().id(id).providerId(id).name("imageName-" + id).description("imageDescription")
+ .version("imageVersion").operatingSystem(os).status(Image.Status.AVAILABLE).location(location).build();
+ }
+ };
+
@Test(expectedExceptions = NullPointerException.class)
public void testRegisterNullImageIsNotAllowed() {
- ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 60);
+ ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 60,
+ Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy));
imageCache.registerImage(null);
}
@Test
public void testRegisterImageIgnoresDuplicates() {
- ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 60);
+ ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 60,
+ Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy));
assertEquals(imageCache.get().size(), 1);
imageCache.registerImage(image);
@@ -67,11 +88,63 @@ public class ImageCacheSupplierTest {
@Test
public void testRegisterNewImage() {
- ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 60);
+ ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 60,
+ Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy));
assertEquals(imageCache.get().size(), 1);
imageCache.registerImage(ImageBuilder.fromImage(image).id("newimage").build());
assertEquals(imageCache.get().size(), 2);
}
+
+ @Test(expectedExceptions = NullPointerException.class)
+ public void testRemoveNullImageIsNotAllowed() {
+ ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 60,
+ Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy));
+ imageCache.removeImage(null);
+ }
+
+ @Test
+ public void testRemoveImage() {
+ ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 60,
+ Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy));
+ assertEquals(imageCache.get().size(), 1);
+
+ imageCache.removeImage(image.getId());
+
+ assertEquals(imageCache.get().size(), 0);
+ }
+
+ @Test
+ public void testLoadImage() {
+ ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 60,
+ Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy));
+ assertEquals(imageCache.get().size(), 1);
+
+ Optional<? extends Image> image = imageCache.get("foo");
+
+ assertTrue(image.isPresent());
+ assertEquals(image.get().getName(), "imageName-foo");
+ assertEquals(imageCache.get().size(), 2);
+ }
+
+ @Test
+ public void testSupplierExpirationReloadsTheCache() {
+ ImageCacheSupplier imageCache = new ImageCacheSupplier(Suppliers.<Set<? extends Image>> ofInstance(images), 3,
+ Atomics.<AuthorizationException> newReference(), Providers.of(getImageStrategy));
+ assertEquals(imageCache.get().size(), 1);
+
+ Optional<? extends Image> image = imageCache.get("foo");
+
+ // Load an image into the cache
+ assertTrue(image.isPresent());
+ assertEquals(image.get().getName(), "imageName-foo");
+ assertEquals(imageCache.get().size(), 2);
+
+ // Once the supplier expires, reloading it will laod the initial values
+ // (it is a hardcoded supplier), so the just loaded image should be gone
+ Uninterruptibles.sleepUninterruptibly(3, TimeUnit.SECONDS);
+ assertEquals(imageCache.get().size(), 1);
+ assertFalse(any(imageCache.get(), idEquals("foo")));
+ }
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/core/src/main/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java b/core/src/main/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java
index 0750a1f..a481e12 100644
--- a/core/src/main/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java
+++ b/core/src/main/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java
@@ -53,16 +53,18 @@ import com.google.common.util.concurrent.UncheckedExecutionException;
*/
public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> extends ForwardingObject implements
Supplier<T> {
-
+
static class SetAndThrowAuthorizationExceptionSupplierBackedLoader<V> extends CacheLoader<String, Optional<V>> {
private final Supplier<V> delegate;
private final AtomicReference<AuthorizationException> authException;
+ private final ValueLoadedCallback<V> valueLoadedCallback;
public SetAndThrowAuthorizationExceptionSupplierBackedLoader(Supplier<V> delegate,
- AtomicReference<AuthorizationException> authException) {
+ AtomicReference<AuthorizationException> authException, ValueLoadedCallback<V> valueLoadedCallback) {
this.delegate = checkNotNull(delegate, "delegate");
this.authException = checkNotNull(authException, "authException");
+ this.valueLoadedCallback = checkNotNull(valueLoadedCallback, "valueLoadedCallback");
}
@Override
@@ -70,7 +72,9 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> ext
if (authException.get() != null)
throw authException.get();
try {
- return Optional.fromNullable(delegate.get());
+ Optional<V> value = Optional.fromNullable(delegate.get());
+ valueLoadedCallback.valueLoaded(value);
+ return value;
} catch (Exception e) {
AuthorizationException aex = getFirstThrowableOfType(e, AuthorizationException.class);
if (aex != null) {
@@ -85,7 +89,24 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> ext
public String toString() {
return Objects.toStringHelper(this).add("delegate", delegate).toString();
}
+ }
+
+ public static class ValueLoadedEvent<V> {
+ private final Object eventKey;
+ private final Optional<V> value;
+
+ public ValueLoadedEvent(Object eventKey, Optional<V> value) {
+ this.eventKey = checkNotNull(eventKey, "eventKey");
+ this.value = checkNotNull(value, "value");
+ }
+
+ public Object getEventKey() {
+ return eventKey;
+ }
+ public Optional<V> getValue() {
+ return value;
+ }
}
private final Supplier<T> delegate;
@@ -96,16 +117,26 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> ext
public static <T> MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> create(
AtomicReference<AuthorizationException> authException, Supplier<T> delegate, long duration, TimeUnit unit) {
return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T>(authException, delegate, duration,
- unit);
+ unit, new ValueLoadedCallback.NoOpCallback<T>());
}
-
+
+ /**
+ * Creates a memoized supplier that calls the given callback each time values are loaded.
+ */
+ public static <T> MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> create(
+ AtomicReference<AuthorizationException> authException, Supplier<T> delegate, long duration, TimeUnit unit,
+ ValueLoadedCallback<T> valueLoadedCallback) {
+ return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T>(authException, delegate, duration,
+ unit, valueLoadedCallback);
+ }
+
MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier(AtomicReference<AuthorizationException> authException,
- Supplier<T> delegate, long duration, TimeUnit unit) {
+ Supplier<T> delegate, long duration, TimeUnit unit, ValueLoadedCallback<T> valueLoadedCallback) {
this.delegate = delegate;
this.duration = duration;
this.unit = unit;
this.cache = CacheBuilder.newBuilder().expireAfterWrite(duration, unit)
- .build(new SetAndThrowAuthorizationExceptionSupplierBackedLoader<T>(delegate, authException));
+ .build(new SetAndThrowAuthorizationExceptionSupplierBackedLoader<T>(delegate, authException, valueLoadedCallback));
}
@Override
http://git-wip-us.apache.org/repos/asf/jclouds/blob/40f31786/core/src/main/java/org/jclouds/rest/suppliers/ValueLoadedCallback.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/jclouds/rest/suppliers/ValueLoadedCallback.java b/core/src/main/java/org/jclouds/rest/suppliers/ValueLoadedCallback.java
new file mode 100644
index 0000000..dc45f0f
--- /dev/null
+++ b/core/src/main/java/org/jclouds/rest/suppliers/ValueLoadedCallback.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.rest.suppliers;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+
+/**
+ * Callback that gets called every time the supplier loads new values.
+ */
+@Beta
+public interface ValueLoadedCallback<V> {
+
+ void valueLoaded(Optional<V> value);
+
+ /** The default implementation does nothing */
+ public static class NoOpCallback<V> implements ValueLoadedCallback<V> {
+ @Override
+ public void valueLoaded(Optional<V> value) {
+ // Do nothing
+ }
+ }
+}