mirror of https://github.com/apache/jclouds.git
JCLOUDS-107: Use Guice multibinding extensions
This commit is contained in:
parent
ea262c3995
commit
2701655f91
|
@ -73,6 +73,12 @@
|
||||||
<type>test-jar</type>
|
<type>test-jar</type>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- for ohai -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.inject.extensions</groupId>
|
||||||
|
<artifactId>guice-multibindings</artifactId>
|
||||||
|
<version>3.0</version>
|
||||||
|
</dependency>
|
||||||
<!-- for transient chef provider -->
|
<!-- for transient chef provider -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.jclouds</groupId>
|
<groupId>org.apache.jclouds</groupId>
|
||||||
|
|
|
@ -25,13 +25,13 @@ import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.jclouds.domain.JsonBall;
|
import org.jclouds.domain.JsonBall;
|
||||||
import org.jclouds.ohai.Automatic;
|
import org.jclouds.ohai.Automatic;
|
||||||
import org.jclouds.ohai.config.multibindings.MapBinder;
|
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.base.Supplier;
|
import com.google.common.base.Supplier;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.inject.Binder;
|
import com.google.inject.Binder;
|
||||||
import com.google.inject.TypeLiteral;
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.multibindings.MapBinder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
|
@ -22,11 +22,11 @@ import java.lang.management.RuntimeMXBean;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.domain.JsonBall;
|
import org.jclouds.domain.JsonBall;
|
||||||
import org.jclouds.ohai.config.multibindings.MapBinder;
|
|
||||||
import org.jclouds.ohai.suppliers.UptimeSecondsSupplier;
|
import org.jclouds.ohai.suppliers.UptimeSecondsSupplier;
|
||||||
|
|
||||||
import com.google.common.base.Supplier;
|
import com.google.common.base.Supplier;
|
||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
|
import com.google.inject.multibindings.MapBinder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wires the components needed to parse ohai data from a JVM
|
* Wires the components needed to parse ohai data from a JVM
|
||||||
|
|
|
@ -32,7 +32,6 @@ import org.jclouds.domain.JsonBall;
|
||||||
import org.jclouds.json.Json;
|
import org.jclouds.json.Json;
|
||||||
import org.jclouds.ohai.Automatic;
|
import org.jclouds.ohai.Automatic;
|
||||||
import org.jclouds.ohai.AutomaticSupplier;
|
import org.jclouds.ohai.AutomaticSupplier;
|
||||||
import org.jclouds.ohai.config.multibindings.MapBinder;
|
|
||||||
import org.jclouds.ohai.functions.ByteArrayToMacAddress;
|
import org.jclouds.ohai.functions.ByteArrayToMacAddress;
|
||||||
import org.jclouds.ohai.functions.MapSetToMultimap;
|
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.AbstractModule;
|
||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
import com.google.inject.TypeLiteral;
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.multibindings.MapBinder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wires the components needed to parse ohai data
|
* Wires the components needed to parse ohai data
|
||||||
|
|
|
@ -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 jessewilson@google.com (Jesse Wilson)
|
|
||||||
*/
|
|
||||||
@Retention(RUNTIME)
|
|
||||||
@BindingAnnotation
|
|
||||||
@interface Element {
|
|
||||||
String setName();
|
|
||||||
|
|
||||||
int uniqueId();
|
|
||||||
}
|
|
|
@ -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 dpb@google.com (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 + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 jessewilson@google.com (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)));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 jessewilson@google.com (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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
|
|
|
@ -31,7 +31,6 @@ import org.jclouds.domain.JsonBall;
|
||||||
import org.jclouds.json.Json;
|
import org.jclouds.json.Json;
|
||||||
import org.jclouds.json.config.GsonModule;
|
import org.jclouds.json.config.GsonModule;
|
||||||
import org.jclouds.ohai.Automatic;
|
import org.jclouds.ohai.Automatic;
|
||||||
import org.jclouds.ohai.config.multibindings.MapBinder;
|
|
||||||
import org.jclouds.rest.annotations.ApiVersion;
|
import org.jclouds.rest.annotations.ApiVersion;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
@ -41,6 +40,7 @@ import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.Guice;
|
import com.google.inject.Guice;
|
||||||
import com.google.inject.Injector;
|
import com.google.inject.Injector;
|
||||||
import com.google.inject.TypeLiteral;
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.multibindings.MapBinder;
|
||||||
import com.google.inject.util.Providers;
|
import com.google.inject.util.Providers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue