diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 54b21307a4b..cf4cf82ed50 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -247,6 +247,10 @@ Bug Fixes this issue reverts the default replica placement policy to the 'legacy' assignment policy that was the default until Solr 7.4. (Gus Heck, Andrzej Bialecki, Bram Van Dam, shalin) +* SOLR-13255 : ClasscastException when URPs try to read a String field which returns a ByteArrayUTF8CHarSequence . This is a regression + in release 7.7 (noble) + + Improvements ---------------------- diff --git a/solr/contrib/langid/src/test/org/apache/solr/update/processor/LanguageIdentifierUpdateProcessorFactoryTestCase.java b/solr/contrib/langid/src/test/org/apache/solr/update/processor/LanguageIdentifierUpdateProcessorFactoryTestCase.java index 9fc3eb10b04..a74c80fea77 100644 --- a/solr/contrib/langid/src/test/org/apache/solr/update/processor/LanguageIdentifierUpdateProcessorFactoryTestCase.java +++ b/solr/contrib/langid/src/test/org/apache/solr/update/processor/LanguageIdentifierUpdateProcessorFactoryTestCase.java @@ -17,16 +17,19 @@ package org.apache.solr.update.processor; import java.util.ArrayList; +import java.util.Collection; import java.util.List; + import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.SolrInputDocument; import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.util.ByteArrayUtf8CharSequence; import org.apache.solr.core.SolrCore; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.servlet.SolrRequestParsers; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.servlet.SolrRequestParsers; public abstract class LanguageIdentifierUpdateProcessorFactoryTestCase extends SolrTestCaseJ4 { @@ -383,6 +386,25 @@ public abstract class LanguageIdentifierUpdateProcessorFactoryTestCase extends S */ private SolrInputDocument process(SolrInputDocument origDoc) { SolrInputDocument modifiedDoc = origDoc.deepCopy(); + if (random().nextBoolean()) { + modifiedDoc.forEach((s, f) -> { + Object rawVal = f.getRawValue(); + if (rawVal instanceof Collection) { + Collection rawValue = (Collection) rawVal; + ArrayList newVal = new ArrayList<>(rawValue.size()); + for (Object o : rawValue) { + if (o instanceof String) { + newVal.add(new ByteArrayUtf8CharSequence((String) o)); + } else { + newVal.add(rawVal); + } + } + f.setValue(newVal); + } else if (rawVal instanceof String) { + f.setValue(new ByteArrayUtf8CharSequence((String) rawVal)); + } + }); + } liProcessor.process(modifiedDoc); return modifiedDoc; } diff --git a/solr/core/src/java/org/apache/solr/update/DocumentBuilder.java b/solr/core/src/java/org/apache/solr/update/DocumentBuilder.java index 86f555ca9c2..90694ff62b2 100644 --- a/solr/core/src/java/org/apache/solr/update/DocumentBuilder.java +++ b/solr/core/src/java/org/apache/solr/update/DocumentBuilder.java @@ -135,7 +135,7 @@ public class DocumentBuilder { // Load fields from SolrDocument to Document for( SolrInputField field : doc ) { - if (field.getFirstValue() instanceof SolrDocumentBase) { + if (field.getFirstRawValue() instanceof SolrDocumentBase) { if (ignoreNestedDocs) { continue; } @@ -159,7 +159,9 @@ public class DocumentBuilder { // load each field value boolean hasField = false; try { - for( Object v : field ) { + Iterator it = field.getRawIterator(); + while (it.hasNext()) { + Object v = it.next(); if( v == null ) { continue; } diff --git a/solr/solrj/src/java/org/apache/solr/common/SolrInputField.java b/solr/solrj/src/java/org/apache/solr/common/SolrInputField.java index 5d99d92d92c..8b4add6523b 100644 --- a/solr/solrj/src/java/org/apache/solr/common/SolrInputField.java +++ b/solr/solrj/src/java/org/apache/solr/common/SolrInputField.java @@ -21,6 +21,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; +import static org.apache.solr.common.util.ByteArrayUtf8CharSequence.convertCharSeq; + /** * * @since solr 1.3 @@ -111,11 +113,11 @@ public class SolrInputField implements Iterable, Serializable if( value instanceof Collection ) { Collection c = (Collection)value; if( c.size() > 0 ) { - return c.iterator().next(); + return convertCharSeq(c.iterator().next()); } return null; } - return value; + return convertCharSeq(value); } /** @@ -123,6 +125,28 @@ public class SolrInputField implements Iterable, Serializable * will be a collection. */ public Object getValue() { + return convertCharSeq(value); + } + + + /** + * Return a value as is without converting and CharSequence Objects + */ + public Object getRawValue() { + return value; + } + + /** + * Return the first value as is without converting and CharSequence Objects + */ + public Object getFirstRawValue() { + if (value instanceof Collection) { + Collection c = (Collection) value; + if (c.size() > 0) { + return c.iterator().next(); + } + return null; + } return value; } @@ -132,12 +156,12 @@ public class SolrInputField implements Iterable, Serializable */ @SuppressWarnings("unchecked") public Collection getValues() { - if( value instanceof Collection ) { - return (Collection)value; + if (value instanceof Collection) { + return convertCharSeq((Collection) value); } if( value != null ) { Collection vals = new ArrayList<>(1); - vals.add( value ); + vals.add(convertCharSeq(value)); return vals; } return null; @@ -165,8 +189,33 @@ public class SolrInputField implements Iterable, Serializable } @Override + public Iterator iterator(){ + if( value instanceof Collection ) { + return (convertCharSeq ((Collection)value)).iterator(); + } + return new Iterator() { + boolean nxt = (value!=null); + + @Override + public boolean hasNext() { + return nxt; + } + + @Override + public Object next() { + nxt = false; + return convertCharSeq(value); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + + } @SuppressWarnings("unchecked") - public Iterator iterator() { + public Iterator getRawIterator() { if( value instanceof Collection ) { return ((Collection)value).iterator(); } diff --git a/solr/solrj/src/java/org/apache/solr/common/util/ByteArrayUtf8CharSequence.java b/solr/solrj/src/java/org/apache/solr/common/util/ByteArrayUtf8CharSequence.java index c9a05cb1548..7a4abe2c303 100644 --- a/solr/solrj/src/java/org/apache/solr/common/util/ByteArrayUtf8CharSequence.java +++ b/solr/solrj/src/java/org/apache/solr/common/util/ByteArrayUtf8CharSequence.java @@ -73,6 +73,10 @@ public class ByteArrayUtf8CharSequence implements Utf8CharSequence { return buf[offset + idx]; } + /** + * this is for internal use to get a cached string value. + * returns null if There is no cached String value + */ public String getStringOrNull() { return utf16; } @@ -209,6 +213,7 @@ public class ByteArrayUtf8CharSequence implements Utf8CharSequence { public static Object convertCharSeq(Object o) { if (o == null) return null; if (o instanceof Utf8CharSequence) return ((Utf8CharSequence) o).toString(); + if (o instanceof Collection) return convertCharSeq((Collection) o); return o; } diff --git a/solr/solrj/src/java/org/apache/solr/common/util/JavaBinCodec.java b/solr/solrj/src/java/org/apache/solr/common/util/JavaBinCodec.java index 9535ee16357..4295d25148b 100644 --- a/solr/solrj/src/java/org/apache/solr/common/util/JavaBinCodec.java +++ b/solr/solrj/src/java/org/apache/solr/common/util/JavaBinCodec.java @@ -357,7 +357,7 @@ public class JavaBinCodec implements PushWriter { return true; } if (val instanceof SolrInputField) { - return writeKnownType(((SolrInputField) val).getValue()); + return writeKnownType(((SolrInputField) val).getRawValue()); } if (val instanceof IteratorWriter) { writeIterator((IteratorWriter) val);