diff --git a/core/src/main/java/org/jclouds/Context.java b/core/src/main/java/org/jclouds/Context.java index 2e70b8732d..7f4399ce54 100644 --- a/core/src/main/java/org/jclouds/Context.java +++ b/core/src/main/java/org/jclouds/Context.java @@ -46,6 +46,12 @@ import com.google.inject.ImplementedBy; @ImplementedBy(ContextImpl.class) public interface Context extends Location, Closeable { + /** + * Identifies the Context. + * @return + */ + String getName(); + /** * will be removed in jclouds 1.6 * diff --git a/core/src/main/java/org/jclouds/ContextBuilder.java b/core/src/main/java/org/jclouds/ContextBuilder.java index 9d8074955d..af6e2d1d09 100644 --- a/core/src/main/java/org/jclouds/ContextBuilder.java +++ b/core/src/main/java/org/jclouds/ContextBuilder.java @@ -30,14 +30,7 @@ import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.find; import static com.google.common.collect.Iterables.transform; import static com.google.common.collect.Lists.newArrayList; -import static org.jclouds.Constants.PROPERTY_API; -import static org.jclouds.Constants.PROPERTY_API_VERSION; -import static org.jclouds.Constants.PROPERTY_BUILD_VERSION; -import static org.jclouds.Constants.PROPERTY_CREDENTIAL; -import static org.jclouds.Constants.PROPERTY_ENDPOINT; -import static org.jclouds.Constants.PROPERTY_IDENTITY; -import static org.jclouds.Constants.PROPERTY_ISO3166_CODES; -import static org.jclouds.Constants.PROPERTY_PROVIDER; +import static org.jclouds.Constants.*; import static org.jclouds.util.Throwables2.propagateAuthorizationOrOriginalException; import java.util.ArrayList; @@ -53,6 +46,7 @@ import org.jclouds.concurrent.MoreExecutors; import org.jclouds.concurrent.SingleThreaded; import org.jclouds.concurrent.config.ConfiguresExecutorService; import org.jclouds.concurrent.config.ExecutorServiceModule; +import org.jclouds.config.BindNameToContext; import org.jclouds.config.BindPropertiesToExpandedValues; import org.jclouds.config.BindRestContextWithWildcardExtendsExplicitAndRawType; import org.jclouds.domain.Credentials; @@ -154,6 +148,7 @@ public class ContextBuilder { } } + protected Optional name = Optional.absent(); protected Optional providerMetadata = Optional.absent(); protected final String providerId; protected Optional endpoint = Optional.absent(); @@ -196,6 +191,11 @@ public class ContextBuilder { this(null, apiMetadata); } + public ContextBuilder name(String name) { + this.name = Optional.of(checkNotNull(name, "name")); + return this; + } + public ContextBuilder credentials(String identity, @Nullable String credential) { this.identity = Optional.of(checkNotNull(identity, "identity")); this.credential = credential; @@ -258,7 +258,10 @@ public class ContextBuilder { ProviderMetadata providerMetadata = new UpdateProviderMetadataFromProperties(apiMetadata, this.providerMetadata).apply(expanded); - return buildInjector(providerMetadata, creds, modules); + //We use either the specified name (optional) or a hash of provider/api, endpoint, api version & identity. Hash is used to be something readable. + String name = this.name.isPresent() ? this.name.get() : String.valueOf(Objects.hashCode(providerMetadata.getId(), providerMetadata.getEndpoint() , apiVersion , identity.get())); + + return buildInjector(name, providerMetadata, creds, modules); } private static String getAndRemove(Properties expanded, String key) { @@ -302,7 +305,7 @@ public class ContextBuilder { return Guice.createInjector(new BindPropertiesToExpandedValues(resolved)).getInstance(Properties.class); } - public static Injector buildInjector(ProviderMetadata providerMetadata, Credentials creds, List inputModules) { + public static Injector buildInjector(String name, ProviderMetadata providerMetadata, Credentials creds, List inputModules) { List modules = newArrayList(); modules.addAll(inputModules); boolean restModuleSpecifiedByUser = restClientModulePresent(inputModules); @@ -318,6 +321,7 @@ public class ContextBuilder { addCredentialStoreIfNotPresent(modules); modules.add(new LifeCycleModule()); modules.add(new BindProviderMetadataContextAndCredentials(providerMetadata, creds)); + modules.add(new BindNameToContext(name)); Injector returnVal = Guice.createInjector(Stage.PRODUCTION, modules); returnVal.getInstance(ExecutionList.class).execute(); return returnVal; diff --git a/core/src/main/java/org/jclouds/annotations/Name.java b/core/src/main/java/org/jclouds/annotations/Name.java new file mode 100644 index 0000000000..a622ad20a7 --- /dev/null +++ b/core/src/main/java/org/jclouds/annotations/Name.java @@ -0,0 +1,36 @@ +/** + * 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.annotations; + +import javax.inject.Qualifier; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Designates that this Resource qualifies an object to a context name. + */ +@Target( { ANNOTATION_TYPE, FIELD, METHOD, PARAMETER }) +@Retention(RUNTIME) +@Qualifier +public @interface Name { + +} diff --git a/core/src/main/java/org/jclouds/config/BindNameToContext.java b/core/src/main/java/org/jclouds/config/BindNameToContext.java new file mode 100644 index 0000000000..b95eeb3d94 --- /dev/null +++ b/core/src/main/java/org/jclouds/config/BindNameToContext.java @@ -0,0 +1,39 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.config; + +import com.google.inject.AbstractModule; +import org.jclouds.annotations.Name; + +/** + * Binds name to Context. + */ +public class BindNameToContext extends AbstractModule { + + private final String name; + + public BindNameToContext(String name) { + this.name = name; + } + + @Override + protected void configure() { + bind(String.class).annotatedWith(Name.class).toInstance(name); + } +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/internal/ContextImpl.java b/core/src/main/java/org/jclouds/internal/ContextImpl.java index 114b24917e..8c92c27839 100644 --- a/core/src/main/java/org/jclouds/internal/ContextImpl.java +++ b/core/src/main/java/org/jclouds/internal/ContextImpl.java @@ -18,15 +18,12 @@ */ package org.jclouds.internal; -import static com.google.common.base.Preconditions.checkNotNull; - -import java.net.URI; -import java.util.Map; -import java.util.Set; - -import javax.inject.Inject; - +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableMap; +import com.google.common.io.Closeables; +import com.google.inject.Singleton; import org.jclouds.Context; +import org.jclouds.annotations.Name; import org.jclouds.domain.Credentials; import org.jclouds.domain.Location; import org.jclouds.domain.LocationScope; @@ -35,10 +32,12 @@ import org.jclouds.providers.ProviderMetadata; import org.jclouds.rest.Utils; import org.jclouds.rest.annotations.Identity; -import com.google.common.base.Objects; -import com.google.common.collect.ImmutableMap; -import com.google.common.io.Closeables; -import com.google.inject.Singleton; +import javax.inject.Inject; +import java.net.URI; +import java.util.Map; +import java.util.Set; + +import static com.google.common.base.Preconditions.checkNotNull; /** * @author Adrian Cole @@ -50,14 +49,16 @@ public class ContextImpl implements Context { private final String identity; private final Utils utils; private final Closer closer; + private final String name; @Inject - protected ContextImpl(ProviderMetadata providerMetadata, @Identity String identity, Utils utils, Closer closer) { + protected ContextImpl(@Name String name, ProviderMetadata providerMetadata, @Identity String identity, Utils utils, Closer closer) { this.providerMetadata = checkNotNull(providerMetadata, "providerMetadata"); this.identity = checkNotNull(identity, "identity"); this.utils = checkNotNull(utils, "utils"); this.closer = checkNotNull(closer, "closer"); - } + this.name = checkNotNull(name, "name"); + } /** * {@inheritDoc} @@ -75,7 +76,15 @@ public class ContextImpl implements Context { return providerMetadata; } - /** + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return name; + } + + /** * {@inheritDoc} */ @Override diff --git a/core/src/main/java/org/jclouds/rest/internal/RestContextImpl.java b/core/src/main/java/org/jclouds/rest/internal/RestContextImpl.java index c592d98809..ed78b52328 100644 --- a/core/src/main/java/org/jclouds/rest/internal/RestContextImpl.java +++ b/core/src/main/java/org/jclouds/rest/internal/RestContextImpl.java @@ -22,6 +22,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import javax.inject.Inject; +import org.jclouds.annotations.Name; import org.jclouds.internal.ContextImpl; import org.jclouds.lifecycle.Closer; import org.jclouds.providers.ProviderMetadata; @@ -44,9 +45,9 @@ public class RestContextImpl extends ContextImpl implements RestContext syncApi, TypeLiteral asyncApi) { - super(providerMetadata, identity, utils, closer); + super(name, providerMetadata, identity, utils, closer); checkNotNull(injector, "injector"); this.asyncApi = injector.getInstance(Key.get(checkNotNull(asyncApi, "asyncApi"))); this.syncApi = injector.getInstance(Key.get(checkNotNull(syncApi, "syncApi"))); diff --git a/core/src/test/java/org/jclouds/ContextBuilderTest.java b/core/src/test/java/org/jclouds/ContextBuilderTest.java index 4d9ea2788e..2d45ad3d5a 100644 --- a/core/src/test/java/org/jclouds/ContextBuilderTest.java +++ b/core/src/test/java/org/jclouds/ContextBuilderTest.java @@ -82,6 +82,14 @@ public class ContextBuilderTest { assertEquals(endpoint, URI.create("http://foo.service.com")); } + @Test + public void testContextName() { + ContextBuilder withNoName = testContextBuilder().endpoint("http://${jclouds.identity}.service.com").name("mytest") + .credentials("foo", "bar"); + Context context = withNoName.build(); + assertEquals(context.getName(), "mytest"); + } + @Test public void testProviderMetadataBoundWithCorrectEndpoint() { ContextBuilder withVariablesToReplace = testContextBuilder().endpoint("http://${jclouds.identity}.service.com") diff --git a/core/src/test/java/org/jclouds/config/BindRestContextWithWildcardExtendsExplicitAndRawTypeTest.java b/core/src/test/java/org/jclouds/config/BindRestContextWithWildcardExtendsExplicitAndRawTypeTest.java index 08aafc8262..f47c6dcd6c 100644 --- a/core/src/test/java/org/jclouds/config/BindRestContextWithWildcardExtendsExplicitAndRawTypeTest.java +++ b/core/src/test/java/org/jclouds/config/BindRestContextWithWildcardExtendsExplicitAndRawTypeTest.java @@ -73,6 +73,7 @@ public class BindRestContextWithWildcardExtendsExplicitAndRawTypeTest { private Injector injectorFor(ProviderMetadata md) { return Guice.createInjector( + new BindNameToContext("test"), new BindProviderMetadataContextAndCredentials(md, new Credentials("user", "pass")), new BindRestContextWithWildcardExtendsExplicitAndRawType(RestApiMetadata.class.cast(md .getApiMetadata())), diff --git a/core/src/test/java/org/jclouds/internal/BaseViewTest.java b/core/src/test/java/org/jclouds/internal/BaseViewTest.java index 506899d066..4c89f9ec0a 100644 --- a/core/src/test/java/org/jclouds/internal/BaseViewTest.java +++ b/core/src/test/java/org/jclouds/internal/BaseViewTest.java @@ -19,6 +19,7 @@ package org.jclouds.internal; import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotEquals; @@ -36,10 +37,11 @@ import com.google.common.reflect.TypeToken; */ @Test(groups = "unit", testName = "BaseViewTest") public class BaseViewTest { + private static class Water extends ContextImpl { protected Water() { - super(createMock(ProviderMetadata.class), "identity", createMock(Utils.class), createMock(Closer.class)); + super("water", createMock(ProviderMetadata.class), "identity", createMock(Utils.class), createMock(Closer.class)); } @Override @@ -55,7 +57,7 @@ public class BaseViewTest { private static class PeanutButter extends ContextImpl { protected PeanutButter() { - super(createMock(ProviderMetadata.class), "identity", createMock(Utils.class), createMock(Closer.class)); + super("peanutbutter", createMock(ProviderMetadata.class), "identity", createMock(Utils.class), createMock(Closer.class)); } @Override diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/internal/VCloudDirectorContextImpl.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/internal/VCloudDirectorContextImpl.java index 6778e5bb92..d2056c779d 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/internal/VCloudDirectorContextImpl.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/internal/VCloudDirectorContextImpl.java @@ -21,6 +21,7 @@ package org.jclouds.vcloud.director.v1_5.internal; import javax.inject.Inject; import javax.inject.Singleton; +import org.jclouds.annotations.Name; import org.jclouds.lifecycle.Closer; import org.jclouds.providers.ProviderMetadata; import org.jclouds.rest.RestContext; @@ -45,9 +46,9 @@ public class VCloudDirectorContextImpl extends RestContextImpl adminContext; @Inject - VCloudDirectorContextImpl(ProviderMetadata providerMetadata, @Identity String identity, Utils utils, Closer closer, + VCloudDirectorContextImpl(@Name String name, ProviderMetadata providerMetadata, @Identity String identity, Utils utils, Closer closer, Injector injector, RestContext adminContext) { - super(providerMetadata, identity, utils, closer, injector, TypeLiteral.get(VCloudDirectorApi.class), + super(name, providerMetadata, identity, utils, closer, injector, TypeLiteral.get(VCloudDirectorApi.class), TypeLiteral.get(VCloudDirectorAsyncApi.class)); this.adminContext = adminContext; }