mirror of
https://github.com/apache/lucene.git
synced 2025-02-10 03:55:46 +00:00
SOLR-13637 Enable loading of plugins from the corecontainer memclassloader
This commit is contained in:
parent
8870b4ee32
commit
63d127c14f
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 ");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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": {
|
||||
|
@ -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."
|
||||
}
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user