diff --git a/changelog/unreleased/SOLR-18085.yml b/changelog/unreleased/SOLR-18085.yml
new file mode 100644
index 000000000000..d6ca801ddc8e
--- /dev/null
+++ b/changelog/unreleased/SOLR-18085.yml
@@ -0,0 +1,8 @@
+# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc
+title: Removed the wt=standard concept that was used internally by Solr.
+type: removed # added, changed, fixed, deprecated, removed, dependency_update, security, other
+authors:
+ - name: Eric Pugh
+links:
+ - name: SOLR-18085
+ url: https://issues.apache.org/jira/browse/SOLR-18085
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 7624a4c13ddd..1af3445e7aaa 100644
--- a/solr/core/src/java/org/apache/solr/core/PluginBag.java
+++ b/solr/core/src/java/org/apache/solr/core/PluginBag.java
@@ -202,7 +202,7 @@ public T get(String name) {
* Fetches a plugin by name , or the default
*
* @param name name using which it is registered
- * @param useDefault Return the default , if a plugin by that name does not exist
+ * @param useDefault Return the default, if a plugin by that name does not exist
*/
public T get(String name, boolean useDefault) {
T result = get(name);
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 dca7c832e3fa..39f043d95f90 100644
--- a/solr/core/src/java/org/apache/solr/core/RequestHandlers.java
+++ b/solr/core/src/java/org/apache/solr/core/RequestHandlers.java
@@ -117,7 +117,7 @@ void initHandlersFromConfig(SolrConfig config) {
modifiedInfos.add(applyInitParams(config, info));
}
handlers.init(Collections.emptyMap(), core, modifiedInfos);
- handlers.alias(handlers.getDefault(), "");
+
if (log.isDebugEnabled()) {
log.debug("Registered paths: {}", StrUtils.join(new ArrayList<>(handlers.keySet()), ','));
}
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 c3aac28380e7..c936dda686f1 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrCore.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java
@@ -3131,14 +3131,11 @@ private void initWriters() {
// Initialize with the built defaults
responseWriters.init(defaultWriters, this);
-
- // configure the default response writer; this one should never be null
- if (responseWriters.getDefault() == null) responseWriters.setDefault("standard");
}
- /** Finds a writer by name, or returns the default writer if not found. */
+ /** Finds a writer by name, or null if not found. */
public final QueryResponseWriter getQueryResponseWriter(String writerName) {
- return responseWriters.get(writerName, true);
+ return responseWriters.get(writerName, false);
}
/**
diff --git a/solr/core/src/java/org/apache/solr/request/SolrQueryRequest.java b/solr/core/src/java/org/apache/solr/request/SolrQueryRequest.java
index 0ce4d82e5516..c4aecf0c47ed 100644
--- a/solr/core/src/java/org/apache/solr/request/SolrQueryRequest.java
+++ b/solr/core/src/java/org/apache/solr/request/SolrQueryRequest.java
@@ -204,14 +204,7 @@ default CloudDescriptor getCloudDescriptor() {
*/
default QueryResponseWriter getResponseWriter() {
// it's weird this method is here instead of SolrQueryResponse, but it's practical/convenient
- SolrCore core = getCore();
- String wt = getParams().get(CommonParams.WT);
- // Use core writers if available, otherwise fall back to built-in writers
- if (core != null) {
- return core.getQueryResponseWriter(wt);
- } else {
- return ResponseWritersRegistry.getWriter(wt);
- }
+ return ResponseWritersRegistry.getWriter(getParams().get(CommonParams.WT), getCore());
}
/**
diff --git a/solr/core/src/java/org/apache/solr/response/RawResponseWriter.java b/solr/core/src/java/org/apache/solr/response/RawResponseWriter.java
index 922b49329525..fe5269497ff1 100644
--- a/solr/core/src/java/org/apache/solr/response/RawResponseWriter.java
+++ b/solr/core/src/java/org/apache/solr/response/RawResponseWriter.java
@@ -47,7 +47,7 @@ public class RawResponseWriter implements QueryResponseWriter {
*/
public static final String CONTENT = "content";
- private String _baseWriter = null;
+ private String baseWriter = null;
/**
* A fallback writer used for requests that don't return raw content and that aren't associated
@@ -62,14 +62,17 @@ public void init(NamedList> n) {
if (n != null) {
Object base = n.get("base");
if (base != null) {
- _baseWriter = base.toString();
+ baseWriter = base.toString();
}
}
}
protected QueryResponseWriter getBaseWriter(SolrQueryRequest request) {
if (request.getCore() != null) {
- return request.getCore().getQueryResponseWriter(_baseWriter);
+ // When baseWriter is null, use the core's default writer (useDefault=true)
+ // Otherwise, look up the specific writer by name (useDefault=false for explicit lookups)
+ boolean useDefault = (baseWriter == null);
+ return request.getCore().getResponseWriters().get(baseWriter, useDefault);
}
// Requests to a specific core already have writers, but we still need a 'default writer' for
diff --git a/solr/core/src/java/org/apache/solr/response/ResponseWritersRegistry.java b/solr/core/src/java/org/apache/solr/response/ResponseWritersRegistry.java
index f7d342bc2868..12d860cad394 100644
--- a/solr/core/src/java/org/apache/solr/response/ResponseWritersRegistry.java
+++ b/solr/core/src/java/org/apache/solr/response/ResponseWritersRegistry.java
@@ -20,7 +20,9 @@
import static org.apache.solr.util.stats.MetricUtils.PROMETHEUS_METRICS_WT;
import java.util.Map;
+import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.core.SolrCore;
import org.apache.solr.handler.admin.api.ReplicationAPIBase;
/**
@@ -46,21 +48,14 @@ private ResponseWritersRegistry() {
PrometheusResponseWriter prometheusWriter = new PrometheusResponseWriter();
BUILTIN_WRITERS =
- Map.of(
- CommonParams.JAVABIN,
- new JavaBinResponseWriter(),
- CommonParams.JSON,
- jsonWriter,
- "standard",
- jsonWriter, // Alias for JSON
- "xml",
- new XMLResponseWriter(),
- PROMETHEUS_METRICS_WT,
- prometheusWriter,
- OPEN_METRICS_WT,
- prometheusWriter,
- ReplicationAPIBase.FILE_STREAM,
- new FileStreamResponseWriter());
+ Map.ofEntries(
+ Map.entry(CommonParams.JAVABIN, new JavaBinResponseWriter()),
+ Map.entry(CommonParams.JSON, jsonWriter),
+ Map.entry("xml", new XMLResponseWriter()),
+ Map.entry("raw", new RawResponseWriter()),
+ Map.entry(PROMETHEUS_METRICS_WT, prometheusWriter),
+ Map.entry(OPEN_METRICS_WT, prometheusWriter),
+ Map.entry(ReplicationAPIBase.FILE_STREAM, new FileStreamResponseWriter()));
}
/**
@@ -69,17 +64,68 @@ private ResponseWritersRegistry() {
*
Built-in writers are always available and provide essential formats needed by admin APIs and
* core functionality. They do not depend on core configuration or ImplicitPlugins.json settings.
*
- *
If the requested writer is not available, returns the "standard" (JSON) writer as a
- * fallback. This ensures requests always get a valid response format.
+ *
If the requested writer is not available, returns the JSON writer as a fallback. This
+ * ensures requests always get a valid response format.
*
* @param writerName the writer name (e.g., "json", "xml", "javabin"), or null for default
- * @return the response writer, never null (returns "standard"/JSON if not found)
+ * @return the response writer, never null (returns JSON if not found)
*/
public static QueryResponseWriter getWriter(String writerName) {
if (writerName == null || writerName.isEmpty()) {
- return BUILTIN_WRITERS.get("standard");
+ writerName = CommonParams.JSON;
}
- return BUILTIN_WRITERS.getOrDefault(writerName, BUILTIN_WRITERS.get("standard"));
+ return BUILTIN_WRITERS.get(writerName);
+ }
+
+ /**
+ * Gets a response writer, trying the core's registry first, then falling back to built-in
+ * writers. This is the unified entry point for all writer resolution.
+ *
+ *
Resolution order:
+ *
+ *
+ *
If core is provided, check core's writer registry
+ *
If not found in core (or no core), check built-in writers
+ *
If writer name is explicitly specified but not found anywhere, throw exception
+ *
If writer name is null/empty, return default (JSON)
+ *
+ *
+ * @param writerName the writer name (e.g., "json", "xml", "javabin"), or null for default
+ * @param core the SolrCore to check first, or null for node-level requests
+ * @return the response writer, never null
+ * @throws SolrException if an explicitly requested writer type is not found
+ */
+ public static QueryResponseWriter getWriter(String writerName, SolrCore core) {
+ QueryResponseWriter writer = null;
+
+ // Try core registry first if available
+ if (core != null) {
+ writer = core.getQueryResponseWriter(writerName);
+ }
+
+ // If not found and writer is explicitly requested, validate it exists in built-in
+ if (writer == null) {
+ if (!hasWriter(writerName)) {
+ throw new SolrException(
+ SolrException.ErrorCode.SERVER_ERROR, "Unknown response writer type: " + writerName);
+ } else {
+ writer = getWriter(writerName);
+ }
+ }
+ return writer;
+ }
+
+ /**
+ * Checks if a writer with the given name exists in the built-in writers.
+ *
+ * @param writerName the writer name to check
+ * @return true if the writer exists, false otherwise
+ */
+ public static boolean hasWriter(String writerName) {
+ if (writerName == null || writerName.isEmpty()) {
+ return true; // null/empty is valid, will use default
+ }
+ return BUILTIN_WRITERS.containsKey(writerName);
}
/**
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 1229aed8d0a0..b73da05e0bfd 100644
--- a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
+++ b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
@@ -727,7 +727,7 @@ private void handleAdminRequest() throws IOException {
protected void logAndFlushAdminRequest(SolrQueryResponse solrResp) throws IOException {
if (solrResp.getToLog().size() > 0) {
- // has to come second and in it's own if to keep ./gradlew check happy.
+ // has to come second and in its own "if" to keep ./gradlew check happy.
if (log.isInfoEnabled()) {
log.info(
handler != null
diff --git a/solr/core/src/java/org/apache/solr/servlet/SolrRequestParsers.java b/solr/core/src/java/org/apache/solr/servlet/SolrRequestParsers.java
index 3cf12aa7982b..e835d844db86 100644
--- a/solr/core/src/java/org/apache/solr/servlet/SolrRequestParsers.java
+++ b/solr/core/src/java/org/apache/solr/servlet/SolrRequestParsers.java
@@ -67,13 +67,6 @@ public class SolrRequestParsers {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
- // Should these constants be in a more public place?
- public static final String MULTIPART = "multipart";
- public static final String FORMDATA = "formdata";
- public static final String RAW = "raw";
- public static final String SIMPLE = "simple";
- public static final String STANDARD = "standard";
-
private static final Charset CHARSET_US_ASCII = StandardCharsets.US_ASCII;
public static final String INPUT_ENCODING_KEY = "ie";
@@ -81,8 +74,7 @@ public class SolrRequestParsers {
public static final String REQUEST_TIMER_SERVLET_ATTRIBUTE = "org.apache.solr.RequestTimer";
- private final HashMap parsers = new HashMap<>();
- private StandardRequestParser standard;
+ private StandardRequestParser parser;
/**
* Default instance for e.g. admin requests. Limits to 2 MB uploads and does not allow remote
@@ -91,7 +83,7 @@ public class SolrRequestParsers {
public static final SolrRequestParsers DEFAULT = new SolrRequestParsers();
/**
- * Pass in an xml configuration. A null configuration will enable everything with maximum values.
+ * Pass in a xml configuration. A null configuration will enable everything with maximum values.
*/
public SolrRequestParsers(SolrConfig globalConfig) {
final int multipartUploadLimitKB, formUploadLimitKB;
@@ -116,21 +108,12 @@ private void init(int multipartUploadLimitKB, int formUploadLimitKB) {
MultipartRequestParser multi = new MultipartRequestParser(multipartUploadLimitKB);
RawRequestParser raw = new RawRequestParser();
FormDataRequestParser formdata = new FormDataRequestParser(formUploadLimitKB);
- standard = new StandardRequestParser(multi, raw, formdata);
-
- // I don't see a need to have this publicly configured just yet
- // adding it is trivial
- parsers.put(MULTIPART, multi);
- parsers.put(FORMDATA, formdata);
- parsers.put(RAW, raw);
- parsers.put(SIMPLE, new SimpleRequestParser());
- parsers.put(STANDARD, standard);
- parsers.put("", standard);
+ parser = new StandardRequestParser(multi, raw, formdata);
}
private static RTimerTree getRequestTimer(HttpServletRequest req) {
final Object reqTimer = req.getAttribute(REQUEST_TIMER_SERVLET_ATTRIBUTE);
- if (reqTimer != null && reqTimer instanceof RTimerTree) {
+ if (reqTimer instanceof RTimerTree) {
return ((RTimerTree) reqTimer);
}
@@ -139,7 +122,6 @@ private static RTimerTree getRequestTimer(HttpServletRequest req) {
public SolrQueryRequest parse(SolrCore core, String path, HttpServletRequest req)
throws Exception {
- SolrRequestParser parser = standard;
// TODO -- in the future, we could pick a different parser based on the request
@@ -164,13 +146,12 @@ public SolrQueryRequest parse(SolrCore core, String path, HttpServletRequest req
/** For embedded Solr use; not related to HTTP. */
public SolrQueryRequest buildRequestFrom(
- SolrCore core, SolrParams params, Collection streams) throws Exception {
+ SolrCore core, SolrParams params, Collection streams) {
return buildRequestFrom(core, params, streams, new RTimerTree(), null, null);
}
public SolrQueryRequest buildRequestFrom(
- SolrCore core, SolrParams params, Collection streams, Principal principal)
- throws Exception {
+ SolrCore core, SolrParams params, Collection streams, Principal principal) {
return buildRequestFrom(core, params, streams, new RTimerTree(), null, principal);
}
@@ -181,7 +162,7 @@ private SolrQueryRequest buildRequestFrom(
RTimerTree requestTimer,
final HttpServletRequest req,
final Principal principal) // from req, if req was provided, otherwise from elsewhere
- throws Exception {
+ {
// ensure streams is non-null and mutable so we can easily add to it
if (streams == null) {
streams = new ArrayList<>();
@@ -213,7 +194,7 @@ public List getCommands(boolean validateInput) {
@Override
public Map getPathTemplateValues() {
- if (httpSolrCall != null && httpSolrCall instanceof V2HttpCall) {
+ if (httpSolrCall instanceof V2HttpCall) {
return ((V2HttpCall) httpSolrCall).getUrlParts();
}
return super.getPathTemplateValues();
@@ -337,9 +318,9 @@ static long parseFormDataContent(
// we have no charset decoder until now, buffer the keys / values for later
// processing:
buffer.add(keyBytes);
- buffer.add(Long.valueOf(keyPos));
+ buffer.add(keyPos);
buffer.add(valueBytes);
- buffer.add(Long.valueOf(valuePos));
+ buffer.add(valuePos);
} else {
// we already have a charsetDecoder, so we can directly decode without buffering:
final String key = decodeChars(keyBytes, keyPos, charsetDecoder),
@@ -457,7 +438,7 @@ private static int digit16(int b) {
// -----------------------------------------------------------------
// -----------------------------------------------------------------
- // I guess we don't really even need the interface, but i'll keep it here just for kicks
+ // I guess we don't really even need the interface, but I'll keep it here just for kicks
interface SolrRequestParser {
public SolrParams parseParamsAndFillStreams(
final HttpServletRequest req, ArrayList streams) throws Exception;
@@ -466,15 +447,6 @@ public SolrParams parseParamsAndFillStreams(
// -----------------------------------------------------------------
// -----------------------------------------------------------------
- /** The simple parser just uses the params directly, does not support POST URL-encoded forms */
- static class SimpleRequestParser implements SolrRequestParser {
- @Override
- public SolrParams parseParamsAndFillStreams(
- final HttpServletRequest req, ArrayList streams) throws Exception {
- return parseQueryString(req.getQueryString());
- }
- }
-
/** Wrap an HttpServletRequest as a ContentStream */
static class HttpRequestContentStream extends ContentStreamBase {
private final InputStream inputStream;
@@ -515,7 +487,7 @@ public SolrParams parseParamsAndFillStreams(
|| req.getHeader("Transfer-Encoding") != null
|| !NO_BODY_METHODS.contains(req.getMethod())) {
// If Content-Length > 0 OR Transfer-Encoding exists OR
- // it's a method that can have a body (POST/PUT/PATCH etc)
+ // it's a method that can have a body (POST/PUT/PATCH etc.)
streams.add(new HttpRequestContentStream(req, req.getInputStream()));
}
@@ -543,7 +515,7 @@ public SolrParams parseParamsAndFillStreams(
throw new SolrException(
ErrorCode.BAD_REQUEST, "Not multipart content! " + req.getContentType());
}
- // Magic way to tell Jetty dynamically we want multi-part processing.
+ // Magic way to tell Jetty dynamically we want multipart processing.
// This is taken from:
// https://github.com/eclipse/jetty.project/blob/jetty-10.0.12/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java#L144
req.setAttribute("org.eclipse.jetty.multipartConfig", multipartConfigElement);
@@ -735,7 +707,7 @@ static class StandardRequestParser implements SolrRequestParser {
public SolrParams parseParamsAndFillStreams(
final HttpServletRequest req, ArrayList streams) throws Exception {
String contentType = req.getContentType();
- String method = req.getMethod(); // No need to uppercase... HTTP verbs are case sensitive
+ String method = req.getMethod(); // No need to uppercase... HTTP verbs are case-sensitive
String uri = req.getRequestURI();
boolean isV2 = getHttpSolrCall(req) instanceof V2HttpCall;
boolean isPost = "POST".equals(method);
diff --git a/solr/core/src/test/org/apache/solr/OutputWriterTest.java b/solr/core/src/test/org/apache/solr/OutputWriterTest.java
index 30df9ed98fab..7f711631ce17 100644
--- a/solr/core/src/test/org/apache/solr/OutputWriterTest.java
+++ b/solr/core/src/test/org/apache/solr/OutputWriterTest.java
@@ -29,32 +29,16 @@
/** Tests the ability to configure multiple query output writers, and select those at query time. */
public class OutputWriterTest extends SolrTestCaseJ4 {
- /** The XML string that's output for testing purposes. */
- public static final String USELESS_OUTPUT = "useless output";
-
@BeforeClass
public static void beforeClass() throws Exception {
initCore("solr/crazy-path-to-config.xml", "solr/crazy-path-to-schema.xml");
}
- /**
- * responseHeader has changed in SOLR-59, check old and new variants, In SOLR-2413, we removed
- * support for the deprecated versions
- */
- @Test
- public void testSOLR59responseHeaderVersions() {
- // default results in "new" responseHeader
- lrf.args.put("wt", "standard");
- assertQ(req("foo"), "/response/lst[@name='responseHeader']/int[@name='status'][.='0']");
- lrf.args.remove("wt");
- assertQ(req("foo"), "/response/lst[@name='responseHeader']/int[@name='QTime']");
- }
-
@Test
public void testUselessWriter() throws Exception {
lrf.args.put("wt", "useless");
String out = h.query(req("foo"));
- assertEquals(USELESS_OUTPUT, out);
+ assertEquals(UselessOutputWriter.USELESS_OUTPUT, out);
}
public void testLazy() {
@@ -71,6 +55,9 @@ public void testLazy() {
/** An output writer that doesn't do anything useful. */
public static class UselessOutputWriter implements TextQueryResponseWriter {
+ /** The XML string that's output for testing purposes. */
+ public static final String USELESS_OUTPUT = "useless output";
+
public UselessOutputWriter() {}
@Override
diff --git a/solr/core/src/test/org/apache/solr/handler/V2ApiIntegrationTest.java b/solr/core/src/test/org/apache/solr/handler/V2ApiIntegrationTest.java
index 3b7a85eb0967..f58e8994107f 100644
--- a/solr/core/src/test/org/apache/solr/handler/V2ApiIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/V2ApiIntegrationTest.java
@@ -48,7 +48,7 @@
import org.junit.Test;
public class V2ApiIntegrationTest extends SolrCloudTestCase {
- private static String COLL_NAME = "collection1";
+ private static final String COLL_NAME = "collection1";
@BeforeClass
public static void createCluster() throws Exception {
@@ -80,11 +80,7 @@ private void testException(
.build();
v2Request.setResponseParser(responseParser);
RemoteSolrException ex =
- expectThrows(
- RemoteSolrException.class,
- () -> {
- v2Request.process(cluster.getSolrClient());
- });
+ expectThrows(RemoteSolrException.class, () -> v2Request.process(cluster.getSolrClient()));
assertEquals(expectedCode, ex.code());
}
@@ -114,26 +110,27 @@ public void testIntrospect() throws Exception {
}
@Test
- public void testWTParam() throws Exception {
+ public void testInvalidWTParamReturnsError() throws Exception {
V2Request request = new V2Request.Builder("/c/" + COLL_NAME + "/get/_introspect").build();
- // TODO: If possible do this in a better way
+ // Using an invalid wt parameter should return a 500 error
request.setResponseParser(new InputStreamResponseParser("bleh"));
NamedList