Issue 690:Update to guava 10.0rc2 and change from MapMaker to CacheBuilder

This commit is contained in:
Adrian Cole 2011-09-22 00:23:29 -07:00
parent d039fe7d96
commit 4cd94489c9
61 changed files with 1236 additions and 864 deletions

View File

@ -25,9 +25,6 @@ import static org.jclouds.tools.ant.taskdefs.compute.ComputeTaskUtils.ipOrEmptyS
import java.io.IOException; import java.io.IOException;
import java.net.URI; 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.BuildException;
import org.apache.tools.ant.Project; 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.compute.predicates.NodePredicates;
import org.jclouds.domain.Location; import org.jclouds.domain.Location;
import org.jclouds.http.HttpUtils; import org.jclouds.http.HttpUtils;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.util.CredentialUtils; import org.jclouds.util.CredentialUtils;
import com.google.common.base.CaseFormat; import com.google.common.base.CaseFormat;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.cache.Cache;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.inject.Provider; import com.google.inject.Provider;
@ -56,7 +55,7 @@ import com.google.inject.Provider;
*/ */
public class ComputeTask extends Task { public class ComputeTask extends Task {
private final Map<URI, ComputeServiceContext> computeMap; private final Cache<URI, ComputeServiceContext> computeMap;
private String provider; private String provider;
private String actions; private String actions;
private NodeElement nodeElement; private NodeElement nodeElement;
@ -72,7 +71,7 @@ public class ComputeTask extends Task {
} }
}; };
public ComputeTask(@Nullable Map<URI, ComputeServiceContext> computeMap) { public ComputeTask(@Nullable Cache<URI, ComputeServiceContext> computeMap) {
this.computeMap = computeMap != null ? computeMap : buildComputeMap(projectProvider); 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 * makes a connection to the compute service and invokes
*/ */
public void execute() throws BuildException { public void execute() throws BuildException {
ComputeServiceContext context = computeMap.get(HttpUtils.createUri(provider)); ComputeServiceContext context = computeMap.getUnchecked(HttpUtils.createUri(provider));
try { try {
for (String action : Splitter.on(',').split(actions)) { for (String action : Splitter.on(',').split(actions)) {

View File

@ -22,7 +22,6 @@ import static org.jclouds.rest.RestContextFactory.getPropertiesFromResource;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
@ -43,11 +42,12 @@ import org.jclouds.ssh.jsch.config.JschSshClientModule;
import org.jclouds.tools.ant.logging.config.AntLoggingModule; import org.jclouds.tools.ant.logging.config.AntLoggingModule;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.base.Function;
import com.google.common.base.Splitter; 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.ImmutableSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.MapMaker;
import com.google.common.io.Files; import com.google.common.io.Files;
import com.google.inject.Module; import com.google.inject.Module;
import com.google.inject.Provider; 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 * allows access to the ant project to retrieve default properties needed for compute
* providers. * providers.
*/ */
static Map<URI, ComputeServiceContext> buildComputeMap(final Provider<Project> projectProvider) { static Cache<URI, ComputeServiceContext> buildComputeMap(final Provider<Project> projectProvider) {
return new MapMaker().makeComputingMap(new Function<URI, ComputeServiceContext>() { return CacheBuilder.newBuilder().build(new CacheLoader<URI, ComputeServiceContext>() {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public ComputeServiceContext apply(URI from) { public ComputeServiceContext load(URI from) {
Properties props = getPropertiesFromResource("/rest.properties"); Properties props = getPropertiesFromResource("/rest.properties");
props.putAll(projectProvider.get().getProperties()); props.putAll(projectProvider.get().getProperties());
// adding the properties to the factory will allow us to pass // adding the properties to the factory will allow us to pass

View File

@ -68,6 +68,7 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableMultimap.Builder; import com.google.common.collect.ImmutableMultimap.Builder;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
@ -78,8 +79,8 @@ import com.google.common.collect.ImmutableSet;
@Singleton @Singleton
public class EC2ComputeService extends BaseComputeService { public class EC2ComputeService extends BaseComputeService {
private final EC2Client ec2Client; private final EC2Client ec2Client;
private final Map<RegionAndName, KeyPair> credentialsMap; private final Cache<RegionAndName, KeyPair> credentialsMap;
private final Map<RegionAndName, String> securityGroupMap; private final Cache<RegionAndName, String> securityGroupMap;
@Inject @Inject
protected EC2ComputeService(ComputeServiceContext context, Map<String, Credentials> credentialStore, protected EC2ComputeService(ComputeServiceContext context, Map<String, Credentials> credentialStore,
@ -96,7 +97,7 @@ public class EC2ComputeService extends BaseComputeService {
RunScriptOnNode.Factory runScriptOnNodeFactory, InitAdminAccess initAdminAccess, RunScriptOnNode.Factory runScriptOnNodeFactory, InitAdminAccess initAdminAccess,
PersistNodeCredentials persistNodeCredentials, Timeouts timeouts, PersistNodeCredentials persistNodeCredentials, Timeouts timeouts,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, EC2Client ec2Client, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, EC2Client ec2Client,
Map<RegionAndName, KeyPair> credentialsMap, @Named("SECURITY") Map<RegionAndName, String> securityGroupMap) { Cache<RegionAndName, KeyPair> credentialsMap, @Named("SECURITY") Cache<RegionAndName, String> securityGroupMap) {
super(context, credentialStore, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy, super(context, credentialStore, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy,
runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, stopNodeStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, stopNodeStrategy,
templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, nodeSuspended, templateBuilderProvider, templateOptionsProvider, nodeRunning, nodeTerminated, nodeSuspended,
@ -116,7 +117,7 @@ public class EC2ComputeService extends BaseComputeService {
try { try {
ec2Client.getSecurityGroupServices().deleteSecurityGroupInRegion(region, groupName); ec2Client.getSecurityGroupServices().deleteSecurityGroupInRegion(region, groupName);
// TODO: test this clear happens // 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); logger.debug("<< deleted securityGroup(%s)", groupName);
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
logger.debug("<< inUse securityGroup(%s)", groupName); logger.debug("<< inUse securityGroup(%s)", groupName);
@ -142,7 +143,7 @@ public class EC2ComputeService extends BaseComputeService {
logger.debug(">> deleting keyPair(%s)", keyPair.getKeyName()); logger.debug(">> deleting keyPair(%s)", keyPair.getKeyName());
ec2Client.getKeyPairServices().deleteKeyPairInRegion(region, keyPair.getKeyName()); ec2Client.getKeyPairServices().deleteKeyPairInRegion(region, keyPair.getKeyName());
// TODO: test this clear happens // 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()); logger.debug("<< deleted keyPair(%s)", keyPair.getKeyName());
} }
} }

View File

@ -22,8 +22,6 @@ import static com.google.common.collect.Iterables.toArray;
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL; import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS; import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS;
import java.util.Map;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; 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.Splitter;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.cache.Cache;
import com.google.inject.Provides; import com.google.inject.Provides;
/** /**
@ -59,12 +58,12 @@ public class EC2ComputeServiceContextModule extends BaseComputeServiceContextMod
@Provides @Provides
@Singleton @Singleton
protected Supplier<Map<RegionAndName, ? extends Image>> provideRegionAndNameToImageSupplierCache( protected Supplier<Cache<RegionAndName, ? extends Image>> provideRegionAndNameToImageSupplierCache(
@Named(PROPERTY_SESSION_INTERVAL) long seconds, final RegionAndNameToImageSupplier supplier) { @Named(PROPERTY_SESSION_INTERVAL) long seconds, final RegionAndNameToImageSupplier supplier) {
return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<RegionAndName, ? extends Image>>( return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Cache<RegionAndName, ? extends Image>>(
authException, seconds, new Supplier<Map<RegionAndName, ? extends Image>>() { authException, seconds, new Supplier<Cache<RegionAndName, ? extends Image>>() {
@Override @Override
public Map<RegionAndName, ? extends Image> get() { public Cache<RegionAndName, ? extends Image> get() {
return supplier.get(); return supplier.get();
} }
}); });

View File

@ -18,8 +18,6 @@
*/ */
package org.jclouds.ec2.compute.config; package org.jclouds.ec2.compute.config;
import static com.google.common.collect.Maps.newLinkedHashMap;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Map; import java.util.Map;
@ -39,7 +37,6 @@ import org.jclouds.ec2.EC2AsyncClient;
import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.EC2Client;
import org.jclouds.ec2.compute.EC2ComputeService; import org.jclouds.ec2.compute.EC2ComputeService;
import org.jclouds.ec2.compute.domain.RegionAndName; 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.CreateSecurityGroupIfNeeded;
import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair; import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair;
import org.jclouds.ec2.compute.functions.CredentialsForInstance; 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.Function;
import com.google.common.base.Supplier; 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.ImmutableMap;
import com.google.common.collect.MapMaker; import com.google.common.collect.Maps;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Provides; import com.google.inject.Provides;
import com.google.inject.Scopes; import com.google.inject.Scopes;
@ -69,11 +69,11 @@ import com.google.inject.TypeLiteral;
public class EC2ComputeServiceDependenciesModule extends AbstractModule { public class EC2ComputeServiceDependenciesModule extends AbstractModule {
public static final Map<InstanceState, NodeState> instanceToNodeState = ImmutableMap public static final Map<InstanceState, NodeState> instanceToNodeState = ImmutableMap
.<InstanceState, NodeState> builder().put(InstanceState.PENDING, NodeState.PENDING).put( .<InstanceState, NodeState> builder().put(InstanceState.PENDING, NodeState.PENDING)
InstanceState.RUNNING, NodeState.RUNNING).put(InstanceState.SHUTTING_DOWN, NodeState.PENDING).put( .put(InstanceState.RUNNING, NodeState.RUNNING).put(InstanceState.SHUTTING_DOWN, NodeState.PENDING)
InstanceState.TERMINATED, NodeState.TERMINATED).put(InstanceState.STOPPING, NodeState.PENDING) .put(InstanceState.TERMINATED, NodeState.TERMINATED).put(InstanceState.STOPPING, NodeState.PENDING)
.put(InstanceState.STOPPED, NodeState.SUSPENDED).put(InstanceState.UNRECOGNIZED, NodeState.UNRECOGNIZED) .put(InstanceState.STOPPED, NodeState.SUSPENDED).put(InstanceState.UNRECOGNIZED, NodeState.UNRECOGNIZED)
.build(); .build();
@Singleton @Singleton
@Provides @Provides
@ -88,13 +88,13 @@ public class EC2ComputeServiceDependenciesModule extends AbstractModule {
bind(ComputeService.class).to(EC2ComputeService.class); bind(ComputeService.class).to(EC2ComputeService.class);
bind(new TypeLiteral<Function<RunningInstance, NodeMetadata>>() { bind(new TypeLiteral<Function<RunningInstance, NodeMetadata>>() {
}).to(RunningInstanceToNodeMetadata.class); }).to(RunningInstanceToNodeMetadata.class);
bind(new TypeLiteral<Function<RunningInstance, Credentials>>() { bind(new TypeLiteral<CacheLoader<RunningInstance, Credentials>>() {
}).to(CredentialsForInstance.class); }).to(CredentialsForInstance.class);
bind(new TypeLiteral<Function<RegionNameAndIngressRules, String>>() { bind(new TypeLiteral<CacheLoader<RegionAndName, String>>() {
}).to(CreateSecurityGroupIfNeeded.class); }).to(CreateSecurityGroupIfNeeded.class);
bind(new TypeLiteral<Function<RegionAndName, KeyPair>>() { bind(new TypeLiteral<CacheLoader<RegionAndName, KeyPair>>() {
}).to(CreateUniqueKeyPair.class); }).to(CreateUniqueKeyPair.class);
bind(new TypeLiteral<Function<RegionAndName, Image>>() { bind(new TypeLiteral<CacheLoader<RegionAndName, Image>>() {
}).to(RegionAndIdToImage.class); }).to(RegionAndIdToImage.class);
bind(new TypeLiteral<ComputeServiceContext>() { bind(new TypeLiteral<ComputeServiceContext>() {
}).to(new TypeLiteral<ComputeServiceContextImpl<EC2Client, EC2AsyncClient>>() { }).to(new TypeLiteral<ComputeServiceContextImpl<EC2Client, EC2AsyncClient>>() {
@ -120,25 +120,35 @@ public class EC2ComputeServiceDependenciesModule extends AbstractModule {
@Provides @Provides
@Singleton @Singleton
protected final Map<RegionAndName, KeyPair> credentialsMap(Function<RegionAndName, KeyPair> in) { protected Cache<RunningInstance, Credentials> credentialsMap(CacheLoader<RunningInstance, Credentials> in) {
// doesn't seem to clear when someone issues remove(key) return CacheBuilder.newBuilder().build(in);
// return new MapMaker().makeComputingMap(in); }
return newLinkedHashMap();
@Provides
@Singleton
protected Cache<RegionAndName, KeyPair> keypairMap(CacheLoader<RegionAndName, KeyPair> in) {
return CacheBuilder.newBuilder().build(in);
}
@Provides
@Singleton
// keys that we know about.
protected Map<RegionAndName, KeyPair> knownKeys(){
return Maps.newConcurrentMap();
} }
@Provides @Provides
@Singleton @Singleton
@Named("SECURITY") @Named("SECURITY")
protected final Map<RegionAndName, String> securityGroupMap(Function<RegionNameAndIngressRules, String> in) { protected Cache<RegionAndName, String> securityGroupMap(CacheLoader<RegionAndName, String> in) {
// doesn't seem to clear when someone issues remove(key) return CacheBuilder.newBuilder().build(in);
// return new MapMaker().makeComputingMap(in);
return newLinkedHashMap();
} }
@Provides @Provides
@Singleton @Singleton
protected Map<RegionAndName, Image> provideImageMap(Function<RegionAndName, Image> regionAndIdToImage) { // keys that we know about.
return new MapMaker().makeComputingMap(regionAndIdToImage); protected Map<RegionAndName, Image> knownImages(){
return Maps.newConcurrentMap();
} }
} }

View File

@ -25,14 +25,15 @@ import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.EC2Client;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules;
import org.jclouds.ec2.domain.IpProtocol; import org.jclouds.ec2.domain.IpProtocol;
import org.jclouds.ec2.domain.UserIdGroupPair; import org.jclouds.ec2.domain.UserIdGroupPair;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
import com.google.common.base.Function; import com.google.common.cache.CacheLoader;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
/** /**
@ -40,7 +41,7 @@ import com.google.common.collect.Iterables;
* @author Adrian Cole * @author Adrian Cole
*/ */
@Singleton @Singleton
public class CreateSecurityGroupIfNeeded implements Function<RegionNameAndIngressRules, String> { public class CreateSecurityGroupIfNeeded extends CacheLoader<RegionAndName, String> {
@Resource @Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER) @Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
@ -52,8 +53,9 @@ public class CreateSecurityGroupIfNeeded implements Function<RegionNameAndIngres
} }
@Override @Override
public String apply(RegionNameAndIngressRules from) { public String load(RegionAndName from) {
createSecurityGroupInRegion(from.getRegion(), from.getName(), from.getPorts()); RegionNameAndIngressRules realFrom = RegionNameAndIngressRules.class.cast(from);
createSecurityGroupInRegion(from.getRegion(), from.getName(), realFrom.getPorts());
return from.getName(); return from.getName();
} }

View File

@ -20,6 +20,8 @@ package org.jclouds.ec2.compute.functions;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Map;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@ -32,30 +34,33 @@ import org.jclouds.ec2.domain.KeyPair;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.cache.CacheLoader;
/** /**
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@Singleton @Singleton
public class CreateUniqueKeyPair implements Function<RegionAndName, KeyPair> { public class CreateUniqueKeyPair extends CacheLoader<RegionAndName, KeyPair> {
@Resource @Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER) @Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
protected final EC2Client ec2Client; protected final EC2Client ec2Client;
protected final Supplier<String> randomSuffix; protected final Supplier<String> randomSuffix;
protected final Map<RegionAndName, KeyPair> knownKeys;
@Inject @Inject
public CreateUniqueKeyPair(EC2Client ec2Client, Supplier<String> randomSuffix) { public CreateUniqueKeyPair(Map<RegionAndName, KeyPair> knownKeys, EC2Client ec2Client, Supplier<String> randomSuffix) {
this.knownKeys = knownKeys;
this.ec2Client = ec2Client; this.ec2Client = ec2Client;
this.randomSuffix = randomSuffix; this.randomSuffix = randomSuffix;
} }
@Override @Override
public KeyPair apply(RegionAndName from) { public KeyPair load(RegionAndName from) {
return createNewKeyPairInRegion(from.getRegion(), from.getName()); return knownKeys.containsKey(from) ? knownKeys.get(from) : createNewKeyPairInRegion(from.getRegion(),
from.getName());
} }
@VisibleForTesting @VisibleForTesting

View File

@ -20,43 +20,44 @@ package org.jclouds.ec2.compute.functions;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Map; import java.util.concurrent.ExecutionException;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; 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.domain.Image;
import org.jclouds.compute.strategy.PopulateDefaultLoginCredentialsForImageStrategy; import org.jclouds.compute.strategy.PopulateDefaultLoginCredentialsForImageStrategy;
import org.jclouds.domain.Credentials; 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.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 * @author Adrian Cole
*/ */
@Singleton @Singleton
public class CredentialsForInstance implements Function<RunningInstance, Credentials> { public class CredentialsForInstance extends CacheLoader<RunningInstance, Credentials> {
private final Map<RegionAndName, KeyPair> credentialsMap; private final Cache<RegionAndName, KeyPair> credentialsMap;
private final PopulateDefaultLoginCredentialsForImageStrategy credentialProvider; private final PopulateDefaultLoginCredentialsForImageStrategy credentialProvider;
private final Map<RegionAndName, Image> imageForInstance; private final Supplier<Cache<RegionAndName, ? extends Image>> imageMap;
@Inject @Inject
CredentialsForInstance(Map<RegionAndName, KeyPair> credentialsMap, CredentialsForInstance(Cache<RegionAndName, KeyPair> credentialsMap,
PopulateDefaultLoginCredentialsForImageStrategy credentialProvider, Map<RegionAndName, Image> imageForInstance) { PopulateDefaultLoginCredentialsForImageStrategy credentialProvider, Supplier<Cache<RegionAndName, ? extends Image>> imageMap) {
this.credentialsMap = checkNotNull(credentialsMap, "credentialsMap"); this.credentialsMap = checkNotNull(credentialsMap, "credentialsMap");
this.credentialProvider = checkNotNull(credentialProvider, "credentialProvider"); this.credentialProvider = checkNotNull(credentialProvider, "credentialProvider");
this.imageForInstance = imageForInstance; this.imageMap = imageMap;
} }
@Override @Override
public Credentials apply(RunningInstance instance) { public Credentials load(RunningInstance instance) throws ExecutionException {
Credentials credentials = null;// default if no keypair exists Credentials credentials = null;// default if no keypair exists
if (instance.getKeyName() != null) { if (instance.getKeyName() != null) {
credentials = new Credentials(getLoginAccountFor(instance), getPrivateKeyOrNull(instance)); credentials = new Credentials(getLoginAccountFor(instance), getPrivateKeyOrNull(instance));
} }
@ -64,15 +65,15 @@ public class CredentialsForInstance implements Function<RunningInstance, Credent
} }
@VisibleForTesting @VisibleForTesting
String getPrivateKeyOrNull(RunningInstance instance) { String getPrivateKeyOrNull(RunningInstance instance) throws ExecutionException {
KeyPair keyPair = credentialsMap.get(new RegionAndName(instance.getRegion(), instance.getKeyName())); KeyPair keyPair = credentialsMap.get(new RegionAndName(instance.getRegion(), instance.getKeyName()));
return keyPair != null ? keyPair.getKeyMaterial() : null; return keyPair != null ? keyPair.getKeyMaterial() : null;
} }
@VisibleForTesting @VisibleForTesting
String getLoginAccountFor(RunningInstance from) { String getLoginAccountFor(RunningInstance from) throws ExecutionException {
return checkNotNull( return checkNotNull(
credentialProvider.execute(imageForInstance.get(new RegionAndName(from.getRegion(), from.getImageId()))), credentialProvider.execute(imageMap.get().get(new RegionAndName(from.getRegion(), from.getImageId()))),
"login from image: " + from.getImageId()).identity; "login from image: " + from.getImageId()).identity;
} }
} }

View File

@ -20,19 +20,19 @@ package org.jclouds.ec2.compute.functions;
import static org.jclouds.ec2.options.DescribeImagesOptions.Builder.imageIds; import static org.jclouds.ec2.options.DescribeImagesOptions.Builder.imageIds;
import java.util.NoSuchElementException; import java.util.Map;
import java.util.concurrent.ExecutionException;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.compute.domain.Image;
import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.EC2Client;
import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.compute.domain.Image;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
import org.jclouds.rest.ResourceNotFoundException;
import com.google.common.base.Function; import com.google.common.cache.CacheLoader;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
/** /**
@ -40,33 +40,31 @@ import com.google.common.collect.Iterables;
* @author Adrian Cole * @author Adrian Cole
*/ */
@Singleton @Singleton
public final class RegionAndIdToImage implements Function<RegionAndName, Image> { public class RegionAndIdToImage extends CacheLoader<RegionAndName, Image> {
@Resource @Resource
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
private final Map<RegionAndName, Image> knownImages;
private final EC2ImageParser parser; private final EC2ImageParser parser;
private final EC2Client sync; private final EC2Client sync;
@Inject @Inject
public RegionAndIdToImage(EC2ImageParser parser, EC2Client sync) { public RegionAndIdToImage(Map<RegionAndName, Image> knownImages, EC2ImageParser parser, EC2Client sync) {
this.knownImages = knownImages;
this.parser = parser; this.parser = parser;
this.sync = sync; 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 { try {
org.jclouds.ec2.domain.Image image = Iterables.getOnlyElement(sync.getAMIServices() org.jclouds.ec2.domain.Image image = Iterables.getOnlyElement(sync.getAMIServices()
.describeImagesInRegion(key.getRegion(), imageIds(key.getName()))); .describeImagesInRegion(key.getRegion(), imageIds(key.getName())));
return parser.apply(image); 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) { } catch (Exception e) {
logger.warn(e, message(key, e)); throw new ExecutionException(message(key, e), e);
return null;
} }
} }

View File

@ -30,11 +30,6 @@ import javax.annotation.Resource;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; 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.collect.Memoized;
import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.HardwareBuilder; 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.compute.domain.internal.VolumeImpl;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.domain.Location; 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.logging.Logger;
import org.jclouds.util.NullSafeCollections; 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.Function;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.cache.Cache;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.util.concurrent.UncheckedExecutionException;
/** /**
* @author Adrian Cole * @author Adrian Cole
@ -67,17 +69,17 @@ public class RunningInstanceToNodeMetadata implements Function<RunningInstance,
protected final Supplier<Set<? extends Location>> locations; protected final Supplier<Set<? extends Location>> locations;
protected final Supplier<Set<? extends Hardware>> hardware; protected final Supplier<Set<? extends Hardware>> hardware;
protected final Map<RegionAndName, Image> instanceToImage; protected final Supplier<Cache<RegionAndName, ? extends Image>> imageMap;
protected final Map<String, Credentials> credentialStore; protected final Map<String, Credentials> credentialStore;
protected final Map<InstanceState, NodeState> instanceToNodeState; protected final Map<InstanceState, NodeState> instanceToNodeState;
@Inject @Inject
protected RunningInstanceToNodeMetadata(Map<InstanceState, NodeState> instanceToNodeState, protected RunningInstanceToNodeMetadata(Map<InstanceState, NodeState> instanceToNodeState,
Map<String, Credentials> credentialStore, Map<RegionAndName, Image> instanceToImage, Map<String, Credentials> credentialStore, Supplier<Cache<RegionAndName, ? extends Image>> imageMap,
@Memoized Supplier<Set<? extends Location>> locations, @Memoized Supplier<Set<? extends Hardware>> hardware) { @Memoized Supplier<Set<? extends Location>> locations, @Memoized Supplier<Set<? extends Hardware>> hardware) {
this.locations = checkNotNull(locations, "locations"); this.locations = checkNotNull(locations, "locations");
this.hardware = checkNotNull(hardware, "hardware"); this.hardware = checkNotNull(hardware, "hardware");
this.instanceToImage = checkNotNull(instanceToImage, "instanceToImage"); this.imageMap = checkNotNull(imageMap, "imageMap");
this.instanceToNodeState = checkNotNull(instanceToNodeState, "instanceToNodeState"); this.instanceToNodeState = checkNotNull(instanceToNodeState, "instanceToNodeState");
this.credentialStore = checkNotNull(credentialStore, "credentialStore"); this.credentialStore = checkNotNull(credentialStore, "credentialStore");
} }
@ -106,17 +108,13 @@ public class RunningInstanceToNodeMetadata implements Function<RunningInstance,
// extract the operating system from the image // extract the operating system from the image
RegionAndName regionAndName = new RegionAndName(instance.getRegion(), instance.getImageId()); RegionAndName regionAndName = new RegionAndName(instance.getRegion(), instance.getImageId());
try { try {
Image image = instanceToImage.get(regionAndName); Image image = imageMap.get().getUnchecked(regionAndName);
if (image != null) if (image != null)
builder.operatingSystem(image.getOperatingSystem()); builder.operatingSystem(image.getOperatingSystem());
} catch (NullPointerException e) { } catch (NullPointerException e) {
// The instanceToImage Map may throw NullPointerException (actually subclass logger.debug("image not found for %s: %s", regionAndName, e);
// NullOutputException) if the } catch (UncheckedExecutionException e) {
// computing Function returns a null value. logger.debug("error getting image for %s: %s", regionAndName, e);
//
// See the following for more information:
// MapMaker.makeComputingMap()
// RegionAndIdToImage.apply()
} }
return builder.build(); return builder.build();

View File

@ -20,9 +20,9 @@ package org.jclouds.ec2.compute.internal;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@ -38,7 +38,8 @@ import org.jclouds.domain.Location;
import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionAndName;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.collect.ComputationException; import com.google.common.cache.Cache;
import com.google.common.util.concurrent.UncheckedExecutionException;
/** /**
* *
@ -46,13 +47,13 @@ import com.google.common.collect.ComputationException;
*/ */
public class EC2TemplateBuilderImpl extends TemplateBuilderImpl { public class EC2TemplateBuilderImpl extends TemplateBuilderImpl {
private final Map<RegionAndName, Image> imageMap; private final Supplier<Cache<RegionAndName, ? extends Image>> imageMap;
@Inject @Inject
protected EC2TemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations, protected EC2TemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations,
@Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> sizes, @Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> sizes,
Supplier<Location> defaultLocation, @Named("DEFAULT") Provider<TemplateOptions> optionsProvider, Supplier<Location> defaultLocation, @Named("DEFAULT") Provider<TemplateOptions> optionsProvider,
@Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider, Map<RegionAndName, Image> imageMap) { @Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider, Supplier<Cache<RegionAndName, ? extends Image>> imageMap) {
super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider); super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider);
this.imageMap = imageMap; 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); "amazon image ids must include the region ( ex. us-east-1/ami-7ea24a17 ) you specified: " + imageId);
RegionAndName key = new RegionAndName(regionName[0], regionName[1]); RegionAndName key = new RegionAndName(regionName[0], regionName[1]);
try { 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) { } catch (NullPointerException nex) {
throw new NoSuchElementException(String.format("imageId(%s/%s) not found", key.getRegion(), key.getName())); 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; return null;

View File

@ -23,7 +23,6 @@ import static com.google.common.base.Preconditions.checkState;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.jclouds.javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Provider; 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.BlockDeviceMapping;
import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.domain.KeyPair;
import org.jclouds.ec2.options.RunInstancesOptions; import org.jclouds.ec2.options.RunInstancesOptions;
import org.jclouds.javax.annotation.Nullable;
import com.google.common.annotations.VisibleForTesting; 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;
import com.google.common.collect.ImmutableSet.Builder; 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 @Singleton
public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions { public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions {
@VisibleForTesting @VisibleForTesting
public final Map<RegionAndName, KeyPair> credentialsMap; public final Map<RegionAndName, KeyPair> knownKeys;
@VisibleForTesting @VisibleForTesting
public final Map<RegionAndName, String> securityGroupMap; public final Cache<RegionAndName, KeyPair> credentialsMap;
@VisibleForTesting @VisibleForTesting
public final Function<RegionAndName, KeyPair> createUniqueKeyPair; public final Cache<RegionAndName, String> securityGroupMap;
@VisibleForTesting
public final Function<RegionNameAndIngressRules, String> createSecurityGroupIfNeeded;
protected final Provider<RunInstancesOptions> optionsProvider; protected final Provider<RunInstancesOptions> optionsProvider;
@Inject @Inject
public CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions(Map<RegionAndName, KeyPair> credentialsMap, public CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions(Map<RegionAndName, KeyPair> knownKeys, Cache<RegionAndName, KeyPair> credentialsMap,
@Named("SECURITY") Map<RegionAndName, String> securityGroupMap, Function<RegionAndName, KeyPair> createUniqueKeyPair, @Named("SECURITY") Cache<RegionAndName, String> securityGroupMap,
Function<RegionNameAndIngressRules, String> createSecurityGroupIfNeeded,
Provider<RunInstancesOptions> optionsProvider) { Provider<RunInstancesOptions> optionsProvider) {
this.knownKeys=knownKeys;
this.credentialsMap = credentialsMap; this.credentialsMap = credentialsMap;
this.securityGroupMap = securityGroupMap; this.securityGroupMap = securityGroupMap;
this.createUniqueKeyPair = createUniqueKeyPair;
this.createSecurityGroupIfNeeded = createSecurityGroupIfNeeded;
this.optionsProvider = optionsProvider; this.optionsProvider = optionsProvider;
} }
@ -123,40 +119,32 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions {
if (options.getOverridingCredentials() != null && options.getOverridingCredentials().credential != null) { if (options.getOverridingCredentials() != null && options.getOverridingCredentials().credential != null) {
KeyPair keyPair = KeyPair.builder().region(region).keyName(keyPairName).keyFingerprint("//TODO") KeyPair keyPair = KeyPair.builder().region(region).keyName(keyPairName).keyFingerprint("//TODO")
.keyMaterial(options.getOverridingCredentials().credential).build(); .keyMaterial(options.getOverridingCredentials().credential).build();
putKeyPairIntoCredentialMap(keyPair);
RegionAndName key = new RegionAndName(region, group);
knownKeys.put(key, keyPair);
credentialsMap.invalidate(key);
} }
} }
if (options.getRunScript() != null) { if (options.getRunScript() != null) {
RegionAndName regionAndName = new RegionAndName(region, keyPairName); RegionAndName regionAndName = new RegionAndName(region, keyPairName);
checkState(credentialsMap.containsKey(regionAndName), String message = String.format("no private key configured for: %s; please use options.overrideLoginCredentialWith(rsa_private_text)",
"no private key configured for: %s; please use options.overrideLoginCredentialWith(rsa_private_text)", regionAndName);
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; return keyPairName;
} }
// base EC2 driver currently does not support key import // base EC2 driver currently does not support key import
protected String createOrImportKeyPair(String region, String group, TemplateOptions options) { protected String createOrImportKeyPair(String region, String group, TemplateOptions options) {
return createUniqueKeyPairAndPutIntoMap(region, group); return credentialsMap.getUnchecked(new RegionAndName(region, group)).getKeyName();
}
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);
} }
@VisibleForTesting @VisibleForTesting
@ -177,11 +165,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions {
regionNameAndIngessRulesForMarkerGroup = new RegionNameAndIngressRules(region, markerGroup, regionNameAndIngessRulesForMarkerGroup = new RegionNameAndIngressRules(region, markerGroup,
options.getInboundPorts(), true); options.getInboundPorts(), true);
} }
// this will create if not yet exists.
if (!securityGroupMap.containsKey(regionNameAndIngessRulesForMarkerGroup)) { securityGroupMap.getUnchecked(regionNameAndIngessRulesForMarkerGroup);
securityGroupMap.put(regionNameAndIngessRulesForMarkerGroup,
createSecurityGroupIfNeeded.apply(regionNameAndIngessRulesForMarkerGroup));
}
} }
return groups.build(); return groups.build();
} }

View File

@ -52,6 +52,7 @@ import org.jclouds.logging.Logger;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
@ -77,7 +78,7 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen
@VisibleForTesting @VisibleForTesting
final ComputeUtils utils; final ComputeUtils utils;
final InstancePresent instancePresent; final InstancePresent instancePresent;
final Function<RunningInstance, Credentials> instanceToCredentials; final Cache<RunningInstance, Credentials> instanceToCredentials;
final Map<String, Credentials> credentialStore; final Map<String, Credentials> credentialStore;
final Provider<TemplateBuilder> templateBuilderProvider; final Provider<TemplateBuilder> templateBuilderProvider;
@ -87,7 +88,7 @@ public class EC2CreateNodesInGroupThenAddToSet implements CreateNodesInGroupThen
Provider<TemplateBuilder> templateBuilderProvider, Provider<TemplateBuilder> templateBuilderProvider,
CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize,
InstancePresent instancePresent, Function<RunningInstance, NodeMetadata> runningInstanceToNodeMetadata, InstancePresent instancePresent, Function<RunningInstance, NodeMetadata> runningInstanceToNodeMetadata,
Function<RunningInstance, Credentials> instanceToCredentials, Map<String, Credentials> credentialStore, Cache<RunningInstance, Credentials> instanceToCredentials, Map<String, Credentials> credentialStore,
ComputeUtils utils) { ComputeUtils utils) {
this.client = checkNotNull(client, "client"); this.client = checkNotNull(client, "client");
this.templateBuilderProvider = checkNotNull(templateBuilderProvider, "templateBuilderProvider"); this.templateBuilderProvider = checkNotNull(templateBuilderProvider, "templateBuilderProvider");

View File

@ -18,16 +18,16 @@
*/ */
package org.jclouds.ec2.compute.suppliers; package org.jclouds.ec2.compute.suppliers;
import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.Image;
import org.jclouds.ec2.compute.domain.RegionAndName;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.cache.Cache;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
/** /**
@ -36,16 +36,16 @@ import com.google.common.collect.Sets;
*/ */
@Singleton @Singleton
public class EC2ImageSupplier implements Supplier<Set<? extends Image>> { public class EC2ImageSupplier implements Supplier<Set<? extends Image>> {
private final Supplier<Map<RegionAndName, ? extends Image>> map; private final Supplier<Cache<RegionAndName, ? extends Image>> map;
@Inject @Inject
EC2ImageSupplier(Supplier<Map<RegionAndName, ? extends Image>> map) { EC2ImageSupplier(Supplier<Cache<RegionAndName, ? extends Image>> map) {
this.map = map; this.map = map;
} }
@Override @Override
public Set<? extends Image> get() { public Set<? extends Image> get() {
return Sets.newLinkedHashSet(map.get().values()); return Sets.newLinkedHashSet(map.get().asMap().values());
} }
} }

View File

@ -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 static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.inject.Inject; import javax.inject.Inject;
@ -45,16 +45,19 @@ import org.jclouds.logging.Logger;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.base.Supplier; 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.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableMap.Builder; import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.ImmutableSet;
/** /**
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@Singleton @Singleton
public class RegionAndNameToImageSupplier implements Supplier<Map<RegionAndName, ? extends Image>> { public class RegionAndNameToImageSupplier implements Supplier<Cache<RegionAndName, ? extends Image>> {
@Resource @Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER) @Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
@ -63,20 +66,23 @@ public class RegionAndNameToImageSupplier implements Supplier<Map<RegionAndName,
private final DescribeImagesParallel describer; private final DescribeImagesParallel describer;
private final String[] amiOwners; private final String[] amiOwners;
private final EC2ImageParser parser; private final EC2ImageParser parser;
private final Map<RegionAndName, Image> images; private final Map<RegionAndName, Image> knownImages;
private final CacheLoader<RegionAndName, Image> regionAndIdToImage;
@Inject @Inject
protected RegionAndNameToImageSupplier(@Region Set<String> regions, DescribeImagesParallel describer, protected RegionAndNameToImageSupplier(@Region Set<String> regions, DescribeImagesParallel describer,
@Named(PROPERTY_EC2_AMI_OWNERS) String[] amiOwners, EC2ImageParser parser, Map<RegionAndName, Image> images) { @Named(PROPERTY_EC2_AMI_OWNERS) String[] amiOwners, EC2ImageParser parser,
Map<RegionAndName, Image> knownImages, CacheLoader<RegionAndName, Image> regionAndIdToImage) {
this.regions = regions; this.regions = regions;
this.describer = describer; this.describer = describer;
this.amiOwners = amiOwners; this.amiOwners = amiOwners;
this.parser = parser; this.parser = parser;
this.images = images; this.knownImages = knownImages;
this.regionAndIdToImage = regionAndIdToImage;
} }
@Override @Override
public Map<RegionAndName, ? extends Image> get() { public Cache<RegionAndName, ? extends Image> get() {
if (amiOwners.length == 0) { if (amiOwners.length == 0) {
logger.debug(">> no owners specified, skipping image parsing"); logger.debug(">> no owners specified, skipping image parsing");
} else { } else {
@ -87,8 +93,8 @@ public class RegionAndNameToImageSupplier implements Supplier<Map<RegionAndName,
Iterable<? extends Image> parsedImages = ImmutableSet.copyOf(filter(transform(describer.apply(queries), parser), Predicates Iterable<? extends Image> parsedImages = ImmutableSet.copyOf(filter(transform(describer.apply(queries), parser), Predicates
.notNull())); .notNull()));
knownImages.clear();
images.putAll(uniqueIndex(parsedImages, new Function<Image, RegionAndName>() { knownImages.putAll(uniqueIndex(parsedImages, new Function<Image, RegionAndName>() {
@Override @Override
public RegionAndName apply(Image from) { public RegionAndName apply(Image from) {
@ -96,14 +102,18 @@ public class RegionAndNameToImageSupplier implements Supplier<Map<RegionAndName,
} }
})); }));
logger.debug("<< images(%d)", knownImages.size());
logger.debug("<< images(%d)", images.size());
} }
return images; Cache<RegionAndName, Image> cache = CacheBuilder.newBuilder().build(regionAndIdToImage);
// seed the cache
for (RegionAndName image : knownImages.keySet()) {
cache.getUnchecked(image);
}
return cache;
} }
public Iterable<Entry<String, DescribeImagesOptions>> getDescribeQueriesForOwnersInRegions(Set<String> regions, public Iterable<Entry<String, DescribeImagesOptions>> getDescribeQueriesForOwnersInRegions(Set<String> regions,
String[] amiOwners) { String[] amiOwners) {
DescribeImagesOptions options = getOptionsForOwners(amiOwners); DescribeImagesOptions options = getOptionsForOwners(amiOwners);
Builder<String, DescribeImagesOptions> builder = ImmutableMap.<String, DescribeImagesOptions> builder(); Builder<String, DescribeImagesOptions> builder = ImmutableMap.<String, DescribeImagesOptions> builder();
for (String region : regions) for (String region : regions)

View File

@ -25,8 +25,10 @@ import static org.easymock.classextension.EasyMock.verify;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.Map;
import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.EC2Client;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.domain.KeyPair;
import org.jclouds.ec2.services.KeyPairClient; import org.jclouds.ec2.services.KeyPairClient;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -38,58 +40,127 @@ import com.google.common.base.Supplier;
*/ */
@Test(groups = "unit") @Test(groups = "unit")
public class CreateUniqueKeyPairTest { public class CreateUniqueKeyPairTest {
@SuppressWarnings( { "unchecked" }) @SuppressWarnings({ "unchecked" })
@Test @Test
public void testApply() throws UnknownHostException { public void testApply() throws UnknownHostException {
EC2Client client = createMock(EC2Client.class); EC2Client client = createMock(EC2Client.class);
KeyPairClient keyClient = createMock(KeyPairClient.class); KeyPairClient keyClient = createMock(KeyPairClient.class);
Supplier<String> uniqueIdSupplier = createMock(Supplier.class); Supplier<String> uniqueIdSupplier = createMock(Supplier.class);
Map<RegionAndName, KeyPair> knownKeys = createMock(Map.class);
KeyPair pair = createMock(KeyPair.class); KeyPair pair = createMock(KeyPair.class);
expect(client.getKeyPairServices()).andReturn(keyClient).atLeastOnce(); expect(client.getKeyPairServices()).andReturn(keyClient).atLeastOnce();
expect(uniqueIdSupplier.get()).andReturn("1"); 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(client);
replay(knownKeys);
replay(keyClient); replay(keyClient);
replay(uniqueIdSupplier); 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(client);
verify(knownKeys);
verify(keyClient); verify(keyClient);
verify(uniqueIdSupplier); verify(uniqueIdSupplier);
} }
@SuppressWarnings( { "unchecked" }) @SuppressWarnings({ "unchecked" })
@Test @Test
public void testApplyWithIllegalStateException() throws UnknownHostException { public void testApplyWithIllegalStateException() throws UnknownHostException {
EC2Client client = createMock(EC2Client.class); EC2Client client = createMock(EC2Client.class);
KeyPairClient keyClient = createMock(KeyPairClient.class); KeyPairClient keyClient = createMock(KeyPairClient.class);
Supplier<String> uniqueIdSupplier = createMock(Supplier.class); Supplier<String> uniqueIdSupplier = createMock(Supplier.class);
Map<RegionAndName, KeyPair> knownKeys = createMock(Map.class);
KeyPair pair = createMock(KeyPair.class); KeyPair pair = createMock(KeyPair.class);
expect(client.getKeyPairServices()).andReturn(keyClient).atLeastOnce(); expect(client.getKeyPairServices()).andReturn(keyClient).atLeastOnce();
expect(uniqueIdSupplier.get()).andReturn("1"); 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(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(client);
replay(knownKeys);
replay(keyClient); replay(keyClient);
replay(uniqueIdSupplier); 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(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<String> uniqueIdSupplier = createMock(Supplier.class);
Map<RegionAndName, KeyPair> 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<String> uniqueIdSupplier = createMock(Supplier.class);
Map<RegionAndName, KeyPair> 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(keyClient);
verify(uniqueIdSupplier); verify(uniqueIdSupplier);

View File

@ -26,13 +26,15 @@ import static org.easymock.classextension.EasyMock.verify;
import static org.jclouds.ec2.options.DescribeImagesOptions.Builder.imageIds; import static org.jclouds.ec2.options.DescribeImagesOptions.Builder.imageIds;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException;
import org.jclouds.compute.domain.Image;
import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.EC2Client;
import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.services.AMIClient; import org.jclouds.ec2.services.AMIClient;
import org.jclouds.compute.domain.Image;
import org.jclouds.rest.ResourceNotFoundException; import org.jclouds.rest.ResourceNotFoundException;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -46,46 +48,85 @@ public class RegionAndIdToImageTest {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Test @Test
public void testApply() { public void testApply() throws ExecutionException {
EC2ImageParser parser = createMock(EC2ImageParser.class); EC2ImageParser parser = createMock(EC2ImageParser.class);
EC2Client caller = createMock(EC2Client.class); EC2Client caller = createMock(EC2Client.class);
AMIClient client = createMock(AMIClient.class); AMIClient client = createMock(AMIClient.class);
Map<RegionAndName, Image> knownImages = createMock(Map.class);
org.jclouds.ec2.domain.Image ec2Image = createMock(org.jclouds.ec2.domain.Image.class); org.jclouds.ec2.domain.Image ec2Image = createMock(org.jclouds.ec2.domain.Image.class);
Image image = createNiceMock(Image.class); Image image = createNiceMock(Image.class);
Set<? extends org.jclouds.ec2.domain.Image> images = ImmutableSet.<org.jclouds.ec2.domain.Image> of(ec2Image); Set<? extends org.jclouds.ec2.domain.Image> images = ImmutableSet.<org.jclouds.ec2.domain.Image> of(ec2Image);
expect(knownImages.containsKey(new RegionAndName("region", "ami"))).andReturn(false);
expect(caller.getAMIServices()).andReturn(client).atLeastOnce(); expect(caller.getAMIServices()).andReturn(client).atLeastOnce();
expect(client.describeImagesInRegion("region", imageIds("ami"))).andReturn(Set.class.cast(images)); expect(client.describeImagesInRegion("region", imageIds("ami"))).andReturn(Set.class.cast(images));
expect(parser.apply(ec2Image)).andReturn(image); expect(parser.apply(ec2Image)).andReturn(image);
replay(knownImages);
replay(caller); replay(caller);
replay(image); replay(image);
replay(parser); replay(parser);
replay(client); 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(caller);
verify(image); verify(image);
verify(parser); verify(image);
verify(knownImages);
verify(client); verify(client);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Test @Test
public void testApplyNotFound() { public void testApplyWhenFoundDoesntCallClient() throws ExecutionException {
EC2ImageParser parser = createMock(EC2ImageParser.class); EC2ImageParser parser = createMock(EC2ImageParser.class);
EC2Client caller = createMock(EC2Client.class); EC2Client caller = createMock(EC2Client.class);
AMIClient client = createMock(AMIClient.class); AMIClient client = createMock(AMIClient.class);
Map<RegionAndName, Image> 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<RegionAndName, Image> knownImages = createMock(Map.class);
org.jclouds.ec2.domain.Image ec2Image = createMock(org.jclouds.ec2.domain.Image.class); org.jclouds.ec2.domain.Image ec2Image = createMock(org.jclouds.ec2.domain.Image.class);
Image image = createNiceMock(Image.class); Image image = createNiceMock(Image.class);
Set<? extends org.jclouds.ec2.domain.Image> images = ImmutableSet.<org.jclouds.ec2.domain.Image> of(ec2Image); Set<? extends org.jclouds.ec2.domain.Image> images = ImmutableSet.<org.jclouds.ec2.domain.Image> of(ec2Image);
expect(knownImages.containsKey(new RegionAndName("region", "ami"))).andReturn(false);
expect(caller.getAMIServices()).andReturn(client).atLeastOnce(); expect(caller.getAMIServices()).andReturn(client).atLeastOnce();
expect(client.describeImagesInRegion("region", imageIds("ami"))).andReturn(Set.class.cast(images)); expect(client.describeImagesInRegion("region", imageIds("ami"))).andReturn(Set.class.cast(images));
expect(parser.apply(ec2Image)).andThrow(new ResourceNotFoundException()); expect(parser.apply(ec2Image)).andThrow(new ResourceNotFoundException());
@ -93,45 +134,52 @@ public class RegionAndIdToImageTest {
replay(caller); replay(caller);
replay(image); replay(image);
replay(parser); replay(parser);
replay(knownImages);
replay(client); 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(caller);
verify(image); verify(image);
verify(parser); verify(parser);
verify(knownImages);
verify(client); verify(client);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Test @Test(expectedExceptions = ExecutionException.class)
public void testApplyNoSuchElementException() { public void testApplyNoSuchElementExceptionMakesExecutionException() throws ExecutionException {
EC2ImageParser parser = createMock(EC2ImageParser.class); EC2ImageParser parser = createMock(EC2ImageParser.class);
EC2Client caller = createMock(EC2Client.class); EC2Client caller = createMock(EC2Client.class);
AMIClient client = createMock(AMIClient.class); AMIClient client = createMock(AMIClient.class);
Map<RegionAndName, Image> knownImages = createMock(Map.class);
org.jclouds.ec2.domain.Image ec2Image = createMock(org.jclouds.ec2.domain.Image.class); org.jclouds.ec2.domain.Image ec2Image = createMock(org.jclouds.ec2.domain.Image.class);
Image image = createNiceMock(Image.class); Image image = createNiceMock(Image.class);
Set<? extends org.jclouds.ec2.domain.Image> images = ImmutableSet.<org.jclouds.ec2.domain.Image> of(ec2Image); Set<? extends org.jclouds.ec2.domain.Image> images = ImmutableSet.<org.jclouds.ec2.domain.Image> of(ec2Image);
expect(knownImages.containsKey(new RegionAndName("region", "ami"))).andReturn(false);
expect(caller.getAMIServices()).andReturn(client).atLeastOnce(); expect(caller.getAMIServices()).andReturn(client).atLeastOnce();
expect(client.describeImagesInRegion("region", imageIds("ami"))).andReturn(Set.class.cast(images)); expect(client.describeImagesInRegion("region", imageIds("ami"))).andReturn(Set.class.cast(images));
expect(parser.apply(ec2Image)).andThrow(new NoSuchElementException()); expect(parser.apply(ec2Image)).andThrow(new NoSuchElementException());
replay(caller); replay(caller);
replay(image); replay(image);
replay(knownImages);
replay(parser); replay(parser);
replay(client); 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(caller);
verify(image); verify(image);
verify(knownImages);
verify(parser); verify(parser);
verify(client); verify(client);

View File

@ -25,8 +25,6 @@ import java.net.UnknownHostException;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.NodeMetadata; 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.InstanceState;
import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.ec2.domain.RunningInstance;
import org.jclouds.ec2.xml.DescribeInstancesResponseHandlerTest; import org.jclouds.ec2.xml.DescribeInstancesResponseHandlerTest;
import org.jclouds.javax.annotation.Nullable;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Supplier; 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.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.MapMaker;
import com.google.common.collect.Maps; 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. // 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. // Create a null-returning function to simulate that the AMI can't be found.
Function<RegionAndName, Image> nullReturningFunction = new Function<RegionAndName, Image>() { CacheLoader<RegionAndName, Image> nullReturningFunction = new CacheLoader<RegionAndName, Image>() {
@Override @Override
public Image apply(@Nullable RegionAndName from) { public Image load(@Nullable RegionAndName from) {
return null; return null;
} }
}; };
Map<RegionAndName, Image> instanceToImage = new MapMaker().makeComputingMap(nullReturningFunction); Cache<RegionAndName, Image> instanceToImage = CacheBuilder.newBuilder().build(nullReturningFunction);
RunningInstanceToNodeMetadata parser = createNodeParser(ImmutableSet.of(m1_small32().build()), ImmutableSet RunningInstanceToNodeMetadata parser = createNodeParser(ImmutableSet.of(m1_small32().build()), ImmutableSet
.of(provider), ImmutableMap.<String, Credentials> of(), .of(provider), ImmutableMap.<String, Credentials> of(),
@ -197,25 +199,31 @@ public class RunningInstanceToNodeMetadataTest {
} }
protected RunningInstanceToNodeMetadata createNodeParser(final ImmutableSet<Hardware> hardware, protected RunningInstanceToNodeMetadata createNodeParser(final ImmutableSet<Hardware> hardware,
final ImmutableSet<Location> locations, Set<org.jclouds.compute.domain.Image> images, final ImmutableSet<Location> locations, final Set<org.jclouds.compute.domain.Image> images,
Map<String, Credentials> credentialStore) { Map<String, Credentials> credentialStore) {
Map<InstanceState, NodeState> instanceToNodeState = EC2ComputeServiceDependenciesModule.instanceToNodeState; Map<InstanceState, NodeState> instanceToNodeState = EC2ComputeServiceDependenciesModule.instanceToNodeState;
Map<RegionAndName, Image> instanceToImage = Maps.uniqueIndex(images, new Function<Image, RegionAndName>() { CacheLoader<RegionAndName, Image> getRealImage = new CacheLoader<RegionAndName, Image>() {
@Override @Override
public RegionAndName apply(Image from) { public Image load(@Nullable RegionAndName from) {
return new RegionAndName(from.getLocation().getId(), from.getProviderId()); return Maps.uniqueIndex(images, new Function<Image, RegionAndName>() {
@Override
public RegionAndName apply(Image from) {
return new RegionAndName(from.getLocation().getId(), from.getProviderId());
}
}).get(from);
} }
};
}); Cache<RegionAndName, Image> instanceToImage = CacheBuilder.newBuilder().build(getRealImage);
return createNodeParser(hardware, locations, credentialStore, instanceToNodeState, instanceToImage); return createNodeParser(hardware, locations, credentialStore, instanceToNodeState, instanceToImage);
} }
private RunningInstanceToNodeMetadata createNodeParser(final ImmutableSet<Hardware> hardware, private RunningInstanceToNodeMetadata createNodeParser(final ImmutableSet<Hardware> hardware,
final ImmutableSet<Location> locations, Map<String, Credentials> credentialStore, final ImmutableSet<Location> locations, Map<String, Credentials> credentialStore,
Map<InstanceState, NodeState> instanceToNodeState, Map<RegionAndName, Image> instanceToImage) { Map<InstanceState, NodeState> instanceToNodeState, Cache<RegionAndName, Image> instanceToImage) {
Supplier<Set<? extends Location>> locationSupplier = new Supplier<Set<? extends Location>>() { Supplier<Set<? extends Location>> locationSupplier = new Supplier<Set<? extends Location>>() {
@Override @Override
@ -233,7 +241,8 @@ public class RunningInstanceToNodeMetadataTest {
}; };
RunningInstanceToNodeMetadata parser = new RunningInstanceToNodeMetadata(instanceToNodeState, credentialStore, RunningInstanceToNodeMetadata parser = new RunningInstanceToNodeMetadata(instanceToNodeState, credentialStore,
instanceToImage, locationSupplier, hardwareSupplier); Suppliers.<Cache<RegionAndName, ? extends Image>> ofInstance(instanceToImage), locationSupplier,
hardwareSupplier);
return parser; return parser;
} }

View File

@ -27,7 +27,6 @@ import static org.testng.Assert.assertEquals;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import javax.inject.Provider; import javax.inject.Provider;
@ -46,11 +45,12 @@ import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.options.EC2TemplateOptions; import org.jclouds.ec2.compute.options.EC2TemplateOptions;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Function;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.base.Suppliers; 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.ImmutableSet;
import com.google.common.collect.MapMaker;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
/** /**
@ -67,22 +67,21 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest {
@Override @Override
protected EC2TemplateBuilderImpl createTemplateBuilder(final Image knownImage, protected EC2TemplateBuilderImpl createTemplateBuilder(final Image knownImage,
@Memoized Supplier<Set<? extends Location>> locations, @Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Location>> locations, @Memoized Supplier<Set<? extends Image>> images,
@Memoized Supplier<Set<? extends Hardware>> sizes, Location defaultLocation, @Memoized Supplier<Set<? extends Hardware>> sizes, Location defaultLocation,
Provider<TemplateOptions> optionsProvider, Provider<TemplateBuilder> templateBuilderProvider) { Provider<TemplateOptions> optionsProvider, Provider<TemplateBuilder> templateBuilderProvider) {
final RegionAndName knownRegionAndName = new RegionAndName("region", "ami"); final RegionAndName knownRegionAndName = new RegionAndName("region", "ami");
ConcurrentMap<RegionAndName, Image> imageMap = new MapMaker() Cache<RegionAndName, ? extends Image> imageMap = CacheBuilder.newBuilder().build(new CacheLoader<RegionAndName, Image>() {
.makeComputingMap(new Function<RegionAndName, Image>() { @Override
@Override public Image load(RegionAndName from) {
public Image apply(RegionAndName from) { return from.equals(knownRegionAndName) ? knownImage : null;
return from.equals(knownRegionAndName) ? knownImage : null; }
}
}); });
return new EC2TemplateBuilderImpl(locations, images, sizes, Suppliers.ofInstance(defaultLocation), return new EC2TemplateBuilderImpl(locations, images, sizes, Suppliers.ofInstance(defaultLocation),
optionsProvider, templateBuilderProvider, imageMap); optionsProvider, templateBuilderProvider, Suppliers.<Cache<RegionAndName, ? extends Image>>ofInstance(imageMap));
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -91,11 +90,11 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest {
Location location = new LocationBuilder().scope(LocationScope.REGION).id("region").description("region").build(); Location location = new LocationBuilder().scope(LocationScope.REGION).id("region").description("region").build();
Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
.<Location> of(location)); .<Location> of(location));
Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(Sets Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(Sets
.<Image> newLinkedHashSet()); .<Image> newLinkedHashSet());
Supplier<Set<? extends Hardware>> sizes = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet Supplier<Set<? extends Hardware>> sizes = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
.<Hardware> of(c1_medium().build())); .<Hardware> of(c1_medium().build()));
Provider<TemplateOptions> optionsProvider = createMock(Provider.class); Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class); Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
@ -127,7 +126,7 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest {
replay(templateBuilderProvider); replay(templateBuilderProvider);
TemplateBuilderImpl template = createTemplateBuilder(knownImage, locations, images, sizes, location, TemplateBuilderImpl template = createTemplateBuilder(knownImage, locations, images, sizes, location,
optionsProvider, templateBuilderProvider); optionsProvider, templateBuilderProvider);
assertEquals(template.imageId("region/ami").build().getImage(), knownImage); 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(); Location location = new LocationBuilder().scope(LocationScope.REGION).id("region").description("region").build();
Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
.<Location> of(location)); .<Location> of(location));
Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.<Image> of()); Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.<Image> of());
Supplier<Set<? extends Hardware>> sizes = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet Supplier<Set<? extends Hardware>> sizes = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
.<Hardware> of(c1_medium().build())); .<Hardware> of(c1_medium().build()));
Provider<TemplateOptions> optionsProvider = createMock(Provider.class); Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class); Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
@ -162,7 +161,7 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest {
replay(templateBuilderProvider); replay(templateBuilderProvider);
TemplateBuilderImpl template = createTemplateBuilder(knownImage, locations, images, sizes, location, TemplateBuilderImpl template = createTemplateBuilder(knownImage, locations, images, sizes, location,
optionsProvider, templateBuilderProvider); optionsProvider, templateBuilderProvider);
try { try {
template.imageId("ami").build(); template.imageId("ami").build();
assert false; assert false;
@ -181,10 +180,10 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest {
Location location = new LocationBuilder().scope(LocationScope.REGION).id("region").description("region").build(); Location location = new LocationBuilder().scope(LocationScope.REGION).id("region").description("region").build();
Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
.<Location> of(location)); .<Location> of(location));
Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.<Image> of()); Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.<Image> of());
Supplier<Set<? extends Hardware>> sizes = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet Supplier<Set<? extends Hardware>> sizes = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
.<Hardware> of(c1_medium().build())); .<Hardware> of(c1_medium().build()));
Location defaultLocation = createMock(Location.class); Location defaultLocation = createMock(Location.class);
Provider<TemplateOptions> optionsProvider = createMock(Provider.class); Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
@ -202,7 +201,7 @@ public class EC2TemplateBuilderImplTest extends TemplateBuilderImplTest {
replay(templateBuilderProvider); replay(templateBuilderProvider);
TemplateBuilderImpl template = createTemplateBuilder(knownImage, locations, images, sizes, defaultLocation, TemplateBuilderImpl template = createTemplateBuilder(knownImage, locations, images, sizes, defaultLocation,
optionsProvider, templateBuilderProvider); optionsProvider, templateBuilderProvider);
assertEquals(template.imageId("region/bad").build().getImage(), knownImage); assertEquals(template.imageId("region/bad").build().getImage(), knownImage);

View File

@ -27,6 +27,7 @@ import static org.testng.Assert.assertEquals;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException;
import javax.inject.Provider; 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.EC2HardwareBuilder;
import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; 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.compute.options.EC2TemplateOptions;
import org.jclouds.ec2.domain.BlockDeviceMapping; import org.jclouds.ec2.domain.BlockDeviceMapping;
import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.domain.KeyPair;
@ -48,14 +47,14 @@ import org.jclouds.encryption.internal.Base64;
import org.jclouds.scriptbuilder.domain.Statements; import org.jclouds.scriptbuilder.domain.Statements;
import org.testng.annotations.Test; 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.ImmutableMultimap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = "unit") @Test(groups = "unit", singleThreaded = true, testName = "CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest")
public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest { public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
private static final Provider<RunInstancesOptions> OPTIONS_PROVIDER = new javax.inject.Provider<RunInstancesOptions>() { private static final Provider<RunInstancesOptions> OPTIONS_PROVIDER = new javax.inject.Provider<RunInstancesOptions>() {
@ -209,7 +208,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
verifyStrategy(strategy); verifyStrategy(strategy);
} }
@Test(expectedExceptions = IllegalStateException.class) @Test(expectedExceptions = IllegalArgumentException.class)
public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_reusesKeyWhenToldToWithRunScriptButNoCredentials() { public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_reusesKeyWhenToldToWithRunScriptButNoCredentials() {
// setup constants // setup constants
String region = Region.AP_SOUTHEAST_1; String region = Region.AP_SOUTHEAST_1;
@ -225,7 +224,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.getKeyPair()).andReturn(userSuppliedKeyPair);
expect(options.getOverridingCredentials()).andReturn(null); expect(options.getOverridingCredentials()).andReturn(null);
expect(options.getRunScript()).andReturn(Statements.exec("echo foo")); 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 mocks
replay(options); replay(options);
@ -256,7 +256,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.getKeyPair()).andReturn(userSuppliedKeyPair);
expect(options.getOverridingCredentials()).andReturn(null); expect(options.getOverridingCredentials()).andReturn(null);
expect(options.getRunScript()).andReturn(Statements.exec("echo foo")); 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 mocks
replay(options); replay(options);
@ -287,11 +288,13 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.getKeyPair()).andReturn(userSuppliedKeyPair);
expect(options.getOverridingCredentials()).andReturn(new Credentials(null, "MyRsa")).atLeastOnce(); expect(options.getOverridingCredentials()).andReturn(new Credentials(null, "MyRsa")).atLeastOnce();
expect( expect(
strategy.credentialsMap.put(new RegionAndName(region, userSuppliedKeyPair), KeyPair.builder() strategy.knownKeys.put(
.region(region).keyName(userSuppliedKeyPair).keyFingerprint("//TODO").keyMaterial("MyRsa").build())) new RegionAndName(region, tag),
.andReturn(null); 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(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 mocks
replay(options); replay(options);
@ -307,7 +310,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
verifyStrategy(strategy); verifyStrategy(strategy);
} }
public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_createsNewKeyPairAndReturnsItsNameByDefault() { public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_createsNewKeyPairAndReturnsItsNameByDefault()
throws ExecutionException {
// setup constants // setup constants
String region = Region.AP_SOUTHEAST_1; String region = Region.AP_SOUTHEAST_1;
String tag = "tag"; String tag = "tag";
@ -323,11 +327,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
// setup expectations // setup expectations
expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.getKeyPair()).andReturn(userSuppliedKeyPair);
expect(options.shouldAutomaticallyCreateKeyPair()).andReturn(shouldAutomaticallyCreateKeyPair); 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(keyPair.getKeyName()).andReturn(systemGeneratedKeyPairName).atLeastOnce();
expect(strategy.credentialsMap.put(new RegionAndName(region, systemGeneratedKeyPairName), keyPair)).andReturn( expect(strategy.credentialsMap.getUnchecked(new RegionAndName(region, tag))).andReturn(keyPair);
null);
expect(options.getRunScript()).andReturn(null); expect(options.getRunScript()).andReturn(null);
// replay mocks // replay mocks
@ -377,7 +378,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
verifyStrategy(strategy); verifyStrategy(strategy);
} }
public void testGetSecurityGroupsForTagAndOptions_createsNewGroupByDefaultWhenNoPortsAreSpecifiedWhenDoesntExist() { public void testGetSecurityGroupsForTagAndOptions_createsNewGroupByDefaultWhenNoPortsAreSpecifiedWhenDoesntExist()
throws ExecutionException {
// setup constants // setup constants
String region = Region.AP_SOUTHEAST_1; String region = Region.AP_SOUTHEAST_1;
String tag = "tag"; String tag = "tag";
@ -385,7 +387,6 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
Set<String> groupIds = ImmutableSet.<String> of(); Set<String> groupIds = ImmutableSet.<String> of();
int[] ports = new int[] {}; int[] ports = new int[] {};
boolean shouldAuthorizeSelf = true; boolean shouldAuthorizeSelf = true;
boolean groupExisted = false;
Set<String> returnVal = ImmutableSet.<String> of(generatedMarkerGroup); Set<String> returnVal = ImmutableSet.<String> of(generatedMarkerGroup);
// create mocks // create mocks
@ -397,9 +398,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
expect(options.getInboundPorts()).andReturn(ports).atLeastOnce(); expect(options.getInboundPorts()).andReturn(ports).atLeastOnce();
RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup,
ports, shouldAuthorizeSelf); ports, shouldAuthorizeSelf);
expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(groupExisted); expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules)).andReturn(tag);
expect(strategy.createSecurityGroupIfNeeded.apply(regionNameAndIngressRules)).andReturn(generatedMarkerGroup);
expect(strategy.securityGroupMap.put(regionNameAndIngressRules, generatedMarkerGroup)).andReturn(null);
// replay mocks // replay mocks
replay(options); replay(options);
@ -413,7 +412,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
verifyStrategy(strategy); verifyStrategy(strategy);
} }
public void testGetSecurityGroupsForTagAndOptions_createsNewGroupByDefaultWhenPortsAreSpecifiedWhenDoesntExist() { public void testGetSecurityGroupsForTagAndOptions_createsNewGroupByDefaultWhenPortsAreSpecifiedWhenDoesntExist()
throws ExecutionException {
// setup constants // setup constants
String region = Region.AP_SOUTHEAST_1; String region = Region.AP_SOUTHEAST_1;
String tag = "tag"; String tag = "tag";
@ -421,7 +421,6 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
Set<String> groupIds = ImmutableSet.<String> of(); Set<String> groupIds = ImmutableSet.<String> of();
int[] ports = new int[] { 22, 80 }; int[] ports = new int[] { 22, 80 };
boolean shouldAuthorizeSelf = true; boolean shouldAuthorizeSelf = true;
boolean groupExisted = false;
Set<String> returnVal = ImmutableSet.<String> of(generatedMarkerGroup); Set<String> returnVal = ImmutableSet.<String> of(generatedMarkerGroup);
// create mocks // create mocks
@ -433,9 +432,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
expect(options.getInboundPorts()).andReturn(ports).atLeastOnce(); expect(options.getInboundPorts()).andReturn(ports).atLeastOnce();
RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup,
ports, shouldAuthorizeSelf); ports, shouldAuthorizeSelf);
expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(groupExisted); expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules)).andReturn(generatedMarkerGroup);
expect(strategy.createSecurityGroupIfNeeded.apply(regionNameAndIngressRules)).andReturn(generatedMarkerGroup);
expect(strategy.securityGroupMap.put(regionNameAndIngressRules, generatedMarkerGroup)).andReturn(null);
// replay mocks // replay mocks
replay(options); replay(options);
@ -449,7 +446,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
verifyStrategy(strategy); verifyStrategy(strategy);
} }
public void testGetSecurityGroupsForTagAndOptions_reusesGroupByDefaultWhenNoPortsAreSpecifiedWhenDoesExist() { public void testGetSecurityGroupsForTagAndOptions_reusesGroupByDefaultWhenNoPortsAreSpecifiedWhenDoesExist()
throws ExecutionException {
// setup constants // setup constants
String region = Region.AP_SOUTHEAST_1; String region = Region.AP_SOUTHEAST_1;
String tag = "tag"; String tag = "tag";
@ -457,7 +455,6 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
Set<String> groupIds = ImmutableSet.<String> of(); Set<String> groupIds = ImmutableSet.<String> of();
int[] ports = new int[] {}; int[] ports = new int[] {};
boolean shouldAuthorizeSelf = true; boolean shouldAuthorizeSelf = true;
boolean groupExisted = true;
Set<String> returnVal = ImmutableSet.<String> of(generatedMarkerGroup); Set<String> returnVal = ImmutableSet.<String> of(generatedMarkerGroup);
// create mocks // create mocks
@ -469,7 +466,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
expect(options.getInboundPorts()).andReturn(ports).atLeastOnce(); expect(options.getInboundPorts()).andReturn(ports).atLeastOnce();
RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup,
ports, shouldAuthorizeSelf); ports, shouldAuthorizeSelf);
expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(groupExisted); expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules)).andReturn(generatedMarkerGroup);
// replay mocks // replay mocks
replay(options); replay(options);
@ -501,13 +498,9 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
// setup expectations // setup expectations
expect(options.getGroups()).andReturn(groupIds).atLeastOnce(); expect(options.getGroups()).andReturn(groupIds).atLeastOnce();
RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup,
ports, shouldAuthorizeSelf); // note ports, shouldAuthorizeSelf);
// this
// works expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules)).andReturn(groupExisted ? "tag" : null);
// since
// there's
// no equals on portsq
expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(groupExisted);
// replay mocks // replay mocks
replay(options); replay(options);
@ -522,28 +515,24 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
} }
private void verifyStrategy(CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions strategy) { private void verifyStrategy(CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions strategy) {
verify(strategy.knownKeys);
verify(strategy.credentialsMap); verify(strategy.credentialsMap);
verify(strategy.securityGroupMap); verify(strategy.securityGroupMap);
verify(strategy.createUniqueKeyPair);
verify(strategy.createSecurityGroupIfNeeded);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions setupStrategy() { private CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions setupStrategy() {
Map<RegionAndName, KeyPair> credentialsMap = createMock(Map.class); Map<RegionAndName, KeyPair> knownKeys = createMock(Map.class);
Map<RegionAndName, String> securityGroupMap = createMock(Map.class); Cache<RegionAndName, KeyPair> credentialsMap = createMock(Cache.class);
CreateUniqueKeyPair createUniqueKeyPair = createMock(CreateUniqueKeyPair.class); Cache<RegionAndName, String> securityGroupMap = createMock(Cache.class);
Function<RegionNameAndIngressRules, String> createSecurityGroupIfNeeded = createMock(CreateSecurityGroupIfNeeded.class); return new CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions(knownKeys, credentialsMap, securityGroupMap,
OPTIONS_PROVIDER);
return new CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions(credentialsMap, securityGroupMap,
createUniqueKeyPair, createSecurityGroupIfNeeded, OPTIONS_PROVIDER);
} }
private void replayStrategy(CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions strategy) { private void replayStrategy(CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions strategy) {
replay(strategy.knownKeys);
replay(strategy.credentialsMap); replay(strategy.credentialsMap);
replay(strategy.securityGroupMap); replay(strategy.securityGroupMap);
replay(strategy.createUniqueKeyPair);
replay(strategy.createSecurityGroupIfNeeded);
} }
} }

View File

@ -51,7 +51,7 @@ import org.jclouds.ec2.options.RunInstancesOptions;
import org.jclouds.ec2.services.InstanceClient; import org.jclouds.ec2.services.InstanceClient;
import org.testng.annotations.Test; 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.ImmutableSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
@ -222,7 +222,7 @@ public class EC2CreateNodesInGroupThenAddToSetTest {
CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize = createMock(CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class); CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize = createMock(CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.class);
InstancePresent instancePresent = createMock(InstancePresent.class); InstancePresent instancePresent = createMock(InstancePresent.class);
RunningInstanceToNodeMetadata runningInstanceToNodeMetadata = createMock(RunningInstanceToNodeMetadata.class); RunningInstanceToNodeMetadata runningInstanceToNodeMetadata = createMock(RunningInstanceToNodeMetadata.class);
Function<RunningInstance, Credentials> instanceToCredentials = createMock(Function.class); Cache<RunningInstance, Credentials> instanceToCredentials = createMock(Cache.class);
Map<String, Credentials> credentialStore = createMock(Map.class); Map<String, Credentials> credentialStore = createMock(Map.class);
ComputeUtils utils = createMock(ComputeUtils.class); ComputeUtils utils = createMock(ComputeUtils.class);
return new EC2CreateNodesInGroupThenAddToSet(client, Providers.<TemplateBuilder> of(template), return new EC2CreateNodesInGroupThenAddToSet(client, Providers.<TemplateBuilder> of(template),

View File

@ -25,13 +25,17 @@ import java.net.URI;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import javax.inject.Singleton;
import org.jclouds.aws.domain.Region; import org.jclouds.aws.domain.Region;
import org.jclouds.aws.filters.FormSigner; import org.jclouds.aws.filters.FormSigner;
import org.jclouds.compute.domain.Image;
import org.jclouds.date.DateService; import org.jclouds.date.DateService;
import org.jclouds.ec2.EC2AsyncClient; import org.jclouds.ec2.EC2AsyncClient;
import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.EC2Client;
import org.jclouds.ec2.EC2ContextBuilder; import org.jclouds.ec2.EC2ContextBuilder;
import org.jclouds.ec2.EC2PropertiesBuilder; import org.jclouds.ec2.EC2PropertiesBuilder;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.config.EC2RestClientModule; import org.jclouds.ec2.config.EC2RestClientModule;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.http.RequiresHttp; import org.jclouds.http.RequiresHttp;
@ -42,8 +46,12 @@ import org.jclouds.rest.RestContextSpec;
import org.testng.annotations.BeforeTest; import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test; 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.common.collect.ImmutableMap;
import com.google.inject.Module; import com.google.inject.Module;
import com.google.inject.Provides;
/** /**
* @author Adrian Cole * @author Adrian Cole
@ -58,6 +66,19 @@ public abstract class BaseEC2AsyncClientTest<T> extends RestClientTest<T> {
super(EC2Client.class, EC2AsyncClient.class, DELEGATE_MAP); super(EC2Client.class, EC2AsyncClient.class, DELEGATE_MAP);
} }
@Provides
@Singleton
Cache<RegionAndName, Image> provide(){
return CacheBuilder.newBuilder().build(new CacheLoader<RegionAndName, Image>() {
@Override
public Image load(RegionAndName key) throws Exception {
return null;
}
});
}
@Override @Override
protected String provideTimeStamp(DateService dateService, int expiration) { protected String provideTimeStamp(DateService dateService, int expiration) {
return "2009-11-08T15:54:08.897Z"; return "2009-11-08T15:54:08.897Z";

View File

@ -46,7 +46,6 @@ import org.jclouds.compute.domain.internal.VolumeImpl;
import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.domain.Location; import org.jclouds.domain.Location;
import org.jclouds.elasticstack.ElasticStackAsyncClient;
import org.jclouds.elasticstack.ElasticStackClient; import org.jclouds.elasticstack.ElasticStackClient;
import org.jclouds.elasticstack.domain.Device; import org.jclouds.elasticstack.domain.Device;
import org.jclouds.elasticstack.domain.Drive; 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.Function;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder; 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 * defines the connection between the {@link ElasticStackClient} implementation
* {@link ComputeService} * and the jclouds {@link ComputeService}
* *
*/ */
@Singleton @Singleton
public class ElasticStackComputeServiceAdapter implements public class ElasticStackComputeServiceAdapter implements
ComputeServiceAdapter<ServerInfo, Hardware, DriveInfo, Location> { ComputeServiceAdapter<ServerInfo, Hardware, DriveInfo, Location> {
private final ElasticStackClient client; private final ElasticStackClient client;
private final ElasticStackAsyncClient aclient;
private final Predicate<DriveInfo> driveNotClaimed; private final Predicate<DriveInfo> driveNotClaimed;
private final Map<String, WellKnownImage> preinstalledImages; private final Map<String, WellKnownImage> preinstalledImages;
private final Map<String, DriveInfo> cache; private final Cache<String, DriveInfo> cache;
private final JustProvider locationSupplier; private final JustProvider locationSupplier;
private final String defaultVncPassword; private final String defaultVncPassword;
private final ExecutorService executor; private final ExecutorService executor;
@ -88,13 +89,11 @@ public class ElasticStackComputeServiceAdapter implements
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
@Inject @Inject
public ElasticStackComputeServiceAdapter(ElasticStackClient client, ElasticStackAsyncClient aclient, public ElasticStackComputeServiceAdapter(ElasticStackClient client, Predicate<DriveInfo> driveNotClaimed,
Predicate<DriveInfo> driveNotClaimed, JustProvider locationSupplier, JustProvider locationSupplier, Map<String, WellKnownImage> preinstalledImages, Cache<String, DriveInfo> cache,
Map<String, WellKnownImage> preinstalledImages, Map<String, DriveInfo> cache, @Named(ElasticStackConstants.PROPERTY_VNC_PASSWORD) String defaultVncPassword,
@Named(ElasticStackConstants.PROPERTY_VNC_PASSWORD) String defaultVncPassword, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) {
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) {
this.client = checkNotNull(client, "client"); this.client = checkNotNull(client, "client");
this.aclient = checkNotNull(aclient, "aclient");
this.driveNotClaimed = checkNotNull(driveNotClaimed, "driveNotClaimed"); this.driveNotClaimed = checkNotNull(driveNotClaimed, "driveNotClaimed");
this.locationSupplier = checkNotNull(locationSupplier, "locationSupplier"); this.locationSupplier = checkNotNull(locationSupplier, "locationSupplier");
this.preinstalledImages = checkNotNull(preinstalledImages, "preinstalledImages"); this.preinstalledImages = checkNotNull(preinstalledImages, "preinstalledImages");
@ -105,12 +104,12 @@ public class ElasticStackComputeServiceAdapter implements
@Override @Override
public ServerInfo createNodeWithGroupEncodedIntoNameThenStoreCredentials(String tag, String name, Template template, public ServerInfo createNodeWithGroupEncodedIntoNameThenStoreCredentials(String tag, String name, Template template,
Map<String, Credentials> credentialStore) { Map<String, Credentials> credentialStore) {
long bootSize = (long) (template.getHardware().getVolumes().get(0).getSize() * 1024 * 1024 * 1024l); long bootSize = (long) (template.getHardware().getVolumes().get(0).getSize() * 1024 * 1024 * 1024l);
logger.debug(">> creating boot drive bytes(%d)", bootSize); logger.debug(">> creating boot drive bytes(%d)", bootSize);
DriveInfo drive = client.createDrive(new Drive.Builder().name(template.getImage().getId()).size(bootSize) DriveInfo drive = client
.build()); .createDrive(new Drive.Builder().name(template.getImage().getId()).size(bootSize).build());
logger.debug("<< drive(%s)", drive.getUuid()); logger.debug("<< drive(%s)", drive.getUuid());
logger.debug(">> imaging boot drive source(%s)", template.getImage().getId()); logger.debug(">> imaging boot drive source(%s)", template.getImage().getId());
@ -121,17 +120,16 @@ public class ElasticStackComputeServiceAdapter implements
client.destroyDrive(drive.getUuid()); client.destroyDrive(drive.getUuid());
throw new IllegalStateException("could not image drive in time!"); 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( Server toCreate = small(name, drive.getUuid(), defaultVncPassword).mem(template.getHardware().getRam())
(int) (template.getHardware().getProcessors().get(0).getSpeed())).build(); .cpu((int) (template.getHardware().getProcessors().get(0).getSpeed())).build();
ServerInfo from = client.createServer(toCreate); ServerInfo from = client.createServer(toCreate);
client.startServer(from.getUuid()); client.startServer(from.getUuid());
from = client.getServerInfo(from.getUuid()); from = client.getServerInfo(from.getUuid());
// store the credentials so that later functions can use them // store the credentials so that later functions can use them
credentialStore.put("node#"+ from.getUuid(), new Credentials(template.getImage().getDefaultCredentials().identity, credentialStore.put("node#" + from.getUuid(), new Credentials(
from.getVnc().getPassword())); template.getImage().getDefaultCredentials().identity, from.getVnc().getPassword()));
return from; return from;
} }
@ -155,30 +153,40 @@ public class ElasticStackComputeServiceAdapter implements
return "sizeLessThanOrEqual(" + size + ")"; return "sizeLessThanOrEqual(" + size + ")";
} }
}).ids(id).ram(ram).processors(ImmutableList.of(new Processor(1, cpu))).volumes( }).ids(id).ram(ram).processors(ImmutableList.of(new Processor(1, cpu)))
ImmutableList.<Volume> of(new VolumeImpl(size, true, true))).build()); .volumes(ImmutableList.<Volume> of(new VolumeImpl(size, true, true))).build());
} }
return hardware.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 @Override
public Iterable<DriveInfo> listImages() { public Iterable<DriveInfo> listImages() {
Iterable<DriveInfo> drives = transformParallel(preinstalledImages.keySet(), Iterable<DriveInfo> drives = transformParallel(preinstalledImages.keySet(),
new Function<String, Future<DriveInfo>>() { new Function<String, Future<DriveInfo>>() {
@Override @Override
public Future<DriveInfo> apply(String input) { public Future<DriveInfo> apply(String input) {
return aclient.getDriveInfo(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"); @Override
Iterable<DriveInfo> returnVal = filter(drives, notNull()); public String toString() {
for (DriveInfo drive : returnVal) return "seedDriveCache()";
cache.put(drive.getUuid(), drive); }
return returnVal;
}, executor, null, logger, "drives");
return filter(drives, notNull());
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View File

@ -38,10 +38,10 @@ import org.jclouds.elasticstack.ElasticStackAsyncClient;
import org.jclouds.elasticstack.ElasticStackClient; import org.jclouds.elasticstack.ElasticStackClient;
import org.jclouds.elasticstack.compute.ElasticStackComputeServiceAdapter; import org.jclouds.elasticstack.compute.ElasticStackComputeServiceAdapter;
import org.jclouds.elasticstack.compute.functions.ServerInfoToNodeMetadata; 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.DeviceToVolume;
import org.jclouds.elasticstack.compute.functions.ServerInfoToNodeMetadata.FindImageForId; import org.jclouds.elasticstack.compute.functions.ServerInfoToNodeMetadata.FindImageForId;
import org.jclouds.elasticstack.compute.functions.ServerInfoToNodeMetadata.GetImageIdFromServer; 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.Device;
import org.jclouds.elasticstack.domain.DriveInfo; import org.jclouds.elasticstack.domain.DriveInfo;
import org.jclouds.elasticstack.domain.Server; 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.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.base.Supplier; 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.common.collect.Maps;
import com.google.inject.Provides; import com.google.inject.Provides;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
@ -104,12 +106,12 @@ public class ElasticStackComputeServiceContextModule
@Provides @Provides
@Singleton @Singleton
protected Map<String, DriveInfo> cache(GetDrive getDrive) { protected Cache<String, DriveInfo> cache(GetDrive getDrive) {
return new MapMaker().makeComputingMap(getDrive); return CacheBuilder.newBuilder().build(getDrive);
} }
@Singleton @Singleton
public static class GetDrive implements Function<String, DriveInfo> { public static class GetDrive extends CacheLoader<String, DriveInfo> {
private final ElasticStackClient client; private final ElasticStackClient client;
@Inject @Inject
@ -118,7 +120,7 @@ public class ElasticStackComputeServiceContextModule
} }
@Override @Override
public DriveInfo apply(String input) { public DriveInfo load(String input) {
return client.getDriveInfo(input); return client.getDriveInfo(input);
} }
} }

View File

@ -18,27 +18,44 @@
*/ */
package org.jclouds.elasticstack.compute.functions; package org.jclouds.elasticstack.compute.functions;
import com.google.common.base.Function; import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.Supplier; import static org.jclouds.compute.util.ComputeServiceUtils.parseGroupFromName;
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 javax.inject.Inject;
import javax.inject.Singleton;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull; import javax.annotation.Resource;
import static org.jclouds.compute.util.ComputeServiceUtils.parseGroupFromName; 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 * @author Adrian Cole
@ -93,16 +110,19 @@ public class ServerInfoToNodeMetadata implements Function<ServerInfo, NodeMetada
builder.state(serverStatusToNodeState.get(from.getStatus())); builder.state(serverStatusToNodeState.get(from.getStatus()));
builder.publicAddresses(ImmutableSet.<String> of(from.getNics().get(0).getDhcp())); builder.publicAddresses(ImmutableSet.<String> of(from.getNics().get(0).getDhcp()));
builder.privateAddresses(ImmutableSet.<String> of()); builder.privateAddresses(ImmutableSet.<String> of());
builder.credentials(credentialStore.get("node#"+ from.getUuid())); builder.credentials(credentialStore.get("node#" + from.getUuid()));
return builder.build(); return builder.build();
} }
@Singleton @Singleton
public static final class DeviceToVolume implements Function<Device, Volume> { public static final class DeviceToVolume implements Function<Device, Volume> {
private final Map<String, DriveInfo> cache; @Resource
protected Logger logger = Logger.NULL;
private final Cache<String, DriveInfo> cache;
@Inject @Inject
public DeviceToVolume(Map<String, DriveInfo> cache) { public DeviceToVolume(Cache<String, DriveInfo> cache) {
this.cache = checkNotNull(cache, "cache"); this.cache = checkNotNull(cache, "cache");
} }
@ -110,27 +130,34 @@ public class ServerInfoToNodeMetadata implements Function<ServerInfo, NodeMetada
public Volume apply(Device input) { public Volume apply(Device input) {
VolumeBuilder builder = new VolumeBuilder(); VolumeBuilder builder = new VolumeBuilder();
builder.id(input.getId()); builder.id(input.getId());
DriveInfo drive = cache.get(input.getDriveUuid()); try {
if (drive != null) { DriveInfo drive = cache.getUnchecked(input.getDriveUuid());
builder.size(drive.getSize() / 1024 / 1024f); builder.size(drive.getSize() / 1024 / 1024f);
} catch (NullPointerException e) {
logger.debug("drive %s not found", input.getDriveUuid());
} catch (UncheckedExecutionException e) {
logger.warn(e, "error finding drive %s: %s", input.getDriveUuid(), e.getMessage());
} }
return new VolumeBuilder().durable(true).type(Volume.Type.NAS).build(); return new VolumeBuilder().durable(true).type(Volume.Type.NAS).build();
} }
} }
/** /**
* When we create the boot drive of the server, by convention we set the name to the image it * When we create the boot drive of the server, by convention we set the name
* came from. * to the image it came from.
* *
* @author Adrian Cole * @author Adrian Cole
* *
*/ */
@Singleton @Singleton
public static class GetImageIdFromServer implements Function<Server, String> { public static class GetImageIdFromServer implements Function<Server, String> {
private final Map<String, DriveInfo> cache; @Resource
protected Logger logger = Logger.NULL;
private final Cache<String, DriveInfo> cache;
@Inject @Inject
public GetImageIdFromServer(Map<String, DriveInfo> cache) { public GetImageIdFromServer(Cache<String, DriveInfo> cache) {
this.cache = cache; this.cache = cache;
} }
@ -141,9 +168,12 @@ public class ServerInfoToNodeMetadata implements Function<ServerInfo, NodeMetada
Device bootDevice = from.getDevices().get(bootDeviceId); Device bootDevice = from.getDevices().get(bootDeviceId);
if (bootDevice != null) { if (bootDevice != null) {
try { try {
imageId = cache.get(bootDevice.getDriveUuid()).getName(); DriveInfo drive = cache.getUnchecked(bootDevice.getDriveUuid());
imageId = drive.getName();
} catch (NullPointerException e) { } catch (NullPointerException e) {
logger.debug("drive %s not found", bootDevice.getDriveUuid());
} catch (UncheckedExecutionException e) {
logger.warn(e, "error finding drive %s: %s", bootDevice.getDriveUuid(), e.getMessage());
} }
} }
return imageId; return imageId;

View File

@ -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 static org.jclouds.trmk.vcloud_0_8.reference.TerremarkConstants.PROPERTY_TERREMARK_EXTENSION_NS;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ExecutionException;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@ -33,6 +34,7 @@ import org.jclouds.rest.binders.BindToStringPayload;
import org.jclouds.util.Patterns; import org.jclouds.util.Patterns;
import org.jclouds.util.Strings2; import org.jclouds.util.Strings2;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
/** /**
@ -61,9 +63,13 @@ public class BindAddInternetServiceToXmlPayload implements MapBinder {
String description = postParams.get("description"); String description = postParams.get("description");
String payload = Strings2.replaceTokens(xmlTemplate, String payload = Strings2.replaceTokens(xmlTemplate,
ImmutableMap.of("name", name, "protocol", protocol, "port", port, "enabled", enabled, "ns", ns)); ImmutableMap.of("name", name, "protocol", protocol, "port", port, "enabled", enabled, "ns", ns));
payload = Strings2.replaceAll(payload, Patterns.TOKEN_TO_PATTERN.get("description"), description == null ? "" try {
: String.format("\n\t<Description>%s</Description>", description)); payload = Strings2.replaceAll(payload, Patterns.TOKEN_TO_PATTERN.get("description"), description == null ? ""
payload = Strings2.replaceAll(payload, Patterns.TOKEN_TO_PATTERN.get("monitor"), getMonitorString(postParams)); : String.format("\n\t<Description>%s</Description>", description));
payload = Strings2.replaceAll(payload, Patterns.TOKEN_TO_PATTERN.get("monitor"), getMonitorString(postParams));
} catch (ExecutionException e) {
Throwables.propagate(e);
}
return stringBinder.bindToRequest(request, payload); return stringBinder.bindToRequest(request, payload);
} }

View File

@ -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 static org.jclouds.trmk.vcloud_0_8.reference.TerremarkConstants.PROPERTY_TERREMARK_EXTENSION_NS;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ExecutionException;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@ -33,6 +34,7 @@ import org.jclouds.rest.binders.BindToStringPayload;
import org.jclouds.util.Patterns; import org.jclouds.util.Patterns;
import org.jclouds.util.Strings2; import org.jclouds.util.Strings2;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
/** /**
@ -62,8 +64,12 @@ public class BindAddNodeServiceToXmlPayload implements MapBinder {
String payload = Strings2.replaceTokens(xmlTemplate, String payload = Strings2.replaceTokens(xmlTemplate,
ImmutableMap.of("name", name, "ipAddress", ipAddress, "port", port, "enabled", enabled, "ns", ns)); ImmutableMap.of("name", name, "ipAddress", ipAddress, "port", port, "enabled", enabled, "ns", ns));
payload = Strings2.replaceAll(payload, Patterns.TOKEN_TO_PATTERN.get("description"), description == null ? "" try {
: String.format("\n <Description>%s</Description>", description)); payload = Strings2.replaceAll(payload, Patterns.TOKEN_TO_PATTERN.get("description"), description == null ? ""
: String.format("\n <Description>%s</Description>", description));
} catch (ExecutionException e) {
Throwables.propagate(e);
}
return stringBinder.bindToRequest(request, payload); return stringBinder.bindToRequest(request, payload);
} }

View File

@ -57,7 +57,7 @@ public interface ComputeServiceConstants {
public static class ReferenceData { public static class ReferenceData {
@Inject(optional = true) @Inject(optional = true)
@Named(PROPERTY_OS_VERSION_MAP_JSON) @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 @Singleton

View File

@ -109,7 +109,7 @@
<dependency> <dependency>
<groupId>com.google.guava</groupId> <groupId>com.google.guava</groupId>
<artifactId>guava</artifactId> <artifactId>guava</artifactId>
<version>r09</version> <version>10.0-rc2</version>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -69,7 +69,7 @@ public class Futures {
// ExecutionException / CancellationException / RuntimeException // ExecutionException / CancellationException / RuntimeException
// The task is done, run the listeners. // The task is done, run the listeners.
} }
executionList.run(); executionList.execute();
} }
@Override @Override
@ -116,7 +116,7 @@ public class Futures {
if (delegate.isDone()) { if (delegate.isDone()) {
// If the delegate is already done, run the execution list // If the delegate is already done, run the execution list
// immediately on the current thread. // immediately on the current thread.
executionList.run(); executionList.execute();
return; return;
} }
adapterExecutor.execute(new CallGetAndRunExecutionList<T>(delegate, executionList)); adapterExecutor.execute(new CallGetAndRunExecutionList<T>(delegate, executionList));

View File

@ -26,7 +26,6 @@ import java.lang.reflect.Proxy;
import java.util.Arrays; import java.util.Arrays;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -38,6 +37,7 @@ import org.jclouds.internal.ClassMethodArgs;
import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.Delegate;
import org.jclouds.util.Throwables2; import org.jclouds.util.Throwables2;
import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
@ -62,13 +62,13 @@ public class SyncProxy implements InvocationHandler {
private final Map<Method, Method> methodMap; private final Map<Method, Method> methodMap;
private final Map<Method, Method> syncMethodMap; private final Map<Method, Method> syncMethodMap;
private final Map<Method, Long> timeoutMap; private final Map<Method, Long> timeoutMap;
private final ConcurrentMap<ClassMethodArgs, Object> delegateMap; private final Cache<ClassMethodArgs, Object> delegateMap;
private final Map<Class<?>, Class<?>> sync2Async; private final Map<Class<?>, Class<?>> sync2Async;
private static final Set<Method> objectMethods = ImmutableSet.of(Object.class.getMethods()); private static final Set<Method> objectMethods = ImmutableSet.of(Object.class.getMethods());
@Inject @Inject
public SyncProxy(Class<?> declaring, Object async, public SyncProxy(Class<?> declaring, Object async,
@Named("sync") ConcurrentMap<ClassMethodArgs, Object> delegateMap, Map<Class<?>, Class<?>> sync2Async) @Named("sync") Cache<ClassMethodArgs, Object> delegateMap, Map<Class<?>, Class<?>> sync2Async)
throws SecurityException, NoSuchMethodException { throws SecurityException, NoSuchMethodException {
this.delegateMap = delegateMap; this.delegateMap = delegateMap;
this.delegate = async; this.delegate = async;

View File

@ -20,10 +20,11 @@ package org.jclouds.json.internal;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Map; import java.util.concurrent.ExecutionException;
import com.google.common.base.Function; import com.google.common.cache.Cache;
import com.google.common.collect.MapMaker; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer; import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
@ -46,29 +47,26 @@ public class EnumTypeAdapterThatReturnsFromValue<T extends Enum<T>> implements J
try { try {
return (T) Enum.valueOf((Class<T>) classOfT, json.getAsString()); return (T) Enum.valueOf((Class<T>) classOfT, json.getAsString());
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
Method converter = classToConvert.get(classOfT); try {
if (converter != null) Method converter = classToConvert.get((Class<?>) classOfT);
try { return (T) converter.invoke(null, json.getAsString());
return (T) converter.invoke(null, json.getAsString()); } catch (Exception e1) {
} catch (Exception e1) {
throw e;
}
else
throw e; throw e;
}
} }
} }
private final static Map<Class<?>, Method> classToConvert = new MapMaker() private final static Cache<Class<?>, Method> classToConvert = CacheBuilder.newBuilder()
.makeComputingMap(new Function<Class<?>, Method>() { .build(new CacheLoader<Class<?>, Method>() {
@Override @Override
public Method apply(Class<?> from) { public Method load(Class<?> from) throws ExecutionException {
try { try {
Method method = from.getMethod("fromValue", String.class); Method method = from.getMethod("fromValue", String.class);
method.setAccessible(true); method.setAccessible(true);
return method; return method;
} catch (Exception e) { } catch (Exception e) {
return null; throw new ExecutionException(e);
} }
} }

View File

@ -20,8 +20,8 @@ package org.jclouds.rest.binders;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.net.InternetDomainName.fromLenient; import static com.google.common.net.InternetDomainName.from;
import static com.google.common.net.InternetDomainName.isValidLenient; import static com.google.common.net.InternetDomainName.isValid;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Provider; import javax.inject.Provider;
@ -51,9 +51,9 @@ public class BindAsHostPrefix implements Binder {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <R extends HttpRequest> R bindToRequest(R request, Object payload) { public <R extends HttpRequest> R bindToRequest(R request, Object payload) {
checkNotNull(payload, "hostprefix"); 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()); 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()); builder.host(name.name());
return (R) request.toBuilder().endpoint(builder.build()).build(); return (R) request.toBuilder().endpoint(builder.build()).build();
} }

View File

@ -19,7 +19,6 @@
package org.jclouds.rest.config; package org.jclouds.rest.config;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
@ -28,6 +27,7 @@ import org.jclouds.concurrent.internal.SyncProxy;
import org.jclouds.internal.ClassMethodArgs; import org.jclouds.internal.ClassMethodArgs;
import com.google.common.base.Throwables; import com.google.common.base.Throwables;
import com.google.common.cache.Cache;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Key; import com.google.inject.Key;
import com.google.inject.Provider; import com.google.inject.Provider;
@ -58,8 +58,8 @@ public class ClientProvider<S, A> implements Provider<S> {
@Singleton @Singleton
public S get() { public S get() {
A client = (A) injector.getInstance(Key.get(asyncClientType)); A client = (A) injector.getInstance(Key.get(asyncClientType));
ConcurrentMap<ClassMethodArgs, Object> delegateMap = injector.getInstance(Key.get( Cache<ClassMethodArgs, Object> delegateMap = injector.getInstance(Key.get(
new TypeLiteral<ConcurrentMap<ClassMethodArgs, Object>>() { new TypeLiteral<Cache<ClassMethodArgs, Object>>() {
}, Names.named("sync"))); }, Names.named("sync")));
try { try {
return (S) SyncProxy.proxy(syncClientType, new SyncProxy(syncClientType, client, return (S) SyncProxy.proxy(syncClientType, new SyncProxy(syncClientType, client,

View File

@ -21,35 +21,37 @@ package org.jclouds.rest.config;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Provider;
import org.jclouds.concurrent.internal.SyncProxy; import org.jclouds.concurrent.internal.SyncProxy;
import org.jclouds.internal.ClassMethodArgs; import org.jclouds.internal.ClassMethodArgs;
import com.google.common.base.Function;
import com.google.common.base.Throwables; 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 * @author Adrian Cole
*/ */
public class CreateClientForCaller implements Function<ClassMethodArgs, Object> { public class CreateClientForCaller extends CacheLoader<ClassMethodArgs, Object> {
private final ConcurrentMap<ClassMethodArgs, Object> asyncMap; private final Cache<ClassMethodArgs, Object> asyncMap;
private final Provider<ConcurrentMap<ClassMethodArgs, Object>> delegateMap; private final Provider<Cache<ClassMethodArgs, Object>> delegateMap;
Map<Class<?>, Class<?>> sync2Async; Map<Class<?>, Class<?>> sync2Async;
@Inject @Inject
CreateClientForCaller(@Named("async") ConcurrentMap<ClassMethodArgs, Object> asyncMap, CreateClientForCaller(@Named("async") Cache<ClassMethodArgs, Object> asyncMap,
@Named("sync") Provider<ConcurrentMap<ClassMethodArgs, Object>> delegateMap) { @Named("sync") Provider<Cache<ClassMethodArgs, Object>> delegateMap) {
this.asyncMap = asyncMap; this.asyncMap = asyncMap;
this.delegateMap = delegateMap; this.delegateMap = delegateMap;
} }
public Object apply(ClassMethodArgs from) { @Override
public Object load(ClassMethodArgs from) throws ExecutionException {
Class<?> syncClass = from.getMethod().getReturnType(); Class<?> syncClass = from.getMethod().getReturnType();
Class<?> asyncClass = sync2Async.get(syncClass); Class<?> asyncClass = sync2Async.get(syncClass);
checkState(asyncClass != null, "configuration error, sync class " + syncClass checkState(asyncClass != null, "configuration error, sync class " + syncClass

View File

@ -19,7 +19,6 @@
package org.jclouds.rest.config; package org.jclouds.rest.config;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
@ -30,8 +29,9 @@ import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.rest.RestContext; import org.jclouds.rest.RestContext;
import org.jclouds.rest.internal.RestContextImpl; 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.ImmutableMap;
import com.google.common.collect.MapMaker;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Provides; import com.google.inject.Provides;
import com.google.inject.Scopes; import com.google.inject.Scopes;
@ -125,10 +125,10 @@ public class RestClientModule<S, A> extends AbstractModule {
@Provides @Provides
@Singleton @Singleton
@Named("sync") @Named("sync")
ConcurrentMap<ClassMethodArgs, Object> provideSyncDelegateMap( Cache<ClassMethodArgs, Object> provideSyncDelegateMap(
CreateClientForCaller createClientForCaller) { CreateClientForCaller createClientForCaller) {
createClientForCaller.sync2Async = delegates; createClientForCaller.sync2Async = delegates;
return new MapMaker().makeComputingMap(createClientForCaller); return CacheBuilder.newBuilder().build(createClientForCaller);
} }
} }

View File

@ -18,8 +18,6 @@
*/ */
package org.jclouds.rest.config; package org.jclouds.rest.config;
import java.util.concurrent.ConcurrentMap;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriBuilder;
@ -42,8 +40,10 @@ import org.jclouds.rest.internal.RestAnnotationProcessor;
import org.jclouds.rest.internal.SeedAnnotationCache; import org.jclouds.rest.internal.SeedAnnotationCache;
import com.google.common.base.Function; 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.ImmutableMap;
import com.google.common.collect.MapMaker;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Injector; import com.google.inject.Injector;
@ -77,18 +77,18 @@ public class RestModule extends AbstractModule {
@Provides @Provides
@Singleton @Singleton
protected ConcurrentMap<Class<?>, Boolean> seedAnnotationCache(SeedAnnotationCache seedAnnotationCache) { protected Cache<Class<?>, Boolean> seedAnnotationCache(SeedAnnotationCache seedAnnotationCache) {
return new MapMaker().makeComputingMap(seedAnnotationCache); return CacheBuilder.newBuilder().build(seedAnnotationCache);
} }
@Provides @Provides
@Singleton @Singleton
@Named("async") @Named("async")
ConcurrentMap<ClassMethodArgs, Object> provideAsyncDelegateMap(CreateAsyncClientForCaller createAsyncClientForCaller) { Cache<ClassMethodArgs, Object> provideAsyncDelegateMap(CreateAsyncClientForCaller createAsyncClientForCaller) {
return new MapMaker().makeComputingMap(createAsyncClientForCaller); return CacheBuilder.newBuilder().build(createAsyncClientForCaller);
} }
static class CreateAsyncClientForCaller implements Function<ClassMethodArgs, Object> { static class CreateAsyncClientForCaller extends CacheLoader<ClassMethodArgs, Object> {
private final Injector injector; private final Injector injector;
private final AsyncRestClientProxy.Factory factory; private final AsyncRestClientProxy.Factory factory;
@ -100,7 +100,7 @@ public class RestModule extends AbstractModule {
@SuppressWarnings( { "unchecked", "rawtypes" }) @SuppressWarnings( { "unchecked", "rawtypes" })
@Override @Override
public Object apply(final ClassMethodArgs from) { public Object load(final ClassMethodArgs from) {
Class clazz = from.getAsyncClass(); Class clazz = from.getAsyncClass();
TypeLiteral typeLiteral = TypeLiteral.get(clazz); TypeLiteral typeLiteral = TypeLiteral.get(clazz);
RestAnnotationProcessor util = (RestAnnotationProcessor) injector.getInstance(Key.get(TypeLiteral.get(Types 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 // cannot use child injectors due to the super coarse guice lock on
// Singleton // Singleton
util.setCaller(from); util.setCaller(from);
ConcurrentMap<ClassMethodArgs, Object> delegateMap = injector.getInstance(Key.get( Cache<ClassMethodArgs, Object> delegateMap = injector.getInstance(Key.get(
new TypeLiteral<ConcurrentMap<ClassMethodArgs, Object>>() { new TypeLiteral<Cache<ClassMethodArgs, Object>>() {
}, Names.named("async"))); }, Names.named("async")));
AsyncRestClientProxy proxy = new AsyncRestClientProxy(injector, factory, util, typeLiteral, delegateMap); AsyncRestClientProxy proxy = new AsyncRestClientProxy(injector, factory, util, typeLiteral, delegateMap);
injector.injectMembers(proxy); injector.injectMembers(proxy);

View File

@ -27,7 +27,6 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import javax.annotation.Resource; import javax.annotation.Resource;
@ -49,6 +48,7 @@ import org.jclouds.util.Throwables2;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
@ -76,12 +76,12 @@ public class AsyncRestClientProxy<T> implements InvocationHandler {
@Resource @Resource
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
private final ConcurrentMap<ClassMethodArgs, Object> delegateMap; private final Cache<ClassMethodArgs, Object> delegateMap;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Inject @Inject
public AsyncRestClientProxy(Injector injector, Factory factory, RestAnnotationProcessor<T> util, public AsyncRestClientProxy(Injector injector, Factory factory, RestAnnotationProcessor<T> util,
TypeLiteral<T> typeLiteral, @Named("async") ConcurrentMap<ClassMethodArgs, Object> delegateMap) { TypeLiteral<T> typeLiteral, @Named("async") Cache<ClassMethodArgs, Object> delegateMap) {
this.injector = injector; this.injector = injector;
this.annotationProcessor = util; this.annotationProcessor = util;
this.declaring = (Class<T>) typeLiteral.getRawType(); this.declaring = (Class<T>) typeLiteral.getRawType();

View File

@ -49,9 +49,8 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.SortedSet; 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.annotation.Resource;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Provider; import javax.inject.Provider;
@ -98,6 +97,7 @@ import org.jclouds.io.Payloads;
import org.jclouds.io.payloads.MultipartForm; import org.jclouds.io.payloads.MultipartForm;
import org.jclouds.io.payloads.Part; import org.jclouds.io.payloads.Part;
import org.jclouds.io.payloads.Part.PartOptions; import org.jclouds.io.payloads.Part.PartOptions;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.json.internal.GsonWrapper; import org.jclouds.json.internal.GsonWrapper;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
import org.jclouds.rest.Binder; 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.Functions;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Predicates; 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.ImmutableList;
import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet; 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.LinkedHashMultimap;
import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.MapMaker;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.inject.Inject; import com.google.inject.Inject;
@ -167,25 +170,25 @@ public class RestAnnotationProcessor<T> {
private final Class<T> declaring; private final Class<T> declaring;
// TODO replace with Table object // TODO replace with Table object
static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToBinderParamAnnotation = createMethodToIndexOfParamToAnnotation(BinderParam.class); static final Cache<Method, Cache<Integer, Set<Annotation>>> methodToIndexOfParamToBinderParamAnnotation = createMethodToIndexOfParamToAnnotation(BinderParam.class);
static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToWrapWithAnnotation = createMethodToIndexOfParamToAnnotation(WrapWith.class); static final Cache<Method, Cache<Integer, Set<Annotation>>> methodToIndexOfParamToWrapWithAnnotation = createMethodToIndexOfParamToAnnotation(WrapWith.class);
static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToHeaderParamAnnotations = createMethodToIndexOfParamToAnnotation(HeaderParam.class); static final Cache<Method, Cache<Integer, Set<Annotation>>> methodToIndexOfParamToHeaderParamAnnotations = createMethodToIndexOfParamToAnnotation(HeaderParam.class);
static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToEndpointAnnotations = createMethodToIndexOfParamToAnnotation(Endpoint.class); static final Cache<Method, Cache<Integer, Set<Annotation>>> methodToIndexOfParamToEndpointAnnotations = createMethodToIndexOfParamToAnnotation(Endpoint.class);
static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToEndpointParamAnnotations = createMethodToIndexOfParamToAnnotation(EndpointParam.class); static final Cache<Method, Cache<Integer, Set<Annotation>>> methodToIndexOfParamToEndpointParamAnnotations = createMethodToIndexOfParamToAnnotation(EndpointParam.class);
static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToMatrixParamAnnotations = createMethodToIndexOfParamToAnnotation(MatrixParam.class); static final Cache<Method, Cache<Integer, Set<Annotation>>> methodToIndexOfParamToMatrixParamAnnotations = createMethodToIndexOfParamToAnnotation(MatrixParam.class);
static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToFormParamAnnotations = createMethodToIndexOfParamToAnnotation(FormParam.class); static final Cache<Method, Cache<Integer, Set<Annotation>>> methodToIndexOfParamToFormParamAnnotations = createMethodToIndexOfParamToAnnotation(FormParam.class);
static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToQueryParamAnnotations = createMethodToIndexOfParamToAnnotation(QueryParam.class); static final Cache<Method, Cache<Integer, Set<Annotation>>> methodToIndexOfParamToQueryParamAnnotations = createMethodToIndexOfParamToAnnotation(QueryParam.class);
static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToPathParamAnnotations = createMethodToIndexOfParamToAnnotation(PathParam.class); static final Cache<Method, Cache<Integer, Set<Annotation>>> methodToIndexOfParamToPathParamAnnotations = createMethodToIndexOfParamToAnnotation(PathParam.class);
static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToPostParamAnnotations = createMethodToIndexOfParamToAnnotation(PayloadParam.class); static final Cache<Method, Cache<Integer, Set<Annotation>>> methodToIndexOfParamToPostParamAnnotations = createMethodToIndexOfParamToAnnotation(PayloadParam.class);
static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToPartParamAnnotations = createMethodToIndexOfParamToAnnotation(PartParam.class); static final Cache<Method, Cache<Integer, Set<Annotation>>> methodToIndexOfParamToPartParamAnnotations = createMethodToIndexOfParamToAnnotation(PartParam.class);
static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToParamParserAnnotations = createMethodToIndexOfParamToAnnotation(ParamParser.class); static final Cache<Method, Cache<Integer, Set<Annotation>>> methodToIndexOfParamToParamParserAnnotations = createMethodToIndexOfParamToAnnotation(ParamParser.class);
static final Map<MethodKey, Method> delegationMap = newHashMap(); static final Map<MethodKey, Method> delegationMap = newHashMap();
static Map<Method, Map<Integer, Set<Annotation>>> createMethodToIndexOfParamToAnnotation( static Cache<Method, Cache<Integer, Set<Annotation>>> createMethodToIndexOfParamToAnnotation(
final Class<? extends Annotation> annotation) { final Class<? extends Annotation> annotation) {
return new MapMaker().makeComputingMap(new Function<Method, Map<Integer, Set<Annotation>>>() { return CacheBuilder.newBuilder().build(new CacheLoader<Method, Cache<Integer, Set<Annotation>>>() {
public Map<Integer, Set<Annotation>> apply(Method method) { public Cache<Integer, Set<Annotation>> load(Method method) {
return new MapMaker().makeComputingMap(new GetAnnotationsForMethodParameterIndex(method, annotation)); return CacheBuilder.newBuilder().build(CacheLoader.from(new GetAnnotationsForMethodParameterIndex(method, annotation)));
} }
}); });
} }
@ -201,17 +204,17 @@ public class RestAnnotationProcessor<T> {
public Set<Annotation> apply(final Integer index) { public Set<Annotation> apply(final Integer index) {
return ImmutableSet.<Annotation> copyOf(filter(ImmutableList.copyOf(method.getParameterAnnotations()[index]), return ImmutableSet.<Annotation> copyOf(filter(ImmutableList.copyOf(method.getParameterAnnotations()[index]),
new Predicate<Annotation>() { new Predicate<Annotation>() {
public boolean apply(Annotation input) { public boolean apply(Annotation input) {
return input.annotationType().equals(clazz); return input.annotationType().equals(clazz);
} }
})); }));
} }
} }
private static final Class<? extends HttpRequestOptions[]> optionsVarArgsClass = new HttpRequestOptions[] {} private static final Class<? extends HttpRequestOptions[]> optionsVarArgsClass = new HttpRequestOptions[] {}
.getClass(); .getClass();
private static final Function<? super Entry<String, String>, ? extends Part> ENTRY_TO_PART = new Function<Entry<String, String>, Part>() { private static final Function<? super Entry<String, String>, ? extends Part> ENTRY_TO_PART = new Function<Entry<String, String>, Part>() {
@ -222,23 +225,24 @@ public class RestAnnotationProcessor<T> {
}; };
static final Map<Method, Set<Integer>> methodToIndexesOfOptions = new MapMaker() static final Cache<Method, Set<Integer>> methodToIndexesOfOptions = CacheBuilder.newBuilder().build(
.makeComputingMap(new Function<Method, Set<Integer>>() { new CacheLoader<Method, Set<Integer>>() {
public Set<Integer> apply(Method method) { @Override
Builder<Integer> toReturn = ImmutableSet.<Integer> builder(); public Set<Integer> load(Method method) {
for (int index = 0; index < method.getParameterTypes().length; index++) { Builder<Integer> toReturn = ImmutableSet.<Integer> builder();
Class<?> type = method.getParameterTypes()[index]; for (int index = 0; index < method.getParameterTypes().length; index++) {
if (HttpRequestOptions.class.isAssignableFrom(type) || optionsVarArgsClass.isAssignableFrom(type)) Class<?> type = method.getParameterTypes()[index];
toReturn.add(index); if (HttpRequestOptions.class.isAssignableFrom(type) || optionsVarArgsClass.isAssignableFrom(type))
} toReturn.add(index);
return toReturn.build();
} }
}); return toReturn.build();
}
});
private final ParseSax.Factory parserFactory; private final ParseSax.Factory parserFactory;
private final HttpUtils utils; private final HttpUtils utils;
private final Provider<UriBuilder> uriBuilderProvider; private final Provider<UriBuilder> uriBuilderProvider;
private final ConcurrentMap<Class<?>, Boolean> seedAnnotationCache; private final Cache<Class<?>, Boolean> seedAnnotationCache;
private final String apiVersion; private final String apiVersion;
private char[] skips; private char[] skips;
@ -252,7 +256,7 @@ public class RestAnnotationProcessor<T> {
@VisibleForTesting @VisibleForTesting
public static Function<HttpResponse, ?> createResponseParser(ParseSax.Factory parserFactory, Injector injector, public static Function<HttpResponse, ?> createResponseParser(ParseSax.Factory parserFactory, Injector injector,
Method method, HttpRequest request) { Method method, HttpRequest request) {
Function<HttpResponse, ?> transformer; Function<HttpResponse, ?> transformer;
Class<? extends HandlerWithResult<?>> handler = getSaxResponseParserClassOrNull(method); Class<? extends HandlerWithResult<?>> handler = getSaxResponseParserClassOrNull(method);
if (handler != null) { if (handler != null) {
@ -272,9 +276,9 @@ public class RestAnnotationProcessor<T> {
if (method.isAnnotationPresent(SelectJson.class)) { if (method.isAnnotationPresent(SelectJson.class)) {
Type returnVal = getReturnTypeForMethod(method); Type returnVal = getReturnTypeForMethod(method);
if (method.isAnnotationPresent(OnlyElement.class)) if (method.isAnnotationPresent(OnlyElement.class))
returnVal = Types.newParameterizedType(Set.class,returnVal); returnVal = Types.newParameterizedType(Set.class, returnVal);
transformer = new ParseFirstJsonValueNamed(injector.getInstance(GsonWrapper.class), TypeLiteral.get(returnVal), transformer = new ParseFirstJsonValueNamed(injector.getInstance(GsonWrapper.class),
method.getAnnotation(SelectJson.class).value()); TypeLiteral.get(returnVal), method.getAnnotation(SelectJson.class).value());
if (method.isAnnotationPresent(OnlyElement.class)) if (method.isAnnotationPresent(OnlyElement.class))
transformer = Functions.compose(new OnlyElementOrNull(), transformer); transformer = Functions.compose(new OnlyElementOrNull(), transformer);
} else { } else {
@ -290,7 +294,7 @@ public class RestAnnotationProcessor<T> {
@VisibleForTesting @VisibleForTesting
public static Function<Exception, ?> createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation( public static Function<Exception, ?> createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation(
Injector injector, Method method) { Injector injector, Method method) {
ExceptionParser annotation = method.getAnnotation(ExceptionParser.class); ExceptionParser annotation = method.getAnnotation(ExceptionParser.class);
if (annotation != null) { if (annotation != null) {
return injector.getInstance(annotation.value()); return injector.getInstance(annotation.value());
@ -300,9 +304,9 @@ public class RestAnnotationProcessor<T> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Inject @Inject
public RestAnnotationProcessor(Injector injector, ConcurrentMap<Class<?>, Boolean> seedAnnotationCache, public RestAnnotationProcessor(Injector injector, Cache<Class<?>, Boolean> seedAnnotationCache,
@Named(Constants.PROPERTY_API_VERSION) String apiVersion, ParseSax.Factory parserFactory, HttpUtils utils, @Named(Constants.PROPERTY_API_VERSION) String apiVersion, ParseSax.Factory parserFactory, HttpUtils utils,
TypeLiteral<T> typeLiteral) { TypeLiteral<T> typeLiteral) throws ExecutionException {
this.declaring = (Class<T>) typeLiteral.getRawType(); this.declaring = (Class<T>) typeLiteral.getRawType();
this.injector = injector; this.injector = injector;
this.parserFactory = parserFactory; this.parserFactory = parserFactory;
@ -366,8 +370,8 @@ public class RestAnnotationProcessor<T> {
this.name = method.getName(); this.name = method.getName();
this.declaringPackage = method.getDeclaringClass().getPackage(); this.declaringPackage = method.getDeclaringClass().getPackage();
int parametersTypeHashCode = 0; int parametersTypeHashCode = 0;
for (Class<?> param: method.getParameterTypes()) for (Class<?> param : method.getParameterTypes())
parametersTypeHashCode +=param.hashCode(); parametersTypeHashCode += param.hashCode();
this.parametersTypeHashCode = parametersTypeHashCode; this.parametersTypeHashCode = parametersTypeHashCode;
} }
@ -379,154 +383,166 @@ public class RestAnnotationProcessor<T> {
private URI callerEndpoint; private URI callerEndpoint;
public void setCaller(ClassMethodArgs caller) { 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; this.caller = caller;
try { try {
callerEndpoint = getEndpointFor(caller.getMethod(), caller.getArgs(), injector); callerEndpoint = getEndpointFor(caller.getMethod(), caller.getArgs(), injector);
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
} catch (ExecutionException e) {
Throwables.propagate(e);
} }
} }
public GeneratedHttpRequest<T> createRequest(Method method, Object... args) { public GeneratedHttpRequest<T> createRequest(Method method, Object... args) {
inputParamValidator.validateMethodParametersOrThrow(method, args); try {
ClassMethodArgs cma = logger.isTraceEnabled() ? new ClassMethodArgs(method.getDeclaringClass(), method, args) inputParamValidator.validateMethodParametersOrThrow(method, args);
ClassMethodArgs cma = logger.isTraceEnabled() ? new ClassMethodArgs(method.getDeclaringClass(), method, args)
: null; : null;
URI endpoint = callerEndpoint; URI endpoint = callerEndpoint;
try { try {
if (endpoint == null) { if (endpoint == null) {
endpoint = getEndpointFor(method, args, injector); endpoint = getEndpointFor(method, args, injector);
logger.trace("using endpoint %s for %s", endpoint, cma); 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<T> requestBuilder;
HttpRequest r = RestAnnotationProcessor.findHttpRequestInArgs(args);
if (r != null) {
requestBuilder = GeneratedHttpRequest.Builder.<T> from(r);
endpoint = r.getEndpoint();
} else { } else {
logger.trace("using endpoint %s from caller %s for %s", caller, endpoint, cma); requestBuilder = GeneratedHttpRequest.<T> builder();
} requestBuilder.method(getHttpMethodOrConstantOrThrowException(method));
} 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<T> requestBuilder;
HttpRequest r = RestAnnotationProcessor.findHttpRequestInArgs(args);
if (r != null) {
requestBuilder = GeneratedHttpRequest.Builder.<T> from(r);
endpoint = r.getEndpoint();
} else {
requestBuilder = GeneratedHttpRequest.<T> 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<String, String> tokenValues = LinkedHashMultimap.create();
tokenValues.put(Constants.PROPERTY_API_VERSION, apiVersion);
tokenValues.putAll(addPathAndGetTokens(declaring, method, args, builder));
Multimap<String, String> formParams = addFormParams(tokenValues.entries(), method, args);
Multimap<String, String> queryParams = addQueryParams(tokenValues.entries(), method, args);
Multimap<String, String> matrixParams = addMatrixParams(tokenValues.entries(), method, args);
Multimap<String, String> 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<String, String> header : options.buildRequestHeaders().entries()) {
headers.put(header.getKey(), Strings2.replaceTokens(header.getValue(), tokenValues.entries()));
}
for (Entry<String, String> matrix : options.buildMatrixParameters().entries()) {
matrixParams.put(matrix.getKey(), Strings2.replaceTokens(matrix.getValue(), tokenValues.entries()));
}
for (Entry<String, String> query : options.buildQueryParameters().entries()) {
queryParams.put(query.getKey(), Strings2.replaceTokens(query.getValue(), tokenValues.entries()));
}
for (Entry<String, String> form : options.buildFormParameters().entries()) {
formParams.put(form.getKey(), Strings2.replaceTokens(form.getValue(), tokenValues.entries()));
} }
String pathSuffix = options.buildPathSuffix(); requestBuilder.declaring(declaring).javaMethod(method).args(args).skips(skips);
if (pathSuffix != null) { requestBuilder.filters(getFiltersIfAnnotated(method));
builder.path(pathSuffix);
UriBuilder builder = uriBuilderProvider.get().uri(endpoint);
Multimap<String, String> tokenValues = LinkedHashMultimap.create();
tokenValues.put(Constants.PROPERTY_API_VERSION, apiVersion);
tokenValues.putAll(addPathAndGetTokens(declaring, method, args, builder));
Multimap<String, String> formParams = addFormParams(tokenValues.entries(), method, args);
Multimap<String, String> queryParams = addQueryParams(tokenValues.entries(), method, args);
Multimap<String, String> matrixParams = addMatrixParams(tokenValues.entries(), method, args);
Multimap<String, String> 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) { Payload payload = null;
for (String key : matrixParams.keySet()) HttpRequestOptions options = findOptionsIn(method, args);
builder.matrixParam(key, Lists.newArrayList(matrixParams.get(key)).toArray()); if (options != null) {
} injector.injectMembers(options);// TODO test case
for (Entry<String, String> header : options.buildRequestHeaders().entries()) {
headers.put(header.getKey(), Strings2.replaceTokens(header.getValue(), tokenValues.entries()));
}
for (Entry<String, String> matrix : options.buildMatrixParameters().entries()) {
matrixParams.put(matrix.getKey(), Strings2.replaceTokens(matrix.getValue(), tokenValues.entries()));
}
for (Entry<String, String> query : options.buildQueryParameters().entries()) {
queryParams.put(query.getKey(), Strings2.replaceTokens(query.getValue(), tokenValues.entries()));
}
for (Entry<String, String> form : options.buildFormParameters().entries()) {
formParams.put(form.getKey(), Strings2.replaceTokens(form.getValue(), tokenValues.entries()));
}
if (queryParams.size() > 0) { String pathSuffix = options.buildPathSuffix();
builder.replaceQuery(ModifyRequest.makeQueryLine(queryParams, null, skips)); if (pathSuffix != null) {
} builder.path(pathSuffix);
}
requestBuilder.headers(filterOutContentHeaders(headers)); String stringPayload = options.buildStringPayload();
if (stringPayload != null)
try { payload = Payloads.newStringPayload(stringPayload);
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<? extends Part> 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) { if (matrixParams.size() > 0) {
payload = Payloads.newUrlEncodedFormPayload(formParams, skips); for (String key : matrixParams.keySet())
} else if (headers.containsKey(CONTENT_TYPE)) { 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) if (payload == null)
payload = Payloads.newByteArrayPayload(new byte[] {}); payload = findPayloadInArgs(args);
payload.getContentMetadata().setContentType(Iterables.get(headers.get(CONTENT_TYPE), 0)); List<? extends Part> parts = getParts(method, args, concat(tokenValues.entries(), formParams.entries()));
} if (parts.size() > 0) {
if (payload != null) { if (formParams.size() > 0) {
requestBuilder.payload(payload); parts = newLinkedList(concat(transform(formParams.entries(), ENTRY_TO_PART), parts));
} }
GeneratedHttpRequest<T> request = requestBuilder.build(); payload = new MultipartForm(BOUNDARY, parts);
} else if (formParams.size() > 0) {
org.jclouds.rest.MapBinder mapBinder = getMapPayloadBinderOrNull(method, args); payload = Payloads.newUrlEncodedFormPayload(formParams, skips);
if (mapBinder != null) { } else if (headers.containsKey(CONTENT_TYPE)) {
Map<String, String> mapParams = buildPostParams(method, args); if (payload == null)
if (method.isAnnotationPresent(PayloadParams.class)) { payload = Payloads.newByteArrayPayload(new byte[] {});
PayloadParams params = method.getAnnotation(PayloadParams.class); payload.getContentMetadata().setContentType(Iterables.get(headers.get(CONTENT_TYPE), 0));
addMapPayload(mapParams, params, headers.entries());
} }
request = mapBinder.bindToRequest(request, mapParams); if (payload != null) {
} else { requestBuilder.payload(payload);
request = decorateRequest(request); }
} GeneratedHttpRequest<T> request = requestBuilder.build();
if (request.getPayload() != null) org.jclouds.rest.MapBinder mapBinder = getMapPayloadBinderOrNull(method, args);
request.getPayload().getContentMetadata().setPropertiesFromHttpHeaders(headers); if (mapBinder != null) {
utils.checkRequestHasRequiredProperties(request); Map<String, String> mapParams = buildPostParams(method, args);
return request; 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<String, String> filterOutContentHeaders(Multimap<String, String> headers) { public static Multimap<String, String> filterOutContentHeaders(Multimap<String, String> headers) {
// TODO make a filter like {@link Maps.filterKeys} instead of this // TODO make a filter like {@link Maps.filterKeys} instead of this
ImmutableMultimap.Builder<String, String> headersBuilder = ImmutableMultimap.builder(); ImmutableMultimap.Builder<String, String> 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())) { for (String header : Iterables.filter(headers.keySet(), Predicates.notNull())) {
if (!ContentMetadata.HTTP_HEADERS.contains(header)) { if (!ContentMetadata.HTTP_HEADERS.contains(header)) {
headersBuilder.putAll(header, headers.get(header)); headersBuilder.putAll(header, headers.get(header));
@ -537,7 +553,7 @@ public class RestAnnotationProcessor<T> {
public static final String BOUNDARY = "--JCLOUDS--"; public static final String BOUNDARY = "--JCLOUDS--";
private Multimap<String, String> addPathAndGetTokens(Class<?> clazz, Method method, Object[] args, UriBuilder builder) { private Multimap<String, String> addPathAndGetTokens(Class<?> clazz, Method method, Object[] args, UriBuilder builder) throws ExecutionException {
if (clazz.isAnnotationPresent(Path.class)) if (clazz.isAnnotationPresent(Path.class))
builder.path(clazz); builder.path(clazz);
if (method.isAnnotationPresent(Path.class)) if (method.isAnnotationPresent(Path.class))
@ -550,14 +566,14 @@ public class RestAnnotationProcessor<T> {
} }
public static URI replaceQuery(Provider<UriBuilder> uriBuilderProvider, URI in, String newQuery, public static URI replaceQuery(Provider<UriBuilder> uriBuilderProvider, URI in, String newQuery,
@Nullable Comparator<Entry<String, String>> sorter, char... skips) { @Nullable Comparator<Entry<String, String>> sorter, char... skips) {
UriBuilder builder = uriBuilderProvider.get().uri(in); UriBuilder builder = uriBuilderProvider.get().uri(in);
builder.replaceQuery(ModifyRequest.makeQueryLine(ModifyRequest.parseQueryToMap(newQuery), sorter, skips)); builder.replaceQuery(ModifyRequest.makeQueryLine(ModifyRequest.parseQueryToMap(newQuery), sorter, skips));
return builder.build(); return builder.build();
} }
private Multimap<String, String> addMatrixParams(Collection<Entry<String, String>> tokenValues, Method method, private Multimap<String, String> addMatrixParams(Collection<Entry<String, String>> tokenValues, Method method,
Object... args) { Object... args) throws ExecutionException {
Multimap<String, String> matrixMap = LinkedListMultimap.create(); Multimap<String, String> matrixMap = LinkedListMultimap.create();
if (declaring.isAnnotationPresent(MatrixParams.class)) { if (declaring.isAnnotationPresent(MatrixParams.class)) {
MatrixParams matrix = declaring.getAnnotation(MatrixParams.class); MatrixParams matrix = declaring.getAnnotation(MatrixParams.class);
@ -576,7 +592,7 @@ public class RestAnnotationProcessor<T> {
} }
private Multimap<String, String> addFormParams(Collection<Entry<String, String>> tokenValues, Method method, private Multimap<String, String> addFormParams(Collection<Entry<String, String>> tokenValues, Method method,
Object... args) { Object... args) throws ExecutionException {
Multimap<String, String> formMap = LinkedListMultimap.create(); Multimap<String, String> formMap = LinkedListMultimap.create();
if (declaring.isAnnotationPresent(FormParams.class)) { if (declaring.isAnnotationPresent(FormParams.class)) {
FormParams form = declaring.getAnnotation(FormParams.class); FormParams form = declaring.getAnnotation(FormParams.class);
@ -595,7 +611,7 @@ public class RestAnnotationProcessor<T> {
} }
private Multimap<String, String> addQueryParams(Collection<Entry<String, String>> tokenValues, Method method, private Multimap<String, String> addQueryParams(Collection<Entry<String, String>> tokenValues, Method method,
Object... args) { Object... args) throws ExecutionException {
Multimap<String, String> queryMap = LinkedListMultimap.create(); Multimap<String, String> queryMap = LinkedListMultimap.create();
if (declaring.isAnnotationPresent(QueryParams.class)) { if (declaring.isAnnotationPresent(QueryParams.class)) {
QueryParams query = declaring.getAnnotation(QueryParams.class); QueryParams query = declaring.getAnnotation(QueryParams.class);
@ -614,7 +630,7 @@ public class RestAnnotationProcessor<T> {
} }
private void addForm(Multimap<String, String> formParams, FormParams form, private void addForm(Multimap<String, String> formParams, FormParams form,
Collection<Entry<String, String>> tokenValues) { Collection<Entry<String, String>> tokenValues) {
for (int i = 0; i < form.keys().length; i++) { for (int i = 0; i < form.keys().length; i++) {
if (form.values()[i].equals(FormParams.NULL)) { if (form.values()[i].equals(FormParams.NULL)) {
formParams.removeAll(form.keys()[i]); formParams.removeAll(form.keys()[i]);
@ -626,7 +642,7 @@ public class RestAnnotationProcessor<T> {
} }
private void addQuery(Multimap<String, String> queryParams, QueryParams query, private void addQuery(Multimap<String, String> queryParams, QueryParams query,
Collection<Entry<String, String>> tokenValues) { Collection<Entry<String, String>> tokenValues) {
for (int i = 0; i < query.keys().length; i++) { for (int i = 0; i < query.keys().length; i++) {
if (query.values()[i].equals(QueryParams.NULL)) { if (query.values()[i].equals(QueryParams.NULL)) {
queryParams.removeAll(query.keys()[i]); queryParams.removeAll(query.keys()[i]);
@ -638,7 +654,7 @@ public class RestAnnotationProcessor<T> {
} }
private void addMatrix(Multimap<String, String> matrixParams, MatrixParams matrix, private void addMatrix(Multimap<String, String> matrixParams, MatrixParams matrix,
Collection<Entry<String, String>> tokenValues) { Collection<Entry<String, String>> tokenValues) {
for (int i = 0; i < matrix.keys().length; i++) { for (int i = 0; i < matrix.keys().length; i++) {
if (matrix.values()[i].equals(MatrixParams.NULL)) { if (matrix.values()[i].equals(MatrixParams.NULL)) {
matrixParams.removeAll(matrix.keys()[i]); matrixParams.removeAll(matrix.keys()[i]);
@ -650,7 +666,7 @@ public class RestAnnotationProcessor<T> {
} }
private void addMapPayload(Map<String, String> postParams, PayloadParams mapDefaults, private void addMapPayload(Map<String, String> postParams, PayloadParams mapDefaults,
Collection<Entry<String, String>> tokenValues) { Collection<Entry<String, String>> tokenValues) {
for (int i = 0; i < mapDefaults.keys().length; i++) { for (int i = 0; i < mapDefaults.keys().length; i++) {
if (mapDefaults.values()[i].equals(PayloadParams.NULL)) { if (mapDefaults.values()[i].equals(PayloadParams.NULL)) {
postParams.put(mapDefaults.keys()[i], null); postParams.put(mapDefaults.keys()[i], null);
@ -683,9 +699,10 @@ public class RestAnnotationProcessor<T> {
} }
@VisibleForTesting @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<Integer, Set<Annotation>> map = indexWithAtLeastOneAnnotation(method, Map<Integer, Set<Annotation>> map = indexWithAtLeastOneAnnotation(method,
methodToIndexOfParamToEndpointParamAnnotations); methodToIndexOfParamToEndpointParamAnnotations);
if (map.size() >= 1 && args.length > 0) { if (map.size() >= 1 && args.length > 0) {
EndpointParam firstAnnotation = (EndpointParam) get(get(map.values(), 0), 0); EndpointParam firstAnnotation = (EndpointParam) get(get(map.values(), 0), 0);
Function<Object, URI> parser = injector.getInstance(firstAnnotation.parser()); Function<Object, URI> parser = injector.getInstance(firstAnnotation.parser());
@ -694,8 +711,8 @@ public class RestAnnotationProcessor<T> {
int index = map.keySet().iterator().next(); int index = map.keySet().iterator().next();
try { try {
URI returnVal = parser.apply(args[index]); URI returnVal = parser.apply(args[index]);
checkArgument(returnVal != null, String.format("endpoint for [%s] not configured for %s", args[index], checkArgument(returnVal != null,
method)); String.format("endpoint for [%s] not configured for %s", args[index], method));
return returnVal; return returnVal;
} catch (NullPointerException e) { } catch (NullPointerException e) {
throw new IllegalArgumentException(String.format("argument at index %d on method %s", index, method), e); throw new IllegalArgumentException(String.format("argument at index %d on method %s", index, method), e);
@ -712,19 +729,19 @@ public class RestAnnotationProcessor<T> {
}); });
try { try {
URI returnVal = parser.apply(argsToParse); URI returnVal = parser.apply(argsToParse);
checkArgument(returnVal != null, String.format("endpoint for [%s] not configured for %s", argsToParse, checkArgument(returnVal != null,
method)); String.format("endpoint for [%s] not configured for %s", argsToParse, method));
return returnVal; return returnVal;
} catch (NullPointerException e) { } catch (NullPointerException e) {
throw new IllegalArgumentException(String.format("illegal argument in [%s] for method %s", argsToParse, throw new IllegalArgumentException(String.format("illegal argument in [%s] for method %s", argsToParse,
method), e); method), e);
} }
} }
} }
return null; 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); URI endpoint = getEndpointInParametersOrNull(method, args, injector);
if (endpoint == null) { if (endpoint == null) {
Endpoint annotation; Endpoint annotation;
@ -753,29 +770,29 @@ public class RestAnnotationProcessor<T> {
public static final TypeLiteral<ListenableFuture<HttpResponse>> futureHttpResponseLiteral = new TypeLiteral<ListenableFuture<HttpResponse>>() { public static final TypeLiteral<ListenableFuture<HttpResponse>> futureHttpResponseLiteral = new TypeLiteral<ListenableFuture<HttpResponse>>() {
}; };
@SuppressWarnings( { "unchecked", "rawtypes" }) @SuppressWarnings({ "unchecked", "rawtypes" })
public static Key<? extends Function<HttpResponse, ?>> getParserOrThrowException(Method method) { public static Key<? extends Function<HttpResponse, ?>> getParserOrThrowException(Method method) {
ResponseParser annotation = method.getAnnotation(ResponseParser.class); ResponseParser annotation = method.getAnnotation(ResponseParser.class);
if (annotation == null) { if (annotation == null) {
if (method.getReturnType().equals(void.class) if (method.getReturnType().equals(void.class)
|| TypeLiteral.get(method.getGenericReturnType()).equals(futureVoidLiteral)) { || TypeLiteral.get(method.getGenericReturnType()).equals(futureVoidLiteral)) {
return Key.get(ReleasePayloadAndReturn.class); return Key.get(ReleasePayloadAndReturn.class);
} else if (method.getReturnType().equals(boolean.class) || method.getReturnType().equals(Boolean.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); return Key.get(ReturnTrueIf2xx.class);
} else if (method.getReturnType().equals(InputStream.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); return Key.get(ReturnInputStream.class);
} else if (method.getReturnType().equals(HttpResponse.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); return Key.get((Class) IdentityFunction.class);
} else if (getAcceptHeadersOrNull(method).contains(MediaType.APPLICATION_JSON)) { } else if (getAcceptHeadersOrNull(method).contains(MediaType.APPLICATION_JSON)) {
return getJsonParserKeyForMethod(method); return getJsonParserKeyForMethod(method);
} else if (method.getReturnType().equals(String.class) } else if (method.getReturnType().equals(String.class)
|| TypeLiteral.get(method.getGenericReturnType()).equals(futureStringLiteral)) { || TypeLiteral.get(method.getGenericReturnType()).equals(futureStringLiteral)) {
return Key.get(ReturnStringIf2xx.class); return Key.get(ReturnStringIf2xx.class);
} else if (method.getReturnType().equals(URI.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); return Key.get(ParseURIFromListOrLocationHeaderIf20x.class);
} else { } else {
throw new IllegalStateException("You must specify a ResponseParser annotation on: " + method.toString()); throw new IllegalStateException("You must specify a ResponseParser annotation on: " + method.toString());
@ -804,7 +821,7 @@ public class RestAnnotationProcessor<T> {
return returnVal; return returnVal;
} }
@SuppressWarnings( { "unchecked", "rawtypes" }) @SuppressWarnings({ "unchecked", "rawtypes" })
public static Key<? extends Function<HttpResponse, ?>> getJsonParserKeyForMethodAnType(Method method, Type returnVal) { public static Key<? extends Function<HttpResponse, ?>> getJsonParserKeyForMethodAnType(Method method, Type returnVal) {
ParameterizedType parserType; ParameterizedType parserType;
if (method.isAnnotationPresent(Unwrap.class)) { if (method.isAnnotationPresent(Unwrap.class)) {
@ -820,7 +837,7 @@ public class RestAnnotationProcessor<T> {
parserType = Types.newParameterizedType(UnwrapOnlyNestedJsonValueInSet.class, returnVal); parserType = Types.newParameterizedType(UnwrapOnlyNestedJsonValueInSet.class, returnVal);
else else
throw new IllegalStateException(String.format("depth(%d) edgeCollection(%s) not yet supported for @Unwrap", throw new IllegalStateException(String.format("depth(%d) edgeCollection(%s) not yet supported for @Unwrap",
depth, edgeCollection)); depth, edgeCollection));
} else { } else {
parserType = Types.newParameterizedType(ParseJson.class, returnVal); parserType = Types.newParameterizedType(ParseJson.class, returnVal);
} }
@ -850,7 +867,7 @@ public class RestAnnotationProcessor<T> {
} else { } else {
if (postBinders[0] instanceof org.jclouds.rest.MapBinder) { if (postBinders[0] instanceof org.jclouds.rest.MapBinder) {
throw new IllegalArgumentException("we currently do not support multiple varargs postBinders in: " throw new IllegalArgumentException("we currently do not support multiple varargs postBinders in: "
+ method.getName()); + method.getName());
} }
} }
} else if (arg instanceof org.jclouds.rest.MapBinder) { } else if (arg instanceof org.jclouds.rest.MapBinder) {
@ -883,8 +900,8 @@ public class RestAnnotationProcessor<T> {
Set<String> requests = getHttpMethods(method); Set<String> requests = getHttpMethods(method);
if (requests == null || requests.size() != 1) { if (requests == null || requests.size() != 1) {
throw new IllegalStateException( throw new IllegalStateException(
"You must use at least one, but no more than one http method or pathparam annotation on: " "You must use at least one, but no more than one http method or pathparam annotation on: "
+ method.toString()); + method.toString());
} }
return requests.iterator().next(); return requests.iterator().next();
} }
@ -902,11 +919,13 @@ public class RestAnnotationProcessor<T> {
} }
}; };
public GeneratedHttpRequest<T> decorateRequest(GeneratedHttpRequest<T> request) { public GeneratedHttpRequest<T> decorateRequest(GeneratedHttpRequest<T> request) throws NegativeArraySizeException,
ExecutionException {
OUTER: for (Entry<Integer, Set<Annotation>> entry : concat(// OUTER: for (Entry<Integer, Set<Annotation>> entry : concat(//
filterValues(methodToIndexOfParamToBinderParamAnnotation.get(request.getJavaMethod()), notEmpty) filterValues(methodToIndexOfParamToBinderParamAnnotation.get(request.getJavaMethod()).asMap(), notEmpty)
.entrySet(), // .entrySet(), //
filterValues(methodToIndexOfParamToWrapWithAnnotation.get(request.getJavaMethod()), notEmpty).entrySet())) { filterValues(methodToIndexOfParamToWrapWithAnnotation.get(request.getJavaMethod()).asMap(), notEmpty)
.entrySet())) {
boolean shouldBreak = false; boolean shouldBreak = false;
Annotation annotation = Iterables.get(entry.getValue(), 0); Annotation annotation = Iterables.get(entry.getValue(), 0);
Binder binder; Binder binder;
@ -914,7 +933,7 @@ public class RestAnnotationProcessor<T> {
binder = injector.getInstance(BinderParam.class.cast(annotation).value()); binder = injector.getInstance(BinderParam.class.cast(annotation).value());
else else
binder = injector.getInstance(BindToJsonPayloadWrappedWith.Factory.class).create( 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) { if (request.getArgs().size() >= entry.getKey() + 1 && request.getArgs().get(entry.getKey()) != null) {
Object input; Object input;
Class<?> parameterType = request.getJavaMethod().getParameterTypes()[entry.getKey()]; Class<?> parameterType = request.getJavaMethod().getParameterTypes()[entry.getKey()];
@ -947,28 +966,28 @@ public class RestAnnotationProcessor<T> {
} }
public static Map<Integer, Set<Annotation>> indexWithOnlyOneAnnotation(Method method, String description, public static Map<Integer, Set<Annotation>> indexWithOnlyOneAnnotation(Method method, String description,
Map<Method, Map<Integer, Set<Annotation>>> toRefine) { Cache<Method, Cache<Integer, Set<Annotation>>> toRefine) throws ExecutionException {
Map<Integer, Set<Annotation>> indexToPayloadAnnotation = indexWithAtLeastOneAnnotation(method, toRefine); Map<Integer, Set<Annotation>> indexToPayloadAnnotation = indexWithAtLeastOneAnnotation(method, toRefine);
if (indexToPayloadAnnotation.size() > 1) { if (indexToPayloadAnnotation.size() > 1) {
throw new IllegalStateException(String.format( throw new IllegalStateException(String.format(
"You must not specify more than one %s annotation on: %s; found %s", description, method.toString(), "You must not specify more than one %s annotation on: %s; found %s", description, method.toString(),
indexToPayloadAnnotation)); indexToPayloadAnnotation));
} }
return indexToPayloadAnnotation; return indexToPayloadAnnotation;
} }
private static Map<Integer, Set<Annotation>> indexWithAtLeastOneAnnotation(Method method, private static Map<Integer, Set<Annotation>> indexWithAtLeastOneAnnotation(Method method,
Map<Method, Map<Integer, Set<Annotation>>> toRefine) { Cache<Method, Cache<Integer, Set<Annotation>>> toRefine) throws ExecutionException {
Map<Integer, Set<Annotation>> indexToPayloadAnnotation = filterValues(toRefine.get(method), Map<Integer, Set<Annotation>> indexToPayloadAnnotation = filterValues(toRefine.get(method).asMap(),
new Predicate<Set<Annotation>>() { new Predicate<Set<Annotation>>() {
public boolean apply(Set<Annotation> input) { public boolean apply(Set<Annotation> input) {
return input.size() == 1; return input.size() == 1;
} }
}); });
return indexToPayloadAnnotation; return indexToPayloadAnnotation;
} }
private HttpRequestOptions findOptionsIn(Method method, Object... args) { private HttpRequestOptions findOptionsIn(Method method, Object... args) throws ExecutionException {
for (int index : methodToIndexesOfOptions.get(method)) { for (int index : methodToIndexesOfOptions.get(method)) {
if (args.length >= index + 1) {// accomodate varargs if (args.length >= index + 1) {// accomodate varargs
if (args[index] instanceof Object[]) { if (args[index] instanceof Object[]) {
@ -983,7 +1002,7 @@ public class RestAnnotationProcessor<T> {
} else { } else {
if (options[0] instanceof HttpRequestOptions) { if (options[0] instanceof HttpRequestOptions) {
throw new IllegalArgumentException("we currently do not support multiple varargs options in: " throw new IllegalArgumentException("we currently do not support multiple varargs options in: "
+ method.getName()); + method.getName());
} }
} }
} else { } else {
@ -995,11 +1014,11 @@ public class RestAnnotationProcessor<T> {
} }
public Multimap<String, String> buildHeaders(Collection<Entry<String, String>> tokenValues, Method method, public Multimap<String, String> buildHeaders(Collection<Entry<String, String>> tokenValues, Method method,
final Object... args) { final Object... args) throws ExecutionException {
Multimap<String, String> headers = LinkedHashMultimap.create(); Multimap<String, String> headers = LinkedHashMultimap.create();
addHeaderIfAnnotationPresentOnMethod(headers, method, tokenValues); addHeaderIfAnnotationPresentOnMethod(headers, method, tokenValues);
Map<Integer, Set<Annotation>> indexToHeaderParam = methodToIndexOfParamToHeaderParamAnnotations.get(method); Cache<Integer, Set<Annotation>> indexToHeaderParam = methodToIndexOfParamToHeaderParamAnnotations.get(method);
for (Entry<Integer, Set<Annotation>> entry : indexToHeaderParam.entrySet()) { for (Entry<Integer, Set<Annotation>> entry : indexToHeaderParam.asMap().entrySet()) {
for (Annotation key : entry.getValue()) { for (Annotation key : entry.getValue()) {
String value = args[entry.getKey()].toString(); String value = args[entry.getKey()].toString();
value = Strings2.replaceTokens(value, tokenValues); value = Strings2.replaceTokens(value, tokenValues);
@ -1042,7 +1061,7 @@ public class RestAnnotationProcessor<T> {
} }
public void addHeaderIfAnnotationPresentOnMethod(Multimap<String, String> headers, Method method, public void addHeaderIfAnnotationPresentOnMethod(Multimap<String, String> headers, Method method,
Collection<Entry<String, String>> tokenValues) { Collection<Entry<String, String>> tokenValues) {
if (declaring.isAnnotationPresent(Headers.class)) { if (declaring.isAnnotationPresent(Headers.class)) {
Headers header = declaring.getAnnotation(Headers.class); Headers header = declaring.getAnnotation(Headers.class);
addHeader(headers, header, tokenValues); addHeader(headers, header, tokenValues);
@ -1054,7 +1073,7 @@ public class RestAnnotationProcessor<T> {
} }
private void addHeader(Multimap<String, String> headers, Headers header, private void addHeader(Multimap<String, String> headers, Headers header,
Collection<Entry<String, String>> tokenValues) { Collection<Entry<String, String>> tokenValues) {
for (int i = 0; i < header.keys().length; i++) { for (int i = 0; i < header.keys().length; i++) {
String value = header.values()[i]; String value = header.values()[i];
value = Strings2.replaceTokens(value, tokenValues); value = Strings2.replaceTokens(value, tokenValues);
@ -1063,10 +1082,10 @@ public class RestAnnotationProcessor<T> {
} }
List<? extends Part> getParts(Method method, Object[] args, Iterable<Entry<String, String>> iterable) { List<? extends Part> getParts(Method method, Object[] args, Iterable<Entry<String, String>> iterable) throws ExecutionException {
List<Part> parts = newLinkedList(); List<Part> parts = newLinkedList();
Map<Integer, Set<Annotation>> indexToPartParam = methodToIndexOfParamToPartParamAnnotations.get(method); Cache<Integer, Set<Annotation>> indexToPartParam = methodToIndexOfParamToPartParamAnnotations.get(method);
for (Entry<Integer, Set<Annotation>> entry : indexToPartParam.entrySet()) { for (Entry<Integer, Set<Annotation>> entry : indexToPartParam.asMap().entrySet()) {
for (Annotation key : entry.getValue()) { for (Annotation key : entry.getValue()) {
PartParam param = (PartParam) key; PartParam param = (PartParam) key;
PartOptions options = new PartOptions(); PartOptions options = new PartOptions();
@ -1101,12 +1120,12 @@ public class RestAnnotationProcessor<T> {
return null; return null;
} }
private Multimap<String, String> getPathParamKeyValues(Method method, Object... args) { private Multimap<String, String> getPathParamKeyValues(Method method, Object... args) throws ExecutionException {
Multimap<String, String> pathParamValues = LinkedHashMultimap.create(); Multimap<String, String> pathParamValues = LinkedHashMultimap.create();
Map<Integer, Set<Annotation>> indexToPathParam = methodToIndexOfParamToPathParamAnnotations.get(method); Cache<Integer, Set<Annotation>> indexToPathParam = methodToIndexOfParamToPathParamAnnotations.get(method);
Map<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method); Cache<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
for (Entry<Integer, Set<Annotation>> entry : indexToPathParam.entrySet()) { for (Entry<Integer, Set<Annotation>> entry : indexToPathParam.asMap().entrySet()) {
for (Annotation key : entry.getValue()) { for (Annotation key : entry.getValue()) {
Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey()); Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
String paramKey = ((PathParam) key).value(); String paramKey = ((PathParam) key).value();
@ -1138,12 +1157,12 @@ public class RestAnnotationProcessor<T> {
return encoded; return encoded;
} }
private Multimap<String, String> getMatrixParamKeyValues(Method method, Object... args) { private Multimap<String, String> getMatrixParamKeyValues(Method method, Object... args) throws ExecutionException {
Multimap<String, String> matrixParamValues = LinkedHashMultimap.create(); Multimap<String, String> matrixParamValues = LinkedHashMultimap.create();
Map<Integer, Set<Annotation>> indexToMatrixParam = methodToIndexOfParamToMatrixParamAnnotations.get(method); Cache<Integer, Set<Annotation>> indexToMatrixParam = methodToIndexOfParamToMatrixParamAnnotations.get(method);
Map<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method); Cache<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
for (Entry<Integer, Set<Annotation>> entry : indexToMatrixParam.entrySet()) { for (Entry<Integer, Set<Annotation>> entry : indexToMatrixParam.asMap().entrySet()) {
for (Annotation key : entry.getValue()) { for (Annotation key : entry.getValue()) {
Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey()); Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
String paramKey = ((MatrixParam) key).value(); String paramKey = ((MatrixParam) key).value();
@ -1167,12 +1186,12 @@ public class RestAnnotationProcessor<T> {
return matrixParamValues; return matrixParamValues;
} }
private Multimap<String, String> getFormParamKeyValues(Method method, Object... args) { private Multimap<String, String> getFormParamKeyValues(Method method, Object... args) throws ExecutionException {
Multimap<String, String> formParamValues = LinkedHashMultimap.create(); Multimap<String, String> formParamValues = LinkedHashMultimap.create();
Map<Integer, Set<Annotation>> indexToFormParam = methodToIndexOfParamToFormParamAnnotations.get(method); Cache<Integer, Set<Annotation>> indexToFormParam = methodToIndexOfParamToFormParamAnnotations.get(method);
Map<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method); Cache<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
for (Entry<Integer, Set<Annotation>> entry : indexToFormParam.entrySet()) { for (Entry<Integer, Set<Annotation>> entry : indexToFormParam.asMap().entrySet()) {
for (Annotation key : entry.getValue()) { for (Annotation key : entry.getValue()) {
Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey()); Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
String paramKey = ((FormParam) key).value(); String paramKey = ((FormParam) key).value();
@ -1196,12 +1215,12 @@ public class RestAnnotationProcessor<T> {
return formParamValues; return formParamValues;
} }
private Multimap<String, String> getQueryParamKeyValues(Method method, Object... args) { private Multimap<String, String> getQueryParamKeyValues(Method method, Object... args) throws ExecutionException {
Multimap<String, String> queryParamValues = LinkedHashMultimap.create(); Multimap<String, String> queryParamValues = LinkedHashMultimap.create();
Map<Integer, Set<Annotation>> indexToQueryParam = methodToIndexOfParamToQueryParamAnnotations.get(method); Cache<Integer, Set<Annotation>> indexToQueryParam = methodToIndexOfParamToQueryParamAnnotations.get(method);
Map<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method); Cache<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
for (Entry<Integer, Set<Annotation>> entry : indexToQueryParam.entrySet()) { for (Entry<Integer, Set<Annotation>> entry : indexToQueryParam.asMap().entrySet()) {
for (Annotation key : entry.getValue()) { for (Annotation key : entry.getValue()) {
Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey()); Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
String paramKey = ((QueryParam) key).value(); String paramKey = ((QueryParam) key).value();
@ -1225,11 +1244,11 @@ public class RestAnnotationProcessor<T> {
return queryParamValues; return queryParamValues;
} }
private Map<String, String> buildPostParams(Method method, Object... args) { private Map<String, String> buildPostParams(Method method, Object... args) throws ExecutionException {
Map<String, String> postParams = newHashMap(); Map<String, String> postParams = newHashMap();
Map<Integer, Set<Annotation>> indexToPathParam = methodToIndexOfParamToPostParamAnnotations.get(method); Cache<Integer, Set<Annotation>> indexToPathParam = methodToIndexOfParamToPostParamAnnotations.get(method);
Map<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method); Cache<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
for (Entry<Integer, Set<Annotation>> entry : indexToPathParam.entrySet()) { for (Entry<Integer, Set<Annotation>> entry : indexToPathParam.asMap().entrySet()) {
for (Annotation key : entry.getValue()) { for (Annotation key : entry.getValue()) {
Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey()); Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
String paramKey = ((PayloadParam) key).value(); String paramKey = ((PayloadParam) key).value();

View File

@ -36,6 +36,7 @@ import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfP
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexesOfOptions; import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexesOfOptions;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.concurrent.ExecutionException;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.inject.Named; import javax.inject.Named;
@ -48,7 +49,7 @@ import org.jclouds.logging.Logger;
import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.Delegate;
import org.jclouds.rest.internal.RestAnnotationProcessor.MethodKey; 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.ImmutableSet;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.inject.Inject; import com.google.inject.Inject;
@ -63,7 +64,7 @@ import com.google.inject.Provides;
*/ */
@Singleton @Singleton
public class SeedAnnotationCache implements Function<Class<?>, Boolean> { public class SeedAnnotationCache extends CacheLoader<Class<?>, Boolean> {
@Resource @Resource
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
@ -82,7 +83,8 @@ public class SeedAnnotationCache implements Function<Class<?>, Boolean> {
constants.put(key, value); 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 for (Method method : difference(ImmutableSet.copyOf(declaring.getMethods()), ImmutableSet.copyOf(Object.class
.getMethods()))) { .getMethods()))) {
if (isHttpMethod(method) || method.isAnnotationPresent(Delegate.class)) { if (isHttpMethod(method) || method.isAnnotationPresent(Delegate.class)) {

View File

@ -20,11 +20,12 @@ package org.jclouds.util;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.Map; import java.util.concurrent.ExecutionException;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import com.google.common.base.Function; import com.google.common.cache.Cache;
import com.google.common.collect.MapMaker; 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 _7E_PATTERN = Pattern.compile("%7E");
public static final Pattern NEWLINE_PATTERN = Pattern.compile("\r?\n"); public static final Pattern NEWLINE_PATTERN = Pattern.compile("\r?\n");
public static final Pattern SLASH_PATTERN = Pattern.compile("[/]"); public static final Pattern SLASH_PATTERN = Pattern.compile("[/]");
public static final Pattern IP_PATTERN = Pattern public static final Pattern IP_PATTERN = Pattern.compile("b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).)"
.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");
+ "{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 LEADING_SLASHES = Pattern.compile("^[/]+");
public static final Pattern TRAILING_SLASHES = Pattern.compile("[/]*$"); public static final Pattern TRAILING_SLASHES = Pattern.compile("[/]*$");
public static final Pattern REST_CONTEXT_BUILDER = Pattern.compile("(.*ContextBuilder)<([^,]+), ?([^>]+)>"); public static final Pattern REST_CONTEXT_BUILDER = Pattern.compile("(.*ContextBuilder)<([^,]+), ?([^>]+)>");
public final static Map<Character, Pattern> CHAR_TO_ENCODED_PATTERN = new MapMaker() public final static Cache<Character, Pattern> CHAR_TO_ENCODED_PATTERN = CacheBuilder.newBuilder()
.makeComputingMap(new Function<Character, Pattern>() { .<Character, Pattern> build(new CacheLoader<Character, Pattern>() {
public Pattern apply(Character plain) { @Override
try { public Pattern load(Character plain) throws ExecutionException {
String encoded = URLEncoder.encode(plain + "", "UTF-8"); try {
return Pattern.compile(encoded); String encoded = URLEncoder.encode(plain + "", "UTF-8");
} catch (UnsupportedEncodingException e) { return Pattern.compile(encoded);
throw new IllegalStateException("Bad encoding on input: " + plain, e); } catch (UnsupportedEncodingException e) {
} throw new ExecutionException("Bad encoding on input: " + plain, e);
} }
}); }
});
public final static Map<Character, Pattern> CHAR_TO_PATTERN = new MapMaker() public final static Cache<Character, Pattern> CHAR_TO_PATTERN = CacheBuilder.newBuilder()
.makeComputingMap(new Function<Character, Pattern>() { .<Character, Pattern> build(new CacheLoader<Character, Pattern>() {
public Pattern apply(Character plain) { @Override
return Pattern.compile(plain + ""); public Pattern load(Character plain) {
} return Pattern.compile(plain + "");
}); }
});
public final static Map<String, Pattern> TOKEN_TO_PATTERN = new MapMaker() public final static Cache<String, Pattern> TOKEN_TO_PATTERN = CacheBuilder.newBuilder()
.makeComputingMap(new Function<String, Pattern>() { .<String, Pattern> build(new CacheLoader<String, Pattern>() {
public Pattern apply(String tokenValue) { @Override
return Pattern.compile("\\{" + tokenValue + "\\}"); public Pattern load(String tokenValue) {
} return Pattern.compile("\\{" + tokenValue + "\\}");
}); }
});
} }

View File

@ -37,6 +37,7 @@ import java.net.URLDecoder;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -70,6 +71,8 @@ public class Strings2 {
return returnVal; return returnVal;
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
throw new IllegalStateException("Bad encoding on input: " + in, 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<Entry<String, String>> tokenValues) { public static String replaceTokens(String value, Iterable<Entry<String, String>> tokenValues) {
for (Entry<String, String> tokenValue : tokenValues) { for (Entry<String, String> 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; return value;
} }
@ -107,7 +114,11 @@ public class Strings2 {
public static String replaceAll(String input, char match, String replacement) { public static String replaceAll(String input, char match, String replacement) {
if (input.indexOf(match) != -1) { 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; return input;
} }

View File

@ -88,8 +88,8 @@ public class Throwables2 {
throw (Exception) throwable; throw (Exception) throwable;
} }
} }
Throwables.throwCause(exception, true); Throwables.propagateIfPossible(exception.getCause(), Exception.class);
return exception; throw exception;
} }
public static <T> T propagateAuthorizationOrOriginalException(Exception e) { public static <T> T propagateAuthorizationOrOriginalException(Exception e) {

View File

@ -67,7 +67,7 @@ import com.google.common.collect.Sets;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = "performance", enabled = false, sequential = true, testName = "FuturesTransformPerformanceTest") @Test(groups = "performance", enabled = false, singleThreaded = true, testName = "FuturesTransformPerformanceTest")
public class FuturesTransformPerformanceTest { public class FuturesTransformPerformanceTest {
private static final int FUDGE = 5; private static final int FUDGE = 5;
private static final int COUNT = 100; private static final int COUNT = 100;
@ -175,7 +175,7 @@ public class FuturesTransformPerformanceTest {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
Map<String, Future<Long>> responses = newHashMap(); Map<String, Future<Long>> responses = newHashMap();
for (int i = 0; i < COUNT; i++) 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<Long, Long>() { new Function<Long, Long>() {
@Override @Override

View File

@ -25,7 +25,6 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -36,6 +35,10 @@ import org.jclouds.internal.ClassMethodArgs;
import org.testng.annotations.BeforeTest; import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test; 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.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
@ -162,8 +165,8 @@ public class SyncProxyTest {
@BeforeTest @BeforeTest
public void setUp() throws IllegalArgumentException, SecurityException, NoSuchMethodException { public void setUp() throws IllegalArgumentException, SecurityException, NoSuchMethodException {
sync = SyncProxy.proxy(Sync.class, new SyncProxy(Sync.class, new Async(), Cache<ClassMethodArgs, Object> cache = CacheBuilder.newBuilder().build(CacheLoader.from(Functions.<Object>constant(null)));
new ConcurrentHashMap<ClassMethodArgs, Object>(), ImmutableMap.<Class<?>, Class<?>> of())); sync = SyncProxy.proxy(Sync.class, new SyncProxy(Sync.class, new Async(),cache, ImmutableMap.<Class<?>, Class<?>> of()));
// just to warm up // just to warm up
sync.string(); sync.string();
} }
@ -225,8 +228,9 @@ public class SyncProxyTest {
@Test(expectedExceptions = IllegalArgumentException.class) @Test(expectedExceptions = IllegalArgumentException.class)
public void testWrongTypedException() throws IllegalArgumentException, SecurityException, NoSuchMethodException, public void testWrongTypedException() throws IllegalArgumentException, SecurityException, NoSuchMethodException,
IOException { IOException {
Cache<ClassMethodArgs, Object> cache = CacheBuilder.newBuilder().build(CacheLoader.from(Functions.<Object>constant(null)));
SyncProxy.proxy(SyncWrongException.class, new SyncProxy(SyncWrongException.class, new Async(), SyncProxy.proxy(SyncWrongException.class, new SyncProxy(SyncWrongException.class, new Async(),
new ConcurrentHashMap<ClassMethodArgs, Object>(), ImmutableMap.<Class<?>, Class<?>> of())); cache, ImmutableMap.<Class<?>, Class<?>> of()));
} }
private static interface SyncNoTimeOut { private static interface SyncNoTimeOut {
@ -243,8 +247,9 @@ public class SyncProxyTest {
@Test(expectedExceptions = IllegalArgumentException.class) @Test(expectedExceptions = IllegalArgumentException.class)
public void testNoTimeOutException() throws IllegalArgumentException, SecurityException, NoSuchMethodException, public void testNoTimeOutException() throws IllegalArgumentException, SecurityException, NoSuchMethodException,
IOException { IOException {
Cache<ClassMethodArgs, Object> cache = CacheBuilder.newBuilder().build(CacheLoader.from(Functions.<Object>constant(null)));
SyncProxy.proxy(SyncNoTimeOut.class, new SyncProxy(SyncNoTimeOut.class, new Async(), SyncProxy.proxy(SyncNoTimeOut.class, new SyncProxy(SyncNoTimeOut.class, new Async(),
new ConcurrentHashMap<ClassMethodArgs, Object>(), ImmutableMap.<Class<?>, Class<?>> of())); cache, ImmutableMap.<Class<?>, Class<?>> of()));
} }
} }

View File

@ -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<String, String> cache = CacheBuilder.newBuilder().build(new CacheLoader<String, String>() {
@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);
}
}

View File

@ -2157,7 +2157,7 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
} }
@Test @Test
public void testOneHeader() throws SecurityException, NoSuchMethodException { public void testOneHeader() throws SecurityException, NoSuchMethodException, ExecutionException {
Method method = TestHeaders.class.getMethod("oneHeader", String.class); Method method = TestHeaders.class.getMethod("oneHeader", String.class);
Multimap<String, String> headers = factory(TestHeaders.class).buildHeaders( Multimap<String, String> headers = factory(TestHeaders.class).buildHeaders(
ImmutableMultimap.<String, String> of().entries(), method, "robot"); ImmutableMultimap.<String, String> of().entries(), method, "robot");
@ -2166,7 +2166,7 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
} }
@Test @Test
public void testOneIntHeader() throws SecurityException, NoSuchMethodException { public void testOneIntHeader() throws SecurityException, NoSuchMethodException, ExecutionException {
Method method = TestHeaders.class.getMethod("oneIntHeader", int.class); Method method = TestHeaders.class.getMethod("oneIntHeader", int.class);
Multimap<String, String> headers = factory(TestHeaders.class).buildHeaders( Multimap<String, String> headers = factory(TestHeaders.class).buildHeaders(
ImmutableMultimap.<String, String> of().entries(), method, 1); ImmutableMultimap.<String, String> of().entries(), method, 1);
@ -2175,7 +2175,7 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
} }
@Test @Test
public void testTwoDifferentHeaders() throws SecurityException, NoSuchMethodException { public void testTwoDifferentHeaders() throws SecurityException, NoSuchMethodException, ExecutionException {
Method method = TestHeaders.class.getMethod("twoDifferentHeaders", String.class, String.class); Method method = TestHeaders.class.getMethod("twoDifferentHeaders", String.class, String.class);
Multimap<String, String> headers = factory(TestHeaders.class).buildHeaders( Multimap<String, String> headers = factory(TestHeaders.class).buildHeaders(
ImmutableMultimap.<String, String> of().entries(), method, "robot", "egg"); ImmutableMultimap.<String, String> of().entries(), method, "robot", "egg");
@ -2185,7 +2185,7 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
} }
@Test @Test
public void testTwoSameHeaders() throws SecurityException, NoSuchMethodException { public void testTwoSameHeaders() throws SecurityException, NoSuchMethodException, ExecutionException {
Method method = TestHeaders.class.getMethod("twoSameHeaders", String.class, String.class); Method method = TestHeaders.class.getMethod("twoSameHeaders", String.class, String.class);
Multimap<String, String> headers = factory(TestHeaders.class).buildHeaders( Multimap<String, String> headers = factory(TestHeaders.class).buildHeaders(
ImmutableMultimap.<String, String> of().entries(), method, "robot", "egg"); ImmutableMultimap.<String, String> of().entries(), method, "robot", "egg");
@ -2228,7 +2228,7 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
@SuppressWarnings("static-access") @SuppressWarnings("static-access")
@Test @Test
public void testOneEndpointParam() throws SecurityException, NoSuchMethodException { public void testOneEndpointParam() throws SecurityException, NoSuchMethodException, ExecutionException {
Method method = TestEndpointParams.class.getMethod("oneEndpointParam", String.class); Method method = TestEndpointParams.class.getMethod("oneEndpointParam", String.class);
URI uri = factory(TestEndpointParams.class).getEndpointInParametersOrNull(method, new Object[] { "robot" }, URI uri = factory(TestEndpointParams.class).getEndpointInParametersOrNull(method, new Object[] { "robot" },
injector); injector);
@ -2238,7 +2238,7 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
@SuppressWarnings("static-access") @SuppressWarnings("static-access")
@Test @Test
public void testTwoDifferentEndpointParams() throws SecurityException, NoSuchMethodException { public void testTwoDifferentEndpointParams() throws SecurityException, NoSuchMethodException, ExecutionException {
Method method = TestEndpointParams.class.getMethod("twoEndpointParams", String.class, String.class); Method method = TestEndpointParams.class.getMethod("twoEndpointParams", String.class, String.class);
URI uri = factory(TestEndpointParams.class).getEndpointInParametersOrNull(method, URI uri = factory(TestEndpointParams.class).getEndpointInParametersOrNull(method,
new Object[] { "robot", "egg" }, injector); new Object[] { "robot", "egg" }, injector);

View File

@ -64,6 +64,7 @@ import org.jclouds.util.Preconditions2;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.cache.Cache;
/** /**
* @author Adrian Cole * @author Adrian Cole
@ -71,7 +72,7 @@ import com.google.common.base.Supplier;
@Singleton @Singleton
public class AWSEC2ComputeService extends EC2ComputeService { public class AWSEC2ComputeService extends EC2ComputeService {
private final Map<RegionAndName, String> placementGroupMap; private final Cache<RegionAndName, String> placementGroupMap;
private final Predicate<PlacementGroup> placementGroupDeleted; private final Predicate<PlacementGroup> placementGroupDeleted;
private final AWSEC2Client ec2Client; private final AWSEC2Client ec2Client;
@ -90,8 +91,8 @@ public class AWSEC2ComputeService extends EC2ComputeService {
RunScriptOnNode.Factory runScriptOnNodeFactory, InitAdminAccess initAdminAccess, RunScriptOnNode.Factory runScriptOnNodeFactory, InitAdminAccess initAdminAccess,
PersistNodeCredentials persistNodeCredentials, Timeouts timeouts, PersistNodeCredentials persistNodeCredentials, Timeouts timeouts,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, AWSEC2Client ec2Client, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, AWSEC2Client ec2Client,
Map<RegionAndName, KeyPair> credentialsMap, @Named("SECURITY") Map<RegionAndName, String> securityGroupMap, Cache<RegionAndName, KeyPair> credentialsMap, @Named("SECURITY") Cache<RegionAndName, String> securityGroupMap,
@Named("PLACEMENT") Map<RegionAndName, String> placementGroupMap, @Named("PLACEMENT") Cache<RegionAndName, String> placementGroupMap,
@Named("DELETED") Predicate<PlacementGroup> placementGroupDeleted) { @Named("DELETED") Predicate<PlacementGroup> placementGroupDeleted) {
super(context, credentialStore, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy, super(context, credentialStore, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy,
runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, stopNodeStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy, stopNodeStrategy,
@ -117,7 +118,7 @@ public class AWSEC2ComputeService extends EC2ComputeService {
checkState( checkState(
placementGroupDeleted.apply(new PlacementGroup(region, placementGroup, "cluster", State.PENDING)), placementGroupDeleted.apply(new PlacementGroup(region, placementGroup, "cluster", State.PENDING)),
String.format("placementGroup region(%s) name(%s) failed to delete", region, placementGroup)); 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); logger.debug("<< deleted placementGroup(%s)", placementGroup);
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
logger.debug("<< inUse placementGroup(%s)", placementGroup); logger.debug("<< inUse placementGroup(%s)", placementGroup);

View File

@ -18,7 +18,6 @@
*/ */
package org.jclouds.aws.ec2.compute; package org.jclouds.aws.ec2.compute;
import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
@ -35,6 +34,7 @@ import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl; import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl;
import com.google.common.base.Supplier; 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<Set<? extends Location>> locations, protected AWSEC2TemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations,
@Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> sizes, @Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> sizes,
Supplier<Location> defaultLocation, @Named("DEFAULT") Provider<TemplateOptions> optionsProvider, Supplier<Location> defaultLocation, @Named("DEFAULT") Provider<TemplateOptions> optionsProvider,
@Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider, Map<RegionAndName, Image> imageMap) { @Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider, Supplier<Cache<RegionAndName, ? extends Image>> imageMap) {
super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider, imageMap); super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider, imageMap);
} }

View File

@ -21,8 +21,6 @@ package org.jclouds.aws.ec2.compute.config;
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL; import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
import static org.jclouds.compute.domain.OsFamily.AMZN_LINUX; import static org.jclouds.compute.domain.OsFamily.AMZN_LINUX;
import java.util.Map;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
@ -56,6 +54,7 @@ import org.jclouds.ec2.compute.suppliers.EC2HardwareSupplier;
import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier; import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.cache.Cache;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Provides; import com.google.inject.Provides;
@ -89,12 +88,12 @@ public class AWSEC2ComputeServiceContextModule extends BaseComputeServiceContext
@Provides @Provides
@Singleton @Singleton
protected Supplier<Map<RegionAndName, ? extends Image>> provideRegionAndNameToImageSupplierCache( protected Supplier<Cache<RegionAndName, ? extends Image>> provideRegionAndNameToImageSupplierCache(
@Named(PROPERTY_SESSION_INTERVAL) long seconds, final AWSRegionAndNameToImageSupplier supplier) { @Named(PROPERTY_SESSION_INTERVAL) long seconds, final AWSRegionAndNameToImageSupplier supplier) {
return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<RegionAndName, ? extends Image>>( return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Cache<RegionAndName, ? extends Image>>(
authException, seconds, new Supplier<Map<RegionAndName, ? extends Image>>() { authException, seconds, new Supplier<Cache<RegionAndName, ? extends Image>>() {
@Override @Override
public Map<RegionAndName, ? extends Image> get() { public Cache<RegionAndName, ? extends Image> get() {
return supplier.get(); return supplier.get();
} }
}); });

View File

@ -18,9 +18,6 @@
*/ */
package org.jclouds.aws.ec2.compute.config; 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.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -47,7 +44,6 @@ import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.ec2.compute.config.EC2ComputeServiceDependenciesModule; import org.jclouds.ec2.compute.config.EC2ComputeServiceDependenciesModule;
import org.jclouds.ec2.compute.domain.RegionAndName; 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.CreateSecurityGroupIfNeeded;
import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair; import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair;
import org.jclouds.ec2.compute.functions.CredentialsForInstance; 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.Function;
import com.google.common.base.Predicate; 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.common.collect.Sets;
import com.google.inject.Provides; import com.google.inject.Provides;
import com.google.inject.Scopes; import com.google.inject.Scopes;
@ -80,15 +79,15 @@ public class AWSEC2ComputeServiceDependenciesModule extends EC2ComputeServiceDep
bind(ComputeService.class).to(AWSEC2ComputeService.class); bind(ComputeService.class).to(AWSEC2ComputeService.class);
bind(new TypeLiteral<Function<RunningInstance, NodeMetadata>>() { bind(new TypeLiteral<Function<RunningInstance, NodeMetadata>>() {
}).to(RunningInstanceToNodeMetadata.class); }).to(RunningInstanceToNodeMetadata.class);
bind(new TypeLiteral<Function<RunningInstance, Credentials>>() { bind(new TypeLiteral<CacheLoader<RunningInstance, Credentials>>() {
}).to(CredentialsForInstance.class); }).to(CredentialsForInstance.class);
bind(new TypeLiteral<Function<RegionNameAndIngressRules, String>>() { bind(new TypeLiteral<CacheLoader<RegionAndName, String>>() {
}).to(CreateSecurityGroupIfNeeded.class); }).to(CreateSecurityGroupIfNeeded.class);
bind(new TypeLiteral<Function<RegionAndName, KeyPair>>() { bind(new TypeLiteral<CacheLoader<RegionAndName, KeyPair>>() {
}).to(CreateUniqueKeyPair.class); }).to(CreateUniqueKeyPair.class);
bind(new TypeLiteral<Function<RegionNameAndPublicKeyMaterial, KeyPair>>() { bind(new TypeLiteral<Function<RegionNameAndPublicKeyMaterial, KeyPair>>() {
}).to(ImportOrReturnExistingKeypair.class); }).to(ImportOrReturnExistingKeypair.class);
bind(new TypeLiteral<Function<RegionAndName, Image>>() { bind(new TypeLiteral<CacheLoader<RegionAndName, Image>>() {
}).to(RegionAndIdToImage.class); }).to(RegionAndIdToImage.class);
bind(new TypeLiteral<ComputeServiceContext>() { bind(new TypeLiteral<ComputeServiceContext>() {
}).to(new TypeLiteral<ComputeServiceContextImpl<AWSEC2Client, AWSEC2AsyncClient>>() { }).to(new TypeLiteral<ComputeServiceContextImpl<AWSEC2Client, AWSEC2AsyncClient>>() {
@ -116,10 +115,8 @@ public class AWSEC2ComputeServiceDependenciesModule extends EC2ComputeServiceDep
@Provides @Provides
@Singleton @Singleton
@Named("PLACEMENT") @Named("PLACEMENT")
protected Map<RegionAndName, String> placementGroupMap(CreateSecurityGroupIfNeeded in) { protected Cache<RegionAndName, String> placementGroupMap(CreateSecurityGroupIfNeeded in) {
// doesn't seem to clear when someone issues remove(key) return CacheBuilder.newBuilder().build(in);
// return new MapMaker().makeComputingMap(in);
return newLinkedHashMap();
} }
@Provides @Provides

View File

@ -38,6 +38,7 @@ import org.jclouds.ec2.domain.InstanceState;
import org.jclouds.ec2.domain.RunningInstance; import org.jclouds.ec2.domain.RunningInstance;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.cache.Cache;
/** /**
* @author Adrian Cole * @author Adrian Cole
@ -47,9 +48,9 @@ public class AWSRunningInstanceToNodeMetadata extends RunningInstanceToNodeMetad
@Inject @Inject
protected AWSRunningInstanceToNodeMetadata(Map<InstanceState, NodeState> instanceToNodeState, protected AWSRunningInstanceToNodeMetadata(Map<InstanceState, NodeState> instanceToNodeState,
Map<String, Credentials> credentialStore, Map<RegionAndName, Image> instanceToImage, Map<String, Credentials> credentialStore, Supplier<Cache<RegionAndName, ? extends Image>> imageMap,
@Memoized Supplier<Set<? extends Location>> locations, @Memoized Supplier<Set<? extends Hardware>> hardware) { @Memoized Supplier<Set<? extends Location>> locations, @Memoized Supplier<Set<? extends Hardware>> hardware) {
super(instanceToNodeState, credentialStore, instanceToImage, locations, hardware); super(instanceToNodeState, credentialStore, imageMap, locations, hardware);
} }
@Override @Override

View File

@ -49,6 +49,7 @@ import org.jclouds.logging.Logger;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.cache.Cache;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
/** /**
@ -73,7 +74,7 @@ public class AWSEC2CreateNodesInGroupThenAddToSet extends EC2CreateNodesInGroupT
CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions createKeyPairAndSecurityGroupsAsNeededAndReturncustomize,
AWSEC2InstancePresent instancePresent, AWSEC2InstancePresent instancePresent,
Function<RunningInstance, NodeMetadata> runningInstanceToNodeMetadata, Function<RunningInstance, NodeMetadata> runningInstanceToNodeMetadata,
Function<RunningInstance, Credentials> instanceToCredentials, Map<String, Credentials> credentialStore, Cache<RunningInstance, Credentials> instanceToCredentials, Map<String, Credentials> credentialStore,
ComputeUtils utils, SpotInstanceRequestToAWSRunningInstance spotConverter) { ComputeUtils utils, SpotInstanceRequestToAWSRunningInstance spotConverter) {
super(client, templateBuilderProvider, createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, instancePresent, super(client, templateBuilderProvider, createKeyPairAndSecurityGroupsAsNeededAndReturncustomize, instancePresent,
runningInstanceToNodeMetadata, instanceToCredentials, credentialStore, utils); runningInstanceToNodeMetadata, instanceToCredentials, credentialStore, utils);

View File

@ -37,7 +37,6 @@ import org.jclouds.compute.domain.Template;
import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.ec2.compute.domain.RegionAndName; 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.options.EC2TemplateOptions;
import org.jclouds.ec2.compute.strategy.CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions; import org.jclouds.ec2.compute.strategy.CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions;
import org.jclouds.ec2.domain.KeyPair; 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.annotations.VisibleForTesting;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.cache.Cache;
/** /**
* *
@ -59,21 +59,21 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions
@Named(ComputeServiceConstants.COMPUTE_LOGGER) @Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
@VisibleForTesting @VisibleForTesting
final Map<RegionAndName, String> placementGroupMap; final Cache<RegionAndName, String> placementGroupMap;
@VisibleForTesting @VisibleForTesting
final CreatePlacementGroupIfNeeded createPlacementGroupIfNeeded; final CreatePlacementGroupIfNeeded createPlacementGroupIfNeeded;
@VisibleForTesting @VisibleForTesting
final Function<RegionNameAndPublicKeyMaterial, KeyPair> importExistingKeyPair; final Function<RegionNameAndPublicKeyMaterial, KeyPair> importExistingKeyPair;
@Inject @Inject
public CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions( public CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions(Map<RegionAndName, KeyPair> knownKeys,
Map<RegionAndName, KeyPair> credentialsMap, @Named("SECURITY") Map<RegionAndName, String> securityGroupMap, Cache<RegionAndName, KeyPair> credentialsMap,
@Named("PLACEMENT") Map<RegionAndName, String> placementGroupMap, @Named("SECURITY") Cache<RegionAndName, String> securityGroupMap,
Function<RegionAndName, KeyPair> createUniqueKeyPair, Provider<RunInstancesOptions> optionsProvider,
Function<RegionNameAndIngressRules, String> createSecurityGroupIfNeeded, @Named("PLACEMENT") Cache<RegionAndName, String> placementGroupMap,
Provider<RunInstancesOptions> optionsProvider, CreatePlacementGroupIfNeeded createPlacementGroupIfNeeded, CreatePlacementGroupIfNeeded createPlacementGroupIfNeeded,
Function<RegionNameAndPublicKeyMaterial, KeyPair> importExistingKeyPair) { Function<RegionNameAndPublicKeyMaterial, KeyPair> importExistingKeyPair) {
super(credentialsMap, securityGroupMap, createUniqueKeyPair, createSecurityGroupIfNeeded, optionsProvider); super(knownKeys, credentialsMap, securityGroupMap, optionsProvider);
this.placementGroupMap = placementGroupMap; this.placementGroupMap = placementGroupMap;
this.createPlacementGroupIfNeeded = createPlacementGroupIfNeeded; this.createPlacementGroupIfNeeded = createPlacementGroupIfNeeded;
this.importExistingKeyPair = importExistingKeyPair; this.importExistingKeyPair = importExistingKeyPair;
@ -110,9 +110,8 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions
// http://docs.amazonwebservices.com/AWSEC2/latest/UserGuide/index.html?using_cluster_computing.html // http://docs.amazonwebservices.com/AWSEC2/latest/UserGuide/index.html?using_cluster_computing.html
placementGroupName = String.format("jclouds#%s#%s", group, region); placementGroupName = String.format("jclouds#%s#%s", group, region);
RegionAndName regionAndName = new RegionAndName(region, placementGroupName); RegionAndName regionAndName = new RegionAndName(region, placementGroupName);
if (!placementGroupMap.containsKey(regionAndName)) { // make this entry as needed
placementGroupMap.put(regionAndName, createPlacementGroupIfNeeded.apply(regionAndName)); placementGroupMap.getUnchecked(regionAndName);
}
} }
return placementGroupName; return placementGroupName;
} }
@ -120,7 +119,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions
@Override @Override
protected String createOrImportKeyPair(String region, String group, TemplateOptions options) { protected String createOrImportKeyPair(String region, String group, TemplateOptions options) {
RegionAndName key = new RegionAndName(region, "jclouds#" + group); RegionAndName key = new RegionAndName(region, "jclouds#" + group);
KeyPair pair = credentialsMap.get(key); KeyPair pair = knownKeys.get(key);
if (pair != null) if (pair != null)
return pair.getKeyName(); return pair.getKeyName();
if (and(hasPublicKeyMaterial, or(doesntNeedSshAfterImportingPublicKey, hasLoginCredential)).apply(options)) { if (and(hasPublicKeyMaterial, or(doesntNeedSshAfterImportingPublicKey, hasLoginCredential)).apply(options)) {
@ -128,12 +127,12 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions
options.dontAuthorizePublicKey(); options.dontAuthorizePublicKey();
if (hasLoginCredential.apply(options)) if (hasLoginCredential.apply(options))
pair = pair.toBuilder().keyMaterial(options.getOverridingCredentials().credential).build(); pair = pair.toBuilder().keyMaterial(options.getOverridingCredentials().credential).build();
credentialsMap.put(key, pair); knownKeys.put(key, pair);
} else { } else {
if (hasPublicKeyMaterial.apply(options)) { if (hasPublicKeyMaterial.apply(options)) {
logger.warn("to avoid creating temporary keys in aws-ec2, use templateOption overrideLoginCredentialWith(id_rsa)"); 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(); return pair.getKeyName();
} }

View File

@ -47,6 +47,9 @@ import com.google.common.base.Function;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.base.Throwables; 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.ImmutableMultimap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
@ -58,12 +61,13 @@ import com.google.common.util.concurrent.Futures;
* @author Adrian Cole * @author Adrian Cole
*/ */
@Singleton @Singleton
public class AWSRegionAndNameToImageSupplier implements Supplier<Map<RegionAndName, ? extends Image>> { public class AWSRegionAndNameToImageSupplier implements Supplier<Cache<RegionAndName, ? extends Image>> {
@Resource @Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER) @Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
private final Map<RegionAndName, Image> images; private final Map<RegionAndName, Image> knownImages;
private final CacheLoader<RegionAndName, Image> regionAndIdToImage;
private final Set<String> clusterComputeIds; private final Set<String> clusterComputeIds;
private final CallForImages.Factory factory; private final CallForImages.Factory factory;
private final ExecutorService executor; private final ExecutorService executor;
@ -73,10 +77,11 @@ public class AWSRegionAndNameToImageSupplier implements Supplier<Map<RegionAndNa
private final Iterable<String> clusterRegions; private final Iterable<String> clusterRegions;
private final String ccAmiQuery; private final String ccAmiQuery;
@Inject @Inject
protected AWSRegionAndNameToImageSupplier(@Region Set<String> regions, protected AWSRegionAndNameToImageSupplier(@Region Set<String> regions,
@Named(PROPERTY_EC2_AMI_QUERY) String amiQuery, @Named(PROPERTY_EC2_CC_REGIONS) String clusterRegions, @Named(PROPERTY_EC2_AMI_QUERY) String amiQuery, @Named(PROPERTY_EC2_CC_REGIONS) String clusterRegions,
@Named(PROPERTY_EC2_CC_AMI_QUERY) String ccAmiQuery, Map<RegionAndName, Image> images, @Named(PROPERTY_EC2_CC_AMI_QUERY) String ccAmiQuery, Map<RegionAndName, Image> knownImages, CacheLoader<RegionAndName, Image> regionAndIdToImage,
CallForImages.Factory factory, @ClusterCompute Set<String> clusterComputeIds, CallForImages.Factory factory, @ClusterCompute Set<String> clusterComputeIds,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) { @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) {
this.factory = factory; this.factory = factory;
@ -84,13 +89,14 @@ public class AWSRegionAndNameToImageSupplier implements Supplier<Map<RegionAndNa
this.amiQuery = amiQuery; this.amiQuery = amiQuery;
this.clusterRegions = Splitter.on(',').split(clusterRegions); this.clusterRegions = Splitter.on(',').split(clusterRegions);
this.ccAmiQuery = ccAmiQuery; this.ccAmiQuery = ccAmiQuery;
this.images = images; this.knownImages = knownImages;
this.regionAndIdToImage=regionAndIdToImage;
this.clusterComputeIds = clusterComputeIds; this.clusterComputeIds = clusterComputeIds;
this.executor = executor; this.executor = executor;
} }
@Override @Override
public Map<RegionAndName, ? extends Image> get() { public Cache<RegionAndName, ? extends Image> get() {
Future<Iterable<Image>> normalImages = images(regions, amiQuery, PROPERTY_EC2_AMI_QUERY); Future<Iterable<Image>> normalImages = images(regions, amiQuery, PROPERTY_EC2_AMI_QUERY);
ImmutableSet<Image> clusterImages; ImmutableSet<Image> clusterImages;
try { try {
@ -115,8 +121,8 @@ public class AWSRegionAndNameToImageSupplier implements Supplier<Map<RegionAndNa
Throwables.propagate(e); Throwables.propagate(e);
return null; return null;
} }
knownImages.clear();
images.putAll(uniqueIndex(parsedImages, new Function<Image, RegionAndName>() { knownImages.putAll(uniqueIndex(parsedImages, new Function<Image, RegionAndName>() {
@Override @Override
public RegionAndName apply(Image from) { public RegionAndName apply(Image from) {
@ -124,7 +130,13 @@ public class AWSRegionAndNameToImageSupplier implements Supplier<Map<RegionAndNa
} }
})); }));
return images; logger.debug("<< images(%d)", knownImages.size());
Cache<RegionAndName, Image> cache = CacheBuilder.newBuilder().build(regionAndIdToImage);
// seed the cache
for (RegionAndName image : knownImages.keySet()){
cache.getUnchecked(image);
}
return cache;
} }
private Future<Iterable<Image>> images(Iterable<String> regions, String query, String tag) { private Future<Iterable<Image>> images(Iterable<String> regions, String query, String tag) {

View File

@ -88,7 +88,7 @@ public class AWSEC2TemplateBuilderLiveTest extends BaseTemplateBuilderLiveTest {
.build(); .build();
assert (template.getImage().getProviderId().startsWith("ami-")) : template; 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().is64Bit(), false);
assertEquals(template.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU); assertEquals(template.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU);
assertEquals(template.getImage().getUserMetadata().get("rootDeviceType"), "instance-store"); assertEquals(template.getImage().getUserMetadata().get("rootDeviceType"), "instance-store");
@ -107,7 +107,7 @@ public class AWSEC2TemplateBuilderLiveTest extends BaseTemplateBuilderLiveTest {
.build(); .build();
assert (template.getImage().getProviderId().startsWith("ami-")) : template; 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().is64Bit(), false);
assertEquals(template.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU); assertEquals(template.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU);
assertEquals(template.getImage().getUserMetadata().get("rootDeviceType"), "instance-store"); assertEquals(template.getImage().getUserMetadata().get("rootDeviceType"), "instance-store");
@ -183,7 +183,7 @@ public class AWSEC2TemplateBuilderLiveTest extends BaseTemplateBuilderLiveTest {
fastestTemplate = context.getComputeService().templateBuilder().fastest().build(); fastestTemplate = context.getComputeService().templateBuilder().fastest().build();
assert (fastestTemplate.getImage().getProviderId().startsWith("ami-")) : fastestTemplate; assert (fastestTemplate.getImage().getProviderId().startsWith("ami-")) : fastestTemplate;
assertEquals(fastestTemplate.getHardware().getProviderId(), InstanceType.CC1_4XLARGE); 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().is64Bit(), true);
assertEquals(fastestTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU); assertEquals(fastestTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU);
assertEquals(fastestTemplate.getImage().getUserMetadata().get("rootDeviceType"), "ebs"); assertEquals(fastestTemplate.getImage().getUserMetadata().get("rootDeviceType"), "ebs");

View File

@ -45,6 +45,10 @@ import org.testng.annotations.Test;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Supplier; 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.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
@ -118,7 +122,7 @@ public class AWSRunningInstanceToNodeMetadataTest {
Map<String, Credentials> credentialStore) { Map<String, Credentials> credentialStore) {
Map<InstanceState, NodeState> instanceToNodeState = EC2ComputeServiceDependenciesModule.instanceToNodeState; Map<InstanceState, NodeState> instanceToNodeState = EC2ComputeServiceDependenciesModule.instanceToNodeState;
Map<RegionAndName, Image> instanceToImage = Maps.uniqueIndex(images, new Function<Image, RegionAndName>() { final ImmutableMap<RegionAndName, Image> backing = Maps.uniqueIndex(images, new Function<Image, RegionAndName>() {
@Override @Override
public RegionAndName apply(Image from) { public RegionAndName apply(Image from) {
@ -127,12 +131,22 @@ public class AWSRunningInstanceToNodeMetadataTest {
}); });
Cache<RegionAndName, Image> instanceToImage = CacheBuilder.newBuilder().build(new CacheLoader<RegionAndName, Image> (){
@Override
public Image load(RegionAndName key) throws Exception {
return backing.get(key);
}
});
return createNodeParser(hardware, locations, credentialStore, instanceToNodeState, instanceToImage); return createNodeParser(hardware, locations, credentialStore, instanceToNodeState, instanceToImage);
} }
private AWSRunningInstanceToNodeMetadata createNodeParser(final ImmutableSet<Hardware> hardware, private AWSRunningInstanceToNodeMetadata createNodeParser(final ImmutableSet<Hardware> hardware,
final ImmutableSet<Location> locations, Map<String, Credentials> credentialStore, final ImmutableSet<Location> locations, Map<String, Credentials> credentialStore,
Map<InstanceState, NodeState> instanceToNodeState, Map<RegionAndName, Image> instanceToImage) { Map<InstanceState, NodeState> instanceToNodeState, Cache<RegionAndName, ? extends Image> instanceToImage) {
Supplier<Set<? extends Location>> locationSupplier = new Supplier<Set<? extends Location>>() { Supplier<Set<? extends Location>> locationSupplier = new Supplier<Set<? extends Location>>() {
@Override @Override
@ -150,7 +164,8 @@ public class AWSRunningInstanceToNodeMetadataTest {
}; };
AWSRunningInstanceToNodeMetadata parser = new AWSRunningInstanceToNodeMetadata(instanceToNodeState, AWSRunningInstanceToNodeMetadata parser = new AWSRunningInstanceToNodeMetadata(instanceToNodeState,
credentialStore, instanceToImage, locationSupplier, hardwareSupplier); credentialStore, Suppliers.<Cache<RegionAndName, ? extends Image>> ofInstance(instanceToImage),
locationSupplier, hardwareSupplier);
return parser; return parser;
} }

View File

@ -54,13 +54,14 @@ import org.jclouds.scriptbuilder.domain.Statements;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = "unit") @Test(groups = "unit", singleThreaded = true, testName = "CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest")
public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest { public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
private static final Provider<RunInstancesOptions> OPTIONS_PROVIDER = new javax.inject.Provider<RunInstancesOptions>() { private static final Provider<RunInstancesOptions> OPTIONS_PROVIDER = new javax.inject.Provider<RunInstancesOptions>() {
@ -385,7 +386,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
} }
@Test(expectedExceptions = IllegalStateException.class) @Test(expectedExceptions = IllegalArgumentException.class)
public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_reusesKeyWhenToldToWithRunScriptButNoCredentials() { public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_reusesKeyWhenToldToWithRunScriptButNoCredentials() {
// setup constants // setup constants
String region = Region.AP_SOUTHEAST_1; String region = Region.AP_SOUTHEAST_1;
@ -401,7 +402,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.getKeyPair()).andReturn(userSuppliedKeyPair);
expect(options.getOverridingCredentials()).andReturn(null); expect(options.getOverridingCredentials()).andReturn(null);
expect(options.getRunScript()).andReturn(Statements.exec("echo foo")); 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 mocks
replay(options); replay(options);
@ -432,7 +433,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.getKeyPair()).andReturn(userSuppliedKeyPair);
expect(options.getOverridingCredentials()).andReturn(null); expect(options.getOverridingCredentials()).andReturn(null);
expect(options.getRunScript()).andReturn(Statements.exec("echo foo")); 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 mocks
replay(options); replay(options);
@ -463,11 +464,13 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.getKeyPair()).andReturn(userSuppliedKeyPair);
expect(options.getOverridingCredentials()).andReturn(new Credentials(null, "MyRsa")).atLeastOnce(); expect(options.getOverridingCredentials()).andReturn(new Credentials(null, "MyRsa")).atLeastOnce();
expect( expect(
strategy.credentialsMap.put(new RegionAndName(region, userSuppliedKeyPair), KeyPair.builder() strategy.knownKeys.put(
.region(region).keyName(userSuppliedKeyPair).keyFingerprint("//TODO").keyMaterial("MyRsa").build())) new RegionAndName(region, tag),
.andReturn(null); 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(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 mocks
replay(options); replay(options);
@ -483,6 +486,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
verifyStrategy(strategy); verifyStrategy(strategy);
} }
@Test(enabled = false)
public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_importsKeyPairAndUnsetsTemplateInstructionWhenPublicKeySuppliedAndAddsCredentialToMapWhenOverridingCredsAreSet() { public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_importsKeyPairAndUnsetsTemplateInstructionWhenPublicKeySuppliedAndAddsCredentialToMapWhenOverridingCredsAreSet() {
// setup constants // setup constants
String region = Region.AP_SOUTHEAST_1; String region = Region.AP_SOUTHEAST_1;
@ -496,18 +500,19 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
KeyPair keyPair = new KeyPair(region, "jclouds#" + group, "fingerprint", null); KeyPair keyPair = new KeyPair(region, "jclouds#" + group, "fingerprint", null);
// setup expectations // setup expectations
expect(strategy.knownKeys.get(new RegionAndName(region, "jclouds#" + group))).andReturn(null);
expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.getKeyPair()).andReturn(userSuppliedKeyPair);
expect(options.shouldAutomaticallyCreateKeyPair()).andReturn(shouldAutomaticallyCreateKeyPair); 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(options.getPublicKey()).andReturn("ssh-rsa").times(2);
expect(strategy.importExistingKeyPair.apply(new RegionNameAndPublicKeyMaterial(region, group, "ssh-rsa"))) expect(strategy.importExistingKeyPair.apply(new RegionNameAndPublicKeyMaterial(region, group, "ssh-rsa")))
.andReturn(keyPair); .andReturn(keyPair);
expect(options.dontAuthorizePublicKey()).andReturn(options);
expect(options.getOverridingCredentials()).andReturn(new Credentials("foo", "bar")).times(3); expect(options.getOverridingCredentials()).andReturn(new Credentials("foo", "bar")).times(3);
expect(options.getRunScript()).andReturn(null).times(2); expect(options.getRunScript()).andReturn(null).times(2);
expect(options.getPrivateKey()).andReturn(null); expect(options.getPrivateKey()).andReturn(null);
expect(options.dontAuthorizePublicKey()).andReturn(options);
expect( expect(
strategy.credentialsMap.put(new RegionAndName(region, "jclouds#" + group), strategy.knownKeys.put(new RegionAndName(region, "jclouds#" + group),
keyPair.toBuilder().keyMaterial("bar").build())).andReturn(null); keyPair.toBuilder().keyMaterial("bar").build())).andReturn(null);
// replay mocks // replay mocks
@ -522,6 +527,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
verifyStrategy(strategy); verifyStrategy(strategy);
} }
@Test(enabled = false)
public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_importsKeyPairAndUnsetsTemplateInstructionWhenPublicKeySupplied() { public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_importsKeyPairAndUnsetsTemplateInstructionWhenPublicKeySupplied() {
// setup constants // setup constants
String region = Region.AP_SOUTHEAST_1; String region = Region.AP_SOUTHEAST_1;
@ -535,9 +541,10 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
KeyPair keyPair = new KeyPair(region, "jclouds#" + group, "fingerprint", null); KeyPair keyPair = new KeyPair(region, "jclouds#" + group, "fingerprint", null);
// setup expectations // setup expectations
expect(strategy.knownKeys.get(new RegionAndName(region, "jclouds#" + group))).andReturn(null);
expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.getKeyPair()).andReturn(userSuppliedKeyPair);
expect(options.shouldAutomaticallyCreateKeyPair()).andReturn(shouldAutomaticallyCreateKeyPair); 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(options.getPublicKey()).andReturn("ssh-rsa").times(2);
expect(strategy.importExistingKeyPair.apply(new RegionNameAndPublicKeyMaterial(region, group, "ssh-rsa"))) expect(strategy.importExistingKeyPair.apply(new RegionNameAndPublicKeyMaterial(region, group, "ssh-rsa")))
.andReturn(keyPair); .andReturn(keyPair);
@ -545,7 +552,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
expect(options.getOverridingCredentials()).andReturn(null); expect(options.getOverridingCredentials()).andReturn(null);
expect(options.getRunScript()).andReturn(null).times(2); expect(options.getRunScript()).andReturn(null).times(2);
expect(options.getPrivateKey()).andReturn(null); 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 mocks
replay(options); replay(options);
@ -573,16 +580,13 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
KeyPair keyPair = createMock(KeyPair.class); KeyPair keyPair = createMock(KeyPair.class);
// setup expectations // setup expectations
expect(strategy.knownKeys.get(new RegionAndName(region, "jclouds#" + group))).andReturn(null);
expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.getKeyPair()).andReturn(userSuppliedKeyPair);
expect(options.shouldAutomaticallyCreateKeyPair()).andReturn(shouldAutomaticallyCreateKeyPair); 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(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(keyPair.getKeyName()).andReturn(systemGeneratedKeyPairName).atLeastOnce();
expect(options.getRunScript()).andReturn(null); expect(options.getRunScript()).andReturn(null);
expect(strategy.credentialsMap.put(new RegionAndName(region, systemGeneratedKeyPairName), keyPair)).andReturn(
null);
// replay mocks // replay mocks
replay(options); replay(options);
@ -613,10 +617,10 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
KeyPair keyPair = createMock(KeyPair.class); KeyPair keyPair = createMock(KeyPair.class);
// setup expectations // setup expectations
expect(strategy.knownKeys.get(new RegionAndName(region, "jclouds#" + group))).andReturn(keyPair);
expect(options.getKeyPair()).andReturn(userSuppliedKeyPair); expect(options.getKeyPair()).andReturn(userSuppliedKeyPair);
expect(options.getRunScript()).andReturn(null); expect(options.getRunScript()).andReturn(null);
expect(options.shouldAutomaticallyCreateKeyPair()).andReturn(shouldAutomaticallyCreateKeyPair); expect(options.shouldAutomaticallyCreateKeyPair()).andReturn(shouldAutomaticallyCreateKeyPair);
expect(strategy.credentialsMap.get(new RegionAndName(region, "jclouds#" + group))).andReturn(keyPair);
expect(keyPair.getKeyName()).andReturn(systemGeneratedKeyPairName).atLeastOnce(); expect(keyPair.getKeyName()).andReturn(systemGeneratedKeyPairName).atLeastOnce();
// replay mocks // replay mocks
replay(options); replay(options);
@ -673,7 +677,6 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
Set<String> groupNames = ImmutableSet.<String> of(); Set<String> groupNames = ImmutableSet.<String> of();
int[] ports = new int[] {}; int[] ports = new int[] {};
boolean shouldAuthorizeSelf = true; boolean shouldAuthorizeSelf = true;
boolean groupExisted = false;
Set<String> returnVal = ImmutableSet.<String> of(generatedMarkerGroup); Set<String> returnVal = ImmutableSet.<String> of(generatedMarkerGroup);
// create mocks // create mocks
@ -686,9 +689,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
expect(options.getInboundPorts()).andReturn(ports).atLeastOnce(); expect(options.getInboundPorts()).andReturn(ports).atLeastOnce();
RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup,
ports, shouldAuthorizeSelf); ports, shouldAuthorizeSelf);
expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(groupExisted); expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules)).andReturn(group);
expect(strategy.createSecurityGroupIfNeeded.apply(regionNameAndIngressRules)).andReturn(generatedMarkerGroup);
expect(strategy.securityGroupMap.put(regionNameAndIngressRules, generatedMarkerGroup)).andReturn(null);
// replay mocks // replay mocks
replay(options); replay(options);
@ -710,7 +711,6 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
Set<String> groupNames = ImmutableSet.<String> of(); Set<String> groupNames = ImmutableSet.<String> of();
int[] ports = new int[] { 22, 80 }; int[] ports = new int[] { 22, 80 };
boolean shouldAuthorizeSelf = true; boolean shouldAuthorizeSelf = true;
boolean groupExisted = false;
Set<String> returnVal = ImmutableSet.<String> of(generatedMarkerGroup); Set<String> returnVal = ImmutableSet.<String> of(generatedMarkerGroup);
// create mocks // create mocks
@ -723,9 +723,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
expect(options.getInboundPorts()).andReturn(ports).atLeastOnce(); expect(options.getInboundPorts()).andReturn(ports).atLeastOnce();
RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup,
ports, shouldAuthorizeSelf); ports, shouldAuthorizeSelf);
expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(groupExisted); expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules)).andReturn(generatedMarkerGroup);
expect(strategy.createSecurityGroupIfNeeded.apply(regionNameAndIngressRules)).andReturn(generatedMarkerGroup);
expect(strategy.securityGroupMap.put(regionNameAndIngressRules, generatedMarkerGroup)).andReturn(null);
// replay mocks // replay mocks
replay(options); replay(options);
@ -747,7 +745,6 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
Set<String> groupNames = ImmutableSet.<String> of(); Set<String> groupNames = ImmutableSet.<String> of();
int[] ports = new int[] {}; int[] ports = new int[] {};
boolean shouldAuthorizeSelf = true; boolean shouldAuthorizeSelf = true;
boolean groupExisted = true;
Set<String> returnVal = ImmutableSet.<String> of(generatedMarkerGroup); Set<String> returnVal = ImmutableSet.<String> of(generatedMarkerGroup);
// create mocks // create mocks
@ -760,7 +757,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
expect(options.getInboundPorts()).andReturn(ports).atLeastOnce(); expect(options.getInboundPorts()).andReturn(ports).atLeastOnce();
RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup,
ports, shouldAuthorizeSelf); ports, shouldAuthorizeSelf);
expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(groupExisted); expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules)).andReturn(generatedMarkerGroup);
// replay mocks // replay mocks
replay(options); replay(options);
@ -793,13 +790,9 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
expect(options.getGroupIds()).andReturn(ImmutableSet.<String>of()); expect(options.getGroupIds()).andReturn(ImmutableSet.<String>of());
expect(options.getGroups()).andReturn(groupNames).atLeastOnce(); expect(options.getGroups()).andReturn(groupNames).atLeastOnce();
RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup,
ports, shouldAuthorizeSelf); // note ports, shouldAuthorizeSelf);
// this
// works expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules)).andReturn(groupExisted ? "tag" : null);
// since
// there's
// no equals on portsq
expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(groupExisted);
// replay mocks // replay mocks
replay(options); replay(options);
@ -832,13 +825,9 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
expect(options.getGroupIds()).andReturn(ImmutableSet.<String>of("group1", "group2")); expect(options.getGroupIds()).andReturn(ImmutableSet.<String>of("group1", "group2"));
expect(options.getGroups()).andReturn(groupNames).atLeastOnce(); expect(options.getGroups()).andReturn(groupNames).atLeastOnce();
RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup, RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup,
ports, shouldAuthorizeSelf); // note ports, shouldAuthorizeSelf);
// this
// works expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules)).andReturn(groupExisted ? "tag" : null);
// since
// there's
// no equals on portsq
expect(strategy.securityGroupMap.containsKey(regionNameAndIngressRules)).andReturn(groupExisted);
// replay mocks // replay mocks
replay(options); replay(options);
@ -895,11 +884,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
// setup expectations // setup expectations
expect(options.getPlacementGroup()).andReturn(userSuppliedPlacementGroup); expect(options.getPlacementGroup()).andReturn(userSuppliedPlacementGroup);
expect(options.shouldAutomaticallyCreatePlacementGroup()).andReturn(shouldAutomaticallyCreatePlacementGroup); expect(options.shouldAutomaticallyCreatePlacementGroup()).andReturn(shouldAutomaticallyCreatePlacementGroup);
expect(strategy.placementGroupMap.containsKey(new RegionAndName(region, generatedMarkerGroup))).andReturn(false); expect(strategy.placementGroupMap.getUnchecked(new RegionAndName(region, generatedMarkerGroup))).andReturn(generatedMarkerGroup);
expect(strategy.createPlacementGroupIfNeeded.apply(new RegionAndName(region, generatedMarkerGroup))).andReturn(
generatedMarkerGroup);
expect(strategy.placementGroupMap.put(new RegionAndName(region, generatedMarkerGroup), generatedMarkerGroup))
.andReturn(null);
// replay mocks // replay mocks
replay(options); replay(options);
@ -946,37 +931,33 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
} }
private void verifyStrategy(CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy) { private void verifyStrategy(CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy) {
verify(strategy.knownKeys);
verify(strategy.credentialsMap); verify(strategy.credentialsMap);
verify(strategy.securityGroupMap); verify(strategy.securityGroupMap);
verify(strategy.placementGroupMap); verify(strategy.placementGroupMap);
verify(strategy.createUniqueKeyPair);
verify(strategy.importExistingKeyPair); verify(strategy.importExistingKeyPair);
verify(strategy.createSecurityGroupIfNeeded);
verify(strategy.createPlacementGroupIfNeeded); verify(strategy.createPlacementGroupIfNeeded);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions setupStrategy() { private CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions setupStrategy() {
Map<RegionAndName, KeyPair> credentialsMap = createMock(Map.class); Map<RegionAndName, KeyPair> knownKeys = createMock(Map.class);
Map<RegionAndName, String> securityGroupMap = createMock(Map.class); Cache<RegionAndName, KeyPair> credentialsMap = createMock(Cache.class);
Map<RegionAndName, String> placementGroupMap = createMock(Map.class); Cache<RegionAndName, String> securityGroupMap = createMock(Cache.class);
Function<RegionAndName, KeyPair> createOrGetKeyPair = createMock(Function.class); Cache<RegionAndName, String> placementGroupMap = createMock(Cache.class);
Function<RegionNameAndIngressRules, String> createSecurityGroupIfNeeded = createMock(Function.class);
Function<RegionNameAndPublicKeyMaterial, KeyPair> importExistingKeyPair = createMock(Function.class); Function<RegionNameAndPublicKeyMaterial, KeyPair> importExistingKeyPair = createMock(Function.class);
CreatePlacementGroupIfNeeded createPlacementGroupIfNeeded = createMock(CreatePlacementGroupIfNeeded.class); CreatePlacementGroupIfNeeded createPlacementGroupIfNeeded = createMock(CreatePlacementGroupIfNeeded.class);
return new CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions(credentialsMap, securityGroupMap, return new CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions(knownKeys, credentialsMap, securityGroupMap,
placementGroupMap, createOrGetKeyPair, createSecurityGroupIfNeeded, OPTIONS_PROVIDER, OPTIONS_PROVIDER, placementGroupMap, createPlacementGroupIfNeeded, importExistingKeyPair);
createPlacementGroupIfNeeded, importExistingKeyPair);
} }
private void replayStrategy(CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy) { private void replayStrategy(CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy) {
replay(strategy.knownKeys);
replay(strategy.credentialsMap); replay(strategy.credentialsMap);
replay(strategy.securityGroupMap); replay(strategy.securityGroupMap);
replay(strategy.placementGroupMap); replay(strategy.placementGroupMap);
replay(strategy.createUniqueKeyPair);
replay(strategy.importExistingKeyPair); replay(strategy.importExistingKeyPair);
replay(strategy.createSecurityGroupIfNeeded);
replay(strategy.createPlacementGroupIfNeeded); replay(strategy.createPlacementGroupIfNeeded);
} }

View File

@ -25,10 +25,14 @@ import java.net.URI;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import javax.inject.Singleton;
import org.jclouds.aws.domain.Region; import org.jclouds.aws.domain.Region;
import org.jclouds.aws.ec2.config.AWSEC2RestClientModule; import org.jclouds.aws.ec2.config.AWSEC2RestClientModule;
import org.jclouds.aws.filters.FormSigner; import org.jclouds.aws.filters.FormSigner;
import org.jclouds.compute.domain.Image;
import org.jclouds.date.DateService; import org.jclouds.date.DateService;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.http.RequiresHttp; import org.jclouds.http.RequiresHttp;
import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.ConfiguresRestClient;
@ -38,8 +42,12 @@ import org.jclouds.rest.RestContextSpec;
import org.testng.annotations.BeforeTest; import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test; 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.common.collect.ImmutableMap;
import com.google.inject.Module; import com.google.inject.Module;
import com.google.inject.Provides;
/** /**
* @author Adrian Cole * @author Adrian Cole
@ -60,6 +68,18 @@ public abstract class BaseAWSEC2AsyncClientTest<T> extends RestClientTest<T> {
bindRegionsToProvider(Regions.class); bindRegionsToProvider(Regions.class);
} }
@Provides
@Singleton
Cache<RegionAndName, Image> provide(){
return CacheBuilder.newBuilder().build(new CacheLoader<RegionAndName, Image>() {
@Override
public Image load(RegionAndName key) throws Exception {
return null;
}
});
}
static class Regions implements javax.inject.Provider<Map<String, URI>> { static class Regions implements javax.inject.Provider<Map<String, URI>> {
@Override @Override
public Map<String, URI> get() { public Map<String, URI> get() {

View File

@ -32,7 +32,6 @@ import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.Constants; import org.jclouds.Constants;
import org.jclouds.cloudsigma.CloudSigmaAsyncClient;
import org.jclouds.cloudsigma.CloudSigmaClient; import org.jclouds.cloudsigma.CloudSigmaClient;
import org.jclouds.cloudsigma.domain.Device; import org.jclouds.cloudsigma.domain.Device;
import org.jclouds.cloudsigma.domain.DriveInfo; 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.Function;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder; 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 * defines the connection between the {@link CloudSigmaClient} implementation
* {@link ComputeService} * and the jclouds {@link ComputeService}
* *
*/ */
@Singleton @Singleton
public class CloudSigmaComputeServiceAdapter implements public class CloudSigmaComputeServiceAdapter implements
ComputeServiceAdapter<ServerInfo, Hardware, DriveInfo, Location> { ComputeServiceAdapter<ServerInfo, Hardware, DriveInfo, Location> {
private static final Predicate<DriveInfo> PREINSTALLED_DISK = Predicates.and(Predicates.notNull(), private static final Predicate<DriveInfo> PREINSTALLED_DISK = Predicates.and(Predicates.notNull(),
new Predicate<DriveInfo>() { new Predicate<DriveInfo>() {
@Override @Override
public boolean apply(DriveInfo drive) { public boolean apply(DriveInfo drive) {
return drive.getType().equals(DriveType.DISK) && drive.getDriveType().contains("preinstalled"); return drive.getType().equals(DriveType.DISK) && drive.getDriveType().contains("preinstalled");
} }
}); });
private final CloudSigmaClient client; private final CloudSigmaClient client;
private final CloudSigmaAsyncClient aclient;
private final Predicate<DriveInfo> driveNotClaimed; private final Predicate<DriveInfo> driveNotClaimed;
private final JustProvider locationSupplier; private final JustProvider locationSupplier;
private final String defaultVncPassword; private final String defaultVncPassword;
private final Map<String, DriveInfo> cache; private final Cache<String, DriveInfo> cache;
private final ExecutorService executor; private final ExecutorService executor;
@Resource @Resource
@ -94,12 +95,10 @@ public class CloudSigmaComputeServiceAdapter implements
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
@Inject @Inject
public CloudSigmaComputeServiceAdapter(CloudSigmaClient client, CloudSigmaAsyncClient aclient, public CloudSigmaComputeServiceAdapter(CloudSigmaClient client, Predicate<DriveInfo> driveNotClaimed,
Predicate<DriveInfo> driveNotClaimed, JustProvider locationSupplier, JustProvider locationSupplier, @Named(CloudSigmaConstants.PROPERTY_VNC_PASSWORD) String defaultVncPassword,
@Named(CloudSigmaConstants.PROPERTY_VNC_PASSWORD) String defaultVncPassword, Map<String, DriveInfo> cache, Cache<String, DriveInfo> cache, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) {
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) {
this.client = checkNotNull(client, "client"); this.client = checkNotNull(client, "client");
this.aclient = checkNotNull(aclient, "aclient");
this.driveNotClaimed = checkNotNull(driveNotClaimed, "driveNotClaimed"); this.driveNotClaimed = checkNotNull(driveNotClaimed, "driveNotClaimed");
this.locationSupplier = checkNotNull(locationSupplier, "locationSupplier"); this.locationSupplier = checkNotNull(locationSupplier, "locationSupplier");
this.defaultVncPassword = checkNotNull(defaultVncPassword, "defaultVncPassword"); this.defaultVncPassword = checkNotNull(defaultVncPassword, "defaultVncPassword");
@ -109,21 +108,20 @@ public class CloudSigmaComputeServiceAdapter implements
@Override @Override
public ServerInfo createNodeWithGroupEncodedIntoNameThenStoreCredentials(String tag, String name, Template template, public ServerInfo createNodeWithGroupEncodedIntoNameThenStoreCredentials(String tag, String name, Template template,
Map<String, Credentials> credentialStore) { Map<String, Credentials> credentialStore) {
long bootSize = (long) (template.getHardware().getVolumes().get(0).getSize() * 1024 * 1024 * 1024l); 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); logger.debug(">> imaging boot drive source(%s) bytes(%d)", template.getImage().getId(), bootSize);
DriveInfo drive = client.cloneDrive(template.getImage().getId(), template.getImage().getId(), DriveInfo drive = client.cloneDrive(template.getImage().getId(), template.getImage().getId(),
new CloneDriveOptions().size(bootSize)); new CloneDriveOptions().size(bootSize));
boolean success = driveNotClaimed.apply(drive); boolean success = driveNotClaimed.apply(drive);
logger.debug("<< image(%s) complete(%s)", drive.getUuid(), success); logger.debug("<< image(%s) complete(%s)", drive.getUuid(), success);
if (!success) { if (!success) {
client.destroyDrive(drive.getUuid()); client.destroyDrive(drive.getUuid());
throw new IllegalStateException("could not image drive in time!"); 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()) 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"); logger.debug(">> creating server");
ServerInfo from = client.createServer(toCreate); ServerInfo from = client.createServer(toCreate);
@ -131,7 +129,7 @@ public class CloudSigmaComputeServiceAdapter implements
logger.debug(">> starting server(%s)", from.getUuid()); logger.debug(">> starting server(%s)", from.getUuid());
client.startServer(from.getUuid()); client.startServer(from.getUuid());
// store the credentials so that later functions can use them // 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; return from;
} }
@ -155,30 +153,39 @@ public class CloudSigmaComputeServiceAdapter implements
return "sizeLessThanOrEqual(" + size + ")"; return "sizeLessThanOrEqual(" + size + ")";
} }
}).ids(id).ram(ram).processors(ImmutableList.of(new Processor(1, cpu))).volumes( }).ids(id).ram(ram).processors(ImmutableList.of(new Processor(1, cpu)))
ImmutableList.<Volume> of(new VolumeImpl(size, true, true))).build()); .volumes(ImmutableList.<Volume> of(new VolumeImpl(size, true, true))).build());
} }
return hardware.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 @Override
public Iterable<DriveInfo> listImages() { public Iterable<DriveInfo> listImages() {
Iterable<DriveInfo> drives = transformParallel(client.listStandardDrives(), Iterable<DriveInfo> drives = transformParallel(client.listStandardDrives(),
new Function<String, Future<DriveInfo>>() { new Function<String, Future<DriveInfo>>() {
@Override @Override
public Future<DriveInfo> apply(String input) { public Future<DriveInfo> apply(String input) {
return aclient.getDriveInfo(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"); @Override
Iterable<DriveInfo> returnVal = filter(drives, PREINSTALLED_DISK); public String toString() {
for (DriveInfo drive : returnVal) return "seedDriveCache()";
cache.put(drive.getUuid(), drive); }
return returnVal; }, executor, null, logger, "drives");
return filter(drives, PREINSTALLED_DISK);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View File

@ -25,6 +25,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.annotation.Resource;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
@ -45,13 +46,16 @@ import org.jclouds.compute.domain.Volume;
import org.jclouds.compute.domain.VolumeBuilder; import org.jclouds.compute.domain.VolumeBuilder;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.domain.Location; import org.jclouds.domain.Location;
import org.jclouds.logging.Logger;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.UncheckedExecutionException;
/** /**
* @author Adrian Cole * @author Adrian Cole
@ -113,10 +117,13 @@ public class ServerInfoToNodeMetadata implements Function<ServerInfo, NodeMetada
@Singleton @Singleton
public static final class DeviceToVolume implements Function<Device, Volume> { public static final class DeviceToVolume implements Function<Device, Volume> {
private final Map<String, DriveInfo> cache; @Resource
protected Logger logger = Logger.NULL;
private final Cache<String, DriveInfo> cache;
@Inject @Inject
public DeviceToVolume(Map<String, DriveInfo> cache) { public DeviceToVolume(Cache<String, DriveInfo> cache) {
this.cache = checkNotNull(cache, "cache"); this.cache = checkNotNull(cache, "cache");
} }
@ -124,9 +131,13 @@ public class ServerInfoToNodeMetadata implements Function<ServerInfo, NodeMetada
public Volume apply(Device input) { public Volume apply(Device input) {
VolumeBuilder builder = new VolumeBuilder(); VolumeBuilder builder = new VolumeBuilder();
builder.id(input.getId()); builder.id(input.getId());
DriveInfo drive = cache.get(input.getDriveUuid()); try {
if (drive != null) { DriveInfo drive = cache.getUnchecked(input.getDriveUuid());
builder.size(drive.getSize() / 1024 / 1024f); builder.size(drive.getSize() / 1024 / 1024f);
} catch (NullPointerException e) {
logger.debug("drive %s not found", input.getDriveUuid());
} catch (UncheckedExecutionException e) {
logger.warn(e, "error finding drive %s: %s", input.getDriveUuid(), e.getMessage());
} }
return new VolumeBuilder().durable(true).type(Volume.Type.NAS).build(); return new VolumeBuilder().durable(true).type(Volume.Type.NAS).build();
} }
@ -141,10 +152,13 @@ public class ServerInfoToNodeMetadata implements Function<ServerInfo, NodeMetada
*/ */
@Singleton @Singleton
public static class GetImageIdFromServer implements Function<Server, String> { public static class GetImageIdFromServer implements Function<Server, String> {
private final Map<String, DriveInfo> cache; @Resource
protected Logger logger = Logger.NULL;
private final Cache<String, DriveInfo> cache;
@Inject @Inject
public GetImageIdFromServer(Map<String, DriveInfo> cache) { public GetImageIdFromServer(Cache<String, DriveInfo> cache) {
this.cache = cache; this.cache = cache;
} }
@ -155,9 +169,12 @@ public class ServerInfoToNodeMetadata implements Function<ServerInfo, NodeMetada
Device bootDevice = from.getDevices().get(bootDeviceId); Device bootDevice = from.getDevices().get(bootDeviceId);
if (bootDevice != null) { if (bootDevice != null) {
try { try {
imageId = cache.get(bootDevice.getDriveUuid()).getName(); DriveInfo drive = cache.getUnchecked(bootDevice.getDriveUuid());
imageId = drive.getName();
} catch (NullPointerException e) { } catch (NullPointerException e) {
logger.debug("drive %s not found", bootDevice.getDriveUuid());
} catch (UncheckedExecutionException e) {
logger.warn(e, "error finding drive %s: %s", bootDevice.getDriveUuid(), e.getMessage());
} }
} }
return imageId; return imageId;