diff --git a/ql/src/java/org/apache/hadoop/hive/ql/exec/SerializationUtilities.java b/ql/src/java/org/apache/hadoop/hive/ql/exec/SerializationUtilities.java index e4d33e82168..e205c08d842 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/exec/SerializationUtilities.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/exec/SerializationUtilities.java @@ -367,67 +367,89 @@ public Path read(Kryo kryo, Input input, Class type) { } /** - * Supports sublists created via {@link ArrayList#subList(int, int)} since java7 (oracle jdk, - * represented by java.util.ArrayList$SubList). + * Supports sublists created via {@link ArrayList#subList(int, int)} since java7 and {@link LinkedList#subList(int, int)} since java9 (openjdk). * This is from kryo-serializers package. */ private static class ArrayListSubListSerializer extends com.esotericsoftware.kryo.Serializer> { - private Field _parentField; - private Field _parentOffsetField; - private Field _sizeField; - - public ArrayListSubListSerializer() { - try { - final Class clazz = Class.forName("java.util.ArrayList$SubList"); - _parentField = clazz.getDeclaredField("parent"); - _parentOffsetField = clazz.getDeclaredField( "parentOffset" ); - _sizeField = clazz.getDeclaredField( "size" ); - _parentField.setAccessible( true ); - _parentOffsetField.setAccessible( true ); - _sizeField.setAccessible( true ); - } catch (final Exception e) { - throw new RuntimeException(e); - } + private Field _parentField; + private Field _parentOffsetField; + private Field _sizeField; + + public ArrayListSubListSerializer() { + try { + final Class clazz = Class.forName("java.util.ArrayList$SubList"); + _parentField = getParentField(clazz); + _parentOffsetField = getOffsetField(clazz); + _sizeField = clazz.getDeclaredField( "size" ); + _parentField.setAccessible( true ); + _parentOffsetField.setAccessible( true ); + _sizeField.setAccessible( true ); + } catch (final Exception e) { + throw new RuntimeException(e); } + } - @Override - public List read(final Kryo kryo, final Input input, final Class> clazz) { - kryo.reference(FAKE_REFERENCE); - final List list = (List) kryo.readClassAndObject(input); - final int fromIndex = input.readInt(true); - final int toIndex = input.readInt(true); - return list.subList(fromIndex, toIndex); + private static Field getParentField(Class clazz) throws NoSuchFieldException { + try { + // java 9+ + return clazz.getDeclaredField("root"); + } catch(NoSuchFieldException e) { + return clazz.getDeclaredField("parent"); } + } - @Override - public void write(final Kryo kryo, final Output output, final List obj) { - try { - kryo.writeClassAndObject(output, _parentField.get(obj)); - final int parentOffset = _parentOffsetField.getInt( obj ); - final int fromIndex = parentOffset; - output.writeInt(fromIndex, true); - final int toIndex = fromIndex + _sizeField.getInt( obj ); - output.writeInt(toIndex, true); - } catch (final Exception e) { - throw new RuntimeException(e); - } + private static Field getOffsetField(Class clazz) throws NoSuchFieldException { + try { + // up to jdk8 (which also has an "offset" field (we don't need) - therefore we check "parentOffset" first + return clazz.getDeclaredField( "parentOffset" ); + } catch (NoSuchFieldException e) { + // jdk9+ only has "offset" which is the parent offset + return clazz.getDeclaredField( "offset" ); } + } - @Override - public List copy(final Kryo kryo, final List original) { - try { - kryo.reference(FAKE_REFERENCE); - final List list = (List) _parentField.get(original); - final int parentOffset = _parentOffsetField.getInt( original ); - final int fromIndex = parentOffset; - final int toIndex = fromIndex + _sizeField.getInt( original ); - return kryo.copy(list).subList(fromIndex, toIndex); - } catch (final Exception e) { - throw new RuntimeException(e); - } + @Override + public List read(final Kryo kryo, final Input input, final Class> clazz) { + kryo.reference(FAKE_REFERENCE); + final List list = (List) kryo.readClassAndObject(input); + final int fromIndex = input.readInt(true); + final int toIndex = input.readInt(true); + return list.subList(fromIndex, toIndex); + } + + @Override + public void write(final Kryo kryo, final Output output, final List obj) { + try { + kryo.writeClassAndObject(output, _parentField.get(obj)); + final int parentOffset = _parentOffsetField.getInt( obj ); + final int fromIndex = parentOffset; + output.writeInt(fromIndex, true); + final int toIndex = fromIndex + _sizeField.getInt( obj ); + output.writeInt(toIndex, true); + } catch (final RuntimeException e) { + // Don't eat and wrap RuntimeExceptions because the ObjectBuffer.write... + // handles SerializationException specifically (resizing the buffer)... + throw e; + } catch (final Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public List copy(final Kryo kryo, final List original) { + kryo.reference(FAKE_REFERENCE); + try { + final List list = (List) _parentField.get(original); + final int parentOffset = _parentOffsetField.getInt( original ); + final int fromIndex = parentOffset; + final int toIndex = fromIndex + _sizeField.getInt( original ); + return kryo.copy(list).subList(fromIndex, toIndex); + } catch(final Exception e) { + throw new RuntimeException(e); } } + } /** * A kryo {@link Serializer} for lists created via {@link Arrays#asList(Object...)}.