SOLR-14155: Load all other SolrCore plugins from packages (#1666)

This commit is contained in:
Noble Paul 2021-01-13 22:28:01 +11:00 committed by GitHub
parent 7a301c736c
commit 9466af576a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 403 additions and 112 deletions

View File

@ -249,6 +249,8 @@ Improvements
* SOLR-15040: Improvements to postlogs timestamp handling (Joel Bernstein) * SOLR-15040: Improvements to postlogs timestamp handling (Joel Bernstein)
* SOLR-14155: Load all other SolrCore plugins from packages (noble)
Optimizations Optimizations
--------------------- ---------------------
* SOLR-14975: Optimize CoreContainer.getAllCoreNames, getLoadedCoreNames and getCoreDescriptors. (Bruno Roustant) * SOLR-14975: Optimize CoreContainer.getAllCoreNames, getLoadedCoreNames and getCoreDescriptors. (Bruno Roustant)

View File

@ -419,7 +419,7 @@ public abstract class DirectoryFactory implements NamedListInitializedPlugin,
final DirectoryFactory dirFactory; final DirectoryFactory dirFactory;
if (info != null) { if (info != null) {
log.debug(info.className); log.debug(info.className);
dirFactory = config.getResourceLoader().newInstance(info.className, DirectoryFactory.class); dirFactory = config.getResourceLoader().newInstance (info, DirectoryFactory.class, true);
// allow DirectoryFactory instances to access the CoreContainer // allow DirectoryFactory instances to access the CoreContainer
dirFactory.initCoreContainer(cc); dirFactory.initCoreContainer(cc);
dirFactory.init(info.initArgs); dirFactory.init(info.initArgs);

View File

@ -396,7 +396,6 @@ public class PluginBag<T> implements AutoCloseable {
if (pluginInfo != null) return pluginInfo.className; if (pluginInfo != null) return pluginInfo.className;
return null; return null;
} }
public PluginInfo getPluginInfo() { public PluginInfo getPluginInfo() {
return pluginInfo; return pluginInfo;
} }

View File

@ -755,7 +755,7 @@ public final class SolrCore implements SolrInfoBean, Closeable {
IndexReaderFactory indexReaderFactory; IndexReaderFactory indexReaderFactory;
PluginInfo info = solrConfig.getPluginInfo(IndexReaderFactory.class.getName()); PluginInfo info = solrConfig.getPluginInfo(IndexReaderFactory.class.getName());
if (info != null) { if (info != null) {
indexReaderFactory = resourceLoader.newInstance(info.className, IndexReaderFactory.class); indexReaderFactory = resourceLoader.newInstance(info, IndexReaderFactory.class, true);
indexReaderFactory.init(info.initArgs); indexReaderFactory.init(info.initArgs);
} else { } else {
indexReaderFactory = new StandardIndexReaderFactory(); indexReaderFactory = new StandardIndexReaderFactory();
@ -955,6 +955,7 @@ public final class SolrCore implements SolrInfoBean, Closeable {
this.solrConfig = configSet.getSolrConfig(); this.solrConfig = configSet.getSolrConfig();
this.resourceLoader = configSet.getSolrConfig().getResourceLoader(); this.resourceLoader = configSet.getSolrConfig().getResourceLoader();
this.resourceLoader.initCore(this);
IndexSchema schema = configSet.getIndexSchema(); IndexSchema schema = configSet.getIndexSchema();
this.configSetProperties = configSet.getProperties(); this.configSetProperties = configSet.getProperties();
@ -1241,7 +1242,7 @@ public final class SolrCore implements SolrInfoBean, Closeable {
private void checkVersionFieldExistsInSchema(IndexSchema schema, CoreDescriptor coreDescriptor) { private void checkVersionFieldExistsInSchema(IndexSchema schema, CoreDescriptor coreDescriptor) {
if (null != coreDescriptor.getCloudDescriptor()) { if (null != coreDescriptor.getCloudDescriptor()) {
// we are evidently running in cloud mode. // we are evidently running in cloud mode.
// //
// In cloud mode, version field is required for correct consistency // In cloud mode, version field is required for correct consistency
// ideally this check would be more fine grained, and individual features // ideally this check would be more fine grained, and individual features
@ -1405,7 +1406,7 @@ public final class SolrCore implements SolrInfoBean, Closeable {
final PluginInfo info = solrConfig.getPluginInfo(CodecFactory.class.getName()); final PluginInfo info = solrConfig.getPluginInfo(CodecFactory.class.getName());
final CodecFactory factory; final CodecFactory factory;
if (info != null) { if (info != null) {
factory = resourceLoader.newInstance(info.className, CodecFactory.class); factory = resourceLoader.newInstance( info, CodecFactory.class, true);
factory.init(info.initArgs); factory.init(info.initArgs);
} else { } else {
factory = new CodecFactory() { factory = new CodecFactory() {
@ -1443,8 +1444,8 @@ public final class SolrCore implements SolrInfoBean, Closeable {
final StatsCache cache; final StatsCache cache;
PluginInfo pluginInfo = solrConfig.getPluginInfo(StatsCache.class.getName()); PluginInfo pluginInfo = solrConfig.getPluginInfo(StatsCache.class.getName());
if (pluginInfo != null && pluginInfo.className != null && pluginInfo.className.length() > 0) { if (pluginInfo != null && pluginInfo.className != null && pluginInfo.className.length() > 0) {
cache = createInitInstance(pluginInfo, StatsCache.class, null, cache = resourceLoader.newInstance( pluginInfo, StatsCache.class, true);
LocalStatsCache.class.getName()); initPlugin(pluginInfo ,cache);
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("Using statsCache impl: {}", cache.getClass().getName()); log.debug("Using statsCache impl: {}", cache.getClass().getName());
} }
@ -2134,7 +2135,7 @@ public final class SolrCore implements SolrInfoBean, Closeable {
newReader = currentReader; newReader = currentReader;
} }
// for now, turn off caches if this is for a realtime reader // for now, turn off caches if this is for a realtime reader
// (caches take a little while to instantiate) // (caches take a little while to instantiate)
final boolean useCaches = !realtime; final boolean useCaches = !realtime;
final String newName = realtime ? "realtime" : "main"; final String newName = realtime ? "realtime" : "main";
@ -3177,7 +3178,7 @@ public final class SolrCore implements SolrInfoBean, Closeable {
public void cleanupOldIndexDirectories(boolean reload) { public void cleanupOldIndexDirectories(boolean reload) {
final DirectoryFactory myDirFactory = getDirectoryFactory(); final DirectoryFactory myDirFactory = getDirectoryFactory();
final String myDataDir = getDataDir(); final String myDataDir = getDataDir();
final String myIndexDir = getNewIndexDir(); // ensure the latest replicated index is protected final String myIndexDir = getNewIndexDir(); // ensure the latest replicated index is protected
final String coreName = getName(); final String coreName = getName();
if (myDirFactory != null && myDataDir != null && myIndexDir != null) { if (myDirFactory != null && myDataDir != null && myIndexDir != null) {
Thread cleanupThread = new Thread(() -> { Thread cleanupThread = new Thread(() -> {
@ -3280,6 +3281,9 @@ public final class SolrCore implements SolrInfoBean, Closeable {
this.coreName = coreName; this.coreName = coreName;
this.coreId = coreId; this.coreId = coreId;
} }
public void reload() {
coreContainer.reload(coreName, coreId);
}
public void withCore(Consumer<SolrCore> r) { public void withCore(Consumer<SolrCore> r) {
try(SolrCore core = coreContainer.getCore(coreName, coreId)) { try(SolrCore core = coreContainer.getCore(coreName, coreId)) {

View File

@ -42,6 +42,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -64,6 +65,7 @@ import org.apache.solr.handler.component.SearchComponent;
import org.apache.solr.handler.component.ShardHandlerFactory; import org.apache.solr.handler.component.ShardHandlerFactory;
import org.apache.solr.logging.DeprecationLog; import org.apache.solr.logging.DeprecationLog;
import org.apache.solr.pkg.PackageListeningClassLoader; import org.apache.solr.pkg.PackageListeningClassLoader;
import org.apache.solr.pkg.PackageLoader;
import org.apache.solr.request.SolrRequestHandler; import org.apache.solr.request.SolrRequestHandler;
import org.apache.solr.response.QueryResponseWriter; import org.apache.solr.response.QueryResponseWriter;
import org.apache.solr.rest.RestManager; import org.apache.solr.rest.RestManager;
@ -100,6 +102,7 @@ public class SolrResourceLoader implements ResourceLoader, Closeable, SolrClassL
private CoreContainer coreContainer; private CoreContainer coreContainer;
private PackageListeningClassLoader schemaLoader ; private PackageListeningClassLoader schemaLoader ;
private PackageListeningClassLoader coreReloadingClassLoader ;
private final List<SolrCoreAware> waitingForCore = Collections.synchronizedList(new ArrayList<SolrCoreAware>()); private final List<SolrCoreAware> waitingForCore = Collections.synchronizedList(new ArrayList<SolrCoreAware>());
private final List<SolrInfoBean> infoMBeans = Collections.synchronizedList(new ArrayList<SolrInfoBean>()); private final List<SolrInfoBean> infoMBeans = Collections.synchronizedList(new ArrayList<SolrInfoBean>());
private final List<ResourceLoaderAware> waitingForResources = Collections.synchronizedList(new ArrayList<ResourceLoaderAware>()); private final List<ResourceLoaderAware> waitingForResources = Collections.synchronizedList(new ArrayList<ResourceLoaderAware>());
@ -109,7 +112,7 @@ public class SolrResourceLoader implements ResourceLoader, Closeable, SolrClassL
// Provide a registry so that managed resources can register themselves while the XML configuration // Provide a registry so that managed resources can register themselves while the XML configuration
// documents are being parsed ... after all are registered, they are asked by the RestManager to // documents are being parsed ... after all are registered, they are asked by the RestManager to
// initialize themselves. This two-step process is required because not all resources are available // initialize themselves. This two-step process is required because not all resources are available
// (such as the SolrZkClient) when XML docs are being parsed. // (such as the SolrZkClient) when XML docs are being parsed.
private RestManager.Registry managedResourceRegistry; private RestManager.Registry managedResourceRegistry;
/** @see #reloadLuceneSPI() */ /** @see #reloadLuceneSPI() */
@ -165,7 +168,7 @@ public class SolrResourceLoader implements ResourceLoader, Closeable, SolrClassL
if (instanceDir == null) { if (instanceDir == null) {
throw new NullPointerException("SolrResourceLoader instanceDir must be non-null"); throw new NullPointerException("SolrResourceLoader instanceDir must be non-null");
} }
this.instanceDir = instanceDir; this.instanceDir = instanceDir;
log.debug("new SolrResourceLoader for directory: '{}'", this.instanceDir); log.debug("new SolrResourceLoader for directory: '{}'", this.instanceDir);
@ -645,15 +648,29 @@ public class SolrResourceLoader implements ResourceLoader, Closeable, SolrClassL
} }
} }
void initCore(SolrCore core) {
this.coreName = core.getName();
this.config = core.getSolrConfig();
this.coreId = core.uniqueId;
this.coreContainer = core.getCoreContainer();
SolrCore.Provider coreProvider = core.coreProvider;
this.coreReloadingClassLoader = new PackageListeningClassLoader(core.getCoreContainer(),
this, s -> config.maxPackageVersion(s), null){
@Override
protected void doReloadAction(Ctx ctx) {
log.info("Core reloading classloader issued reload for: {}/{} ", coreName, coreId);
coreProvider.reload();
}
};
core.getPackageListeners().addListener(coreReloadingClassLoader, true);
}
/** /**
* Tell all {@link SolrCoreAware} instances about the SolrCore * Tell all {@link SolrCoreAware} instances about the SolrCore
*/ */
public void inform(SolrCore core) { public void inform(SolrCore core) {
this.coreName = core.getName();
this.config = core.getSolrConfig();
this.coreId = core.uniqueId;
this.coreContainer = core.getCoreContainer();
if(getSchemaLoader() != null) core.getPackageListeners().addListener(schemaLoader); if(getSchemaLoader() != null) core.getPackageListeners().addListener(schemaLoader);
// make a copy to avoid potential deadlock of a callback calling newInstance and trying to // make a copy to avoid potential deadlock of a callback calling newInstance and trying to
@ -834,6 +851,47 @@ public class SolrResourceLoader implements ResourceLoader, Closeable, SolrClassL
public List<SolrInfoBean> getInfoMBeans() { public List<SolrInfoBean> getInfoMBeans() {
return Collections.unmodifiableList(infoMBeans); return Collections.unmodifiableList(infoMBeans);
} }
/**
* Load a class using an appropriate {@link SolrResourceLoader} depending of the package on that class
* @param registerCoreReloadListener register a listener for the package and reload the core if the package is changed.
* Use this sparingly. This will result in core reloads across all the cores in
* all collections using this configset
*/
public <T> Class<? extends T> findClass( PluginInfo info, Class<T> type, boolean registerCoreReloadListener) {
if(info.cName.pkg == null) return findClass(info.className, type);
return _classLookup(info,
(Function<PackageLoader.Package.Version, Class<? extends T>>) ver -> ver.getLoader().findClass(info.cName.className, type), registerCoreReloadListener);
}
private <T> T _classLookup(PluginInfo info, Function<PackageLoader.Package.Version, T> fun, boolean registerCoreReloadListener ) {
PluginInfo.ClassName cName = info.cName;
if (registerCoreReloadListener) {
if (coreReloadingClassLoader == null) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Core not set");
}
return fun.apply(coreReloadingClassLoader.findPackageVersion(cName, true));
} else {
return fun.apply(coreReloadingClassLoader.findPackageVersion(cName, false));
}
}
/**
*Create a n instance of a class using an appropriate {@link SolrResourceLoader} depending on the package of that class
* @param registerCoreReloadListener register a listener for the package and reload the core if the package is changed.
* Use this sparingly. This will result in core reloads across all the cores in
* all collections using this configset
*/
public <T> T newInstance(PluginInfo info, Class<T> type, boolean registerCoreReloadListener) {
if(info.cName.pkg == null) {
return newInstance(info.cName.className == null?
type.getName():
info.cName.className ,
type);
}
return _classLookup( info, version -> version.getLoader().newInstance(info.cName.className, type), registerCoreReloadListener);
}
private PackageListeningClassLoader createSchemaLoader() { private PackageListeningClassLoader createSchemaLoader() {
CoreContainer cc = getCoreContainer(); CoreContainer cc = getCoreContainer();

View File

@ -48,6 +48,7 @@ import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.io.stream.expr.Expressible; import org.apache.solr.client.solrj.io.stream.expr.Expressible;
import org.apache.solr.cloud.ZkController; import org.apache.solr.cloud.ZkController;
import org.apache.solr.cloud.ZkSolrResourceLoader; import org.apache.solr.cloud.ZkSolrResourceLoader;
import org.apache.solr.common.MapSerializable;
import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException;
import org.apache.solr.common.cloud.ClusterState; import org.apache.solr.common.cloud.ClusterState;
import org.apache.solr.common.cloud.DocCollection; import org.apache.solr.common.cloud.DocCollection;
@ -69,6 +70,7 @@ import org.apache.solr.core.RequestParams;
import org.apache.solr.core.SolrConfig; import org.apache.solr.core.SolrConfig;
import org.apache.solr.core.SolrCore; import org.apache.solr.core.SolrCore;
import org.apache.solr.core.SolrResourceLoader; import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.pkg.PackageAPI;
import org.apache.solr.pkg.PackageListeners; import org.apache.solr.pkg.PackageListeners;
import org.apache.solr.request.LocalSolrQueryRequest; import org.apache.solr.request.LocalSolrQueryRequest;
import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrQueryRequest;
@ -250,25 +252,28 @@ public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAwa
String componentName = req.getParams().get("componentName"); String componentName = req.getParams().get("componentName");
if (componentName != null) { if (componentName != null) {
@SuppressWarnings({"rawtypes"}) @SuppressWarnings({"rawtypes"})
Map map = (Map) val.get(parts.get(1)); Map pluginNameVsPluginInfo = (Map) val.get(parts.get(1));
if (map != null) { if (pluginNameVsPluginInfo != null) {
Object o = map.get(componentName); @SuppressWarnings({"rawtypes"})
val.put(parts.get(1), makeMap(componentName, o)); Object o = pluginNameVsPluginInfo instanceof MapSerializable ?
pluginNameVsPluginInfo:
pluginNameVsPluginInfo.get(componentName);
@SuppressWarnings({"rawtypes"})
Map pluginInfo = o instanceof MapSerializable? ((MapSerializable) o).toMap(new LinkedHashMap<>()): (Map) o;
val.put(parts.get(1),pluginNameVsPluginInfo instanceof PluginInfo? pluginInfo : makeMap(componentName, pluginInfo));
if (req.getParams().getBool("meta", false)) { if (req.getParams().getBool("meta", false)) {
// meta=true is asking for the package info of the plugin // meta=true is asking for the package info of the plugin
// We go through all the listeners and see if there is one registered for this plugin // We go through all the listeners and see if there is one registered for this plugin
List<PackageListeners.Listener> listeners = req.getCore().getPackageListeners().getListeners(); List<PackageListeners.Listener> listeners = req.getCore().getPackageListeners().getListeners();
for (PackageListeners.Listener listener : for (PackageListeners.Listener listener :
listeners) { listeners) {
PluginInfo info = listener.pluginInfo(); Map<String, PackageAPI.PkgVersion> infos = listener.packageDetails();
if(info == null) continue; if(infos == null || infos.isEmpty()) continue;
if (info.type.equals(parts.get(1)) && info.name.equals(componentName)) { infos.forEach((s, mapWriter) -> {
if (o instanceof Map) { if(s.equals(pluginInfo.get("class"))) {
@SuppressWarnings({"rawtypes"}) (pluginInfo).put("_packageinfo_", mapWriter);
Map m1 = (Map) o;
m1.put("_packageinfo_", listener.getPackageVersion(info.cName));
} }
} });
} }
} }
} }

View File

@ -16,16 +16,6 @@
*/ */
package org.apache.solr.handler.component; package org.apache.solr.handler.component;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.index.ExitableDirectoryReader; import org.apache.lucene.index.ExitableDirectoryReader;
import org.apache.lucene.search.TotalHits; import org.apache.lucene.search.TotalHits;
@ -44,6 +34,7 @@ import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.PluginInfo; import org.apache.solr.core.PluginInfo;
import org.apache.solr.core.SolrCore; import org.apache.solr.core.SolrCore;
import org.apache.solr.handler.RequestHandlerBase; import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.pkg.PackageAPI;
import org.apache.solr.pkg.PackageListeners; import org.apache.solr.pkg.PackageListeners;
import org.apache.solr.pkg.PackageLoader; import org.apache.solr.pkg.PackageLoader;
import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrQueryRequest;
@ -61,10 +52,13 @@ import org.apache.solr.util.plugin.SolrCoreAware;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import static org.apache.solr.common.params.CommonParams.DISTRIB; import java.io.PrintWriter;
import static org.apache.solr.common.params.CommonParams.FAILURE; import java.io.StringWriter;
import static org.apache.solr.common.params.CommonParams.PATH; import java.lang.invoke.MethodHandles;
import static org.apache.solr.common.params.CommonParams.STATUS; import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import static org.apache.solr.common.params.CommonParams.*;
/** /**
@ -162,8 +156,8 @@ public class SearchHandler extends RequestHandlerBase implements SolrCoreAware,
} }
@Override @Override
public PluginInfo pluginInfo() { public Map<String , PackageAPI.PkgVersion> packageDetails() {
return null; return Collections.emptyMap();
} }
@Override @Override

View File

@ -47,6 +47,14 @@ public class PackageListeners {
public synchronized void addListener(Listener listener) { public synchronized void addListener(Listener listener) {
listeners.add(new SoftReference<>(listener)); listeners.add(new SoftReference<>(listener));
}
public synchronized void addListener(Listener listener, boolean addFirst) {
if(addFirst) {
listeners.add(0, new SoftReference<>(listener));
} else {
addListener(listener);
}
} }
@ -71,7 +79,7 @@ public class PackageListeners {
invokeListeners(pkgInfo, ctx); invokeListeners(pkgInfo, ctx);
} }
} finally { } finally {
ctx.runLaterTasks(core::runAsync); ctx.runLaterTasks(r -> core.getCoreContainer().runAsync(r));
MDCLoggingContext.clear(); MDCLoggingContext.clear();
} }
} }
@ -102,7 +110,10 @@ public class PackageListeners {
/**Name of the package or null to listen to all package changes */ /**Name of the package or null to listen to all package changes */
String packageName(); String packageName();
PluginInfo pluginInfo(); /** fetch the package versions of class names
*
*/
Map<String, PackageAPI.PkgVersion> packageDetails();
/**A callback when the package is updated */ /**A callback when the package is updated */
void changed(PackageLoader.Package pkg, Ctx ctx); void changed(PackageLoader.Package pkg, Ctx ctx);

View File

@ -26,9 +26,8 @@ import org.apache.solr.common.cloud.SolrClassLoader;
import org.apache.solr.core.SolrResourceLoader; import org.apache.solr.core.SolrResourceLoader;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.*;
import java.util.Map; import java.util.concurrent.ConcurrentHashMap;
import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
/** /**
* A {@link SolrClassLoader} that is designed to listen to a set of packages. * A {@link SolrClassLoader} that is designed to listen to a set of packages.
@ -37,22 +36,29 @@ import java.util.function.Function;
* */ * */
public class PackageListeningClassLoader implements SolrClassLoader , PackageListeners.Listener { public class PackageListeningClassLoader implements SolrClassLoader , PackageListeners.Listener {
private final CoreContainer coreContainer; private final CoreContainer coreContainer;
private final SolrResourceLoader coreResourceLoader; private final SolrClassLoader fallbackClassLoader;
private final Function<String, String> pkgVersionSupplier; private final Function<String, String> pkgVersionSupplier;
/** package name and the versions that we are tracking /** package name and the versions that we are tracking
*/ */
private Map<String ,PackageAPI.PkgVersion> packageVersions = new HashMap<>(1); private Map<String ,PackageAPI.PkgVersion> packageVersions = new ConcurrentHashMap<>(1);
private Map<String, String> classNameVsPackageName = new ConcurrentHashMap<>();
private final Runnable onReload; private final Runnable onReload;
/**
* @param fallbackClassLoader The {@link SolrClassLoader} to use if no package is specified
* @param pkgVersionSupplier Get the version configured for a given package
* @param onReload The callback function that should be run if a package is updated
*/
public PackageListeningClassLoader(CoreContainer coreContainer, public PackageListeningClassLoader(CoreContainer coreContainer,
SolrResourceLoader coreResourceLoader, SolrClassLoader fallbackClassLoader,
Function<String, String> pkgVersionSupplier, Function<String, String> pkgVersionSupplier,
Runnable onReload) { Runnable onReload) {
this.coreContainer = coreContainer; this.coreContainer = coreContainer;
this.coreResourceLoader = coreResourceLoader; this.fallbackClassLoader = fallbackClassLoader;
this.pkgVersionSupplier = pkgVersionSupplier; this.pkgVersionSupplier = pkgVersionSupplier;
this.onReload = () -> { this.onReload = () -> {
packageVersions = new HashMap<>(); packageVersions = new ConcurrentHashMap<>();
classNameVsPackageName = new ConcurrentHashMap<>();
onReload.run(); onReload.run();
}; };
} }
@ -61,18 +67,28 @@ public class PackageListeningClassLoader implements SolrClassLoader , PackageLis
@Override @Override
public <T> T newInstance(String cname, Class<T> expectedType, String... subpackages) { public <T> T newInstance(String cname, Class<T> expectedType, String... subpackages) {
PluginInfo.ClassName cName = new PluginInfo.ClassName(cname); PluginInfo.ClassName cName = new PluginInfo.ClassName(cname);
if(cName.pkg == null){ if(cName.pkg == null) {
return coreResourceLoader.newInstance(cname, expectedType, subpackages); return fallbackClassLoader.newInstance(cname, expectedType, subpackages);
} else { } else {
PackageLoader.Package.Version version = findPkgVersion(cName); PackageLoader.Package.Version version = findPackageVersion(cName, true);
return applyResourceLoaderAware(version, version.getLoader().newInstance(cName.className, expectedType, subpackages)); T obj = version.getLoader().newInstance(cName.className, expectedType, subpackages);
classNameVsPackageName.put(cName.original, cName.pkg);
return applyResourceLoaderAware(version, obj);
} }
} }
private PackageLoader.Package.Version findPkgVersion(PluginInfo.ClassName cName) {
/**
* This looks up for package and also listens for that package if required
* @param cName The class name
*/
public PackageLoader.Package.Version findPackageVersion(PluginInfo.ClassName cName, boolean registerListener) {
PackageLoader.Package.Version theVersion = coreContainer.getPackageLoader().getPackage(cName.pkg).getLatest(pkgVersionSupplier.apply(cName.pkg)); PackageLoader.Package.Version theVersion = coreContainer.getPackageLoader().getPackage(cName.pkg).getLatest(pkgVersionSupplier.apply(cName.pkg));
packageVersions.put(cName.pkg, theVersion.getPkgVersion()); if(registerListener) {
classNameVsPackageName.put(cName.original, cName.pkg);
PackageAPI.PkgVersion pkgVersion = theVersion.getPkgVersion();
if(pkgVersion !=null) packageVersions.put(cName.pkg, pkgVersion);
}
return theVersion; return theVersion;
} }
@ -101,10 +117,12 @@ public class PackageListeningClassLoader implements SolrClassLoader , PackageLis
public <T> T newInstance(String cname, Class<T> expectedType, String[] subPackages, Class[] params, Object[] args) { public <T> T newInstance(String cname, Class<T> expectedType, String[] subPackages, Class[] params, Object[] args) {
PluginInfo.ClassName cName = new PluginInfo.ClassName(cname); PluginInfo.ClassName cName = new PluginInfo.ClassName(cname);
if (cName.pkg == null) { if (cName.pkg == null) {
return coreResourceLoader.newInstance(cname, expectedType, subPackages, params, args); return fallbackClassLoader.newInstance(cname, expectedType, subPackages, params, args);
} else { } else {
PackageLoader.Package.Version version = findPkgVersion(cName); PackageLoader.Package.Version version = findPackageVersion(cName, true);
return applyResourceLoaderAware(version, version.getLoader().newInstance(cName.className, expectedType, subPackages, params, args)); T obj = version.getLoader().newInstance(cName.className, expectedType, subPackages, params, args);
classNameVsPackageName.put(cName.original, cName.pkg);
return applyResourceLoaderAware(version, obj);
} }
} }
@ -112,10 +130,12 @@ public class PackageListeningClassLoader implements SolrClassLoader , PackageLis
public <T> Class<? extends T> findClass(String cname, Class<T> expectedType) { public <T> Class<? extends T> findClass(String cname, Class<T> expectedType) {
PluginInfo.ClassName cName = new PluginInfo.ClassName(cname); PluginInfo.ClassName cName = new PluginInfo.ClassName(cname);
if (cName.pkg == null) { if (cName.pkg == null) {
return coreResourceLoader.findClass(cname, expectedType); return fallbackClassLoader.findClass(cname, expectedType);
} else { } else {
PackageLoader.Package.Version version = findPkgVersion(cName); PackageLoader.Package.Version version = findPackageVersion(cName, true);
return version.getLoader().findClass(cName.className, expectedType); Class<? extends T> klas = version.getLoader().findClass(cName.className, expectedType);
classNameVsPackageName.put(cName.original, cName.pkg);
return klas;
} }
} }
@ -126,8 +146,10 @@ public class PackageListeningClassLoader implements SolrClassLoader , PackageLis
} }
@Override @Override
public PluginInfo pluginInfo() { public Map<String, PackageAPI.PkgVersion> packageDetails() {
return null; Map<String, PackageAPI.PkgVersion> result = new LinkedHashMap<>();
classNameVsPackageName.forEach((k, v) -> result.put(k, packageVersions.get(v)));
return result;
} }
@Override @Override
@ -146,6 +168,11 @@ public class PackageListeningClassLoader implements SolrClassLoader , PackageLis
//no need to update //no need to update
return; return;
} }
doReloadAction(ctx);
}
protected void doReloadAction(Ctx ctx) {
if(onReload == null) return;
ctx.runLater(null, onReload); ctx.runLater(null, onReload);
} }
} }

View File

@ -19,6 +19,8 @@ package org.apache.solr.pkg;
import java.io.IOException; import java.io.IOException;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.util.Collections;
import java.util.Map;
import org.apache.lucene.util.ResourceLoaderAware; import org.apache.lucene.util.ResourceLoaderAware;
import org.apache.solr.common.MapWriter; import org.apache.solr.common.MapWriter;
@ -57,8 +59,8 @@ public class PackagePluginHolder<T> extends PluginBag.PluginHolder<T> {
} }
@Override @Override
public PluginInfo pluginInfo() { public Map<String, PackageAPI.PkgVersion> packageDetails() {
return info; return Collections.singletonMap(info.cName.original, pkgVersion.getPkgVersion());
} }
@Override @Override
@ -75,6 +77,12 @@ public class PackagePluginHolder<T> extends PluginBag.PluginHolder<T> {
}); });
} }
public static <T> PluginBag.PluginHolder<T> createHolder(T inst, Class<T> type) {
SolrConfig.SolrPluginInfo plugin = SolrConfig.classVsSolrPluginInfo.get(type.getName());
PluginInfo info = new PluginInfo(plugin.tag, Collections.singletonMap("class", inst.getClass().getName()));
return new PluginBag.PluginHolder<T>(info,inst);
}
public static <T> PluginBag.PluginHolder<T> createHolder(PluginInfo info, SolrCore core, Class<T> type, String msg) { public static <T> PluginBag.PluginHolder<T> createHolder(PluginInfo info, SolrCore core, Class<T> type, String msg) {
if(info.cName.pkg == null) { if(info.cName.pkg == null) {
return new PluginBag.PluginHolder<T>(info, core.createInitInstance(info, type,msg, null)); return new PluginBag.PluginHolder<T>(info, core.createInitInstance(info, type,msg, null));
@ -108,7 +116,7 @@ public class PackagePluginHolder<T> extends PluginBag.PluginHolder<T> {
if (log.isInfoEnabled()) { if (log.isInfoEnabled()) {
log.info("loading plugin: {} -> {} using package {}:{}", log.info("loading plugin: {} -> {} using package {}:{}",
pluginInfo.type, pluginInfo.name, pkg.name(), newest.getVersion()); pluginInfo.type, pluginInfo.name, pkg.name(), newest.getVersion());
} }
initNewInstance(newest, core); initNewInstance(newest, core);
@ -119,7 +127,7 @@ public class PackagePluginHolder<T> extends PluginBag.PluginHolder<T> {
@SuppressWarnings({"unchecked"}) @SuppressWarnings({"unchecked"})
protected Object initNewInstance(PackageLoader.Package.Version newest, SolrCore core) { protected Object initNewInstance(PackageLoader.Package.Version newest, SolrCore core) {
Object instance = SolrCore.createInstance(pluginInfo.className, Object instance = SolrCore.createInstance(pluginInfo.className,
pluginMeta.clazz, pluginMeta.getCleanTag(), core, newest.getLoader()); pluginMeta.clazz, pluginMeta.getCleanTag(), core, newest.getLoader());
PluginBag.initInstance(instance, pluginInfo); PluginBag.initInstance(instance, pluginInfo);
handleAwareCallbacks(newest.getLoader(), instance, core); handleAwareCallbacks(newest.getLoader(), instance, core);
T old = inst; T old = inst;

View File

@ -23,11 +23,15 @@ import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Supplier;
import org.apache.solr.common.MapSerializable; import org.apache.solr.common.MapSerializable;
import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.DOMUtil; import org.apache.solr.common.util.DOMUtil;
import org.apache.solr.common.util.StrUtils; import org.apache.solr.common.util.StrUtils;
import org.apache.solr.core.PluginInfo;
import org.apache.solr.core.SolrConfig; import org.apache.solr.core.SolrConfig;
import org.apache.solr.core.SolrResourceLoader; import org.apache.solr.core.SolrResourceLoader;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -46,11 +50,17 @@ import static org.apache.solr.common.params.CommonParams.NAME;
*/ */
public class CacheConfig implements MapSerializable{ public class CacheConfig implements MapSerializable{
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private String nodeName; private String nodeName;
/**
* When this object is created, the core is not yet available . So, if the class is to be
* loaded from a package we should have a corresponding core
*
*/
@SuppressWarnings({"rawtypes"}) @SuppressWarnings({"rawtypes"})
private Class<? extends SolrCache> clazz; private Supplier<Class<? extends SolrCache>> clazz;
private Map<String,String> args; private Map<String,String> args;
private CacheRegenerator regenerator; private CacheRegenerator regenerator;
@ -64,9 +74,10 @@ public class CacheConfig implements MapSerializable{
@SuppressWarnings({"rawtypes"}) @SuppressWarnings({"rawtypes"})
public CacheConfig(Class<? extends SolrCache> clazz, Map<String,String> args, CacheRegenerator regenerator) { public CacheConfig(Class<? extends SolrCache> clazz, Map<String,String> args, CacheRegenerator regenerator) {
this.clazz = clazz; this.clazz = () -> clazz;
this.args = args; this.args = args;
this.regenerator = regenerator; this.regenerator = regenerator;
this.nodeName = args.get(NAME);
} }
public CacheRegenerator getRegenerator() { public CacheRegenerator getRegenerator() {
@ -85,7 +96,7 @@ public class CacheConfig implements MapSerializable{
Node node = nodes.item(i); Node node = nodes.item(i);
if ("true".equals(DOMUtil.getAttrOrDefault(node, "enabled", "true"))) { if ("true".equals(DOMUtil.getAttrOrDefault(node, "enabled", "true"))) {
CacheConfig config = getConfig(solrConfig, node.getNodeName(), CacheConfig config = getConfig(solrConfig, node.getNodeName(),
DOMUtil.toMap(node.getAttributes()), configPath); DOMUtil.toMap(node.getAttributes()), configPath);
result.put(config.args.get(NAME), config); result.put(config.args.get(NAME), config);
} }
} }
@ -133,20 +144,32 @@ public class CacheConfig implements MapSerializable{
SolrResourceLoader loader = solrConfig.getResourceLoader(); SolrResourceLoader loader = solrConfig.getResourceLoader();
config.cacheImpl = config.args.get("class"); config.cacheImpl = config.args.get("class");
if(config.cacheImpl == null) config.cacheImpl = "solr.CaffeineCache"; if (config.cacheImpl == null) config.cacheImpl = "solr.CaffeineCache";
config.clazz = new Supplier<>() {
@SuppressWarnings("rawtypes")
Class<SolrCache> loadedClass;
@Override
@SuppressWarnings("rawtypes")
public Class<? extends SolrCache> get() {
if (loadedClass != null) return loadedClass;
return loadedClass = (Class<SolrCache>) loader.findClass(
new PluginInfo("cache", Collections.singletonMap("class", config.cacheImpl)),
SolrCache.class, true);
}
};
config.regenImpl = config.args.get("regenerator"); config.regenImpl = config.args.get("regenerator");
config.clazz = loader.findClass(config.cacheImpl, SolrCache.class);
if (config.regenImpl != null) { if (config.regenImpl != null) {
config.regenerator = loader.newInstance(config.regenImpl, CacheRegenerator.class); config.regenerator = loader.newInstance(config.regenImpl, CacheRegenerator.class);
} }
return config; return config;
} }
@SuppressWarnings({"rawtypes"}) @SuppressWarnings({"rawtypes"})
public SolrCache newInstance() { public SolrCache newInstance() {
try { try {
SolrCache cache = clazz.getConstructor().newInstance(); SolrCache cache = clazz.get().getConstructor().newInstance();
persistence[0] = cache.init(args, persistence[0], regenerator); persistence[0] = cache.init(args, persistence[0], regenerator);
return cache; return cache;
} catch (Exception e) { } catch (Exception e) {
@ -158,11 +181,8 @@ public class CacheConfig implements MapSerializable{
} }
@Override @Override
@SuppressWarnings({"unchecked"})
public Map<String, Object> toMap(Map<String, Object> map) { public Map<String, Object> toMap(Map<String, Object> map) {
@SuppressWarnings({"rawtypes"}) return new HashMap<>(args);
Map result = Collections.unmodifiableMap(args);
return result;
} }
public String getNodeName() { public String getNodeName() {

View File

@ -20,12 +20,7 @@ import java.io.IOException;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.util.Vector; import java.util.Vector;
import org.apache.solr.core.DirectoryFactory; import org.apache.solr.core.*;
import org.apache.solr.core.HdfsDirectoryFactory;
import org.apache.solr.core.PluginInfo;
import org.apache.solr.core.SolrCore;
import org.apache.solr.core.SolrEventListener;
import org.apache.solr.core.SolrInfoBean;
import org.apache.solr.metrics.SolrMetricsContext; import org.apache.solr.metrics.SolrMetricsContext;
import org.apache.solr.schema.FieldType; import org.apache.solr.schema.FieldType;
import org.apache.solr.schema.SchemaField; import org.apache.solr.schema.SchemaField;
@ -108,7 +103,7 @@ public abstract class UpdateHandler implements SolrInfoBean {
public UpdateHandler(SolrCore core) { public UpdateHandler(SolrCore core) {
this(core, null); this(core, null);
} }
public UpdateHandler(SolrCore core, UpdateLog updateLog) { public UpdateHandler(SolrCore core, UpdateLog updateLog) {
this.core=core; this.core=core;
idField = core.getLatestSchema().getUniqueKeyField(); idField = core.getLatestSchema().getUniqueKeyField();
@ -123,8 +118,8 @@ public abstract class UpdateHandler implements SolrInfoBean {
if (dirFactory instanceof HdfsDirectoryFactory) { if (dirFactory instanceof HdfsDirectoryFactory) {
ulog = new HdfsUpdateLog(((HdfsDirectoryFactory)dirFactory).getConfDir()); ulog = new HdfsUpdateLog(((HdfsDirectoryFactory)dirFactory).getConfDir());
} else { } else {
String className = ulogPluginInfo.className == null ? UpdateLog.class.getName() : ulogPluginInfo.className; ulog = ulogPluginInfo.className == null ? new UpdateLog():
ulog = core.getResourceLoader().newInstance(className, UpdateLog.class); core.getResourceLoader().newInstance(ulogPluginInfo, UpdateLog.class, true);
} }
if (!core.isReloaded() && !dirFactory.isPersistent()) { if (!core.isReloaded() && !dirFactory.isPersistent()) {

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
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.
-->
<schema name="minimal" version="1.1">
<fieldType name="string" class="solr.StrField"/>
<fieldType name="int" class="${solr.tests.IntegerFieldType}" docValues="${solr.tests.numeric.dv}" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
<fieldType name="long" class="${solr.tests.LongFieldType}" docValues="${solr.tests.numeric.dv}" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
<dynamicField name="*" type="string" indexed="true" stored="true"/>
<!-- for versioning -->
<field name="_version_" type="long" indexed="true" stored="true"/>
<field name="_root_" type="string" indexed="true" stored="true" multiValued="false" required="false"/>
<field name="id" type="string" indexed="true" stored="true"/>
<dynamicField name="*_s" type="string" indexed="true" stored="true" />
<uniqueKey>id</uniqueKey>
<query>
<filterCache
size="0"
initialSize="0"
autowarmCount="0"/>
<queryResultCache
size="0"
initialSize="0"
autowarmCount="0"/>
<documentCache
size="0"
initialSize="0"
autowarmCount="0"/>
</query>
</schema>

View File

@ -0,0 +1,68 @@
<?xml version="1.0" ?>
<!--
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.
-->
<!-- Minimal solrconfig.xml with /select, /admin and /update only -->
<config>
<dataDir>${solr.data.dir:}</dataDir>
<directoryFactory name="DirectoryFactory"
class="${solr.directoryFactory:solr.NRTCachingDirectoryFactory}"/>
<schemaFactory class="ClassicIndexSchemaFactory"/>
<luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
<updateHandler class="solr.DirectUpdateHandler2">
<commitWithin>
<softCommit>${solr.commitwithin.softcommit:true}</softCommit>
</commitWithin>
<updateLog class="${solr.ulog:solr.UpdateLog}"></updateLog>
</updateHandler>
<requestHandler name="/select" class="solr.SearchHandler">
<lst name="defaults">
<str name="echoParams">explicit</str>
<str name="indent">true</str>
<str name="df">text</str>
</lst>
</requestHandler>
<query>
<filterCache
class = "mypkg:org.apache.solr.search.CaffeineCache"
size="512"
initialSize="512"
autowarmCount="0" />
<queryResultCache
size="512"
initialSize="512"
autowarmCount="0" />
<documentCache
size="512"
initialSize="512"
autowarmCount="0" />
</query>
<indexConfig>
<mergeScheduler class="${solr.mscheduler:org.apache.lucene.index.ConcurrentMergeScheduler}"/>
</indexConfig>
</config>

View File

@ -17,17 +17,6 @@
package org.apache.solr.pkg; package org.apache.solr.pkg;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
import org.apache.lucene.analysis.core.WhitespaceTokenizerFactory; import org.apache.lucene.analysis.core.WhitespaceTokenizerFactory;
import org.apache.lucene.analysis.pattern.PatternReplaceCharFilterFactory; import org.apache.lucene.analysis.pattern.PatternReplaceCharFilterFactory;
@ -40,11 +29,7 @@ import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.embedded.JettySolrRunner; import org.apache.solr.client.solrj.embedded.JettySolrRunner;
import org.apache.solr.client.solrj.impl.BaseHttpSolrClient; import org.apache.solr.client.solrj.impl.BaseHttpSolrClient;
import org.apache.solr.client.solrj.impl.HttpSolrClient; import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.request.CollectionAdminRequest; import org.apache.solr.client.solrj.request.*;
import org.apache.solr.client.solrj.request.GenericSolrRequest;
import org.apache.solr.client.solrj.request.RequestWriter;
import org.apache.solr.client.solrj.request.UpdateRequest;
import org.apache.solr.client.solrj.request.V2Request;
import org.apache.solr.client.solrj.request.beans.Package; import org.apache.solr.client.solrj.request.beans.Package;
import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.util.ClientUtils; import org.apache.solr.client.solrj.util.ClientUtils;
@ -75,13 +60,22 @@ import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import static org.apache.solr.common.cloud.ZkStateReader.SOLR_PKGS_PATH; import static org.apache.solr.common.cloud.ZkStateReader.SOLR_PKGS_PATH;
import static org.apache.solr.common.params.CommonParams.JAVABIN; import static org.apache.solr.common.params.CommonParams.JAVABIN;
import static org.apache.solr.common.params.CommonParams.WT; import static org.apache.solr.common.params.CommonParams.WT;
import static org.apache.solr.core.TestSolrConfigHandler.getFileContent; import static org.apache.solr.core.TestSolrConfigHandler.getFileContent;
import static org.apache.solr.filestore.TestDistribPackageStore.checkAllNodesForFile; import static org.apache.solr.filestore.TestDistribPackageStore.*;
import static org.apache.solr.filestore.TestDistribPackageStore.readFile;
import static org.apache.solr.filestore.TestDistribPackageStore.uploadKey;
@LogLevel("org.apache.solr.pkg.PackageLoader=DEBUG;org.apache.solr.pkg.PackageAPI=DEBUG") @LogLevel("org.apache.solr.pkg.PackageLoader=DEBUG;org.apache.solr.pkg.PackageAPI=DEBUG")
public class TestPackages extends SolrCloudTestCase { public class TestPackages extends SolrCloudTestCase {
@ -102,6 +96,69 @@ public class TestPackages extends SolrCloudTestCase {
@JsonProperty("class") @JsonProperty("class")
public String klass; public String klass;
} }
public void testCoreReloadingPlugin() throws Exception {
MiniSolrCloudCluster cluster =
configureCluster(4)
.withJettyConfig(jetty -> jetty.enableV2(true))
.addConfig("conf", configset("conf2"))
.configure();
try {
String FILE1 = "/mypkg/runtimelibs.jar";
String COLLECTION_NAME = "testCoreReloadingPluginColl";
byte[] derFile = readFile("cryptokeys/pub_key512.der");
uploadKey(derFile, PackageStoreAPI.KEYS_DIR+"/pub_key512.der", cluster);
postFileAndWait(cluster, "runtimecode/runtimelibs.jar.bin", FILE1,
"L3q/qIGs4NaF6JiO0ZkMUFa88j0OmYc+I6O7BOdNuMct/xoZ4h73aZHZGc0+nmI1f/U3bOlMPINlSOM6LK3JpQ==");
Package.AddVersion add = new Package.AddVersion();
add.version = "1.0";
add.pkg = "mypkg";
add.files = Arrays.asList(new String[]{FILE1});
V2Request req = new V2Request.Builder("/cluster/package")
.forceV2(true)
.withMethod(SolrRequest.METHOD.POST)
.withPayload(Collections.singletonMap("add", add))
.build();
req.process(cluster.getSolrClient());
TestDistribPackageStore.assertResponseValues(10,
() -> new V2Request.Builder("/cluster/package").
withMethod(SolrRequest.METHOD.GET)
.build().process(cluster.getSolrClient()),
Utils.makeMap(
":result:packages:mypkg[0]:version", "1.0",
":result:packages:mypkg[0]:files[0]", FILE1
));
CollectionAdminRequest
.createCollection(COLLECTION_NAME, "conf", 2, 2)
.process(cluster.getSolrClient());
cluster.waitForActiveCollection(COLLECTION_NAME, 2, 4);
verifyComponent(cluster.getSolrClient(), COLLECTION_NAME, "query", "filterCache", add.pkg, add.version);
add.version = "2.0";
req.process(cluster.getSolrClient());
TestDistribPackageStore.assertResponseValues(10,
() -> new V2Request.Builder("/cluster/package").
withMethod(SolrRequest.METHOD.GET)
.build().process(cluster.getSolrClient()),
Utils.makeMap(
":result:packages:mypkg[1]:version", "2.0",
":result:packages:mypkg[1]:files[0]", FILE1
));
new UpdateRequest().commit(cluster.getSolrClient(), COLLECTION_NAME);
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "query", "filterCache",
"mypkg", "2.0" );
} finally {
cluster.shutdown();
}
}
@Test @Test
@SuppressWarnings({"unchecked"}) @SuppressWarnings({"unchecked"})
public void testPluginLoading() throws Exception { public void testPluginLoading() throws Exception {