This is an automated email from the ASF dual-hosted git repository. noble pushed a commit to branch jira/solr16547 in repository https://gitbox.apache.org/repos/asf/solr.git
commit 2d07feac9a48be3690bbbc75f08574a5947e39bb Author: Noble Paul <[email protected]> AuthorDate: Wed Nov 16 13:06:50 2022 +1100 refactor QueryResponseWriters --- .../java/org/apache/solr/core/CoreContainer.java | 4 +- .../src/java/org/apache/solr/core/PluginBag.java | 141 ++++----------------- .../org/apache/solr/core/RequestHandlerBag.java | 125 ++++++++++++++++++ .../java/org/apache/solr/core/RequestHandlers.java | 6 +- .../java/org/apache/solr/core/ResponseWriters.java | 117 +++++++++++++++++ .../src/java/org/apache/solr/core/SolrCore.java | 73 +---------- .../org/apache/solr/filestore/PackageStoreAPI.java | 4 +- .../java/org/apache/solr/handler/BlobHandler.java | 33 +++-- .../java/org/apache/solr/handler/ClusterAPI.java | 10 ++ .../apache/solr/handler/ReplicationHandler.java | 9 +- .../apache/solr/handler/export/ExportWriter.java | 3 +- .../org/apache/solr/jersey/JerseyApplications.java | 9 +- .../org/apache/solr/jersey/MetricBeanFactory.java | 14 +- .../apache/solr/jersey/RequestMetricHandling.java | 7 +- .../java/org/apache/solr/servlet/HttpSolrCall.java | 6 +- .../processor/UpdateRequestProcessorChain.java | 2 +- .../src/test/org/apache/solr/OutputWriterTest.java | 4 +- .../test/org/apache/solr/core/PluginBagTest.java | 20 +-- .../org/apache/solr/core/RequestHandlersTest.java | 2 +- 19 files changed, 342 insertions(+), 247 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java index 00eb7454b8d..e47475103b0 100644 --- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java +++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java @@ -187,8 +187,8 @@ public class CoreContainer { } } - private volatile PluginBag<SolrRequestHandler> containerHandlers = - new PluginBag<>(SolrRequestHandler.class, null); + private volatile RequestHandlerBag containerHandlers = + new RequestHandlerBag( null); private volatile ApplicationHandler jerseyAppHandler; 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 5ecf681ad62..2cbc1a13a3f 100644 --- a/solr/core/src/java/org/apache/solr/core/PluginBag.java +++ b/solr/core/src/java/org/apache/solr/core/PluginBag.java @@ -16,9 +16,6 @@ */ package org.apache.solr.core; -import static java.util.Collections.singletonMap; -import static org.apache.solr.api.ApiBag.HANDLER_NAME; - import java.io.IOException; import java.lang.invoke.MethodHandles; import java.util.Collection; @@ -32,24 +29,17 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; import java.util.stream.Collectors; -import org.apache.commons.collections4.CollectionUtils; + import org.apache.lucene.util.ResourceLoader; import org.apache.lucene.util.ResourceLoaderAware; -import org.apache.solr.api.Api; -import org.apache.solr.api.ApiBag; -import org.apache.solr.api.ApiSupport; -import org.apache.solr.api.JerseyResource; import org.apache.solr.common.SolrException; -import org.apache.solr.common.util.StrUtils; import org.apache.solr.handler.RequestHandlerBase; import org.apache.solr.handler.component.SearchComponent; -import org.apache.solr.jersey.JerseyApplications; import org.apache.solr.pkg.PackagePluginHolder; import org.apache.solr.request.SolrRequestHandler; import org.apache.solr.util.plugin.NamedListInitializedPlugin; import org.apache.solr.util.plugin.PluginInfoInitialized; import org.apache.solr.util.plugin.SolrCoreAware; -import org.glassfish.jersey.server.ResourceConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,34 +47,16 @@ import org.slf4j.LoggerFactory; public class PluginBag<T> implements AutoCloseable { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - private final Map<String, PluginHolder<T>> registry; - private final Map<String, PluginHolder<T>> immutableRegistry; + protected Map<String, PluginHolder<T>> registry; private String def; private final Class<T> klass; - private SolrCore core; + protected SolrCore core; private final SolrConfig.SolrPluginInfo meta; - private final ApiBag apiBag; - private final ResourceConfig jerseyResources; - public static class JerseyMetricsLookupRegistry - extends HashMap<Class<? extends JerseyResource>, RequestHandlerBase> {} - - private final JerseyMetricsLookupRegistry infoBeanByResource; + private Map<String, PluginHolder<T>> defaults; /** Pass needThreadSafety=true if plugins can be added and removed concurrently with lookups. */ public PluginBag(Class<T> klass, SolrCore core, boolean needThreadSafety) { - if (klass == SolrRequestHandler.class) { - this.apiBag = new ApiBag(core != null); - this.infoBeanByResource = new JerseyMetricsLookupRegistry(); - this.jerseyResources = - (core == null) - ? new JerseyApplications.CoreContainerApp(infoBeanByResource) - : new JerseyApplications.SolrCoreApp(core, infoBeanByResource); - } else { - this.apiBag = null; - this.jerseyResources = null; - this.infoBeanByResource = null; - } this.core = core; this.klass = klass; // TODO: since reads will dominate writes, we could also think about creating a new instance of @@ -93,7 +65,6 @@ public class PluginBag<T> implements AutoCloseable { // We could also perhaps make this constructor into a factory method to return different // implementations depending on thread safety needs. this.registry = needThreadSafety ? new ConcurrentHashMap<>() : new HashMap<>(); - this.immutableRegistry = Collections.unmodifiableMap(registry); meta = SolrConfig.classVsSolrPluginInfo.get(klass.getName()); if (meta == null) { throw new SolrException( @@ -101,6 +72,15 @@ public class PluginBag<T> implements AutoCloseable { } } + public PluginBag(Class<T> klass, SolrCore core, boolean needThreadSafety, Map<String, + PluginHolder<T>> defaults,SolrConfig.SolrPluginInfo pluginMetaData) { + this.core = core; + this.klass = klass; + this.registry = needThreadSafety ? new ConcurrentHashMap<>(0) : new HashMap<>(0); + this.meta = pluginMetaData; + this.defaults = defaults; + } + /** Constructs a non-threadsafe plugin registry */ public PluginBag(Class<T> klass, SolrCore core) { this(klass, core, false); @@ -177,9 +157,14 @@ public class PluginBag<T> implements AutoCloseable { /** Get a plugin by name. If the plugin is not already instantiated, it is done here */ public T get(String name) { PluginHolder<T> result = registry.get(name); + if(result == null && defaults != null) result = defaults.get(name); return result == null ? null : result.get(); } + public PluginHolder<T> getHolder(String name) { + return registry.get(name); + } + /** * Fetches a plugin by name , or the default * @@ -193,7 +178,7 @@ public class PluginBag<T> implements AutoCloseable { } public Set<String> keySet() { - return immutableRegistry.keySet(); + return registry.keySet(); } /** register a plugin by a name */ @@ -207,61 +192,8 @@ public class PluginBag<T> implements AutoCloseable { @SuppressWarnings({"unchecked"}) public PluginHolder<T> put(String name, PluginHolder<T> plugin) { - Boolean registerApi = null; - Boolean disableHandler = null; - if (plugin.pluginInfo != null) { - String registerAt = plugin.pluginInfo.attributes.get("registerPath"); - if (registerAt != null) { - List<String> strs = StrUtils.splitSmart(registerAt, ','); - disableHandler = !strs.contains("/solr"); - registerApi = strs.contains("/v2"); - } - } - - if (apiBag != null) { - if (plugin.isLoaded()) { - T inst = plugin.get(); - if (inst instanceof ApiSupport) { - ApiSupport apiSupport = (ApiSupport) inst; - if (registerApi == null) registerApi = apiSupport.registerV2(); - if (disableHandler == null) disableHandler = !apiSupport.registerV1(); - - if (registerApi) { - Collection<Api> apis = apiSupport.getApis(); - if (apis != null) { - Map<String, String> nameSubstitutes = singletonMap(HANDLER_NAME, name); - for (Api api : apis) { - apiBag.register(api, nameSubstitutes); - } - } - - // TODO Should we use <requestHandler name="/blah"> to override the path that each - // resource registers under? - Collection<Class<? extends JerseyResource>> jerseyApis = - apiSupport.getJerseyResources(); - if (!CollectionUtils.isEmpty(jerseyApis)) { - for (Class<? extends JerseyResource> jerseyClazz : jerseyApis) { - if (log.isDebugEnabled()) { - log.debug("Registering jersey resource class: {}", jerseyClazz.getName()); - } - jerseyResources.register(jerseyClazz); - // See MetricsBeanFactory javadocs for a better understanding of this resource->RH - // mapping - if (inst instanceof RequestHandlerBase) { - infoBeanByResource.put(jerseyClazz, (RequestHandlerBase) inst); - } - } - } - } - } - } else { - if (registerApi != null && registerApi) - apiBag.registerLazy((PluginHolder<SolrRequestHandler>) plugin, plugin.pluginInfo); - } - } - if (disableHandler == null) disableHandler = Boolean.FALSE; PluginHolder<T> old = null; - if (!disableHandler) old = registry.put(name, plugin); + old = registry.put(name, plugin); if (plugin.pluginInfo != null && plugin.pluginInfo.isDefault()) setDefault(name); if (plugin.isLoaded()) registerMBean(plugin.get(), core, name); // old instance has been replaced - close it to prevent mem leaks @@ -279,10 +211,6 @@ public class PluginBag<T> implements AutoCloseable { this.def = def; } - public Map<String, PluginHolder<T>> getRegistry() { - return immutableRegistry; - } - public boolean contains(String name) { return registry.containsKey(name); } @@ -327,9 +255,11 @@ public class PluginBag<T> implements AutoCloseable { infos.stream().map(i -> i.name).collect(Collectors.toList())); } } - for (Map.Entry<String, T> e : defaults.entrySet()) { - if (!contains(e.getKey())) { - put(e.getKey(), new PluginHolder<>(null, e.getValue())); + if(defaults != null) { + for (Map.Entry<String, T> e : defaults.entrySet()) { + if (!contains(e.getKey())) { + put(e.getKey(), new PluginHolder<>(null, e.getValue())); + } } } } @@ -341,7 +271,7 @@ public class PluginBag<T> implements AutoCloseable { return result.isLoaded(); } - private void registerMBean(Object inst, SolrCore core, String pluginKey) { + protected void registerMBean(Object inst, SolrCore core, String pluginKey) { if (core == null) return; if (inst instanceof SolrInfoBean) { SolrInfoBean mBean = (SolrInfoBean) inst; @@ -398,7 +328,6 @@ public class PluginBag<T> implements AutoCloseable { return Optional.ofNullable(inst); } - @Override public T get() { return inst; } @@ -436,7 +365,6 @@ public class PluginBag<T> implements AutoCloseable { return pluginInfo; } - @Override public String toString() { return String.valueOf(inst); } @@ -518,21 +446,4 @@ public class PluginBag<T> implements AutoCloseable { return true; } } - - public Api v2lookup(String path, String method, Map<String, String> parts) { - if (apiBag == null) { - throw new SolrException( - SolrException.ErrorCode.SERVER_ERROR, - "this should not happen, looking up for v2 API at the wrong place"); - } - return apiBag.lookup(path, method, parts); - } - - public ApiBag getApiBag() { - return apiBag; - } - - public ResourceConfig getJerseyEndpoints() { - return jerseyResources; - } } diff --git a/solr/core/src/java/org/apache/solr/core/RequestHandlerBag.java b/solr/core/src/java/org/apache/solr/core/RequestHandlerBag.java new file mode 100644 index 00000000000..cfddb08e4a7 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/core/RequestHandlerBag.java @@ -0,0 +1,125 @@ +package org.apache.solr.core; + + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.solr.api.Api; +import org.apache.solr.api.ApiBag; +import org.apache.solr.api.ApiSupport; +import org.apache.solr.api.JerseyResource; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.util.StrUtils; +import org.apache.solr.handler.RequestHandlerBase; +import org.apache.solr.request.SolrRequestHandler; +import org.glassfish.jersey.server.ResourceConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.invoke.MethodHandles; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static java.util.Collections.singletonMap; +import static org.apache.solr.api.ApiBag.HANDLER_NAME; + +public class RequestHandlerBag extends PluginBag<SolrRequestHandler> { + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private ApiBag apiBag; + private ResourceConfig jerseyResources; + private JerseyMetricsLookupRegistry infoBeanByResource; + + public RequestHandlerBag(SolrCore core) { + super(SolrRequestHandler.class, core, true); + this.apiBag = new ApiBag(core != null); + } + + @Override + public PluginHolder<SolrRequestHandler> put(String name, PluginHolder<SolrRequestHandler> plugin) { + Boolean registerApi = null; + Boolean disableHandler = null; + if (plugin.pluginInfo != null) { + String registerAt = plugin.pluginInfo.attributes.get("registerPath"); + if (registerAt != null) { + List<String> strs = StrUtils.splitSmart(registerAt, ','); + disableHandler = !strs.contains("/solr"); + registerApi = strs.contains("/v2"); + } + } + + if (apiBag != null) { + if (plugin.isLoaded()) { + SolrRequestHandler inst = plugin.get(); + if (inst instanceof ApiSupport) { + ApiSupport apiSupport = (ApiSupport) inst; + if (registerApi == null) registerApi = apiSupport.registerV2(); + if (disableHandler == null) disableHandler = !apiSupport.registerV1(); + + if (registerApi) { + Collection<Api> apis = apiSupport.getApis(); + if (apis != null) { + Map<String, String> nameSubstitutes = singletonMap(HANDLER_NAME, name); + for (Api api : apis) { + apiBag.register(api, nameSubstitutes); + } + } + + // TODO Should we use <requestHandler name="/blah"> to override the path that each + // resource registers under? + Collection<Class<? extends JerseyResource>> jerseyApis = + apiSupport.getJerseyResources(); + if (!CollectionUtils.isEmpty(jerseyApis)) { + for (Class<? extends JerseyResource> jerseyClazz : jerseyApis) { + if (log.isDebugEnabled()) { + log.debug("Registering jersey resource class: {}", jerseyClazz.getName()); + } + jerseyResources.register(jerseyClazz); + // See MetricsBeanFactory javadocs for a better understanding of this resource->RH + // mapping + if (inst instanceof RequestHandlerBase) { + infoBeanByResource.put(jerseyClazz, (RequestHandlerBase) inst); + } + } + } + } + } + } else { + if (registerApi != null && registerApi) + apiBag.registerLazy((PluginHolder<SolrRequestHandler>) plugin, plugin.pluginInfo); + } + } + PluginHolder<SolrRequestHandler> old = null; + if (disableHandler == null) disableHandler = Boolean.FALSE; + if (!disableHandler) old = registry.put(name, plugin); + if (plugin.pluginInfo != null && plugin.pluginInfo.isDefault()) setDefault(name); + if (plugin.isLoaded()) registerMBean(plugin.get(), core, name); + // old instance has been replaced - close it to prevent mem leaks + if (old != null && old != plugin) { + closeQuietly(old); + } + return old; + } + + public Api v2lookup(String path, String method, Map<String, String> parts) { + if (apiBag == null) { + throw new SolrException( + SolrException.ErrorCode.SERVER_ERROR, + "this should not happen, looking up for v2 API at the wrong place"); + } + return apiBag.lookup(path, method, parts); + } + + + + public ApiBag getApiBag() { + return apiBag; + } + + public ResourceConfig getJerseyEndpoints() { + return jerseyResources; + } + + public static class JerseyMetricsLookupRegistry + extends HashMap<Class<? extends JerseyResource>, RequestHandlerBase> {} +} diff --git a/solr/core/src/java/org/apache/solr/core/RequestHandlers.java b/solr/core/src/java/org/apache/solr/core/RequestHandlers.java index dca7c832e3f..4c2e7d15041 100644 --- a/solr/core/src/java/org/apache/solr/core/RequestHandlers.java +++ b/solr/core/src/java/org/apache/solr/core/RequestHandlers.java @@ -33,7 +33,7 @@ public final class RequestHandlers { private final SolrCore core; - final PluginBag<SolrRequestHandler> handlers; + final RequestHandlerBag handlers; /** * Trim the trailing '/' if it's there, and convert null to empty string. @@ -51,7 +51,7 @@ public final class RequestHandlers { this.core = core; // we need a thread safe registry since methods like register are currently documented to be // thread safe. - handlers = new PluginBag<>(SolrRequestHandler.class, core, true); + handlers = new RequestHandlerBag( core); } /** @@ -79,7 +79,7 @@ public final class RequestHandlers { } /** Returns an unmodifiable Map containing the registered handlers */ - public PluginBag<SolrRequestHandler> getRequestHandlers() { + public RequestHandlerBag getRequestHandlers() { return handlers; } diff --git a/solr/core/src/java/org/apache/solr/core/ResponseWriters.java b/solr/core/src/java/org/apache/solr/core/ResponseWriters.java new file mode 100644 index 00000000000..8a11bb3390f --- /dev/null +++ b/solr/core/src/java/org/apache/solr/core/ResponseWriters.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.core; + +import com.google.common.collect.ImmutableMap; +import org.apache.solr.client.solrj.impl.BinaryResponseParser; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.handler.ReplicationHandler; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.*; + +import java.io.Closeable; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class ResponseWriters { + public static final SolrConfig.SolrPluginInfo info = SolrConfig.classVsSolrPluginInfo.get(QueryResponseWriter.class.getName()); + private static QueryResponseWriter standard; + + public static QueryResponseWriter get(String name) { + return DEFAULT_RESPONSE_WRITERS.get(name); + } + public static QueryResponseWriter getOrDefault(String name){ + QueryResponseWriter result = DEFAULT_RESPONSE_WRITERS.get(name); + return result==null? standard: result; + + } + + public static final Map<String, QueryResponseWriter> DEFAULT_RESPONSE_WRITERS; + public static final Map<String, PluginBag.PluginHolder<QueryResponseWriter>> DEFAULT_RESPONSE_WRITER_HOLDERS ; + + + static { + HashMap<String, QueryResponseWriter> m = new HashMap<>(15, 1); + m.put("xml", new XMLResponseWriter()); + m.put(CommonParams.JSON, new JSONResponseWriter()); + m.put("standard", m.get(CommonParams.JSON)); + m.put("geojson", new GeoJSONResponseWriter()); + m.put("graphml", new GraphMLResponseWriter()); + m.put("python", new PythonResponseWriter()); + m.put("php", new PHPResponseWriter()); + m.put("phps", new PHPSerializedResponseWriter()); + m.put("ruby", new RubyResponseWriter()); + m.put("raw", new RawResponseWriter()); + m.put(CommonParams.JAVABIN, new BinaryResponseWriter()); + m.put("csv", new CSVResponseWriter()); + m.put("schema.xml", new SchemaXmlResponseWriter()); + m.put("smile", new SmileResponseWriter()); + standard = m.get("standard"); + m.put(ReplicationHandler.FILE_STREAM, getFileStreamWriter()); + DEFAULT_RESPONSE_WRITERS = Collections.unmodifiableMap(m); + try { + m.put( + "xlsx", + (QueryResponseWriter) + Class.forName("org.apache.solr.handler.extraction.XLSXResponseWriter") + .getConstructor() + .newInstance()); + } catch (Exception e) { + // don't worry; extraction module not in class path + } + ImmutableMap.Builder<String, PluginBag.PluginHolder<QueryResponseWriter>> b = ImmutableMap.builder(); + DEFAULT_RESPONSE_WRITERS.forEach((k, v) -> b.put(k, new PluginBag.PluginHolder<>(v, info))); + DEFAULT_RESPONSE_WRITER_HOLDERS = b.build(); + } + + private static BinaryResponseWriter getFileStreamWriter() { + return new BinaryResponseWriter() { + @Override + public void write(OutputStream out, SolrQueryRequest req, SolrQueryResponse response) + throws IOException { + RawWriter rawWriter = (RawWriter) response.getValues().get(ReplicationHandler.FILE_STREAM); + if (rawWriter != null) { + rawWriter.write(out); + if (rawWriter instanceof Closeable) ((Closeable) rawWriter).close(); + } + } + + @Override + public String getContentType(SolrQueryRequest request, SolrQueryResponse response) { + RawWriter rawWriter = (RawWriter) response.getValues().get(ReplicationHandler.FILE_STREAM); + if (rawWriter != null) { + return rawWriter.getContentType(); + } else { + return BinaryResponseParser.BINARY_CONTENT_TYPE; + } + } + }; + } + + + + public interface RawWriter { + default String getContentType() { + return BinaryResponseParser.BINARY_CONTENT_TYPE; + } + + void write(OutputStream os) throws IOException; + } +} 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 cf6e7d51cc9..8ff9255d221 100644 --- a/solr/core/src/java/org/apache/solr/core/SolrCore.java +++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java @@ -17,6 +17,7 @@ package org.apache.solr.core; import static org.apache.solr.common.params.CommonParams.PATH; +import static org.apache.solr.core.ResponseWriters.DEFAULT_RESPONSE_WRITERS; import com.codahale.metrics.Counter; import com.codahale.metrics.Timer; @@ -1997,7 +1998,7 @@ public class SolrCore implements SolrInfoBean, Closeable { && searchComponents.get(name) instanceof HighlightComponent) { if (!HighlightComponent.COMPONENT_NAME.equals(name)) { searchComponents.put( - HighlightComponent.COMPONENT_NAME, searchComponents.getRegistry().get(name)); + HighlightComponent.COMPONENT_NAME, searchComponents.getHolder(name)); } break; } @@ -2991,84 +2992,20 @@ public class SolrCore implements SolrInfoBean, Closeable { public PluginBag<QueryResponseWriter> getResponseWriters() { return responseWriters; } - private final PluginBag<QueryResponseWriter> responseWriters = - new PluginBag<>(QueryResponseWriter.class, this); - public static final Map<String, QueryResponseWriter> DEFAULT_RESPONSE_WRITERS; - - static { - HashMap<String, QueryResponseWriter> m = new HashMap<>(15, 1); - m.put("xml", new XMLResponseWriter()); - m.put(CommonParams.JSON, new JSONResponseWriter()); - m.put("standard", m.get(CommonParams.JSON)); - m.put("geojson", new GeoJSONResponseWriter()); - m.put("graphml", new GraphMLResponseWriter()); - m.put("python", new PythonResponseWriter()); - m.put("php", new PHPResponseWriter()); - m.put("phps", new PHPSerializedResponseWriter()); - m.put("ruby", new RubyResponseWriter()); - m.put("raw", new RawResponseWriter()); - m.put(CommonParams.JAVABIN, new BinaryResponseWriter()); - m.put("csv", new CSVResponseWriter()); - m.put("schema.xml", new SchemaXmlResponseWriter()); - m.put("smile", new SmileResponseWriter()); - m.put(ReplicationHandler.FILE_STREAM, getFileStreamWriter()); - DEFAULT_RESPONSE_WRITERS = Collections.unmodifiableMap(m); - try { - m.put( - "xlsx", - (QueryResponseWriter) - Class.forName("org.apache.solr.handler.extraction.XLSXResponseWriter") - .getConstructor() - .newInstance()); - } catch (Exception e) { - // don't worry; extraction module not in class path - } - } - - private static BinaryResponseWriter getFileStreamWriter() { - return new BinaryResponseWriter() { - @Override - public void write(OutputStream out, SolrQueryRequest req, SolrQueryResponse response) - throws IOException { - RawWriter rawWriter = (RawWriter) response.getValues().get(ReplicationHandler.FILE_STREAM); - if (rawWriter != null) { - rawWriter.write(out); - if (rawWriter instanceof Closeable) ((Closeable) rawWriter).close(); - } - } - - @Override - public String getContentType(SolrQueryRequest request, SolrQueryResponse response) { - RawWriter rawWriter = (RawWriter) response.getValues().get(ReplicationHandler.FILE_STREAM); - if (rawWriter != null) { - return rawWriter.getContentType(); - } else { - return BinaryResponseParser.BINARY_CONTENT_TYPE; - } - } - }; - } - + new PluginBag<>(QueryResponseWriter.class, this, false, + ResponseWriters.DEFAULT_RESPONSE_WRITER_HOLDERS, ResponseWriters.info); public void fetchLatestSchema() { IndexSchema schema = configSet.getIndexSchema(true); setLatestSchema(schema); } - public interface RawWriter { - default String getContentType() { - return BinaryResponseParser.BINARY_CONTENT_TYPE; - } - - void write(OutputStream os) throws IOException; - } - /** * Configure the query response writers. There will always be a default writer; additional writers * may also be configured. */ private void initWriters() { - responseWriters.init(DEFAULT_RESPONSE_WRITERS, this); + responseWriters.init(null, this); // configure the default response writer; this one should never be null if (responseWriters.getDefault() == null) responseWriters.setDefault("standard"); } diff --git a/solr/core/src/java/org/apache/solr/filestore/PackageStoreAPI.java b/solr/core/src/java/org/apache/solr/filestore/PackageStoreAPI.java index 0a98bba9a2f..d5985e06f2c 100644 --- a/solr/core/src/java/org/apache/solr/filestore/PackageStoreAPI.java +++ b/solr/core/src/java/org/apache/solr/filestore/PackageStoreAPI.java @@ -46,7 +46,7 @@ import org.apache.solr.common.util.StrUtils; import org.apache.solr.common.util.Utils; import org.apache.solr.core.BlobRepository; import org.apache.solr.core.CoreContainer; -import org.apache.solr.core.SolrCore; +import org.apache.solr.core.ResponseWriters; import org.apache.solr.pkg.PackageAPI; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; @@ -348,7 +348,7 @@ public class PackageStoreAPI { req.setParams(SolrParams.wrapDefaults(solrParams, req.getParams())); rsp.add( FILE_STREAM, - (SolrCore.RawWriter) + (ResponseWriters.RawWriter) os -> packageStore.get( path, diff --git a/solr/core/src/java/org/apache/solr/handler/BlobHandler.java b/solr/core/src/java/org/apache/solr/handler/BlobHandler.java index 3bb14b19ed4..789be7117be 100644 --- a/solr/core/src/java/org/apache/solr/handler/BlobHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/BlobHandler.java @@ -53,6 +53,7 @@ import org.apache.solr.common.util.ContentStream; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.StrUtils; import org.apache.solr.core.PluginInfo; +import org.apache.solr.core.ResponseWriters; import org.apache.solr.core.SolrCore; import org.apache.solr.handler.admin.api.GetBlobInfoAPI; import org.apache.solr.handler.admin.api.UploadBlobAPI; @@ -210,24 +211,20 @@ public class BlobHandler extends RequestHandlerBase if (docs.totalHits.value > 0) { rsp.add( ReplicationHandler.FILE_STREAM, - new SolrCore.RawWriter() { - - @Override - public void write(OutputStream os) throws IOException { - Document doc = req.getSearcher().doc(docs.scoreDocs[0].doc); - IndexableField sf = doc.getField("blob"); - FieldType fieldType = req.getSchema().getField("blob").getType(); - ByteBuffer buf = (ByteBuffer) fieldType.toObject(sf); - if (buf == null) { - // should never happen unless a user wrote this document directly - throw new SolrException( - SolrException.ErrorCode.NOT_FOUND, - "Invalid document . No field called blob"); - } else { - os.write(buf.array(), buf.arrayOffset(), buf.limit()); - } - } - }); + (ResponseWriters.RawWriter) os -> { + Document doc = req.getSearcher().doc(docs.scoreDocs[0].doc); + IndexableField sf = doc.getField("blob"); + FieldType fieldType = req.getSchema().getField("blob").getType(); + ByteBuffer buf = (ByteBuffer) fieldType.toObject(sf); + if (buf == null) { + // should never happen unless a user wrote this document directly + throw new SolrException( + SolrException.ErrorCode.NOT_FOUND, + "Invalid document . No field called blob"); + } else { + os.write(buf.array(), buf.arrayOffset(), buf.limit()); + } + }); } else { throw new SolrException( diff --git a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java index 1a56f7980d5..236159bbe0a 100644 --- a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java @@ -77,6 +77,16 @@ public class ClusterAPI { this.configSetsHandler = configSetsHandler; } + @EndPoint(method = GET, path = "/node/heap", permission = COLL_READ_PERM) + public void heap(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { + Runtime rt = Runtime.getRuntime(); + if(req.getParams().getBool("gc", false)) { + rt.gc(); + } + rsp.add("heap", rt.totalMemory() - rt.freeMemory()); + } + + @EndPoint(method = GET, path = "/cluster/node-roles", permission = COLL_READ_PERM) public void roles(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { rsp.add( diff --git a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java index 7cae4115598..29b0e47aaa7 100644 --- a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java @@ -83,13 +83,8 @@ import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.common.util.SolrNamedThreadFactory; import org.apache.solr.common.util.StrUtils; import org.apache.solr.common.util.SuppressForbidden; -import org.apache.solr.core.CloseHook; -import org.apache.solr.core.CoreContainer; +import org.apache.solr.core.*; import org.apache.solr.core.DirectoryFactory.DirContext; -import org.apache.solr.core.IndexDeletionPolicyWrapper; -import org.apache.solr.core.SolrCore; -import org.apache.solr.core.SolrDeletionPolicy; -import org.apache.solr.core.SolrEventListener; import org.apache.solr.core.backup.repository.BackupRepository; import org.apache.solr.core.backup.repository.LocalFileSystemRepository; import org.apache.solr.handler.IndexFetcher.IndexFetchResult; @@ -1582,7 +1577,7 @@ public class ReplicationHandler extends RequestHandlerBase implements SolrCoreAw } /** This class is used to read and send files in the lucene index */ - private class DirectoryFileStream implements SolrCore.RawWriter { + private class DirectoryFileStream implements ResponseWriters.RawWriter { protected SolrParams params; protected FastOutputStream fos; diff --git a/solr/core/src/java/org/apache/solr/handler/export/ExportWriter.java b/solr/core/src/java/org/apache/solr/handler/export/ExportWriter.java index cc1599562a3..d3764c1088b 100644 --- a/solr/core/src/java/org/apache/solr/handler/export/ExportWriter.java +++ b/solr/core/src/java/org/apache/solr/handler/export/ExportWriter.java @@ -55,6 +55,7 @@ import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.params.StreamParams; import org.apache.solr.common.util.JavaBinCodec; +import org.apache.solr.core.ResponseWriters; import org.apache.solr.core.SolrCore; import org.apache.solr.metrics.SolrMetricsContext; import org.apache.solr.request.SolrQueryRequest; @@ -91,7 +92,7 @@ import org.slf4j.LoggerFactory; * across the wire) and marked as sent (unset in the bitmap). This process repeats until all * matching documents have been sent. */ -public class ExportWriter implements SolrCore.RawWriter, Closeable { +public class ExportWriter implements ResponseWriters.RawWriter, Closeable { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); public static final String BATCH_SIZE_PARAM = "batchSize"; diff --git a/solr/core/src/java/org/apache/solr/jersey/JerseyApplications.java b/solr/core/src/java/org/apache/solr/jersey/JerseyApplications.java index 7a4bb484e4b..dc613dbe958 100644 --- a/solr/core/src/java/org/apache/solr/jersey/JerseyApplications.java +++ b/solr/core/src/java/org/apache/solr/jersey/JerseyApplications.java @@ -21,7 +21,8 @@ import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.info.Info; import io.swagger.v3.oas.annotations.info.License; import javax.inject.Singleton; -import org.apache.solr.core.PluginBag; + +import org.apache.solr.core.RequestHandlerBag; import org.apache.solr.core.SolrCore; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; @@ -44,7 +45,7 @@ import org.glassfish.jersey.server.ResourceConfig; public class JerseyApplications { public static class CoreContainerApp extends ResourceConfig { - public CoreContainerApp(PluginBag.JerseyMetricsLookupRegistry beanRegistry) { + public CoreContainerApp(RequestHandlerBag.JerseyMetricsLookupRegistry beanRegistry) { super(); // Authentication and authorization @@ -68,7 +69,7 @@ public class JerseyApplications { @Override protected void configure() { bindFactory(new MetricBeanFactory(beanRegistry)) - .to(PluginBag.JerseyMetricsLookupRegistry.class) + .to(RequestHandlerBag.JerseyMetricsLookupRegistry.class) .in(Singleton.class); } }); @@ -102,7 +103,7 @@ public class JerseyApplications { public static class SolrCoreApp extends CoreContainerApp { - public SolrCoreApp(SolrCore solrCore, PluginBag.JerseyMetricsLookupRegistry beanRegistry) { + public SolrCoreApp(SolrCore solrCore, RequestHandlerBag.JerseyMetricsLookupRegistry beanRegistry) { super(beanRegistry); // Dependency Injection for Jersey resources diff --git a/solr/core/src/java/org/apache/solr/jersey/MetricBeanFactory.java b/solr/core/src/java/org/apache/solr/jersey/MetricBeanFactory.java index c23851359d9..9821cd51f87 100644 --- a/solr/core/src/java/org/apache/solr/jersey/MetricBeanFactory.java +++ b/solr/core/src/java/org/apache/solr/jersey/MetricBeanFactory.java @@ -17,7 +17,7 @@ package org.apache.solr.jersey; -import org.apache.solr.core.PluginBag; +import org.apache.solr.core.RequestHandlerBag; import org.glassfish.hk2.api.Factory; /** @@ -26,7 +26,7 @@ import org.glassfish.hk2.api.Factory; * <p>Currently, Jersey resources that have a corresponding v1 API produce the same metrics as their * v1 equivalent and rely on the v1 requestHandler instance to do so. Solr facilitates this by * building a map of the Jersey resource to requestHandler mapping (a {@link - * org.apache.solr.core.PluginBag.JerseyMetricsLookupRegistry}), and injecting it into the pre- and + * RequestHandlerBag.JerseyMetricsLookupRegistry}), and injecting it into the pre- and * post- Jersey filters that handle metrics. * * <p>This isn't ideal, as requestHandler's don't really "fit" conceptually here. But it's @@ -35,21 +35,21 @@ import org.glassfish.hk2.api.Factory; * @see RequestMetricHandling.PreRequestMetricsFilter * @see RequestMetricHandling.PostRequestMetricsFilter */ -public class MetricBeanFactory implements Factory<PluginBag.JerseyMetricsLookupRegistry> { +public class MetricBeanFactory implements Factory<RequestHandlerBag.JerseyMetricsLookupRegistry> { - private final PluginBag.JerseyMetricsLookupRegistry metricsLookupRegistry; + private final RequestHandlerBag.JerseyMetricsLookupRegistry metricsLookupRegistry; - public MetricBeanFactory(PluginBag.JerseyMetricsLookupRegistry metricsLookupRegistry) { + public MetricBeanFactory(RequestHandlerBag.JerseyMetricsLookupRegistry metricsLookupRegistry) { this.metricsLookupRegistry = metricsLookupRegistry; } @Override - public PluginBag.JerseyMetricsLookupRegistry provide() { + public RequestHandlerBag.JerseyMetricsLookupRegistry provide() { return metricsLookupRegistry; } @Override - public void dispose(PluginBag.JerseyMetricsLookupRegistry instance) { + public void dispose(RequestHandlerBag.JerseyMetricsLookupRegistry instance) { /* No-op */ } } diff --git a/solr/core/src/java/org/apache/solr/jersey/RequestMetricHandling.java b/solr/core/src/java/org/apache/solr/jersey/RequestMetricHandling.java index 595027005ad..b4543a2084b 100644 --- a/solr/core/src/java/org/apache/solr/jersey/RequestMetricHandling.java +++ b/solr/core/src/java/org/apache/solr/jersey/RequestMetricHandling.java @@ -31,7 +31,8 @@ import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; import javax.ws.rs.container.ResourceInfo; import javax.ws.rs.core.Context; -import org.apache.solr.core.PluginBag; + +import org.apache.solr.core.RequestHandlerBag; import org.apache.solr.handler.RequestHandlerBase; import org.apache.solr.request.SolrQueryRequest; import org.slf4j.Logger; @@ -58,10 +59,10 @@ public class RequestMetricHandling { @Context private ResourceInfo resourceInfo; - private PluginBag.JerseyMetricsLookupRegistry beanRegistry; + private RequestHandlerBag.JerseyMetricsLookupRegistry beanRegistry; @Inject - public PreRequestMetricsFilter(PluginBag.JerseyMetricsLookupRegistry beanRegistry) { + public PreRequestMetricsFilter(RequestHandlerBag.JerseyMetricsLookupRegistry beanRegistry) { this.beanRegistry = beanRegistry; } diff --git a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java index debd481a9a3..692dde2f8d6 100644 --- a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java +++ b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java @@ -101,6 +101,7 @@ import org.apache.solr.common.util.TimeSource; import org.apache.solr.common.util.Utils; import org.apache.solr.common.util.ValidatingJsonMap; import org.apache.solr.core.CoreContainer; +import org.apache.solr.core.ResponseWriters; import org.apache.solr.core.SolrConfig; import org.apache.solr.core.SolrCore; import org.apache.solr.handler.ContentStreamHandlerBase; @@ -876,7 +877,7 @@ public class HttpSolrCall { } } QueryResponseWriter respWriter = - SolrCore.DEFAULT_RESPONSE_WRITERS.get(solrReq.getParams().get(CommonParams.WT)); + ResponseWriters.get(solrReq.getParams().get(CommonParams.WT)); if (respWriter == null) respWriter = getResponseWriter(); writeResponse(solrResp, respWriter, Method.getMethod(req.getMethod())); if (shouldAudit()) { @@ -905,8 +906,7 @@ public class HttpSolrCall { if (core != null) { return core.getQueryResponseWriter(wt); } else { - return SolrCore.DEFAULT_RESPONSE_WRITERS.getOrDefault( - wt, SolrCore.DEFAULT_RESPONSE_WRITERS.get("standard")); + return ResponseWriters.getOrDefault(wt); } } diff --git a/solr/core/src/java/org/apache/solr/update/processor/UpdateRequestProcessorChain.java b/solr/core/src/java/org/apache/solr/update/processor/UpdateRequestProcessorChain.java index 1f5476632f8..e0bfc06c371 100644 --- a/solr/core/src/java/org/apache/solr/update/processor/UpdateRequestProcessorChain.java +++ b/solr/core/src/java/org/apache/solr/update/processor/UpdateRequestProcessorChain.java @@ -302,7 +302,7 @@ public final class UpdateRequestProcessorChain implements PluginInfoInitialized if (s.isEmpty()) continue; UpdateRequestProcessorFactory p = null; PluginBag.PluginHolder<UpdateRequestProcessorFactory> holder = - core.getUpdateProcessors().getRegistry().get(s); + core.getUpdateProcessors().getHolder(s); if (holder instanceof PackagePluginHolder) { p = new LazyUpdateRequestProcessorFactory(holder); } else { diff --git a/solr/core/src/test/org/apache/solr/OutputWriterTest.java b/solr/core/src/test/org/apache/solr/OutputWriterTest.java index 13eb69d9014..471471b3e41 100644 --- a/solr/core/src/test/org/apache/solr/OutputWriterTest.java +++ b/solr/core/src/test/org/apache/solr/OutputWriterTest.java @@ -67,10 +67,10 @@ public class OutputWriterTest extends SolrTestCaseJ4 { public void testLazy() { PluginBag.PluginHolder<QueryResponseWriter> qrw = - h.getCore().getResponseWriters().getRegistry().get("useless"); + h.getCore().getResponseWriters().getHolder("useless"); assertTrue("Should be a lazy class", qrw instanceof PluginBag.LazyPluginHolder); - qrw = h.getCore().getResponseWriters().getRegistry().get("xml"); + qrw = h.getCore().getResponseWriters().getHolder("xml"); assertTrue("Should not be a lazy class", qrw.isLoaded()); assertSame("Should not be a lazy class", qrw.getClass(), PluginBag.PluginHolder.class); } diff --git a/solr/core/src/test/org/apache/solr/core/PluginBagTest.java b/solr/core/src/test/org/apache/solr/core/PluginBagTest.java index f0281038197..c0a011bc67d 100644 --- a/solr/core/src/test/org/apache/solr/core/PluginBagTest.java +++ b/solr/core/src/test/org/apache/solr/core/PluginBagTest.java @@ -49,14 +49,14 @@ public class PluginBagTest extends SolrTestCaseJ4 { @Test public void testOnlyInitsJerseyIfHoldingRequestHandlers() { - final PluginBag<SearchComponent> nonRequestHandlerBag = - new PluginBag<>(SearchComponent.class, null); + final RequestHandlerBag nonRequestHandlerBag = + new RequestHandlerBag(null); assertNull( "Jersey app should not be created for plugin bags that aren't managing RequestHandler's", nonRequestHandlerBag.getJerseyEndpoints()); - final PluginBag<SolrRequestHandler> handlerPluginBag = - new PluginBag<>(SolrRequestHandler.class, null); + final RequestHandlerBag handlerPluginBag = + new RequestHandlerBag(null); assertNotNull( "Jersey app should be created for plugin bags that manage RequestHandlers", handlerPluginBag.getJerseyEndpoints()); @@ -64,16 +64,16 @@ public class PluginBagTest extends SolrTestCaseJ4 { @Test public void testCreatesCoreSpecificJerseyAppIfCoreProvided() { - final PluginBag<SolrRequestHandler> handlerPluginBag = - new PluginBag<>(SolrRequestHandler.class, solrCore); + final RequestHandlerBag handlerPluginBag = + new RequestHandlerBag(solrCore); assertEquals( JerseyApplications.SolrCoreApp.class, handlerPluginBag.getJerseyEndpoints().getClass()); } @Test public void testCreatesContainerSpecificJerseyAppIfNoCoreProvided() { - final PluginBag<SolrRequestHandler> handlerPluginBag = - new PluginBag<>(SolrRequestHandler.class, null); + final RequestHandlerBag handlerPluginBag = + new RequestHandlerBag(null); assertEquals( JerseyApplications.CoreContainerApp.class, handlerPluginBag.getJerseyEndpoints().getClass()); @@ -81,8 +81,8 @@ public class PluginBagTest extends SolrTestCaseJ4 { @Test public void testRegistersJerseyResourcesAssociatedWithRequestHandlers() { - final PluginBag<SolrRequestHandler> handlerPluginBag = - new PluginBag<>(SolrRequestHandler.class, null); + final RequestHandlerBag handlerPluginBag = + new RequestHandlerBag(null); assertFalse(handlerPluginBag.getJerseyEndpoints().isRegistered(ListConfigSetsAPI.class)); handlerPluginBag.put("/foo", new ConfigSetsHandler(coreContainer)); diff --git a/solr/core/src/test/org/apache/solr/core/RequestHandlersTest.java b/solr/core/src/test/org/apache/solr/core/RequestHandlersTest.java index 7ac9d6a107e..42fd4a466ab 100644 --- a/solr/core/src/test/org/apache/solr/core/RequestHandlersTest.java +++ b/solr/core/src/test/org/apache/solr/core/RequestHandlersTest.java @@ -52,7 +52,7 @@ public class RequestHandlersTest extends SolrTestCaseJ4 { public void testLazyLoading() { SolrCore core = h.getCore(); PluginBag.PluginHolder<SolrRequestHandler> handler = - core.getRequestHandlers().getRegistry().get("/lazy"); + core.getRequestHandlers().getHolder("/lazy"); assertFalse(handler.isLoaded()); assertU(
