Skip to content

Instantly share code, notes, and snippets.

@bbeck
Last active December 10, 2015 17:49
Show Gist options
  • Save bbeck/4470742 to your computer and use it in GitHub Desktop.
Save bbeck/4470742 to your computer and use it in GitHub Desktop.
import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import com.google.inject.AbstractModule;
import com.google.inject.Binding;
import com.google.inject.BindingAnnotation;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.PrivateModule;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.multibindings.Multibinder;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.List;
import java.util.Set;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
public class GuiceProblem {
public static void main(String[] args) {
// Pretend this came from args
List<Region> regions = Lists.newArrayList(US_EAST_1, US_WEST_1, US_WEST_2, EU_WEST_1);
Injector injector = createInjector(regions);
System.out.println("Crawlers:");
System.out.println(" global:");
System.out.println(" crawler service: " + injector.getInstance(Key.get(CrawlerService.class, GLOBAL)));
System.out.println();
for (Region region : regions) {
System.out.println(" " + region + ":");
System.out.println(" crawler service: " + injector.getInstance(Key.get(CrawlerService.class, region)));
System.out.println();
}
System.out.println(" all crawler services:");
for (CrawlerService crawler : findBindingsByType(injector, CrawlerService.class)) {
System.out.println(" " + crawler);
}
System.out.println();
}
private static Injector createInjector(List<Region> regions) {
List<Module> modules = Lists.newArrayList();
modules.add(new AWSModule());
// Global modules
modules.add(new PrivateModule() {
@Override
protected void configure() {
install(new GlobalClientModule());
install(new GlobalCrawlerModule());
bind(CrawlerService.class).annotatedWith(GLOBAL).to(CrawlerService.class).asEagerSingleton();
expose(CrawlerService.class).annotatedWith(GLOBAL);
}
});
// Regional modules
for (final Region region : regions) {
modules.add(new PrivateModule() {
@Override
protected void configure() {
install(new RegionClientModule());
install(new RegionCrawlerModule());
bind(Region.class).toInstance(region);
bind(CrawlerService.class).annotatedWith(region).to(CrawlerService.class).asEagerSingleton();
expose(CrawlerService.class).annotatedWith(region);
}
});
}
return Guice.createInjector(modules);
}
private static <T> List<T> findBindingsByType(Injector injector, Class<T> cls) {
List<Binding<T>> bindings = injector.findBindingsByType(TypeLiteral.get(cls));
List<T> instances = Lists.newArrayList();
for (Binding<T> binding : bindings) {
Key<T> key = binding.getKey();
T instance = injector.getInstance(key);
instances.add(instance);
}
return instances;
}
//////////////////////////////////////////////////////////////////////
// Global AWS parameters
//////////////////////////////////////////////////////////////////////
private static final class AWSCredentials {}
private static final class AWSModule extends AbstractModule {
@Override
protected void configure() {
bind(AWSCredentials.class).asEagerSingleton();
}
}
//////////////////////////////////////////////////////////////////////
// Clients
//////////////////////////////////////////////////////////////////////
private static final class IAMClient {
IAMClient(AWSCredentials credentials) {}
}
private static final class IAMClientProvider implements Provider<IAMClient> {
private final AWSCredentials credentials;
@Inject IAMClientProvider(AWSCredentials credentials) { this.credentials = credentials; }
public IAMClient get() { return new IAMClient(credentials); }
}
private static final class EC2Client {
EC2Client(AWSCredentials credentials, Region region) {}
}
private static final class EC2ClientProvider implements Provider<EC2Client> {
private final AWSCredentials credentials;
private final Region region;
@Inject EC2ClientProvider(AWSCredentials credentials, Region region) { this.credentials = credentials; this.region = region; }
public EC2Client get() { return new EC2Client(credentials, region); }
}
private static final class CFNClient {
CFNClient(AWSCredentials credentials, Region region) {}
}
private static final class CFNClientProvider implements Provider<CFNClient> {
private final AWSCredentials credentials;
private final Region region;
@Inject CFNClientProvider(AWSCredentials credentials, Region region) { this.credentials = credentials; this.region = region; }
public CFNClient get() { return new CFNClient(credentials, region); }
}
private static final class GlobalClientModule extends AbstractModule {
@Override
protected void configure() {
bind(IAMClient.class).toProvider(IAMClientProvider.class).asEagerSingleton();
}
}
private static final class RegionClientModule extends AbstractModule {
@Override
protected void configure() {
bind(EC2Client.class).toProvider(EC2ClientProvider.class).asEagerSingleton();
bind(CFNClient.class).toProvider(CFNClientProvider.class).asEagerSingleton();
}
}
//////////////////////////////////////////////////////////////////////
// Crawlers
//////////////////////////////////////////////////////////////////////
private static interface Crawler {}
private static final class IAMProfileCrawler implements Crawler {
private final IAMClient iam;
@Inject IAMProfileCrawler(IAMClient iam) { this.iam = iam; }
public String toString() { return Objects.toStringHelper(this).add("iam", iam).toString(); }
}
private static final class EC2InstanceCrawler implements Crawler {
private final EC2Client ec2;
private final Region region;
@Inject EC2InstanceCrawler(EC2Client ec2, Region region) { this.ec2 = ec2; this.region = region; }
public String toString() { return Objects.toStringHelper(this).add("ec2", ec2).add("region", region).toString(); }
}
private static final class EC2SecurityGroupCrawler implements Crawler {
private final EC2Client ec2;
private final Region region;
@Inject EC2SecurityGroupCrawler(EC2Client ec2, Region region) { this.ec2 = ec2; this.region = region; }
public String toString() { return Objects.toStringHelper(this).add("ec2", ec2).add("region", region).toString(); }
}
private static final class CFNStackCrawler implements Crawler {
private final CFNClient cfn;
private final Region region;
@Inject CFNStackCrawler(CFNClient cfn, Region region) { this.cfn = cfn; this.region = region; }
public String toString() { return Objects.toStringHelper(this).add("cfn", cfn).add("region", region).toString(); }
}
private static final class GlobalCrawlerModule extends AbstractModule {
@Override
protected void configure() {
Multibinder<Crawler> crawlers = Multibinder.newSetBinder(binder(), Crawler.class);
crawlers.addBinding().to(IAMProfileCrawler.class);
bind(CrawlerService.class).asEagerSingleton();
}
}
private static final class RegionCrawlerModule extends AbstractModule {
@Override
protected void configure() {
Multibinder<Crawler> crawlers = Multibinder.newSetBinder(binder(), Crawler.class);
crawlers.addBinding().to(EC2InstanceCrawler.class);
crawlers.addBinding().to(EC2SecurityGroupCrawler.class);
crawlers.addBinding().to(CFNStackCrawler.class);
bind(CrawlerService.class).asEagerSingleton();
}
}
//////////////////////////////////////////////////////////////////////
// Crawler service
//////////////////////////////////////////////////////////////////////
private static final class CrawlerService {
private final Set<Crawler> crawlers;
@Inject CrawlerService(Set<Crawler> crawlers) { this.crawlers = crawlers; }
public String toString() { return Objects.toStringHelper(this).add("crawlers", crawlers).toString(); }
}
//////////////////////////////////////////////////////////////////////
// Region, this is the same technique used for @Named.
//////////////////////////////////////////////////////////////////////
@Retention(RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@BindingAnnotation
public @interface Region {
String name();
}
@SuppressWarnings("ClassExplicitlyAnnotation")
private static final class RegionImpl implements Region {
private final String name;
public RegionImpl(String name) { this.name = name; }
public Class<? extends Annotation> annotationType() { return Region.class; }
public String name() { return name;}
public String toString() { return name; }
}
private static final Region GLOBAL = new RegionImpl("global");
private static final Region US_EAST_1 = new RegionImpl("us-east-1");
private static final Region US_WEST_1 = new RegionImpl("us-west-1");
private static final Region US_WEST_2 = new RegionImpl("us-west-2");
private static final Region EU_WEST_1 = new RegionImpl("eu-west-1");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment