Updated Branches: refs/heads/master 04c2e1c35 -> 404b40795
JCLOUDS-107: Use Guice multibinding extensions Project: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/commit/404b4079 Tree: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/tree/404b4079 Diff: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/diff/404b4079 Branch: refs/heads/master Commit: 404b407953eaaaff554f45393fd189bae5a8a901 Parents: 04c2e1c Author: Ignasi Barrera <[email protected]> Authored: Tue Jun 4 18:36:36 2013 +0200 Committer: Ignasi Barrera <[email protected]> Committed: Thu Jun 6 17:35:52 2013 +0200 ---------------------------------------------------------------------- core/pom.xml | 6 + .../main/java/org/jclouds/chef/util/ChefUtils.java | 2 +- .../org/jclouds/ohai/config/JMXOhaiModule.java | 2 +- .../java/org/jclouds/ohai/config/OhaiModule.java | 2 +- .../jclouds/ohai/config/multibindings/Element.java | 37 - .../ohai/config/multibindings/MapBinder.java | 539 --------------- .../ohai/config/multibindings/Multibinder.java | 409 ----------- .../ohai/config/multibindings/RealElement.java | 63 -- .../ohai/config/multibindings/package-info.java | 21 - .../org/jclouds/ohai/config/OhaiModuleTest.java | 2 +- 10 files changed, 10 insertions(+), 1073 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/blob/404b4079/core/pom.xml ---------------------------------------------------------------------- diff --git a/core/pom.xml b/core/pom.xml index f4c4ac8..2035020 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -73,6 +73,12 @@ <type>test-jar</type> <scope>test</scope> </dependency> + <!-- for ohai --> + <dependency> + <groupId>com.google.inject.extensions</groupId> + <artifactId>guice-multibindings</artifactId> + <version>3.0</version> + </dependency> <!-- for transient chef provider --> <dependency> <groupId>org.apache.jclouds</groupId> http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/blob/404b4079/core/src/main/java/org/jclouds/chef/util/ChefUtils.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/jclouds/chef/util/ChefUtils.java b/core/src/main/java/org/jclouds/chef/util/ChefUtils.java index 0e9e66a..aff08d3 100644 --- a/core/src/main/java/org/jclouds/chef/util/ChefUtils.java +++ b/core/src/main/java/org/jclouds/chef/util/ChefUtils.java @@ -25,13 +25,13 @@ import java.util.regex.Pattern; import org.jclouds.domain.JsonBall; import org.jclouds.ohai.Automatic; -import org.jclouds.ohai.config.multibindings.MapBinder; import com.google.common.base.Predicate; import com.google.common.base.Supplier; import com.google.common.collect.Iterables; import com.google.inject.Binder; import com.google.inject.TypeLiteral; +import com.google.inject.multibindings.MapBinder; /** * http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/blob/404b4079/core/src/main/java/org/jclouds/ohai/config/JMXOhaiModule.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/jclouds/ohai/config/JMXOhaiModule.java b/core/src/main/java/org/jclouds/ohai/config/JMXOhaiModule.java index acff81a..1f106ec 100644 --- a/core/src/main/java/org/jclouds/ohai/config/JMXOhaiModule.java +++ b/core/src/main/java/org/jclouds/ohai/config/JMXOhaiModule.java @@ -22,11 +22,11 @@ import java.lang.management.RuntimeMXBean; import javax.inject.Singleton; import org.jclouds.domain.JsonBall; -import org.jclouds.ohai.config.multibindings.MapBinder; import org.jclouds.ohai.suppliers.UptimeSecondsSupplier; import com.google.common.base.Supplier; import com.google.inject.Provides; +import com.google.inject.multibindings.MapBinder; /** * Wires the components needed to parse ohai data from a JVM http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/blob/404b4079/core/src/main/java/org/jclouds/ohai/config/OhaiModule.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/jclouds/ohai/config/OhaiModule.java b/core/src/main/java/org/jclouds/ohai/config/OhaiModule.java index 62a7630..152b120 100644 --- a/core/src/main/java/org/jclouds/ohai/config/OhaiModule.java +++ b/core/src/main/java/org/jclouds/ohai/config/OhaiModule.java @@ -32,7 +32,6 @@ import org.jclouds.domain.JsonBall; import org.jclouds.json.Json; import org.jclouds.ohai.Automatic; import org.jclouds.ohai.AutomaticSupplier; -import org.jclouds.ohai.config.multibindings.MapBinder; import org.jclouds.ohai.functions.ByteArrayToMacAddress; import org.jclouds.ohai.functions.MapSetToMultimap; @@ -42,6 +41,7 @@ import com.google.common.collect.Multimap; import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.google.inject.TypeLiteral; +import com.google.inject.multibindings.MapBinder; /** * Wires the components needed to parse ohai data http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/blob/404b4079/core/src/main/java/org/jclouds/ohai/config/multibindings/Element.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/jclouds/ohai/config/multibindings/Element.java b/core/src/main/java/org/jclouds/ohai/config/multibindings/Element.java deleted file mode 100644 index db8d16a..0000000 --- a/core/src/main/java/org/jclouds/ohai/config/multibindings/Element.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jclouds.ohai.config.multibindings; - -import com.google.inject.BindingAnnotation; - -import java.lang.annotation.Retention; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * An internal binding annotation applied to each element in a multibinding. All - * elements are assigned a globally-unique id to allow different modules to - * contribute multibindings independently. - * - * @author [email protected] (Jesse Wilson) - */ -@Retention(RUNTIME) -@BindingAnnotation -@interface Element { - String setName(); - - int uniqueId(); -} http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/blob/404b4079/core/src/main/java/org/jclouds/ohai/config/multibindings/MapBinder.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/jclouds/ohai/config/multibindings/MapBinder.java b/core/src/main/java/org/jclouds/ohai/config/multibindings/MapBinder.java deleted file mode 100644 index 2cde1b0..0000000 --- a/core/src/main/java/org/jclouds/ohai/config/multibindings/MapBinder.java +++ /dev/null @@ -1,539 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jclouds.ohai.config.multibindings; - -import static com.google.inject.util.Types.newParameterizedTypeWithOwner; -import static org.jclouds.ohai.config.multibindings.Multibinder.checkConfiguration; -import static org.jclouds.ohai.config.multibindings.Multibinder.checkNotNull; -import static org.jclouds.ohai.config.multibindings.Multibinder.setOf; - -import java.lang.annotation.Annotation; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; -import java.util.Map.Entry; - -import org.jclouds.ohai.config.multibindings.Multibinder.RealMultibinder; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.inject.Binder; -import com.google.inject.Inject; -import com.google.inject.Injector; -import com.google.inject.Key; -import com.google.inject.Module; -import com.google.inject.Provider; -import com.google.inject.TypeLiteral; -import com.google.inject.binder.LinkedBindingBuilder; -import com.google.inject.spi.Dependency; -import com.google.inject.spi.ProviderWithDependencies; -import com.google.inject.util.Types; - -/** - * An API to bind multiple map entries separately, only to later inject them as - * a complete map. MapBinder is intended for use in your application's module: - * - * <pre> - * <code> - * public class SnacksModule extends AbstractModule { - * protected void configure() { - * MapBinder<String, Snack> mapbinder - * = MapBinder.newMapBinder(binder(), String.class, Snack.class); - * mapbinder.addBinding("twix").toInstance(new Twix()); - * mapbinder.addBinding("snickers").toProvider(SnickersProvider.class); - * mapbinder.addBinding("skittles").to(Skittles.class); - * } - * }</code> - * </pre> - * - * <p> - * With this binding, a {@link Map}{@code <String, Snack>} can now be injected: - * - * <pre> - * <code> - * class SnackMachine { - * {@literal @}Inject - * public SnackMachine(Map<String, Snack> snacks) { ... } - * }</code> - * </pre> - * - * <p> - * In addition to binding {@code Map<K, V>}, a mapbinder will also bind - * {@code Map<K, Provider<V>>} for lazy value provision: - * - * <pre> - * <code> - * class SnackMachine { - * {@literal @}Inject - * public SnackMachine(Map<String, Provider<Snack>> snackSuppliers) { ... } - * }</code> - * </pre> - * - * <p> - * Contributing mapbindings from different modules is supported. For example, it - * is okay to have both {@code CandyModule} and {@code ChipsModule} both create - * their own {@code MapBinder<String, Snack>}, and to each contribute bindings - * to the snacks map. When that map is injected, it will contain entries from - * both modules. - * - * <p> - * The map's iteration order is consistent with the binding order. This is - * convenient when multiple elements are contributed by the same module because - * that module can order its bindings appropriately. Avoid relying on the - * iteration order of elements contributed by different modules, since there is - * no equivalent mechanism to order modules. - * - * <p> - * Values are resolved at map injection time. If a value is bound to a provider, - * that provider's get method will be called each time the map is injected - * (unless the binding is also scoped, or a map of providers is injected). - * - * <p> - * Annotations are used to create different maps of the same key/value type. - * Each distinct annotation gets its own independent map. - * - * <p> - * <strong>Keys must be distinct.</strong> If the same key is bound more than - * once, map injection will fail. However, use {@link #permitDuplicates()} in - * order to allow duplicate keys; extra bindings to {@code Map<K, Set<V>>} and - * {@code Map<K, Set<Provider<V>>} will be added. - * - * <p> - * <strong>Keys must be non-null.</strong> {@code addBinding(null)} will throw - * an unchecked exception. - * - * <p> - * <strong>Values must be non-null to use map injection.</strong> If any value - * is null, map injection will fail (although injecting a map of providers will - * not). - * - * @author [email protected] (David P. Baker) - */ -public abstract class MapBinder<K, V> { - private MapBinder() { - } - - /** - * Returns a new mapbinder that collects entries of {@code keyType}/ - * {@code valueType} in a {@link Map} that is itself bound with no binding - * annotation. - */ - public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType) { - binder = binder.skipSources(MapBinder.class, RealMapBinder.class); - return newMapBinder(binder, valueType, Key.get(mapOf(keyType, valueType)), - Key.get(mapOfProviderOf(keyType, valueType)), Key.get(mapOf(keyType, setOf(valueType))), - Key.get(mapOfSetOfProviderOf(keyType, valueType)), - Multibinder.newSetBinder(binder, entryOfProviderOf(keyType, valueType))); - } - - /** - * Returns a new mapbinder that collects entries of {@code keyType}/ - * {@code valueType} in a {@link Map} that is itself bound with no binding - * annotation. - */ - public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, Class<K> keyType, Class<V> valueType) { - return newMapBinder(binder, TypeLiteral.get(keyType), TypeLiteral.get(valueType)); - } - - /** - * Returns a new mapbinder that collects entries of {@code keyType}/ - * {@code valueType} in a {@link Map} that is itself bound with - * {@code annotation}. - */ - public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType, - Annotation annotation) { - binder = binder.skipSources(MapBinder.class, RealMapBinder.class); - return newMapBinder(binder, valueType, Key.get(mapOf(keyType, valueType), annotation), - Key.get(mapOfProviderOf(keyType, valueType), annotation), - Key.get(mapOf(keyType, setOf(valueType)), annotation), - Key.get(mapOfSetOfProviderOf(keyType, valueType), annotation), - Multibinder.newSetBinder(binder, entryOfProviderOf(keyType, valueType), annotation)); - } - - /** - * Returns a new mapbinder that collects entries of {@code keyType}/ - * {@code valueType} in a {@link Map} that is itself bound with - * {@code annotation}. - */ - public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, Class<K> keyType, Class<V> valueType, - Annotation annotation) { - return newMapBinder(binder, TypeLiteral.get(keyType), TypeLiteral.get(valueType), annotation); - } - - /** - * Returns a new mapbinder that collects entries of {@code keyType}/ - * {@code valueType} in a {@link Map} that is itself bound with - * {@code annotationType}. - */ - public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType, - Class<? extends Annotation> annotationType) { - binder = binder.skipSources(MapBinder.class, RealMapBinder.class); - return newMapBinder(binder, valueType, Key.get(mapOf(keyType, valueType), annotationType), - Key.get(mapOfProviderOf(keyType, valueType), annotationType), - Key.get(mapOf(keyType, setOf(valueType)), annotationType), - Key.get(mapOfSetOfProviderOf(keyType, valueType), annotationType), - Multibinder.newSetBinder(binder, entryOfProviderOf(keyType, valueType), annotationType)); - } - - /** - * Returns a new mapbinder that collects entries of {@code keyType}/ - * {@code valueType} in a {@link Map} that is itself bound with - * {@code annotationType}. - */ - public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, Class<K> keyType, Class<V> valueType, - Class<? extends Annotation> annotationType) { - return newMapBinder(binder, TypeLiteral.get(keyType), TypeLiteral.get(valueType), annotationType); - } - - @SuppressWarnings("unchecked") - // a map of <K, V> is safely a Map<K, V> - private static <K, V> TypeLiteral<Map<K, V>> mapOf(TypeLiteral<K> keyType, TypeLiteral<V> valueType) { - return (TypeLiteral<Map<K, V>>) TypeLiteral.get(Types.mapOf(keyType.getType(), valueType.getType())); - } - - @SuppressWarnings("unchecked") - // a provider map <K, V> is safely a Map<K, Provider<V>> - private static <K, V> TypeLiteral<Map<K, Provider<V>>> mapOfProviderOf(TypeLiteral<K> keyType, - TypeLiteral<V> valueType) { - return (TypeLiteral<Map<K, Provider<V>>>) TypeLiteral.get(Types.mapOf(keyType.getType(), - Types.providerOf(valueType.getType()))); - } - - @SuppressWarnings("unchecked") - // a provider map <K, Set<V>> is safely a Map<K, Set<Provider<V>>> - private static <K, V> TypeLiteral<Map<K, Set<Provider<V>>>> mapOfSetOfProviderOf(TypeLiteral<K> keyType, - TypeLiteral<V> valueType) { - return (TypeLiteral<Map<K, Set<Provider<V>>>>) TypeLiteral.get(Types.mapOf(keyType.getType(), - Types.setOf(Types.providerOf(valueType.getType())))); - } - - @SuppressWarnings("unchecked") - // a provider entry <K, V> is safely a Map.Entry<K, Provider<V>> - private static <K, V> TypeLiteral<Map.Entry<K, Provider<V>>> entryOfProviderOf(TypeLiteral<K> keyType, - TypeLiteral<V> valueType) { - return (TypeLiteral<Entry<K, Provider<V>>>) TypeLiteral.get(newParameterizedTypeWithOwner(Map.class, Entry.class, - keyType.getType(), Types.providerOf(valueType.getType()))); - } - - private static <K, V> MapBinder<K, V> newMapBinder(Binder binder, TypeLiteral<V> valueType, Key<Map<K, V>> mapKey, - Key<Map<K, Provider<V>>> providerMapKey, Key<Map<K, Set<V>>> multimapKey, - Key<Map<K, Set<Provider<V>>>> providerMultimapKey, Multibinder<Entry<K, Provider<V>>> entrySetBinder) { - RealMapBinder<K, V> mapBinder = new RealMapBinder<K, V>(binder, valueType, mapKey, providerMapKey, multimapKey, - providerMultimapKey, entrySetBinder); - binder.install(mapBinder); - return mapBinder; - } - - /** - * Configures the {@code MapBinder} to handle duplicate entries. - * <p> - * When multiple equal keys are bound, the value that gets included in the - * map is arbitrary. - * <p> - * In addition to the {@code Map<K, V>} and {@code Map<K, Provider<V>>} maps - * that are normally bound, a {@code Map<K, Set<V>>} and {@code Map<K, - * Set<Provider<V>>>} are <em>also</em> bound, which contain all values bound - * to each key. - * <p> - * When multiple modules contribute elements to the map, this configuration - * option impacts all of them. - * - * @return this map binder - */ - public abstract MapBinder<K, V> permitDuplicates(); - - /** - * Returns a binding builder used to add a new entry in the map. Each key - * must be distinct (and non-null). Bound providers will be evaluated each - * time the map is injected. - * - * <p> - * It is an error to call this method without also calling one of the - * {@code to} methods on the returned binding builder. - * - * <p> - * Scoping elements independently is supported. Use the {@code in} method to - * specify a binding scope. - */ - public abstract LinkedBindingBuilder<V> addBinding(K key); - - /** - * The actual mapbinder plays several roles: - * - * <p> - * As a MapBinder, it acts as a factory for LinkedBindingBuilders for each of - * the map's values. It delegates to a {@link Multibinder} of entries (keys - * to value providers). - * - * <p> - * As a Module, it installs the binding to the map itself, as well as to a - * corresponding map whose values are providers. It uses the entry set - * multibinder to construct the map and the provider map. - * - * <p> - * As a module, this implements equals() and hashcode() in order to trick - * Guice into executing its configure() method only once. That makes it so - * that multiple mapbinders can be created for the same target map, but only - * one is bound. Since the list of bindings is retrieved from the injector - * itself (and not the mapbinder), each mapbinder has access to all - * contributions from all equivalent mapbinders. - * - * <p> - * Rather than binding a single Map.Entry<K, V>, the map binder binds - * keys and values independently. This allows the values to be properly - * scoped. - * - * <p> - * We use a subclass to hide 'implements Module' from the public API. - */ - private static final class RealMapBinder<K, V> extends MapBinder<K, V> implements Module { - private final TypeLiteral<V> valueType; - private final Key<Map<K, V>> mapKey; - private final Key<Map<K, Provider<V>>> providerMapKey; - private final Key<Map<K, Set<V>>> multimapKey; - private final Key<Map<K, Set<Provider<V>>>> providerMultimapKey; - private final RealMultibinder<Map.Entry<K, Provider<V>>> entrySetBinder; - - /* - * the target injector's binder. non-null until initialization, null - * afterwards - */ - private Binder binder; - - private RealMapBinder(Binder binder, TypeLiteral<V> valueType, Key<Map<K, V>> mapKey, - Key<Map<K, Provider<V>>> providerMapKey, Key<Map<K, Set<V>>> multimapKey, - Key<Map<K, Set<Provider<V>>>> providerMultimapKey, Multibinder<Map.Entry<K, Provider<V>>> entrySetBinder) { - this.valueType = valueType; - this.mapKey = mapKey; - this.providerMapKey = providerMapKey; - this.multimapKey = multimapKey; - this.providerMultimapKey = providerMultimapKey; - this.entrySetBinder = (RealMultibinder<Entry<K, Provider<V>>>) entrySetBinder; - this.binder = binder; - } - - @Override - public MapBinder<K, V> permitDuplicates() { - entrySetBinder.permitDuplicates(); - binder.install(new MultimapBinder<K, V>(multimapKey, providerMultimapKey, entrySetBinder.getSetKey())); - return this; - } - - /** - * This creates two bindings. One for the {@code Map.Entry<K, - * Provider<V>>} and another for {@code V}. - */ - @Override - public LinkedBindingBuilder<V> addBinding(K key) { - checkNotNull(key, "key"); - checkConfiguration(!isInitialized(), "MapBinder was already initialized"); - - Key<V> valueKey = Key.get(valueType, new RealElement(entrySetBinder.getSetName())); - entrySetBinder.addBinding().toInstance(new MapEntry<K, Provider<V>>(key, binder.getProvider(valueKey))); - return binder.bind(valueKey); - } - - public void configure(Binder binder) { - checkConfiguration(!isInitialized(), "MapBinder was already initialized"); - - final ImmutableSet<Dependency<?>> dependencies = ImmutableSet.<Dependency<?>> of(Dependency.get(entrySetBinder - .getSetKey())); - - // Binds a Map<K, Provider<V>> from a collection of Map<Entry<K, - // Provider<V>>. - final Provider<Set<Entry<K, Provider<V>>>> entrySetProvider = binder.getProvider(entrySetBinder.getSetKey()); - binder.bind(providerMapKey).toProvider(new ProviderWithDependencies<Map<K, Provider<V>>>() { - private Map<K, Provider<V>> providerMap; - - @SuppressWarnings("unused") - @Inject - void initialize(Injector injector) { - RealMapBinder.this.binder = null; - boolean permitDuplicates = entrySetBinder.permitsDuplicates(injector); - - Map<K, Provider<V>> providerMapMutable = new LinkedHashMap<K, Provider<V>>(); - for (Entry<K, Provider<V>> entry : entrySetProvider.get()) { - Provider<V> previous = providerMapMutable.put(entry.getKey(), entry.getValue()); - checkConfiguration(previous == null || permitDuplicates, - "Map injection failed due to duplicated key \"%s\"", entry.getKey()); - } - - providerMap = ImmutableMap.copyOf(providerMapMutable); - } - - public Map<K, Provider<V>> get() { - return providerMap; - } - - public Set<Dependency<?>> getDependencies() { - return dependencies; - } - }); - - final Provider<Map<K, Provider<V>>> mapProvider = binder.getProvider(providerMapKey); - binder.bind(mapKey).toProvider(new ProviderWithDependencies<Map<K, V>>() { - public Map<K, V> get() { - Map<K, V> map = new LinkedHashMap<K, V>(); - for (Entry<K, Provider<V>> entry : mapProvider.get().entrySet()) { - V value = entry.getValue().get(); - K key = entry.getKey(); - checkConfiguration(value != null, "Map injection failed due to null value for key \"%s\"", key); - map.put(key, value); - } - return Collections.unmodifiableMap(map); - } - - public Set<Dependency<?>> getDependencies() { - return dependencies; - } - }); - } - - private boolean isInitialized() { - return binder == null; - } - - @Override - public boolean equals(Object o) { - return o instanceof RealMapBinder<?, ?> && ((RealMapBinder<?, ?>) o).mapKey.equals(mapKey); - } - - @Override - public int hashCode() { - return mapKey.hashCode(); - } - - /** - * Binds {@code Map<K, Set<V>>} and {{@code Map<K, Set<Provider<V>>>}. - */ - private static final class MultimapBinder<K, V> implements Module { - - private final Key<Map<K, Set<V>>> multimapKey; - private final Key<Map<K, Set<Provider<V>>>> providerMultimapKey; - private final Key<Set<Entry<K, Provider<V>>>> entrySetKey; - - public MultimapBinder(Key<Map<K, Set<V>>> multimapKey, Key<Map<K, Set<Provider<V>>>> providerMultimapKey, - Key<Set<Entry<K, Provider<V>>>> entrySetKey) { - this.multimapKey = multimapKey; - this.providerMultimapKey = providerMultimapKey; - this.entrySetKey = entrySetKey; - } - - public void configure(Binder binder) { - final ImmutableSet<Dependency<?>> dependencies = ImmutableSet.<Dependency<?>> of(Dependency - .get(entrySetKey)); - - final Provider<Set<Entry<K, Provider<V>>>> entrySetProvider = binder.getProvider(entrySetKey); - // Binds a Map<K, Set<Provider<V>>> from a collection of - // Map<Entry<K, Provider<V>> if - // permitDuplicates was called. - binder.bind(providerMultimapKey).toProvider(new ProviderWithDependencies<Map<K, Set<Provider<V>>>>() { - private Map<K, Set<Provider<V>>> providerMultimap; - - @SuppressWarnings("unused") - @Inject - void initialize(Injector injector) { - Map<K, ImmutableSet.Builder<Provider<V>>> providerMultimapMutable = new LinkedHashMap<K, ImmutableSet.Builder<Provider<V>>>(); - for (Entry<K, Provider<V>> entry : entrySetProvider.get()) { - if (!providerMultimapMutable.containsKey(entry.getKey())) { - providerMultimapMutable.put(entry.getKey(), ImmutableSet.<Provider<V>> builder()); - } - providerMultimapMutable.get(entry.getKey()).add(entry.getValue()); - } - - ImmutableMap.Builder<K, Set<Provider<V>>> providerMultimapBuilder = ImmutableMap.builder(); - for (Entry<K, ImmutableSet.Builder<Provider<V>>> entry : providerMultimapMutable.entrySet()) { - providerMultimapBuilder.put(entry.getKey(), entry.getValue().build()); - } - providerMultimap = providerMultimapBuilder.build(); - } - - public Map<K, Set<Provider<V>>> get() { - return providerMultimap; - } - - public Set<Dependency<?>> getDependencies() { - return dependencies; - } - }); - - final Provider<Map<K, Set<Provider<V>>>> multimapProvider = binder.getProvider(providerMultimapKey); - binder.bind(multimapKey).toProvider(new ProviderWithDependencies<Map<K, Set<V>>>() { - - public Map<K, Set<V>> get() { - ImmutableMap.Builder<K, Set<V>> multimapBuilder = ImmutableMap.builder(); - for (Entry<K, Set<Provider<V>>> entry : multimapProvider.get().entrySet()) { - K key = entry.getKey(); - ImmutableSet.Builder<V> valuesBuilder = ImmutableSet.builder(); - for (Provider<V> valueProvider : entry.getValue()) { - V value = valueProvider.get(); - checkConfiguration(value != null, "Multimap injection failed due to null value for key \"%s\"", - key); - valuesBuilder.add(value); - } - multimapBuilder.put(key, valuesBuilder.build()); - } - return multimapBuilder.build(); - } - - public Set<Dependency<?>> getDependencies() { - return dependencies; - } - }); - } - } - - private static final class MapEntry<K, V> implements Map.Entry<K, V> { - private final K key; - private final V value; - - private MapEntry(K key, V value) { - this.key = key; - this.value = value; - } - - public K getKey() { - return key; - } - - public V getValue() { - return value; - } - - public V setValue(V value) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean equals(Object obj) { - return obj instanceof Map.Entry<?, ?> && key.equals(((Map.Entry<?, ?>) obj).getKey()) - && value.equals(((Map.Entry<?, ?>) obj).getValue()); - } - - @Override - public int hashCode() { - return 127 * ("key".hashCode() ^ key.hashCode()) + 127 * ("value".hashCode() ^ value.hashCode()); - } - - @Override - public String toString() { - return "MapEntry(" + key + ", " + value + ")"; - } - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/blob/404b4079/core/src/main/java/org/jclouds/ohai/config/multibindings/Multibinder.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/jclouds/ohai/config/multibindings/Multibinder.java b/core/src/main/java/org/jclouds/ohai/config/multibindings/Multibinder.java deleted file mode 100644 index 3d521b2..0000000 --- a/core/src/main/java/org/jclouds/ohai/config/multibindings/Multibinder.java +++ /dev/null @@ -1,409 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jclouds.ohai.config.multibindings; - -import static com.google.inject.name.Names.named; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Lists; -import com.google.inject.AbstractModule; -import com.google.inject.Binder; -import com.google.inject.Binding; -import com.google.inject.ConfigurationException; -import com.google.inject.Inject; -import com.google.inject.Injector; -import com.google.inject.Key; -import com.google.inject.Module; -import com.google.inject.Provider; -import com.google.inject.TypeLiteral; -import com.google.inject.binder.LinkedBindingBuilder; -import com.google.inject.spi.Dependency; -import com.google.inject.spi.HasDependencies; -import com.google.inject.spi.Message; -import com.google.inject.spi.Toolable; -import com.google.inject.util.Types; - -/** - * - * An API to bind multiple values separately, only to later inject them as a - * complete collection. Multibinder is intended for use in your application's - * module: - * - * <pre> - * <code> - * public class SnacksModule extends AbstractModule { - * protected void configure() { - * Multibinder<Snack> multibinder - * = Multibinder.newSetBinder(binder(), Snack.class); - * multibinder.addBinding().toInstance(new Twix()); - * multibinder.addBinding().toProvider(SnickersProvider.class); - * multibinder.addBinding().to(Skittles.class); - * } - * }</code> - * </pre> - * - * <p> - * With this binding, a {@link Set}{@code <Snack>} can now be injected: - * - * <pre> - * <code> - * class SnackMachine { - * {@literal @}Inject - * public SnackMachine(Set<Snack> snacks) { ... } - * }</code> - * </pre> - * - * <p> - * Contributing multibindings from different modules is supported. For example, - * it is okay to have both {@code CandyModule} and {@code ChipsModule} to both - * create their own {@code Multibinder<Snack>}, and to each contribute bindings - * to the set of snacks. When that set is injected, it will contain elements - * from both modules. - * - * <p> - * The set's iteration order is consistent with the binding order. This is - * convenient when multiple elements are contributed by the same module because - * that module can order its bindings appropriately. Avoid relying on the - * iteration order of elements contributed by different modules, since there is - * no equivalent mechanism to order modules. - * - * <p> - * Elements are resolved at set injection time. If an element is bound to a - * provider, that provider's get method will be called each time the set is - * injected (unless the binding is also scoped). - * - * <p> - * Annotations are be used to create different sets of the same element type. - * Each distinct annotation gets its own independent collection of elements. - * - * <p> - * <strong>Elements must be distinct.</strong> If multiple bound elements have - * the same value, set injection will fail. - * - * <p> - * <strong>Elements must be non-null.</strong> If any set element is null, set - * injection will fail. - * - * @author [email protected] (Jesse Wilson) - */ -public abstract class Multibinder<T> { - private Multibinder() { - } - - /** - * Returns a new multibinder that collects instances of {@code type} in a - * {@link Set} that is itself bound with no binding annotation. - */ - public static <T> Multibinder<T> newSetBinder(Binder binder, TypeLiteral<T> type) { - binder = binder.skipSources(RealMultibinder.class, Multibinder.class); - RealMultibinder<T> result = new RealMultibinder<T>(binder, type, "", Key.get(Multibinder.<T> setOf(type))); - binder.install(result); - return result; - } - - /** - * Returns a new multibinder that collects instances of {@code type} in a - * {@link Set} that is itself bound with no binding annotation. - */ - public static <T> Multibinder<T> newSetBinder(Binder binder, Class<T> type) { - return newSetBinder(binder, TypeLiteral.get(type)); - } - - /** - * Returns a new multibinder that collects instances of {@code type} in a - * {@link Set} that is itself bound with {@code annotation}. - */ - public static <T> Multibinder<T> newSetBinder(Binder binder, TypeLiteral<T> type, Annotation annotation) { - binder = binder.skipSources(RealMultibinder.class, Multibinder.class); - RealMultibinder<T> result = new RealMultibinder<T>(binder, type, annotation.toString(), Key.get( - Multibinder.<T> setOf(type), annotation)); - binder.install(result); - return result; - } - - /** - * Returns a new multibinder that collects instances of {@code type} in a - * {@link Set} that is itself bound with {@code annotation}. - */ - public static <T> Multibinder<T> newSetBinder(Binder binder, Class<T> type, Annotation annotation) { - return newSetBinder(binder, TypeLiteral.get(type), annotation); - } - - /** - * Returns a new multibinder that collects instances of {@code type} in a - * {@link Set} that is itself bound with {@code annotationType}. - */ - public static <T> Multibinder<T> newSetBinder(Binder binder, TypeLiteral<T> type, - Class<? extends Annotation> annotationType) { - binder = binder.skipSources(RealMultibinder.class, Multibinder.class); - RealMultibinder<T> result = new RealMultibinder<T>(binder, type, "@" + annotationType.getName(), Key.get( - Multibinder.<T> setOf(type), annotationType)); - binder.install(result); - return result; - } - - /** - * Returns a new multibinder that collects instances of {@code type} in a - * {@link Set} that is itself bound with {@code annotationType}. - */ - public static <T> Multibinder<T> newSetBinder(Binder binder, Class<T> type, - Class<? extends Annotation> annotationType) { - return newSetBinder(binder, TypeLiteral.get(type), annotationType); - } - - @SuppressWarnings("unchecked") - // wrapping a T in a Set safely returns a Set<T> - static <T> TypeLiteral<Set<T>> setOf(TypeLiteral<T> elementType) { - Type type = Types.setOf(elementType.getType()); - return (TypeLiteral<Set<T>>) TypeLiteral.get(type); - } - - /** - * Configures the bound set to silently discard duplicate elements. When - * multiple equal values are bound, the one that gets included is arbitrary. - * When multiple modules contribute elements to the set, this configuration - * option impacts all of them. - * - * @return this multibinder - */ - public abstract Multibinder<T> permitDuplicates(); - - /** - * Returns a binding builder used to add a new element in the set. Each bound - * element must have a distinct value. Bound providers will be evaluated each - * time the set is injected. - * - * <p> - * It is an error to call this method without also calling one of the - * {@code to} methods on the returned binding builder. - * - * <p> - * Scoping elements independently is supported. Use the {@code in} method to - * specify a binding scope. - */ - public abstract LinkedBindingBuilder<T> addBinding(); - - /** - * The actual multibinder plays several roles: - * - * <p> - * As a Multibinder, it acts as a factory for LinkedBindingBuilders for each - * of the set's elements. Each binding is given an annotation that identifies - * it as a part of this set. - * - * <p> - * As a Module, it installs the binding to the set itself. As a module, this - * implements equals() and hashcode() in order to trick Guice into executing - * its configure() method only once. That makes it so that multiple - * multibinders can be created for the same target collection, but only one - * is bound. Since the list of bindings is retrieved from the injector itself - * (and not the multibinder), each multibinder has access to all - * contributions from all multibinders. - * - * <p> - * As a Provider, this constructs the set instances. - * - * <p> - * We use a subclass to hide 'implements Module, Provider' from the public - * API. - */ - static final class RealMultibinder<T> extends Multibinder<T> implements Module, Provider<Set<T>>, HasDependencies { - - private final TypeLiteral<T> elementType; - private final String setName; - private final Key<Set<T>> setKey; - private final Key<Boolean> permitDuplicatesKey; - - /* - * the target injector's binder. non-null until initialization, null - * afterwards - */ - private Binder binder; - - /* - * a provider for each element in the set. null until initialization, - * non-null afterwards - */ - private List<Provider<T>> providers; - private Set<Dependency<?>> dependencies; - - /** - * whether duplicates are allowed. Possibly configured by a different - * instance - */ - private boolean permitDuplicates; - - private RealMultibinder(Binder binder, TypeLiteral<T> elementType, String setName, Key<Set<T>> setKey) { - this.binder = checkNotNull(binder, "binder"); - this.elementType = checkNotNull(elementType, "elementType"); - this.setName = checkNotNull(setName, "setName"); - this.setKey = checkNotNull(setKey, "setKey"); - this.permitDuplicatesKey = Key.get(Boolean.class, named(toString() + " permits duplicates")); - } - - public void configure(Binder binder) { - checkConfiguration(!isInitialized(), "Multibinder was already initialized"); - binder.bind(setKey).toProvider(this); - } - - @Override - public Multibinder<T> permitDuplicates() { - binder.install(new PermitDuplicatesModule(permitDuplicatesKey)); - return this; - } - - @Override - public LinkedBindingBuilder<T> addBinding() { - checkConfiguration(!isInitialized(), "Multibinder was already initialized"); - - return binder.bind(Key.get(elementType, new RealElement(setName))); - } - - /** - * Invoked by Guice at Injector-creation time to prepare providers for - * each element in this set. At this time the set's size is known, but its - * contents are only evaluated when get() is invoked. - */ - @Toolable - @Inject - void initialize(Injector injector) { - providers = Lists.newArrayList(); - List<Dependency<?>> dependencies = Lists.newArrayList(); - for (Binding<?> entry : injector.findBindingsByType(elementType)) { - - if (keyMatches(entry.getKey())) { - @SuppressWarnings("unchecked") - // protected by findBindingsByType() - Binding<T> binding = (Binding<T>) entry; - providers.add(binding.getProvider()); - dependencies.add(Dependency.get(binding.getKey())); - } - } - - this.dependencies = ImmutableSet.copyOf(dependencies); - this.permitDuplicates = permitsDuplicates(injector); - this.binder = null; - } - - boolean permitsDuplicates(Injector injector) { - return injector.getBindings().containsKey(permitDuplicatesKey); - } - - private boolean keyMatches(Key<?> key) { - return key.getTypeLiteral().equals(elementType) && key.getAnnotation() instanceof Element - && ((Element) key.getAnnotation()).setName().equals(setName); - } - - private boolean isInitialized() { - return binder == null; - } - - public Set<T> get() { - checkConfiguration(isInitialized(), "Multibinder is not initialized"); - - Set<T> result = new LinkedHashSet<T>(); - for (Provider<T> provider : providers) { - final T newValue = provider.get(); - checkConfiguration(newValue != null, "Set injection failed due to null element"); - checkConfiguration(result.add(newValue) || permitDuplicates, - "Set injection failed due to duplicated element \"%s\"", newValue); - } - return Collections.unmodifiableSet(result); - } - - String getSetName() { - return setName; - } - - Key<Set<T>> getSetKey() { - return setKey; - } - - public Set<Dependency<?>> getDependencies() { - return dependencies; - } - - @Override - public boolean equals(Object o) { - return o instanceof RealMultibinder<?> && ((RealMultibinder<?>) o).setKey.equals(setKey); - } - - @Override - public int hashCode() { - return setKey.hashCode(); - } - - @Override - public String toString() { - return new StringBuilder().append(setName).append(setName.length() > 0 ? " " : "").append("Multibinder<") - .append(elementType).append(">").toString(); - } - } - - /** - * We install the permit duplicates configuration as its own binding, all by - * itself. This way, if only one of a multibinder's users remember to call - * permitDuplicates(), they're still permitted. - */ - private static class PermitDuplicatesModule extends AbstractModule { - private final Key<Boolean> key; - - PermitDuplicatesModule(Key<Boolean> key) { - this.key = key; - } - - @Override - protected void configure() { - bind(key).toInstance(true); - } - - @Override - public boolean equals(Object o) { - return o instanceof PermitDuplicatesModule && ((PermitDuplicatesModule) o).key.equals(key); - } - - @Override - public int hashCode() { - return getClass().hashCode() ^ key.hashCode(); - } - } - - static void checkConfiguration(boolean condition, String format, Object... args) { - if (condition) { - return; - } - - throw new ConfigurationException(ImmutableSet.of(new Message(String.format(format, args)))); - } - - static <T> T checkNotNull(T reference, String name) { - if (reference != null) { - return reference; - } - - NullPointerException npe = new NullPointerException(name); - throw new ConfigurationException(ImmutableSet.of(new Message(ImmutableList.of(), npe.toString(), npe))); - } -} http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/blob/404b4079/core/src/main/java/org/jclouds/ohai/config/multibindings/RealElement.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/jclouds/ohai/config/multibindings/RealElement.java b/core/src/main/java/org/jclouds/ohai/config/multibindings/RealElement.java deleted file mode 100644 index f727445..0000000 --- a/core/src/main/java/org/jclouds/ohai/config/multibindings/RealElement.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jclouds.ohai.config.multibindings; - -import java.lang.annotation.Annotation; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * @author [email protected] (Jesse Wilson) - */ -class RealElement implements Element { - private static final AtomicInteger nextUniqueId = new AtomicInteger(1); - - private final int uniqueId; - private final String setName; - - RealElement(String setName) { - uniqueId = nextUniqueId.getAndIncrement(); - this.setName = setName; - } - - public String setName() { - return setName; - } - - public int uniqueId() { - return uniqueId; - } - - public Class<? extends Annotation> annotationType() { - return Element.class; - } - - @Override - public String toString() { - return "@" + Element.class.getName() + "(setName=" + setName + ",uniqueId=" + uniqueId + ")"; - } - - @Override - public boolean equals(Object o) { - return o instanceof Element && ((Element) o).setName().equals(setName()) - && ((Element) o).uniqueId() == uniqueId(); - } - - @Override - public int hashCode() { - return 127 * ("setName".hashCode() ^ setName.hashCode()) + 127 * ("uniqueId".hashCode() ^ uniqueId); - } -} http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/blob/404b4079/core/src/main/java/org/jclouds/ohai/config/multibindings/package-info.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/jclouds/ohai/config/multibindings/package-info.java b/core/src/main/java/org/jclouds/ohai/config/multibindings/package-info.java deleted file mode 100644 index cccfea4..0000000 --- a/core/src/main/java/org/jclouds/ohai/config/multibindings/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Taken from r1154 of Guice, as they decided to stop supporting multiple bindings and instead silently throw them away. - */ -package org.jclouds.ohai.config.multibindings; - http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/blob/404b4079/core/src/test/java/org/jclouds/ohai/config/OhaiModuleTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/jclouds/ohai/config/OhaiModuleTest.java b/core/src/test/java/org/jclouds/ohai/config/OhaiModuleTest.java index 213106d..e2bed2e 100644 --- a/core/src/test/java/org/jclouds/ohai/config/OhaiModuleTest.java +++ b/core/src/test/java/org/jclouds/ohai/config/OhaiModuleTest.java @@ -31,7 +31,6 @@ import org.jclouds.domain.JsonBall; import org.jclouds.json.Json; import org.jclouds.json.config.GsonModule; import org.jclouds.ohai.Automatic; -import org.jclouds.ohai.config.multibindings.MapBinder; import org.jclouds.rest.annotations.ApiVersion; import org.testng.annotations.Test; @@ -41,6 +40,7 @@ import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.TypeLiteral; +import com.google.inject.multibindings.MapBinder; import com.google.inject.util.Providers; /**
