mirror of https://github.com/apache/lucene.git
SOLR-14977 : ContainerPlugins should be configurable (#2065)
This commit is contained in:
parent
ebc87a8a27
commit
73d5e7ae77
|
@ -176,6 +176,8 @@ Improvements
|
||||||
|
|
||||||
* SOLR-14683: Metrics API should ensure consistent placeholders for missing values. (ab)
|
* SOLR-14683: Metrics API should ensure consistent placeholders for missing values. (ab)
|
||||||
|
|
||||||
|
* SOLR-14977 : ContainerPlugins should be configurable with custom objects (noble, ab)
|
||||||
|
|
||||||
Optimizations
|
Optimizations
|
||||||
---------------------
|
---------------------
|
||||||
* SOLR-14975: Optimize CoreContainer.getAllCoreNames, getLoadedCoreNames and getCoreDescriptors. (Bruno Roustant)
|
* SOLR-14975: Optimize CoreContainer.getAllCoreNames, getLoadedCoreNames and getCoreDescriptors. (Bruno Roustant)
|
||||||
|
|
|
@ -33,6 +33,7 @@ import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.apache.solr.client.solrj.SolrRequest;
|
import org.apache.solr.client.solrj.SolrRequest;
|
||||||
import org.apache.solr.common.SolrException;
|
import org.apache.solr.common.SolrException;
|
||||||
|
@ -62,6 +63,8 @@ import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class AnnotatedApi extends Api implements PermissionNameProvider , Closeable {
|
public class AnnotatedApi extends Api implements PermissionNameProvider , Closeable {
|
||||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||||
|
private static final ObjectMapper mapper = SolrJacksonAnnotationInspector.createObjectMapper()
|
||||||
|
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
|
|
||||||
public static final String ERR = "Error executing commands :";
|
public static final String ERR = "Error executing commands :";
|
||||||
private EndPoint endPoint;
|
private EndPoint endPoint;
|
||||||
|
@ -222,7 +225,7 @@ public class AnnotatedApi extends Api implements PermissionNameProvider , Closea
|
||||||
final String command;
|
final String command;
|
||||||
final MethodHandle method;
|
final MethodHandle method;
|
||||||
final Object obj;
|
final Object obj;
|
||||||
ObjectMapper mapper = SolrJacksonAnnotationInspector.createObjectMapper();
|
|
||||||
int paramsCount;
|
int paramsCount;
|
||||||
@SuppressWarnings({"rawtypes"})
|
@SuppressWarnings({"rawtypes"})
|
||||||
Class parameterClass;
|
Class parameterClass;
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* 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.apache.solr.api;
|
||||||
|
|
||||||
|
import org.apache.solr.common.MapWriter;
|
||||||
|
|
||||||
|
/**Implement this interface if your plugin needs to accept some configuration
|
||||||
|
*
|
||||||
|
* @param <T> the configuration Object type
|
||||||
|
*/
|
||||||
|
public interface ConfigurablePlugin<T extends MapWriter> {
|
||||||
|
|
||||||
|
/**This is invoked soon after the Object is initialized
|
||||||
|
*
|
||||||
|
* @param cfg value deserialized from JSON
|
||||||
|
*/
|
||||||
|
void configure(T cfg);
|
||||||
|
}
|
|
@ -22,6 +22,8 @@ import java.io.IOException;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -31,6 +33,7 @@ import java.util.Optional;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.apache.lucene.util.ResourceLoaderAware;
|
import org.apache.lucene.util.ResourceLoaderAware;
|
||||||
import org.apache.solr.client.solrj.SolrRequest;
|
import org.apache.solr.client.solrj.SolrRequest;
|
||||||
|
@ -69,7 +72,8 @@ import static org.apache.solr.common.util.Utils.makeMap;
|
||||||
public class ContainerPluginsRegistry implements ClusterPropertiesListener, MapWriter, Closeable {
|
public class ContainerPluginsRegistry implements ClusterPropertiesListener, MapWriter, Closeable {
|
||||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||||
|
|
||||||
private final ObjectMapper mapper = SolrJacksonAnnotationInspector.createObjectMapper();
|
private static final ObjectMapper mapper = SolrJacksonAnnotationInspector.createObjectMapper()
|
||||||
|
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
|
|
||||||
private final List<PluginRegistryListener> listeners = new CopyOnWriteArrayList<>();
|
private final List<PluginRegistryListener> listeners = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
|
@ -114,6 +118,30 @@ public class ContainerPluginsRegistry implements ClusterPropertiesListener, MapW
|
||||||
return currentPlugins.get(name);
|
return currentPlugins.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class PluginMetaHolder {
|
||||||
|
private final Map<String, Object> original;
|
||||||
|
private final PluginMeta meta;
|
||||||
|
|
||||||
|
PluginMetaHolder(Map<String, Object> original) throws IOException {
|
||||||
|
this.original = original;
|
||||||
|
meta = mapper.readValue(Utils.toJSON(original), PluginMeta.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof PluginMetaHolder) {
|
||||||
|
PluginMetaHolder that = (PluginMetaHolder) obj;
|
||||||
|
return Objects.equals(this.original,that.original);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return original.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public synchronized void refresh() {
|
public synchronized void refresh() {
|
||||||
Map<String, Object> pluginInfos = null;
|
Map<String, Object> pluginInfos = null;
|
||||||
try {
|
try {
|
||||||
|
@ -122,19 +150,18 @@ public class ContainerPluginsRegistry implements ClusterPropertiesListener, MapW
|
||||||
log.error("Could not read plugins data", e);
|
log.error("Could not read plugins data", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Map<String,PluginMeta> newState = new HashMap<>(pluginInfos.size());
|
Map<String,PluginMetaHolder> newState = new HashMap<>(pluginInfos.size());
|
||||||
for (Map.Entry<String, Object> e : pluginInfos.entrySet()) {
|
for (Map.Entry<String, Object> e : pluginInfos.entrySet()) {
|
||||||
try {
|
try {
|
||||||
newState.put(e.getKey(),
|
newState.put(e.getKey(),new PluginMetaHolder((Map<String, Object>) e.getValue()));
|
||||||
mapper.readValue(Utils.toJSON(e.getValue()), PluginMeta.class));
|
|
||||||
} catch (Exception exp) {
|
} catch (Exception exp) {
|
||||||
log.error("Invalid apiInfo configuration :", exp);
|
log.error("Invalid apiInfo configuration :", exp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, PluginMeta> currentState = new HashMap<>();
|
Map<String, PluginMetaHolder> currentState = new HashMap<>();
|
||||||
for (Map.Entry<String, ApiInfo> e : currentPlugins.entrySet()) {
|
for (Map.Entry<String, ApiInfo> e : currentPlugins.entrySet()) {
|
||||||
currentState.put(e.getKey(), e.getValue().info);
|
currentState.put(e.getKey(), e.getValue().holder);
|
||||||
}
|
}
|
||||||
Map<String, Diff> diff = compareMaps(currentState, newState);
|
Map<String, Diff> diff = compareMaps(currentState, newState);
|
||||||
if (diff == null) return;//nothing has changed
|
if (diff == null) return;//nothing has changed
|
||||||
|
@ -153,7 +180,7 @@ public class ContainerPluginsRegistry implements ClusterPropertiesListener, MapW
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//ADDED or UPDATED
|
//ADDED or UPDATED
|
||||||
PluginMeta info = newState.get(e.getKey());
|
PluginMetaHolder info = newState.get(e.getKey());
|
||||||
ApiInfo apiInfo = null;
|
ApiInfo apiInfo = null;
|
||||||
List<String> errs = new ArrayList<>();
|
List<String> errs = new ArrayList<>();
|
||||||
apiInfo = new ApiInfo(info,errs);
|
apiInfo = new ApiInfo(info,errs);
|
||||||
|
@ -243,8 +270,10 @@ public class ContainerPluginsRegistry implements ClusterPropertiesListener, MapW
|
||||||
public class ApiInfo implements ReflectMapWriter {
|
public class ApiInfo implements ReflectMapWriter {
|
||||||
List<ApiHolder> holders;
|
List<ApiHolder> holders;
|
||||||
|
|
||||||
|
private final PluginMetaHolder holder;
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private final PluginMeta info;
|
private PluginMeta info;
|
||||||
|
|
||||||
@JsonProperty(value = "package")
|
@JsonProperty(value = "package")
|
||||||
public final String pkg;
|
public final String pkg;
|
||||||
|
@ -272,8 +301,9 @@ public class ContainerPluginsRegistry implements ClusterPropertiesListener, MapW
|
||||||
return info.copy();
|
return info.copy();
|
||||||
}
|
}
|
||||||
@SuppressWarnings({"unchecked","rawtypes"})
|
@SuppressWarnings({"unchecked","rawtypes"})
|
||||||
public ApiInfo(PluginMeta info, List<String> errs) {
|
public ApiInfo(PluginMetaHolder infoHolder, List<String> errs) {
|
||||||
this.info = info;
|
this.holder = infoHolder;
|
||||||
|
this.info = infoHolder.meta;
|
||||||
PluginInfo.ClassName klassInfo = new PluginInfo.ClassName(info.klass);
|
PluginInfo.ClassName klassInfo = new PluginInfo.ClassName(info.klass);
|
||||||
pkg = klassInfo.pkg;
|
pkg = klassInfo.pkg;
|
||||||
if (pkg != null) {
|
if (pkg != null) {
|
||||||
|
@ -349,7 +379,7 @@ public class ContainerPluginsRegistry implements ClusterPropertiesListener, MapW
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({"rawtypes"})
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
public void init() throws Exception {
|
public void init() throws Exception {
|
||||||
if (this.holders != null) return;
|
if (this.holders != null) return;
|
||||||
Constructor constructor = klas.getConstructors()[0];
|
Constructor constructor = klas.getConstructors()[0];
|
||||||
|
@ -360,6 +390,13 @@ public class ContainerPluginsRegistry implements ClusterPropertiesListener, MapW
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException("Must have a no-arg constructor or CoreContainer constructor ");
|
throw new RuntimeException("Must have a no-arg constructor or CoreContainer constructor ");
|
||||||
}
|
}
|
||||||
|
if (instance instanceof ConfigurablePlugin) {
|
||||||
|
Class<? extends MapWriter> c = getConfigClass((ConfigurablePlugin<? extends MapWriter>) instance);
|
||||||
|
if (c != null) {
|
||||||
|
MapWriter initVal = mapper.readValue(Utils.toJSON(holder.original), c);
|
||||||
|
((ConfigurablePlugin) instance).configure(initVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (instance instanceof ResourceLoaderAware) {
|
if (instance instanceof ResourceLoaderAware) {
|
||||||
try {
|
try {
|
||||||
((ResourceLoaderAware) instance).inform(pkgVersion.getLoader());
|
((ResourceLoaderAware) instance).inform(pkgVersion.getLoader());
|
||||||
|
@ -372,9 +409,34 @@ public class ContainerPluginsRegistry implements ClusterPropertiesListener, MapW
|
||||||
holders.add(new ApiHolder((AnnotatedApi) api));
|
holders.add(new ApiHolder((AnnotatedApi) api));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ApiInfo createInfo(PluginMeta info, List<String> errs) {
|
/**Get the generic type of a {@link ConfigurablePlugin}
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public static Class getConfigClass(ConfigurablePlugin<?> o) {
|
||||||
|
Class klas = o.getClass();
|
||||||
|
do {
|
||||||
|
Type[] interfaces = klas.getGenericInterfaces();
|
||||||
|
for (Type type : interfaces) {
|
||||||
|
if (type instanceof ParameterizedType) {
|
||||||
|
ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||||
|
if (parameterizedType.getRawType() == ConfigurablePlugin.class) {
|
||||||
|
return (Class) parameterizedType.getActualTypeArguments()[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
klas = klas.getSuperclass();
|
||||||
|
} while (klas != null && klas != Object.class);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApiInfo createInfo(Map<String,Object> info, List<String> errs) throws IOException {
|
||||||
|
return new ApiInfo(new PluginMetaHolder(info), errs);
|
||||||
|
|
||||||
|
}
|
||||||
|
public ApiInfo createInfo(PluginMetaHolder info, List<String> errs) {
|
||||||
return new ApiInfo(info, errs);
|
return new ApiInfo(info, errs);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,7 +136,7 @@ public class V2HttpCall extends HttpSolrCall {
|
||||||
initAdminRequest(path);
|
initAdminRequest(path);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
throw new SolrException(SolrException.ErrorCode.NOT_FOUND, "no core retrieved for " + origCorename);
|
throw new SolrException(SolrException.ErrorCode.NOT_FOUND, "no core retrieved for core name: " + origCorename + ". Path : "+ path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,10 +19,7 @@ package org.apache.solr.handler.admin;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
@ -94,7 +91,7 @@ public class ContainerPluginsApi {
|
||||||
payload.addError(info.name + " already exists");
|
payload.addError(info.name + " already exists");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
map.put(info.name, info);
|
map.put(info.name, payload.getDataMap());
|
||||||
return map;
|
return map;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -122,14 +119,19 @@ public class ContainerPluginsApi {
|
||||||
payload.addError("No such plugin: " + info.name);
|
payload.addError("No such plugin: " + info.name);
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
map.put(info.name, info);
|
Map<String, Object> jsonObj = payload.getDataMap();
|
||||||
|
if(Objects.equals(jsonObj, existing)) {
|
||||||
|
//no need to change anything
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
map.put(info.name, jsonObj);
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateConfig(PayloadObj<PluginMeta> payload, PluginMeta info) {
|
private void validateConfig(PayloadObj<PluginMeta> payload, PluginMeta info) throws IOException {
|
||||||
if (info.klass.indexOf(':') > 0) {
|
if (info.klass.indexOf(':') > 0) {
|
||||||
if (info.version == null) {
|
if (info.version == null) {
|
||||||
payload.addError("Using package. must provide a packageVersion");
|
payload.addError("Using package. must provide a packageVersion");
|
||||||
|
@ -137,7 +139,7 @@ public class ContainerPluginsApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<String> errs = new ArrayList<>();
|
List<String> errs = new ArrayList<>();
|
||||||
ContainerPluginsRegistry.ApiInfo apiInfo = coreContainer.getContainerPluginsRegistry().createInfo(info, errs);
|
ContainerPluginsRegistry.ApiInfo apiInfo = coreContainer.getContainerPluginsRegistry().createInfo( payload.getDataMap(), errs);
|
||||||
if (!errs.isEmpty()) {
|
if (!errs.isEmpty()) {
|
||||||
for (String err : errs) payload.addError(err);
|
for (String err : errs) payload.addError(err);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -28,6 +28,8 @@ import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.lucene.util.ResourceLoader;
|
import org.apache.lucene.util.ResourceLoader;
|
||||||
import org.apache.lucene.util.ResourceLoaderAware;
|
import org.apache.lucene.util.ResourceLoaderAware;
|
||||||
import org.apache.solr.api.Command;
|
import org.apache.solr.api.Command;
|
||||||
|
import org.apache.solr.api.ConfigurablePlugin;
|
||||||
|
import org.apache.solr.api.ContainerPluginsRegistry;
|
||||||
import org.apache.solr.api.EndPoint;
|
import org.apache.solr.api.EndPoint;
|
||||||
import org.apache.solr.client.solrj.SolrClient;
|
import org.apache.solr.client.solrj.SolrClient;
|
||||||
import org.apache.solr.client.solrj.SolrServerException;
|
import org.apache.solr.client.solrj.SolrServerException;
|
||||||
|
@ -41,6 +43,8 @@ import org.apache.solr.cloud.ClusterSingleton;
|
||||||
import org.apache.solr.cloud.MiniSolrCloudCluster;
|
import org.apache.solr.cloud.MiniSolrCloudCluster;
|
||||||
import org.apache.solr.cloud.SolrCloudTestCase;
|
import org.apache.solr.cloud.SolrCloudTestCase;
|
||||||
import org.apache.solr.common.NavigableObject;
|
import org.apache.solr.common.NavigableObject;
|
||||||
|
import org.apache.solr.common.annotation.JsonProperty;
|
||||||
|
import org.apache.solr.common.util.ReflectMapWriter;
|
||||||
import org.apache.solr.common.util.Utils;
|
import org.apache.solr.common.util.Utils;
|
||||||
import org.apache.solr.core.CoreContainer;
|
import org.apache.solr.core.CoreContainer;
|
||||||
import org.apache.solr.core.SolrResourceLoader;
|
import org.apache.solr.core.SolrResourceLoader;
|
||||||
|
@ -58,7 +62,6 @@ import org.junit.Test;
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static org.apache.solr.client.solrj.SolrRequest.METHOD.GET;
|
import static org.apache.solr.client.solrj.SolrRequest.METHOD.GET;
|
||||||
import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST;
|
|
||||||
import static org.apache.solr.filestore.TestDistribPackageStore.readFile;
|
import static org.apache.solr.filestore.TestDistribPackageStore.readFile;
|
||||||
import static org.apache.solr.filestore.TestDistribPackageStore.uploadKey;
|
import static org.apache.solr.filestore.TestDistribPackageStore.uploadKey;
|
||||||
|
|
||||||
|
@ -88,7 +91,7 @@ public class TestContainerPlugin extends SolrCloudTestCase {
|
||||||
//test with an invalid class
|
//test with an invalid class
|
||||||
V2Request req = new V2Request.Builder("/cluster/plugin")
|
V2Request req = new V2Request.Builder("/cluster/plugin")
|
||||||
.forceV2(true)
|
.forceV2(true)
|
||||||
.withMethod(POST)
|
.POST()
|
||||||
.withPayload(singletonMap("add", plugin))
|
.withPayload(singletonMap("add", plugin))
|
||||||
.build();
|
.build();
|
||||||
expectError(req, cluster.getSolrClient(), errPath, "No method with @Command in class");
|
expectError(req, cluster.getSolrClient(), errPath, "No method with @Command in class");
|
||||||
|
@ -100,7 +103,7 @@ public class TestContainerPlugin extends SolrCloudTestCase {
|
||||||
//just check if the plugin is indeed registered
|
//just check if the plugin is indeed registered
|
||||||
V2Request readPluginState = new V2Request.Builder("/cluster/plugin")
|
V2Request readPluginState = new V2Request.Builder("/cluster/plugin")
|
||||||
.forceV2(true)
|
.forceV2(true)
|
||||||
.withMethod(GET)
|
.GET()
|
||||||
.build();
|
.build();
|
||||||
V2Response rsp = readPluginState.process(cluster.getSolrClient());
|
V2Response rsp = readPluginState.process(cluster.getSolrClient());
|
||||||
assertEquals(C3.class.getName(), rsp._getStr("/plugin/testplugin/class", null));
|
assertEquals(C3.class.getName(), rsp._getStr("/plugin/testplugin/class", null));
|
||||||
|
@ -109,13 +112,13 @@ public class TestContainerPlugin extends SolrCloudTestCase {
|
||||||
TestDistribPackageStore.assertResponseValues(10,
|
TestDistribPackageStore.assertResponseValues(10,
|
||||||
() -> new V2Request.Builder("/plugin/my/plugin")
|
() -> new V2Request.Builder("/plugin/my/plugin")
|
||||||
.forceV2(true)
|
.forceV2(true)
|
||||||
.withMethod(GET)
|
.GET()
|
||||||
.build().process(cluster.getSolrClient()),
|
.build().process(cluster.getSolrClient()),
|
||||||
ImmutableMap.of("/testkey", "testval"));
|
ImmutableMap.of("/testkey", "testval"));
|
||||||
|
|
||||||
//now remove the plugin
|
//now remove the plugin
|
||||||
new V2Request.Builder("/cluster/plugin")
|
new V2Request.Builder("/cluster/plugin")
|
||||||
.withMethod(POST)
|
.POST()
|
||||||
.forceV2(true)
|
.forceV2(true)
|
||||||
.withPayload("{remove : testplugin}")
|
.withPayload("{remove : testplugin}")
|
||||||
.build()
|
.build()
|
||||||
|
@ -140,19 +143,19 @@ public class TestContainerPlugin extends SolrCloudTestCase {
|
||||||
TestDistribPackageStore.assertResponseValues(10,
|
TestDistribPackageStore.assertResponseValues(10,
|
||||||
() -> new V2Request.Builder("/my-random-name/my/plugin")
|
() -> new V2Request.Builder("/my-random-name/my/plugin")
|
||||||
.forceV2(true)
|
.forceV2(true)
|
||||||
.withMethod(GET)
|
.GET()
|
||||||
.build().process(cluster.getSolrClient()),
|
.build().process(cluster.getSolrClient()),
|
||||||
ImmutableMap.of("/method.name", "m1"));
|
ImmutableMap.of("/method.name", "m1"));
|
||||||
|
|
||||||
TestDistribPackageStore.assertResponseValues(10,
|
TestDistribPackageStore.assertResponseValues(10,
|
||||||
() -> new V2Request.Builder("/my-random-prefix/their/plugin")
|
() -> new V2Request.Builder("/my-random-prefix/their/plugin")
|
||||||
.forceV2(true)
|
.forceV2(true)
|
||||||
.withMethod(GET)
|
.GET()
|
||||||
.build().process(cluster.getSolrClient()),
|
.build().process(cluster.getSolrClient()),
|
||||||
ImmutableMap.of("/method.name", "m2"));
|
ImmutableMap.of("/method.name", "m2"));
|
||||||
//now remove the plugin
|
//now remove the plugin
|
||||||
new V2Request.Builder("/cluster/plugin")
|
new V2Request.Builder("/cluster/plugin")
|
||||||
.withMethod(POST)
|
.POST()
|
||||||
.forceV2(true)
|
.forceV2(true)
|
||||||
.withPayload("{remove : my-random-name}")
|
.withPayload("{remove : my-random-name}")
|
||||||
.build()
|
.build()
|
||||||
|
@ -160,12 +163,12 @@ public class TestContainerPlugin extends SolrCloudTestCase {
|
||||||
|
|
||||||
expectFail( () -> new V2Request.Builder("/my-random-prefix/their/plugin")
|
expectFail( () -> new V2Request.Builder("/my-random-prefix/their/plugin")
|
||||||
.forceV2(true)
|
.forceV2(true)
|
||||||
.withMethod(GET)
|
.GET()
|
||||||
.build()
|
.build()
|
||||||
.process(cluster.getSolrClient()));
|
.process(cluster.getSolrClient()));
|
||||||
expectFail(() -> new V2Request.Builder("/my-random-prefix/their/plugin")
|
expectFail(() -> new V2Request.Builder("/my-random-prefix/their/plugin")
|
||||||
.forceV2(true)
|
.forceV2(true)
|
||||||
.withMethod(GET)
|
.GET()
|
||||||
.build()
|
.build()
|
||||||
.process(cluster.getSolrClient()));
|
.process(cluster.getSolrClient()));
|
||||||
|
|
||||||
|
@ -177,7 +180,7 @@ public class TestContainerPlugin extends SolrCloudTestCase {
|
||||||
//just check if the plugin is indeed registered
|
//just check if the plugin is indeed registered
|
||||||
readPluginState = new V2Request.Builder("/cluster/plugin")
|
readPluginState = new V2Request.Builder("/cluster/plugin")
|
||||||
.forceV2(true)
|
.forceV2(true)
|
||||||
.withMethod(GET)
|
.GET()
|
||||||
.build();
|
.build();
|
||||||
rsp = readPluginState.process(cluster.getSolrClient());
|
rsp = readPluginState.process(cluster.getSolrClient());
|
||||||
assertEquals(C6.class.getName(), rsp._getStr("/plugin/clusterSingleton/class", null));
|
assertEquals(C6.class.getName(), rsp._getStr("/plugin/clusterSingleton/class", null));
|
||||||
|
@ -185,6 +188,46 @@ public class TestContainerPlugin extends SolrCloudTestCase {
|
||||||
assertTrue("ccProvided", C6.ccProvided);
|
assertTrue("ccProvided", C6.ccProvided);
|
||||||
assertTrue("startCalled", C6.startCalled);
|
assertTrue("startCalled", C6.startCalled);
|
||||||
assertFalse("stopCalled", C6.stopCalled);
|
assertFalse("stopCalled", C6.stopCalled);
|
||||||
|
|
||||||
|
assertEquals( CConfig.class, ContainerPluginsRegistry.getConfigClass(new CC()));
|
||||||
|
assertEquals( CConfig.class, ContainerPluginsRegistry.getConfigClass(new CC1()));
|
||||||
|
assertEquals( CConfig.class, ContainerPluginsRegistry.getConfigClass(new CC2()));
|
||||||
|
|
||||||
|
CConfig p = new CConfig();
|
||||||
|
p.boolVal = Boolean.TRUE;
|
||||||
|
p.strVal = "Something";
|
||||||
|
p.longVal = 1234L;
|
||||||
|
p.name = "hello";
|
||||||
|
p.klass = CC.class.getName();
|
||||||
|
|
||||||
|
new V2Request.Builder("/cluster/plugin")
|
||||||
|
.forceV2(true)
|
||||||
|
.POST()
|
||||||
|
.withPayload(singletonMap("add", p))
|
||||||
|
.build()
|
||||||
|
.process(cluster.getSolrClient());
|
||||||
|
TestDistribPackageStore.assertResponseValues(10,
|
||||||
|
() -> new V2Request.Builder("hello/plugin")
|
||||||
|
.forceV2(true)
|
||||||
|
.GET()
|
||||||
|
.build().process(cluster.getSolrClient()),
|
||||||
|
ImmutableMap.of("/config/boolVal", "true", "/config/strVal", "Something","/config/longVal", "1234" ));
|
||||||
|
|
||||||
|
p.strVal = "Something else";
|
||||||
|
new V2Request.Builder("/cluster/plugin")
|
||||||
|
.forceV2(true)
|
||||||
|
.POST()
|
||||||
|
.withPayload(singletonMap("update", p))
|
||||||
|
.build()
|
||||||
|
.process(cluster.getSolrClient());
|
||||||
|
|
||||||
|
TestDistribPackageStore.assertResponseValues(10,
|
||||||
|
() -> new V2Request.Builder("hello/plugin")
|
||||||
|
.forceV2(true)
|
||||||
|
.GET()
|
||||||
|
.build().process(cluster.getSolrClient()),
|
||||||
|
ImmutableMap.of("/config/boolVal", "true", "/config/strVal", p.strVal,"/config/longVal", "1234" ));
|
||||||
|
|
||||||
// kill the Overseer leader
|
// kill the Overseer leader
|
||||||
for (JettySolrRunner jetty : cluster.getJettySolrRunners()) {
|
for (JettySolrRunner jetty : cluster.getJettySolrRunners()) {
|
||||||
if (!jetty.getCoreContainer().getZkController().getOverseer().isClosed()) {
|
if (!jetty.getCoreContainer().getZkController().getOverseer().isClosed()) {
|
||||||
|
@ -234,7 +277,7 @@ public class TestContainerPlugin extends SolrCloudTestCase {
|
||||||
add.files = singletonList(FILE1);
|
add.files = singletonList(FILE1);
|
||||||
V2Request addPkgVersionReq = new V2Request.Builder("/cluster/package")
|
V2Request addPkgVersionReq = new V2Request.Builder("/cluster/package")
|
||||||
.forceV2(true)
|
.forceV2(true)
|
||||||
.withMethod(POST)
|
.POST()
|
||||||
.withPayload(singletonMap("add", add))
|
.withPayload(singletonMap("add", add))
|
||||||
.build();
|
.build();
|
||||||
addPkgVersionReq.process(cluster.getSolrClient());
|
addPkgVersionReq.process(cluster.getSolrClient());
|
||||||
|
@ -251,14 +294,14 @@ public class TestContainerPlugin extends SolrCloudTestCase {
|
||||||
plugin.version = add.version;
|
plugin.version = add.version;
|
||||||
final V2Request req1 = new V2Request.Builder("/cluster/plugin")
|
final V2Request req1 = new V2Request.Builder("/cluster/plugin")
|
||||||
.forceV2(true)
|
.forceV2(true)
|
||||||
.withMethod(POST)
|
.POST()
|
||||||
.withPayload(singletonMap("add", plugin))
|
.withPayload(singletonMap("add", plugin))
|
||||||
.build();
|
.build();
|
||||||
req1.process(cluster.getSolrClient());
|
req1.process(cluster.getSolrClient());
|
||||||
//verify the plugin creation
|
//verify the plugin creation
|
||||||
TestDistribPackageStore.assertResponseValues(10,
|
TestDistribPackageStore.assertResponseValues(10,
|
||||||
() -> new V2Request.Builder("/cluster/plugin").
|
() -> new V2Request.Builder("/cluster/plugin").
|
||||||
withMethod(GET)
|
GET()
|
||||||
.build().process(cluster.getSolrClient()),
|
.build().process(cluster.getSolrClient()),
|
||||||
ImmutableMap.of(
|
ImmutableMap.of(
|
||||||
"/plugin/myplugin/class", plugin.klass,
|
"/plugin/myplugin/class", plugin.klass,
|
||||||
|
@ -267,7 +310,7 @@ public class TestContainerPlugin extends SolrCloudTestCase {
|
||||||
//let's test this now
|
//let's test this now
|
||||||
Callable<NavigableObject> invokePlugin = () -> new V2Request.Builder("/plugin/my/path")
|
Callable<NavigableObject> invokePlugin = () -> new V2Request.Builder("/plugin/my/path")
|
||||||
.forceV2(true)
|
.forceV2(true)
|
||||||
.withMethod(GET)
|
.GET()
|
||||||
.build().process(cluster.getSolrClient());
|
.build().process(cluster.getSolrClient());
|
||||||
TestDistribPackageStore.assertResponseValues(10,
|
TestDistribPackageStore.assertResponseValues(10,
|
||||||
invokePlugin,
|
invokePlugin,
|
||||||
|
@ -282,7 +325,7 @@ public class TestContainerPlugin extends SolrCloudTestCase {
|
||||||
plugin.version = add.version;
|
plugin.version = add.version;
|
||||||
new V2Request.Builder("/cluster/plugin")
|
new V2Request.Builder("/cluster/plugin")
|
||||||
.forceV2(true)
|
.forceV2(true)
|
||||||
.withMethod(POST)
|
.POST()
|
||||||
.withPayload(singletonMap("update", plugin))
|
.withPayload(singletonMap("update", plugin))
|
||||||
.build()
|
.build()
|
||||||
.process(cluster.getSolrClient());
|
.process(cluster.getSolrClient());
|
||||||
|
@ -290,7 +333,7 @@ public class TestContainerPlugin extends SolrCloudTestCase {
|
||||||
//now verify if it is indeed updated
|
//now verify if it is indeed updated
|
||||||
TestDistribPackageStore.assertResponseValues(10,
|
TestDistribPackageStore.assertResponseValues(10,
|
||||||
() -> new V2Request.Builder("/cluster/plugin").
|
() -> new V2Request.Builder("/cluster/plugin").
|
||||||
withMethod(GET)
|
GET()
|
||||||
.build().process(cluster.getSolrClient()),
|
.build().process(cluster.getSolrClient()),
|
||||||
ImmutableMap.of(
|
ImmutableMap.of(
|
||||||
"/plugin/myplugin/class", plugin.klass,
|
"/plugin/myplugin/class", plugin.klass,
|
||||||
|
@ -312,6 +355,50 @@ public class TestContainerPlugin extends SolrCloudTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class CC1 extends CC {
|
||||||
|
|
||||||
|
}
|
||||||
|
public static class CC2 extends CC1 {
|
||||||
|
|
||||||
|
}
|
||||||
|
public static class CC implements ConfigurablePlugin<CConfig> {
|
||||||
|
private CConfig cfg;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(CConfig cfg) {
|
||||||
|
this.cfg = cfg;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EndPoint(method = GET,
|
||||||
|
path = "/hello/plugin",
|
||||||
|
permission = PermissionNameProvider.Name.READ_PERM)
|
||||||
|
public void m2(SolrQueryRequest req, SolrQueryResponse rsp) {
|
||||||
|
rsp.add("config", cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CConfig implements ReflectMapWriter {
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
public String strVal;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
public Long longVal;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
public Boolean boolVal;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
public String name;
|
||||||
|
|
||||||
|
@JsonProperty(value = "class", required = true)
|
||||||
|
public String klass;
|
||||||
|
}
|
||||||
|
|
||||||
public static class C6 implements ClusterSingleton {
|
public static class C6 implements ClusterSingleton {
|
||||||
static boolean startCalled = false;
|
static boolean startCalled = false;
|
||||||
static boolean stopCalled = false;
|
static boolean stopCalled = false;
|
||||||
|
@ -356,7 +443,6 @@ public class TestContainerPlugin extends SolrCloudTestCase {
|
||||||
private SolrResourceLoader resourceLoader;
|
private SolrResourceLoader resourceLoader;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void inform(ResourceLoader loader) throws IOException {
|
public void inform(ResourceLoader loader) throws IOException {
|
||||||
this.resourceLoader = (SolrResourceLoader) loader;
|
this.resourceLoader = (SolrResourceLoader) loader;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -165,6 +165,26 @@ public class V2Request extends SolrRequest<V2Response> implements MapWriter {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder POST() {
|
||||||
|
this.method = METHOD.POST;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder GET() {
|
||||||
|
this.method = METHOD.GET;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder PUT() {
|
||||||
|
this.method = METHOD.PUT;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder DELETE() {
|
||||||
|
this.method = METHOD.DELETE;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only for testing. It's always true otherwise
|
* Only for testing. It's always true otherwise
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -87,6 +87,7 @@ public class JsonSchemaCreator {
|
||||||
if(p.required()) required.add(name);
|
if(p.required()) required.add(name);
|
||||||
}
|
}
|
||||||
if(!required.isEmpty()) map.put("required", new ArrayList<>(required));
|
if(!required.isEmpty()) map.put("required", new ArrayList<>(required));
|
||||||
|
map.put("additionalProperties", true);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue