From fb4ec47727639084a786ecb1446d55d32e9c5df8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Fri, 3 Jul 2026 13:20:10 +0200 Subject: [PATCH 1/5] Modernize String-based charset APIs to java.nio.charset.Charset Replace charset-name String overloads (which throw the checked UnsupportedEncodingException) with Charset overloads, as flagged by Error Prone's JdkObsolete pattern. Charset.forName() throws unchecked IllegalArgumentException, so where the charset name comes from a client-supplied Content-Type header, the new ContentStreamBase.charsetForName() converts it back to UnsupportedEncodingException, preserving the previous contract that an invalid charset is handled as an I/O error. Dead UnsupportedEncodingException handling for compile-time-constant charsets is removed. --- .../DefaultSampleDocumentsLoader.java | 5 +-- .../apache/solr/handler/loader/XMLLoader.java | 7 +++- .../response/TextQueryResponseWriter.java | 2 +- .../solr/spelling/FileBasedSpellChecker.java | 4 ++- .../TestManagedSynonymGraphFilterFactory.java | 12 ++++--- .../solr/crossdc/common/ConfUtilTest.java | 7 ++-- .../client/solrj/impl/HttpSolrClient.java | 6 ++-- .../json/JacksonDataBindResponseParser.java | 5 ++- .../response/json/JsonMapResponseParser.java | 8 ++++- .../apache/solr/common/params/SolrParams.java | 31 +++++++--------- .../solr/common/util/ContentStreamBase.java | 35 +++++++++++++------ 11 files changed, 78 insertions(+), 44 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/handler/designer/DefaultSampleDocumentsLoader.java b/solr/core/src/java/org/apache/solr/handler/designer/DefaultSampleDocumentsLoader.java index 785be168ca93..0b0251154f13 100644 --- a/solr/core/src/java/org/apache/solr/handler/designer/DefaultSampleDocumentsLoader.java +++ b/solr/core/src/java/org/apache/solr/handler/designer/DefaultSampleDocumentsLoader.java @@ -139,7 +139,7 @@ protected List loadCsvDocs( throws IOException { ContentStream stream; if (params.get(SEPARATOR) == null) { - String csvStr = new String(streamBytes, charset); + String csvStr = new String(streamBytes, ContentStreamBase.charsetForName(charset)); char sep = detectTSV(csvStr); ModifiableSolrParams modifiableSolrParams = new ModifiableSolrParams(params); modifiableSolrParams.set(SEPARATOR, String.valueOf(sep)); @@ -198,7 +198,8 @@ protected List loadJsonDocs( String charset = ContentStreamBase.getCharsetFromContentType(stream.getContentType()); String jsonStr = new String( - readAllBytes(stream), charset != null ? charset : ContentStreamBase.DEFAULT_CHARSET); + readAllBytes(stream), + charset != null ? ContentStreamBase.charsetForName(charset) : StandardCharsets.UTF_8); String[] lines = jsonStr.split("\n"); if (lines.length > 1) { for (String line : lines) { diff --git a/solr/core/src/java/org/apache/solr/handler/loader/XMLLoader.java b/solr/core/src/java/org/apache/solr/handler/loader/XMLLoader.java index 72d93f87b78d..0a561d805dd4 100644 --- a/solr/core/src/java/org/apache/solr/handler/loader/XMLLoader.java +++ b/solr/core/src/java/org/apache/solr/handler/loader/XMLLoader.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.io.InputStream; import java.lang.invoke.MethodHandles; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -134,7 +135,11 @@ private InputStream getStream(ContentStream cs, String charset) throws IOExcepti if (log.isTraceEnabled()) { log.trace( "body: {}", - new String(body, (charset == null) ? ContentStreamBase.DEFAULT_CHARSET : charset)); + new String( + body, + (charset == null) + ? StandardCharsets.UTF_8 + : ContentStreamBase.charsetForName(charset))); } return new ByteArrayInputStream(body); } diff --git a/solr/core/src/java/org/apache/solr/response/TextQueryResponseWriter.java b/solr/core/src/java/org/apache/solr/response/TextQueryResponseWriter.java index f4df38f830d9..71b67d4b3b41 100644 --- a/solr/core/src/java/org/apache/solr/response/TextQueryResponseWriter.java +++ b/solr/core/src/java/org/apache/solr/response/TextQueryResponseWriter.java @@ -70,7 +70,7 @@ private static Writer buildWriter(OutputStream outputStream, String charset) Writer writer = (charset == null) ? new OutputStreamWriter(outputStream, StandardCharsets.UTF_8) - : new OutputStreamWriter(outputStream, charset); + : new OutputStreamWriter(outputStream, ContentStreamBase.charsetForName(charset)); return new FastWriter(writer); // note: buffered; therefore we need to call flush() } diff --git a/solr/core/src/java/org/apache/solr/spelling/FileBasedSpellChecker.java b/solr/core/src/java/org/apache/solr/spelling/FileBasedSpellChecker.java index 2300f0a8dca4..aeedb5145fb9 100644 --- a/solr/core/src/java/org/apache/solr/spelling/FileBasedSpellChecker.java +++ b/solr/core/src/java/org/apache/solr/spelling/FileBasedSpellChecker.java @@ -33,6 +33,7 @@ import org.apache.lucene.search.spell.PlainTextDictionary; import org.apache.lucene.store.ByteBuffersDirectory; import org.apache.lucene.store.Directory; +import org.apache.solr.common.util.ContentStreamBase; import org.apache.solr.common.util.NamedList; import org.apache.solr.core.SolrCore; import org.apache.solr.schema.FieldType; @@ -122,7 +123,8 @@ private void loadExternalFileDictionary(SolrCore core, SolrIndexSearcher searche dictionary = new PlainTextDictionary( new InputStreamReader( - core.getResourceLoader().openResource(sourceLocation), characterEncoding)); + core.getResourceLoader().openResource(sourceLocation), + ContentStreamBase.charsetForName(characterEncoding))); } } diff --git a/solr/core/src/test/org/apache/solr/rest/schema/analysis/TestManagedSynonymGraphFilterFactory.java b/solr/core/src/test/org/apache/solr/rest/schema/analysis/TestManagedSynonymGraphFilterFactory.java index 9bb95fc52a5a..dc58da73485f 100644 --- a/solr/core/src/test/org/apache/solr/rest/schema/analysis/TestManagedSynonymGraphFilterFactory.java +++ b/solr/core/src/test/org/apache/solr/rest/schema/analysis/TestManagedSynonymGraphFilterFactory.java @@ -20,6 +20,7 @@ import static org.apache.solr.common.util.Utils.toJSONString; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; @@ -150,7 +151,10 @@ public void testManagedSynonyms() throws Exception { "/response/result[@name='response'][@numFound='1']", "/response/result[@name='response']/doc/str[@name='id'][.='5150']"); assertQ( - "/select?q=" + newFieldName + ":" + URLEncoder.encode(multiTermOrigin, "UTF-8"), + "/select?q=" + + newFieldName + + ":" + + URLEncoder.encode(multiTermOrigin, StandardCharsets.UTF_8), "/response/lst[@name='responseHeader']/int[@name='status'] = '0'", "/response/result[@name='response'][@numFound='1']", "/response/result[@name='response']/doc/str[@name='id'][.='040']"); @@ -168,7 +172,7 @@ public void testManagedSynonyms() throws Exception { assertJPut(endpoint, toJSONString(syns), "/responseHeader/status==0"); assertJQ( - endpoint + "/" + URLEncoder.encode(multiTermSynonym, "UTF-8"), + endpoint + "/" + URLEncoder.encode(multiTermSynonym, StandardCharsets.UTF_8), "/" + multiTermSynonym + "==['" + multiTermOrigin + "']"); // should not match as the synonym mapping between mad and angry does not @@ -184,7 +188,7 @@ public void testManagedSynonyms() throws Exception { "/select?q=" + newFieldName + ":(" - + URLEncoder.encode(multiTermSynonym, "UTF-8") + + URLEncoder.encode(multiTermSynonym, StandardCharsets.UTF_8) + ")&sow=false", "/response/lst[@name='responseHeader']/int[@name='status'] = '0'", "/response/result[@name='response'][@numFound='0']"); @@ -203,7 +207,7 @@ public void testManagedSynonyms() throws Exception { "/select?q=" + newFieldName + ":(" - + URLEncoder.encode(multiTermSynonym, "UTF-8") + + URLEncoder.encode(multiTermSynonym, StandardCharsets.UTF_8) + ")&sow=false", "/response/lst[@name='responseHeader']/int[@name='status'] = '0'", "/response/result[@name='response'][@numFound='1']", diff --git a/solr/modules/cross-dc/src/test/org/apache/solr/crossdc/common/ConfUtilTest.java b/solr/modules/cross-dc/src/test/org/apache/solr/crossdc/common/ConfUtilTest.java index 49de459b288a..28a98d6d7a1e 100644 --- a/solr/modules/cross-dc/src/test/org/apache/solr/crossdc/common/ConfUtilTest.java +++ b/solr/modules/cross-dc/src/test/org/apache/solr/crossdc/common/ConfUtilTest.java @@ -22,6 +22,7 @@ import java.io.ByteArrayOutputStream; import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.Properties; @@ -129,7 +130,7 @@ public void testFillProperties_FromZooKeeper() throws Exception { zkProps.setProperty("custom.zk.property", "zk-value"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStreamWriter writer = new OutputStreamWriter(baos, "UTF-8"); + OutputStreamWriter writer = new OutputStreamWriter(baos, StandardCharsets.UTF_8); zkProps.store(writer, null); writer.close(); byte[] zkData = baos.toByteArray(); @@ -155,7 +156,7 @@ public void testFillProperties_PriorityOrder() throws Exception { zkProps.setProperty(KafkaCrossDcConf.TOPIC_NAME, "zk-topic"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStreamWriter writer = new OutputStreamWriter(baos, "UTF-8"); + OutputStreamWriter writer = new OutputStreamWriter(baos, StandardCharsets.UTF_8); zkProps.store(writer, null); writer.close(); byte[] zkData = baos.toByteArray(); @@ -318,7 +319,7 @@ public void testFillProperties_ComplexScenario() throws Exception { zkProps.setProperty("zk.only.property", "zk-value"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStreamWriter writer = new OutputStreamWriter(baos, "UTF-8"); + OutputStreamWriter writer = new OutputStreamWriter(baos, StandardCharsets.UTF_8); zkProps.store(writer, null); writer.close(); byte[] zkData = baos.toByteArray(); diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClient.java index bcf5eb6b3c17..fccc4357fcde 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClient.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClient.java @@ -290,9 +290,11 @@ private void checkContentType( try { ByteArrayOutputStream body = new ByteArrayOutputStream(); is.transferTo(body); + Charset charset = encoding != null ? Charset.forName(encoding) : FALLBACK_CHARSET; throw new RemoteSolrException( - urlExceptionMessage, httpStatus, prefix + body.toString(exceptionEncoding), null); - } catch (IOException e) { + urlExceptionMessage, httpStatus, prefix + body.toString(charset), null); + // IllegalArgumentException covers an unsupported/invalid charset name from the response + } catch (IOException | IllegalArgumentException e) { throw new RemoteSolrException( urlExceptionMessage, httpStatus, diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/response/json/JacksonDataBindResponseParser.java b/solr/solrj/src/java/org/apache/solr/client/solrj/response/json/JacksonDataBindResponseParser.java index a09ac3d2155a..b244b295afb5 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/response/json/JacksonDataBindResponseParser.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/response/json/JacksonDataBindResponseParser.java @@ -25,6 +25,7 @@ import org.apache.solr.client.api.model.SolrJerseyResponse; import org.apache.solr.client.solrj.request.json.JacksonContentWriter; import org.apache.solr.client.solrj.response.ResponseParser; +import org.apache.solr.common.util.ContentStreamBase; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SimpleOrderedMap; @@ -68,7 +69,9 @@ public NamedList processResponse(InputStream stream, String encoding) th if (encoding == null) { parsedVal = mapper.readValue(stream, typeParam); } else { - parsedVal = mapper.readValue(new InputStreamReader(stream, encoding), typeParam); + parsedVal = + mapper.readValue( + new InputStreamReader(stream, ContentStreamBase.charsetForName(encoding)), typeParam); } final var result = new SimpleOrderedMap(); diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/response/json/JsonMapResponseParser.java b/solr/solrj/src/java/org/apache/solr/client/solrj/response/json/JsonMapResponseParser.java index e1848ba44ff0..8a0d6f271149 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/response/json/JsonMapResponseParser.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/response/json/JsonMapResponseParser.java @@ -20,10 +20,12 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.Set; import org.apache.solr.client.solrj.response.ResponseParser; import org.apache.solr.common.SolrException; +import org.apache.solr.common.util.ContentStreamBase; import org.apache.solr.common.util.NamedList; import org.noggit.JSONParser; import org.noggit.ObjectBuilder; @@ -43,7 +45,11 @@ public NamedList processResponse(InputStream body, String encoding) thro @SuppressWarnings({"rawtypes"}) Map map = null; try (InputStreamReader reader = - new InputStreamReader(body, encoding == null ? "UTF-8" : encoding)) { + new InputStreamReader( + body, + encoding == null + ? StandardCharsets.UTF_8 + : ContentStreamBase.charsetForName(encoding))) { ObjectBuilder builder = new ObjectBuilder(new JSONParser(reader)); map = (Map) builder.getObject(); } catch (JSONParser.ParseException e) { diff --git a/solr/solrj/src/java/org/apache/solr/common/params/SolrParams.java b/solr/solrj/src/java/org/apache/solr/common/params/SolrParams.java index ec414d763134..e06cc4cd7cb3 100644 --- a/solr/solrj/src/java/org/apache/solr/common/params/SolrParams.java +++ b/solr/solrj/src/java/org/apache/solr/common/params/SolrParams.java @@ -18,8 +18,8 @@ import java.io.IOException; import java.io.Serializable; -import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Iterator; @@ -458,25 +458,20 @@ public NamedList toNamedList() { * empty. */ public String toQueryString() { - try { - final String charset = StandardCharsets.UTF_8.name(); - final StringBuilder sb = new StringBuilder(128); - boolean first = true; - for (final Iterator it = getParameterNamesIterator(); it.hasNext(); ) { - final String name = it.next(), nameEnc = URLEncoder.encode(name, charset); - for (String val : getParams(name)) { - sb.append(first ? '?' : '&') - .append(nameEnc) - .append('=') - .append(URLEncoder.encode(val, charset)); - first = false; - } + final Charset charset = StandardCharsets.UTF_8; + final StringBuilder sb = new StringBuilder(128); + boolean first = true; + for (final Iterator it = getParameterNamesIterator(); it.hasNext(); ) { + final String name = it.next(), nameEnc = URLEncoder.encode(name, charset); + for (String val : getParams(name)) { + sb.append(first ? '?' : '&') + .append(nameEnc) + .append('=') + .append(URLEncoder.encode(val, charset)); + first = false; } - return sb.toString(); - } catch (UnsupportedEncodingException e) { - // impossible! - throw new AssertionError(e); } + return sb.toString(); } /** diff --git a/solr/solrj/src/java/org/apache/solr/common/util/ContentStreamBase.java b/solr/solrj/src/java/org/apache/solr/common/util/ContentStreamBase.java index ee46e6ddded5..e2a96b70029f 100644 --- a/solr/solrj/src/java/org/apache/solr/common/util/ContentStreamBase.java +++ b/solr/solrj/src/java/org/apache/solr/common/util/ContentStreamBase.java @@ -27,6 +27,7 @@ import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLConnection; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -77,6 +78,23 @@ public static String getCharsetFromContentType(String contentType) { return null; } + /** + * Resolves a charset name (typically from a Content-Type header) to a {@link Charset}. Unlike + * {@link Charset#forName(String)}, an illegal or unsupported name results in a checked {@link + * UnsupportedEncodingException}, matching the behavior of the legacy {@code String}-based JDK + * charset APIs, so callers can treat a bad charset as an I/O error. + */ + public static Charset charsetForName(String charsetName) throws UnsupportedEncodingException { + try { + return Charset.forName(charsetName); + } catch (IllegalArgumentException e) { + UnsupportedEncodingException uee = + new UnsupportedEncodingException("Unsupported charset: " + charsetName); + uee.initCause(e); + throw uee; + } + } + protected String attemptToDetermineContentType() { String type = null; if (name != null) { @@ -214,12 +232,7 @@ public StringStream(String str, String contentType) { this.str = str; this.contentType = contentType; name = null; - try { - size = (long) str.getBytes(DEFAULT_CHARSET).length; - } catch (UnsupportedEncodingException e) { - // won't happen - throw new RuntimeException(e); - } + size = (long) str.getBytes(StandardCharsets.UTF_8).length; sourceInfo = "string"; } @@ -249,14 +262,16 @@ public static String detect(String str) { @Override public InputStream getStream() throws IOException { - return new ByteArrayInputStream(str.getBytes(DEFAULT_CHARSET)); + return new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8)); } /** If a charset is defined (by the contentType) use that, otherwise use a StringReader */ @Override public Reader getReader() throws IOException { String charset = getCharsetFromContentType(contentType); - return charset == null ? new StringReader(str) : new InputStreamReader(getStream(), charset); + return charset == null + ? new StringReader(str) + : new InputStreamReader(getStream(), charsetForName(charset)); } } @@ -268,8 +283,8 @@ public Reader getReader() throws IOException { public Reader getReader() throws IOException { String charset = getCharsetFromContentType(getContentType()); return charset == null - ? new InputStreamReader(getStream(), DEFAULT_CHARSET) - : new InputStreamReader(getStream(), charset); + ? new InputStreamReader(getStream(), StandardCharsets.UTF_8) + : new InputStreamReader(getStream(), charsetForName(charset)); } // ------------------------------------------------------------------ From ec6fecf1adec432ccc24955735d031e2548c8c92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Fri, 3 Jul 2026 13:20:48 +0200 Subject: [PATCH 2/5] Add changelog entry --- .../unreleased/PR#4606-charset-api-modernization.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 changelog/unreleased/PR#4606-charset-api-modernization.yml diff --git a/changelog/unreleased/PR#4606-charset-api-modernization.yml b/changelog/unreleased/PR#4606-charset-api-modernization.yml new file mode 100644 index 000000000000..79b724b022df --- /dev/null +++ b/changelog/unreleased/PR#4606-charset-api-modernization.yml @@ -0,0 +1,8 @@ +# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc +title: Modernize String-based charset APIs to java.nio.charset.Charset; invalid client-supplied charset names are still handled as I/O errors +type: other +authors: + - name: Jan Høydahl +links: +- name: PR#4606 + url: https://github.com/apache/solr/pull/4606 From 6ee6a608a7f0798d33b2351d40ab811a5a321f79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Fri, 3 Jul 2026 13:36:30 +0200 Subject: [PATCH 3/5] Use "fixed" as changelog type --- changelog/unreleased/PR#4606-charset-api-modernization.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/unreleased/PR#4606-charset-api-modernization.yml b/changelog/unreleased/PR#4606-charset-api-modernization.yml index 79b724b022df..f106fca69bd7 100644 --- a/changelog/unreleased/PR#4606-charset-api-modernization.yml +++ b/changelog/unreleased/PR#4606-charset-api-modernization.yml @@ -1,6 +1,6 @@ # See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc title: Modernize String-based charset APIs to java.nio.charset.Charset; invalid client-supplied charset names are still handled as I/O errors -type: other +type: fixed authors: - name: Jan Høydahl links: From 8126340559582e4fb5441bdf191ca71fdfe65007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Fri, 3 Jul 2026 16:59:11 +0200 Subject: [PATCH 4/5] other --- changelog/unreleased/PR#4606-charset-api-modernization.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/unreleased/PR#4606-charset-api-modernization.yml b/changelog/unreleased/PR#4606-charset-api-modernization.yml index f106fca69bd7..79b724b022df 100644 --- a/changelog/unreleased/PR#4606-charset-api-modernization.yml +++ b/changelog/unreleased/PR#4606-charset-api-modernization.yml @@ -1,6 +1,6 @@ # See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc title: Modernize String-based charset APIs to java.nio.charset.Charset; invalid client-supplied charset names are still handled as I/O errors -type: fixed +type: other authors: - name: Jan Høydahl links: From 0b08994b745cfd6bc9cdd18c1d0974b83c619f48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Fri, 3 Jul 2026 17:09:06 +0200 Subject: [PATCH 5/5] Moved utility method to IOUtils --- .../DefaultSampleDocumentsLoader.java | 5 ++-- .../apache/solr/handler/loader/XMLLoader.java | 5 ++-- .../response/TextQueryResponseWriter.java | 3 ++- .../solr/spelling/FileBasedSpellChecker.java | 4 ++-- .../json/JacksonDataBindResponseParser.java | 4 ++-- .../response/json/JsonMapResponseParser.java | 7 ++---- .../solr/common/util/ContentStreamBase.java | 23 ++----------------- .../org/apache/solr/common/util/IOUtils.java | 19 +++++++++++++++ 8 files changed, 34 insertions(+), 36 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/handler/designer/DefaultSampleDocumentsLoader.java b/solr/core/src/java/org/apache/solr/handler/designer/DefaultSampleDocumentsLoader.java index 0b0251154f13..fa796fc0709c 100644 --- a/solr/core/src/java/org/apache/solr/handler/designer/DefaultSampleDocumentsLoader.java +++ b/solr/core/src/java/org/apache/solr/handler/designer/DefaultSampleDocumentsLoader.java @@ -43,6 +43,7 @@ import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.ContentStream; import org.apache.solr.common.util.ContentStreamBase; +import org.apache.solr.common.util.IOUtils; import org.apache.solr.handler.loader.CSVLoaderBase; import org.apache.solr.handler.loader.JsonLoader; import org.apache.solr.handler.loader.XMLLoader; @@ -139,7 +140,7 @@ protected List loadCsvDocs( throws IOException { ContentStream stream; if (params.get(SEPARATOR) == null) { - String csvStr = new String(streamBytes, ContentStreamBase.charsetForName(charset)); + String csvStr = new String(streamBytes, IOUtils.charsetForName(charset)); char sep = detectTSV(csvStr); ModifiableSolrParams modifiableSolrParams = new ModifiableSolrParams(params); modifiableSolrParams.set(SEPARATOR, String.valueOf(sep)); @@ -199,7 +200,7 @@ protected List loadJsonDocs( String jsonStr = new String( readAllBytes(stream), - charset != null ? ContentStreamBase.charsetForName(charset) : StandardCharsets.UTF_8); + charset != null ? IOUtils.charsetForName(charset) : StandardCharsets.UTF_8); String[] lines = jsonStr.split("\n"); if (lines.length > 1) { for (String line : lines) { diff --git a/solr/core/src/java/org/apache/solr/handler/loader/XMLLoader.java b/solr/core/src/java/org/apache/solr/handler/loader/XMLLoader.java index 0a561d805dd4..a6490299d5ac 100644 --- a/solr/core/src/java/org/apache/solr/handler/loader/XMLLoader.java +++ b/solr/core/src/java/org/apache/solr/handler/loader/XMLLoader.java @@ -46,6 +46,7 @@ import org.apache.solr.common.util.CollectionUtil; import org.apache.solr.common.util.ContentStream; import org.apache.solr.common.util.ContentStreamBase; +import org.apache.solr.common.util.IOUtils; import org.apache.solr.common.util.StrUtils; import org.apache.solr.common.util.XMLErrorLogger; import org.apache.solr.handler.RequestHandlerUtils; @@ -137,9 +138,7 @@ private InputStream getStream(ContentStream cs, String charset) throws IOExcepti "body: {}", new String( body, - (charset == null) - ? StandardCharsets.UTF_8 - : ContentStreamBase.charsetForName(charset))); + (charset == null) ? StandardCharsets.UTF_8 : IOUtils.charsetForName(charset))); } return new ByteArrayInputStream(body); } diff --git a/solr/core/src/java/org/apache/solr/response/TextQueryResponseWriter.java b/solr/core/src/java/org/apache/solr/response/TextQueryResponseWriter.java index 71b67d4b3b41..34b464864a27 100644 --- a/solr/core/src/java/org/apache/solr/response/TextQueryResponseWriter.java +++ b/solr/core/src/java/org/apache/solr/response/TextQueryResponseWriter.java @@ -26,6 +26,7 @@ import java.nio.charset.StandardCharsets; import org.apache.solr.common.util.ContentStreamBase; import org.apache.solr.common.util.FastWriter; +import org.apache.solr.common.util.IOUtils; import org.apache.solr.request.SolrQueryRequest; /** A writer supporting character streams ({@link Writer} based). */ @@ -70,7 +71,7 @@ private static Writer buildWriter(OutputStream outputStream, String charset) Writer writer = (charset == null) ? new OutputStreamWriter(outputStream, StandardCharsets.UTF_8) - : new OutputStreamWriter(outputStream, ContentStreamBase.charsetForName(charset)); + : new OutputStreamWriter(outputStream, IOUtils.charsetForName(charset)); return new FastWriter(writer); // note: buffered; therefore we need to call flush() } diff --git a/solr/core/src/java/org/apache/solr/spelling/FileBasedSpellChecker.java b/solr/core/src/java/org/apache/solr/spelling/FileBasedSpellChecker.java index aeedb5145fb9..e3bf30127da1 100644 --- a/solr/core/src/java/org/apache/solr/spelling/FileBasedSpellChecker.java +++ b/solr/core/src/java/org/apache/solr/spelling/FileBasedSpellChecker.java @@ -33,7 +33,7 @@ import org.apache.lucene.search.spell.PlainTextDictionary; import org.apache.lucene.store.ByteBuffersDirectory; import org.apache.lucene.store.Directory; -import org.apache.solr.common.util.ContentStreamBase; +import org.apache.solr.common.util.IOUtils; import org.apache.solr.common.util.NamedList; import org.apache.solr.core.SolrCore; import org.apache.solr.schema.FieldType; @@ -124,7 +124,7 @@ private void loadExternalFileDictionary(SolrCore core, SolrIndexSearcher searche new PlainTextDictionary( new InputStreamReader( core.getResourceLoader().openResource(sourceLocation), - ContentStreamBase.charsetForName(characterEncoding))); + IOUtils.charsetForName(characterEncoding))); } } diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/response/json/JacksonDataBindResponseParser.java b/solr/solrj/src/java/org/apache/solr/client/solrj/response/json/JacksonDataBindResponseParser.java index b244b295afb5..9e91af8416b6 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/response/json/JacksonDataBindResponseParser.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/response/json/JacksonDataBindResponseParser.java @@ -25,7 +25,7 @@ import org.apache.solr.client.api.model.SolrJerseyResponse; import org.apache.solr.client.solrj.request.json.JacksonContentWriter; import org.apache.solr.client.solrj.response.ResponseParser; -import org.apache.solr.common.util.ContentStreamBase; +import org.apache.solr.common.util.IOUtils; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SimpleOrderedMap; @@ -71,7 +71,7 @@ public NamedList processResponse(InputStream stream, String encoding) th } else { parsedVal = mapper.readValue( - new InputStreamReader(stream, ContentStreamBase.charsetForName(encoding)), typeParam); + new InputStreamReader(stream, IOUtils.charsetForName(encoding)), typeParam); } final var result = new SimpleOrderedMap(); diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/response/json/JsonMapResponseParser.java b/solr/solrj/src/java/org/apache/solr/client/solrj/response/json/JsonMapResponseParser.java index 8a0d6f271149..2eb2d376e7ef 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/response/json/JsonMapResponseParser.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/response/json/JsonMapResponseParser.java @@ -25,7 +25,7 @@ import java.util.Set; import org.apache.solr.client.solrj.response.ResponseParser; import org.apache.solr.common.SolrException; -import org.apache.solr.common.util.ContentStreamBase; +import org.apache.solr.common.util.IOUtils; import org.apache.solr.common.util.NamedList; import org.noggit.JSONParser; import org.noggit.ObjectBuilder; @@ -46,10 +46,7 @@ public NamedList processResponse(InputStream body, String encoding) thro Map map = null; try (InputStreamReader reader = new InputStreamReader( - body, - encoding == null - ? StandardCharsets.UTF_8 - : ContentStreamBase.charsetForName(encoding))) { + body, encoding == null ? StandardCharsets.UTF_8 : IOUtils.charsetForName(encoding))) { ObjectBuilder builder = new ObjectBuilder(new JSONParser(reader)); map = (Map) builder.getObject(); } catch (JSONParser.ParseException e) { diff --git a/solr/solrj/src/java/org/apache/solr/common/util/ContentStreamBase.java b/solr/solrj/src/java/org/apache/solr/common/util/ContentStreamBase.java index e2a96b70029f..bd87e049b8a7 100644 --- a/solr/solrj/src/java/org/apache/solr/common/util/ContentStreamBase.java +++ b/solr/solrj/src/java/org/apache/solr/common/util/ContentStreamBase.java @@ -24,10 +24,8 @@ import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; -import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLConnection; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -78,23 +76,6 @@ public static String getCharsetFromContentType(String contentType) { return null; } - /** - * Resolves a charset name (typically from a Content-Type header) to a {@link Charset}. Unlike - * {@link Charset#forName(String)}, an illegal or unsupported name results in a checked {@link - * UnsupportedEncodingException}, matching the behavior of the legacy {@code String}-based JDK - * charset APIs, so callers can treat a bad charset as an I/O error. - */ - public static Charset charsetForName(String charsetName) throws UnsupportedEncodingException { - try { - return Charset.forName(charsetName); - } catch (IllegalArgumentException e) { - UnsupportedEncodingException uee = - new UnsupportedEncodingException("Unsupported charset: " + charsetName); - uee.initCause(e); - throw uee; - } - } - protected String attemptToDetermineContentType() { String type = null; if (name != null) { @@ -271,7 +252,7 @@ public Reader getReader() throws IOException { String charset = getCharsetFromContentType(contentType); return charset == null ? new StringReader(str) - : new InputStreamReader(getStream(), charsetForName(charset)); + : new InputStreamReader(getStream(), IOUtils.charsetForName(charset)); } } @@ -284,7 +265,7 @@ public Reader getReader() throws IOException { String charset = getCharsetFromContentType(getContentType()); return charset == null ? new InputStreamReader(getStream(), StandardCharsets.UTF_8) - : new InputStreamReader(getStream(), charsetForName(charset)); + : new InputStreamReader(getStream(), IOUtils.charsetForName(charset)); } // ------------------------------------------------------------------ diff --git a/solr/solrj/src/java/org/apache/solr/common/util/IOUtils.java b/solr/solrj/src/java/org/apache/solr/common/util/IOUtils.java index 93a2c5eb22ef..c1d1a58f736e 100644 --- a/solr/solrj/src/java/org/apache/solr/common/util/IOUtils.java +++ b/solr/solrj/src/java/org/apache/solr/common/util/IOUtils.java @@ -16,7 +16,9 @@ */ package org.apache.solr.common.util; +import java.io.UnsupportedEncodingException; import java.lang.invoke.MethodHandles; +import java.nio.charset.Charset; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,4 +39,21 @@ public static void closeQuietly(AutoCloseable closeable) { log.error("Error while closing", e); } } + + /** + * Resolves a charset name (typically from a Content-Type header) to a {@link Charset}. Unlike + * {@link Charset#forName(String)}, an illegal or unsupported name results in a checked {@link + * UnsupportedEncodingException}, matching the behavior of the legacy {@code String}-based JDK + * charset APIs, so callers can treat a bad charset as an I/O error. + */ + public static Charset charsetForName(String charsetName) throws UnsupportedEncodingException { + try { + return Charset.forName(charsetName); + } catch (IllegalArgumentException e) { + UnsupportedEncodingException uee = + new UnsupportedEncodingException("Unsupported charset: " + charsetName); + uee.initCause(e); + throw uee; + } + } }