SOLR-13637 Enable loading of plugins from the corecontainer memclassloader

This commit is contained in:
Noble Paul 2019-07-21 12:21:56 -07:00 committed by noble
parent 8870b4ee32
commit 63d127c14f
16 changed files with 327 additions and 64 deletions

View File

@ -230,16 +230,21 @@ public class ApiBag {
}
public static class ReqHandlerToApi extends Api implements PermissionNameProvider {
SolrRequestHandler rh;
PluginBag.PluginHolder<SolrRequestHandler> rh;
public ReqHandlerToApi(SolrRequestHandler rh, SpecProvider spec) {
super(spec);
this.rh = new PluginBag.PluginHolder(new PluginInfo(SolrRequestHandler.TYPE, Collections.emptyMap()),rh );
}
public ReqHandlerToApi(PluginBag.PluginHolder<SolrRequestHandler> rh, SpecProvider spec) {
super(spec);
this.rh = rh;
}
@Override
public void call(SolrQueryRequest req, SolrQueryResponse rsp) {
rh.handleRequest(req, rsp);
rh.get().handleRequest(req, rsp);
}
@Override
@ -339,22 +344,22 @@ public class ApiBag {
}
public static class LazyLoadedApi extends Api {
private final PluginBag.PluginHolder<SolrRequestHandler> holder;
private Api delegate;
protected LazyLoadedApi(SpecProvider specProvider, PluginBag.PluginHolder<SolrRequestHandler> lazyPluginHolder) {
super(specProvider);
this.holder = lazyPluginHolder;
delegate = new ReqHandlerToApi(lazyPluginHolder, spec);
}
@Override
public void call(SolrQueryRequest req, SolrQueryResponse rsp) {
if (!holder.isLoaded()) {
delegate = new ReqHandlerToApi(holder.get(), ApiBag.EMPTY_SPEC);
}
delegate.call(req, rsp);
}
@Override
public ValidatingJsonMap getSpec() {
return super.getSpec();
}
}
}

View File

@ -28,6 +28,7 @@ import java.util.Objects;
import java.util.stream.Collectors;
import com.google.common.collect.ImmutableMap;
import org.apache.lucene.analysis.util.ResourceLoader;
import org.apache.solr.api.Api;
import org.apache.solr.api.V2HttpCall;
import org.apache.solr.common.IteratorWriter;
@ -102,6 +103,9 @@ public class LibListener implements ClusterPropertiesListener {
log.info("clusterprops.json changed , version {}", coreContainer.getZkController().getZkStateReader().getClusterPropsVersion());
boolean forceReload = updateRuntimeLibs(properties);
extHandler.updateReqHandlers(properties, forceReload);
for (SolrCore core : coreContainer.solrCores.getCores()) {
core.globalClassLoaderChanged();
}
myversion = coreContainer.getZkController().getZkStateReader().getClusterPropsVersion();
return false;
}
@ -130,7 +134,9 @@ public class LibListener implements ClusterPropertiesListener {
}
return needsReload[0];
}
public ResourceLoader getResourceLoader() {
return memClassLoader == null ? coreContainer.getResourceLoader() : memClassLoader;
}
void createNewClassLoader(Map m) {
boolean[] loadedAll = new boolean[1];
loadedAll[0] = true;

View File

@ -180,6 +180,7 @@ public class MemClassLoader extends ClassLoader implements AutoCloseable, Resour
try {
return findClass(cname).asSubclass(expectedType);
} catch (Exception e) {
log.error("Error loading class from runtime libs ", e);
if (e instanceof SolrException) {
throw (SolrException) e;
} else {

View File

@ -115,24 +115,36 @@ public class PluginBag<T> implements AutoCloseable {
return result;
}
public PluginHolder<T> createPlugin(PluginInfo info) {
if ("true".equals(String.valueOf(info.attributes.get(RuntimeLib.TYPE)))) {
log.debug(" {} : '{}' created with runtimeLib=true ", meta.getCleanTag(), info.name);
LazyPluginHolder<T> holder = new LazyPluginHolder<>(meta, info, core, RuntimeLib.isEnabled() ?
core.getMemClassLoader() :
core.getResourceLoader(), true);
private static <T> T createInitInstance(PluginInfo pluginInfo, SolrConfig.SolrPluginInfo pluginMeta,
SolrCore core, ResourceLoader resourceLoader,
boolean isRuntimeLib) {
T localInst = null;
try {
localInst = (T) core.createInstance(pluginInfo.className, pluginMeta.clazz, pluginMeta.getCleanTag(), core, resourceLoader);
} catch (SolrException e) {
if (isRuntimeLib && !(resourceLoader instanceof MemClassLoader)) {
throw new SolrException(SolrException.ErrorCode.getErrorCode(e.code()),
e.getMessage() + ". runtime library loading is not enabled, start Solr with -Denable.runtime.lib=true",
e.getCause());
}
throw e;
return meta.clazz == UpdateRequestProcessorFactory.class ?
(PluginHolder<T>) new UpdateRequestProcessorChain.LazyUpdateProcessorFactoryHolder(holder) :
holder;
} else if ("lazy".equals(info.attributes.get("startup")) && meta.options.contains(SolrConfig.PluginOpts.LAZY)) {
log.debug("{} : '{}' created with startup=lazy ", meta.getCleanTag(), info.name);
return new LazyPluginHolder<T>(meta, info, core, core.getResourceLoader(), false);
} else {
T inst = core.createInstance(info.className, (Class<T>) meta.clazz, meta.getCleanTag(), null, core.getResourceLoader());
initInstance(inst, info);
return new PluginHolder<>(info, inst);
}
initInstance(localInst, pluginInfo);
if (localInst instanceof SolrCoreAware) {
SolrResourceLoader.assertAwareCompatibility(SolrCoreAware.class, localInst);
((SolrCoreAware) localInst).inform(core);
}
if (localInst instanceof ResourceLoaderAware) {
SolrResourceLoader.assertAwareCompatibility(ResourceLoaderAware.class, localInst);
try {
((ResourceLoaderAware) localInst).inform(core.getResourceLoader());
} catch (IOException e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "error initializing component", e);
}
}
return localInst;
}
/** make a plugin available in an alternate name. This is an internal API and not for public use
@ -323,13 +335,52 @@ public class PluginBag<T> implements AutoCloseable {
}
}
public PluginHolder<T> createPlugin(PluginInfo info) {
String runtimeType = info.getRuntimeLibType();
if ("global".equals(runtimeType)) {
log.debug(" {} : '{}' created with runtimeLib=global ", meta.getCleanTag(), info.name);
PluginHolder<T> holder = new GlobalLoaderPluginHolder<T>(info, core, meta);
return meta.clazz == UpdateRequestProcessorFactory.class ?
(PluginHolder<T>) new UpdateRequestProcessorChain.LazyUpdateProcessorFactoryHolder((PluginHolder<UpdateRequestProcessorFactory>) holder) :
holder;
} else if ("core".equals(String.valueOf(runtimeType))) {
log.debug(" {} : '{}' created with runtimeLib=true ", meta.getCleanTag(), info.name);
LazyPluginHolder<T> holder = new LazyPluginHolder<>(meta, info, core, RuntimeLib.isEnabled() ?
core.getMemClassLoader() :
core.getResourceLoader(), true);
return meta.clazz == UpdateRequestProcessorFactory.class ?
(PluginHolder<T>) new UpdateRequestProcessorChain.LazyUpdateProcessorFactoryHolder((PluginHolder<UpdateRequestProcessorFactory>) holder) :
holder;
} else if ("lazy".equals(info.attributes.get("startup")) && meta.options.contains(SolrConfig.PluginOpts.LAZY)) {
log.debug("{} : '{}' created with startup=lazy ", meta.getCleanTag(), info.name);
return new LazyPluginHolder<T>(meta, info, core, core.getResourceLoader(), false);
} else {
T inst = core.createInstance(info.className, (Class<T>) meta.clazz, meta.getCleanTag(), null, core.getResourceLoader());
initInstance(inst, info);
return new PluginHolder<>(info, inst);
}
}
public Api v2lookup(String path, String method, Map<String, String> parts) {
if (apiBag == null) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "this should not happen, looking up for v2 API at the wrong place");
}
return apiBag.lookup(path, method, parts);
}
public ApiBag getApiBag() {
return apiBag;
}
/**
* An indirect reference to a plugin. It just wraps a plugin instance.
* subclasses may choose to lazily load the plugin
*/
public static class PluginHolder<T> implements AutoCloseable {
private T inst;
protected final PluginInfo pluginInfo;
T inst;
boolean registerAPI = false;
public PluginHolder(PluginInfo info) {
@ -425,7 +476,8 @@ public class PluginBag<T> implements AutoCloseable {
MemClassLoader loader = (MemClassLoader) resourceLoader;
loader.loadJars();
}
Class<T> clazz = (Class<T>) pluginMeta.clazz;
lazyInst = createInitInstance(pluginInfo,pluginMeta,core,resourceLoader, isRuntimeLib);
/* Class<T> clazz = (Class<T>) pluginMeta.clazz;
T localInst = null;
try {
localInst = core.createInstance(pluginInfo.className, clazz, pluginMeta.getCleanTag(), null, resourceLoader);
@ -452,22 +504,35 @@ public class PluginBag<T> implements AutoCloseable {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "error initializing component", e);
}
}
lazyInst = localInst; // only assign the volatile until after the plugin is completely ready to use
lazyInst = localInst; // only assign the volatile until after the plugin is completely ready to use*/
return true;
}
}
public Api v2lookup(String path, String method, Map<String, String> parts) {
if (apiBag == null) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "this should not happen, looking up for v2 API at the wrong place");
public class GlobalLoaderPluginHolder<T> extends PluginHolder<T> {
private final SolrCore core;
private final SolrConfig.SolrPluginInfo pluginMeta;
private final LibListener libListener;
public GlobalLoaderPluginHolder(PluginInfo info, SolrCore core, SolrConfig.SolrPluginInfo pluginMeta) {
super(info);
this.core = core;
this.pluginMeta = pluginMeta;
this.core.addGlobalClassLoaderListener(() -> reload());
this.libListener = core.getCoreContainer().getClusterPropertiesListener();
reload();
}
return apiBag.lookup(path, method, parts);
private void reload() {
if(inst == null) log.info("reloading plugin {} ", pluginInfo.name);
inst = createInitInstance(pluginInfo, pluginMeta, core, core.getCoreContainer().getClusterPropertiesListener().getResourceLoader(), true);
}
}
public ApiBag getApiBag() {
return apiBag;
}
}

View File

@ -16,14 +16,22 @@
*/
package org.apache.solr.core;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.solr.common.MapSerializable;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.util.DOMUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.util.*;
import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap;
@ -35,6 +43,8 @@ import static org.apache.solr.schema.FieldType.CLASS_NAME;
*
*/
public class PluginInfo implements MapSerializable {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public final String name, className, type;
public final NamedList initArgs;
public final Map<String, String> attributes;
@ -187,4 +197,15 @@ public class PluginInfo implements MapSerializable {
result.isFromSolrConfig = isFromSolrConfig;
return result;
}
public String getRuntimeLibType(){
Object runtimeType = attributes.get(RuntimeLib.TYPE);
if(runtimeType == null || "false".equals(runtimeType) || Boolean.FALSE.equals(runtimeType)) return null;
runtimeType = runtimeType.toString();
if ("true".equals(String.valueOf(runtimeType))) {
log.warn("runtimeLib = true is deprecated, use runtimeLib=core/global");
runtimeType = "core";
}
return runtimeType.toString();
}
}

View File

@ -238,6 +238,7 @@ public final class SolrCore implements SolrInfoBean, SolrMetricProducer, Closeab
public volatile boolean searchEnabled = true;
public volatile boolean indexEnabled = true;
public volatile boolean readOnly = false;
private List<Runnable> globalClassLoaderListeners = new ArrayList<>();
public Set<String> getMetricNames() {
return metricNames;
@ -352,6 +353,15 @@ public final class SolrCore implements SolrInfoBean, SolrMetricProducer, Closeab
.getPath();
}
}
void globalClassLoaderChanged(){
for (Runnable r : globalClassLoaderListeners) {
r.run();
}
}
void addGlobalClassLoaderListener(Runnable r){
globalClassLoaderListeners.add(r);
}
/**
@ -858,7 +868,11 @@ public final class SolrCore implements SolrInfoBean, SolrMetricProducer, Closeab
public <T extends Object> T createInitInstance(PluginInfo info, Class<T> cast, String msg, String defClassName) {
if (info == null) return null;
T o = createInstance(info.className == null ? defClassName : info.className, cast, msg, this, getResourceLoader());
ResourceLoader resourceLoader = "global".equals(info.getRuntimeLibType())?
coreContainer.getClusterPropertiesListener().
getResourceLoader(): getResourceLoader();
T o = createInstance(info.className == null ? defClassName : info.className, cast, msg, this, resourceLoader);
if (o instanceof PluginInfoInitialized) {
((PluginInfoInitialized) o).init(info);
} else if (o instanceof NamedListInitializedPlugin) {

View File

@ -600,6 +600,8 @@ public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAwa
}
}
} catch (Exception e) {
log.error( "error executing commands "+ Utils.toJSONString(ops) ,e);
resp.setException(e);
resp.add(CommandOperation.ERR_MSGS, singletonList(SchemaManager.getErrorStr(e)));
}
@ -788,7 +790,7 @@ public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAwa
return overlay;
}
}
if (!verifyClass(op, clz, info.clazz)) return overlay;
if (!verifyClass(op, clz, info)) return overlay;
if (pluginExists(info, overlay, name)) {
if (isCeate) {
op.addError(formatString(" ''{0}'' already exists . Do an ''{1}'' , if you want to change it ", name, "update-" + info.getTagCleanLower()));
@ -812,12 +814,19 @@ public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAwa
return overlay.getNamedPlugins(info.getCleanTag()).containsKey(name);
}
private boolean verifyClass(CommandOperation op, String clz, Class expected) {
private boolean verifyClass(CommandOperation op, String clz, SolrConfig.SolrPluginInfo pluginMeta) {
if (clz == null) return true;
if (!"true".equals(String.valueOf(op.getStr(RuntimeLib.TYPE, null)))) {
PluginInfo info = new PluginInfo(pluginMeta.getCleanTag(), op.getDataMap());
if(info.getRuntimeLibType() != null && !RuntimeLib.isEnabled()){
op.addError("node not started with enable.runtime.lib=true");
return false;
}
if ( !"true".equals(String.valueOf(op.getStr(RuntimeLib.TYPE, null)))) {
//this is not dynamically loaded so we can verify the class right away
try {
req.getCore().createInitInstance(new PluginInfo(SolrRequestHandler.TYPE, op.getDataMap()), expected, clz, "");
req.getCore().createInitInstance(new PluginInfo(SolrRequestHandler.TYPE, op.getDataMap()), pluginMeta.clazz, clz, "");
} catch (Exception e) {
op.addError(e.getMessage());
return false;

View File

@ -328,7 +328,7 @@ public final class UpdateRequestProcessorChain implements PluginInfoInitialized
public static class LazyUpdateProcessorFactoryHolder extends PluginBag.PluginHolder<UpdateRequestProcessorFactory> {
private volatile UpdateRequestProcessorFactory lazyFactory;
public LazyUpdateProcessorFactoryHolder(final PluginBag.LazyPluginHolder holder) {
public LazyUpdateProcessorFactoryHolder(final PluginBag.PluginHolder<UpdateRequestProcessorFactory> holder) {
super(holder.getPluginInfo());
lazyFactory = new LazyUpdateRequestProcessorFactory(holder);
}
@ -340,26 +340,20 @@ public final class UpdateRequestProcessorChain implements PluginInfoInitialized
}
public class LazyUpdateRequestProcessorFactory extends UpdateRequestProcessorFactory {
private final PluginBag.LazyPluginHolder holder;
UpdateRequestProcessorFactory delegate;
private final PluginBag.PluginHolder<UpdateRequestProcessorFactory> holder;
public LazyUpdateRequestProcessorFactory(PluginBag.LazyPluginHolder holder) {
public LazyUpdateRequestProcessorFactory(PluginBag.PluginHolder holder) {
this.holder = holder;
}
public UpdateRequestProcessorFactory getDelegate() {
return delegate;
return holder.get();
}
@Override
public UpdateRequestProcessor getInstance(SolrQueryRequest req, SolrQueryResponse rsp, UpdateRequestProcessor next) {
if (delegate != null) return delegate.getInstance(req, rsp, next);
return holder.get().getInstance(req, rsp, next);
synchronized (this) {
if (delegate == null)
delegate = (UpdateRequestProcessorFactory) holder.get();
}
return delegate.getInstance(req, rsp, next);
}
}
}

View File

@ -72,11 +72,11 @@ public final class CryptoKeys {
boolean verified;
try {
verified = CryptoKeys.verify(entry.getValue(), Base64.base64ToByteArray(sig), data);
log.info("verified {} ", verified);
log.debug("verified {} ", verified);
if (verified) return entry.getKey();
} catch (Exception e) {
exception = e;
log.info("NOT verified ");
log.debug("NOT verified ");
}
}

View File

@ -18,6 +18,8 @@
package org.apache.solr.handler;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;
import java.util.List;
@ -27,18 +29,24 @@ import java.util.function.Predicate;
import com.carrotsearch.ant.tasks.junit4.dependencies.com.google.common.collect.ImmutableMap;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.client.solrj.ResponseParser;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrResponse;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.BaseHttpSolrClient;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.request.GenericSolrRequest;
import org.apache.solr.client.solrj.request.V2Request;
import org.apache.solr.client.solrj.response.V2Response;
import org.apache.solr.cloud.ConfigRequest;
import org.apache.solr.cloud.MiniSolrCloudCluster;
import org.apache.solr.cloud.SolrCloudTestCase;
import org.apache.solr.common.cloud.ClusterProperties;
import org.apache.solr.common.cloud.SolrZkClient;
import org.apache.solr.common.params.MapSolrParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.Pair;
import org.apache.solr.common.util.Utils;
import org.apache.solr.core.MemClassLoader;
@ -54,6 +62,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.solr.cloud.TestCryptoKeys.readFile;
import static org.apache.solr.common.params.CommonParams.JAVABIN;
import static org.apache.solr.common.params.CommonParams.WT;
import static org.apache.solr.common.util.Utils.getObjectByPath;
import static org.apache.solr.core.TestDynamicLoading.getFileContent;
import static org.apache.solr.core.TestDynamicLoadingUrl.runHttpServer;
@ -98,7 +108,6 @@ public class TestContainerReqHandler extends SolrCloudTestCase {
} catch (Exception e) {
if (i >= repeats - 1) throw e;
log.error("exception in request", e);
continue;
}
}
@ -223,8 +232,8 @@ public class TestContainerReqHandler extends SolrCloudTestCase {
} finally {
server.first().stop();
cluster.shutdown();
server.first().stop();
}
}
@ -237,7 +246,7 @@ public class TestContainerReqHandler extends SolrCloudTestCase {
Pair<Server, Integer> server = runHttpServer(jars);
int port = server.second();
MiniSolrCloudCluster cluster = configureCluster(4).configure();
MiniSolrCloudCluster cluster = configureCluster(4).configure();
try {
@ -328,7 +337,7 @@ public class TestContainerReqHandler extends SolrCloudTestCase {
Pair<Server, Integer> server = runHttpServer(jars);
int port = server.second();
MiniSolrCloudCluster cluster = configureCluster(4).configure();
MiniSolrCloudCluster cluster = configureCluster(4).configure();
try {
@ -428,4 +437,122 @@ public class TestContainerReqHandler extends SolrCloudTestCase {
}
}
public void testPluginGlobalLoader() throws Exception {
String COLLECTION_NAME = "globalLoaderColl";
Map<String, Object> jars = Utils.makeMap(
"/jar1.jar", getFileContent("runtimecode/runtimelibs.jar.bin"),
"/jar2.jar", getFileContent("runtimecode/runtimelibs_v2.jar.bin"),
"/jar3.jar", getFileContent("runtimecode/runtimelibs_v3.jar.bin"));
Pair<Server, Integer> server = runHttpServer(jars);
int port = server.second();
System.setProperty("enable.runtime.lib", "true");
MiniSolrCloudCluster cluster = configureCluster(4)
.addConfig("conf", configset("cloud-minimal"))
.configure();
try {
CollectionAdminRequest
.createCollection(COLLECTION_NAME, "conf", 2, 1)
.setMaxShardsPerNode(100)
.process(cluster.getSolrClient());
cluster.waitForActiveCollection(COLLECTION_NAME, 2, 2);
String payload = "{add-runtimelib:{name : 'foo', url: 'http://localhost:" + port + "/jar1.jar', " +
"sha512 : 'd01b51de67ae1680a84a813983b1de3b592fc32f1a22b662fc9057da5953abd1b72476388ba342cad21671cd0b805503c78ab9075ff2f3951fdf75fa16981420'}}";
new V2Request.Builder("/cluster")
.withPayload(payload)
.withMethod(SolrRequest.METHOD.POST)
.build().process(cluster.getSolrClient());
assertEquals(getObjectByPath(Utils.fromJSONString(payload), true, "add-runtimelib/sha512"),
getObjectByPath(new ClusterProperties(cluster.getZkClient()).getClusterProperties(), true, "runtimeLib/foo/sha512"));
payload = "{\n" +
"'create-requesthandler' : { 'name' : '/runtime', 'class': 'org.apache.solr.core.RuntimeLibReqHandler' , 'runtimeLib':global }," +
"'create-searchcomponent' : { 'name' : 'get', 'class': 'org.apache.solr.core.RuntimeLibSearchComponent' , 'runtimeLib':global }," +
"'create-queryResponseWriter' : { 'name' : 'json1', 'class': 'org.apache.solr.core.RuntimeLibResponseWriter' , 'runtimeLib':global }" +
"}";
cluster.getSolrClient().request(new ConfigRequest(payload) {
@Override
public String getCollection() {
return COLLECTION_NAME;
}
});
SolrParams params = new MapSolrParams((Map) Utils.makeMap("collection", COLLECTION_NAME, WT, JAVABIN));
assertResponseValues(10,
cluster.getSolrClient(),
new GenericSolrRequest(SolrRequest.METHOD.GET, "/config/overlay", params),
Utils.makeMap(
"overlay/queryResponseWriter/json1/class", "org.apache.solr.core.RuntimeLibResponseWriter",
"overlay/searchComponent/get/class", "org.apache.solr.core.RuntimeLibSearchComponent"
));
assertResponseValues(10,
cluster.getSolrClient(),
new GenericSolrRequest(SolrRequest.METHOD.GET, "/runtime", params),
Utils.makeMap("class", "org.apache.solr.core.RuntimeLibReqHandler",
"loader", MemClassLoader.class.getName()));
assertResponseValues(10,
cluster.getSolrClient(),
new GenericSolrRequest(SolrRequest.METHOD.GET, "/get?abc=xyz", params),
Utils.makeMap("get", "org.apache.solr.core.RuntimeLibSearchComponent",
"loader", MemClassLoader.class.getName()));
GenericSolrRequest req = new GenericSolrRequest(SolrRequest.METHOD.GET, "/runtime",
new MapSolrParams((Map) Utils.makeMap("collection", COLLECTION_NAME, WT, "json1")));
req.setResponseParser(new ResponseParser() {
@Override
public String getWriterType() {
return "json1";
}
@Override
public NamedList<Object> processResponse(InputStream body, String encoding) {
return new NamedList<>((Map) Utils.fromJSON(body));
}
@Override
public NamedList<Object> processResponse(Reader reader) {
return new NamedList<>((Map) Utils.fromJSON(reader));
}
});
assertResponseValues(10,
cluster.getSolrClient(),
req,
Utils.makeMap("wt", "org.apache.solr.core.RuntimeLibResponseWriter",
"loader", MemClassLoader.class.getName()));
payload = "{update-runtimelib:{name : 'foo', url: 'http://localhost:" + port + "/jar2.jar', " +
"sha512 : 'bc5ce45ad281b6a08fb7e529b1eb475040076834816570902acb6ebdd809410e31006efdeaa7f78a6c35574f3504963f5f7e4d92247d0eb4db3fc9abdda5d417'}}";
new V2Request.Builder("/cluster")
.withPayload(payload)
.withMethod(SolrRequest.METHOD.POST)
.build().process(cluster.getSolrClient());
assertEquals(getObjectByPath(Utils.fromJSONString(payload), true, "update-runtimelib/sha512"),
getObjectByPath(new ClusterProperties(cluster.getZkClient()).getClusterProperties(), true, "runtimeLib/foo/sha512"));
assertResponseValues(10,
cluster.getSolrClient(),
new GenericSolrRequest(SolrRequest.METHOD.GET, "/get?abc=xyz", params),
Utils.makeMap("get", "org.apache.solr.core.RuntimeLibSearchComponent",
"loader", MemClassLoader.class.getName(),
"Version","2"));
} finally {
cluster.deleteAllCollections();
cluster.shutdown();
server.first().stop();
}
}
}

View File

@ -31,7 +31,7 @@ public class RuntimeUrp extends SimpleUpdateProcessorFactory {
List<String> names = new ArrayList<>();
for (UpdateRequestProcessorFactory p : processorChain.getProcessors()) {
if (p instanceof UpdateRequestProcessorChain.LazyUpdateProcessorFactoryHolder.LazyUpdateRequestProcessorFactory) {
p = ((UpdateRequestProcessorChain.LazyUpdateProcessorFactoryHolder.LazyUpdateRequestProcessorFactory) p).delegate;
p = ((UpdateRequestProcessorChain.LazyUpdateProcessorFactoryHolder.LazyUpdateRequestProcessorFactory) p).getDelegate();
}
names.add(p.getClass().getSimpleName());
}

View File

@ -18,10 +18,12 @@ package org.apache.solr.client.solrj;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import org.apache.solr.common.MapWriter;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.util.NamedList;
@ -32,7 +34,7 @@ import org.apache.solr.common.util.NamedList;
*
* @since solr 1.3
*/
public abstract class SolrResponse implements Serializable {
public abstract class SolrResponse implements Serializable, MapWriter {
/** Elapsed time in milliseconds for the request as seen from the client. */
public abstract long getElapsedTime();
@ -43,6 +45,11 @@ public abstract class SolrResponse implements Serializable {
public abstract NamedList<Object> getResponse();
@Override
public void writeMap(EntryWriter ew) throws IOException {
getResponse().writeMap(ew);
}
public Exception getException() {
NamedList exp = (NamedList) getResponse().get("exception");
if (exp == null) {

View File

@ -28,6 +28,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.solr.common.MapWriter;
import org.apache.solr.common.SolrException;
import org.noggit.JSONParser;
import org.noggit.ObjectBuilder;
@ -38,7 +39,7 @@ import static java.util.Collections.singletonMap;
import static org.apache.solr.common.util.StrUtils.formatString;
import static org.apache.solr.common.util.Utils.toJSON;
public class CommandOperation {
public class CommandOperation implements MapWriter {
public final String name;
private Object commandData;//this is most often a map
private List<String> errors = new ArrayList<>();
@ -386,4 +387,10 @@ public class CommandOperation {
if (o == null) return null;
return getInt(name, null);
}
@Override
public void writeMap(EntryWriter ew) throws IOException {
ew.put(name, commandData);
ew.putIfNotNull("errors", errors);
}
}

View File

@ -722,4 +722,11 @@ public class Utils {
return def;
}
public static void main(String[] args) {
List<String> strings = StrUtils.splitSmart("overlay/requestHandler//runtime/class",
'/', true);
System.out.println(strings);
}
}

View File

@ -10,7 +10,7 @@
"description": "The request handler class. Class names do not need to be fully qualified if they are included with Solr, so you can abbreviate the name as 'solr.SearchHandler'. Custom or third-party class names may need to be fully qualified, however."
},
"runtimeLib": {
"type": "boolean",
"type": "string",
"description": "An optional parameter to use a custom .jar file that has been uploaded to Solr's blobstore. This additionally requires that the .jar has also been registered with the 'add-runtimelib' command, which is one of the available commands for the Config API."
},
"startup": {

View File

@ -10,7 +10,7 @@
"description": "The configuration item class. Class names do not need to be fully qualified if they are included with Solr, so you can abbreviate the name as 'solr.SearchHandler'. Custom or third-party class names may need to be fully qualified, however."
},
"runtimeLib": {
"type": "boolean",
"type": "string",
"description": "An optional parameter to use a custom .jar file that has been uploaded to Solr's blobstore. This additionally requires that the .jar has also been registered with the 'add-runtimelib' command, which is one of the available commands for the Config API."
}
},