diff --git a/solr/core/src/java/org/apache/solr/core/PackageManager.java b/solr/core/src/java/org/apache/solr/core/PackageManager.java index ab963c091b9e..ecc6950d2577 100644 --- a/solr/core/src/java/org/apache/solr/core/PackageManager.java +++ b/solr/core/src/java/org/apache/solr/core/PackageManager.java @@ -67,6 +67,10 @@ public int getZNodeVersion(String pkg) { Package p = pkgs.get(pkg); return p == null ? -1 : p.lib.getZnodeVersion(); } + public RuntimeLib getLib(String name){ + Package p = pkgs.get(name); + return p == null? null: p.lib; + } static class Package implements MapWriter { final RuntimeLib lib; @@ -90,12 +94,8 @@ public String getName() { public boolean isModified(Map map) { - if ((Objects.equals(lib.getSha512(), (map).get(SHA512)) && - Objects.equals(lib.getSig(), (map).get(SHA512)))) { - return false; - } else { - return true; - } + return (!Objects.equals(lib.getSha512(), (map).get(SHA512)) || + !Objects.equals(lib.getSig(), (map).get(SHA512))); } } @@ -153,7 +153,7 @@ public boolean onChange(Map properties) { private boolean updatePackages(Map properties, int ver) { Map m = (Map) properties.getOrDefault(PACKAGE, Collections.emptyMap()); if (pkgs.isEmpty() && m.isEmpty()) return false; - boolean needsReload[] = new boolean[1]; + boolean[] needsReload = new boolean[1]; if (m.size() == pkgs.size()) { m.forEach((k, v) -> { if (v instanceof Map) { @@ -361,11 +361,8 @@ public void writeMap(EntryWriter ew) throws IOException { public boolean shouldReload(Map metaData, Map pkgs) { Package p = pkgs.get(pkg); - if (meta.equals(metaData) && p != null && p.lib.getZnodeVersion() <= zkversion) { - //the metadata is same and the package has not changed since we last loaded - return false; - } - return true; + //the metadata is same and the package has not changed since we last loaded + return !meta.equals(metaData) || p == null || p.lib.getZnodeVersion() > zkversion; } } } diff --git a/solr/core/src/java/org/apache/solr/core/PluginBag.java b/solr/core/src/java/org/apache/solr/core/PluginBag.java index ed7cd90d6a51..dd15a1a26c29 100644 --- a/solr/core/src/java/org/apache/solr/core/PluginBag.java +++ b/solr/core/src/java/org/apache/solr/core/PluginBag.java @@ -33,6 +33,7 @@ import org.apache.solr.api.Api; import org.apache.solr.api.ApiBag; import org.apache.solr.api.ApiSupport; +import org.apache.solr.common.MapWriter; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.util.StrUtils; @@ -121,7 +122,7 @@ private static T createInitInstance(PluginInfo pluginInfo, SolrConfig.SolrPl boolean isRuntimeLib) { T localInst = null; try { - localInst = (T) core.createInstance(pluginInfo.className, pluginMeta.clazz, pluginMeta.getCleanTag(), core, resourceLoader); + localInst = (T) SolrCore.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()), @@ -359,7 +360,7 @@ public PluginHolder createPlugin(PluginInfo info) { log.debug("{} : '{}' created with startup=lazy ", meta.getCleanTag(), info.name); return new LazyPluginHolder(meta, info, core, core.getResourceLoader(), false); } else { - T inst = core.createInstance(info.className, (Class) meta.clazz, meta.getCleanTag(), null, core.getResourceLoader()); + T inst = SolrCore.createInstance(info.className, (Class) meta.clazz, meta.getCleanTag(), null, core.getResourceLoader()); initInstance(inst, info); return new PluginHolder<>(info, inst); } @@ -490,15 +491,34 @@ public class PackagePluginHolder extends PluginHolder { private final SolrConfig.SolrPluginInfo pluginMeta; private final PackageManager packageManager; private final String pkg; - private int znodeVersion =-1; + private RuntimeLib runtimeLib; public PackagePluginHolder(PluginInfo info, SolrCore core, SolrConfig.SolrPluginInfo pluginMeta) { super(info); this.core = core; this.pluginMeta = pluginMeta; this.pkg = info.attributes.get(CommonParams.PACKAGE); - this.core.addPackageListener(pkg, (lib) -> { - if(lib.getZnodeVersion() > znodeVersion) reload(); + this.core.addPackageListener(new SolrCore.PkgListener() { + @Override + public String packageName() { + return pkg; + } + + @Override + public PluginInfo pluginInfo() { + return info; + } + + @Override + public MapWriter lib() { + return runtimeLib; + } + + @Override + public void changed(RuntimeLib lib) { + int myVersion = runtimeLib == null? -1 : runtimeLib.znodeVersion; + if(lib.getZnodeVersion() > myVersion) reload(); + } }); this.packageManager = core.getCoreContainer().getPackageManager(); reload(); @@ -509,7 +529,8 @@ private void reload() { if(inst == null) log.info("reloading plugin {} ", pluginInfo.name); inst = createInitInstance(pluginInfo, pluginMeta, core, packageManager.getResourceLoader(this.pkg), true); - znodeVersion = packageManager.getZNodeVersion(pkg); + this.runtimeLib = packageManager.getLib(pkg); + } diff --git a/solr/core/src/java/org/apache/solr/core/PluginInfo.java b/solr/core/src/java/org/apache/solr/core/PluginInfo.java index 4ab40deff151..e25bd92fed2a 100644 --- a/solr/core/src/java/org/apache/solr/core/PluginInfo.java +++ b/solr/core/src/java/org/apache/solr/core/PluginInfo.java @@ -51,13 +51,15 @@ public class PluginInfo implements MapSerializable { public final List children; private boolean isFromSolrConfig; + public List pathInConfig; + public PluginInfo(String type, Map attrs, NamedList initArgs, List children) { this.type = type; this.name = attrs.get(NAME); this.className = attrs.get(CLASS_NAME); this.initArgs = initArgs; attributes = unmodifiableMap(attrs); - this.children = children == null ? Collections.emptyList() : unmodifiableList(children); + this.children = children == null ? Collections.emptyList() : unmodifiableList(children); isFromSolrConfig = false; } @@ -97,7 +99,7 @@ public PluginInfo(String type, Map map) { this.name = (String) m.get(NAME); this.className = (String) m.get(CLASS_NAME); attributes = unmodifiableMap(m); - this.children = Collections.emptyList(); + this.children = Collections.emptyList(); isFromSolrConfig = true; } @@ -112,7 +114,7 @@ private List loadSubPlugins(Node node) { PluginInfo pluginInfo = new PluginInfo(nd, null, false, false); if (pluginInfo.isEnabled()) children.add(pluginInfo); } - return children.isEmpty() ? Collections.emptyList() : unmodifiableList(children); + return children.isEmpty() ? Collections.emptyList() : unmodifiableList(children); } @Override @@ -178,7 +180,7 @@ public List getChildren(String type) { return result; } - public static final PluginInfo EMPTY_INFO = new PluginInfo("", Collections.emptyMap(), new NamedList(), Collections.emptyList()); + public static final PluginInfo EMPTY_INFO = new PluginInfo("", Collections.emptyMap(), new NamedList(), Collections.emptyList()); private static final HashSet NL_TAGS = new HashSet<> (asList("lst", "arr", @@ -199,6 +201,7 @@ public PluginInfo copy() { PluginInfo result = new PluginInfo(type, attributes, initArgs != null ? initArgs.clone() : null, children); result.isFromSolrConfig = isFromSolrConfig; + result.pathInConfig = pathInConfig; return result; } diff --git a/solr/core/src/java/org/apache/solr/core/SolrConfig.java b/solr/core/src/java/org/apache/solr/core/SolrConfig.java index c7a45fb60f24..914a102ad6c3 100644 --- a/solr/core/src/java/org/apache/solr/core/SolrConfig.java +++ b/solr/core/src/java/org/apache/solr/core/SolrConfig.java @@ -29,6 +29,7 @@ import java.nio.file.Paths; import java.text.ParseException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; @@ -55,6 +56,7 @@ import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.util.IOUtils; +import org.apache.solr.common.util.StrUtils; import org.apache.solr.handler.component.SearchComponent; import org.apache.solr.request.SolrRequestHandler; import org.apache.solr.response.QueryResponseWriter; @@ -271,7 +273,7 @@ public SolrConfig(SolrResourceLoader loader, String name, InputSource is) args.put("initialSize", "10"); args.put("showItems", "-1"); args.put("class", FastLRUCache.class.getName()); - conf = new CacheConfig(args); + conf = new CacheConfig(args,"query/fieldValueCache"); } fieldValueCacheConfig = conf; useColdSearcher = getBool("query/useColdSearcher", false); @@ -298,7 +300,7 @@ public SolrConfig(SolrResourceLoader loader, String name, InputSource is) List caches = getPluginInfos(SolrCache.class.getName()); if (!caches.isEmpty()) { for (PluginInfo c : caches) { - userCacheConfigs.put(c.name, new CacheConfig(c.attributes)); + userCacheConfigs.put(c.name, new CacheConfig(c.attributes, StrUtils.join(c.pathInConfig, '/'))); } } this.userCacheConfigs = Collections.unmodifiableMap(userCacheConfigs); @@ -372,17 +374,17 @@ public static final Version parseLuceneVersionString(final String matchVersion) .add(new SolrPluginInfo(TransformerFactory.class, "transformer", REQUIRE_NAME, REQUIRE_CLASS, MULTI_OK)) .add(new SolrPluginInfo(SearchComponent.class, "searchComponent", REQUIRE_NAME, REQUIRE_CLASS, MULTI_OK)) .add(new SolrPluginInfo(UpdateRequestProcessorFactory.class, "updateProcessor", REQUIRE_NAME, REQUIRE_CLASS, MULTI_OK)) - .add(new SolrPluginInfo(SolrCache.class, "cache", REQUIRE_NAME, REQUIRE_CLASS, MULTI_OK)) - // TODO: WTF is up with queryConverter??? - // it apparently *only* works as a singleton? - SOLR-4304 - // and even then -- only if there is a single SpellCheckComponent - // because of queryConverter.setIndexAnalyzer + .add(new SolrPluginInfo(SolrCache.class, SolrCache.TYPE, REQUIRE_NAME, REQUIRE_CLASS, MULTI_OK)) + // TODO: WTF is up with queryConverter??? + // it apparently *only* works as a singleton? - SOLR-4304 + // and even then -- only if there is a single SpellCheckComponent + // because of queryConverter.setIndexAnalyzer .add(new SolrPluginInfo(QueryConverter.class, "queryConverter", REQUIRE_NAME, REQUIRE_CLASS)) .add(new SolrPluginInfo(RuntimeLib.class, RuntimeLib.TYPE, REQUIRE_NAME, MULTI_OK)) - // this is hackish, since it picks up all SolrEventListeners, - // regardless of when/how/why they are used (or even if they are - // declared outside of the appropriate context) but there's no nice - // way around that in the PluginInfo framework + // this is hackish, since it picks up all SolrEventListeners, + // regardless of when/how/why they are used (or even if they are + // declared outside of the appropriate context) but there's no nice + // way around that in the PluginInfo framework .add(new SolrPluginInfo(InitParams.class, InitParams.TYPE, MULTI_OK, REQUIRE_NAME_IN_OVERLAY)) .add(new SolrPluginInfo(SolrEventListener.class, "//listener", REQUIRE_CLASS, MULTI_OK, REQUIRE_NAME_IN_OVERLAY)) @@ -532,6 +534,9 @@ public List readPluginInfos(String tag, boolean requireName, boolean NodeList nodes = (NodeList) evaluate(tag, XPathConstants.NODESET); for (int i = 0; i < nodes.getLength(); i++) { PluginInfo pluginInfo = new PluginInfo(nodes.item(i), "[solrconfig.xml] " + tag, requireName, requireClass); + if (requireName) { + pluginInfo.pathInConfig = Arrays.asList(tag, pluginInfo.name); + } if (pluginInfo.isEnabled()) result.add(pluginInfo); } return result; @@ -605,7 +610,7 @@ public Map toMap(Map map) { "cacheControl", cacheControlHeader); } - public static enum LastModFrom { + public enum LastModFrom { OPENTIME, DIRLASTMOD, BOGUS; /** @@ -757,20 +762,24 @@ public List getPluginInfos(String type) { Map infos = overlay.getNamedPlugins(info.getCleanTag()); if (!infos.isEmpty()) { LinkedHashMap map = new LinkedHashMap<>(); - if (result != null) for (PluginInfo pluginInfo : result) { - //just create a UUID for the time being so that map key is not null - String name = pluginInfo.name == null ? - UUID.randomUUID().toString().toLowerCase(Locale.ROOT) : - pluginInfo.name; - map.put(name, pluginInfo); + if (result != null) { + for (PluginInfo pluginInfo : result) { + //just create a UUID for the time being so that map key is not null + String name = pluginInfo.name == null ? + UUID.randomUUID().toString().toLowerCase(Locale.ROOT) : + pluginInfo.name; + map.put(name, pluginInfo); + } } for (Map.Entry e : infos.entrySet()) { - map.put(e.getKey(), new PluginInfo(info.getCleanTag(), e.getValue())); + PluginInfo value = new PluginInfo(info.getCleanTag(), e.getValue()); + value.pathInConfig = Arrays.asList(info.getCleanTag(),e.getKey()); + map.put(e.getKey(), value); } result = new ArrayList<>(map.values()); } } - return result == null ? Collections.emptyList() : result; + return result == null ? Collections.emptyList() : result; } public PluginInfo getPluginInfo(String type) { diff --git a/solr/core/src/java/org/apache/solr/core/SolrCore.java b/solr/core/src/java/org/apache/solr/core/SolrCore.java index befdd458498b..3729ff13bfc2 100644 --- a/solr/core/src/java/org/apache/solr/core/SolrCore.java +++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java @@ -56,7 +56,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; -import java.util.function.Consumer; import com.codahale.metrics.Counter; import com.codahale.metrics.MetricRegistry; @@ -80,6 +79,7 @@ import org.apache.solr.cloud.CloudDescriptor; import org.apache.solr.cloud.RecoveryStrategy; import org.apache.solr.cloud.ZkSolrResourceLoader; +import org.apache.solr.common.MapWriter; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.cloud.ClusterState; @@ -95,7 +95,6 @@ import org.apache.solr.common.util.IOUtils; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.ObjectReleaseTracker; -import org.apache.solr.common.util.Pair; import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.common.util.Utils; import org.apache.solr.core.DirectoryFactory.DirContext; @@ -240,13 +239,17 @@ 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>> packageListeners = new ArrayList<>(); + private List packageListeners = new ArrayList<>(); public Set getMetricNames() { return metricNames; } + public List getPackageListeners(){ + return Collections.unmodifiableList(packageListeners); + } + public Date getStartTimeStamp() { return startTime; } @@ -358,12 +361,23 @@ public String getIndexDir() { } void packageUpdated(RuntimeLib lib) { - for (Pair> pair : packageListeners) { - if(lib.equals(pair.first())) pair.second().accept(lib); + for (PkgListener listener : packageListeners) { + if(lib.getName().equals(listener.packageName())) listener.changed(lib); } } - public void addPackageListener(String pkg, Consumer r){ - packageListeners.add(new Pair<>(pkg, r)); + public void addPackageListener(PkgListener listener){ + packageListeners.add(listener); + } + + public interface PkgListener { + + String packageName(); + + PluginInfo pluginInfo(); + + void changed(RuntimeLib lib); + + MapWriter lib(); } @@ -851,7 +865,7 @@ private UpdateHandler createReloadedUpdateHandler(String className, String msg, for (Constructor con : cons) { Class[] types = con.getParameterTypes(); if (types.length == 2 && types[0] == SolrCore.class && types[1] == UpdateHandler.class) { - return UpdateHandler.class.cast(con.newInstance(this, updateHandler)); + return (UpdateHandler) con.newInstance(this, updateHandler); } } throw new SolrException(ErrorCode.SERVER_ERROR, "Error Instantiating " + msg + ", " + className + " could not find proper constructor for " + UpdateHandler.class.getName()); @@ -2423,7 +2437,6 @@ public RefCounted getSearcher(boolean forceNew, boolean retur if (!success) { newSearcherOtherErrorsCounter.inc(); - ; synchronized (searcherLock) { onDeckSearchers--; @@ -3126,8 +3139,7 @@ private static boolean checkStale(SolrZkClient zkClient, String zkPath, int curr try { Stat stat = zkClient.exists(zkPath, null, true); if (stat == null) { - if (currentVersion > -1) return true; - return false; + return currentVersion > -1; } if (stat.getVersion() > currentVersion) { log.debug("{} is stale will need an update from {} to {}", zkPath, currentVersion, stat.getVersion()); diff --git a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java index 2d4487f29e12..789526e476ce 100644 --- a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java @@ -154,7 +154,7 @@ public static boolean getImmutable(SolrCore core) { NamedList configSetProperties = core.getConfigSetProperties(); if (configSetProperties == null) return false; Object immutable = configSetProperties.get(IMMUTABLE_CONFIGSET_ARG); - return immutable != null ? Boolean.parseBoolean(immutable.toString()) : false; + return immutable != null && Boolean.parseBoolean(immutable.toString()); } public static String validateName(String s) { @@ -234,6 +234,10 @@ public static void execInparallel(List concurrentT public SolrRequestHandler getSubHandler(String path) { if (subPaths.contains(path)) return this; if (path.startsWith("/params/")) return this; + List p = StrUtils.splitSmart(path, '/', true); + if (p.size() > 1) { + if (subPaths.contains("/" + p.get(0))) return this; + } return null; } @@ -506,25 +510,54 @@ private void handleGET() { private Map getConfigDetails(String componentType, SolrQueryRequest req) { String componentName = componentType == null ? null : req.getParams().get("componentName"); + if(componentName == null && parts.size() > 2){ + componentName = parts.get(2); + if(SolrRequestHandler.TYPE.equals(componentType)){ + componentName = "/"+componentName; + } + } + boolean showParams = req.getParams().getBool("expandParams", false); Map map = this.req.getCore().getSolrConfig().toMap(new LinkedHashMap<>()); - if (componentType != null && !SolrRequestHandler.TYPE.equals(componentType)) return map; - Map reqHandlers = (Map) map.get(SolrRequestHandler.TYPE); - if (reqHandlers == null) map.put(SolrRequestHandler.TYPE, reqHandlers = new LinkedHashMap<>()); - List plugins = this.req.getCore().getImplicitHandlers(); - for (PluginInfo plugin : plugins) { - if (SolrRequestHandler.TYPE.equals(plugin.type)) { - if (!reqHandlers.containsKey(plugin.name)) { - reqHandlers.put(plugin.name, plugin); + if (SolrRequestHandler.TYPE.equals(componentType) || componentType == null) { + Map reqHandlers = (Map) map.get(SolrRequestHandler.TYPE); + if (reqHandlers == null) map.put(SolrRequestHandler.TYPE, reqHandlers = new LinkedHashMap<>()); + List plugins = this.req.getCore().getImplicitHandlers(); + for (PluginInfo plugin : plugins) { + if (SolrRequestHandler.TYPE.equals(plugin.type)) { + if (!reqHandlers.containsKey(plugin.name)) { + reqHandlers.put(plugin.name, plugin); + } + } + } + if (showParams) { + for (Object o : reqHandlers.entrySet()) { + Map.Entry e = (Map.Entry) o; + if (componentName == null || e.getKey().equals(componentName)) { + Map m = expandUseParams(req, e.getValue()); + e.setValue(m); + } } } + } - if (!showParams) return map; - for (Object o : reqHandlers.entrySet()) { - Map.Entry e = (Map.Entry) o; - if (componentName == null || e.getKey().equals(componentName)) { - Map m = expandUseParams(req, e.getValue()); - e.setValue(m); + + if (req.getParams().getBool("meta", false)) { + for (SolrCore.PkgListener pkgListener : req.getCore().getPackageListeners()) { + PluginInfo meta = pkgListener.pluginInfo(); + if (meta.pathInConfig != null) { + Object obj = Utils.getObjectByPath(map, false, meta.pathInConfig); + if (obj instanceof Map) { + Map m = (Map) obj; + m.put("_packageinfo_", pkgListener.lib()); + } else if(obj instanceof MapWriter){ + MapWriter mw = (MapWriter) obj; + Utils.setObjectByPath(map, meta.pathInConfig, (MapWriter) ew -> { + mw.writeMap(ew); + ew.put("_packageinfo_", pkgListener.lib()); + }, false); + } + } } } diff --git a/solr/core/src/java/org/apache/solr/metrics/SolrMetrics.java b/solr/core/src/java/org/apache/solr/metrics/SolrMetrics.java index 28aea4e46757..d73b04b33c98 100644 --- a/solr/core/src/java/org/apache/solr/metrics/SolrMetrics.java +++ b/solr/core/src/java/org/apache/solr/metrics/SolrMetrics.java @@ -63,7 +63,7 @@ public Meter meter(SolrInfoBean info, String metricName, String... metricpath) { private String createName(String metricName, String... metricpath) { ArrayList l = new ArrayList<>(); - if(metricpath != null ) { + if (metricpath != null) { Collections.addAll(l, metricpath); } l.add(scope); @@ -76,7 +76,8 @@ public Counter counter(SolrInfoBean info, String metricName, String... metricpat } public void gauge(SolrInfoBean info, Gauge gauge, boolean force, String metricName, String... metricpath) { - metricManager.registerGauge(info, getRegistry(), new SolrMetricManager.GaugeWrapper<>(gauge, tag), force, createName(metricName, metricpath)); + String name = metricpath == null || metricpath.length == 0 ? metricName : createName(metricName, metricpath); + metricManager.registerGauge(info, getRegistry(), new SolrMetricManager.GaugeWrapper<>(gauge, tag), force, name); } public Timer timer(SolrInfoBean info, String metricName, String... metricpath) { diff --git a/solr/core/src/java/org/apache/solr/search/CacheConfig.java b/solr/core/src/java/org/apache/solr/search/CacheConfig.java index cc92f0dce393..753762a07c17 100644 --- a/solr/core/src/java/org/apache/solr/search/CacheConfig.java +++ b/solr/core/src/java/org/apache/solr/search/CacheConfig.java @@ -32,6 +32,8 @@ import org.apache.solr.common.util.Utils; import org.apache.solr.core.ConfigOverlay; import org.apache.solr.core.MemClassLoader; +import org.apache.solr.core.PluginInfo; +import org.apache.solr.core.RuntimeLib; import org.apache.solr.core.SolrConfig; import org.apache.solr.core.SolrCore; import org.apache.solr.util.DOMUtil; @@ -41,18 +43,19 @@ import static org.apache.solr.common.params.CommonParams.NAME; public class CacheConfig implements MapWriter { - final Map args; + final PluginInfo args; private CacheRegenerator defRegen; private final String name; private String cacheImpl, regenImpl; Object[] persistence = new Object[1]; - public CacheConfig(Map args) { - this.args = copyValsAsString(args); + public CacheConfig(Map args, String path) { + this.args = new PluginInfo(SolrCache.TYPE, (Map) copyValsAsString(args)); this.name = args.get(NAME); this.cacheImpl = args.getOrDefault("class", "solr.LRUCache"); this.regenImpl = args.get("regenerator"); + this.args.pathInConfig = StrUtils.splitSmart(path, '/', true); } static Map copyValsAsString(Map m) { @@ -70,11 +73,11 @@ public static CacheConfig getConfig(SolrConfig solrConfig, String xpath) { String name = pieces.get(pieces.size() - 1); m = Utils.getDeepCopy(m, 2); m.put(NAME, name); - return new CacheConfig(m); + return new CacheConfig(m, xpath); } else { Map attrs = DOMUtil.toMap(node.getAttributes()); attrs.put(NAME, node.getNodeName()); - return new CacheConfig(applyOverlay(xpath, solrConfig.getOverlay(), attrs)); + return new CacheConfig(applyOverlay(xpath, solrConfig.getOverlay(), attrs), xpath); } @@ -99,7 +102,7 @@ public static Map getConfigs(SolrConfig solrConfig, String Map result = new HashMap<>(nodes.getLength()); for (int i = 0; i < nodes.getLength(); i++) { Map args = DOMUtil.toMap(nodes.item(i).getAttributes()); - result.put(args.get(NAME), new CacheConfig(args)); + result.put(args.get(NAME), new CacheConfig(args, configPath+"/"+args.get(NAME))); } return result; } @@ -117,15 +120,15 @@ static class CacheInfo { final CacheConfig cfg; SolrCore core; SolrCache cache = null; - int znodeVersion = -1; String pkg; + RuntimeLib runtimeLib; CacheRegenerator regen = null; CacheInfo(CacheConfig cfg, SolrCore core) { this.core = core; this.cfg = cfg; - pkg = cfg.args.get(CommonParams.PACKAGE); + pkg = cfg.args.attributes.get(CommonParams.PACKAGE); ResourceLoader loader = pkg == null ? core.getResourceLoader() : core.getCoreContainer().getPackageManager().getResourceLoader(pkg); @@ -139,10 +142,10 @@ static class CacheInfo { throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error loading cache " + cfg.jsonStr(), e); } if (regen == null && cfg.defRegen != null) regen = cfg.defRegen; - cfg.persistence[0] = cache.init(cfg.args, cfg.persistence[0], regen); - if (loader instanceof MemClassLoader) { + cfg.persistence[0] = cache.init(cfg.args.attributes, cfg.persistence[0], regen); + if (pkg!=null && loader instanceof MemClassLoader) { MemClassLoader memClassLoader = (MemClassLoader) loader; - znodeVersion = memClassLoader.getZnodeVersion(); + runtimeLib = core.getCoreContainer().getPackageManager().getLib(pkg); } } @@ -155,6 +158,6 @@ public void setDefaultRegenerator(CacheRegenerator regen) { @Override public void writeMap(EntryWriter ew) throws IOException { - args.forEach(ew.getBiConsumer()); + args.attributes.forEach(ew.getBiConsumer()); } } diff --git a/solr/core/src/java/org/apache/solr/search/FastLRUCache.java b/solr/core/src/java/org/apache/solr/search/FastLRUCache.java index 5487987b057a..5a5ea01b1970 100644 --- a/solr/core/src/java/org/apache/solr/search/FastLRUCache.java +++ b/solr/core/src/java/org/apache/solr/search/FastLRUCache.java @@ -25,10 +25,12 @@ import java.util.concurrent.TimeUnit; import com.codahale.metrics.MetricRegistry; +import com.google.common.collect.ImmutableList; import org.apache.lucene.util.Accountable; import org.apache.lucene.util.RamUsageEstimator; import org.apache.solr.common.SolrException; import org.apache.solr.metrics.MetricsMap; +import org.apache.solr.metrics.SolrMetricManager; import org.apache.solr.metrics.SolrMetrics; import org.apache.solr.util.ConcurrentLRUCache; import org.slf4j.Logger; @@ -102,7 +104,7 @@ public Object init(Map args, Object persistence, CacheRegenerator regenerator) { str = (String) args.get(INITIAL_SIZE_PARAM); initialSize = str == null ? maxSize : Integer.parseInt(str); str = (String) args.get(CLEANUP_THREAD_PARAM); - cleanupThread = str == null ? false : Boolean.parseBoolean(str); + cleanupThread = str != null && Boolean.parseBoolean(str); str = (String) args.get(SHOW_ITEMS_PARAM); showItems = str == null ? 0 : Integer.parseInt(str); @@ -223,7 +225,7 @@ public void warm(SolrIndexSearcher searcher, SolrCache old) { @Override public void close() { - if (metricsInfo != null) metricsInfo.unregister(); + if (solrMetrics != null) solrMetrics.unregister(); // add the stats to the cumulative stats object (the first in the statsList) statsList.get(0).add(cache.getStats()); statsList.remove(cache.getStats()); @@ -247,16 +249,16 @@ public Set getMetricNames() { } - SolrMetrics metricsInfo; + SolrMetrics solrMetrics; @Override public SolrMetrics getMetrics() { - return metricsInfo; + return solrMetrics; } @Override public void initializeMetrics(SolrMetrics info) { - metricsInfo = info.getChildInfo(this); + solrMetrics = info.getChildInfo(this); cacheMap = new MetricsMap((detailed, map) -> { if (cache != null) { ConcurrentLRUCache.Stats stats = cache.getStats(); @@ -309,7 +311,8 @@ public void initializeMetrics(SolrMetrics info) { } } }); - metricsInfo.metricManager.registerGauge(this, metricsInfo.registry, cacheMap, metricsInfo.tag, true, metricsInfo.scope, getCategory().toString()); + String metricName = SolrMetricManager.makeName(ImmutableList.of(getCategory().toString()), solrMetrics.scope); + solrMetrics.gauge(this, cacheMap,true, metricName); } @@ -320,7 +323,7 @@ MetricsMap getMetricsMap() { @Override public MetricRegistry getMetricRegistry() { - return metricsInfo ==null ?null:metricsInfo.getRegistry(); + return solrMetrics == null ? null : solrMetrics.getRegistry(); } @Override diff --git a/solr/core/src/java/org/apache/solr/search/LFUCache.java b/solr/core/src/java/org/apache/solr/search/LFUCache.java index 0d69e4fbd1bb..78e7cb8c0be1 100644 --- a/solr/core/src/java/org/apache/solr/search/LFUCache.java +++ b/solr/core/src/java/org/apache/solr/search/LFUCache.java @@ -25,10 +25,12 @@ import java.util.concurrent.TimeUnit; import com.codahale.metrics.MetricRegistry; +import com.google.common.collect.ImmutableList; import org.apache.lucene.util.Accountable; import org.apache.lucene.util.RamUsageEstimator; import org.apache.solr.common.SolrException; import org.apache.solr.metrics.MetricsMap; +import org.apache.solr.metrics.SolrMetricManager; import org.apache.solr.metrics.SolrMetrics; import org.apache.solr.util.ConcurrentLFUCache; import org.slf4j.Logger; @@ -114,14 +116,14 @@ public Object init(Map args, Object persistence, CacheRegenerator regenerator) { str = (String) args.get(AUTOWARM_COUNT_PARAM); autowarmCount = str == null ? 0 : Integer.parseInt(str); str = (String) args.get(CLEANUP_THREAD_PARAM); - cleanupThread = str == null ? false : Boolean.parseBoolean(str); + cleanupThread = str != null && Boolean.parseBoolean(str); str = (String) args.get(SHOW_ITEMS_PARAM); showItems = str == null ? 0 : Integer.parseInt(str); // Don't make this "efficient" by removing the test, default is true and omitting the param will make it false. str = (String) args.get(TIME_DECAY_PARAM); - timeDecay = (str == null) ? true : Boolean.parseBoolean(str); + timeDecay = (str == null) || Boolean.parseBoolean(str); description = generateDescription(); @@ -145,7 +147,7 @@ public Object init(Map args, Object persistence, CacheRegenerator regenerator) { private String generateDescription() { String descr = "Concurrent LFU Cache(maxSize=" + maxSize + ", initialSize=" + initialSize + ", minSize=" + minSizeLimit + ", acceptableSize=" + acceptableSize + ", cleanupThread=" + cleanupThread + - ", timeDecay=" + Boolean.toString(timeDecay); + ", timeDecay=" + timeDecay; if (autowarmCount > 0) { descr += ", autowarmCount=" + autowarmCount + ", regenerator=" + regenerator; } @@ -324,7 +326,8 @@ public void initializeMetrics(SolrMetrics info) { } }); - solrMetrics.metricManager.registerGauge(this, solrMetrics.registry, cacheMap, solrMetrics.getTag(), true, solrMetrics.scope, getCategory().toString()); + String metricName = SolrMetricManager.makeName(ImmutableList.of(getCategory().toString()), solrMetrics.scope); + solrMetrics.gauge(this, cacheMap, true, metricName); } // for unit tests only diff --git a/solr/core/src/java/org/apache/solr/search/LRUCache.java b/solr/core/src/java/org/apache/solr/search/LRUCache.java index e26563025b3c..e9e378987e77 100644 --- a/solr/core/src/java/org/apache/solr/search/LRUCache.java +++ b/solr/core/src/java/org/apache/solr/search/LRUCache.java @@ -27,11 +27,13 @@ import java.util.concurrent.atomic.LongAdder; import com.codahale.metrics.MetricRegistry; +import com.google.common.collect.ImmutableList; import org.apache.lucene.util.Accountable; import org.apache.lucene.util.Accountables; import org.apache.lucene.util.RamUsageEstimator; import org.apache.solr.common.SolrException; import org.apache.solr.metrics.MetricsMap; +import org.apache.solr.metrics.SolrMetricManager; import org.apache.solr.metrics.SolrMetrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -335,7 +337,8 @@ public void initializeMetrics(SolrMetrics m) { res.put("cumulative_evictions", stats.evictions.longValue()); res.put("cumulative_evictionsRamUsage", stats.evictionsRamUsage.longValue()); }); - solrMetrics.metricManager.registerGauge(this, solrMetrics.registry, cacheMap, solrMetrics.tag, true, solrMetrics.scope, getCategory().toString()); + String metricName = SolrMetricManager.makeName(ImmutableList.of(getCategory().toString()), solrMetrics.scope); + solrMetrics.gauge(this, cacheMap, true, metricName); } // for unit tests only diff --git a/solr/core/src/java/org/apache/solr/search/SolrCache.java b/solr/core/src/java/org/apache/solr/search/SolrCache.java index b96cb133a747..b4817f5c36be 100644 --- a/solr/core/src/java/org/apache/solr/search/SolrCache.java +++ b/solr/core/src/java/org/apache/solr/search/SolrCache.java @@ -16,16 +16,17 @@ */ package org.apache.solr.search; +import java.util.Map; + import org.apache.solr.core.SolrInfoBean; import org.apache.solr.metrics.SolrMetricProducer; -import java.util.Map; - /** * Primary API for dealing with Solr's internal caches. */ public interface SolrCache extends SolrInfoBean, SolrMetricProducer { + String TYPE = "cache"; String HIT_RATIO_PARAM = "hitratio"; String HITS_PARAM = "hits"; @@ -59,7 +60,7 @@ public interface SolrCache extends SolrInfoBean, SolrMetricProducer { * regenerate an item in the new cache from an entry in the old cache. * */ - public Object init(Map args, Object persistence, CacheRegenerator regenerator); + Object init(Map args, Object persistence, CacheRegenerator regenerator); // I don't think we need a factory for faster creation given that these // will be associated with slow-to-create SolrIndexSearchers. // change to NamedList when other plugins do? @@ -75,29 +76,29 @@ public interface SolrCache extends SolrInfoBean, SolrMetricProducer { * * :TODO: verify this. */ - public String name(); + String name(); // Should SolrCache just extend the java.util.Map interface? // Following the conventions of the java.util.Map interface in any case. /** :TODO: copy from Map */ - public int size(); + int size(); /** :TODO: copy from Map */ - public V put(K key, V value); + V put(K key, V value); /** :TODO: copy from Map */ - public V get(K key); + V get(K key); /** :TODO: copy from Map */ - public void clear(); + void clear(); /** * Enumeration of possible States for cache instances. * :TODO: only state that seems to ever be set is LIVE ? */ - public enum State { + enum State { /** :TODO */ CREATED, /** :TODO */ @@ -114,14 +115,14 @@ public enum State { * The cache user (SolrIndexSearcher) will take care of switching * cache states. */ - public void setState(State state); + void setState(State state); /** * Returns the last State set on this instance * * @see #setState */ - public State getState(); + State getState(); /** @@ -134,7 +135,7 @@ public enum State { /** Frees any non-memory resources */ - public void close(); + void close(); /** Returns maximum size limit (number of items) if set and supported, -1 otherwise. */ int getMaxSize(); diff --git a/solr/core/src/java/org/apache/solr/search/SolrCacheHolder.java b/solr/core/src/java/org/apache/solr/search/SolrCacheHolder.java index 266b743d72fb..9a83a984eb9d 100644 --- a/solr/core/src/java/org/apache/solr/search/SolrCacheHolder.java +++ b/solr/core/src/java/org/apache/solr/search/SolrCacheHolder.java @@ -22,7 +22,10 @@ import java.util.Set; import com.codahale.metrics.MetricRegistry; +import org.apache.solr.common.MapWriter; +import org.apache.solr.core.PluginInfo; import org.apache.solr.core.RuntimeLib; +import org.apache.solr.core.SolrCore; import org.apache.solr.metrics.SolrMetrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,15 +42,41 @@ public class SolrCacheHolder implements SolrCache { public SolrCacheHolder(CacheConfig.CacheInfo cacheInfo) { this.info = cacheInfo; this.delegate = cacheInfo.cache; - if(info.pkg != null){ - info.core.addPackageListener(info.pkg, lib -> reloadCache(lib)); + + if(info.pkg != null) { + info.core.addPackageListener(new SolrCore.PkgListener() { + @Override + public String packageName() { + return info.pkg; + } + + @Override + public PluginInfo pluginInfo() { + return info.cfg.args; + } + + @Override + public MapWriter lib() { + return info.runtimeLib; + } + + @Override + public void changed(RuntimeLib lib) { + reloadCache(lib); + } + }); } } private void reloadCache(RuntimeLib lib) { - if (lib.getZnodeVersion() > info.znodeVersion) { + int znodeVersion = info.runtimeLib == null ? -1 : info.runtimeLib.getZnodeVersion(); + if (lib.getZnodeVersion() > znodeVersion) { + log.info("Cache {} being reloaded, package: {} loaded from: {} ", delegate.getClass().getSimpleName(), info.pkg, lib.getUrl()); info = new CacheConfig.CacheInfo(info.cfg, info.core); + delegate.close(); delegate = info.cache; + delegate.initializeMetrics(metrics); + } } @@ -153,10 +182,11 @@ public Category getCategory() { return delegate.getCategory(); } + private SolrMetrics metrics; @Override public void initializeMetrics(SolrMetrics info) { + this.metrics = info; delegate.initializeMetrics(info); - } } diff --git a/solr/core/src/test/org/apache/solr/handler/TestContainerReqHandler.java b/solr/core/src/test/org/apache/solr/handler/TestContainerReqHandler.java index 6de5fd029252..a393086821bf 100644 --- a/solr/core/src/test/org/apache/solr/handler/TestContainerReqHandler.java +++ b/solr/core/src/test/org/apache/solr/handler/TestContainerReqHandler.java @@ -53,6 +53,7 @@ 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.StrUtils; import org.apache.solr.common.util.Utils; import org.apache.solr.core.ConfigOverlay; import org.apache.solr.core.MemClassLoader; @@ -98,16 +99,19 @@ static SolrResponse assertResponseValues(int repeats, SolrClient client, SolrReq rsp = req.process(client); } catch (Exception e) { if (i >= repeats - 1) throw e; + continue; } for (Object e : vals.entrySet()) { Map.Entry entry = (Map.Entry) e; - String key = (String) entry.getKey(); + String k = (String) entry.getKey(); + List key = StrUtils.split(k, '/'); + Object val = entry.getValue(); Predicate p = val instanceof Predicate ? (Predicate) val : o -> { String v = o == null ? null : String.valueOf(o); return Objects.equals(val, o); }; - boolean isPass = p.test(rsp.getResponse()._get(key, null)); + boolean isPass = p.test(rsp._get(key, null)); if (isPass) return rsp; else if (i >= repeats - 1) { fail("attempt: " + i + " Mismatch for value : '" + key + "' in response " + Utils.toJSONString(rsp)); @@ -442,7 +446,7 @@ public void testSetClusterReqHandler() throws Exception { } - public void testPluginGlobalLoader() throws Exception { + public void testPluginFrompackage() throws Exception { String COLLECTION_NAME = "globalLoaderColl"; Map jars = Utils.makeMap( "/jar1.jar", getFileContent("runtimecode/runtimelibs.jar.bin"), @@ -469,7 +473,10 @@ public void testPluginGlobalLoader() throws Exception { .withPayload(payload) .withMethod(SolrRequest.METHOD.POST) .build().process(cluster.getSolrClient()); - assertEquals(getObjectByPath(Utils.fromJSONString(payload), true, "add-package/sha512"), + String sha512 = (String) getObjectByPath(Utils.fromJSONString(payload), true, "add-package/sha512"); + String url = (String) getObjectByPath(Utils.fromJSONString(payload), true, "add-package/url"); + + assertEquals(sha512, getObjectByPath(new ClusterProperties(cluster.getZkClient()).getClusterProperties(), true, "package/global/sha512")); @@ -485,8 +492,44 @@ public String getCollection() { } }); + SolrParams params = new MapSolrParams((Map) Utils.makeMap("collection", COLLECTION_NAME, + WT, JAVABIN, + "meta","true")); - SolrParams params = new MapSolrParams((Map) Utils.makeMap("collection", COLLECTION_NAME, WT, JAVABIN)); + assertResponseValues(10, + cluster.getSolrClient(), + new GenericSolrRequest(SolrRequest.METHOD.GET, "/config/queryResponseWriter/json1", params), + Utils.makeMap( + "/config/queryResponseWriter/json1/_packageinfo_/url", url, + "/config/queryResponseWriter/json1/_meta_/sha512", sha512 + )); + + params = new MapSolrParams((Map) Utils.makeMap("collection", COLLECTION_NAME, + WT, JAVABIN, + "meta","true")); + + assertResponseValues(10, + cluster.getSolrClient(), + new GenericSolrRequest(SolrRequest.METHOD.GET, "/config/searchComponent/get", params), + Utils.makeMap( + "config/searchComponent/get/_packageinfo_/url", url, + "config/searchComponent/get/_packageinfo_/sha512", sha512 + )); + + params = new MapSolrParams((Map) Utils.makeMap("collection", COLLECTION_NAME, + WT, JAVABIN, + "meta","true")); + + assertResponseValues(10, + cluster.getSolrClient(), + new GenericSolrRequest(SolrRequest.METHOD.GET, "/config/requestHandler/runtime", params), + Utils.makeMap( + ":config:requestHandler:/runtime:_packageinfo_:url", url, + ":config:requestHandler:/runtime:_packageinfo_:sha512", sha512 + )); + + + params = new MapSolrParams((Map) Utils.makeMap("collection", COLLECTION_NAME, WT, JAVABIN)); assertResponseValues(10, cluster.getSolrClient(), new GenericSolrRequest(SolrRequest.METHOD.GET, "/config/overlay", params), @@ -540,9 +583,50 @@ public NamedList processResponse(Reader reader) { .withPayload(payload) .withMethod(SolrRequest.METHOD.POST) .build().process(cluster.getSolrClient()); - assertEquals(getObjectByPath(Utils.fromJSONString(payload), true, "update-package/sha512"), + sha512 = (String) getObjectByPath(Utils.fromJSONString(payload), true, "update-package/sha512"); + url = (String) getObjectByPath(Utils.fromJSONString(payload), true, "update-package/url"); + + assertEquals(sha512, getObjectByPath(new ClusterProperties(cluster.getZkClient()).getClusterProperties(), true, "package/global/sha512")); + params = new MapSolrParams((Map) Utils.makeMap("collection", COLLECTION_NAME, + WT, JAVABIN, + "meta","true")); + + assertResponseValues(10, + cluster.getSolrClient(), + new GenericSolrRequest(SolrRequest.METHOD.GET, "/config/queryResponseWriter/json1", params), + Utils.makeMap( + "/config/queryResponseWriter/json1/_packageinfo_/url", url, + "/config/queryResponseWriter/json1/_packageinfo_/sha512", sha512 + )); + + params = new MapSolrParams((Map) Utils.makeMap("collection", COLLECTION_NAME, + WT, JAVABIN, + "meta","true")); + + assertResponseValues(10, + cluster.getSolrClient(), + new GenericSolrRequest(SolrRequest.METHOD.GET, "/config/searchComponent/get", params), + Utils.makeMap( + "/config/searchComponent/get/_packageinfo_/url", url, + "/config/searchComponent/get/_packageinfo_/sha512", sha512 + )); + + params = new MapSolrParams((Map) Utils.makeMap("collection", COLLECTION_NAME, + WT, JAVABIN, + "meta","true")); + + assertResponseValues(10, + cluster.getSolrClient(), + new GenericSolrRequest(SolrRequest.METHOD.GET, "/config/requestHandler/runtime", params), + Utils.makeMap( + ":config:requestHandler:/runtime:_packageinfo_:url", url, + ":config:requestHandler:/runtime:_packageinfo_:sha512", sha512 + )); + + + try { new V2Request.Builder("/cluster") .withPayload(payload) @@ -568,8 +652,7 @@ public NamedList processResponse(Reader reader) { } - @AwaitsFix(bugUrl="https://issues.apache.org/jira/browse/SOLR-13650") - public void testCacheFromGlobalLoader() throws Exception { + public void testCacheLoadFromPackage() throws Exception { String COLLECTION_NAME = "globalCacheColl"; Map jars = Utils.makeMap( "/jar1.jar", getFileContent("runtimecode/cache.jar.bin"), @@ -609,6 +692,24 @@ public void testCacheFromGlobalLoader() throws Exception { NamedList rsp = cluster.getSolrClient().request(new GenericSolrRequest(SolrRequest.METHOD.GET, "/config/overlay", params)); assertEquals("org.apache.solr.core.MyDocCache", rsp._getStr("overlay/props/query/documentCache/class", null)); + + String sha512 = (String) getObjectByPath(Utils.fromJSONString(payload), true, "add-package/sha512"); + String url = (String) getObjectByPath(Utils.fromJSONString(payload), true, "add-package/url"); + + + params = new MapSolrParams((Map) Utils.makeMap("collection", COLLECTION_NAME, + WT, JAVABIN, + "meta","true")); + + assertResponseValues(10, + cluster.getSolrClient(), + new GenericSolrRequest(SolrRequest.METHOD.GET, "/config/query/documentCache", params), + Utils.makeMap( + "/config/query/documentCache/_packageinfo_/url", url, + "/config/query/documentCache/_packageinfo_/sha512", sha512 + )); + + UpdateRequest req = new UpdateRequest(); req.add("id", "1", "desc_s", "document 1") @@ -629,9 +730,22 @@ public void testCacheFromGlobalLoader() throws Exception { .withPayload(payload) .withMethod(SolrRequest.METHOD.POST) .build().process(cluster.getSolrClient()); + sha512 = (String) getObjectByPath(Utils.fromJSONString(payload), true, "update-package/sha512"); + url = (String) getObjectByPath(Utils.fromJSONString(payload), true, "update-package/url"); assertEquals(getObjectByPath(Utils.fromJSONString(payload), true, "update-package/sha512"), getObjectByPath(new ClusterProperties(cluster.getZkClient()).getClusterProperties(), true, "package/cache_pkg/sha512")); + params = new MapSolrParams((Map) Utils.makeMap("collection", COLLECTION_NAME, + WT, JAVABIN, + "meta","true")); + + assertResponseValues(10, + cluster.getSolrClient(), + new GenericSolrRequest(SolrRequest.METHOD.GET, "/config/query/documentCache", params), + Utils.makeMap( + "/config/query/documentCache/_packageinfo_/url", url, + "/config/query/documentCache/_packageinfo_/sha512", sha512 + )); req = new UpdateRequest(); req.add("id", "2", "desc_s", "document 1") .setAction(AbstractUpdateRequest.ACTION.COMMIT, true, true) diff --git a/solr/solrj/src/java/org/apache/solr/common/util/StrUtils.java b/solr/solrj/src/java/org/apache/solr/common/util/StrUtils.java index c0b19f57f7a6..9a68c3bacefd 100644 --- a/solr/solrj/src/java/org/apache/solr/common/util/StrUtils.java +++ b/solr/solrj/src/java/org/apache/solr/common/util/StrUtils.java @@ -40,6 +40,15 @@ public static List splitSmart(String s, char separator) { } + static final String DELIM_CHARS = "/:;.,%#"; + public static List split(String s, char sep){ + if(DELIM_CHARS.indexOf(s.charAt(0)) >-1){ + sep = s.charAt(0); + } + return splitSmart(s,sep, true); + + } + public static List splitSmart(String s, char separator, boolean trimEmpty) { List l = splitSmart(s, separator); if(trimEmpty){ @@ -148,7 +157,7 @@ public static List splitSmart(String s, String separator, boolean decode */ public static List splitFileNames(String fileNames) { if (fileNames == null) - return Collections.emptyList(); + return Collections.emptyList(); List result = new ArrayList<>(); for (String file : fileNames.split("(?