/* * Copyright 2010 Proofpoint, Inc. * * Licensed 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 io.airlift.discovery.store; import com.google.common.base.Supplier; import com.google.inject.Binder; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Module; import com.google.inject.Provider; import com.google.inject.Scopes; import com.google.inject.TypeLiteral; import io.airlift.discovery.client.ServiceSelector; import io.airlift.http.client.HttpClient; import io.airlift.node.NodeInfo; import org.joda.time.DateTime; import org.weakref.jmx.MBeanExporter; import javax.annotation.PreDestroy; import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.ThreadSafe; import javax.inject.Inject; import java.lang.annotation.Annotation; import static com.google.inject.multibindings.MapBinder.newMapBinder; import static com.google.inject.name.Names.named; import static io.airlift.configuration.ConfigBinder.configBinder; import static io.airlift.http.client.HttpClientBinder.httpClientBinder; import static io.airlift.jaxrs.JaxrsBinder.jaxrsBinder; import static org.weakref.jmx.ObjectNames.generatedNameOf; import static org.weakref.jmx.guice.ExportBinder.newExporter; /** * Expects a LocalStore to be bound elsewhere. * Provides a DistributedStore with the specified annotation. */ public class ReplicatedStoreModule implements Module { private final String name; private final Class<? extends Annotation> annotation; private final Class<? extends LocalStore> localStoreClass; public ReplicatedStoreModule(String name, Class<? extends Annotation> annotation, Class<? extends LocalStore> localStoreClass) { this.name = name; this.annotation = annotation; this.localStoreClass = localStoreClass; } @Override public void configure(Binder binder) { binder.requireExplicitBindings(); binder.disableCircularProxies(); // global jaxrsBinder(binder).bind(StoreResource.class); binder.bind(new TypeLiteral<Supplier<DateTime>>() {}).to(RealTimeSupplier.class).in(Scopes.SINGLETON); binder.bind(ConflictResolver.class).in(Scopes.SINGLETON); // per store Key<HttpClient> httpClientKey = Key.get(HttpClient.class, annotation); Key<LocalStore> localStoreKey = Key.get(LocalStore.class, annotation); Key<StoreConfig> storeConfigKey = Key.get(StoreConfig.class, annotation); Key<RemoteStore> remoteStoreKey = Key.get(RemoteStore.class, annotation); configBinder(binder).bindConfig(StoreConfig.class, annotation, name); httpClientBinder(binder).bindHttpClient(name, annotation); binder.bind(DistributedStore.class).annotatedWith(annotation).toProvider(new DistributedStoreProvider(name, localStoreKey, storeConfigKey, remoteStoreKey)).in(Scopes.SINGLETON); binder.bind(Replicator.class).annotatedWith(annotation).toProvider(new ReplicatorProvider(name, localStoreKey, httpClientKey, storeConfigKey)).in(Scopes.SINGLETON); binder.bind(HttpRemoteStore.class).annotatedWith(annotation).toProvider(new RemoteHttpStoreProvider(name, httpClientKey, storeConfigKey)).in(Scopes.SINGLETON); binder.bind(LocalStore.class).annotatedWith(annotation).to(localStoreClass).in(Scopes.SINGLETON); binder.bind(RemoteStore.class).annotatedWith(annotation).to(Key.get(HttpRemoteStore.class, annotation)); newExporter(binder).export(DistributedStore.class).annotatedWith(annotation).as(generatedNameOf(DistributedStore.class, named(name))); newExporter(binder).export(HttpRemoteStore.class).annotatedWith(annotation).as(generatedNameOf(HttpRemoteStore.class, named(name))); newExporter(binder).export(Replicator.class).annotatedWith(annotation).as(generatedNameOf(Replicator.class, named(name))); newMapBinder(binder, String.class, LocalStore.class) .addBinding(name) .to(localStoreKey); newMapBinder(binder, String.class, StoreConfig.class) .addBinding(name) .to(storeConfigKey); } @ThreadSafe private static class ReplicatorProvider implements Provider<Replicator> { private final String name; private final Key<? extends LocalStore> localStoreKey; private final Key<? extends HttpClient> httpClientKey; private final Key<StoreConfig> storeConfigKey; @GuardedBy("this") private Injector injector; @GuardedBy("this") private NodeInfo nodeInfo; @GuardedBy("this") private ServiceSelector serviceSelector; @GuardedBy("this") private Replicator replicator; private ReplicatorProvider(String name, Key<? extends LocalStore> localStoreKey, Key<? extends HttpClient> httpClientKey, Key<StoreConfig> storeConfigKey) { this.name = name; this.localStoreKey = localStoreKey; this.httpClientKey = httpClientKey; this.storeConfigKey = storeConfigKey; } @Override public synchronized Replicator get() { if (replicator == null) { LocalStore localStore = injector.getInstance(localStoreKey); HttpClient httpClient = injector.getInstance(httpClientKey); StoreConfig storeConfig = injector.getInstance(storeConfigKey); replicator = new Replicator(name, nodeInfo, serviceSelector, httpClient, localStore, storeConfig); replicator.start(); } return replicator; } @PreDestroy public synchronized void shutdown() { if (replicator != null) { replicator.shutdown(); } } @Inject public synchronized void setInjector(Injector injector) { this.injector = injector; } @Inject public synchronized void setNodeInfo(NodeInfo nodeInfo) { this.nodeInfo = nodeInfo; } @Inject public synchronized void setServiceSelector(ServiceSelector serviceSelector) { this.serviceSelector = serviceSelector; } } @ThreadSafe private static class RemoteHttpStoreProvider implements Provider<HttpRemoteStore> { @GuardedBy("this") private HttpRemoteStore remoteStore; @GuardedBy("this") private Injector injector; @GuardedBy("this") private NodeInfo nodeInfo; @GuardedBy("this") private ServiceSelector serviceSelector; @GuardedBy("this") private MBeanExporter mbeanExporter; private final String name; private final Key<? extends HttpClient> httpClientKey; private final Key<StoreConfig> storeConfigKey; @Inject private RemoteHttpStoreProvider(String name, Key<? extends HttpClient> httpClientKey, Key<StoreConfig> storeConfigKey) { this.name = name; this.httpClientKey = httpClientKey; this.storeConfigKey = storeConfigKey; } public synchronized HttpRemoteStore get() { if (remoteStore == null) { HttpClient httpClient = injector.getInstance(httpClientKey); StoreConfig storeConfig = injector.getInstance(storeConfigKey); remoteStore = new HttpRemoteStore(name, nodeInfo, serviceSelector, storeConfig, httpClient, mbeanExporter); remoteStore.start(); } return remoteStore; } @PreDestroy public synchronized void shutdown() { if (remoteStore != null) { remoteStore.shutdown(); } } @Inject public synchronized void setInjector(Injector injector) { this.injector = injector; } @Inject public synchronized void setNodeInfo(NodeInfo nodeInfo) { this.nodeInfo = nodeInfo; } @Inject public synchronized void setServiceSelector(ServiceSelector serviceSelector) { this.serviceSelector = serviceSelector; } @Inject public synchronized void setMbeanExporter(MBeanExporter mbeanExporter) { this.mbeanExporter = mbeanExporter; } } private static class DistributedStoreProvider implements Provider<DistributedStore> { private final String name; private final Key<? extends LocalStore> localStoreKey; private final Key<StoreConfig> storeConfigKey; private final Key<? extends RemoteStore> remoteStoreKey; private Injector injector; private Supplier<DateTime> timeSupplier; private DistributedStore store; public DistributedStoreProvider(String name, Key<? extends LocalStore> localStoreKey, Key<StoreConfig> storeConfigKey, Key<? extends RemoteStore> remoteStoreKey) { this.name = name; this.localStoreKey = localStoreKey; this.storeConfigKey = storeConfigKey; this.remoteStoreKey = remoteStoreKey; } @Override public synchronized DistributedStore get() { if (store == null) { LocalStore localStore = injector.getInstance(localStoreKey); StoreConfig storeConfig = injector.getInstance(storeConfigKey); RemoteStore remoteStore = injector.getInstance(remoteStoreKey); store = new DistributedStore(name, localStore, remoteStore, storeConfig, timeSupplier); store.start(); } return store; } @PreDestroy public synchronized void shutdown() { if (store != null) { store.shutdown(); } } @Inject public synchronized void setInjector(Injector injector) { this.injector = injector; } @Inject public synchronized void setTimeSupplier(Supplier<DateTime> timeSupplier) { this.timeSupplier = timeSupplier; } } }