From 37d4727c0a8874f367beaea57d6e9867b6835c92 Mon Sep 17 00:00:00 2001 From: Boaz Leskes Date: Tue, 8 Sep 2015 21:50:16 +0200 Subject: [PATCH] Gateway: allow overriding the Gateway implementation Allows mocking the gateway and overriding it from plugins. Closes #13412 --- .../common/util/ExtensionPoint.java | 6 ++- .../elasticsearch/gateway/GatewayModule.java | 23 ++++++++- .../java/org/elasticsearch/node/Node.java | 2 +- .../recovery/ReplicaRecoveryBenchmark.java | 1 - .../cluster/allocation/ClusterRerouteIT.java | 1 - .../common/inject/ModuleTestCase.java | 38 +++++++-------- .../gateway/GatewayModuleTests.java | 48 +++++++++++++++++++ 7 files changed, 94 insertions(+), 25 deletions(-) create mode 100644 core/src/test/java/org/elasticsearch/gateway/GatewayModuleTests.java diff --git a/core/src/main/java/org/elasticsearch/common/util/ExtensionPoint.java b/core/src/main/java/org/elasticsearch/common/util/ExtensionPoint.java index 1ec8eb755a8..a5878e1c421 100644 --- a/core/src/main/java/org/elasticsearch/common/util/ExtensionPoint.java +++ b/core/src/main/java/org/elasticsearch/common/util/ExtensionPoint.java @@ -145,7 +145,11 @@ public abstract class ExtensionPoint { if (instance == null) { throw new IllegalArgumentException("Unknown [" + this.name + "] type [" + type + "]"); } - binder.bind(extensionClass).to(instance).asEagerSingleton(); + if (extensionClass == instance) { + binder.bind(extensionClass).asEagerSingleton(); + } else { + binder.bind(extensionClass).to(instance).asEagerSingleton(); + } return type; } diff --git a/core/src/main/java/org/elasticsearch/gateway/GatewayModule.java b/core/src/main/java/org/elasticsearch/gateway/GatewayModule.java index 9378302a750..340248865b6 100644 --- a/core/src/main/java/org/elasticsearch/gateway/GatewayModule.java +++ b/core/src/main/java/org/elasticsearch/gateway/GatewayModule.java @@ -19,19 +19,38 @@ package org.elasticsearch.gateway; -import org.elasticsearch.common.inject.*; +import org.elasticsearch.common.inject.AbstractModule; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.ExtensionPoint; /** * */ public class GatewayModule extends AbstractModule { + public static final String GATEWAY_TYPE_KEY = "gateway.type"; + + private final ExtensionPoint.SelectedType gatewayTypes = new ExtensionPoint.SelectedType<>("gateway", Gateway.class); + private final Settings settings; + + public GatewayModule(Settings settings) { + this.settings = settings; + registerGatewayType("default", Gateway.class); + } + + /** + * Adds a custom Discovery type. + */ + public void registerGatewayType(String type, Class clazz) { + gatewayTypes.registerExtension(type, clazz); + } + @Override protected void configure() { bind(MetaStateService.class).asEagerSingleton(); bind(DanglingIndicesState.class).asEagerSingleton(); bind(GatewayService.class).asEagerSingleton(); - bind(Gateway.class).asEagerSingleton(); + gatewayTypes.bindType(binder(), settings, GATEWAY_TYPE_KEY, "default"); bind(TransportNodesListGatewayMetaState.class).asEagerSingleton(); bind(GatewayMetaState.class).asEagerSingleton(); bind(TransportNodesListGatewayStartedShards.class).asEagerSingleton(); diff --git a/core/src/main/java/org/elasticsearch/node/Node.java b/core/src/main/java/org/elasticsearch/node/Node.java index cf6d60f6acc..e0532ed7b01 100644 --- a/core/src/main/java/org/elasticsearch/node/Node.java +++ b/core/src/main/java/org/elasticsearch/node/Node.java @@ -188,7 +188,7 @@ public class Node implements Releasable { modules.add(new SearchModule(settings)); modules.add(new ActionModule(false)); modules.add(new MonitorModule(settings)); - modules.add(new GatewayModule()); + modules.add(new GatewayModule(settings)); modules.add(new NodeClientModule()); modules.add(new ShapeModule()); modules.add(new PercolatorModule()); diff --git a/core/src/test/java/org/elasticsearch/benchmark/recovery/ReplicaRecoveryBenchmark.java b/core/src/test/java/org/elasticsearch/benchmark/recovery/ReplicaRecoveryBenchmark.java index e7ef3259199..555a3326a04 100644 --- a/core/src/test/java/org/elasticsearch/benchmark/recovery/ReplicaRecoveryBenchmark.java +++ b/core/src/test/java/org/elasticsearch/benchmark/recovery/ReplicaRecoveryBenchmark.java @@ -60,7 +60,6 @@ public class ReplicaRecoveryBenchmark { BootstrapForTesting.ensureInitialized(); Settings settings = settingsBuilder() - .put("gateway.type", "local") .put(DiskThresholdDecider.CLUSTER_ROUTING_ALLOCATION_DISK_THRESHOLD_ENABLED, "false") .put(SETTING_NUMBER_OF_SHARDS, 1) .put(SETTING_NUMBER_OF_REPLICAS, 0) diff --git a/core/src/test/java/org/elasticsearch/cluster/allocation/ClusterRerouteIT.java b/core/src/test/java/org/elasticsearch/cluster/allocation/ClusterRerouteIT.java index c6235524c81..0eecc58d72c 100644 --- a/core/src/test/java/org/elasticsearch/cluster/allocation/ClusterRerouteIT.java +++ b/core/src/test/java/org/elasticsearch/cluster/allocation/ClusterRerouteIT.java @@ -166,7 +166,6 @@ public class ClusterRerouteIT extends ESIntegTestCase { @Test public void testDelayWithALargeAmountOfShards() throws Exception { Settings commonSettings = settingsBuilder() - .put("gateway.type", "local") .put(ThrottlingAllocationDecider.CLUSTER_ROUTING_ALLOCATION_CONCURRENT_RECOVERIES, 1) .build(); logger.info("--> starting 4 nodes"); diff --git a/core/src/test/java/org/elasticsearch/common/inject/ModuleTestCase.java b/core/src/test/java/org/elasticsearch/common/inject/ModuleTestCase.java index 84a66107642..3d4eaf92812 100644 --- a/core/src/test/java/org/elasticsearch/common/inject/ModuleTestCase.java +++ b/core/src/test/java/org/elasticsearch/common/inject/ModuleTestCase.java @@ -18,12 +18,7 @@ */ package org.elasticsearch.common.inject; -import org.elasticsearch.common.inject.spi.Element; -import org.elasticsearch.common.inject.spi.Elements; -import org.elasticsearch.common.inject.spi.InstanceBinding; -import org.elasticsearch.common.inject.spi.LinkedKeyBinding; -import org.elasticsearch.common.inject.spi.ProviderInstanceBinding; -import org.elasticsearch.common.inject.spi.ProviderLookup; +import org.elasticsearch.common.inject.spi.*; import org.elasticsearch.test.ESTestCase; import java.lang.annotation.Annotation; @@ -45,11 +40,17 @@ public abstract class ModuleTestCase extends ESTestCase { List elements = Elements.getElements(module); for (Element element : elements) { if (element instanceof LinkedKeyBinding) { - LinkedKeyBinding binding = (LinkedKeyBinding)element; + LinkedKeyBinding binding = (LinkedKeyBinding) element; if (to.equals(binding.getKey().getTypeLiteral().getType())) { assertSame(clazz, binding.getLinkedKey().getTypeLiteral().getType()); return; } + } else if (element instanceof UntargettedBinding) { + UntargettedBinding binding = (UntargettedBinding) element; + if (to.equals(binding.getKey().getTypeLiteral().getType())) { + assertSame(clazz, to); + return; + } } } StringBuilder s = new StringBuilder(); @@ -88,12 +89,12 @@ public abstract class ModuleTestCase extends ESTestCase { boolean providerFound = false; for (Element element : elements) { if (element instanceof LinkedKeyBinding) { - LinkedKeyBinding binding = (LinkedKeyBinding)element; + LinkedKeyBinding binding = (LinkedKeyBinding) element; if (to.equals(binding.getKey().getTypeLiteral().getType())) { bindings.add(binding.getLinkedKey().getTypeLiteral().getType()); } } else if (element instanceof ProviderInstanceBinding) { - ProviderInstanceBinding binding = (ProviderInstanceBinding)element; + ProviderInstanceBinding binding = (ProviderInstanceBinding) element; String setType = binding.getKey().getTypeLiteral().getType().toString(); if (setType.equals("java.util.Map")) { providerFound = true; @@ -108,7 +109,6 @@ public abstract class ModuleTestCase extends ESTestCase { } - /** * Configures the module and checks a Set of the "to" class * is bound to "classes". There may be more classes bound @@ -120,12 +120,12 @@ public abstract class ModuleTestCase extends ESTestCase { boolean providerFound = false; for (Element element : elements) { if (element instanceof LinkedKeyBinding) { - LinkedKeyBinding binding = (LinkedKeyBinding)element; + LinkedKeyBinding binding = (LinkedKeyBinding) element; if (to.equals(binding.getKey().getTypeLiteral().getType())) { bindings.add(binding.getLinkedKey().getTypeLiteral().getType()); } } else if (element instanceof ProviderInstanceBinding) { - ProviderInstanceBinding binding = (ProviderInstanceBinding)element; + ProviderInstanceBinding binding = (ProviderInstanceBinding) element; String setType = binding.getKey().getTypeLiteral().getType().toString(); if (setType.equals("java.util.Set<" + to.getName() + ">")) { providerFound = true; @@ -178,23 +178,23 @@ public abstract class ModuleTestCase extends ESTestCase { * and that all of the "expected" values are bound. */ @SuppressWarnings("unchecked") - public void assertMapInstanceBinding(Module module, Class keyType, Class valueType, Map expected) throws Exception { + public void assertMapInstanceBinding(Module module, Class keyType, Class valueType, Map expected) throws Exception { // this method is insane because java type erasure makes it incredibly difficult... - Map keys = new HashMap<>(); - Map values = new HashMap<>(); + Map keys = new HashMap<>(); + Map values = new HashMap<>(); List elements = Elements.getElements(module); for (Element element : elements) { if (element instanceof InstanceBinding) { InstanceBinding binding = (InstanceBinding) element; if (binding.getKey().getRawType().equals(valueType)) { - values.put(binding.getKey(), (V)binding.getInstance()); + values.put(binding.getKey(), (V) binding.getInstance()); } else if (binding.getInstance() instanceof Map.Entry) { - Map.Entry entry = (Map.Entry)binding.getInstance(); + Map.Entry entry = (Map.Entry) binding.getInstance(); Object key = entry.getKey(); Object providerValue = entry.getValue(); if (key.getClass().equals(keyType) && providerValue instanceof ProviderLookup.ProviderImpl) { - ProviderLookup.ProviderImpl provider = (ProviderLookup.ProviderImpl)providerValue; - keys.put((K)key, provider.getKey()); + ProviderLookup.ProviderImpl provider = (ProviderLookup.ProviderImpl) providerValue; + keys.put((K) key, provider.getKey()); } } } diff --git a/core/src/test/java/org/elasticsearch/gateway/GatewayModuleTests.java b/core/src/test/java/org/elasticsearch/gateway/GatewayModuleTests.java new file mode 100644 index 00000000000..ffd5454b471 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/gateway/GatewayModuleTests.java @@ -0,0 +1,48 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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.elasticsearch.gateway; + +import org.elasticsearch.cluster.ClusterName; +import org.elasticsearch.cluster.ClusterService; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.inject.ModuleTestCase; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.NodeEnvironment; + +public class GatewayModuleTests extends ModuleTestCase { + + public void testCustomGateway() { + GatewayModule gatewayModule = new GatewayModule(Settings.builder().put(GatewayModule.GATEWAY_TYPE_KEY, "mock").build()); + gatewayModule.registerGatewayType("mock", MockGateway.class); + assertBinding(gatewayModule, Gateway.class, MockGateway.class); + } + + public void testDefaultGateway() { + GatewayModule gatewayModule = new GatewayModule(Settings.EMPTY); + assertBinding(gatewayModule, Gateway.class, Gateway.class); + } + + public static class MockGateway extends Gateway { + + @Inject + public MockGateway(Settings settings, ClusterService clusterService, NodeEnvironment nodeEnv, GatewayMetaState metaState, TransportNodesListGatewayMetaState listGatewayMetaState, ClusterName clusterName) { + super(settings, clusterService, nodeEnv, metaState, listGatewayMetaState, clusterName); + } + } +}