diff --git a/antcontrib/src/main/java/org/jclouds/tools/ant/taskdefs/compute/ComputeTask.java b/antcontrib/src/main/java/org/jclouds/tools/ant/taskdefs/compute/ComputeTask.java index 7145b395ff..f5e9fd05ef 100644 --- a/antcontrib/src/main/java/org/jclouds/tools/ant/taskdefs/compute/ComputeTask.java +++ b/antcontrib/src/main/java/org/jclouds/tools/ant/taskdefs/compute/ComputeTask.java @@ -25,9 +25,6 @@ import static org.jclouds.tools.ant.taskdefs.compute.ComputeTaskUtils.ipOrEmptyS import java.io.IOException; import java.net.URI; -import java.util.Map; - -import org.jclouds.javax.annotation.Nullable; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; @@ -43,10 +40,12 @@ import org.jclouds.compute.domain.Template; import org.jclouds.compute.predicates.NodePredicates; import org.jclouds.domain.Location; import org.jclouds.http.HttpUtils; +import org.jclouds.javax.annotation.Nullable; import org.jclouds.util.CredentialUtils; import com.google.common.base.CaseFormat; import com.google.common.base.Splitter; +import com.google.common.cache.Cache; import com.google.common.collect.Iterables; import com.google.inject.Provider; @@ -56,7 +55,7 @@ import com.google.inject.Provider; */ public class ComputeTask extends Task { - private final Map computeMap; + private final Cache computeMap; private String provider; private String actions; private NodeElement nodeElement; @@ -72,7 +71,7 @@ public class ComputeTask extends Task { } }; - public ComputeTask(@Nullable Map computeMap) { + public ComputeTask(@Nullable Cache computeMap) { this.computeMap = computeMap != null ? computeMap : buildComputeMap(projectProvider); } @@ -88,7 +87,7 @@ public class ComputeTask extends Task { * makes a connection to the compute service and invokes */ public void execute() throws BuildException { - ComputeServiceContext context = computeMap.get(HttpUtils.createUri(provider)); + ComputeServiceContext context = computeMap.getUnchecked(HttpUtils.createUri(provider)); try { for (String action : Splitter.on(',').split(actions)) { diff --git a/antcontrib/src/main/java/org/jclouds/tools/ant/taskdefs/compute/ComputeTaskUtils.java b/antcontrib/src/main/java/org/jclouds/tools/ant/taskdefs/compute/ComputeTaskUtils.java index 1c36706f7e..06022ad5a2 100644 --- a/antcontrib/src/main/java/org/jclouds/tools/ant/taskdefs/compute/ComputeTaskUtils.java +++ b/antcontrib/src/main/java/org/jclouds/tools/ant/taskdefs/compute/ComputeTaskUtils.java @@ -22,7 +22,6 @@ import static org.jclouds.rest.RestContextFactory.getPropertiesFromResource; import java.io.IOException; import java.net.URI; -import java.util.Map; import java.util.NoSuchElementException; import java.util.Properties; import java.util.Set; @@ -43,11 +42,12 @@ import org.jclouds.ssh.jsch.config.JschSshClientModule; import org.jclouds.tools.ant.logging.config.AntLoggingModule; import com.google.common.base.Charsets; -import com.google.common.base.Function; import com.google.common.base.Splitter; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; -import com.google.common.collect.MapMaker; import com.google.common.io.Files; import com.google.inject.Module; import com.google.inject.Provider; @@ -67,12 +67,12 @@ public class ComputeTaskUtils { * allows access to the ant project to retrieve default properties needed for compute * providers. */ - static Map buildComputeMap(final Provider projectProvider) { - return new MapMaker().makeComputingMap(new Function() { + static Cache buildComputeMap(final Provider projectProvider) { + return CacheBuilder.newBuilder().build(new CacheLoader() { @SuppressWarnings("unchecked") @Override - public ComputeServiceContext apply(URI from) { + public ComputeServiceContext load(URI from) { Properties props = getPropertiesFromResource("/rest.properties"); props.putAll(projectProvider.get().getProperties()); // adding the properties to the factory will allow us to pass diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java index 3b9eb242c7..956e65c163 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java @@ -68,6 +68,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.base.Supplier; +import com.google.common.cache.Cache; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap.Builder; import com.google.common.collect.ImmutableSet; @@ -78,8 +79,8 @@ import com.google.common.collect.ImmutableSet; @Singleton public class EC2ComputeService extends BaseComputeService { private final EC2Client ec2Client; - private final Map credentialsMap; - private final Map securityGroupMap; + private final Cache credentialsMap; + private final Cache securityGroupMap; @Inject protected EC2ComputeService(ComputeServiceContext context, Map credentialStore, @@ -96,7 +97,7 @@ public class EC2ComputeService extends BaseComputeService { RunScriptOnNode.Factory runScriptOnNodeFactory, InitAdminAccess initAdminAccess, PersistNodeCredentials persistNodeCredentials, Timeouts timeouts, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, EC2Client ec2Client, - Map credentialsMap, @Named("SECURITY") Map securityGroupMap) { + Cache credentialsMap, @Named("SECURITY") Cache securityGroupMap) { super(context, credentialStore, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, nodeSuspended, @@ -116,7 +117,7 @@ public class EC2ComputeService extends BaseComputeService { try { ec2Client.getSecurityGroupServices().deleteSecurityGroupInRegion(region, groupName); // TODO: test this clear happens - securityGroupMap.remove(new RegionNameAndIngressRules(region, groupName, null, false)); + securityGroupMap.invalidate(new RegionNameAndIngressRules(region, groupName, null, false)); logger.debug("<< deleted securityGroup(%s)", groupName); } catch (IllegalStateException e) { logger.debug("<< inUse securityGroup(%s)", groupName); @@ -142,7 +143,7 @@ public class EC2ComputeService extends BaseComputeService { logger.debug(">> deleting keyPair(%s)", keyPair.getKeyName()); ec2Client.getKeyPairServices().deleteKeyPairInRegion(region, keyPair.getKeyName()); // TODO: test this clear happens - credentialsMap.remove(new RegionAndName(region, keyPair.getKeyName())); + credentialsMap.invalidate(new RegionAndName(region, keyPair.getKeyName())); logger.debug("<< deleted keyPair(%s)", keyPair.getKeyName()); } } 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 3b809ee3cf..3b0e839c4a 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 @@ -22,8 +22,6 @@ 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.Map; - import javax.inject.Named; import javax.inject.Singleton; @@ -37,6 +35,7 @@ import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExc import com.google.common.base.Splitter; import com.google.common.base.Supplier; +import com.google.common.cache.Cache; import com.google.inject.Provides; /** @@ -59,12 +58,12 @@ public class EC2ComputeServiceContextModule extends BaseComputeServiceContextMod @Provides @Singleton - protected Supplier> provideRegionAndNameToImageSupplierCache( + protected Supplier> provideRegionAndNameToImageSupplierCache( @Named(PROPERTY_SESSION_INTERVAL) long seconds, final RegionAndNameToImageSupplier supplier) { - return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>( - authException, seconds, new Supplier>() { + return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>( + authException, seconds, new Supplier>() { @Override - public Map get() { + public Cache get() { return supplier.get(); } }); diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java index af3f2dbd17..21f9f2f7c7 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java @@ -18,8 +18,6 @@ */ package org.jclouds.ec2.compute.config; -import static com.google.common.collect.Maps.newLinkedHashMap; - import java.security.SecureRandom; import java.util.Map; @@ -39,7 +37,6 @@ import org.jclouds.ec2.EC2AsyncClient; import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.compute.EC2ComputeService; import org.jclouds.ec2.compute.domain.RegionAndName; -import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; import org.jclouds.ec2.compute.functions.CreateSecurityGroupIfNeeded; import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair; import org.jclouds.ec2.compute.functions.CredentialsForInstance; @@ -55,8 +52,11 @@ import org.jclouds.rest.internal.RestContextImpl; import com.google.common.base.Function; 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.collect.ImmutableMap; -import com.google.common.collect.MapMaker; +import com.google.common.collect.Maps; import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.google.inject.Scopes; @@ -69,11 +69,11 @@ import com.google.inject.TypeLiteral; public class EC2ComputeServiceDependenciesModule extends AbstractModule { public static final Map instanceToNodeState = ImmutableMap - . builder().put(InstanceState.PENDING, NodeState.PENDING).put( - InstanceState.RUNNING, NodeState.RUNNING).put(InstanceState.SHUTTING_DOWN, NodeState.PENDING).put( - InstanceState.TERMINATED, NodeState.TERMINATED).put(InstanceState.STOPPING, NodeState.PENDING) - .put(InstanceState.STOPPED, NodeState.SUSPENDED).put(InstanceState.UNRECOGNIZED, NodeState.UNRECOGNIZED) - .build(); + . builder().put(InstanceState.PENDING, NodeState.PENDING) + .put(InstanceState.RUNNING, NodeState.RUNNING).put(InstanceState.SHUTTING_DOWN, NodeState.PENDING) + .put(InstanceState.TERMINATED, NodeState.TERMINATED).put(InstanceState.STOPPING, NodeState.PENDING) + .put(InstanceState.STOPPED, NodeState.SUSPENDED).put(InstanceState.UNRECOGNIZED, NodeState.UNRECOGNIZED) + .build(); @Singleton @Provides @@ -88,13 +88,13 @@ public class EC2ComputeServiceDependenciesModule extends AbstractModule { bind(ComputeService.class).to(EC2ComputeService.class); bind(new TypeLiteral>() { }).to(RunningInstanceToNodeMetadata.class); - bind(new TypeLiteral>() { + bind(new TypeLiteral>() { }).to(CredentialsForInstance.class); - bind(new TypeLiteral>() { + bind(new TypeLiteral>() { }).to(CreateSecurityGroupIfNeeded.class); - bind(new TypeLiteral>() { + bind(new TypeLiteral>() { }).to(CreateUniqueKeyPair.class); - bind(new TypeLiteral>() { + bind(new TypeLiteral>() { }).to(RegionAndIdToImage.class); bind(new TypeLiteral() { }).to(new TypeLiteral>() { @@ -120,25 +120,35 @@ public class EC2ComputeServiceDependenciesModule extends AbstractModule { @Provides @Singleton - protected final Map credentialsMap(Function in) { - // doesn't seem to clear when someone issues remove(key) - // return new MapMaker().makeComputingMap(in); - return newLinkedHashMap(); + protected Cache credentialsMap(CacheLoader in) { + return CacheBuilder.newBuilder().build(in); } @Provides @Singleton - @Named("SECURITY") - protected final Map securityGroupMap(Function in) { - // doesn't seem to clear when someone issues remove(key) - // return new MapMaker().makeComputingMap(in); - return newLinkedHashMap(); + protected Cache keypairMap(CacheLoader in) { + return CacheBuilder.newBuilder().build(in); } @Provides @Singleton - protected Map provideImageMap(Function regionAndIdToImage) { - return new MapMaker().makeComputingMap(regionAndIdToImage); + // keys that we know about. + protected Map knownKeys(){ + return Maps.newConcurrentMap(); } - + + @Provides + @Singleton + @Named("SECURITY") + protected Cache securityGroupMap(CacheLoader in) { + return CacheBuilder.newBuilder().build(in); + } + + @Provides + @Singleton + // keys that we know about. + protected Map knownImages(){ + return Maps.newConcurrentMap(); + } + } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateSecurityGroupIfNeeded.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateSecurityGroupIfNeeded.java index 10d7c6552c..030fdc95fa 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateSecurityGroupIfNeeded.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateSecurityGroupIfNeeded.java @@ -25,14 +25,15 @@ import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; +import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.ec2.EC2Client; +import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; import org.jclouds.ec2.domain.IpProtocol; import org.jclouds.ec2.domain.UserIdGroupPair; -import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.logging.Logger; -import com.google.common.base.Function; +import com.google.common.cache.CacheLoader; import com.google.common.collect.Iterables; /** @@ -40,7 +41,7 @@ import com.google.common.collect.Iterables; * @author Adrian Cole */ @Singleton -public class CreateSecurityGroupIfNeeded implements Function { +public class CreateSecurityGroupIfNeeded extends CacheLoader { @Resource @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; @@ -52,8 +53,9 @@ public class CreateSecurityGroupIfNeeded implements Function { +public class CreateUniqueKeyPair extends CacheLoader { @Resource @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; protected final EC2Client ec2Client; protected final Supplier randomSuffix; + protected final Map knownKeys; @Inject - public CreateUniqueKeyPair(EC2Client ec2Client, Supplier randomSuffix) { + public CreateUniqueKeyPair(Map knownKeys, EC2Client ec2Client, Supplier randomSuffix) { + this.knownKeys = knownKeys; this.ec2Client = ec2Client; this.randomSuffix = randomSuffix; } @Override - public KeyPair apply(RegionAndName from) { - return createNewKeyPairInRegion(from.getRegion(), from.getName()); + public KeyPair load(RegionAndName from) { + return knownKeys.containsKey(from) ? knownKeys.get(from) : createNewKeyPairInRegion(from.getRegion(), + from.getName()); } @VisibleForTesting diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CredentialsForInstance.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CredentialsForInstance.java index dfd357aaf5..11283a0a9f 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CredentialsForInstance.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CredentialsForInstance.java @@ -20,43 +20,44 @@ package org.jclouds.ec2.compute.functions; import static com.google.common.base.Preconditions.checkNotNull; -import java.util.Map; +import java.util.concurrent.ExecutionException; import javax.inject.Inject; import javax.inject.Singleton; -import org.jclouds.ec2.compute.domain.RegionAndName; -import org.jclouds.ec2.domain.KeyPair; -import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.compute.domain.Image; import org.jclouds.compute.strategy.PopulateDefaultLoginCredentialsForImageStrategy; import org.jclouds.domain.Credentials; +import org.jclouds.ec2.compute.domain.RegionAndName; +import org.jclouds.ec2.domain.KeyPair; +import org.jclouds.ec2.domain.RunningInstance; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Function; +import com.google.common.base.Supplier; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheLoader; /** * * @author Adrian Cole */ @Singleton -public class CredentialsForInstance implements Function { - private final Map credentialsMap; +public class CredentialsForInstance extends CacheLoader { + private final Cache credentialsMap; private final PopulateDefaultLoginCredentialsForImageStrategy credentialProvider; - private final Map imageForInstance; + private final Supplier> imageMap; @Inject - CredentialsForInstance(Map credentialsMap, - PopulateDefaultLoginCredentialsForImageStrategy credentialProvider, Map imageForInstance) { + CredentialsForInstance(Cache credentialsMap, + PopulateDefaultLoginCredentialsForImageStrategy credentialProvider, Supplier> imageMap) { this.credentialsMap = checkNotNull(credentialsMap, "credentialsMap"); this.credentialProvider = checkNotNull(credentialProvider, "credentialProvider"); - this.imageForInstance = imageForInstance; + this.imageMap = imageMap; } @Override - public Credentials apply(RunningInstance instance) { + public Credentials load(RunningInstance instance) throws ExecutionException { Credentials credentials = null;// default if no keypair exists - if (instance.getKeyName() != null) { credentials = new Credentials(getLoginAccountFor(instance), getPrivateKeyOrNull(instance)); } @@ -64,15 +65,15 @@ public class CredentialsForInstance implements Function { +public class RegionAndIdToImage extends CacheLoader { @Resource protected Logger logger = Logger.NULL; + private final Map knownImages; private final EC2ImageParser parser; private final EC2Client sync; @Inject - public RegionAndIdToImage(EC2ImageParser parser, EC2Client sync) { + public RegionAndIdToImage(Map knownImages, EC2ImageParser parser, EC2Client sync) { + this.knownImages = knownImages; this.parser = parser; this.sync = sync; } - public Image apply(RegionAndName key) { + @Override + public Image load(RegionAndName key) throws ExecutionException{ + if (knownImages.containsKey(key)) + return knownImages.get(key); try { org.jclouds.ec2.domain.Image image = Iterables.getOnlyElement(sync.getAMIServices() .describeImagesInRegion(key.getRegion(), imageIds(key.getName()))); return parser.apply(image); - } catch (NoSuchElementException e) { - logger.debug(message(key, e)); - return null; - } catch (ResourceNotFoundException e) { - logger.debug(message(key, e)); - return null; } catch (Exception e) { - logger.warn(e, message(key, e)); - return null; + throw new ExecutionException(message(key, e), e); } } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java index c72fa472ae..3d5835febe 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java @@ -30,11 +30,6 @@ import javax.annotation.Resource; import javax.inject.Inject; import javax.inject.Singleton; -import org.jclouds.ec2.compute.domain.RegionAndName; -import org.jclouds.ec2.domain.BlockDevice; -import org.jclouds.ec2.domain.InstanceState; -import org.jclouds.ec2.domain.RootDeviceType; -import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.collect.Memoized; import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.HardwareBuilder; @@ -46,6 +41,11 @@ import org.jclouds.compute.domain.Volume; import org.jclouds.compute.domain.internal.VolumeImpl; import org.jclouds.domain.Credentials; import org.jclouds.domain.Location; +import org.jclouds.ec2.compute.domain.RegionAndName; +import org.jclouds.ec2.domain.BlockDevice; +import org.jclouds.ec2.domain.InstanceState; +import org.jclouds.ec2.domain.RootDeviceType; +import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.logging.Logger; import org.jclouds.util.NullSafeCollections; @@ -53,8 +53,10 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.base.Supplier; +import com.google.common.cache.Cache; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import com.google.common.util.concurrent.UncheckedExecutionException; /** * @author Adrian Cole @@ -67,17 +69,17 @@ public class RunningInstanceToNodeMetadata implements Function> locations; protected final Supplier> hardware; - protected final Map instanceToImage; + protected final Supplier> imageMap; protected final Map credentialStore; protected final Map instanceToNodeState; @Inject protected RunningInstanceToNodeMetadata(Map instanceToNodeState, - Map credentialStore, Map instanceToImage, + Map credentialStore, Supplier> imageMap, @Memoized Supplier> locations, @Memoized Supplier> hardware) { this.locations = checkNotNull(locations, "locations"); this.hardware = checkNotNull(hardware, "hardware"); - this.instanceToImage = checkNotNull(instanceToImage, "instanceToImage"); + this.imageMap = checkNotNull(imageMap, "imageMap"); this.instanceToNodeState = checkNotNull(instanceToNodeState, "instanceToNodeState"); this.credentialStore = checkNotNull(credentialStore, "credentialStore"); } @@ -106,17 +108,13 @@ public class RunningInstanceToNodeMetadata implements Function imageMap; + private final Supplier> imageMap; @Inject protected EC2TemplateBuilderImpl(@Memoized Supplier> locations, @Memoized Supplier> images, @Memoized Supplier> sizes, Supplier defaultLocation, @Named("DEFAULT") Provider optionsProvider, - @Named("DEFAULT") Provider defaultTemplateProvider, Map imageMap) { + @Named("DEFAULT") Provider defaultTemplateProvider, Supplier> imageMap) { super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider); this.imageMap = imageMap; } @@ -67,11 +68,13 @@ public class EC2TemplateBuilderImpl extends TemplateBuilderImpl { "amazon image ids must include the region ( ex. us-east-1/ami-7ea24a17 ) you specified: " + imageId); RegionAndName key = new RegionAndName(regionName[0], regionName[1]); try { - return imageMap.get(key); + return imageMap.get().get(key); + } catch (ExecutionException e) { + throw new NoSuchElementException(String.format("could not get imageId(%s/%s)", key.getRegion(), key.getName())); + } catch (UncheckedExecutionException e) { + throw new NoSuchElementException(String.format("could not get imageId(%s/%s)", key.getRegion(), key.getName())); } catch (NullPointerException nex) { throw new NoSuchElementException(String.format("imageId(%s/%s) not found", key.getRegion(), key.getName())); - } catch (ComputationException nex) { - throw new NoSuchElementException(String.format("imageId(%s/%s) not found", key.getRegion(), key.getName())); } } return null; diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java index a852aed562..baca5d70ae 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java @@ -23,7 +23,6 @@ import static com.google.common.base.Preconditions.checkState; import java.util.Map; import java.util.Set; -import org.jclouds.javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Provider; @@ -37,11 +36,13 @@ import org.jclouds.ec2.compute.options.EC2TemplateOptions; import org.jclouds.ec2.domain.BlockDeviceMapping; import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.options.RunInstancesOptions; +import org.jclouds.javax.annotation.Nullable; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Function; +import com.google.common.cache.Cache; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet.Builder; +import com.google.common.util.concurrent.UncheckedExecutionException; /** * @@ -49,26 +50,21 @@ import com.google.common.collect.ImmutableSet.Builder; */ @Singleton public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions { - @VisibleForTesting - public final Map credentialsMap; + public final Map knownKeys; @VisibleForTesting - public final Map securityGroupMap; + public final Cache credentialsMap; @VisibleForTesting - public final Function createUniqueKeyPair; - @VisibleForTesting - public final Function createSecurityGroupIfNeeded; + public final Cache securityGroupMap; protected final Provider optionsProvider; @Inject - public CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions(Map credentialsMap, - @Named("SECURITY") Map securityGroupMap, Function createUniqueKeyPair, - Function createSecurityGroupIfNeeded, + public CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions(Map knownKeys, Cache credentialsMap, + @Named("SECURITY") Cache securityGroupMap, Provider optionsProvider) { + this.knownKeys=knownKeys; this.credentialsMap = credentialsMap; this.securityGroupMap = securityGroupMap; - this.createUniqueKeyPair = createUniqueKeyPair; - this.createSecurityGroupIfNeeded = createSecurityGroupIfNeeded; this.optionsProvider = optionsProvider; } @@ -123,40 +119,32 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions { if (options.getOverridingCredentials() != null && options.getOverridingCredentials().credential != null) { KeyPair keyPair = KeyPair.builder().region(region).keyName(keyPairName).keyFingerprint("//TODO") .keyMaterial(options.getOverridingCredentials().credential).build(); - putKeyPairIntoCredentialMap(keyPair); + + RegionAndName key = new RegionAndName(region, group); + knownKeys.put(key, keyPair); + credentialsMap.invalidate(key); } } if (options.getRunScript() != null) { RegionAndName regionAndName = new RegionAndName(region, keyPairName); - checkState(credentialsMap.containsKey(regionAndName), - "no private key configured for: %s; please use options.overrideLoginCredentialWith(rsa_private_text)", - regionAndName); + String message = String.format("no private key configured for: %s; please use options.overrideLoginCredentialWith(rsa_private_text)", + regionAndName); + // test to see if this is in cache. + try { + credentialsMap.getUnchecked(regionAndName); + } catch (NullPointerException nex) { + throw new IllegalArgumentException(message, nex); + } catch (UncheckedExecutionException nex) { + throw new IllegalArgumentException(message, nex); + } } - return keyPairName; } // base EC2 driver currently does not support key import protected String createOrImportKeyPair(String region, String group, TemplateOptions options) { - return createUniqueKeyPairAndPutIntoMap(region, group); - } - - protected String createUniqueKeyPairAndPutIntoMap(String region, String group) { - RegionAndName regionAndName = new RegionAndName(region, group); - KeyPair keyPair = createUniqueKeyPair.apply(regionAndName); - putKeyPairIntoCredentialMap(keyPair); - return keyPair.getKeyName(); - } - - protected void putKeyPairIntoCredentialMap(KeyPair keyPair) { - // get or create incidental resources - // TODO race condition. we were using MapMaker, but it doesn't seem to - // refresh properly - // when - // another thread - // deletes a key - credentialsMap.put(new RegionAndName(keyPair.getRegion(), keyPair.getKeyName()), keyPair); + return credentialsMap.getUnchecked(new RegionAndName(region, group)).getKeyName(); } @VisibleForTesting @@ -177,11 +165,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions { regionNameAndIngessRulesForMarkerGroup = new RegionNameAndIngressRules(region, markerGroup, options.getInboundPorts(), true); } - - if (!securityGroupMap.containsKey(regionNameAndIngessRulesForMarkerGroup)) { - securityGroupMap.put(regionNameAndIngessRulesForMarkerGroup, - createSecurityGroupIfNeeded.apply(regionNameAndIngessRulesForMarkerGroup)); - } + // this will create if not yet exists. + securityGroupMap.getUnchecked(regionNameAndIngessRulesForMarkerGroup); } return groups.build(); } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSet.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSet.java index 406df0d08e..75cba22a5e 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSet.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSet.java @@ -52,6 +52,7 @@ import org.jclouds.logging.Logger; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Joiner; +import com.google.common.cache.Cache; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; @@ -77,7 +78,7 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen @VisibleForTesting final ComputeUtils utils; final InstancePresent instancePresent; - final Function instanceToCredentials; + final Cache instanceToCredentials; final Map credentialStore; final Provider templateBuilderProvider; @@ -87,7 +88,7 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen Provider templateBuilderProvider, CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, InstancePresent instancePresent, Function runningInstanceToNodeMetadata, - Function instanceToCredentials, Map credentialStore, + Cache instanceToCredentials, Map credentialStore, ComputeUtils utils) { this.client = checkNotNull(client, "client"); this.templateBuilderProvider = checkNotNull(templateBuilderProvider, "templateBuilderProvider"); diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/suppliers/EC2ImageSupplier.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/suppliers/EC2ImageSupplier.java index a619c0c11e..3ef6d2ba88 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/suppliers/EC2ImageSupplier.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/suppliers/EC2ImageSupplier.java @@ -18,16 +18,16 @@ */ package org.jclouds.ec2.compute.suppliers; -import java.util.Map; import java.util.Set; import javax.inject.Inject; import javax.inject.Singleton; -import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.compute.domain.Image; +import org.jclouds.ec2.compute.domain.RegionAndName; import com.google.common.base.Supplier; +import com.google.common.cache.Cache; import com.google.common.collect.Sets; /** @@ -36,16 +36,16 @@ import com.google.common.collect.Sets; */ @Singleton public class EC2ImageSupplier implements Supplier> { - private final Supplier> map; + private final Supplier> map; @Inject - EC2ImageSupplier(Supplier> map) { + EC2ImageSupplier(Supplier> map) { this.map = map; } @Override public Set get() { - return Sets.newLinkedHashSet(map.get().values()); + return Sets.newLinkedHashSet(map.get().asMap().values()); } } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/suppliers/RegionAndNameToImageSupplier.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/suppliers/RegionAndNameToImageSupplier.java index 72a28f3e82..65dbcceb90 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/suppliers/RegionAndNameToImageSupplier.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/suppliers/RegionAndNameToImageSupplier.java @@ -25,8 +25,8 @@ import static org.jclouds.ec2.options.DescribeImagesOptions.Builder.ownedBy; import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS; import java.util.Map; -import java.util.Set; import java.util.Map.Entry; +import java.util.Set; import javax.annotation.Resource; import javax.inject.Inject; @@ -45,16 +45,19 @@ import org.jclouds.logging.Logger; import com.google.common.base.Function; import com.google.common.base.Predicates; 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.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableMap.Builder; +import com.google.common.collect.ImmutableSet; /** * * @author Adrian Cole */ @Singleton -public class RegionAndNameToImageSupplier implements Supplier> { +public class RegionAndNameToImageSupplier implements Supplier> { @Resource @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; @@ -63,20 +66,23 @@ public class RegionAndNameToImageSupplier implements Supplier images; + private final Map knownImages; + private final CacheLoader regionAndIdToImage; @Inject protected RegionAndNameToImageSupplier(@Region Set regions, DescribeImagesParallel describer, - @Named(PROPERTY_EC2_AMI_OWNERS) String[] amiOwners, EC2ImageParser parser, Map images) { + @Named(PROPERTY_EC2_AMI_OWNERS) String[] amiOwners, EC2ImageParser parser, + Map knownImages, CacheLoader regionAndIdToImage) { this.regions = regions; this.describer = describer; this.amiOwners = amiOwners; this.parser = parser; - this.images = images; + this.knownImages = knownImages; + this.regionAndIdToImage = regionAndIdToImage; } @Override - public Map get() { + public Cache get() { if (amiOwners.length == 0) { logger.debug(">> no owners specified, skipping image parsing"); } else { @@ -87,8 +93,8 @@ public class RegionAndNameToImageSupplier implements Supplier parsedImages = ImmutableSet.copyOf(filter(transform(describer.apply(queries), parser), Predicates .notNull())); - - images.putAll(uniqueIndex(parsedImages, new Function() { + knownImages.clear(); + knownImages.putAll(uniqueIndex(parsedImages, new Function() { @Override public RegionAndName apply(Image from) { @@ -96,14 +102,18 @@ public class RegionAndNameToImageSupplier implements Supplier cache = CacheBuilder.newBuilder().build(regionAndIdToImage); + // seed the cache + for (RegionAndName image : knownImages.keySet()) { + cache.getUnchecked(image); + } + return cache; } public Iterable> getDescribeQueriesForOwnersInRegions(Set regions, - String[] amiOwners) { + String[] amiOwners) { DescribeImagesOptions options = getOptionsForOwners(amiOwners); Builder builder = ImmutableMap. builder(); for (String region : regions) diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/CreateUniqueKeyPairTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/CreateUniqueKeyPairTest.java index aa7fd1a3a9..a03a77aacf 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/CreateUniqueKeyPairTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/CreateUniqueKeyPairTest.java @@ -25,8 +25,10 @@ import static org.easymock.classextension.EasyMock.verify; import static org.testng.Assert.assertEquals; import java.net.UnknownHostException; +import java.util.Map; import org.jclouds.ec2.EC2Client; +import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.services.KeyPairClient; import org.testng.annotations.Test; @@ -38,58 +40,127 @@ import com.google.common.base.Supplier; */ @Test(groups = "unit") public class CreateUniqueKeyPairTest { - @SuppressWarnings( { "unchecked" }) + @SuppressWarnings({ "unchecked" }) @Test public void testApply() throws UnknownHostException { EC2Client client = createMock(EC2Client.class); KeyPairClient keyClient = createMock(KeyPairClient.class); Supplier uniqueIdSupplier = createMock(Supplier.class); + Map knownKeys = createMock(Map.class); KeyPair pair = createMock(KeyPair.class); expect(client.getKeyPairServices()).andReturn(keyClient).atLeastOnce(); expect(uniqueIdSupplier.get()).andReturn("1"); - expect(keyClient.createKeyPairInRegion("region", "jclouds#tag#region#1")).andReturn(pair); + expect(keyClient.createKeyPairInRegion("region", "jclouds#group#region#1")).andReturn(pair); replay(client); + replay(knownKeys); replay(keyClient); replay(uniqueIdSupplier); - CreateUniqueKeyPair parser = new CreateUniqueKeyPair(client, uniqueIdSupplier); + CreateUniqueKeyPair parser = new CreateUniqueKeyPair(knownKeys, client, uniqueIdSupplier); - assertEquals(parser.createNewKeyPairInRegion("region", "tag"), pair); + assertEquals(parser.createNewKeyPairInRegion("region", "group"), pair); verify(client); + verify(knownKeys); verify(keyClient); verify(uniqueIdSupplier); } - @SuppressWarnings( { "unchecked" }) + @SuppressWarnings({ "unchecked" }) @Test public void testApplyWithIllegalStateException() throws UnknownHostException { EC2Client client = createMock(EC2Client.class); KeyPairClient keyClient = createMock(KeyPairClient.class); Supplier uniqueIdSupplier = createMock(Supplier.class); + Map knownKeys = createMock(Map.class); KeyPair pair = createMock(KeyPair.class); expect(client.getKeyPairServices()).andReturn(keyClient).atLeastOnce(); expect(uniqueIdSupplier.get()).andReturn("1"); - expect(keyClient.createKeyPairInRegion("region", "jclouds#tag#region#1")).andThrow(new IllegalStateException()); + expect(keyClient.createKeyPairInRegion("region", "jclouds#group#region#1")).andThrow(new IllegalStateException()); expect(uniqueIdSupplier.get()).andReturn("2"); - expect(keyClient.createKeyPairInRegion("region", "jclouds#tag#region#2")).andReturn(pair); + expect(keyClient.createKeyPairInRegion("region", "jclouds#group#region#2")).andReturn(pair); replay(client); + replay(knownKeys); replay(keyClient); replay(uniqueIdSupplier); - CreateUniqueKeyPair parser = new CreateUniqueKeyPair(client, uniqueIdSupplier); + CreateUniqueKeyPair parser = new CreateUniqueKeyPair(knownKeys, client, uniqueIdSupplier); - assertEquals(parser.createNewKeyPairInRegion("region", "tag"), pair); + assertEquals(parser.createNewKeyPairInRegion("region", "group"), pair); verify(client); + verify(knownKeys); + verify(keyClient); + verify(uniqueIdSupplier); + + } + + @SuppressWarnings({ "unchecked" }) + @Test + public void testApplyWhenKnownKeyExists() throws UnknownHostException { + EC2Client client = createMock(EC2Client.class); + KeyPairClient keyClient = createMock(KeyPairClient.class); + Supplier uniqueIdSupplier = createMock(Supplier.class); + Map knownKeys = createMock(Map.class); + + KeyPair pair = createMock(KeyPair.class); + + expect(knownKeys.containsKey(new RegionAndName("region", "group"))).andReturn(true); + expect(knownKeys.get(new RegionAndName("region", "group"))).andReturn(pair); + + replay(client); + replay(knownKeys); + replay(keyClient); + replay(uniqueIdSupplier); + + CreateUniqueKeyPair parser = new CreateUniqueKeyPair(knownKeys, client, uniqueIdSupplier); + + assertEquals(parser.load(new RegionAndName("region", "group")), pair); + + verify(client); + verify(knownKeys); + verify(keyClient); + verify(uniqueIdSupplier); + + } + + @SuppressWarnings({ "unchecked" }) + @Test + public void testApplyWhenKnownKeyDoesntExist() throws UnknownHostException { + EC2Client client = createMock(EC2Client.class); + KeyPairClient keyClient = createMock(KeyPairClient.class); + Supplier uniqueIdSupplier = createMock(Supplier.class); + Map knownKeys = createMock(Map.class); + + KeyPair pair = createMock(KeyPair.class); + + expect(client.getKeyPairServices()).andReturn(keyClient).atLeastOnce(); + + expect(knownKeys.containsKey(new RegionAndName("region", "group"))).andReturn(false); + expect(uniqueIdSupplier.get()).andReturn("1"); + expect(keyClient.createKeyPairInRegion("region", "jclouds#group#region#1")).andThrow(new IllegalStateException()); + expect(uniqueIdSupplier.get()).andReturn("2"); + expect(keyClient.createKeyPairInRegion("region", "jclouds#group#region#2")).andReturn(pair); + + replay(client); + replay(knownKeys); + replay(keyClient); + replay(uniqueIdSupplier); + + CreateUniqueKeyPair parser = new CreateUniqueKeyPair(knownKeys, client, uniqueIdSupplier); + + assertEquals(parser.load(new RegionAndName("region", "group")), pair); + + verify(client); + verify(knownKeys); verify(keyClient); verify(uniqueIdSupplier); diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/RegionAndIdToImageTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/RegionAndIdToImageTest.java index 9abaab87d8..1d1430ad5d 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/RegionAndIdToImageTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/RegionAndIdToImageTest.java @@ -26,13 +26,15 @@ import static org.easymock.classextension.EasyMock.verify; import static org.jclouds.ec2.options.DescribeImagesOptions.Builder.imageIds; import static org.testng.Assert.assertEquals; +import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; +import java.util.concurrent.ExecutionException; +import org.jclouds.compute.domain.Image; import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.services.AMIClient; -import org.jclouds.compute.domain.Image; import org.jclouds.rest.ResourceNotFoundException; import org.testng.annotations.Test; @@ -46,46 +48,85 @@ public class RegionAndIdToImageTest { @SuppressWarnings("unchecked") @Test - public void testApply() { + public void testApply() throws ExecutionException { EC2ImageParser parser = createMock(EC2ImageParser.class); EC2Client caller = createMock(EC2Client.class); AMIClient client = createMock(AMIClient.class); + Map knownImages = createMock(Map.class); + org.jclouds.ec2.domain.Image ec2Image = createMock(org.jclouds.ec2.domain.Image.class); Image image = createNiceMock(Image.class); Set images = ImmutableSet. of(ec2Image); + expect(knownImages.containsKey(new RegionAndName("region", "ami"))).andReturn(false); expect(caller.getAMIServices()).andReturn(client).atLeastOnce(); expect(client.describeImagesInRegion("region", imageIds("ami"))).andReturn(Set.class.cast(images)); expect(parser.apply(ec2Image)).andReturn(image); + replay(knownImages); replay(caller); replay(image); replay(parser); replay(client); - RegionAndIdToImage function = new RegionAndIdToImage(parser, caller); + RegionAndIdToImage function = new RegionAndIdToImage(knownImages, parser, caller); - assertEquals(function.apply(new RegionAndName("region", "ami")), image); + assertEquals(function.load(new RegionAndName("region", "ami")), image); verify(caller); verify(image); - verify(parser); + verify(image); + verify(knownImages); verify(client); } @SuppressWarnings("unchecked") @Test - public void testApplyNotFound() { + public void testApplyWhenFoundDoesntCallClient() throws ExecutionException { EC2ImageParser parser = createMock(EC2ImageParser.class); EC2Client caller = createMock(EC2Client.class); AMIClient client = createMock(AMIClient.class); + Map knownImages = createMock(Map.class); + + Image image = createNiceMock(Image.class); + + expect(knownImages.containsKey(new RegionAndName("region", "ami"))).andReturn(true); + expect(knownImages.get(new RegionAndName("region", "ami"))).andReturn(image); + + replay(knownImages); + replay(caller); + replay(image); + replay(parser); + replay(client); + + RegionAndIdToImage function = new RegionAndIdToImage(knownImages, parser, caller); + + assertEquals(function.load(new RegionAndName("region", "ami")), image); + + verify(caller); + verify(image); + verify(image); + verify(knownImages); + verify(client); + + } + @SuppressWarnings("unchecked") + @Test(expectedExceptions = ExecutionException.class) + public void testApplyNotFoundMakesExecutionException() throws ExecutionException { + + EC2ImageParser parser = createMock(EC2ImageParser.class); + EC2Client caller = createMock(EC2Client.class); + AMIClient client = createMock(AMIClient.class); + Map knownImages = createMock(Map.class); + org.jclouds.ec2.domain.Image ec2Image = createMock(org.jclouds.ec2.domain.Image.class); Image image = createNiceMock(Image.class); Set images = ImmutableSet. of(ec2Image); + expect(knownImages.containsKey(new RegionAndName("region", "ami"))).andReturn(false); expect(caller.getAMIServices()).andReturn(client).atLeastOnce(); expect(client.describeImagesInRegion("region", imageIds("ami"))).andReturn(Set.class.cast(images)); expect(parser.apply(ec2Image)).andThrow(new ResourceNotFoundException()); @@ -93,45 +134,52 @@ public class RegionAndIdToImageTest { replay(caller); replay(image); replay(parser); + replay(knownImages); replay(client); - RegionAndIdToImage function = new RegionAndIdToImage(parser, caller); + RegionAndIdToImage function = new RegionAndIdToImage(knownImages, parser, caller); - assertEquals(function.apply(new RegionAndName("region", "ami")), null); + assertEquals(function.load(new RegionAndName("region", "ami")), null); verify(caller); verify(image); verify(parser); + verify(knownImages); verify(client); } @SuppressWarnings("unchecked") - @Test - public void testApplyNoSuchElementException() { + @Test(expectedExceptions = ExecutionException.class) + public void testApplyNoSuchElementExceptionMakesExecutionException() throws ExecutionException { EC2ImageParser parser = createMock(EC2ImageParser.class); EC2Client caller = createMock(EC2Client.class); AMIClient client = createMock(AMIClient.class); + Map knownImages = createMock(Map.class); + org.jclouds.ec2.domain.Image ec2Image = createMock(org.jclouds.ec2.domain.Image.class); Image image = createNiceMock(Image.class); Set images = ImmutableSet. of(ec2Image); + expect(knownImages.containsKey(new RegionAndName("region", "ami"))).andReturn(false); expect(caller.getAMIServices()).andReturn(client).atLeastOnce(); expect(client.describeImagesInRegion("region", imageIds("ami"))).andReturn(Set.class.cast(images)); expect(parser.apply(ec2Image)).andThrow(new NoSuchElementException()); replay(caller); replay(image); + replay(knownImages); replay(parser); replay(client); - RegionAndIdToImage function = new RegionAndIdToImage(parser, caller); + RegionAndIdToImage function = new RegionAndIdToImage(knownImages, parser, caller); - assertEquals(function.apply(new RegionAndName("region", "ami")), null); + assertEquals(function.load(new RegionAndName("region", "ami")), null); verify(caller); verify(image); + verify(knownImages); verify(parser); verify(client); diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadataTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadataTest.java index a5c9ef37c8..50a1eec4da 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadataTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadataTest.java @@ -25,8 +25,6 @@ import java.net.UnknownHostException; import java.util.Map; import java.util.Set; -import org.jclouds.javax.annotation.Nullable; - import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadata; @@ -43,14 +41,18 @@ import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.domain.InstanceState; import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.ec2.xml.DescribeInstancesResponseHandlerTest; +import org.jclouds.javax.annotation.Nullable; import org.testng.annotations.Test; import com.google.common.base.Function; import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; -import com.google.common.collect.MapMaker; import com.google.common.collect.Maps; /** @@ -167,14 +169,14 @@ public class RunningInstanceToNodeMetadataTest { // Handle the case when the installed AMI no longer can be found in AWS. // Create a null-returning function to simulate that the AMI can't be found. - Function nullReturningFunction = new Function() { + CacheLoader nullReturningFunction = new CacheLoader() { @Override - public Image apply(@Nullable RegionAndName from) { + public Image load(@Nullable RegionAndName from) { return null; } }; - Map instanceToImage = new MapMaker().makeComputingMap(nullReturningFunction); + Cache instanceToImage = CacheBuilder.newBuilder().build(nullReturningFunction); RunningInstanceToNodeMetadata parser = createNodeParser(ImmutableSet.of(m1_small32().build()), ImmutableSet .of(provider), ImmutableMap. of(), @@ -197,25 +199,31 @@ public class RunningInstanceToNodeMetadataTest { } protected RunningInstanceToNodeMetadata createNodeParser(final ImmutableSet hardware, - final ImmutableSet locations, Set images, + final ImmutableSet locations, final Set images, Map credentialStore) { Map instanceToNodeState = EC2ComputeServiceDependenciesModule.instanceToNodeState; - - Map instanceToImage = Maps.uniqueIndex(images, new Function() { + + CacheLoader getRealImage = new CacheLoader() { @Override - public RegionAndName apply(Image from) { - return new RegionAndName(from.getLocation().getId(), from.getProviderId()); + public Image load(@Nullable RegionAndName from) { + return Maps.uniqueIndex(images, new Function() { + + @Override + public RegionAndName apply(Image from) { + return new RegionAndName(from.getLocation().getId(), from.getProviderId()); + } + + }).get(from); } - - }); - + }; + Cache instanceToImage = CacheBuilder.newBuilder().build(getRealImage); return createNodeParser(hardware, locations, credentialStore, instanceToNodeState, instanceToImage); } private RunningInstanceToNodeMetadata createNodeParser(final ImmutableSet hardware, final ImmutableSet locations, Map credentialStore, - Map instanceToNodeState, Map instanceToImage) { + Map instanceToNodeState, Cache instanceToImage) { Supplier> locationSupplier = new Supplier>() { @Override @@ -233,7 +241,8 @@ public class RunningInstanceToNodeMetadataTest { }; RunningInstanceToNodeMetadata parser = new RunningInstanceToNodeMetadata(instanceToNodeState, credentialStore, - instanceToImage, locationSupplier, hardwareSupplier); + Suppliers.> ofInstance(instanceToImage), locationSupplier, + hardwareSupplier); return parser; } 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 48d19f2ea4..9be5825d5d 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 @@ -27,7 +27,6 @@ import static org.testng.Assert.assertEquals; import java.util.NoSuchElementException; import java.util.Set; -import java.util.concurrent.ConcurrentMap; import javax.inject.Provider; @@ -46,11 +45,12 @@ import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.options.EC2TemplateOptions; import org.testng.annotations.Test; -import com.google.common.base.Function; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.MapMaker; import com.google.common.collect.Sets; /** @@ -67,22 +67,21 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest { @Override protected EC2TemplateBuilderImpl createTemplateBuilder(final Image knownImage, - @Memoized Supplier> locations, @Memoized Supplier> images, - @Memoized Supplier> sizes, Location defaultLocation, - Provider optionsProvider, Provider templateBuilderProvider) { + @Memoized Supplier> locations, @Memoized Supplier> images, + @Memoized Supplier> sizes, Location defaultLocation, + Provider optionsProvider, Provider templateBuilderProvider) { final RegionAndName knownRegionAndName = new RegionAndName("region", "ami"); - ConcurrentMap imageMap = new MapMaker() - .makeComputingMap(new Function() { - @Override - public Image apply(RegionAndName from) { - return from.equals(knownRegionAndName) ? knownImage : null; - } + Cache imageMap = CacheBuilder.newBuilder().build(new CacheLoader() { + @Override + public Image load(RegionAndName from) { + return from.equals(knownRegionAndName) ? knownImage : null; + } - }); + }); return new EC2TemplateBuilderImpl(locations, images, sizes, Suppliers.ofInstance(defaultLocation), - optionsProvider, templateBuilderProvider, imageMap); + optionsProvider, templateBuilderProvider, Suppliers.>ofInstance(imageMap)); } @SuppressWarnings("unchecked") @@ -91,11 +90,11 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest { Location location = new LocationBuilder().scope(LocationScope.REGION).id("region").description("region").build(); Supplier> locations = Suppliers.> ofInstance(ImmutableSet - . of(location)); + . of(location)); Supplier> images = Suppliers.> ofInstance(Sets - . newLinkedHashSet()); + . newLinkedHashSet()); Supplier> sizes = Suppliers.> ofInstance(ImmutableSet - . of(c1_medium().build())); + . of(c1_medium().build())); Provider optionsProvider = createMock(Provider.class); Provider templateBuilderProvider = createMock(Provider.class); @@ -127,7 +126,7 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest { replay(templateBuilderProvider); TemplateBuilderImpl template = createTemplateBuilder(knownImage, locations, images, sizes, location, - optionsProvider, templateBuilderProvider); + optionsProvider, templateBuilderProvider); assertEquals(template.imageId("region/ami").build().getImage(), knownImage); @@ -144,10 +143,10 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest { Location location = new LocationBuilder().scope(LocationScope.REGION).id("region").description("region").build(); Supplier> locations = Suppliers.> ofInstance(ImmutableSet - . of(location)); + . of(location)); Supplier> images = Suppliers.> ofInstance(ImmutableSet. of()); Supplier> sizes = Suppliers.> ofInstance(ImmutableSet - . of(c1_medium().build())); + . of(c1_medium().build())); Provider optionsProvider = createMock(Provider.class); Provider templateBuilderProvider = createMock(Provider.class); @@ -162,7 +161,7 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest { replay(templateBuilderProvider); TemplateBuilderImpl template = createTemplateBuilder(knownImage, locations, images, sizes, location, - optionsProvider, templateBuilderProvider); + optionsProvider, templateBuilderProvider); try { template.imageId("ami").build(); assert false; @@ -181,10 +180,10 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest { Location location = new LocationBuilder().scope(LocationScope.REGION).id("region").description("region").build(); Supplier> locations = Suppliers.> ofInstance(ImmutableSet - . of(location)); + . of(location)); Supplier> images = Suppliers.> ofInstance(ImmutableSet. of()); Supplier> sizes = Suppliers.> ofInstance(ImmutableSet - . of(c1_medium().build())); + . of(c1_medium().build())); Location defaultLocation = createMock(Location.class); Provider optionsProvider = createMock(Provider.class); @@ -202,7 +201,7 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest { replay(templateBuilderProvider); TemplateBuilderImpl template = createTemplateBuilder(knownImage, locations, images, sizes, defaultLocation, - optionsProvider, templateBuilderProvider); + optionsProvider, templateBuilderProvider); assertEquals(template.imageId("region/bad").build().getImage(), knownImage); diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java index c3550d9240..bc7412e6e5 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java @@ -27,6 +27,7 @@ import static org.testng.Assert.assertEquals; import java.lang.reflect.Method; import java.util.Map; import java.util.Set; +import java.util.concurrent.ExecutionException; import javax.inject.Provider; @@ -38,8 +39,6 @@ import org.jclouds.domain.Credentials; import org.jclouds.ec2.compute.domain.EC2HardwareBuilder; import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; -import org.jclouds.ec2.compute.functions.CreateSecurityGroupIfNeeded; -import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair; import org.jclouds.ec2.compute.options.EC2TemplateOptions; import org.jclouds.ec2.domain.BlockDeviceMapping; import org.jclouds.ec2.domain.KeyPair; @@ -48,14 +47,14 @@ import org.jclouds.encryption.internal.Base64; import org.jclouds.scriptbuilder.domain.Statements; import org.testng.annotations.Test; -import com.google.common.base.Function; +import com.google.common.cache.Cache; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; /** * @author Adrian Cole */ -@Test(groups = "unit") +@Test(groups = "unit", singleThreaded = true, testName = "CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest") public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { private static final Provider OPTIONS_PROVIDER = new javax.inject.Provider() { @@ -209,7 +208,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { verifyStrategy(strategy); } - @Test(expectedExceptions = IllegalStateException.class) + @Test(expectedExceptions = IllegalArgumentException.class) public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_reusesKeyWhenToldToWithRunScriptButNoCredentials() { // setup constants String region = Region.AP_SOUTHEAST_1; @@ -225,7 +224,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.getOverridingCredentials()).andReturn(null); expect(options.getRunScript()).andReturn(Statements.exec("echo foo")); - expect(strategy.credentialsMap.containsKey(new RegionAndName(region, userSuppliedKeyPair))).andReturn(false); + + expect(strategy.credentialsMap.getUnchecked(new RegionAndName(region, userSuppliedKeyPair))).andThrow(new NullPointerException()); // replay mocks replay(options); @@ -256,7 +256,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.getOverridingCredentials()).andReturn(null); expect(options.getRunScript()).andReturn(Statements.exec("echo foo")); - expect(strategy.credentialsMap.containsKey(new RegionAndName(region, userSuppliedKeyPair))).andReturn(true); + + expect(strategy.credentialsMap.getUnchecked(new RegionAndName(region, userSuppliedKeyPair))).andReturn(keyPair); // replay mocks replay(options); @@ -287,11 +288,13 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.getOverridingCredentials()).andReturn(new Credentials(null, "MyRsa")).atLeastOnce(); expect( - strategy.credentialsMap.put(new RegionAndName(region, userSuppliedKeyPair), KeyPair.builder() - .region(region).keyName(userSuppliedKeyPair).keyFingerprint("//TODO").keyMaterial("MyRsa").build())) - .andReturn(null); + strategy.knownKeys.put( + new RegionAndName(region, tag), + KeyPair.builder().region(region).keyName(userSuppliedKeyPair).keyFingerprint("//TODO") + .keyMaterial("MyRsa").build())).andReturn(null); + strategy.credentialsMap.invalidate(new RegionAndName(region, tag)); expect(options.getRunScript()).andReturn(Statements.exec("echo foo")); - expect(strategy.credentialsMap.containsKey(new RegionAndName(region, userSuppliedKeyPair))).andReturn(true); + expect(strategy.credentialsMap.getUnchecked(new RegionAndName(region, userSuppliedKeyPair))).andReturn(keyPair); // replay mocks replay(options); @@ -307,7 +310,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { verifyStrategy(strategy); } - public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_createsNewKeyPairAndReturnsItsNameByDefault() { + public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_createsNewKeyPairAndReturnsItsNameByDefault() + throws ExecutionException { // setup constants String region = Region.AP_SOUTHEAST_1; String tag = "tag"; @@ -323,11 +327,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { // setup expectations expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.shouldAutomaticallyCreateKeyPair()).andReturn(shouldAutomaticallyCreateKeyPair); - expect(strategy.createUniqueKeyPair.apply(new RegionAndName(region, tag))).andReturn(keyPair); - expect(keyPair.getRegion()).andReturn(region).atLeastOnce(); expect(keyPair.getKeyName()).andReturn(systemGeneratedKeyPairName).atLeastOnce(); - expect(strategy.credentialsMap.put(new RegionAndName(region, systemGeneratedKeyPairName), keyPair)).andReturn( - null); + expect(strategy.credentialsMap.getUnchecked(new RegionAndName(region, tag))).andReturn(keyPair); expect(options.getRunScript()).andReturn(null); // replay mocks @@ -377,7 +378,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { verifyStrategy(strategy); } - public void testGetSecurityGroupsForTagAndOptions_createsNewGroupByDefaultWhenNoPortsAreSpecifiedWhenDoesntExist() { + public void testGetSecurityGroupsForTagAndOptions_createsNewGroupByDefaultWhenNoPortsAreSpecifiedWhenDoesntExist() + throws ExecutionException { // setup constants String region = Region.AP_SOUTHEAST_1; String tag = "tag"; @@ -385,7 +387,6 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { Set groupIds = ImmutableSet. of(); int[] ports = new int[] {}; boolean shouldAuthorizeSelf = true; - boolean groupExisted = false; Set returnVal = ImmutableSet. of(generatedMarkerGroup); // create mocks @@ -397,9 +398,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { expect(options.getInboundPorts()).andReturn(ports).atLeastOnce(); RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, ports, shouldAuthorizeSelf); - expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(groupExisted); - expect(strategy.createSecurityGroupIfNeeded.apply(regionNameAndIngressRules)).andReturn(generatedMarkerGroup); - expect(strategy.securityGroupMap.put(regionNameAndIngressRules, generatedMarkerGroup)).andReturn(null); + expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules)).andReturn(tag); // replay mocks replay(options); @@ -413,7 +412,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { verifyStrategy(strategy); } - public void testGetSecurityGroupsForTagAndOptions_createsNewGroupByDefaultWhenPortsAreSpecifiedWhenDoesntExist() { + public void testGetSecurityGroupsForTagAndOptions_createsNewGroupByDefaultWhenPortsAreSpecifiedWhenDoesntExist() + throws ExecutionException { // setup constants String region = Region.AP_SOUTHEAST_1; String tag = "tag"; @@ -421,7 +421,6 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { Set groupIds = ImmutableSet. of(); int[] ports = new int[] { 22, 80 }; boolean shouldAuthorizeSelf = true; - boolean groupExisted = false; Set returnVal = ImmutableSet. of(generatedMarkerGroup); // create mocks @@ -433,9 +432,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { expect(options.getInboundPorts()).andReturn(ports).atLeastOnce(); RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, ports, shouldAuthorizeSelf); - expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(groupExisted); - expect(strategy.createSecurityGroupIfNeeded.apply(regionNameAndIngressRules)).andReturn(generatedMarkerGroup); - expect(strategy.securityGroupMap.put(regionNameAndIngressRules, generatedMarkerGroup)).andReturn(null); + expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules)).andReturn(generatedMarkerGroup); // replay mocks replay(options); @@ -449,7 +446,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { verifyStrategy(strategy); } - public void testGetSecurityGroupsForTagAndOptions_reusesGroupByDefaultWhenNoPortsAreSpecifiedWhenDoesExist() { + public void testGetSecurityGroupsForTagAndOptions_reusesGroupByDefaultWhenNoPortsAreSpecifiedWhenDoesExist() + throws ExecutionException { // setup constants String region = Region.AP_SOUTHEAST_1; String tag = "tag"; @@ -457,7 +455,6 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { Set groupIds = ImmutableSet. of(); int[] ports = new int[] {}; boolean shouldAuthorizeSelf = true; - boolean groupExisted = true; Set returnVal = ImmutableSet. of(generatedMarkerGroup); // create mocks @@ -469,7 +466,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { expect(options.getInboundPorts()).andReturn(ports).atLeastOnce(); RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, ports, shouldAuthorizeSelf); - expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(groupExisted); + expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules)).andReturn(generatedMarkerGroup); // replay mocks replay(options); @@ -501,13 +498,9 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { // setup expectations expect(options.getGroups()).andReturn(groupIds).atLeastOnce(); RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, - ports, shouldAuthorizeSelf); // note - // this - // works - // since - // there's - // no equals on portsq - expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(groupExisted); + ports, shouldAuthorizeSelf); + + expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules)).andReturn(groupExisted ? "tag" : null); // replay mocks replay(options); @@ -522,28 +515,24 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { } private void verifyStrategy(CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions strategy) { + verify(strategy.knownKeys); verify(strategy.credentialsMap); verify(strategy.securityGroupMap); - verify(strategy.createUniqueKeyPair); - verify(strategy.createSecurityGroupIfNeeded); } @SuppressWarnings("unchecked") private CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions setupStrategy() { - Map credentialsMap = createMock(Map.class); - Map securityGroupMap = createMock(Map.class); - CreateUniqueKeyPair createUniqueKeyPair = createMock(CreateUniqueKeyPair.class); - Function createSecurityGroupIfNeeded = createMock(CreateSecurityGroupIfNeeded.class); - - return new CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions(credentialsMap, securityGroupMap, - createUniqueKeyPair, createSecurityGroupIfNeeded, OPTIONS_PROVIDER); + Map knownKeys = createMock(Map.class); + Cache credentialsMap = createMock(Cache.class); + Cache securityGroupMap = createMock(Cache.class); + return new CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions(knownKeys, credentialsMap, securityGroupMap, + OPTIONS_PROVIDER); } private void replayStrategy(CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions strategy) { + replay(strategy.knownKeys); replay(strategy.credentialsMap); replay(strategy.securityGroupMap); - replay(strategy.createUniqueKeyPair); - replay(strategy.createSecurityGroupIfNeeded); } } diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSetTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSetTest.java index 5a973d4365..66a32a1b08 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSetTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/EC2CreateNodesInGroupThenAddToSetTest.java @@ -51,7 +51,7 @@ import org.jclouds.ec2.options.RunInstancesOptions; import org.jclouds.ec2.services.InstanceClient; import org.testng.annotations.Test; -import com.google.common.base.Function; +import com.google.common.cache.Cache; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; @@ -222,7 +222,7 @@ public class EC2CreateNodesInGroupThenAddToSetTest { CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize = createMock(CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class); InstancePresent instancePresent = createMock(InstancePresent.class); RunningInstanceToNodeMetadata runningInstanceToNodeMetadata = createMock(RunningInstanceToNodeMetadata.class); - Function instanceToCredentials = createMock(Function.class); + Cache instanceToCredentials = createMock(Cache.class); Map credentialStore = createMock(Map.class); ComputeUtils utils = createMock(ComputeUtils.class); return new EC2CreateNodesInGroupThenAddToSet(client, Providers. of(template), diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/BaseEC2AsyncClientTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/BaseEC2AsyncClientTest.java index 283e7e0a7b..4625aaaf1e 100644 --- a/apis/ec2/src/test/java/org/jclouds/ec2/services/BaseEC2AsyncClientTest.java +++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/BaseEC2AsyncClientTest.java @@ -25,13 +25,17 @@ import java.net.URI; import java.util.Map; import java.util.Properties; +import javax.inject.Singleton; + import org.jclouds.aws.domain.Region; import org.jclouds.aws.filters.FormSigner; +import org.jclouds.compute.domain.Image; import org.jclouds.date.DateService; import org.jclouds.ec2.EC2AsyncClient; import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.EC2ContextBuilder; import org.jclouds.ec2.EC2PropertiesBuilder; +import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.config.EC2RestClientModule; import org.jclouds.http.HttpRequest; import org.jclouds.http.RequiresHttp; @@ -42,8 +46,12 @@ import org.jclouds.rest.RestContextSpec; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; import com.google.common.collect.ImmutableMap; import com.google.inject.Module; +import com.google.inject.Provides; /** * @author Adrian Cole @@ -58,6 +66,19 @@ public abstract class BaseEC2AsyncClientTest extends RestClientTest { super(EC2Client.class, EC2AsyncClient.class, DELEGATE_MAP); } + @Provides + @Singleton + Cache provide(){ + return CacheBuilder.newBuilder().build(new CacheLoader() { + + @Override + public Image load(RegionAndName key) throws Exception { + return null; + } + + }); + } + @Override protected String provideTimeStamp(DateService dateService, int expiration) { return "2009-11-08T15:54:08.897Z"; diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/ElasticStackComputeServiceAdapter.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/ElasticStackComputeServiceAdapter.java index ac9110d1bc..c14f191f84 100644 --- a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/ElasticStackComputeServiceAdapter.java +++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/ElasticStackComputeServiceAdapter.java @@ -46,7 +46,6 @@ import org.jclouds.compute.domain.internal.VolumeImpl; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.domain.Credentials; import org.jclouds.domain.Location; -import org.jclouds.elasticstack.ElasticStackAsyncClient; import org.jclouds.elasticstack.ElasticStackClient; import org.jclouds.elasticstack.domain.Device; import org.jclouds.elasticstack.domain.Drive; @@ -62,23 +61,25 @@ import org.jclouds.logging.Logger; import com.google.common.base.Function; import com.google.common.base.Predicate; +import com.google.common.cache.Cache; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet.Builder; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.UncheckedExecutionException; /** - * defines the connection between the {@link ElasticStackClient} implementation and the jclouds - * {@link ComputeService} + * defines the connection between the {@link ElasticStackClient} implementation + * and the jclouds {@link ComputeService} * */ @Singleton public class ElasticStackComputeServiceAdapter implements - ComputeServiceAdapter { + ComputeServiceAdapter { private final ElasticStackClient client; - private final ElasticStackAsyncClient aclient; private final Predicate driveNotClaimed; private final Map preinstalledImages; - private final Map cache; + private final Cache cache; private final JustProvider locationSupplier; private final String defaultVncPassword; private final ExecutorService executor; @@ -88,13 +89,11 @@ public class ElasticStackComputeServiceAdapter implements protected Logger logger = Logger.NULL; @Inject - public ElasticStackComputeServiceAdapter(ElasticStackClient client, ElasticStackAsyncClient aclient, - Predicate driveNotClaimed, JustProvider locationSupplier, - Map preinstalledImages, Map cache, - @Named(ElasticStackConstants.PROPERTY_VNC_PASSWORD) String defaultVncPassword, - @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) { + public ElasticStackComputeServiceAdapter(ElasticStackClient client, Predicate driveNotClaimed, + JustProvider locationSupplier, Map preinstalledImages, Cache cache, + @Named(ElasticStackConstants.PROPERTY_VNC_PASSWORD) String defaultVncPassword, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) { this.client = checkNotNull(client, "client"); - this.aclient = checkNotNull(aclient, "aclient"); this.driveNotClaimed = checkNotNull(driveNotClaimed, "driveNotClaimed"); this.locationSupplier = checkNotNull(locationSupplier, "locationSupplier"); this.preinstalledImages = checkNotNull(preinstalledImages, "preinstalledImages"); @@ -105,12 +104,12 @@ public class ElasticStackComputeServiceAdapter implements @Override public ServerInfo createNodeWithGroupEncodedIntoNameThenStoreCredentials(String tag, String name, Template template, - Map credentialStore) { + Map credentialStore) { long bootSize = (long) (template.getHardware().getVolumes().get(0).getSize() * 1024 * 1024 * 1024l); - + logger.debug(">> creating boot drive bytes(%d)", bootSize); - DriveInfo drive = client.createDrive(new Drive.Builder().name(template.getImage().getId()).size(bootSize) - .build()); + DriveInfo drive = client + .createDrive(new Drive.Builder().name(template.getImage().getId()).size(bootSize).build()); logger.debug("<< drive(%s)", drive.getUuid()); logger.debug(">> imaging boot drive source(%s)", template.getImage().getId()); @@ -121,17 +120,16 @@ public class ElasticStackComputeServiceAdapter implements client.destroyDrive(drive.getUuid()); throw new IllegalStateException("could not image drive in time!"); } - cache.put(drive.getUuid(), drive); - Server toCreate = small(name, drive.getUuid(), defaultVncPassword).mem(template.getHardware().getRam()).cpu( - (int) (template.getHardware().getProcessors().get(0).getSpeed())).build(); + Server toCreate = small(name, drive.getUuid(), defaultVncPassword).mem(template.getHardware().getRam()) + .cpu((int) (template.getHardware().getProcessors().get(0).getSpeed())).build(); ServerInfo from = client.createServer(toCreate); client.startServer(from.getUuid()); from = client.getServerInfo(from.getUuid()); // store the credentials so that later functions can use them - credentialStore.put("node#"+ from.getUuid(), new Credentials(template.getImage().getDefaultCredentials().identity, - from.getVnc().getPassword())); + credentialStore.put("node#" + from.getUuid(), new Credentials( + template.getImage().getDefaultCredentials().identity, from.getVnc().getPassword())); return from; } @@ -155,30 +153,40 @@ public class ElasticStackComputeServiceAdapter implements return "sizeLessThanOrEqual(" + size + ")"; } - }).ids(id).ram(ram).processors(ImmutableList.of(new Processor(1, cpu))).volumes( - ImmutableList. of(new VolumeImpl(size, true, true))).build()); + }).ids(id).ram(ram).processors(ImmutableList.of(new Processor(1, cpu))) + .volumes(ImmutableList. of(new VolumeImpl(size, true, true))).build()); } return hardware.build(); } /** - * look up the current standard images and do not error out, if they are not found. + * look up the current standard images and do not error out, if they are not + * found. */ @Override public Iterable listImages() { Iterable drives = transformParallel(preinstalledImages.keySet(), - new Function>() { + new Function>() { - @Override - public Future apply(String input) { - return aclient.getDriveInfo(input); + @Override + public Future apply(String input) { + try { + return Futures.immediateFuture(cache.getUnchecked(input)); + } catch (NullPointerException e) { + logger.debug("drive %s not found", input); + } catch (UncheckedExecutionException e) { + logger.warn(e, "error finding drive %s: %s", input, e.getMessage()); } + return Futures.immediateFuture(null); + } - }, executor, null, logger, "drives"); - Iterable returnVal = filter(drives, notNull()); - for (DriveInfo drive : returnVal) - cache.put(drive.getUuid(), drive); - return returnVal; + @Override + public String toString() { + return "seedDriveCache()"; + } + + }, executor, null, logger, "drives"); + return filter(drives, notNull()); } @SuppressWarnings("unchecked") @@ -186,7 +194,7 @@ public class ElasticStackComputeServiceAdapter implements public Iterable listNodes() { return (Iterable) client.listServerInfo(); } - + @SuppressWarnings("unchecked") @Override public Iterable listLocations() { diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/config/ElasticStackComputeServiceContextModule.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/config/ElasticStackComputeServiceContextModule.java index 3f3945aad2..2cdf9c2825 100644 --- a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/config/ElasticStackComputeServiceContextModule.java +++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/config/ElasticStackComputeServiceContextModule.java @@ -38,10 +38,10 @@ import org.jclouds.elasticstack.ElasticStackAsyncClient; import org.jclouds.elasticstack.ElasticStackClient; import org.jclouds.elasticstack.compute.ElasticStackComputeServiceAdapter; import org.jclouds.elasticstack.compute.functions.ServerInfoToNodeMetadata; -import org.jclouds.elasticstack.compute.functions.WellKnownImageToImage; import org.jclouds.elasticstack.compute.functions.ServerInfoToNodeMetadata.DeviceToVolume; import org.jclouds.elasticstack.compute.functions.ServerInfoToNodeMetadata.FindImageForId; import org.jclouds.elasticstack.compute.functions.ServerInfoToNodeMetadata.GetImageIdFromServer; +import org.jclouds.elasticstack.compute.functions.WellKnownImageToImage; import org.jclouds.elasticstack.domain.Device; import org.jclouds.elasticstack.domain.DriveInfo; import org.jclouds.elasticstack.domain.Server; @@ -59,7 +59,9 @@ import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.base.Supplier; -import com.google.common.collect.MapMaker; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; import com.google.common.collect.Maps; import com.google.inject.Provides; import com.google.inject.TypeLiteral; @@ -104,12 +106,12 @@ public class ElasticStackComputeServiceContextModule @Provides @Singleton - protected Map cache(GetDrive getDrive) { - return new MapMaker().makeComputingMap(getDrive); + protected Cache cache(GetDrive getDrive) { + return CacheBuilder.newBuilder().build(getDrive); } @Singleton - public static class GetDrive implements Function { + public static class GetDrive extends CacheLoader { private final ElasticStackClient client; @Inject @@ -118,7 +120,7 @@ public class ElasticStackComputeServiceContextModule } @Override - public DriveInfo apply(String input) { + public DriveInfo load(String input) { return client.getDriveInfo(input); } } diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/functions/ServerInfoToNodeMetadata.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/functions/ServerInfoToNodeMetadata.java index eea5cc077b..7576e9f6f5 100644 --- a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/functions/ServerInfoToNodeMetadata.java +++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/functions/ServerInfoToNodeMetadata.java @@ -18,27 +18,44 @@ */ package org.jclouds.elasticstack.compute.functions; -import com.google.common.base.Function; -import com.google.common.base.Supplier; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import org.jclouds.collect.FindResourceInSet; -import org.jclouds.collect.Memoized; -import org.jclouds.compute.domain.*; -import org.jclouds.domain.Credentials; -import org.jclouds.domain.Location; -import org.jclouds.elasticstack.domain.*; +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.compute.util.ComputeServiceUtils.parseGroupFromName; -import javax.inject.Inject; -import javax.inject.Singleton; import java.util.List; import java.util.Map; import java.util.Set; -import static com.google.common.base.Preconditions.checkNotNull; -import static org.jclouds.compute.util.ComputeServiceUtils.parseGroupFromName; +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.collect.FindResourceInSet; +import org.jclouds.collect.Memoized; +import org.jclouds.compute.domain.HardwareBuilder; +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.NodeMetadataBuilder; +import org.jclouds.compute.domain.NodeState; +import org.jclouds.compute.domain.Processor; +import org.jclouds.compute.domain.Volume; +import org.jclouds.compute.domain.VolumeBuilder; +import org.jclouds.domain.Credentials; +import org.jclouds.domain.Location; +import org.jclouds.elasticstack.domain.Device; +import org.jclouds.elasticstack.domain.DriveInfo; +import org.jclouds.elasticstack.domain.Server; +import org.jclouds.elasticstack.domain.ServerInfo; +import org.jclouds.elasticstack.domain.ServerStatus; +import org.jclouds.logging.Logger; + +import com.google.common.base.Function; +import com.google.common.base.Supplier; +import com.google.common.cache.Cache; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.util.concurrent.UncheckedExecutionException; /** * @author Adrian Cole @@ -93,16 +110,19 @@ public class ServerInfoToNodeMetadata implements Function of(from.getNics().get(0).getDhcp())); builder.privateAddresses(ImmutableSet. of()); - builder.credentials(credentialStore.get("node#"+ from.getUuid())); + builder.credentials(credentialStore.get("node#" + from.getUuid())); return builder.build(); } @Singleton public static final class DeviceToVolume implements Function { - private final Map cache; + @Resource + protected Logger logger = Logger.NULL; + + private final Cache cache; @Inject - public DeviceToVolume(Map cache) { + public DeviceToVolume(Cache cache) { this.cache = checkNotNull(cache, "cache"); } @@ -110,27 +130,34 @@ public class ServerInfoToNodeMetadata implements Function { - private final Map cache; + @Resource + protected Logger logger = Logger.NULL; + + private final Cache cache; @Inject - public GetImageIdFromServer(Map cache) { + public GetImageIdFromServer(Cache cache) { this.cache = cache; } @@ -141,9 +168,12 @@ public class ServerInfoToNodeMetadata implements Function%s", description)); - payload = Strings2.replaceAll(payload, Patterns.TOKEN_TO_PATTERN.get("monitor"), getMonitorString(postParams)); + try { + payload = Strings2.replaceAll(payload, Patterns.TOKEN_TO_PATTERN.get("description"), description == null ? "" + : String.format("\n\t%s", description)); + payload = Strings2.replaceAll(payload, Patterns.TOKEN_TO_PATTERN.get("monitor"), getMonitorString(postParams)); + } catch (ExecutionException e) { + Throwables.propagate(e); + } return stringBinder.bindToRequest(request, payload); } diff --git a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindAddNodeServiceToXmlPayload.java b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindAddNodeServiceToXmlPayload.java index 5eaef0d0c4..eeef9b59e4 100644 --- a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindAddNodeServiceToXmlPayload.java +++ b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/binders/BindAddNodeServiceToXmlPayload.java @@ -22,6 +22,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import static org.jclouds.trmk.vcloud_0_8.reference.TerremarkConstants.PROPERTY_TERREMARK_EXTENSION_NS; import java.util.Map; +import java.util.concurrent.ExecutionException; import javax.inject.Inject; import javax.inject.Named; @@ -33,6 +34,7 @@ import org.jclouds.rest.binders.BindToStringPayload; import org.jclouds.util.Patterns; import org.jclouds.util.Strings2; +import com.google.common.base.Throwables; import com.google.common.collect.ImmutableMap; /** @@ -62,8 +64,12 @@ public class BindAddNodeServiceToXmlPayload implements MapBinder { String payload = Strings2.replaceTokens(xmlTemplate, ImmutableMap.of("name", name, "ipAddress", ipAddress, "port", port, "enabled", enabled, "ns", ns)); - payload = Strings2.replaceAll(payload, Patterns.TOKEN_TO_PATTERN.get("description"), description == null ? "" - : String.format("\n %s", description)); + try { + payload = Strings2.replaceAll(payload, Patterns.TOKEN_TO_PATTERN.get("description"), description == null ? "" + : String.format("\n %s", description)); + } catch (ExecutionException e) { + Throwables.propagate(e); + } return stringBinder.bindToRequest(request, payload); } diff --git a/compute/src/main/java/org/jclouds/compute/reference/ComputeServiceConstants.java b/compute/src/main/java/org/jclouds/compute/reference/ComputeServiceConstants.java index 809803eec7..7de0a682d6 100644 --- a/compute/src/main/java/org/jclouds/compute/reference/ComputeServiceConstants.java +++ b/compute/src/main/java/org/jclouds/compute/reference/ComputeServiceConstants.java @@ -57,7 +57,7 @@ public interface ComputeServiceConstants { public static class ReferenceData { @Inject(optional = true) @Named(PROPERTY_OS_VERSION_MAP_JSON) - public String osVersionMapJson = "{\"suse\":{\"\":\"\",\"11\":\"11\",\"11 SP1\":\"11 SP1\"},\"debian\":{\"\":\"\",\"lenny\":\"5.0\",\"squeeze\":\"6.0\"},\"centos\":{\"\":\"\",\"5\":\"5.0\",\"5.2\":\"5.2\",\"5.3\":\"5.3\",\"5.4\":\"5.4\",\"5.5\":\"5.5\"},\"rhel\":{\"\":\"\",\"5\":\"5.0\",\"5.2\":\"5.2\",\"5.3\":\"5.3\",\"5.4\":\"5.4\",\"5.5\":\"5.5\"},\"solaris\":{\"\":\"\",\"10\":\"10\"},\"ubuntu\":{\"\":\"\",\"hardy\":\"8.04\",\"karmic\":\"9.10\",\"lucid\":\"10.04\",\"10.04.1\":\"10.04\",\"maverick\":\"10.10\",\"natty\":\"11.04\",\"oneiric\":\"11.10\"},\"windows\":{\"\":\"\",\"2003\":\"2003\",\"2003 Standard\":\"2003\",\"2003 R2\":\"2003 R2\",\"2008\":\"2008\",\"2008 Web\":\"2008\",\"2008 Server\":\"2008\",\"Server 2008\":\"2008\",\"2008 R1\":\"2008 R1\",\"2008 R2\":\"2008 R2\",\"Server 2008 R2\":\"2008 R2\",\"2008 Server R2\":\"2008 R2\",\"2008 SP2\":\"2008 SP2\",\"Server 2008 SP2\":\"2008 SP2\"}}"; + public String osVersionMapJson = "{\"suse\":{\"\":\"\",\"11\":\"11\",\"11 SP1\":\"11 SP1\"},\"debian\":{\"\":\"\",\"lenny\":\"5.0\",\"squeeze\":\"6.0\"},\"centos\":{\"\":\"\",\"5\":\"5.0\",\"5.2\":\"5.2\",\"5.3\":\"5.3\",\"5.4\":\"5.4\",\"5.5\":\"5.5\",\"5.6\":\"5.6\",\"5.7\":\"5.7\",\"6.0\":\"6.0\"},\"rhel\":{\"\":\"\",\"5\":\"5.0\",\"5.2\":\"5.2\",\"5.3\":\"5.3\",\"5.4\":\"5.4\",\"5.5\":\"5.5\",\"5.6\":\"5.6\",\"5.7\":\"5.7\",\"6.0\":\"6.0\"},\"solaris\":{\"\":\"\",\"10\":\"10\"},\"ubuntu\":{\"\":\"\",\"hardy\":\"8.04\",\"karmic\":\"9.10\",\"lucid\":\"10.04\",\"10.04.1\":\"10.04\",\"maverick\":\"10.10\",\"natty\":\"11.04\",\"oneiric\":\"11.10\"},\"windows\":{\"\":\"\",\"2003\":\"2003\",\"2003 Standard\":\"2003\",\"2003 R2\":\"2003 R2\",\"2008\":\"2008\",\"2008 Web\":\"2008\",\"2008 Server\":\"2008\",\"Server 2008\":\"2008\",\"2008 R1\":\"2008 R1\",\"2008 R2\":\"2008 R2\",\"Server 2008 R2\":\"2008 R2\",\"2008 Server R2\":\"2008 R2\",\"2008 SP2\":\"2008 SP2\",\"Server 2008 SP2\":\"2008 SP2\"}}"; } @Singleton diff --git a/core/pom.xml b/core/pom.xml index ab088d0e01..ce79e6fb7c 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -109,7 +109,7 @@ com.google.guava guava - r09 + 10.0-rc2 diff --git a/core/src/main/java/org/jclouds/concurrent/Futures.java b/core/src/main/java/org/jclouds/concurrent/Futures.java index 45797f05c5..2d85ec6af7 100644 --- a/core/src/main/java/org/jclouds/concurrent/Futures.java +++ b/core/src/main/java/org/jclouds/concurrent/Futures.java @@ -69,7 +69,7 @@ public class Futures { // ExecutionException / CancellationException / RuntimeException // The task is done, run the listeners. } - executionList.run(); + executionList.execute(); } @Override @@ -116,7 +116,7 @@ public class Futures { if (delegate.isDone()) { // If the delegate is already done, run the execution list // immediately on the current thread. - executionList.run(); + executionList.execute(); return; } adapterExecutor.execute(new CallGetAndRunExecutionList(delegate, executionList)); diff --git a/core/src/main/java/org/jclouds/concurrent/internal/SyncProxy.java b/core/src/main/java/org/jclouds/concurrent/internal/SyncProxy.java index 1b8d4482c6..42d7a83f2f 100644 --- a/core/src/main/java/org/jclouds/concurrent/internal/SyncProxy.java +++ b/core/src/main/java/org/jclouds/concurrent/internal/SyncProxy.java @@ -26,7 +26,6 @@ import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -38,6 +37,7 @@ import org.jclouds.internal.ClassMethodArgs; import org.jclouds.rest.annotations.Delegate; import org.jclouds.util.Throwables2; +import com.google.common.cache.Cache; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.common.util.concurrent.ListenableFuture; @@ -62,13 +62,13 @@ public class SyncProxy implements InvocationHandler { private final Map methodMap; private final Map syncMethodMap; private final Map timeoutMap; - private final ConcurrentMap delegateMap; + private final Cache delegateMap; private final Map, Class> sync2Async; private static final Set objectMethods = ImmutableSet.of(Object.class.getMethods()); @Inject public SyncProxy(Class declaring, Object async, - @Named("sync") ConcurrentMap delegateMap, Map, Class> sync2Async) + @Named("sync") Cache delegateMap, Map, Class> sync2Async) throws SecurityException, NoSuchMethodException { this.delegateMap = delegateMap; this.delegate = async; diff --git a/core/src/main/java/org/jclouds/json/internal/EnumTypeAdapterThatReturnsFromValue.java b/core/src/main/java/org/jclouds/json/internal/EnumTypeAdapterThatReturnsFromValue.java index 2abe7d3237..fc77d3a73f 100644 --- a/core/src/main/java/org/jclouds/json/internal/EnumTypeAdapterThatReturnsFromValue.java +++ b/core/src/main/java/org/jclouds/json/internal/EnumTypeAdapterThatReturnsFromValue.java @@ -20,10 +20,11 @@ package org.jclouds.json.internal; import java.lang.reflect.Method; import java.lang.reflect.Type; -import java.util.Map; +import java.util.concurrent.ExecutionException; -import com.google.common.base.Function; -import com.google.common.collect.MapMaker; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; @@ -46,29 +47,26 @@ public class EnumTypeAdapterThatReturnsFromValue> implements J try { return (T) Enum.valueOf((Class) classOfT, json.getAsString()); } catch (IllegalArgumentException e) { - Method converter = classToConvert.get(classOfT); - if (converter != null) - try { - return (T) converter.invoke(null, json.getAsString()); - } catch (Exception e1) { - throw e; - } - else + try { + Method converter = classToConvert.get((Class) classOfT); + return (T) converter.invoke(null, json.getAsString()); + } catch (Exception e1) { throw e; + } } } - private final static Map, Method> classToConvert = new MapMaker() - .makeComputingMap(new Function, Method>() { + private final static Cache, Method> classToConvert = CacheBuilder.newBuilder() + .build(new CacheLoader, Method>() { @Override - public Method apply(Class from) { + public Method load(Class from) throws ExecutionException { try { Method method = from.getMethod("fromValue", String.class); method.setAccessible(true); return method; } catch (Exception e) { - return null; + throw new ExecutionException(e); } } diff --git a/core/src/main/java/org/jclouds/rest/binders/BindAsHostPrefix.java b/core/src/main/java/org/jclouds/rest/binders/BindAsHostPrefix.java index b5f38027fb..3a06daee15 100644 --- a/core/src/main/java/org/jclouds/rest/binders/BindAsHostPrefix.java +++ b/core/src/main/java/org/jclouds/rest/binders/BindAsHostPrefix.java @@ -20,8 +20,8 @@ package org.jclouds.rest.binders; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.net.InternetDomainName.fromLenient; -import static com.google.common.net.InternetDomainName.isValidLenient; +import static com.google.common.net.InternetDomainName.from; +import static com.google.common.net.InternetDomainName.isValid; import javax.inject.Inject; import javax.inject.Provider; @@ -51,9 +51,9 @@ public class BindAsHostPrefix implements Binder { @SuppressWarnings("unchecked") public R bindToRequest(R request, Object payload) { checkNotNull(payload, "hostprefix"); - checkArgument(isValidLenient(request.getEndpoint().getHost()), "this is only valid for hostnames: " + request); + checkArgument(isValid(request.getEndpoint().getHost()), "this is only valid for hostnames: " + request); UriBuilder builder = uriBuilderProvider.get().uri(request.getEndpoint()); - InternetDomainName name = fromLenient(request.getEndpoint().getHost()).child(payload.toString()); + InternetDomainName name = from(request.getEndpoint().getHost()).child(payload.toString()); builder.host(name.name()); return (R) request.toBuilder().endpoint(builder.build()).build(); } diff --git a/core/src/main/java/org/jclouds/rest/config/ClientProvider.java b/core/src/main/java/org/jclouds/rest/config/ClientProvider.java index 1b698d454d..bcc05e4a05 100644 --- a/core/src/main/java/org/jclouds/rest/config/ClientProvider.java +++ b/core/src/main/java/org/jclouds/rest/config/ClientProvider.java @@ -19,7 +19,6 @@ package org.jclouds.rest.config; import java.util.Map; -import java.util.concurrent.ConcurrentMap; import javax.inject.Inject; import javax.inject.Singleton; @@ -28,6 +27,7 @@ import org.jclouds.concurrent.internal.SyncProxy; import org.jclouds.internal.ClassMethodArgs; import com.google.common.base.Throwables; +import com.google.common.cache.Cache; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Provider; @@ -58,8 +58,8 @@ public class ClientProvider implements Provider { @Singleton public S get() { A client = (A) injector.getInstance(Key.get(asyncClientType)); - ConcurrentMap delegateMap = injector.getInstance(Key.get( - new TypeLiteral>() { + Cache delegateMap = injector.getInstance(Key.get( + new TypeLiteral>() { }, Names.named("sync"))); try { return (S) SyncProxy.proxy(syncClientType, new SyncProxy(syncClientType, client, diff --git a/core/src/main/java/org/jclouds/rest/config/CreateClientForCaller.java b/core/src/main/java/org/jclouds/rest/config/CreateClientForCaller.java index 00c97e7946..1ccf097e13 100644 --- a/core/src/main/java/org/jclouds/rest/config/CreateClientForCaller.java +++ b/core/src/main/java/org/jclouds/rest/config/CreateClientForCaller.java @@ -21,35 +21,37 @@ package org.jclouds.rest.config; import static com.google.common.base.Preconditions.checkState; import java.util.Map; -import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; import javax.inject.Inject; import javax.inject.Named; +import javax.inject.Provider; import org.jclouds.concurrent.internal.SyncProxy; import org.jclouds.internal.ClassMethodArgs; -import com.google.common.base.Function; import com.google.common.base.Throwables; -import com.google.inject.Provider; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheLoader; /** * * @author Adrian Cole */ -public class CreateClientForCaller implements Function { - private final ConcurrentMap asyncMap; - private final Provider> delegateMap; +public class CreateClientForCaller extends CacheLoader { + private final Cache asyncMap; + private final Provider> delegateMap; Map, Class> sync2Async; @Inject - CreateClientForCaller(@Named("async") ConcurrentMap asyncMap, - @Named("sync") Provider> delegateMap) { + CreateClientForCaller(@Named("async") Cache asyncMap, + @Named("sync") Provider> delegateMap) { this.asyncMap = asyncMap; this.delegateMap = delegateMap; } - public Object apply(ClassMethodArgs from) { + @Override + public Object load(ClassMethodArgs from) throws ExecutionException { Class syncClass = from.getMethod().getReturnType(); Class asyncClass = sync2Async.get(syncClass); checkState(asyncClass != null, "configuration error, sync class " + syncClass diff --git a/core/src/main/java/org/jclouds/rest/config/RestClientModule.java b/core/src/main/java/org/jclouds/rest/config/RestClientModule.java index e7f263305b..87e77f1e12 100644 --- a/core/src/main/java/org/jclouds/rest/config/RestClientModule.java +++ b/core/src/main/java/org/jclouds/rest/config/RestClientModule.java @@ -19,7 +19,6 @@ package org.jclouds.rest.config; import java.util.Map; -import java.util.concurrent.ConcurrentMap; import javax.inject.Named; import javax.inject.Singleton; @@ -30,8 +29,9 @@ import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.RestContext; import org.jclouds.rest.internal.RestContextImpl; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.MapMaker; import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.google.inject.Scopes; @@ -125,10 +125,10 @@ public class RestClientModule extends AbstractModule { @Provides @Singleton @Named("sync") - ConcurrentMap provideSyncDelegateMap( + Cache provideSyncDelegateMap( CreateClientForCaller createClientForCaller) { createClientForCaller.sync2Async = delegates; - return new MapMaker().makeComputingMap(createClientForCaller); + return CacheBuilder.newBuilder().build(createClientForCaller); } } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/rest/config/RestModule.java b/core/src/main/java/org/jclouds/rest/config/RestModule.java index 15b8123ab2..d582a53f81 100644 --- a/core/src/main/java/org/jclouds/rest/config/RestModule.java +++ b/core/src/main/java/org/jclouds/rest/config/RestModule.java @@ -18,8 +18,6 @@ */ package org.jclouds.rest.config; -import java.util.concurrent.ConcurrentMap; - import javax.inject.Named; import javax.inject.Singleton; import javax.ws.rs.core.UriBuilder; @@ -42,8 +40,10 @@ import org.jclouds.rest.internal.RestAnnotationProcessor; import org.jclouds.rest.internal.SeedAnnotationCache; import com.google.common.base.Function; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.MapMaker; import com.google.inject.AbstractModule; import com.google.inject.Inject; import com.google.inject.Injector; @@ -77,18 +77,18 @@ public class RestModule extends AbstractModule { @Provides @Singleton - protected ConcurrentMap, Boolean> seedAnnotationCache(SeedAnnotationCache seedAnnotationCache) { - return new MapMaker().makeComputingMap(seedAnnotationCache); + protected Cache, Boolean> seedAnnotationCache(SeedAnnotationCache seedAnnotationCache) { + return CacheBuilder.newBuilder().build(seedAnnotationCache); } @Provides @Singleton @Named("async") - ConcurrentMap provideAsyncDelegateMap(CreateAsyncClientForCaller createAsyncClientForCaller) { - return new MapMaker().makeComputingMap(createAsyncClientForCaller); + Cache provideAsyncDelegateMap(CreateAsyncClientForCaller createAsyncClientForCaller) { + return CacheBuilder.newBuilder().build(createAsyncClientForCaller); } - static class CreateAsyncClientForCaller implements Function { + static class CreateAsyncClientForCaller extends CacheLoader { private final Injector injector; private final AsyncRestClientProxy.Factory factory; @@ -100,7 +100,7 @@ public class RestModule extends AbstractModule { @SuppressWarnings( { "unchecked", "rawtypes" }) @Override - public Object apply(final ClassMethodArgs from) { + public Object load(final ClassMethodArgs from) { Class clazz = from.getAsyncClass(); TypeLiteral typeLiteral = TypeLiteral.get(clazz); RestAnnotationProcessor util = (RestAnnotationProcessor) injector.getInstance(Key.get(TypeLiteral.get(Types @@ -108,8 +108,8 @@ public class RestModule extends AbstractModule { // cannot use child injectors due to the super coarse guice lock on // Singleton util.setCaller(from); - ConcurrentMap delegateMap = injector.getInstance(Key.get( - new TypeLiteral>() { + Cache delegateMap = injector.getInstance(Key.get( + new TypeLiteral>() { }, Names.named("async"))); AsyncRestClientProxy proxy = new AsyncRestClientProxy(injector, factory, util, typeLiteral, delegateMap); injector.injectMembers(proxy); diff --git a/core/src/main/java/org/jclouds/rest/internal/AsyncRestClientProxy.java b/core/src/main/java/org/jclouds/rest/internal/AsyncRestClientProxy.java index b49595fa1b..d7a3b74731 100644 --- a/core/src/main/java/org/jclouds/rest/internal/AsyncRestClientProxy.java +++ b/core/src/main/java/org/jclouds/rest/internal/AsyncRestClientProxy.java @@ -27,7 +27,6 @@ import java.lang.annotation.Annotation; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.NoSuchElementException; -import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import javax.annotation.Resource; @@ -49,6 +48,7 @@ import org.jclouds.util.Throwables2; import com.google.common.base.Function; import com.google.common.base.Predicate; +import com.google.common.cache.Cache; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.util.concurrent.Futures; @@ -76,12 +76,12 @@ public class AsyncRestClientProxy implements InvocationHandler { @Resource protected Logger logger = Logger.NULL; - private final ConcurrentMap delegateMap; + private final Cache delegateMap; @SuppressWarnings("unchecked") @Inject public AsyncRestClientProxy(Injector injector, Factory factory, RestAnnotationProcessor util, - TypeLiteral typeLiteral, @Named("async") ConcurrentMap delegateMap) { + TypeLiteral typeLiteral, @Named("async") Cache delegateMap) { this.injector = injector; this.annotationProcessor = util; this.declaring = (Class) typeLiteral.getRawType(); diff --git a/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java b/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java index ace4d646c5..b6f7b95403 100644 --- a/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java +++ b/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java @@ -49,9 +49,8 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.SortedSet; -import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; -import org.jclouds.javax.annotation.Nullable; import javax.annotation.Resource; import javax.inject.Named; import javax.inject.Provider; @@ -98,6 +97,7 @@ import org.jclouds.io.Payloads; import org.jclouds.io.payloads.MultipartForm; import org.jclouds.io.payloads.Part; import org.jclouds.io.payloads.Part.PartOptions; +import org.jclouds.javax.annotation.Nullable; import org.jclouds.json.internal.GsonWrapper; import org.jclouds.logging.Logger; import org.jclouds.rest.Binder; @@ -137,6 +137,10 @@ import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; +import com.google.common.base.Throwables; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; @@ -145,7 +149,6 @@ import com.google.common.collect.Iterables; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.Lists; -import com.google.common.collect.MapMaker; import com.google.common.collect.Multimap; import com.google.common.util.concurrent.ListenableFuture; import com.google.inject.Inject; @@ -167,25 +170,25 @@ public class RestAnnotationProcessor { private final Class declaring; // TODO replace with Table object - static final Map>> methodToIndexOfParamToBinderParamAnnotation = createMethodToIndexOfParamToAnnotation(BinderParam.class); - static final Map>> methodToIndexOfParamToWrapWithAnnotation = createMethodToIndexOfParamToAnnotation(WrapWith.class); - static final Map>> methodToIndexOfParamToHeaderParamAnnotations = createMethodToIndexOfParamToAnnotation(HeaderParam.class); - static final Map>> methodToIndexOfParamToEndpointAnnotations = createMethodToIndexOfParamToAnnotation(Endpoint.class); - static final Map>> methodToIndexOfParamToEndpointParamAnnotations = createMethodToIndexOfParamToAnnotation(EndpointParam.class); - static final Map>> methodToIndexOfParamToMatrixParamAnnotations = createMethodToIndexOfParamToAnnotation(MatrixParam.class); - static final Map>> methodToIndexOfParamToFormParamAnnotations = createMethodToIndexOfParamToAnnotation(FormParam.class); - static final Map>> methodToIndexOfParamToQueryParamAnnotations = createMethodToIndexOfParamToAnnotation(QueryParam.class); - static final Map>> methodToIndexOfParamToPathParamAnnotations = createMethodToIndexOfParamToAnnotation(PathParam.class); - static final Map>> methodToIndexOfParamToPostParamAnnotations = createMethodToIndexOfParamToAnnotation(PayloadParam.class); - static final Map>> methodToIndexOfParamToPartParamAnnotations = createMethodToIndexOfParamToAnnotation(PartParam.class); - static final Map>> methodToIndexOfParamToParamParserAnnotations = createMethodToIndexOfParamToAnnotation(ParamParser.class); + static final Cache>> methodToIndexOfParamToBinderParamAnnotation = createMethodToIndexOfParamToAnnotation(BinderParam.class); + static final Cache>> methodToIndexOfParamToWrapWithAnnotation = createMethodToIndexOfParamToAnnotation(WrapWith.class); + static final Cache>> methodToIndexOfParamToHeaderParamAnnotations = createMethodToIndexOfParamToAnnotation(HeaderParam.class); + static final Cache>> methodToIndexOfParamToEndpointAnnotations = createMethodToIndexOfParamToAnnotation(Endpoint.class); + static final Cache>> methodToIndexOfParamToEndpointParamAnnotations = createMethodToIndexOfParamToAnnotation(EndpointParam.class); + static final Cache>> methodToIndexOfParamToMatrixParamAnnotations = createMethodToIndexOfParamToAnnotation(MatrixParam.class); + static final Cache>> methodToIndexOfParamToFormParamAnnotations = createMethodToIndexOfParamToAnnotation(FormParam.class); + static final Cache>> methodToIndexOfParamToQueryParamAnnotations = createMethodToIndexOfParamToAnnotation(QueryParam.class); + static final Cache>> methodToIndexOfParamToPathParamAnnotations = createMethodToIndexOfParamToAnnotation(PathParam.class); + static final Cache>> methodToIndexOfParamToPostParamAnnotations = createMethodToIndexOfParamToAnnotation(PayloadParam.class); + static final Cache>> methodToIndexOfParamToPartParamAnnotations = createMethodToIndexOfParamToAnnotation(PartParam.class); + static final Cache>> methodToIndexOfParamToParamParserAnnotations = createMethodToIndexOfParamToAnnotation(ParamParser.class); static final Map delegationMap = newHashMap(); - static Map>> createMethodToIndexOfParamToAnnotation( + static Cache>> createMethodToIndexOfParamToAnnotation( final Class annotation) { - return new MapMaker().makeComputingMap(new Function>>() { - public Map> apply(Method method) { - return new MapMaker().makeComputingMap(new GetAnnotationsForMethodParameterIndex(method, annotation)); + return CacheBuilder.newBuilder().build(new CacheLoader>>() { + public Cache> load(Method method) { + return CacheBuilder.newBuilder().build(CacheLoader.from(new GetAnnotationsForMethodParameterIndex(method, annotation))); } }); } @@ -201,17 +204,17 @@ public class RestAnnotationProcessor { public Set apply(final Integer index) { return ImmutableSet. copyOf(filter(ImmutableList.copyOf(method.getParameterAnnotations()[index]), - new Predicate() { - public boolean apply(Annotation input) { - return input.annotationType().equals(clazz); - } - })); + new Predicate() { + public boolean apply(Annotation input) { + return input.annotationType().equals(clazz); + } + })); } } private static final Class optionsVarArgsClass = new HttpRequestOptions[] {} - .getClass(); + .getClass(); private static final Function, ? extends Part> ENTRY_TO_PART = new Function, Part>() { @@ -222,23 +225,24 @@ public class RestAnnotationProcessor { }; - static final Map> methodToIndexesOfOptions = new MapMaker() - .makeComputingMap(new Function>() { - public Set apply(Method method) { - Builder toReturn = ImmutableSet. builder(); - for (int index = 0; index < method.getParameterTypes().length; index++) { - Class type = method.getParameterTypes()[index]; - if (HttpRequestOptions.class.isAssignableFrom(type) || optionsVarArgsClass.isAssignableFrom(type)) - toReturn.add(index); - } - return toReturn.build(); + static final Cache> methodToIndexesOfOptions = CacheBuilder.newBuilder().build( + new CacheLoader>() { + @Override + public Set load(Method method) { + Builder toReturn = ImmutableSet. builder(); + for (int index = 0; index < method.getParameterTypes().length; index++) { + Class type = method.getParameterTypes()[index]; + if (HttpRequestOptions.class.isAssignableFrom(type) || optionsVarArgsClass.isAssignableFrom(type)) + toReturn.add(index); } - }); + return toReturn.build(); + } + }); private final ParseSax.Factory parserFactory; private final HttpUtils utils; private final Provider uriBuilderProvider; - private final ConcurrentMap, Boolean> seedAnnotationCache; + private final Cache, Boolean> seedAnnotationCache; private final String apiVersion; private char[] skips; @@ -252,7 +256,7 @@ public class RestAnnotationProcessor { @VisibleForTesting public static Function createResponseParser(ParseSax.Factory parserFactory, Injector injector, - Method method, HttpRequest request) { + Method method, HttpRequest request) { Function transformer; Class> handler = getSaxResponseParserClassOrNull(method); if (handler != null) { @@ -272,9 +276,9 @@ public class RestAnnotationProcessor { if (method.isAnnotationPresent(SelectJson.class)) { Type returnVal = getReturnTypeForMethod(method); if (method.isAnnotationPresent(OnlyElement.class)) - returnVal = Types.newParameterizedType(Set.class,returnVal); - transformer = new ParseFirstJsonValueNamed(injector.getInstance(GsonWrapper.class), TypeLiteral.get(returnVal), - method.getAnnotation(SelectJson.class).value()); + returnVal = Types.newParameterizedType(Set.class, returnVal); + transformer = new ParseFirstJsonValueNamed(injector.getInstance(GsonWrapper.class), + TypeLiteral.get(returnVal), method.getAnnotation(SelectJson.class).value()); if (method.isAnnotationPresent(OnlyElement.class)) transformer = Functions.compose(new OnlyElementOrNull(), transformer); } else { @@ -290,7 +294,7 @@ public class RestAnnotationProcessor { @VisibleForTesting public static Function createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation( - Injector injector, Method method) { + Injector injector, Method method) { ExceptionParser annotation = method.getAnnotation(ExceptionParser.class); if (annotation != null) { return injector.getInstance(annotation.value()); @@ -300,9 +304,9 @@ public class RestAnnotationProcessor { @SuppressWarnings("unchecked") @Inject - public RestAnnotationProcessor(Injector injector, ConcurrentMap, Boolean> seedAnnotationCache, - @Named(Constants.PROPERTY_API_VERSION) String apiVersion, ParseSax.Factory parserFactory, HttpUtils utils, - TypeLiteral typeLiteral) { + public RestAnnotationProcessor(Injector injector, Cache, Boolean> seedAnnotationCache, + @Named(Constants.PROPERTY_API_VERSION) String apiVersion, ParseSax.Factory parserFactory, HttpUtils utils, + TypeLiteral typeLiteral) throws ExecutionException { this.declaring = (Class) typeLiteral.getRawType(); this.injector = injector; this.parserFactory = parserFactory; @@ -366,8 +370,8 @@ public class RestAnnotationProcessor { this.name = method.getName(); this.declaringPackage = method.getDeclaringClass().getPackage(); int parametersTypeHashCode = 0; - for (Class param: method.getParameterTypes()) - parametersTypeHashCode +=param.hashCode(); + for (Class param : method.getParameterTypes()) + parametersTypeHashCode += param.hashCode(); this.parametersTypeHashCode = parametersTypeHashCode; } @@ -379,154 +383,166 @@ public class RestAnnotationProcessor { private URI callerEndpoint; public void setCaller(ClassMethodArgs caller) { - seedAnnotationCache.get(caller.getMethod().getDeclaringClass()); + try { + seedAnnotationCache.get(caller.getMethod().getDeclaringClass()); + } catch (ExecutionException e) { + Throwables.propagate(e); + } this.caller = caller; try { callerEndpoint = getEndpointFor(caller.getMethod(), caller.getArgs(), injector); } catch (IllegalStateException e) { + } catch (ExecutionException e) { + Throwables.propagate(e); } } public GeneratedHttpRequest createRequest(Method method, Object... args) { - inputParamValidator.validateMethodParametersOrThrow(method, args); - ClassMethodArgs cma = logger.isTraceEnabled() ? new ClassMethodArgs(method.getDeclaringClass(), method, args) + try { + inputParamValidator.validateMethodParametersOrThrow(method, args); + ClassMethodArgs cma = logger.isTraceEnabled() ? new ClassMethodArgs(method.getDeclaringClass(), method, args) : null; - URI endpoint = callerEndpoint; - try { - if (endpoint == null) { - endpoint = getEndpointFor(method, args, injector); - logger.trace("using endpoint %s for %s", endpoint, cma); + URI endpoint = callerEndpoint; + try { + if (endpoint == null) { + endpoint = getEndpointFor(method, args, injector); + logger.trace("using endpoint %s for %s", endpoint, cma); + } else { + logger.trace("using endpoint %s from caller %s for %s", caller, endpoint, cma); + } + } catch (IllegalStateException e) { + logger.trace("looking up default endpoint for %s", cma); + endpoint = injector.getInstance(Key.get(URI.class, org.jclouds.location.Provider.class)); + logger.trace("using default endpoint %s for %s", endpoint, cma); + } + GeneratedHttpRequest.Builder requestBuilder; + HttpRequest r = RestAnnotationProcessor.findHttpRequestInArgs(args); + if (r != null) { + requestBuilder = GeneratedHttpRequest.Builder. from(r); + endpoint = r.getEndpoint(); } else { - logger.trace("using endpoint %s from caller %s for %s", caller, endpoint, cma); - } - } catch (IllegalStateException e) { - logger.trace("looking up default endpoint for %s", cma); - endpoint = injector.getInstance(Key.get(URI.class, org.jclouds.location.Provider.class)); - logger.trace("using default endpoint %s for %s", endpoint, cma); - } - GeneratedHttpRequest.Builder requestBuilder; - HttpRequest r = RestAnnotationProcessor.findHttpRequestInArgs(args); - if (r != null) { - requestBuilder = GeneratedHttpRequest.Builder. from(r); - endpoint = r.getEndpoint(); - } else { - requestBuilder = GeneratedHttpRequest. builder(); - requestBuilder.method(getHttpMethodOrConstantOrThrowException(method)); - } - - requestBuilder.declaring(declaring).javaMethod(method).args(args).skips(skips); - requestBuilder.filters(getFiltersIfAnnotated(method)); - - UriBuilder builder = uriBuilderProvider.get().uri(endpoint); - - Multimap tokenValues = LinkedHashMultimap.create(); - - tokenValues.put(Constants.PROPERTY_API_VERSION, apiVersion); - - tokenValues.putAll(addPathAndGetTokens(declaring, method, args, builder)); - - Multimap formParams = addFormParams(tokenValues.entries(), method, args); - Multimap queryParams = addQueryParams(tokenValues.entries(), method, args); - Multimap matrixParams = addMatrixParams(tokenValues.entries(), method, args); - Multimap headers = buildHeaders(tokenValues.entries(), method, args); - if (r != null) - headers.putAll(r.getHeaders()); - - if (shouldAddHostHeader(method)) { - StringBuilder hostHeader = new StringBuilder(endpoint.getHost()); - if (endpoint.getPort() != -1) - hostHeader.append(":").append(endpoint.getPort()); - headers.put(HOST, hostHeader.toString()); - } - - Payload payload = null; - HttpRequestOptions options = findOptionsIn(method, args); - if (options != null) { - injector.injectMembers(options);// TODO test case - for (Entry header : options.buildRequestHeaders().entries()) { - headers.put(header.getKey(), Strings2.replaceTokens(header.getValue(), tokenValues.entries())); - } - for (Entry matrix : options.buildMatrixParameters().entries()) { - matrixParams.put(matrix.getKey(), Strings2.replaceTokens(matrix.getValue(), tokenValues.entries())); - } - for (Entry query : options.buildQueryParameters().entries()) { - queryParams.put(query.getKey(), Strings2.replaceTokens(query.getValue(), tokenValues.entries())); - } - for (Entry form : options.buildFormParameters().entries()) { - formParams.put(form.getKey(), Strings2.replaceTokens(form.getValue(), tokenValues.entries())); + requestBuilder = GeneratedHttpRequest. builder(); + requestBuilder.method(getHttpMethodOrConstantOrThrowException(method)); } - String pathSuffix = options.buildPathSuffix(); - if (pathSuffix != null) { - builder.path(pathSuffix); + requestBuilder.declaring(declaring).javaMethod(method).args(args).skips(skips); + requestBuilder.filters(getFiltersIfAnnotated(method)); + + UriBuilder builder = uriBuilderProvider.get().uri(endpoint); + + Multimap tokenValues = LinkedHashMultimap.create(); + + tokenValues.put(Constants.PROPERTY_API_VERSION, apiVersion); + + tokenValues.putAll(addPathAndGetTokens(declaring, method, args, builder)); + + Multimap formParams = addFormParams(tokenValues.entries(), method, args); + Multimap queryParams = addQueryParams(tokenValues.entries(), method, args); + Multimap matrixParams = addMatrixParams(tokenValues.entries(), method, args); + Multimap headers = buildHeaders(tokenValues.entries(), method, args); + if (r != null) + headers.putAll(r.getHeaders()); + + if (shouldAddHostHeader(method)) { + StringBuilder hostHeader = new StringBuilder(endpoint.getHost()); + if (endpoint.getPort() != -1) + hostHeader.append(":").append(endpoint.getPort()); + headers.put(HOST, hostHeader.toString()); } - String stringPayload = options.buildStringPayload(); - if (stringPayload != null) - payload = Payloads.newStringPayload(stringPayload); - } - if (matrixParams.size() > 0) { - for (String key : matrixParams.keySet()) - builder.matrixParam(key, Lists.newArrayList(matrixParams.get(key)).toArray()); - } + Payload payload = null; + HttpRequestOptions options = findOptionsIn(method, args); + if (options != null) { + injector.injectMembers(options);// TODO test case + for (Entry header : options.buildRequestHeaders().entries()) { + headers.put(header.getKey(), Strings2.replaceTokens(header.getValue(), tokenValues.entries())); + } + for (Entry matrix : options.buildMatrixParameters().entries()) { + matrixParams.put(matrix.getKey(), Strings2.replaceTokens(matrix.getValue(), tokenValues.entries())); + } + for (Entry query : options.buildQueryParameters().entries()) { + queryParams.put(query.getKey(), Strings2.replaceTokens(query.getValue(), tokenValues.entries())); + } + for (Entry form : options.buildFormParameters().entries()) { + formParams.put(form.getKey(), Strings2.replaceTokens(form.getValue(), tokenValues.entries())); + } - if (queryParams.size() > 0) { - builder.replaceQuery(ModifyRequest.makeQueryLine(queryParams, null, skips)); - } - - requestBuilder.headers(filterOutContentHeaders(headers)); - - try { - requestBuilder.endpoint(builder.buildFromEncodedMap(Maps2.convertUnsafe(tokenValues))); - } catch (IllegalArgumentException e) { - throw new IllegalStateException(e); - } catch (UriBuilderException e) { - throw new IllegalStateException(e); - } - - if (payload == null) - payload = findPayloadInArgs(args); - List parts = getParts(method, args, concat(tokenValues.entries(), formParams.entries())); - if (parts.size() > 0) { - if (formParams.size() > 0) { - parts = newLinkedList(concat(transform(formParams.entries(), ENTRY_TO_PART), parts)); + String pathSuffix = options.buildPathSuffix(); + if (pathSuffix != null) { + builder.path(pathSuffix); + } + String stringPayload = options.buildStringPayload(); + if (stringPayload != null) + payload = Payloads.newStringPayload(stringPayload); } - payload = new MultipartForm(BOUNDARY, parts); - } else if (formParams.size() > 0) { - payload = Payloads.newUrlEncodedFormPayload(formParams, skips); - } else if (headers.containsKey(CONTENT_TYPE)) { + + if (matrixParams.size() > 0) { + for (String key : matrixParams.keySet()) + builder.matrixParam(key, Lists.newArrayList(matrixParams.get(key)).toArray()); + } + + if (queryParams.size() > 0) { + builder.replaceQuery(ModifyRequest.makeQueryLine(queryParams, null, skips)); + } + + requestBuilder.headers(filterOutContentHeaders(headers)); + + try { + requestBuilder.endpoint(builder.buildFromEncodedMap(Maps2.convertUnsafe(tokenValues))); + } catch (IllegalArgumentException e) { + throw new IllegalStateException(e); + } catch (UriBuilderException e) { + throw new IllegalStateException(e); + } + if (payload == null) - payload = Payloads.newByteArrayPayload(new byte[] {}); - payload.getContentMetadata().setContentType(Iterables.get(headers.get(CONTENT_TYPE), 0)); - } - if (payload != null) { - requestBuilder.payload(payload); - } - GeneratedHttpRequest request = requestBuilder.build(); - - org.jclouds.rest.MapBinder mapBinder = getMapPayloadBinderOrNull(method, args); - if (mapBinder != null) { - Map mapParams = buildPostParams(method, args); - if (method.isAnnotationPresent(PayloadParams.class)) { - PayloadParams params = method.getAnnotation(PayloadParams.class); - addMapPayload(mapParams, params, headers.entries()); + payload = findPayloadInArgs(args); + List parts = getParts(method, args, concat(tokenValues.entries(), formParams.entries())); + if (parts.size() > 0) { + if (formParams.size() > 0) { + parts = newLinkedList(concat(transform(formParams.entries(), ENTRY_TO_PART), parts)); + } + payload = new MultipartForm(BOUNDARY, parts); + } else if (formParams.size() > 0) { + payload = Payloads.newUrlEncodedFormPayload(formParams, skips); + } else if (headers.containsKey(CONTENT_TYPE)) { + if (payload == null) + payload = Payloads.newByteArrayPayload(new byte[] {}); + payload.getContentMetadata().setContentType(Iterables.get(headers.get(CONTENT_TYPE), 0)); } - request = mapBinder.bindToRequest(request, mapParams); - } else { - request = decorateRequest(request); - } + if (payload != null) { + requestBuilder.payload(payload); + } + GeneratedHttpRequest request = requestBuilder.build(); - if (request.getPayload() != null) - request.getPayload().getContentMetadata().setPropertiesFromHttpHeaders(headers); - utils.checkRequestHasRequiredProperties(request); - return request; + org.jclouds.rest.MapBinder mapBinder = getMapPayloadBinderOrNull(method, args); + if (mapBinder != null) { + Map mapParams = buildPostParams(method, args); + if (method.isAnnotationPresent(PayloadParams.class)) { + PayloadParams params = method.getAnnotation(PayloadParams.class); + addMapPayload(mapParams, params, headers.entries()); + } + request = mapBinder.bindToRequest(request, mapParams); + } else { + request = decorateRequest(request); + } + + if (request.getPayload() != null) + request.getPayload().getContentMetadata().setPropertiesFromHttpHeaders(headers); + utils.checkRequestHasRequiredProperties(request); + return request; + } catch (ExecutionException e) { + Throwables.propagate(e); + return null; + } } public static Multimap filterOutContentHeaders(Multimap headers) { // TODO make a filter like {@link Maps.filterKeys} instead of this ImmutableMultimap.Builder headersBuilder = ImmutableMultimap.builder(); - // http message usually comes in as a null key header, let's filter it out. + // http message usually comes in as a null key header, let's filter it + // out. for (String header : Iterables.filter(headers.keySet(), Predicates.notNull())) { if (!ContentMetadata.HTTP_HEADERS.contains(header)) { headersBuilder.putAll(header, headers.get(header)); @@ -537,7 +553,7 @@ public class RestAnnotationProcessor { public static final String BOUNDARY = "--JCLOUDS--"; - private Multimap addPathAndGetTokens(Class clazz, Method method, Object[] args, UriBuilder builder) { + private Multimap addPathAndGetTokens(Class clazz, Method method, Object[] args, UriBuilder builder) throws ExecutionException { if (clazz.isAnnotationPresent(Path.class)) builder.path(clazz); if (method.isAnnotationPresent(Path.class)) @@ -550,14 +566,14 @@ public class RestAnnotationProcessor { } public static URI replaceQuery(Provider uriBuilderProvider, URI in, String newQuery, - @Nullable Comparator> sorter, char... skips) { + @Nullable Comparator> sorter, char... skips) { UriBuilder builder = uriBuilderProvider.get().uri(in); builder.replaceQuery(ModifyRequest.makeQueryLine(ModifyRequest.parseQueryToMap(newQuery), sorter, skips)); return builder.build(); } private Multimap addMatrixParams(Collection> tokenValues, Method method, - Object... args) { + Object... args) throws ExecutionException { Multimap matrixMap = LinkedListMultimap.create(); if (declaring.isAnnotationPresent(MatrixParams.class)) { MatrixParams matrix = declaring.getAnnotation(MatrixParams.class); @@ -576,7 +592,7 @@ public class RestAnnotationProcessor { } private Multimap addFormParams(Collection> tokenValues, Method method, - Object... args) { + Object... args) throws ExecutionException { Multimap formMap = LinkedListMultimap.create(); if (declaring.isAnnotationPresent(FormParams.class)) { FormParams form = declaring.getAnnotation(FormParams.class); @@ -595,7 +611,7 @@ public class RestAnnotationProcessor { } private Multimap addQueryParams(Collection> tokenValues, Method method, - Object... args) { + Object... args) throws ExecutionException { Multimap queryMap = LinkedListMultimap.create(); if (declaring.isAnnotationPresent(QueryParams.class)) { QueryParams query = declaring.getAnnotation(QueryParams.class); @@ -614,7 +630,7 @@ public class RestAnnotationProcessor { } private void addForm(Multimap formParams, FormParams form, - Collection> tokenValues) { + Collection> tokenValues) { for (int i = 0; i < form.keys().length; i++) { if (form.values()[i].equals(FormParams.NULL)) { formParams.removeAll(form.keys()[i]); @@ -626,7 +642,7 @@ public class RestAnnotationProcessor { } private void addQuery(Multimap queryParams, QueryParams query, - Collection> tokenValues) { + Collection> tokenValues) { for (int i = 0; i < query.keys().length; i++) { if (query.values()[i].equals(QueryParams.NULL)) { queryParams.removeAll(query.keys()[i]); @@ -638,7 +654,7 @@ public class RestAnnotationProcessor { } private void addMatrix(Multimap matrixParams, MatrixParams matrix, - Collection> tokenValues) { + Collection> tokenValues) { for (int i = 0; i < matrix.keys().length; i++) { if (matrix.values()[i].equals(MatrixParams.NULL)) { matrixParams.removeAll(matrix.keys()[i]); @@ -650,7 +666,7 @@ public class RestAnnotationProcessor { } private void addMapPayload(Map postParams, PayloadParams mapDefaults, - Collection> tokenValues) { + Collection> tokenValues) { for (int i = 0; i < mapDefaults.keys().length; i++) { if (mapDefaults.values()[i].equals(PayloadParams.NULL)) { postParams.put(mapDefaults.keys()[i], null); @@ -683,9 +699,10 @@ public class RestAnnotationProcessor { } @VisibleForTesting - public static URI getEndpointInParametersOrNull(Method method, final Object[] args, Injector injector) { + public static URI getEndpointInParametersOrNull(Method method, final Object[] args, Injector injector) + throws ExecutionException { Map> map = indexWithAtLeastOneAnnotation(method, - methodToIndexOfParamToEndpointParamAnnotations); + methodToIndexOfParamToEndpointParamAnnotations); if (map.size() >= 1 && args.length > 0) { EndpointParam firstAnnotation = (EndpointParam) get(get(map.values(), 0), 0); Function parser = injector.getInstance(firstAnnotation.parser()); @@ -694,8 +711,8 @@ public class RestAnnotationProcessor { int index = map.keySet().iterator().next(); try { URI returnVal = parser.apply(args[index]); - checkArgument(returnVal != null, String.format("endpoint for [%s] not configured for %s", args[index], - method)); + checkArgument(returnVal != null, + String.format("endpoint for [%s] not configured for %s", args[index], method)); return returnVal; } catch (NullPointerException e) { throw new IllegalArgumentException(String.format("argument at index %d on method %s", index, method), e); @@ -712,19 +729,19 @@ public class RestAnnotationProcessor { }); try { URI returnVal = parser.apply(argsToParse); - checkArgument(returnVal != null, String.format("endpoint for [%s] not configured for %s", argsToParse, - method)); + checkArgument(returnVal != null, + String.format("endpoint for [%s] not configured for %s", argsToParse, method)); return returnVal; } catch (NullPointerException e) { throw new IllegalArgumentException(String.format("illegal argument in [%s] for method %s", argsToParse, - method), e); + method), e); } } } return null; } - public static URI getEndpointFor(Method method, Object[] args, Injector injector) { + public static URI getEndpointFor(Method method, Object[] args, Injector injector) throws ExecutionException { URI endpoint = getEndpointInParametersOrNull(method, args, injector); if (endpoint == null) { Endpoint annotation; @@ -753,29 +770,29 @@ public class RestAnnotationProcessor { public static final TypeLiteral> futureHttpResponseLiteral = new TypeLiteral>() { }; - @SuppressWarnings( { "unchecked", "rawtypes" }) + @SuppressWarnings({ "unchecked", "rawtypes" }) public static Key> getParserOrThrowException(Method method) { ResponseParser annotation = method.getAnnotation(ResponseParser.class); if (annotation == null) { if (method.getReturnType().equals(void.class) - || TypeLiteral.get(method.getGenericReturnType()).equals(futureVoidLiteral)) { + || TypeLiteral.get(method.getGenericReturnType()).equals(futureVoidLiteral)) { return Key.get(ReleasePayloadAndReturn.class); } else if (method.getReturnType().equals(boolean.class) || method.getReturnType().equals(Boolean.class) - || TypeLiteral.get(method.getGenericReturnType()).equals(futureBooleanLiteral)) { + || TypeLiteral.get(method.getGenericReturnType()).equals(futureBooleanLiteral)) { return Key.get(ReturnTrueIf2xx.class); } else if (method.getReturnType().equals(InputStream.class) - || TypeLiteral.get(method.getGenericReturnType()).equals(futureInputStreamLiteral)) { + || TypeLiteral.get(method.getGenericReturnType()).equals(futureInputStreamLiteral)) { return Key.get(ReturnInputStream.class); } else if (method.getReturnType().equals(HttpResponse.class) - || TypeLiteral.get(method.getGenericReturnType()).equals(futureHttpResponseLiteral)) { + || TypeLiteral.get(method.getGenericReturnType()).equals(futureHttpResponseLiteral)) { return Key.get((Class) IdentityFunction.class); } else if (getAcceptHeadersOrNull(method).contains(MediaType.APPLICATION_JSON)) { return getJsonParserKeyForMethod(method); } else if (method.getReturnType().equals(String.class) - || TypeLiteral.get(method.getGenericReturnType()).equals(futureStringLiteral)) { + || TypeLiteral.get(method.getGenericReturnType()).equals(futureStringLiteral)) { return Key.get(ReturnStringIf2xx.class); } else if (method.getReturnType().equals(URI.class) - || TypeLiteral.get(method.getGenericReturnType()).equals(futureURILiteral)) { + || TypeLiteral.get(method.getGenericReturnType()).equals(futureURILiteral)) { return Key.get(ParseURIFromListOrLocationHeaderIf20x.class); } else { throw new IllegalStateException("You must specify a ResponseParser annotation on: " + method.toString()); @@ -804,7 +821,7 @@ public class RestAnnotationProcessor { return returnVal; } - @SuppressWarnings( { "unchecked", "rawtypes" }) + @SuppressWarnings({ "unchecked", "rawtypes" }) public static Key> getJsonParserKeyForMethodAnType(Method method, Type returnVal) { ParameterizedType parserType; if (method.isAnnotationPresent(Unwrap.class)) { @@ -820,7 +837,7 @@ public class RestAnnotationProcessor { parserType = Types.newParameterizedType(UnwrapOnlyNestedJsonValueInSet.class, returnVal); else throw new IllegalStateException(String.format("depth(%d) edgeCollection(%s) not yet supported for @Unwrap", - depth, edgeCollection)); + depth, edgeCollection)); } else { parserType = Types.newParameterizedType(ParseJson.class, returnVal); } @@ -850,7 +867,7 @@ public class RestAnnotationProcessor { } else { if (postBinders[0] instanceof org.jclouds.rest.MapBinder) { throw new IllegalArgumentException("we currently do not support multiple varargs postBinders in: " - + method.getName()); + + method.getName()); } } } else if (arg instanceof org.jclouds.rest.MapBinder) { @@ -883,8 +900,8 @@ public class RestAnnotationProcessor { Set requests = getHttpMethods(method); if (requests == null || requests.size() != 1) { throw new IllegalStateException( - "You must use at least one, but no more than one http method or pathparam annotation on: " - + method.toString()); + "You must use at least one, but no more than one http method or pathparam annotation on: " + + method.toString()); } return requests.iterator().next(); } @@ -902,11 +919,13 @@ public class RestAnnotationProcessor { } }; - public GeneratedHttpRequest decorateRequest(GeneratedHttpRequest request) { + public GeneratedHttpRequest decorateRequest(GeneratedHttpRequest request) throws NegativeArraySizeException, + ExecutionException { OUTER: for (Entry> entry : concat(// - filterValues(methodToIndexOfParamToBinderParamAnnotation.get(request.getJavaMethod()), notEmpty) - .entrySet(), // - filterValues(methodToIndexOfParamToWrapWithAnnotation.get(request.getJavaMethod()), notEmpty).entrySet())) { + filterValues(methodToIndexOfParamToBinderParamAnnotation.get(request.getJavaMethod()).asMap(), notEmpty) + .entrySet(), // + filterValues(methodToIndexOfParamToWrapWithAnnotation.get(request.getJavaMethod()).asMap(), notEmpty) + .entrySet())) { boolean shouldBreak = false; Annotation annotation = Iterables.get(entry.getValue(), 0); Binder binder; @@ -914,7 +933,7 @@ public class RestAnnotationProcessor { binder = injector.getInstance(BinderParam.class.cast(annotation).value()); else binder = injector.getInstance(BindToJsonPayloadWrappedWith.Factory.class).create( - WrapWith.class.cast(annotation).value()); + WrapWith.class.cast(annotation).value()); if (request.getArgs().size() >= entry.getKey() + 1 && request.getArgs().get(entry.getKey()) != null) { Object input; Class parameterType = request.getJavaMethod().getParameterTypes()[entry.getKey()]; @@ -947,28 +966,28 @@ public class RestAnnotationProcessor { } public static Map> indexWithOnlyOneAnnotation(Method method, String description, - Map>> toRefine) { + Cache>> toRefine) throws ExecutionException { Map> indexToPayloadAnnotation = indexWithAtLeastOneAnnotation(method, toRefine); if (indexToPayloadAnnotation.size() > 1) { throw new IllegalStateException(String.format( - "You must not specify more than one %s annotation on: %s; found %s", description, method.toString(), - indexToPayloadAnnotation)); + "You must not specify more than one %s annotation on: %s; found %s", description, method.toString(), + indexToPayloadAnnotation)); } return indexToPayloadAnnotation; } private static Map> indexWithAtLeastOneAnnotation(Method method, - Map>> toRefine) { - Map> indexToPayloadAnnotation = filterValues(toRefine.get(method), - new Predicate>() { - public boolean apply(Set input) { - return input.size() == 1; - } - }); + Cache>> toRefine) throws ExecutionException { + Map> indexToPayloadAnnotation = filterValues(toRefine.get(method).asMap(), + new Predicate>() { + public boolean apply(Set input) { + return input.size() == 1; + } + }); return indexToPayloadAnnotation; } - private HttpRequestOptions findOptionsIn(Method method, Object... args) { + private HttpRequestOptions findOptionsIn(Method method, Object... args) throws ExecutionException { for (int index : methodToIndexesOfOptions.get(method)) { if (args.length >= index + 1) {// accomodate varargs if (args[index] instanceof Object[]) { @@ -983,7 +1002,7 @@ public class RestAnnotationProcessor { } else { if (options[0] instanceof HttpRequestOptions) { throw new IllegalArgumentException("we currently do not support multiple varargs options in: " - + method.getName()); + + method.getName()); } } } else { @@ -995,11 +1014,11 @@ public class RestAnnotationProcessor { } public Multimap buildHeaders(Collection> tokenValues, Method method, - final Object... args) { + final Object... args) throws ExecutionException { Multimap headers = LinkedHashMultimap.create(); addHeaderIfAnnotationPresentOnMethod(headers, method, tokenValues); - Map> indexToHeaderParam = methodToIndexOfParamToHeaderParamAnnotations.get(method); - for (Entry> entry : indexToHeaderParam.entrySet()) { + Cache> indexToHeaderParam = methodToIndexOfParamToHeaderParamAnnotations.get(method); + for (Entry> entry : indexToHeaderParam.asMap().entrySet()) { for (Annotation key : entry.getValue()) { String value = args[entry.getKey()].toString(); value = Strings2.replaceTokens(value, tokenValues); @@ -1042,7 +1061,7 @@ public class RestAnnotationProcessor { } public void addHeaderIfAnnotationPresentOnMethod(Multimap headers, Method method, - Collection> tokenValues) { + Collection> tokenValues) { if (declaring.isAnnotationPresent(Headers.class)) { Headers header = declaring.getAnnotation(Headers.class); addHeader(headers, header, tokenValues); @@ -1054,7 +1073,7 @@ public class RestAnnotationProcessor { } private void addHeader(Multimap headers, Headers header, - Collection> tokenValues) { + Collection> tokenValues) { for (int i = 0; i < header.keys().length; i++) { String value = header.values()[i]; value = Strings2.replaceTokens(value, tokenValues); @@ -1063,10 +1082,10 @@ public class RestAnnotationProcessor { } - List getParts(Method method, Object[] args, Iterable> iterable) { + List getParts(Method method, Object[] args, Iterable> iterable) throws ExecutionException { List parts = newLinkedList(); - Map> indexToPartParam = methodToIndexOfParamToPartParamAnnotations.get(method); - for (Entry> entry : indexToPartParam.entrySet()) { + Cache> indexToPartParam = methodToIndexOfParamToPartParamAnnotations.get(method); + for (Entry> entry : indexToPartParam.asMap().entrySet()) { for (Annotation key : entry.getValue()) { PartParam param = (PartParam) key; PartOptions options = new PartOptions(); @@ -1101,12 +1120,12 @@ public class RestAnnotationProcessor { return null; } - private Multimap getPathParamKeyValues(Method method, Object... args) { + private Multimap getPathParamKeyValues(Method method, Object... args) throws ExecutionException { Multimap pathParamValues = LinkedHashMultimap.create(); - Map> indexToPathParam = methodToIndexOfParamToPathParamAnnotations.get(method); + Cache> indexToPathParam = methodToIndexOfParamToPathParamAnnotations.get(method); - Map> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method); - for (Entry> entry : indexToPathParam.entrySet()) { + Cache> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method); + for (Entry> entry : indexToPathParam.asMap().entrySet()) { for (Annotation key : entry.getValue()) { Set extractors = indexToParamExtractor.get(entry.getKey()); String paramKey = ((PathParam) key).value(); @@ -1138,12 +1157,12 @@ public class RestAnnotationProcessor { return encoded; } - private Multimap getMatrixParamKeyValues(Method method, Object... args) { + private Multimap getMatrixParamKeyValues(Method method, Object... args) throws ExecutionException { Multimap matrixParamValues = LinkedHashMultimap.create(); - Map> indexToMatrixParam = methodToIndexOfParamToMatrixParamAnnotations.get(method); + Cache> indexToMatrixParam = methodToIndexOfParamToMatrixParamAnnotations.get(method); - Map> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method); - for (Entry> entry : indexToMatrixParam.entrySet()) { + Cache> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method); + for (Entry> entry : indexToMatrixParam.asMap().entrySet()) { for (Annotation key : entry.getValue()) { Set extractors = indexToParamExtractor.get(entry.getKey()); String paramKey = ((MatrixParam) key).value(); @@ -1167,12 +1186,12 @@ public class RestAnnotationProcessor { return matrixParamValues; } - private Multimap getFormParamKeyValues(Method method, Object... args) { + private Multimap getFormParamKeyValues(Method method, Object... args) throws ExecutionException { Multimap formParamValues = LinkedHashMultimap.create(); - Map> indexToFormParam = methodToIndexOfParamToFormParamAnnotations.get(method); + Cache> indexToFormParam = methodToIndexOfParamToFormParamAnnotations.get(method); - Map> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method); - for (Entry> entry : indexToFormParam.entrySet()) { + Cache> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method); + for (Entry> entry : indexToFormParam.asMap().entrySet()) { for (Annotation key : entry.getValue()) { Set extractors = indexToParamExtractor.get(entry.getKey()); String paramKey = ((FormParam) key).value(); @@ -1196,12 +1215,12 @@ public class RestAnnotationProcessor { return formParamValues; } - private Multimap getQueryParamKeyValues(Method method, Object... args) { + private Multimap getQueryParamKeyValues(Method method, Object... args) throws ExecutionException { Multimap queryParamValues = LinkedHashMultimap.create(); - Map> indexToQueryParam = methodToIndexOfParamToQueryParamAnnotations.get(method); + Cache> indexToQueryParam = methodToIndexOfParamToQueryParamAnnotations.get(method); - Map> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method); - for (Entry> entry : indexToQueryParam.entrySet()) { + Cache> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method); + for (Entry> entry : indexToQueryParam.asMap().entrySet()) { for (Annotation key : entry.getValue()) { Set extractors = indexToParamExtractor.get(entry.getKey()); String paramKey = ((QueryParam) key).value(); @@ -1225,11 +1244,11 @@ public class RestAnnotationProcessor { return queryParamValues; } - private Map buildPostParams(Method method, Object... args) { + private Map buildPostParams(Method method, Object... args) throws ExecutionException { Map postParams = newHashMap(); - Map> indexToPathParam = methodToIndexOfParamToPostParamAnnotations.get(method); - Map> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method); - for (Entry> entry : indexToPathParam.entrySet()) { + Cache> indexToPathParam = methodToIndexOfParamToPostParamAnnotations.get(method); + Cache> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method); + for (Entry> entry : indexToPathParam.asMap().entrySet()) { for (Annotation key : entry.getValue()) { Set extractors = indexToParamExtractor.get(entry.getKey()); String paramKey = ((PayloadParam) key).value(); diff --git a/core/src/main/java/org/jclouds/rest/internal/SeedAnnotationCache.java b/core/src/main/java/org/jclouds/rest/internal/SeedAnnotationCache.java index e3f6ce0e73..abb5429c11 100644 --- a/core/src/main/java/org/jclouds/rest/internal/SeedAnnotationCache.java +++ b/core/src/main/java/org/jclouds/rest/internal/SeedAnnotationCache.java @@ -36,6 +36,7 @@ import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfP import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexesOfOptions; import java.lang.reflect.Method; +import java.util.concurrent.ExecutionException; import javax.annotation.Resource; import javax.inject.Named; @@ -48,7 +49,7 @@ import org.jclouds.logging.Logger; import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.internal.RestAnnotationProcessor.MethodKey; -import com.google.common.base.Function; +import com.google.common.cache.CacheLoader; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import com.google.inject.Inject; @@ -63,7 +64,7 @@ import com.google.inject.Provides; */ @Singleton -public class SeedAnnotationCache implements Function, Boolean> { +public class SeedAnnotationCache extends CacheLoader, Boolean> { @Resource protected Logger logger = Logger.NULL; @@ -82,7 +83,8 @@ public class SeedAnnotationCache implements Function, Boolean> { constants.put(key, value); } - public Boolean apply(Class declaring) { + @Override + public Boolean load(Class declaring) throws ExecutionException { for (Method method : difference(ImmutableSet.copyOf(declaring.getMethods()), ImmutableSet.copyOf(Object.class .getMethods()))) { if (isHttpMethod(method) || method.isAnnotationPresent(Delegate.class)) { diff --git a/core/src/main/java/org/jclouds/util/Patterns.java b/core/src/main/java/org/jclouds/util/Patterns.java index f6ba188dd8..c293e0d85d 100644 --- a/core/src/main/java/org/jclouds/util/Patterns.java +++ b/core/src/main/java/org/jclouds/util/Patterns.java @@ -20,11 +20,12 @@ package org.jclouds.util; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; -import java.util.Map; +import java.util.concurrent.ExecutionException; import java.util.regex.Pattern; -import com.google.common.base.Function; -import com.google.common.collect.MapMaker; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; /** * @@ -43,36 +44,38 @@ public class Patterns { public static final Pattern _7E_PATTERN = Pattern.compile("%7E"); public static final Pattern NEWLINE_PATTERN = Pattern.compile("\r?\n"); public static final Pattern SLASH_PATTERN = Pattern.compile("[/]"); - public static final Pattern IP_PATTERN = Pattern - .compile("b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).)" - + "{3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)b"); + public static final Pattern IP_PATTERN = Pattern.compile("b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).)" + + "{3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)b"); public static final Pattern LEADING_SLASHES = Pattern.compile("^[/]+"); public static final Pattern TRAILING_SLASHES = Pattern.compile("[/]*$"); public static final Pattern REST_CONTEXT_BUILDER = Pattern.compile("(.*ContextBuilder)<([^,]+), ?([^>]+)>"); - public final static Map CHAR_TO_ENCODED_PATTERN = new MapMaker() - .makeComputingMap(new Function() { - public Pattern apply(Character plain) { - try { - String encoded = URLEncoder.encode(plain + "", "UTF-8"); - return Pattern.compile(encoded); - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException("Bad encoding on input: " + plain, e); - } + public final static Cache CHAR_TO_ENCODED_PATTERN = CacheBuilder.newBuilder() + . build(new CacheLoader() { + @Override + public Pattern load(Character plain) throws ExecutionException { + try { + String encoded = URLEncoder.encode(plain + "", "UTF-8"); + return Pattern.compile(encoded); + } catch (UnsupportedEncodingException e) { + throw new ExecutionException("Bad encoding on input: " + plain, e); } - }); + } + }); - public final static Map CHAR_TO_PATTERN = new MapMaker() - .makeComputingMap(new Function() { - public Pattern apply(Character plain) { - return Pattern.compile(plain + ""); - } - }); + public final static Cache CHAR_TO_PATTERN = CacheBuilder.newBuilder() + . build(new CacheLoader() { + @Override + public Pattern load(Character plain) { + return Pattern.compile(plain + ""); + } + }); - public final static Map TOKEN_TO_PATTERN = new MapMaker() - .makeComputingMap(new Function() { - public Pattern apply(String tokenValue) { - return Pattern.compile("\\{" + tokenValue + "\\}"); - } - }); + public final static Cache TOKEN_TO_PATTERN = CacheBuilder.newBuilder() + . build(new CacheLoader() { + @Override + public Pattern load(String tokenValue) { + return Pattern.compile("\\{" + tokenValue + "\\}"); + } + }); } diff --git a/core/src/main/java/org/jclouds/util/Strings2.java b/core/src/main/java/org/jclouds/util/Strings2.java index 4211869f60..71dd46631e 100644 --- a/core/src/main/java/org/jclouds/util/Strings2.java +++ b/core/src/main/java/org/jclouds/util/Strings2.java @@ -37,6 +37,7 @@ import java.net.URLDecoder; import java.net.URLEncoder; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.ExecutionException; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -70,6 +71,8 @@ public class Strings2 { return returnVal; } catch (UnsupportedEncodingException e) { throw new IllegalStateException("Bad encoding on input: " + in, e); + } catch (ExecutionException e) { + throw new IllegalStateException("error creating pattern: " + in, e); } } @@ -87,7 +90,11 @@ public class Strings2 { public static String replaceTokens(String value, Iterable> tokenValues) { for (Entry tokenValue : tokenValues) { - value = Strings2.replaceAll(value, TOKEN_TO_PATTERN.get(tokenValue.getKey()), tokenValue.getValue()); + try { + value = Strings2.replaceAll(value, TOKEN_TO_PATTERN.get(tokenValue.getKey()), tokenValue.getValue()); + } catch (ExecutionException e) { + throw new IllegalStateException("error creating pattern: " + tokenValue.getKey(), e); + } } return value; } @@ -107,7 +114,11 @@ public class Strings2 { public static String replaceAll(String input, char match, String replacement) { if (input.indexOf(match) != -1) { - input = CHAR_TO_PATTERN.get(match).matcher(input).replaceAll(replacement); + try { + input = CHAR_TO_PATTERN.get(match).matcher(input).replaceAll(replacement); + } catch (ExecutionException e) { + throw new IllegalStateException("error creating pattern: " + match, e); + } } return input; } diff --git a/core/src/main/java/org/jclouds/util/Throwables2.java b/core/src/main/java/org/jclouds/util/Throwables2.java index 57b2f1e1d6..b7c1de840e 100644 --- a/core/src/main/java/org/jclouds/util/Throwables2.java +++ b/core/src/main/java/org/jclouds/util/Throwables2.java @@ -88,8 +88,8 @@ public class Throwables2 { throw (Exception) throwable; } } - Throwables.throwCause(exception, true); - return exception; + Throwables.propagateIfPossible(exception.getCause(), Exception.class); + throw exception; } public static T propagateAuthorizationOrOriginalException(Exception e) { diff --git a/core/src/test/java/com/google/common/util/concurrent/FuturesTransformPerformanceTest.java b/core/src/test/java/com/google/common/util/concurrent/FuturesTransformPerformanceTest.java index 95853bf184..3165f3bd72 100644 --- a/core/src/test/java/com/google/common/util/concurrent/FuturesTransformPerformanceTest.java +++ b/core/src/test/java/com/google/common/util/concurrent/FuturesTransformPerformanceTest.java @@ -67,7 +67,7 @@ import com.google.common.collect.Sets; * * @author Adrian Cole */ -@Test(groups = "performance", enabled = false, sequential = true, testName = "FuturesTransformPerformanceTest") +@Test(groups = "performance", enabled = false, singleThreaded = true, testName = "FuturesTransformPerformanceTest") public class FuturesTransformPerformanceTest { private static final int FUDGE = 5; private static final int COUNT = 100; @@ -175,7 +175,7 @@ public class FuturesTransformPerformanceTest { long start = System.currentTimeMillis(); Map> responses = newHashMap(); for (int i = 0; i < COUNT; i++) - responses.put(i + "", Futures.transform(Futures.makeListenable(simultateIO(), chainExecutor), + responses.put(i + "", Futures.transform(JdkFutureAdapters.listenInPoolThread(simultateIO(), chainExecutor), new Function() { @Override diff --git a/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java b/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java index 13c972697e..60fcf93f5a 100644 --- a/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java +++ b/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java @@ -25,7 +25,6 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Set; import java.util.concurrent.Callable; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -36,6 +35,10 @@ import org.jclouds.internal.ClassMethodArgs; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; +import com.google.common.base.Functions; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.ListenableFuture; @@ -162,8 +165,8 @@ public class SyncProxyTest { @BeforeTest public void setUp() throws IllegalArgumentException, SecurityException, NoSuchMethodException { - sync = SyncProxy.proxy(Sync.class, new SyncProxy(Sync.class, new Async(), - new ConcurrentHashMap(), ImmutableMap., Class> of())); + Cache cache = CacheBuilder.newBuilder().build(CacheLoader.from(Functions.constant(null))); + sync = SyncProxy.proxy(Sync.class, new SyncProxy(Sync.class, new Async(),cache, ImmutableMap., Class> of())); // just to warm up sync.string(); } @@ -225,8 +228,9 @@ public class SyncProxyTest { @Test(expectedExceptions = IllegalArgumentException.class) public void testWrongTypedException() throws IllegalArgumentException, SecurityException, NoSuchMethodException, IOException { + Cache cache = CacheBuilder.newBuilder().build(CacheLoader.from(Functions.constant(null))); SyncProxy.proxy(SyncWrongException.class, new SyncProxy(SyncWrongException.class, new Async(), - new ConcurrentHashMap(), ImmutableMap., Class> of())); + cache, ImmutableMap., Class> of())); } private static interface SyncNoTimeOut { @@ -243,8 +247,9 @@ public class SyncProxyTest { @Test(expectedExceptions = IllegalArgumentException.class) public void testNoTimeOutException() throws IllegalArgumentException, SecurityException, NoSuchMethodException, IOException { + Cache cache = CacheBuilder.newBuilder().build(CacheLoader.from(Functions.constant(null))); SyncProxy.proxy(SyncNoTimeOut.class, new SyncProxy(SyncNoTimeOut.class, new Async(), - new ConcurrentHashMap(), ImmutableMap., Class> of())); + cache, ImmutableMap., Class> of())); } } diff --git a/core/src/test/java/org/jclouds/functions/CacheLearningTest.java b/core/src/test/java/org/jclouds/functions/CacheLearningTest.java new file mode 100644 index 0000000000..58113af471 --- /dev/null +++ b/core/src/test/java/org/jclouds/functions/CacheLearningTest.java @@ -0,0 +1,81 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.functions; + +import static org.testng.Assert.assertEquals; + +import java.util.concurrent.ExecutionException; + +import org.testng.annotations.Test; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.util.concurrent.UncheckedExecutionException; + +/** + * + * @author Adrian Cole + */ +@Test(groups = "unit", singleThreaded = true, testName = "CacheLearningTest") +public class CacheLearningTest { + @Test + public void howTo() throws ExecutionException { + Cache cache = CacheBuilder.newBuilder().build(new CacheLoader() { + + @Override + public String load(String key) throws Exception { + if (key.equals("runtimeexception")) + throw new RuntimeException("runtimeexception"); + if (key.equals("exception")) + throw new Exception("exception"); + return key.equals("foo") ? null : key; + } + + @Override + public String toString() { + return "testLoader"; + } + + }); + try { + cache.get("foo"); + } catch (NullPointerException e) { + assertEquals(e.getMessage(), "testLoader returned null for key foo."); + } + try { + cache.get("exception"); + } catch (ExecutionException e) { + assertEquals(e.getMessage(), "java.lang.Exception: exception"); + } + try { + cache.get("runtimeexception"); + } catch (UncheckedExecutionException e) { + assertEquals(e.getMessage(), "java.lang.RuntimeException: runtimeexception"); + } + try { + cache.getUnchecked("exception"); + } catch (UncheckedExecutionException e) { + assertEquals(e.getMessage(), "java.lang.Exception: exception"); + } + assertEquals(cache.get("bar"), "bar"); + assertEquals(cache.get("baz"), "baz"); + assertEquals(cache.asMap().size(), 2); + } +} diff --git a/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java b/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java index 31b301c971..f98fb7ab18 100644 --- a/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java +++ b/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java @@ -2157,7 +2157,7 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { } @Test - public void testOneHeader() throws SecurityException, NoSuchMethodException { + public void testOneHeader() throws SecurityException, NoSuchMethodException, ExecutionException { Method method = TestHeaders.class.getMethod("oneHeader", String.class); Multimap headers = factory(TestHeaders.class).buildHeaders( ImmutableMultimap. of().entries(), method, "robot"); @@ -2166,7 +2166,7 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { } @Test - public void testOneIntHeader() throws SecurityException, NoSuchMethodException { + public void testOneIntHeader() throws SecurityException, NoSuchMethodException, ExecutionException { Method method = TestHeaders.class.getMethod("oneIntHeader", int.class); Multimap headers = factory(TestHeaders.class).buildHeaders( ImmutableMultimap. of().entries(), method, 1); @@ -2175,7 +2175,7 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { } @Test - public void testTwoDifferentHeaders() throws SecurityException, NoSuchMethodException { + public void testTwoDifferentHeaders() throws SecurityException, NoSuchMethodException, ExecutionException { Method method = TestHeaders.class.getMethod("twoDifferentHeaders", String.class, String.class); Multimap headers = factory(TestHeaders.class).buildHeaders( ImmutableMultimap. of().entries(), method, "robot", "egg"); @@ -2185,7 +2185,7 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { } @Test - public void testTwoSameHeaders() throws SecurityException, NoSuchMethodException { + public void testTwoSameHeaders() throws SecurityException, NoSuchMethodException, ExecutionException { Method method = TestHeaders.class.getMethod("twoSameHeaders", String.class, String.class); Multimap headers = factory(TestHeaders.class).buildHeaders( ImmutableMultimap. of().entries(), method, "robot", "egg"); @@ -2228,7 +2228,7 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { @SuppressWarnings("static-access") @Test - public void testOneEndpointParam() throws SecurityException, NoSuchMethodException { + public void testOneEndpointParam() throws SecurityException, NoSuchMethodException, ExecutionException { Method method = TestEndpointParams.class.getMethod("oneEndpointParam", String.class); URI uri = factory(TestEndpointParams.class).getEndpointInParametersOrNull(method, new Object[] { "robot" }, injector); @@ -2238,7 +2238,7 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { @SuppressWarnings("static-access") @Test - public void testTwoDifferentEndpointParams() throws SecurityException, NoSuchMethodException { + public void testTwoDifferentEndpointParams() throws SecurityException, NoSuchMethodException, ExecutionException { Method method = TestEndpointParams.class.getMethod("twoEndpointParams", String.class, String.class); URI uri = factory(TestEndpointParams.class).getEndpointInParametersOrNull(method, new Object[] { "robot", "egg" }, injector); diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java index 981043ad9d..afc688ed70 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2ComputeService.java @@ -64,6 +64,7 @@ import org.jclouds.util.Preconditions2; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Predicate; import com.google.common.base.Supplier; +import com.google.common.cache.Cache; /** * @author Adrian Cole @@ -71,7 +72,7 @@ import com.google.common.base.Supplier; @Singleton public class AWSEC2ComputeService extends EC2ComputeService { - private final Map placementGroupMap; + private final Cache placementGroupMap; private final Predicate placementGroupDeleted; private final AWSEC2Client ec2Client; @@ -90,8 +91,8 @@ public class AWSEC2ComputeService extends EC2ComputeService { RunScriptOnNode.Factory runScriptOnNodeFactory, InitAdminAccess initAdminAccess, PersistNodeCredentials persistNodeCredentials, Timeouts timeouts, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, AWSEC2Client ec2Client, - Map credentialsMap, @Named("SECURITY") Map securityGroupMap, - @Named("PLACEMENT") Map placementGroupMap, + Cache credentialsMap, @Named("SECURITY") Cache securityGroupMap, + @Named("PLACEMENT") Cache placementGroupMap, @Named("DELETED") Predicate placementGroupDeleted) { super(context, credentialStore, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, stopNodeStrategy, @@ -117,7 +118,7 @@ public class AWSEC2ComputeService extends EC2ComputeService { checkState( placementGroupDeleted.apply(new PlacementGroup(region, placementGroup, "cluster", State.PENDING)), String.format("placementGroup region(%s) name(%s) failed to delete", region, placementGroup)); - placementGroupMap.remove(new RegionAndName(region, placementGroup)); + placementGroupMap.invalidate(new RegionAndName(region, placementGroup)); logger.debug("<< deleted placementGroup(%s)", placementGroup); } catch (IllegalStateException e) { logger.debug("<< inUse placementGroup(%s)", placementGroup); 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 eb9a4350fa..4d95533b72 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 @@ -18,7 +18,6 @@ */ package org.jclouds.aws.ec2.compute; -import java.util.Map; import java.util.Set; import javax.inject.Inject; @@ -35,6 +34,7 @@ import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl; import com.google.common.base.Supplier; +import com.google.common.cache.Cache; /** * @@ -46,7 +46,7 @@ public class AWSEC2TemplateBuilderImpl extends EC2TemplateBuilderImpl { protected AWSEC2TemplateBuilderImpl(@Memoized Supplier> locations, @Memoized Supplier> images, @Memoized Supplier> sizes, Supplier defaultLocation, @Named("DEFAULT") Provider optionsProvider, - @Named("DEFAULT") Provider defaultTemplateProvider, Map imageMap) { + @Named("DEFAULT") Provider defaultTemplateProvider, Supplier> imageMap) { super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider, imageMap); } 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 22d40cdc4f..8da3c976e0 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 @@ -21,8 +21,6 @@ package org.jclouds.aws.ec2.compute.config; import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL; import static org.jclouds.compute.domain.OsFamily.AMZN_LINUX; -import java.util.Map; - import javax.inject.Named; import javax.inject.Singleton; @@ -56,6 +54,7 @@ import org.jclouds.ec2.compute.suppliers.EC2HardwareSupplier; import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier; import com.google.common.base.Supplier; +import com.google.common.cache.Cache; import com.google.inject.Injector; import com.google.inject.Provides; @@ -89,12 +88,12 @@ public class AWSEC2ComputeServiceContextModule extends BaseComputeServiceContext @Provides @Singleton - protected Supplier> provideRegionAndNameToImageSupplierCache( + protected Supplier> provideRegionAndNameToImageSupplierCache( @Named(PROPERTY_SESSION_INTERVAL) long seconds, final AWSRegionAndNameToImageSupplier supplier) { - return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>( - authException, seconds, new Supplier>() { + return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>( + authException, seconds, new Supplier>() { @Override - public Map get() { + public Cache get() { return supplier.get(); } }); diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java index 2beedbc7d8..1eff004f23 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java @@ -18,9 +18,6 @@ */ package org.jclouds.aws.ec2.compute.config; -import static com.google.common.collect.Maps.newLinkedHashMap; - -import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -47,7 +44,6 @@ import org.jclouds.compute.options.TemplateOptions; import org.jclouds.domain.Credentials; import org.jclouds.ec2.compute.config.EC2ComputeServiceDependenciesModule; import org.jclouds.ec2.compute.domain.RegionAndName; -import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; import org.jclouds.ec2.compute.functions.CreateSecurityGroupIfNeeded; import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair; import org.jclouds.ec2.compute.functions.CredentialsForInstance; @@ -62,6 +58,9 @@ import org.jclouds.rest.internal.RestContextImpl; import com.google.common.base.Function; import com.google.common.base.Predicate; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; import com.google.common.collect.Sets; import com.google.inject.Provides; import com.google.inject.Scopes; @@ -80,15 +79,15 @@ public class AWSEC2ComputeServiceDependenciesModule extends EC2ComputeServiceDep bind(ComputeService.class).to(AWSEC2ComputeService.class); bind(new TypeLiteral>() { }).to(RunningInstanceToNodeMetadata.class); - bind(new TypeLiteral>() { + bind(new TypeLiteral>() { }).to(CredentialsForInstance.class); - bind(new TypeLiteral>() { + bind(new TypeLiteral>() { }).to(CreateSecurityGroupIfNeeded.class); - bind(new TypeLiteral>() { + bind(new TypeLiteral>() { }).to(CreateUniqueKeyPair.class); bind(new TypeLiteral>() { }).to(ImportOrReturnExistingKeypair.class); - bind(new TypeLiteral>() { + bind(new TypeLiteral>() { }).to(RegionAndIdToImage.class); bind(new TypeLiteral() { }).to(new TypeLiteral>() { @@ -116,10 +115,8 @@ public class AWSEC2ComputeServiceDependenciesModule extends EC2ComputeServiceDep @Provides @Singleton @Named("PLACEMENT") - protected Map placementGroupMap(CreateSecurityGroupIfNeeded in) { - // doesn't seem to clear when someone issues remove(key) - // return new MapMaker().makeComputingMap(in); - return newLinkedHashMap(); + protected Cache placementGroupMap(CreateSecurityGroupIfNeeded in) { + return CacheBuilder.newBuilder().build(in); } @Provides diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadata.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadata.java index 8f67c03864..06b2802834 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadata.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadata.java @@ -38,6 +38,7 @@ import org.jclouds.ec2.domain.InstanceState; import org.jclouds.ec2.domain.RunningInstance; import com.google.common.base.Supplier; +import com.google.common.cache.Cache; /** * @author Adrian Cole @@ -47,9 +48,9 @@ public class AWSRunningInstanceToNodeMetadata extends RunningInstanceToNodeMetad @Inject protected AWSRunningInstanceToNodeMetadata(Map instanceToNodeState, - Map credentialStore, Map instanceToImage, + Map credentialStore, Supplier> imageMap, @Memoized Supplier> locations, @Memoized Supplier> hardware) { - super(instanceToNodeState, credentialStore, instanceToImage, locations, hardware); + super(instanceToNodeState, credentialStore, imageMap, locations, hardware); } @Override diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2CreateNodesInGroupThenAddToSet.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2CreateNodesInGroupThenAddToSet.java index c6806880fb..485e7f580c 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2CreateNodesInGroupThenAddToSet.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2CreateNodesInGroupThenAddToSet.java @@ -49,6 +49,7 @@ import org.jclouds.logging.Logger; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; +import com.google.common.cache.Cache; import com.google.common.collect.Iterables; /** @@ -73,7 +74,7 @@ public class AWSEC2CreateNodesInGroupThenAddToSet extends EC2CreateNodesInGroupT CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, AWSEC2InstancePresent instancePresent, Function runningInstanceToNodeMetadata, - Function instanceToCredentials, Map credentialStore, + Cache instanceToCredentials, Map credentialStore, ComputeUtils utils, SpotInstanceRequestToAWSRunningInstance spotConverter) { super(client, templateBuilderProvider, createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, instancePresent, runningInstanceToNodeMetadata, instanceToCredentials, credentialStore, utils); diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.java index 5ebba9a5d2..5f981fa14a 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions.java @@ -37,7 +37,6 @@ import org.jclouds.compute.domain.Template; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.ec2.compute.domain.RegionAndName; -import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; import org.jclouds.ec2.compute.options.EC2TemplateOptions; import org.jclouds.ec2.compute.strategy.CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions; import org.jclouds.ec2.domain.KeyPair; @@ -47,6 +46,7 @@ import org.jclouds.logging.Logger; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Predicate; +import com.google.common.cache.Cache; /** * @@ -59,21 +59,21 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; @VisibleForTesting - final Map placementGroupMap; + final Cache placementGroupMap; @VisibleForTesting final CreatePlacementGroupIfNeeded createPlacementGroupIfNeeded; @VisibleForTesting final Function importExistingKeyPair; @Inject - public CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions( - Map credentialsMap, @Named("SECURITY") Map securityGroupMap, - @Named("PLACEMENT") Map placementGroupMap, - Function createUniqueKeyPair, - Function createSecurityGroupIfNeeded, - Provider optionsProvider, CreatePlacementGroupIfNeeded createPlacementGroupIfNeeded, + public CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions(Map knownKeys, + Cache credentialsMap, + @Named("SECURITY") Cache securityGroupMap, + Provider optionsProvider, + @Named("PLACEMENT") Cache placementGroupMap, + CreatePlacementGroupIfNeeded createPlacementGroupIfNeeded, Function importExistingKeyPair) { - super(credentialsMap, securityGroupMap, createUniqueKeyPair, createSecurityGroupIfNeeded, optionsProvider); + super(knownKeys, credentialsMap, securityGroupMap, optionsProvider); this.placementGroupMap = placementGroupMap; this.createPlacementGroupIfNeeded = createPlacementGroupIfNeeded; this.importExistingKeyPair = importExistingKeyPair; @@ -110,9 +110,8 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions // http://docs.amazonwebservices.com/AWSEC2/latest/UserGuide/index.html?using_cluster_computing.html placementGroupName = String.format("jclouds#%s#%s", group, region); RegionAndName regionAndName = new RegionAndName(region, placementGroupName); - if (!placementGroupMap.containsKey(regionAndName)) { - placementGroupMap.put(regionAndName, createPlacementGroupIfNeeded.apply(regionAndName)); - } + // make this entry as needed + placementGroupMap.getUnchecked(regionAndName); } return placementGroupName; } @@ -120,7 +119,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions @Override protected String createOrImportKeyPair(String region, String group, TemplateOptions options) { RegionAndName key = new RegionAndName(region, "jclouds#" + group); - KeyPair pair = credentialsMap.get(key); + KeyPair pair = knownKeys.get(key); if (pair != null) return pair.getKeyName(); if (and(hasPublicKeyMaterial, or(doesntNeedSshAfterImportingPublicKey, hasLoginCredential)).apply(options)) { @@ -128,12 +127,12 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions options.dontAuthorizePublicKey(); if (hasLoginCredential.apply(options)) pair = pair.toBuilder().keyMaterial(options.getOverridingCredentials().credential).build(); - credentialsMap.put(key, pair); + knownKeys.put(key, pair); } else { if (hasPublicKeyMaterial.apply(options)) { logger.warn("to avoid creating temporary keys in aws-ec2, use templateOption overrideLoginCredentialWith(id_rsa)"); } - return createUniqueKeyPairAndPutIntoMap(region, group); + return super.createOrImportKeyPair(region, group, options); } return pair.getKeyName(); } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/suppliers/AWSRegionAndNameToImageSupplier.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/suppliers/AWSRegionAndNameToImageSupplier.java index bb91fab4dd..7dbe3beff4 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/suppliers/AWSRegionAndNameToImageSupplier.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/suppliers/AWSRegionAndNameToImageSupplier.java @@ -47,6 +47,9 @@ import com.google.common.base.Function; import com.google.common.base.Splitter; import com.google.common.base.Supplier; import com.google.common.base.Throwables; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; @@ -58,12 +61,13 @@ import com.google.common.util.concurrent.Futures; * @author Adrian Cole */ @Singleton -public class AWSRegionAndNameToImageSupplier implements Supplier> { +public class AWSRegionAndNameToImageSupplier implements Supplier> { @Resource @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; - private final Map images; + private final Map knownImages; + private final CacheLoader regionAndIdToImage; private final Set clusterComputeIds; private final CallForImages.Factory factory; private final ExecutorService executor; @@ -73,10 +77,11 @@ public class AWSRegionAndNameToImageSupplier implements Supplier clusterRegions; private final String ccAmiQuery; + @Inject protected AWSRegionAndNameToImageSupplier(@Region Set regions, @Named(PROPERTY_EC2_AMI_QUERY) String amiQuery, @Named(PROPERTY_EC2_CC_REGIONS) String clusterRegions, - @Named(PROPERTY_EC2_CC_AMI_QUERY) String ccAmiQuery, Map images, + @Named(PROPERTY_EC2_CC_AMI_QUERY) String ccAmiQuery, Map knownImages, CacheLoader regionAndIdToImage, CallForImages.Factory factory, @ClusterCompute Set clusterComputeIds, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) { this.factory = factory; @@ -84,13 +89,14 @@ public class AWSRegionAndNameToImageSupplier implements Supplier get() { + public Cache get() { Future> normalImages = images(regions, amiQuery, PROPERTY_EC2_AMI_QUERY); ImmutableSet clusterImages; try { @@ -115,8 +121,8 @@ public class AWSRegionAndNameToImageSupplier implements Supplier() { + knownImages.clear(); + knownImages.putAll(uniqueIndex(parsedImages, new Function() { @Override public RegionAndName apply(Image from) { @@ -124,7 +130,13 @@ public class AWSRegionAndNameToImageSupplier implements Supplier cache = CacheBuilder.newBuilder().build(regionAndIdToImage); + // seed the cache + for (RegionAndName image : knownImages.keySet()){ + cache.getUnchecked(image); + } + return cache; } private Future> images(Iterable regions, String query, String tag) { diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java index 85675ca761..95085efd5a 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java @@ -88,7 +88,7 @@ public class AWSEC2TemplateBuilderLiveTest extends BaseTemplateBuilderLiveTest { .build(); assert (template.getImage().getProviderId().startsWith("ami-")) : template; - assertEquals(template.getImage().getOperatingSystem().getVersion(), "11.10"); + assertEquals(template.getImage().getOperatingSystem().getVersion(), "11.04"); assertEquals(template.getImage().getOperatingSystem().is64Bit(), false); assertEquals(template.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU); assertEquals(template.getImage().getUserMetadata().get("rootDeviceType"), "instance-store"); @@ -107,7 +107,7 @@ public class AWSEC2TemplateBuilderLiveTest extends BaseTemplateBuilderLiveTest { .build(); assert (template.getImage().getProviderId().startsWith("ami-")) : template; - assertEquals(template.getImage().getOperatingSystem().getVersion(), "11.10"); + assertEquals(template.getImage().getOperatingSystem().getVersion(), "11.04"); assertEquals(template.getImage().getOperatingSystem().is64Bit(), false); assertEquals(template.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU); assertEquals(template.getImage().getUserMetadata().get("rootDeviceType"), "instance-store"); @@ -183,7 +183,7 @@ public class AWSEC2TemplateBuilderLiveTest extends BaseTemplateBuilderLiveTest { fastestTemplate = context.getComputeService().templateBuilder().fastest().build(); assert (fastestTemplate.getImage().getProviderId().startsWith("ami-")) : fastestTemplate; assertEquals(fastestTemplate.getHardware().getProviderId(), InstanceType.CC1_4XLARGE); - assertEquals(fastestTemplate.getImage().getOperatingSystem().getVersion(), "11.10"); + assertEquals(fastestTemplate.getImage().getOperatingSystem().getVersion(), "11.04"); assertEquals(fastestTemplate.getImage().getOperatingSystem().is64Bit(), true); assertEquals(fastestTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU); assertEquals(fastestTemplate.getImage().getUserMetadata().get("rootDeviceType"), "ebs"); diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadataTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadataTest.java index 6f1205e7bf..4533d7fbb5 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadataTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/functions/AWSRunningInstanceToNodeMetadataTest.java @@ -45,6 +45,10 @@ import org.testng.annotations.Test; import com.google.common.base.Function; import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; @@ -118,7 +122,7 @@ public class AWSRunningInstanceToNodeMetadataTest { Map credentialStore) { Map instanceToNodeState = EC2ComputeServiceDependenciesModule.instanceToNodeState; - Map instanceToImage = Maps.uniqueIndex(images, new Function() { + final ImmutableMap backing = Maps.uniqueIndex(images, new Function() { @Override public RegionAndName apply(Image from) { @@ -126,13 +130,23 @@ public class AWSRunningInstanceToNodeMetadataTest { } }); - + + Cache instanceToImage = CacheBuilder.newBuilder().build(new CacheLoader (){ + + @Override + public Image load(RegionAndName key) throws Exception { + return backing.get(key); + } + + }); + + return createNodeParser(hardware, locations, credentialStore, instanceToNodeState, instanceToImage); } private AWSRunningInstanceToNodeMetadata createNodeParser(final ImmutableSet hardware, final ImmutableSet locations, Map credentialStore, - Map instanceToNodeState, Map instanceToImage) { + Map instanceToNodeState, Cache instanceToImage) { Supplier> locationSupplier = new Supplier>() { @Override @@ -150,7 +164,8 @@ public class AWSRunningInstanceToNodeMetadataTest { }; AWSRunningInstanceToNodeMetadata parser = new AWSRunningInstanceToNodeMetadata(instanceToNodeState, - credentialStore, instanceToImage, locationSupplier, hardwareSupplier); + credentialStore, Suppliers.> ofInstance(instanceToImage), + locationSupplier, hardwareSupplier); return parser; } diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java index 20a1a17e5d..dd5a071cf4 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java @@ -54,13 +54,14 @@ import org.jclouds.scriptbuilder.domain.Statements; import org.testng.annotations.Test; import com.google.common.base.Function; +import com.google.common.cache.Cache; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; /** * @author Adrian Cole */ -@Test(groups = "unit") +@Test(groups = "unit", singleThreaded = true, testName = "CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest") public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest { private static final Provider OPTIONS_PROVIDER = new javax.inject.Provider() { @@ -385,7 +386,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT } - @Test(expectedExceptions = IllegalStateException.class) + @Test(expectedExceptions = IllegalArgumentException.class) public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_reusesKeyWhenToldToWithRunScriptButNoCredentials() { // setup constants String region = Region.AP_SOUTHEAST_1; @@ -401,7 +402,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.getOverridingCredentials()).andReturn(null); expect(options.getRunScript()).andReturn(Statements.exec("echo foo")); - expect(strategy.credentialsMap.containsKey(new RegionAndName(region, userSuppliedKeyPair))).andReturn(false); + expect(strategy.credentialsMap.getUnchecked(new RegionAndName(region, userSuppliedKeyPair))).andThrow(new NullPointerException()); // replay mocks replay(options); @@ -432,7 +433,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.getOverridingCredentials()).andReturn(null); expect(options.getRunScript()).andReturn(Statements.exec("echo foo")); - expect(strategy.credentialsMap.containsKey(new RegionAndName(region, userSuppliedKeyPair))).andReturn(true); + expect(strategy.credentialsMap.getUnchecked(new RegionAndName(region, userSuppliedKeyPair))).andReturn(keyPair); // replay mocks replay(options); @@ -463,11 +464,13 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.getOverridingCredentials()).andReturn(new Credentials(null, "MyRsa")).atLeastOnce(); expect( - strategy.credentialsMap.put(new RegionAndName(region, userSuppliedKeyPair), KeyPair.builder() - .region(region).keyName(userSuppliedKeyPair).keyFingerprint("//TODO").keyMaterial("MyRsa").build())) - .andReturn(null); + strategy.knownKeys.put( + new RegionAndName(region, tag), + KeyPair.builder().region(region).keyName(userSuppliedKeyPair).keyFingerprint("//TODO") + .keyMaterial("MyRsa").build())).andReturn(null); + strategy.credentialsMap.invalidate(new RegionAndName(region, tag)); expect(options.getRunScript()).andReturn(Statements.exec("echo foo")); - expect(strategy.credentialsMap.containsKey(new RegionAndName(region, userSuppliedKeyPair))).andReturn(true); + expect(strategy.credentialsMap.getUnchecked(new RegionAndName(region, userSuppliedKeyPair))).andReturn(keyPair); // replay mocks replay(options); @@ -483,6 +486,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT verifyStrategy(strategy); } + @Test(enabled = false) public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_importsKeyPairAndUnsetsTemplateInstructionWhenPublicKeySuppliedAndAddsCredentialToMapWhenOverridingCredsAreSet() { // setup constants String region = Region.AP_SOUTHEAST_1; @@ -496,18 +500,19 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT KeyPair keyPair = new KeyPair(region, "jclouds#" + group, "fingerprint", null); // setup expectations + expect(strategy.knownKeys.get(new RegionAndName(region, "jclouds#" + group))).andReturn(null); expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.shouldAutomaticallyCreateKeyPair()).andReturn(shouldAutomaticallyCreateKeyPair); - expect(strategy.credentialsMap.get(new RegionAndName(region, "jclouds#" + group))).andReturn(null); + expect(strategy.credentialsMap.getUnchecked(new RegionAndName(region, "jclouds#" + group))).andThrow(new NullPointerException()); expect(options.getPublicKey()).andReturn("ssh-rsa").times(2); expect(strategy.importExistingKeyPair.apply(new RegionNameAndPublicKeyMaterial(region, group, "ssh-rsa"))) .andReturn(keyPair); - expect(options.dontAuthorizePublicKey()).andReturn(options); expect(options.getOverridingCredentials()).andReturn(new Credentials("foo", "bar")).times(3); expect(options.getRunScript()).andReturn(null).times(2); expect(options.getPrivateKey()).andReturn(null); + expect(options.dontAuthorizePublicKey()).andReturn(options); expect( - strategy.credentialsMap.put(new RegionAndName(region, "jclouds#" + group), + strategy.knownKeys.put(new RegionAndName(region, "jclouds#" + group), keyPair.toBuilder().keyMaterial("bar").build())).andReturn(null); // replay mocks @@ -521,7 +526,8 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT verify(options); verifyStrategy(strategy); } - + + @Test(enabled = false) public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_importsKeyPairAndUnsetsTemplateInstructionWhenPublicKeySupplied() { // setup constants String region = Region.AP_SOUTHEAST_1; @@ -535,9 +541,10 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT KeyPair keyPair = new KeyPair(region, "jclouds#" + group, "fingerprint", null); // setup expectations + expect(strategy.knownKeys.get(new RegionAndName(region, "jclouds#" + group))).andReturn(null); expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.shouldAutomaticallyCreateKeyPair()).andReturn(shouldAutomaticallyCreateKeyPair); - expect(strategy.credentialsMap.get(new RegionAndName(region, "jclouds#" + group))).andReturn(null); + expect(strategy.credentialsMap.getUnchecked(new RegionAndName(region, "jclouds#" + group))).andThrow(new NullPointerException()); expect(options.getPublicKey()).andReturn("ssh-rsa").times(2); expect(strategy.importExistingKeyPair.apply(new RegionNameAndPublicKeyMaterial(region, group, "ssh-rsa"))) .andReturn(keyPair); @@ -545,7 +552,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT expect(options.getOverridingCredentials()).andReturn(null); expect(options.getRunScript()).andReturn(null).times(2); expect(options.getPrivateKey()).andReturn(null); - expect(strategy.credentialsMap.put(new RegionAndName(region, "jclouds#" + group), keyPair)).andReturn(null); + expect(strategy.knownKeys.put(new RegionAndName(region, "jclouds#" + group), keyPair)).andReturn(null); // replay mocks replay(options); @@ -571,18 +578,15 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy = setupStrategy(); AWSEC2TemplateOptions options = createMock(AWSEC2TemplateOptions.class); KeyPair keyPair = createMock(KeyPair.class); - + // setup expectations + expect(strategy.knownKeys.get(new RegionAndName(region, "jclouds#" + group))).andReturn(null); expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.shouldAutomaticallyCreateKeyPair()).andReturn(shouldAutomaticallyCreateKeyPair); - expect(strategy.credentialsMap.get(new RegionAndName(region, "jclouds#" + group))).andReturn(null); + expect(strategy.credentialsMap.getUnchecked(new RegionAndName(region, group))).andReturn(keyPair); expect(options.getPublicKey()).andReturn(null).times(2); - expect(strategy.createUniqueKeyPair.apply(new RegionAndName(region, group))).andReturn(keyPair); - expect(keyPair.getRegion()).andReturn(region).atLeastOnce(); expect(keyPair.getKeyName()).andReturn(systemGeneratedKeyPairName).atLeastOnce(); expect(options.getRunScript()).andReturn(null); - expect(strategy.credentialsMap.put(new RegionAndName(region, systemGeneratedKeyPairName), keyPair)).andReturn( - null); // replay mocks replay(options); @@ -613,10 +617,10 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT KeyPair keyPair = createMock(KeyPair.class); // setup expectations + expect(strategy.knownKeys.get(new RegionAndName(region, "jclouds#" + group))).andReturn(keyPair); expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.getRunScript()).andReturn(null); expect(options.shouldAutomaticallyCreateKeyPair()).andReturn(shouldAutomaticallyCreateKeyPair); - expect(strategy.credentialsMap.get(new RegionAndName(region, "jclouds#" + group))).andReturn(keyPair); expect(keyPair.getKeyName()).andReturn(systemGeneratedKeyPairName).atLeastOnce(); // replay mocks replay(options); @@ -673,7 +677,6 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT Set groupNames = ImmutableSet. of(); int[] ports = new int[] {}; boolean shouldAuthorizeSelf = true; - boolean groupExisted = false; Set returnVal = ImmutableSet. of(generatedMarkerGroup); // create mocks @@ -686,9 +689,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT expect(options.getInboundPorts()).andReturn(ports).atLeastOnce(); RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, ports, shouldAuthorizeSelf); - expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(groupExisted); - expect(strategy.createSecurityGroupIfNeeded.apply(regionNameAndIngressRules)).andReturn(generatedMarkerGroup); - expect(strategy.securityGroupMap.put(regionNameAndIngressRules, generatedMarkerGroup)).andReturn(null); + expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules)).andReturn(group); // replay mocks replay(options); @@ -710,7 +711,6 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT Set groupNames = ImmutableSet. of(); int[] ports = new int[] { 22, 80 }; boolean shouldAuthorizeSelf = true; - boolean groupExisted = false; Set returnVal = ImmutableSet. of(generatedMarkerGroup); // create mocks @@ -723,9 +723,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT expect(options.getInboundPorts()).andReturn(ports).atLeastOnce(); RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, ports, shouldAuthorizeSelf); - expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(groupExisted); - expect(strategy.createSecurityGroupIfNeeded.apply(regionNameAndIngressRules)).andReturn(generatedMarkerGroup); - expect(strategy.securityGroupMap.put(regionNameAndIngressRules, generatedMarkerGroup)).andReturn(null); + expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules)).andReturn(generatedMarkerGroup); // replay mocks replay(options); @@ -747,7 +745,6 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT Set groupNames = ImmutableSet. of(); int[] ports = new int[] {}; boolean shouldAuthorizeSelf = true; - boolean groupExisted = true; Set returnVal = ImmutableSet. of(generatedMarkerGroup); // create mocks @@ -760,7 +757,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT expect(options.getInboundPorts()).andReturn(ports).atLeastOnce(); RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, ports, shouldAuthorizeSelf); - expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(groupExisted); + expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules)).andReturn(generatedMarkerGroup); // replay mocks replay(options); @@ -793,13 +790,9 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT expect(options.getGroupIds()).andReturn(ImmutableSet.of()); expect(options.getGroups()).andReturn(groupNames).atLeastOnce(); RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, - ports, shouldAuthorizeSelf); // note - // this - // works - // since - // there's - // no equals on portsq - expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(groupExisted); + ports, shouldAuthorizeSelf); + + expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules)).andReturn(groupExisted ? "tag" : null); // replay mocks replay(options); @@ -832,13 +825,9 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT expect(options.getGroupIds()).andReturn(ImmutableSet.of("group1", "group2")); expect(options.getGroups()).andReturn(groupNames).atLeastOnce(); RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, - ports, shouldAuthorizeSelf); // note - // this - // works - // since - // there's - // no equals on portsq - expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(groupExisted); + ports, shouldAuthorizeSelf); + + expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules)).andReturn(groupExisted ? "tag" : null); // replay mocks replay(options); @@ -895,11 +884,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT // setup expectations expect(options.getPlacementGroup()).andReturn(userSuppliedPlacementGroup); expect(options.shouldAutomaticallyCreatePlacementGroup()).andReturn(shouldAutomaticallyCreatePlacementGroup); - expect(strategy.placementGroupMap.containsKey(new RegionAndName(region, generatedMarkerGroup))).andReturn(false); - expect(strategy.createPlacementGroupIfNeeded.apply(new RegionAndName(region, generatedMarkerGroup))).andReturn( - generatedMarkerGroup); - expect(strategy.placementGroupMap.put(new RegionAndName(region, generatedMarkerGroup), generatedMarkerGroup)) - .andReturn(null); + expect(strategy.placementGroupMap.getUnchecked(new RegionAndName(region, generatedMarkerGroup))).andReturn(generatedMarkerGroup); // replay mocks replay(options); @@ -946,37 +931,33 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT } private void verifyStrategy(CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy) { + verify(strategy.knownKeys); verify(strategy.credentialsMap); verify(strategy.securityGroupMap); verify(strategy.placementGroupMap); - verify(strategy.createUniqueKeyPair); verify(strategy.importExistingKeyPair); - verify(strategy.createSecurityGroupIfNeeded); verify(strategy.createPlacementGroupIfNeeded); } @SuppressWarnings("unchecked") private CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions setupStrategy() { - Map credentialsMap = createMock(Map.class); - Map securityGroupMap = createMock(Map.class); - Map placementGroupMap = createMock(Map.class); - Function createOrGetKeyPair = createMock(Function.class); - Function createSecurityGroupIfNeeded = createMock(Function.class); + Map knownKeys = createMock(Map.class); + Cache credentialsMap = createMock(Cache.class); + Cache securityGroupMap = createMock(Cache.class); + Cache placementGroupMap = createMock(Cache.class); Function importExistingKeyPair = createMock(Function.class); CreatePlacementGroupIfNeeded createPlacementGroupIfNeeded = createMock(CreatePlacementGroupIfNeeded.class); - return new CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions(credentialsMap, securityGroupMap, - placementGroupMap, createOrGetKeyPair, createSecurityGroupIfNeeded, OPTIONS_PROVIDER, - createPlacementGroupIfNeeded, importExistingKeyPair); + return new CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions(knownKeys, credentialsMap, securityGroupMap, + OPTIONS_PROVIDER, placementGroupMap, createPlacementGroupIfNeeded, importExistingKeyPair); } private void replayStrategy(CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy) { + replay(strategy.knownKeys); replay(strategy.credentialsMap); replay(strategy.securityGroupMap); replay(strategy.placementGroupMap); - replay(strategy.createUniqueKeyPair); replay(strategy.importExistingKeyPair); - replay(strategy.createSecurityGroupIfNeeded); replay(strategy.createPlacementGroupIfNeeded); } diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/BaseAWSEC2AsyncClientTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/BaseAWSEC2AsyncClientTest.java index c8b7783ae2..a08888ab7b 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/BaseAWSEC2AsyncClientTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/BaseAWSEC2AsyncClientTest.java @@ -25,10 +25,14 @@ import java.net.URI; import java.util.Map; import java.util.Properties; +import javax.inject.Singleton; + import org.jclouds.aws.domain.Region; import org.jclouds.aws.ec2.config.AWSEC2RestClientModule; import org.jclouds.aws.filters.FormSigner; +import org.jclouds.compute.domain.Image; import org.jclouds.date.DateService; +import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.http.HttpRequest; import org.jclouds.http.RequiresHttp; import org.jclouds.rest.ConfiguresRestClient; @@ -38,8 +42,12 @@ import org.jclouds.rest.RestContextSpec; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; import com.google.common.collect.ImmutableMap; import com.google.inject.Module; +import com.google.inject.Provides; /** * @author Adrian Cole @@ -60,6 +68,18 @@ public abstract class BaseAWSEC2AsyncClientTest extends RestClientTest { bindRegionsToProvider(Regions.class); } + @Provides + @Singleton + Cache provide(){ + return CacheBuilder.newBuilder().build(new CacheLoader() { + + @Override + public Image load(RegionAndName key) throws Exception { + return null; + } + + }); + } static class Regions implements javax.inject.Provider> { @Override public Map get() { diff --git a/providers/cloudsigma-zrh/src/main/java/org/jclouds/cloudsigma/compute/CloudSigmaComputeServiceAdapter.java b/providers/cloudsigma-zrh/src/main/java/org/jclouds/cloudsigma/compute/CloudSigmaComputeServiceAdapter.java index 805d8ed14e..05c2570160 100644 --- a/providers/cloudsigma-zrh/src/main/java/org/jclouds/cloudsigma/compute/CloudSigmaComputeServiceAdapter.java +++ b/providers/cloudsigma-zrh/src/main/java/org/jclouds/cloudsigma/compute/CloudSigmaComputeServiceAdapter.java @@ -32,7 +32,6 @@ import javax.inject.Named; import javax.inject.Singleton; import org.jclouds.Constants; -import org.jclouds.cloudsigma.CloudSigmaAsyncClient; import org.jclouds.cloudsigma.CloudSigmaClient; import org.jclouds.cloudsigma.domain.Device; import org.jclouds.cloudsigma.domain.DriveInfo; @@ -60,33 +59,35 @@ import org.jclouds.logging.Logger; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.base.Predicates; +import com.google.common.cache.Cache; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet.Builder; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.UncheckedExecutionException; /** - * defines the connection between the {@link CloudSigmaClient} implementation and the jclouds - * {@link ComputeService} + * defines the connection between the {@link CloudSigmaClient} implementation + * and the jclouds {@link ComputeService} * */ @Singleton public class CloudSigmaComputeServiceAdapter implements - ComputeServiceAdapter { + ComputeServiceAdapter { private static final Predicate PREINSTALLED_DISK = Predicates.and(Predicates.notNull(), - new Predicate() { + new Predicate() { - @Override - public boolean apply(DriveInfo drive) { - return drive.getType().equals(DriveType.DISK) && drive.getDriveType().contains("preinstalled"); - } + @Override + public boolean apply(DriveInfo drive) { + return drive.getType().equals(DriveType.DISK) && drive.getDriveType().contains("preinstalled"); + } - }); + }); private final CloudSigmaClient client; - private final CloudSigmaAsyncClient aclient; private final Predicate driveNotClaimed; private final JustProvider locationSupplier; private final String defaultVncPassword; - private final Map cache; + private final Cache cache; private final ExecutorService executor; @Resource @@ -94,12 +95,10 @@ public class CloudSigmaComputeServiceAdapter implements protected Logger logger = Logger.NULL; @Inject - public CloudSigmaComputeServiceAdapter(CloudSigmaClient client, CloudSigmaAsyncClient aclient, - Predicate driveNotClaimed, JustProvider locationSupplier, - @Named(CloudSigmaConstants.PROPERTY_VNC_PASSWORD) String defaultVncPassword, Map cache, - @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) { + public CloudSigmaComputeServiceAdapter(CloudSigmaClient client, Predicate driveNotClaimed, + JustProvider locationSupplier, @Named(CloudSigmaConstants.PROPERTY_VNC_PASSWORD) String defaultVncPassword, + Cache cache, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) { this.client = checkNotNull(client, "client"); - this.aclient = checkNotNull(aclient, "aclient"); this.driveNotClaimed = checkNotNull(driveNotClaimed, "driveNotClaimed"); this.locationSupplier = checkNotNull(locationSupplier, "locationSupplier"); this.defaultVncPassword = checkNotNull(defaultVncPassword, "defaultVncPassword"); @@ -109,21 +108,20 @@ public class CloudSigmaComputeServiceAdapter implements @Override public ServerInfo createNodeWithGroupEncodedIntoNameThenStoreCredentials(String tag, String name, Template template, - Map credentialStore) { + Map credentialStore) { long bootSize = (long) (template.getHardware().getVolumes().get(0).getSize() * 1024 * 1024 * 1024l); logger.debug(">> imaging boot drive source(%s) bytes(%d)", template.getImage().getId(), bootSize); DriveInfo drive = client.cloneDrive(template.getImage().getId(), template.getImage().getId(), - new CloneDriveOptions().size(bootSize)); + new CloneDriveOptions().size(bootSize)); boolean success = driveNotClaimed.apply(drive); logger.debug("<< image(%s) complete(%s)", drive.getUuid(), success); if (!success) { client.destroyDrive(drive.getUuid()); throw new IllegalStateException("could not image drive in time!"); } - cache.put(drive.getUuid(), drive); Server toCreate = Servers.small(name, drive.getUuid(), defaultVncPassword).mem(template.getHardware().getRam()) - .cpu((int) (template.getHardware().getProcessors().get(0).getSpeed())).build(); + .cpu((int) (template.getHardware().getProcessors().get(0).getSpeed())).build(); logger.debug(">> creating server"); ServerInfo from = client.createServer(toCreate); @@ -131,7 +129,7 @@ public class CloudSigmaComputeServiceAdapter implements logger.debug(">> starting server(%s)", from.getUuid()); client.startServer(from.getUuid()); // store the credentials so that later functions can use them - credentialStore.put("node#"+ from.getUuid(), new Credentials("cloudsigma", "cloudsigma")); + credentialStore.put("node#" + from.getUuid(), new Credentials("root", defaultVncPassword)); return from; } @@ -155,30 +153,39 @@ public class CloudSigmaComputeServiceAdapter implements return "sizeLessThanOrEqual(" + size + ")"; } - }).ids(id).ram(ram).processors(ImmutableList.of(new Processor(1, cpu))).volumes( - ImmutableList. of(new VolumeImpl(size, true, true))).build()); + }).ids(id).ram(ram).processors(ImmutableList.of(new Processor(1, cpu))) + .volumes(ImmutableList. of(new VolumeImpl(size, true, true))).build()); } return hardware.build(); } /** - * look up the current standard images and do not error out, if they are not found. + * look up the current standard images and do not error out, if they are not + * found. */ @Override public Iterable listImages() { Iterable drives = transformParallel(client.listStandardDrives(), - new Function>() { + new Function>() { - @Override - public Future apply(String input) { - return aclient.getDriveInfo(input); + @Override + public Future apply(String input) { + try { + return Futures.immediateFuture(cache.getUnchecked(input)); + } catch (NullPointerException e) { + logger.debug("drive %s not found", input); + } catch (UncheckedExecutionException e) { + logger.warn(e, "error finding drive %s: %s", input, e.getMessage()); } + return Futures.immediateFuture(null); + } - }, executor, null, logger, "drives"); - Iterable returnVal = filter(drives, PREINSTALLED_DISK); - for (DriveInfo drive : returnVal) - cache.put(drive.getUuid(), drive); - return returnVal; + @Override + public String toString() { + return "seedDriveCache()"; + } + }, executor, null, logger, "drives"); + return filter(drives, PREINSTALLED_DISK); } @SuppressWarnings("unchecked") diff --git a/providers/cloudsigma-zrh/src/main/java/org/jclouds/cloudsigma/compute/functions/ServerInfoToNodeMetadata.java b/providers/cloudsigma-zrh/src/main/java/org/jclouds/cloudsigma/compute/functions/ServerInfoToNodeMetadata.java index ee9d5de097..3b50862a0e 100644 --- a/providers/cloudsigma-zrh/src/main/java/org/jclouds/cloudsigma/compute/functions/ServerInfoToNodeMetadata.java +++ b/providers/cloudsigma-zrh/src/main/java/org/jclouds/cloudsigma/compute/functions/ServerInfoToNodeMetadata.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import javax.annotation.Resource; import javax.inject.Inject; import javax.inject.Singleton; @@ -45,13 +46,16 @@ import org.jclouds.compute.domain.Volume; import org.jclouds.compute.domain.VolumeBuilder; import org.jclouds.domain.Credentials; import org.jclouds.domain.Location; +import org.jclouds.logging.Logger; import com.google.common.base.Function; import com.google.common.base.Supplier; +import com.google.common.cache.Cache; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import com.google.common.util.concurrent.UncheckedExecutionException; /** * @author Adrian Cole @@ -113,10 +117,13 @@ public class ServerInfoToNodeMetadata implements Function { - private final Map cache; + @Resource + protected Logger logger = Logger.NULL; + + private final Cache cache; @Inject - public DeviceToVolume(Map cache) { + public DeviceToVolume(Cache cache) { this.cache = checkNotNull(cache, "cache"); } @@ -124,9 +131,13 @@ public class ServerInfoToNodeMetadata implements Function { - private final Map cache; + @Resource + protected Logger logger = Logger.NULL; + + private final Cache cache; @Inject - public GetImageIdFromServer(Map cache) { + public GetImageIdFromServer(Cache cache) { this.cache = cache; } @@ -155,9 +169,12 @@ public class ServerInfoToNodeMetadata implements Function