diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneApi.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneApi.java index 659fc4d629..0085f34c39 100644 --- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneApi.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneApi.java @@ -21,12 +21,16 @@ package org.jclouds.openstack.keystone.v2_0; import java.util.concurrent.TimeUnit; import org.jclouds.concurrent.Timeout; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.location.functions.ZoneToEndpoint; import org.jclouds.openstack.keystone.v2_0.domain.ApiMetadata; import org.jclouds.openstack.keystone.v2_0.features.ServiceApi; import org.jclouds.openstack.keystone.v2_0.features.TenantApi; import org.jclouds.openstack.keystone.v2_0.features.TokenApi; import org.jclouds.openstack.keystone.v2_0.features.UserApi; +import org.jclouds.openstack.v2_0.features.ExtensionApi; import org.jclouds.rest.annotations.Delegate; +import org.jclouds.rest.annotations.EndpointParam; import com.google.common.base.Optional; @@ -54,6 +58,12 @@ public interface KeystoneApi { @Delegate ServiceApi getServiceApi(); + /** + * Provides synchronous access to Extension features. + */ + @Delegate + ExtensionApi getExtensionApi(); + /** * Provides synchronous access to Token features */ @@ -66,7 +76,6 @@ public interface KeystoneApi { @Delegate Optional getUserApi(); - /** * Provides synchronous access to Tenant features */ diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneAsyncApi.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneAsyncApi.java index 50560fc9ae..52ac90d88d 100644 --- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneAsyncApi.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneAsyncApi.java @@ -27,6 +27,7 @@ import org.jclouds.openstack.keystone.v2_0.features.ServiceAsyncApi; import org.jclouds.openstack.keystone.v2_0.features.TenantAsyncApi; import org.jclouds.openstack.keystone.v2_0.features.TokenAsyncApi; import org.jclouds.openstack.keystone.v2_0.features.UserAsyncApi; +import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi; import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.SelectJson; @@ -60,6 +61,12 @@ public interface KeystoneAsyncApi { @Delegate ServiceAsyncApi getServiceApi(); + /** + * Provides asynchronous access to Extension features. + */ + @Delegate + ExtensionAsyncApi getExtensionApi(); + /** * @see KeystoneApi#getTokenApi() */ @@ -71,7 +78,6 @@ public interface KeystoneAsyncApi { */ @Delegate Optional getUserApi(); - /** * @see KeystoneApi#getTenantApi() diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneRestClientModule.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneRestClientModule.java index f3615eb573..f473be2466 100644 --- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneRestClientModule.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneRestClientModule.java @@ -23,6 +23,8 @@ import static org.jclouds.util.Suppliers2.getLastValueInMap; import java.net.URI; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Set; +import java.util.concurrent.TimeUnit; import javax.inject.Singleton; @@ -41,11 +43,14 @@ import org.jclouds.openstack.keystone.v2_0.features.TokenApi; import org.jclouds.openstack.keystone.v2_0.features.TokenAsyncApi; import org.jclouds.openstack.keystone.v2_0.features.UserApi; import org.jclouds.openstack.keystone.v2_0.features.UserAsyncApi; -import org.jclouds.openstack.keystone.v2_0.functions.PresentWhenAdminURLExistsForIdentityService; import org.jclouds.openstack.keystone.v2_0.handlers.KeystoneErrorHandler; import org.jclouds.openstack.keystone.v2_0.suppliers.RegionIdToAdminURIFromAccessForTypeAndVersion; import org.jclouds.openstack.keystone.v2_0.suppliers.RegionIdToAdminURISupplier; import org.jclouds.openstack.v2_0.ServiceType; +import org.jclouds.openstack.v2_0.domain.Extension; +import org.jclouds.openstack.v2_0.features.ExtensionApi; +import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi; +import org.jclouds.openstack.v2_0.functions.PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet; import org.jclouds.openstack.v2_0.services.Identity; import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.annotations.ApiVersion; @@ -54,7 +59,13 @@ import org.jclouds.rest.functions.ImplicitOptionalConverter; import org.jclouds.util.Suppliers2; import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Multimap; import com.google.common.reflect.TypeToken; import com.google.inject.AbstractModule; import com.google.inject.Provides; @@ -70,17 +81,18 @@ public class KeystoneRestClientModule { public static final Map, Class> DELEGATE_MAP = ImmutableMap., Class> builder() - .put(ServiceApi.class, ServiceAsyncApi.class).put(TokenApi.class, TokenAsyncApi.class) - .put(UserApi.class, UserAsyncApi.class).put(TenantApi.class, TenantAsyncApi.class).build(); + .put(ServiceApi.class, ServiceAsyncApi.class) + .put(ExtensionApi.class, ExtensionAsyncApi.class) + .put(TokenApi.class, TokenAsyncApi.class) + .put(UserApi.class, UserAsyncApi.class) + .put(TenantApi.class, TenantAsyncApi.class) + .build(); - @SuppressWarnings("unchecked") public KeystoneRestClientModule() { - super(TypeToken.class.cast(TypeToken.of(KeystoneApi.class)), TypeToken.class.cast(TypeToken - .of(KeystoneAsyncApi.class)), DELEGATE_MAP); + super(TypeToken.class.cast(TypeToken.of(KeystoneApi.class)), TypeToken.class.cast(TypeToken.of(KeystoneAsyncApi.class)), DELEGATE_MAP); } - protected KeystoneRestClientModule(TypeToken syncApiType, TypeToken asyncApiType, - Map, Class> sync2Async) { + protected KeystoneRestClientModule(TypeToken syncApiType, TypeToken asyncApiType, Map, Class> sync2Async) { super(syncApiType, asyncApiType, sync2Async); } @@ -88,7 +100,6 @@ public class KeystoneRestClientModule aliases() { + return ImmutableMultimap.builder() + .build(); + } + + @Provides + @Singleton + public LoadingCache> provideExtensionsByZone(final javax.inject.Provider keystoneApi) { + return CacheBuilder.newBuilder().expireAfterWrite(23, TimeUnit.HOURS) + .build(CacheLoader.from(Suppliers.memoize(new Supplier>() { + @Override + public Set get() { + return keystoneApi.get().getExtensionApi().listExtensions(); + } + }))); + } + @Override protected void bindErrorHandlers() { bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(KeystoneErrorHandler.class); diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/PresentWhenAdminURLExistsForIdentityService.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/PresentWhenAdminURLExistsForIdentityService.java deleted file mode 100644 index a49a916fc1..0000000000 --- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/PresentWhenAdminURLExistsForIdentityService.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * 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.openstack.keystone.v2_0.functions; - -import javax.inject.Singleton; - -import org.jclouds.internal.ClassMethodArgsAndReturnVal; -import org.jclouds.rest.functions.ImplicitOptionalConverter; - -import com.google.common.base.Optional; - -/** - * - * @author Adrian Cole - * - */ -@Singleton -public class PresentWhenAdminURLExistsForIdentityService implements ImplicitOptionalConverter { - - @Override - public Optional apply(ClassMethodArgsAndReturnVal input) { - //TODO: log and return absent when the admin url for identity service isn't available - return Optional.of(input.getReturnVal()); - } - - public String toString() { - return "presentWhenAdminURLExistsForIdentityService()"; - } - -} diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Extension.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/Extension.java similarity index 97% rename from apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Extension.java rename to apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/Extension.java index e96d583e49..6def36a8b8 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Extension.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/Extension.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.jclouds.openstack.nova.v2_0.domain; +package org.jclouds.openstack.v2_0.domain; import static com.google.common.base.Preconditions.checkNotNull; @@ -26,8 +26,6 @@ import java.util.Date; import java.util.Set; import org.jclouds.javax.annotation.Nullable; -import org.jclouds.openstack.v2_0.domain.Link; -import org.jclouds.openstack.v2_0.domain.Resource; import com.google.common.base.Objects; import com.google.common.base.Objects.ToStringHelper; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ExtensionApi.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/features/ExtensionApi.java similarity index 93% rename from apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ExtensionApi.java rename to apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/features/ExtensionApi.java index 1b5c4d73cb..e8f746ea2a 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ExtensionApi.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/features/ExtensionApi.java @@ -16,13 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -package org.jclouds.openstack.nova.v2_0.features; +package org.jclouds.openstack.v2_0.features; import java.util.Set; import java.util.concurrent.TimeUnit; import org.jclouds.concurrent.Timeout; -import org.jclouds.openstack.nova.v2_0.domain.Extension; +import org.jclouds.openstack.v2_0.domain.Extension; /** * Provides asynchronous access to Extensions via their REST API. diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ExtensionAsyncApi.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/features/ExtensionAsyncApi.java similarity index 95% rename from apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ExtensionAsyncApi.java rename to apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/features/ExtensionAsyncApi.java index 91b012db36..c6b957f807 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ExtensionAsyncApi.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/features/ExtensionAsyncApi.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.jclouds.openstack.nova.v2_0.features; +package org.jclouds.openstack.v2_0.features; import java.util.Set; @@ -27,7 +27,7 @@ import javax.ws.rs.PathParam; import javax.ws.rs.core.MediaType; import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest; -import org.jclouds.openstack.nova.v2_0.domain.Extension; +import org.jclouds.openstack.v2_0.domain.Extension; import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.SelectJson; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/functions/ExtensionToNameSpace.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/functions/ExtensionToNameSpace.java similarity index 93% rename from apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/functions/ExtensionToNameSpace.java rename to apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/functions/ExtensionToNameSpace.java index 97b814ec27..5174ae9fc2 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/functions/ExtensionToNameSpace.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/functions/ExtensionToNameSpace.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.jclouds.openstack.nova.v2_0.functions; +package org.jclouds.openstack.v2_0.functions; import java.net.URI; @@ -25,7 +25,7 @@ import javax.inject.Provider; import javax.inject.Singleton; import javax.ws.rs.core.UriBuilder; -import org.jclouds.openstack.nova.v2_0.domain.Extension; +import org.jclouds.openstack.v2_0.domain.Extension; import com.google.common.base.Function; diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java new file mode 100644 index 0000000000..6681f71b15 --- /dev/null +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java @@ -0,0 +1,88 @@ +/** + * 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.openstack.v2_0.functions; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.net.URI; +import java.util.Set; + +import javax.inject.Inject; + +import org.jclouds.internal.ClassMethodArgsAndReturnVal; +import org.jclouds.openstack.v2_0.domain.Extension; +import org.jclouds.openstack.v2_0.predicates.ExtensionPredicates; +import org.jclouds.rest.functions.ImplicitOptionalConverter; + +import com.google.common.base.Optional; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Multimap; + +/** + * We use the annotation {@link org.jclouds.openstack.services.Extension} to + * bind a class that iimplements an extension API to an {@link Extension}. + * + * @author Adrian Cole + * + */ +public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet implements + ImplicitOptionalConverter { + private final LoadingCache> extensions; + private final Multimap aliases; + + @Inject + public PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet( + LoadingCache> extensions, + Multimap aliases) { + this.extensions = checkNotNull(extensions, "extensions"); + this.aliases = aliases == null ? ImmutableMultimap.of() : ImmutableMultimap.copyOf(aliases); + } + + @Override + public Optional apply(ClassMethodArgsAndReturnVal input) { + Optional ext = Optional.fromNullable(input.getClazz().getAnnotation( + org.jclouds.openstack.v2_0.services.Extension.class)); + if (ext.isPresent()) { + URI namespace = URI.create(ext.get().namespace()); + if (input.getArgs() == null || input.getArgs().length == 0) { + if (Iterables.any(extensions.getUnchecked(""), + ExtensionPredicates.namespaceOrAliasEquals(namespace, aliases.get(namespace)))) + return Optional.of(input.getReturnVal()); + } else if (input.getArgs() != null && input.getArgs().length == 1) { + if (Iterables.any(extensions.getUnchecked(checkNotNull(input.getArgs()[0], "arg[0] in %s", input).toString()), + ExtensionPredicates.namespaceOrAliasEquals(namespace, aliases.get(namespace)))) + return Optional.of(input.getReturnVal()); + } else { + throw new RuntimeException(String.format("expecting zero or one args %s", input)); + } + return Optional.absent(); + } else { + // No extension annotation, should check whether to return absent + return Optional.of(input.getReturnVal()); + } + } + + @Override + public String toString() { + return "presentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet()"; + } + +} diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/ExtensionPredicates.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/predicates/ExtensionPredicates.java similarity index 96% rename from apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/ExtensionPredicates.java rename to apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/predicates/ExtensionPredicates.java index 140cacca80..228859e414 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/ExtensionPredicates.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/predicates/ExtensionPredicates.java @@ -16,14 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -package org.jclouds.openstack.nova.v2_0.predicates; +package org.jclouds.openstack.v2_0.predicates; import static com.google.common.base.Preconditions.checkNotNull; import java.net.URI; import java.util.Collection; -import org.jclouds.openstack.nova.v2_0.domain.Extension; +import org.jclouds.openstack.v2_0.domain.Extension; import com.google.common.base.Predicate; diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/ExtensionToNameSpaceTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/v2_0/functions/ExtensionToNameSpaceTest.java similarity index 83% rename from apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/ExtensionToNameSpaceTest.java rename to apis/openstack-keystone/src/test/java/org/jclouds/openstack/v2_0/functions/ExtensionToNameSpaceTest.java index ff57b2c10b..2518e69492 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/ExtensionToNameSpaceTest.java +++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/v2_0/functions/ExtensionToNameSpaceTest.java @@ -1,4 +1,4 @@ -package org.jclouds.openstack.nova.v2_0.functions; +package org.jclouds.openstack.v2_0.functions; import static org.testng.Assert.assertEquals; @@ -8,8 +8,7 @@ import javax.inject.Provider; import javax.ws.rs.core.UriBuilder; import org.jclouds.date.internal.SimpleDateFormatDateService; -import org.jclouds.openstack.nova.v2_0.domain.Extension; -import org.jclouds.openstack.nova.v2_0.extensions.ExtensionNamespaces; +import org.jclouds.openstack.v2_0.domain.Extension; import org.testng.annotations.Test; import com.sun.jersey.api.uri.UriBuilderImpl; @@ -39,6 +38,6 @@ public class ExtensionToNameSpaceTest { assertEquals(fn.apply(Extension.builder().alias("security_groups").name("SecurityGroups").namespace( URI.create("https://docs.openstack.org/ext/securitygroups/api/v1.1")).updated( new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-07-21T00:00:00+00:00")).description( - "Security group support").build()), URI.create(ExtensionNamespaces.SECURITY_GROUPS)); + "Security group support").build()), URI.create("http://docs.openstack.org/ext/securitygroups/api/v1.1")); } } diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSetTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSetTest.java similarity index 73% rename from apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSetTest.java rename to apis/openstack-keystone/src/test/java/org/jclouds/openstack/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSetTest.java index 60f56cdbb8..9fa985b1ca 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSetTest.java +++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSetTest.java @@ -1,4 +1,4 @@ -package org.jclouds.openstack.nova.v2_0.functions; +package org.jclouds.openstack.v2_0.functions; import static org.testng.Assert.assertEquals; @@ -9,10 +9,9 @@ import javax.inject.Named; import org.jclouds.date.internal.SimpleDateFormatDateService; import org.jclouds.internal.ClassMethodArgsAndReturnVal; -import org.jclouds.openstack.nova.v2_0.domain.Extension; -import org.jclouds.openstack.nova.v2_0.extensions.ExtensionNamespaces; -import org.jclouds.openstack.nova.v2_0.extensions.KeyPairAsyncApi; import org.jclouds.openstack.v2_0.ServiceType; +import org.jclouds.openstack.v2_0.domain.Extension; +import org.jclouds.openstack.v2_0.functions.PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet; import org.jclouds.rest.annotations.Delegate; import org.testng.annotations.Test; @@ -40,8 +39,8 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-08-08T00:00:00+00:00")).description( "Keypair Support").build(); - @org.jclouds.openstack.v2_0.services.Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.KEYPAIRS) - static interface KeyPairIPAsyncApi { + @org.jclouds.openstack.v2_0.services.Extension(of = ServiceType.COMPUTE, namespace = "http://docs.openstack.org/ext/keypairs/api/v1.1") + static interface KeyPairAsyncApi { } @@ -50,7 +49,7 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-06-16T00:00:00+00:00")).description( "Floating IPs support").build(); - @org.jclouds.openstack.v2_0.services.Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.FLOATING_IPS) + @org.jclouds.openstack.v2_0.services.Extension(of = ServiceType.COMPUTE, namespace = "http://docs.openstack.org/ext/floating_ips/api/v1.1") static interface FloatingIPAsyncApi { } @@ -68,27 +67,27 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio ClassMethodArgsAndReturnVal getFloatingIPExtension() throws SecurityException, NoSuchMethodException { return ClassMethodArgsAndReturnVal.builder().clazz(FloatingIPAsyncApi.class).method( NovaAsyncApi.class.getDeclaredMethod("getFloatingIPExtensionForZone", String.class)).args( - new Object[] { "expectedzone" }).returnVal("foo").build(); + new Object[] { "zone" }).returnVal("foo").build(); } ClassMethodArgsAndReturnVal getKeyPairExtension() throws SecurityException, NoSuchMethodException { return ClassMethodArgsAndReturnVal.builder().clazz(KeyPairAsyncApi.class).method( NovaAsyncApi.class.getDeclaredMethod("getKeyPairExtensionForZone", String.class)).args( - new Object[] { "expectedzone" }).returnVal("foo").build(); + new Object[] { "zone" }).returnVal("foo").build(); } public void testPresentWhenExtensionsIncludeNamespaceFromAnnotationAbsentWhenNot() throws SecurityException, NoSuchMethodException { - assertEquals(whenExtensionsInclude(keypairs, floatingIps).apply(getFloatingIPExtension()), Optional.of("foo")); - assertEquals(whenExtensionsInclude(keypairs, floatingIps).apply(getKeyPairExtension()), Optional.of("foo")); - assertEquals(whenExtensionsInclude(keypairs).apply(getFloatingIPExtension()), Optional.absent()); - assertEquals(whenExtensionsInclude(floatingIps).apply(getKeyPairExtension()), Optional.absent()); + assertEquals(whenExtensionsInZoneInclude("zone", keypairs, floatingIps).apply(getFloatingIPExtension()), Optional.of("foo")); + assertEquals(whenExtensionsInZoneInclude("zone", keypairs, floatingIps).apply(getKeyPairExtension()), Optional.of("foo")); + assertEquals(whenExtensionsInZoneInclude("zone", keypairs).apply(getFloatingIPExtension()), Optional.absent()); + assertEquals(whenExtensionsInZoneInclude("zone", floatingIps).apply(getKeyPairExtension()), Optional.absent()); } public void testZoneWithoutExtensionsReturnsAbsent() throws SecurityException, NoSuchMethodException { - assertEquals(whenExtensionsInclude(floatingIps).apply( + assertEquals(whenExtensionsInZoneInclude("zone", floatingIps).apply( getFloatingIPExtension().toBuilder().args(new Object[] { "differentzone" }).build()), Optional.absent()); - assertEquals(whenExtensionsInclude(keypairs).apply( + assertEquals(whenExtensionsInZoneInclude("zone", keypairs).apply( getKeyPairExtension().toBuilder().args(new Object[] { "differentzone" }).build()), Optional.absent()); } @@ -105,22 +104,22 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio Multimap aliases = ImmutableMultimap.of(keypairs.getNamespace(), keypairsWithDifferentNamespace .getNamespace()); - assertEquals(whenExtensionsAndAliasesInclude(ImmutableSet.of(keypairsWithDifferentNamespace), aliases).apply( + assertEquals(whenExtensionsAndAliasesInZoneInclude("zone", ImmutableSet.of(keypairsWithDifferentNamespace), aliases).apply( getKeyPairExtension()), Optional.of("foo")); - assertEquals(whenExtensionsAndAliasesInclude(ImmutableSet.of(keypairsWithDifferentNamespace), aliases).apply( + assertEquals(whenExtensionsAndAliasesInZoneInclude("zone", ImmutableSet.of(keypairsWithDifferentNamespace), aliases).apply( getFloatingIPExtension()), Optional.absent()); } - private PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet whenExtensionsInclude( - Extension... extensions) { - return whenExtensionsAndAliasesInclude(ImmutableSet.copyOf(extensions), ImmutableMultimap. of()); + private PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet whenExtensionsInZoneInclude( + String zone, Extension... extensions) { + return whenExtensionsAndAliasesInZoneInclude(zone, ImmutableSet.copyOf(extensions), ImmutableMultimap. of()); } - private PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet whenExtensionsAndAliasesInclude( - final Set extensions, final Multimap aliases) { + private PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet whenExtensionsAndAliasesInZoneInclude( + String zone, final Set extensions, final Multimap aliases) { final LoadingCache> extensionsForZone = CacheBuilder.newBuilder().build( - CacheLoader.from(Functions.forMap(ImmutableMap.>of("expectedzone", extensions, "differentzone", + CacheLoader.from(Functions.forMap(ImmutableMap.>of(zone, extensions, "differentzone", ImmutableSet. of())))); PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet fn = Guice.createInjector( @@ -135,7 +134,6 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio } @Provides - @Named("openstack.nova.extensions") Multimap getAliases() { return aliases; } diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/predicates/ExtensionPredicatesTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/v2_0/predicates/ExtensionPredicatesTest.java similarity index 88% rename from apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/predicates/ExtensionPredicatesTest.java rename to apis/openstack-keystone/src/test/java/org/jclouds/openstack/v2_0/predicates/ExtensionPredicatesTest.java index 02ad0307fa..859d1a3410 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/predicates/ExtensionPredicatesTest.java +++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/v2_0/predicates/ExtensionPredicatesTest.java @@ -16,15 +16,15 @@ * specific language governing permissions and limitations * under the License. */ -package org.jclouds.openstack.nova.v2_0.predicates; +package org.jclouds.openstack.v2_0.predicates; -import static org.jclouds.openstack.nova.v2_0.predicates.ExtensionPredicates.aliasEquals; -import static org.jclouds.openstack.nova.v2_0.predicates.ExtensionPredicates.namespaceEquals; +import static org.jclouds.openstack.v2_0.predicates.ExtensionPredicates.aliasEquals; +import static org.jclouds.openstack.v2_0.predicates.ExtensionPredicates.namespaceEquals; import java.net.URI; import org.jclouds.date.internal.SimpleDateFormatDateService; -import org.jclouds.openstack.nova.v2_0.domain.Extension; +import org.jclouds.openstack.v2_0.domain.Extension; import org.testng.annotations.Test; /** diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/NovaApi.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/NovaApi.java index 2cf173afd5..35eb3db0ca 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/NovaApi.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/NovaApi.java @@ -39,10 +39,10 @@ import org.jclouds.openstack.nova.v2_0.extensions.SimpleTenantUsageApi; import org.jclouds.openstack.nova.v2_0.extensions.VirtualInterfaceApi; import org.jclouds.openstack.nova.v2_0.extensions.VolumeApi; import org.jclouds.openstack.nova.v2_0.extensions.VolumeTypeApi; -import org.jclouds.openstack.nova.v2_0.features.ExtensionApi; import org.jclouds.openstack.nova.v2_0.features.FlavorApi; import org.jclouds.openstack.nova.v2_0.features.ImageApi; import org.jclouds.openstack.nova.v2_0.features.ServerApi; +import org.jclouds.openstack.v2_0.features.ExtensionApi; import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.EndpointParam; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/NovaAsyncApi.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/NovaAsyncApi.java index 53b780e320..3e71a78478 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/NovaAsyncApi.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/NovaAsyncApi.java @@ -37,10 +37,10 @@ import org.jclouds.openstack.nova.v2_0.extensions.SimpleTenantUsageAsyncApi; import org.jclouds.openstack.nova.v2_0.extensions.VirtualInterfaceAsyncApi; import org.jclouds.openstack.nova.v2_0.extensions.VolumeAsyncApi; import org.jclouds.openstack.nova.v2_0.extensions.VolumeTypeAsyncApi; -import org.jclouds.openstack.nova.v2_0.features.ExtensionAsyncApi; import org.jclouds.openstack.nova.v2_0.features.FlavorAsyncApi; import org.jclouds.openstack.nova.v2_0.features.ImageAsyncApi; import org.jclouds.openstack.nova.v2_0.features.ServerAsyncApi; +import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi; import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.EndpointParam; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/config/NovaRestClientModule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/config/NovaRestClientModule.java index a75b52dcb8..0bef0894e7 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/config/NovaRestClientModule.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/config/NovaRestClientModule.java @@ -18,6 +18,7 @@ */ package org.jclouds.openstack.nova.v2_0.config; +import java.net.URI; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -29,47 +30,48 @@ import org.jclouds.http.HttpErrorHandler; import org.jclouds.http.annotation.ClientError; import org.jclouds.http.annotation.Redirection; import org.jclouds.http.annotation.ServerError; -import org.jclouds.openstack.nova.v2_0.NovaAsyncApi; import org.jclouds.openstack.nova.v2_0.NovaApi; -import org.jclouds.openstack.nova.v2_0.domain.Extension; -import org.jclouds.openstack.nova.v2_0.extensions.AdminActionsAsyncApi; +import org.jclouds.openstack.nova.v2_0.NovaAsyncApi; import org.jclouds.openstack.nova.v2_0.extensions.AdminActionsApi; -import org.jclouds.openstack.nova.v2_0.extensions.FlavorExtraSpecsAsyncApi; +import org.jclouds.openstack.nova.v2_0.extensions.AdminActionsAsyncApi; +import org.jclouds.openstack.nova.v2_0.extensions.ExtensionNamespaces; import org.jclouds.openstack.nova.v2_0.extensions.FlavorExtraSpecsApi; -import org.jclouds.openstack.nova.v2_0.extensions.FloatingIPAsyncApi; +import org.jclouds.openstack.nova.v2_0.extensions.FlavorExtraSpecsAsyncApi; import org.jclouds.openstack.nova.v2_0.extensions.FloatingIPApi; -import org.jclouds.openstack.nova.v2_0.extensions.HostAdministrationAsyncApi; +import org.jclouds.openstack.nova.v2_0.extensions.FloatingIPAsyncApi; import org.jclouds.openstack.nova.v2_0.extensions.HostAdministrationApi; -import org.jclouds.openstack.nova.v2_0.extensions.HostAggregateAsyncApi; +import org.jclouds.openstack.nova.v2_0.extensions.HostAdministrationAsyncApi; import org.jclouds.openstack.nova.v2_0.extensions.HostAggregateApi; -import org.jclouds.openstack.nova.v2_0.extensions.KeyPairAsyncApi; +import org.jclouds.openstack.nova.v2_0.extensions.HostAggregateAsyncApi; import org.jclouds.openstack.nova.v2_0.extensions.KeyPairApi; -import org.jclouds.openstack.nova.v2_0.extensions.QuotaAsyncApi; -import org.jclouds.openstack.nova.v2_0.extensions.QuotaClassAsyncApi; -import org.jclouds.openstack.nova.v2_0.extensions.QuotaClassApi; +import org.jclouds.openstack.nova.v2_0.extensions.KeyPairAsyncApi; import org.jclouds.openstack.nova.v2_0.extensions.QuotaApi; -import org.jclouds.openstack.nova.v2_0.extensions.SecurityGroupAsyncApi; +import org.jclouds.openstack.nova.v2_0.extensions.QuotaAsyncApi; +import org.jclouds.openstack.nova.v2_0.extensions.QuotaClassApi; +import org.jclouds.openstack.nova.v2_0.extensions.QuotaClassAsyncApi; import org.jclouds.openstack.nova.v2_0.extensions.SecurityGroupApi; -import org.jclouds.openstack.nova.v2_0.extensions.ServerWithSecurityGroupsAsyncApi; +import org.jclouds.openstack.nova.v2_0.extensions.SecurityGroupAsyncApi; import org.jclouds.openstack.nova.v2_0.extensions.ServerWithSecurityGroupsApi; -import org.jclouds.openstack.nova.v2_0.extensions.SimpleTenantUsageAsyncApi; +import org.jclouds.openstack.nova.v2_0.extensions.ServerWithSecurityGroupsAsyncApi; import org.jclouds.openstack.nova.v2_0.extensions.SimpleTenantUsageApi; -import org.jclouds.openstack.nova.v2_0.extensions.VirtualInterfaceAsyncApi; +import org.jclouds.openstack.nova.v2_0.extensions.SimpleTenantUsageAsyncApi; import org.jclouds.openstack.nova.v2_0.extensions.VirtualInterfaceApi; -import org.jclouds.openstack.nova.v2_0.extensions.VolumeAsyncApi; +import org.jclouds.openstack.nova.v2_0.extensions.VirtualInterfaceAsyncApi; import org.jclouds.openstack.nova.v2_0.extensions.VolumeApi; -import org.jclouds.openstack.nova.v2_0.extensions.VolumeTypeAsyncApi; +import org.jclouds.openstack.nova.v2_0.extensions.VolumeAsyncApi; import org.jclouds.openstack.nova.v2_0.extensions.VolumeTypeApi; -import org.jclouds.openstack.nova.v2_0.features.ExtensionAsyncApi; -import org.jclouds.openstack.nova.v2_0.features.ExtensionApi; -import org.jclouds.openstack.nova.v2_0.features.FlavorAsyncApi; +import org.jclouds.openstack.nova.v2_0.extensions.VolumeTypeAsyncApi; import org.jclouds.openstack.nova.v2_0.features.FlavorApi; -import org.jclouds.openstack.nova.v2_0.features.ImageAsyncApi; +import org.jclouds.openstack.nova.v2_0.features.FlavorAsyncApi; import org.jclouds.openstack.nova.v2_0.features.ImageApi; -import org.jclouds.openstack.nova.v2_0.features.ServerAsyncApi; +import org.jclouds.openstack.nova.v2_0.features.ImageAsyncApi; import org.jclouds.openstack.nova.v2_0.features.ServerApi; -import org.jclouds.openstack.nova.v2_0.functions.PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet; +import org.jclouds.openstack.nova.v2_0.features.ServerAsyncApi; import org.jclouds.openstack.nova.v2_0.handlers.NovaErrorHandler; +import org.jclouds.openstack.v2_0.domain.Extension; +import org.jclouds.openstack.v2_0.features.ExtensionApi; +import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi; +import org.jclouds.openstack.v2_0.functions.PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet; import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.config.RestClientModule; import org.jclouds.rest.functions.ImplicitOptionalConverter; @@ -78,6 +80,8 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Multimap; import com.google.common.reflect.TypeToken; import com.google.inject.Provides; @@ -122,22 +126,54 @@ public class NovaRestClientModule ext @Override protected void configure() { install(new NovaParserModule()); - bind(ImplicitOptionalConverter.class).to( - PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.class); + bind(ImplicitOptionalConverter.class).to(PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.class); super.configure(); } + + @Provides + @Singleton + public Multimap aliases() { + return ImmutableMultimap.builder() + .put(URI.create(ExtensionNamespaces.SECURITY_GROUPS), + URI.create("http://docs.openstack.org/compute/ext/securitygroups/api/v1.1")) + .put(URI.create(ExtensionNamespaces.FLOATING_IPS), + URI.create("http://docs.openstack.org/compute/ext/floating_ips/api/v1.1")) + .put(URI.create(ExtensionNamespaces.KEYPAIRS), + URI.create("http://docs.openstack.org/compute/ext/keypairs/api/v1.1")) + .put(URI.create(ExtensionNamespaces.SIMPLE_TENANT_USAGE), + URI.create("http://docs.openstack.org/compute/ext/os-simple-tenant-usage/api/v1.1")) + .put(URI.create(ExtensionNamespaces.HOSTS), + URI.create("http://docs.openstack.org/compute/ext/hosts/api/v1.1")) + .put(URI.create(ExtensionNamespaces.VOLUMES), + URI.create("http://docs.openstack.org/compute/ext/volumes/api/v1.1")) + .put(URI.create(ExtensionNamespaces.VIRTUAL_INTERFACES), + URI.create("http://docs.openstack.org/compute/ext/virtual_interfaces/api/v1.1")) + .put(URI.create(ExtensionNamespaces.CREATESERVEREXT), + URI.create("http://docs.openstack.org/compute/ext/createserverext/api/v1.1")) + .put(URI.create(ExtensionNamespaces.ADMIN_ACTIONS), + URI.create("http://docs.openstack.org/compute/ext/admin-actions/api/v1.1")) + .put(URI.create(ExtensionNamespaces.AGGREGATES), + URI.create("http://docs.openstack.org/compute/ext/aggregates/api/v1.1")) + .put(URI.create(ExtensionNamespaces.FLAVOR_EXTRA_SPECS), + URI.create("http://docs.openstack.org/compute/ext/flavor_extra_specs/api/v1.1")) + .put(URI.create(ExtensionNamespaces.QUOTAS), + URI.create("http://docs.openstack.org/compute/ext/quotas-sets/api/v1.1")) + .put(URI.create(ExtensionNamespaces.QUOTA_CLASSES), + URI.create("http://docs.openstack.org/compute/ext/quota-classes-sets/api/v1.1")) + .put(URI.create(ExtensionNamespaces.VOLUME_TYPES), + URI.create("http://docs.openstack.org/compute/ext/volume_types/api/v1.1")) + .build(); + } @Provides @Singleton public LoadingCache> provideExtensionsByZone(final Provider novaApi) { return CacheBuilder.newBuilder().expireAfterWrite(23, TimeUnit.HOURS) .build(new CacheLoader>() { - @Override public Set load(String key) throws Exception { return novaApi.get().getExtensionApiForZone(key).listExtensions(); } - }); } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/extensions/FloatingIPAsyncApi.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/extensions/FloatingIPAsyncApi.java index 3b31192c95..2c09f82b5e 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/extensions/FloatingIPAsyncApi.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/extensions/FloatingIPAsyncApi.java @@ -31,8 +31,8 @@ import javax.ws.rs.core.MediaType; import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest; import org.jclouds.openstack.nova.v2_0.domain.FloatingIP; -import org.jclouds.openstack.nova.v2_0.features.ExtensionAsyncApi; import org.jclouds.openstack.v2_0.ServiceType; +import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi; import org.jclouds.openstack.v2_0.services.Extension; import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.Payload; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/extensions/KeyPairAsyncApi.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/extensions/KeyPairAsyncApi.java index 2b62fa85bd..c433b71e2a 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/extensions/KeyPairAsyncApi.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/extensions/KeyPairAsyncApi.java @@ -32,8 +32,8 @@ import javax.ws.rs.core.MediaType; import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest; import org.jclouds.openstack.nova.v2_0.domain.KeyPair; -import org.jclouds.openstack.nova.v2_0.features.ExtensionAsyncApi; import org.jclouds.openstack.v2_0.ServiceType; +import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi; import org.jclouds.openstack.v2_0.services.Extension; import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.Payload; diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java deleted file mode 100644 index 2656b978dc..0000000000 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java +++ /dev/null @@ -1,113 +0,0 @@ -/** - * 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.openstack.nova.v2_0.functions; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; - -import java.net.URI; -import java.util.Set; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; - -import org.jclouds.internal.ClassMethodArgsAndReturnVal; -import org.jclouds.openstack.nova.v2_0.domain.Extension; -import org.jclouds.openstack.nova.v2_0.extensions.ExtensionNamespaces; -import org.jclouds.openstack.nova.v2_0.predicates.ExtensionPredicates; -import org.jclouds.rest.functions.ImplicitOptionalConverter; - -import com.google.common.base.Optional; -import com.google.common.cache.LoadingCache; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.Iterables; -import com.google.common.collect.Multimap; - -/** - * We use the annotation {@link org.jclouds.openstack.services.Extension} to - * bind a class that is an extension to an extension found in the - * {@link org.jclouds.openstack.nova.v2_0.features.ExtensionApi#listExtensions} call. - * - * @author Adrian Cole - * - */ -@Singleton -public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet implements - ImplicitOptionalConverter { - private final LoadingCache> extensions; - - @com.google.inject.Inject(optional=true) - @Named("openstack.nova.extensions") - Multimap aliases = ImmutableMultimap.builder() - .put(URI.create(ExtensionNamespaces.SECURITY_GROUPS), - URI.create("http://docs.openstack.org/compute/ext/securitygroups/api/v1.1")) - .put(URI.create(ExtensionNamespaces.FLOATING_IPS), - URI.create("http://docs.openstack.org/compute/ext/floating_ips/api/v1.1")) - .put(URI.create(ExtensionNamespaces.KEYPAIRS), - URI.create("http://docs.openstack.org/compute/ext/keypairs/api/v1.1")) - .put(URI.create(ExtensionNamespaces.SIMPLE_TENANT_USAGE), - URI.create("http://docs.openstack.org/compute/ext/os-simple-tenant-usage/api/v1.1")) - .put(URI.create(ExtensionNamespaces.HOSTS), - URI.create("http://docs.openstack.org/compute/ext/hosts/api/v1.1")) - .put(URI.create(ExtensionNamespaces.VOLUMES), - URI.create("http://docs.openstack.org/compute/ext/volumes/api/v1.1")) - .put(URI.create(ExtensionNamespaces.VIRTUAL_INTERFACES), - URI.create("http://docs.openstack.org/compute/ext/virtual_interfaces/api/v1.1")) - .put(URI.create(ExtensionNamespaces.CREATESERVEREXT), - URI.create("http://docs.openstack.org/compute/ext/createserverext/api/v1.1")) - .put(URI.create(ExtensionNamespaces.ADMIN_ACTIONS), - URI.create("http://docs.openstack.org/compute/ext/admin-actions/api/v1.1")) - .put(URI.create(ExtensionNamespaces.AGGREGATES), - URI.create("http://docs.openstack.org/compute/ext/aggregates/api/v1.1")) - .put(URI.create(ExtensionNamespaces.FLAVOR_EXTRA_SPECS), - URI.create("http://docs.openstack.org/compute/ext/flavor_extra_specs/api/v1.1")) - .put(URI.create(ExtensionNamespaces.QUOTAS), - URI.create("http://docs.openstack.org/compute/ext/quotas-sets/api/v1.1")) - .put(URI.create(ExtensionNamespaces.QUOTA_CLASSES), - URI.create("http://docs.openstack.org/compute/ext/quota-classes-sets/api/v1.1")) - .put(URI.create(ExtensionNamespaces.VOLUME_TYPES), - URI.create("http://docs.openstack.org/compute/ext/volume_types/api/v1.1")) - .build(); - - @Inject - public PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet( - LoadingCache> extensions) { - this.extensions = checkNotNull(extensions, "extensions"); - } - - @Override - public Optional apply(ClassMethodArgsAndReturnVal input) { - Optional ext = Optional.fromNullable(input.getClazz().getAnnotation( - org.jclouds.openstack.v2_0.services.Extension.class)); - if (ext.isPresent()) { - checkState(input.getArgs() != null && input.getArgs().length == 1, "expecting an arg %s", input); - URI namespace = URI.create(ext.get().namespace()); - if (Iterables.any(extensions.getUnchecked(checkNotNull(input.getArgs()[0], "arg[0] in %s", input).toString()), - ExtensionPredicates.namespaceOrAliasEquals(namespace, aliases.get(namespace)))) - return Optional.of(input.getReturnVal()); - } - return Optional.absent(); - } - - public String toString() { - return "presentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet()"; - } - -} diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/AdminActionsApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/AdminActionsApiLiveTest.java index 803bb23e17..ae1bc4a33d 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/AdminActionsApiLiveTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/AdminActionsApiLiveTest.java @@ -27,11 +27,11 @@ import org.jclouds.http.HttpResponseException; import org.jclouds.openstack.nova.v2_0.domain.BackupType; import org.jclouds.openstack.nova.v2_0.domain.Image; import org.jclouds.openstack.nova.v2_0.domain.Server.Status; -import org.jclouds.openstack.nova.v2_0.features.ExtensionApi; import org.jclouds.openstack.nova.v2_0.features.ImageApi; import org.jclouds.openstack.nova.v2_0.features.ServerApi; import org.jclouds.openstack.nova.v2_0.internal.BaseNovaApiLiveTest; import org.jclouds.openstack.nova.v2_0.options.CreateBackupOfServerOptions; +import org.jclouds.openstack.v2_0.features.ExtensionApi; import org.testng.annotations.AfterGroups; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeGroups; diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ExtensionApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ExtensionApiLiveTest.java index 60d53b0a17..066b4e406f 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ExtensionApiLiveTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ExtensionApiLiveTest.java @@ -23,8 +23,9 @@ import static org.testng.Assert.assertTrue; import java.util.Set; -import org.jclouds.openstack.nova.v2_0.domain.Extension; import org.jclouds.openstack.nova.v2_0.internal.BaseNovaApiLiveTest; +import org.jclouds.openstack.v2_0.domain.Extension; +import org.jclouds.openstack.v2_0.features.ExtensionApi; import org.testng.annotations.Test; /** diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionListNormalTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionListNormalTest.java index 8902516d64..6debbdf10d 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionListNormalTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionListNormalTest.java @@ -28,7 +28,7 @@ import org.jclouds.date.internal.SimpleDateFormatDateService; import org.jclouds.json.BaseSetParserTest; import org.jclouds.json.config.GsonModule; import org.jclouds.openstack.nova.v2_0.config.NovaParserModule; -import org.jclouds.openstack.nova.v2_0.domain.Extension; +import org.jclouds.openstack.v2_0.domain.Extension; import org.jclouds.rest.annotations.SelectJson; import org.testng.annotations.Test; diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionListTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionListTest.java index c6741a7518..a19f6b6b04 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionListTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionListTest.java @@ -28,7 +28,7 @@ import org.jclouds.date.internal.SimpleDateFormatDateService; import org.jclouds.json.BaseSetParserTest; import org.jclouds.json.config.GsonModule; import org.jclouds.openstack.nova.v2_0.config.NovaParserModule; -import org.jclouds.openstack.nova.v2_0.domain.Extension; +import org.jclouds.openstack.v2_0.domain.Extension; import org.jclouds.openstack.v2_0.domain.Link; import org.jclouds.openstack.v2_0.domain.Link.Relation; import org.jclouds.rest.annotations.SelectJson; diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionTest.java index 4da904b432..956c3eedf6 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionTest.java @@ -27,7 +27,7 @@ import org.jclouds.date.internal.SimpleDateFormatDateService; import org.jclouds.json.BaseItemParserTest; import org.jclouds.json.config.GsonModule; import org.jclouds.openstack.nova.v2_0.config.NovaParserModule; -import org.jclouds.openstack.nova.v2_0.domain.Extension; +import org.jclouds.openstack.v2_0.domain.Extension; import org.jclouds.openstack.v2_0.domain.Link; import org.jclouds.openstack.v2_0.domain.Link.Relation; import org.jclouds.rest.annotations.SelectJson; diff --git a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/GlanceApi.java b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/GlanceApi.java index b066d7724f..e1a4867740 100644 --- a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/GlanceApi.java +++ b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/GlanceApi.java @@ -26,6 +26,7 @@ import org.jclouds.javax.annotation.Nullable; import org.jclouds.location.Region; import org.jclouds.location.functions.RegionToEndpoint; import org.jclouds.openstack.glance.v1_0.features.ImageApi; +import org.jclouds.openstack.v2_0.features.ExtensionApi; import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.EndpointParam; @@ -49,6 +50,13 @@ public interface GlanceApi { @Region Set getConfiguredRegions(); + /** + * Provides synchronous access to Extension features. + */ + @Delegate + ExtensionApi getExtensionApiForRegion( + @EndpointParam(parser = RegionToEndpoint.class) @Nullable String region); + /** * Provides synchronous access to Image features. */ diff --git a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/GlanceAsyncApi.java b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/GlanceAsyncApi.java index c67d9fd62b..d494d7a418 100644 --- a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/GlanceAsyncApi.java +++ b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/GlanceAsyncApi.java @@ -24,6 +24,7 @@ import org.jclouds.javax.annotation.Nullable; import org.jclouds.location.Region; import org.jclouds.location.functions.RegionToEndpoint; import org.jclouds.openstack.glance.v1_0.features.ImageAsyncApi; +import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi; import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.EndpointParam; @@ -46,6 +47,13 @@ public interface GlanceAsyncApi { @Region Set getConfiguredRegions(); + /** + * Provides asynchronous access to Extension features. + */ + @Delegate + ExtensionAsyncApi getExtensionApiForRegion( + @EndpointParam(parser = RegionToEndpoint.class) @Nullable String region); + /** * Provides asynchronous access to Image features. */ diff --git a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/config/GlanceRestClientModule.java b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/config/GlanceRestClientModule.java index c960417c48..e6d266a052 100644 --- a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/config/GlanceRestClientModule.java +++ b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/config/GlanceRestClientModule.java @@ -18,7 +18,13 @@ */ package org.jclouds.openstack.glance.v1_0.config; +import java.net.URI; import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import javax.inject.Provider; +import javax.inject.Singleton; import org.jclouds.http.HttpErrorHandler; import org.jclouds.http.annotation.ClientError; @@ -26,15 +32,27 @@ import org.jclouds.http.annotation.Redirection; import org.jclouds.http.annotation.ServerError; import org.jclouds.json.config.GsonModule.DateAdapter; import org.jclouds.json.config.GsonModule.Iso8601DateAdapter; -import org.jclouds.openstack.glance.v1_0.GlanceAsyncApi; import org.jclouds.openstack.glance.v1_0.GlanceApi; -import org.jclouds.openstack.glance.v1_0.features.ImageAsyncApi; +import org.jclouds.openstack.glance.v1_0.GlanceAsyncApi; import org.jclouds.openstack.glance.v1_0.features.ImageApi; +import org.jclouds.openstack.glance.v1_0.features.ImageAsyncApi; import org.jclouds.openstack.glance.v1_0.handlers.GlanceErrorHandler; +import org.jclouds.openstack.v2_0.domain.Extension; +import org.jclouds.openstack.v2_0.features.ExtensionApi; +import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi; +import org.jclouds.openstack.v2_0.functions.PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet; import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.config.RestClientModule; +import org.jclouds.rest.functions.ImplicitOptionalConverter; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Multimap; +import com.google.common.reflect.TypeToken; +import com.google.inject.Provides; /** * Configures the Glance connection. @@ -42,22 +60,47 @@ import com.google.common.collect.ImmutableMap; * @author Adrian Cole */ @ConfiguresRestClient -public class GlanceRestClientModule extends RestClientModule { +public class GlanceRestClientModule extends RestClientModule { public static final Map, Class> DELEGATE_MAP = ImmutableMap., Class> builder() + .put(ExtensionApi.class, ExtensionAsyncApi.class) .put(ImageApi.class, ImageAsyncApi.class) .build(); public GlanceRestClientModule() { - super(DELEGATE_MAP); + super(TypeToken.class.cast(TypeToken.of(GlanceApi.class)), TypeToken.class.cast(TypeToken.of(GlanceAsyncApi.class)), DELEGATE_MAP); + } + + protected GlanceRestClientModule(TypeToken syncClientType, TypeToken asyncClientType, Map, Class> sync2Async) { + super(syncClientType, asyncClientType, sync2Async); } @Override protected void configure() { bind(DateAdapter.class).to(Iso8601DateAdapter.class); + bind(ImplicitOptionalConverter.class).to(PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.class); super.configure(); } + @Provides + @Singleton + public Multimap aliases() { + return ImmutableMultimap.builder() + .build(); + } + + @Provides + @Singleton + public LoadingCache> provideExtensionsByZone(final Provider glanceApi) { + return CacheBuilder.newBuilder().expireAfterWrite(23, TimeUnit.HOURS) + .build(new CacheLoader>() { + @Override + public Set load(String key) throws Exception { + return glanceApi.get().getExtensionApiForRegion(key).listExtensions(); + } + }); + } + @Override protected void bindErrorHandlers() { bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(GlanceErrorHandler.class); diff --git a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/QuantumApi.java b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/QuantumApi.java index 92f2a67ca3..2f9a5ed1e0 100644 --- a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/QuantumApi.java +++ b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/QuantumApi.java @@ -28,8 +28,10 @@ import org.jclouds.concurrent.Timeout; import org.jclouds.javax.annotation.Nullable; import org.jclouds.location.Region; import org.jclouds.location.functions.RegionToEndpoint; +import org.jclouds.location.functions.ZoneToEndpoint; import org.jclouds.openstack.quantum.v1_0.features.NetworkApi; import org.jclouds.openstack.quantum.v1_0.features.PortApi; +import org.jclouds.openstack.v2_0.features.ExtensionApi; import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.EndpointParam; @@ -52,6 +54,13 @@ public interface QuantumApi { @Region Set getConfiguredRegions(); + /** + * Provides synchronous access to Extension features. + */ + @Delegate + ExtensionApi getExtensionApiForRegion( + @EndpointParam(parser = RegionToEndpoint.class) @Nullable String region); + /** * Provides synchronous access to Network features. */ diff --git a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/QuantumAsyncApi.java b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/QuantumAsyncApi.java index 70f1d198ea..0ea08a3960 100644 --- a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/QuantumAsyncApi.java +++ b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/QuantumAsyncApi.java @@ -28,6 +28,7 @@ import org.jclouds.location.Region; import org.jclouds.location.functions.RegionToEndpoint; import org.jclouds.openstack.quantum.v1_0.features.NetworkAsyncApi; import org.jclouds.openstack.quantum.v1_0.features.PortAsyncApi; +import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi; import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.EndpointParam; @@ -50,6 +51,13 @@ public interface QuantumAsyncApi { @Region Set getConfiguredRegions(); + /** + * Provides asynchronous access to Extension features. + */ + @Delegate + ExtensionAsyncApi getExtensionApiForRegion( + @EndpointParam(parser = RegionToEndpoint.class) @Nullable String region); + /** * Provides asynchronous access to Network features. */ diff --git a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/config/QuantumRestClientModule.java b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/config/QuantumRestClientModule.java index 7b659b1a20..4b7be5ba64 100644 --- a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/config/QuantumRestClientModule.java +++ b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/config/QuantumRestClientModule.java @@ -18,7 +18,13 @@ */ package org.jclouds.openstack.quantum.v1_0.config; +import java.net.URI; import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import javax.inject.Provider; +import javax.inject.Singleton; import org.jclouds.http.HttpErrorHandler; import org.jclouds.http.annotation.ClientError; @@ -26,17 +32,29 @@ import org.jclouds.http.annotation.Redirection; import org.jclouds.http.annotation.ServerError; import org.jclouds.json.config.GsonModule.DateAdapter; import org.jclouds.json.config.GsonModule.Iso8601DateAdapter; -import org.jclouds.openstack.quantum.v1_0.QuantumAsyncApi; import org.jclouds.openstack.quantum.v1_0.QuantumApi; -import org.jclouds.openstack.quantum.v1_0.features.NetworkAsyncApi; +import org.jclouds.openstack.quantum.v1_0.QuantumAsyncApi; import org.jclouds.openstack.quantum.v1_0.features.NetworkApi; -import org.jclouds.openstack.quantum.v1_0.features.PortAsyncApi; +import org.jclouds.openstack.quantum.v1_0.features.NetworkAsyncApi; import org.jclouds.openstack.quantum.v1_0.features.PortApi; +import org.jclouds.openstack.quantum.v1_0.features.PortAsyncApi; import org.jclouds.openstack.quantum.v1_0.handlers.QuantumErrorHandler; +import org.jclouds.openstack.v2_0.domain.Extension; +import org.jclouds.openstack.v2_0.features.ExtensionApi; +import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi; +import org.jclouds.openstack.v2_0.functions.PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet; import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.config.RestClientModule; +import org.jclouds.rest.functions.ImplicitOptionalConverter; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Multimap; +import com.google.common.reflect.TypeToken; +import com.google.inject.Provides; /** * Configures the Quantum connection. @@ -44,23 +62,48 @@ import com.google.common.collect.ImmutableMap; * @author Adam Lowe */ @ConfiguresRestClient -public class QuantumRestClientModule extends RestClientModule { +public class QuantumRestClientModule extends RestClientModule { public static final Map, Class> DELEGATE_MAP = ImmutableMap., Class> builder() + .put(ExtensionApi.class, ExtensionAsyncApi.class) .put(NetworkApi.class, NetworkAsyncApi.class) .put(PortApi.class, PortAsyncApi.class) .build(); public QuantumRestClientModule() { - super(DELEGATE_MAP); + super(TypeToken.class.cast(TypeToken.of(QuantumApi.class)), TypeToken.class.cast(TypeToken.of(QuantumAsyncApi.class)), DELEGATE_MAP); + } + + protected QuantumRestClientModule(TypeToken syncClientType, TypeToken asyncClientType, Map, Class> sync2Async) { + super(syncClientType, asyncClientType, sync2Async); } @Override protected void configure() { bind(DateAdapter.class).to(Iso8601DateAdapter.class); + bind(ImplicitOptionalConverter.class).to(PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.class); super.configure(); } + @Provides + @Singleton + public Multimap aliases() { + return ImmutableMultimap.builder() + .build(); + } + + @Provides + @Singleton + public LoadingCache> provideExtensionsByZone(final Provider quantumApi) { + return CacheBuilder.newBuilder().expireAfterWrite(23, TimeUnit.HOURS) + .build(new CacheLoader>() { + @Override + public Set load(String key) throws Exception { + return quantumApi.get().getExtensionApiForRegion(key).listExtensions(); + } + }); + } + @Override protected void bindErrorHandlers() { bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(QuantumErrorHandler.class); diff --git a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApi.java b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApi.java index f9e3fc5852..3fdbe8341a 100644 --- a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApi.java +++ b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApi.java @@ -28,6 +28,7 @@ import org.jclouds.location.functions.RegionToEndpoint; import org.jclouds.openstack.swift.v1.features.AccountApi; import org.jclouds.openstack.swift.v1.features.ContainerApi; import org.jclouds.openstack.swift.v1.features.ObjectApi; +import org.jclouds.openstack.v2_0.features.ExtensionApi; import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.EndpointParam; @@ -51,6 +52,13 @@ public interface SwiftApi { @Region Set getConfiguredRegions(); + /** + * Provides synchronous access to Extension features. + */ + @Delegate + ExtensionApi getExtensionApiForRegion( + @EndpointParam(parser = RegionToEndpoint.class) @Nullable String region); + /** * Provides synchronous access to Account features. */ diff --git a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftAsyncApi.java b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftAsyncApi.java index f74ad262c5..4f11970233 100644 --- a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftAsyncApi.java +++ b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftAsyncApi.java @@ -26,6 +26,7 @@ import org.jclouds.location.functions.RegionToEndpoint; import org.jclouds.openstack.swift.v1.features.AccountAsyncApi; import org.jclouds.openstack.swift.v1.features.ContainerAsyncApi; import org.jclouds.openstack.swift.v1.features.ObjectAsyncApi; +import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi; import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.EndpointParam; @@ -48,6 +49,13 @@ public interface SwiftAsyncApi { @Region Set getConfiguredRegions(); + /** + * Provides asynchronous access to Extension features. + */ + @Delegate + ExtensionAsyncApi getExtensionApiForRegion( + @EndpointParam(parser = RegionToEndpoint.class) @Nullable String region); + /** * Provides asynchronous access to Account features. */ diff --git a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/config/SwiftRestClientModule.java b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/config/SwiftRestClientModule.java index 5089f033b6..a0489d09d5 100644 --- a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/config/SwiftRestClientModule.java +++ b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/config/SwiftRestClientModule.java @@ -18,7 +18,13 @@ */ package org.jclouds.openstack.swift.v1.config; +import java.net.URI; import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import javax.inject.Provider; +import javax.inject.Singleton; import org.jclouds.http.HttpErrorHandler; import org.jclouds.http.annotation.ClientError; @@ -26,19 +32,31 @@ import org.jclouds.http.annotation.Redirection; import org.jclouds.http.annotation.ServerError; import org.jclouds.json.config.GsonModule.DateAdapter; import org.jclouds.json.config.GsonModule.Iso8601DateAdapter; -import org.jclouds.openstack.swift.v1.SwiftAsyncApi; import org.jclouds.openstack.swift.v1.SwiftApi; -import org.jclouds.openstack.swift.v1.features.AccountAsyncApi; +import org.jclouds.openstack.swift.v1.SwiftAsyncApi; import org.jclouds.openstack.swift.v1.features.AccountApi; -import org.jclouds.openstack.swift.v1.features.ContainerAsyncApi; +import org.jclouds.openstack.swift.v1.features.AccountAsyncApi; import org.jclouds.openstack.swift.v1.features.ContainerApi; -import org.jclouds.openstack.swift.v1.features.ObjectAsyncApi; +import org.jclouds.openstack.swift.v1.features.ContainerAsyncApi; import org.jclouds.openstack.swift.v1.features.ObjectApi; +import org.jclouds.openstack.swift.v1.features.ObjectAsyncApi; import org.jclouds.openstack.swift.v1.handlers.SwiftErrorHandler; +import org.jclouds.openstack.v2_0.domain.Extension; +import org.jclouds.openstack.v2_0.features.ExtensionApi; +import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi; +import org.jclouds.openstack.v2_0.functions.PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet; import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.config.RestClientModule; +import org.jclouds.rest.functions.ImplicitOptionalConverter; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Multimap; +import com.google.common.reflect.TypeToken; +import com.google.inject.Provides; /** * Configures the Swift connection. @@ -46,23 +64,48 @@ import com.google.common.collect.ImmutableMap; * @author Adrian Cole */ @ConfiguresRestClient -public class SwiftRestClientModule extends RestClientModule { +public class SwiftRestClientModule extends RestClientModule { public static final Map, Class> DELEGATE_MAP = ImmutableMap., Class> builder() + .put(ExtensionApi.class, ExtensionAsyncApi.class) .put(AccountApi.class, AccountAsyncApi.class) .put(ContainerApi.class, ContainerAsyncApi.class) .put(ObjectApi.class, ObjectAsyncApi.class) .build(); public SwiftRestClientModule() { - super(DELEGATE_MAP); + super(TypeToken.class.cast(TypeToken.of(SwiftApi.class)), TypeToken.class.cast(TypeToken.of(SwiftAsyncApi.class)), DELEGATE_MAP); + } + + protected SwiftRestClientModule(TypeToken syncClientType, TypeToken asyncClientType, Map, Class> sync2Async) { + super(syncClientType, asyncClientType, sync2Async); } @Override protected void configure() { bind(DateAdapter.class).to(Iso8601DateAdapter.class); + bind(ImplicitOptionalConverter.class).to(PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.class); super.configure(); } + + @Provides + @Singleton + public Multimap aliases() { + return ImmutableMultimap.builder() + .build(); + } + + @Provides + @Singleton + public LoadingCache> provideExtensionsByZone(final Provider swiftApi) { + return CacheBuilder.newBuilder().expireAfterWrite(23, TimeUnit.HOURS) + .build(new CacheLoader>() { + @Override + public Set load(String key) throws Exception { + return swiftApi.get().getExtensionApiForRegion(key).listExtensions(); + } + }); + } @Override protected void bindErrorHandlers() {