This is an automated email from the ASF dual-hosted git repository.
mpetrov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new c8f225248f9 IGNITE-20688 Fixed orphaned handles after binary object
detach operation. (#11272)
c8f225248f9 is described below
commit c8f225248f912083f184a7060f37c91902e3c2a9
Author: Mikhail Petrov <[email protected]>
AuthorDate: Thu May 30 14:28:19 2024 +0300
IGNITE-20688 Fixed orphaned handles after binary object detach operation.
(#11272)
---
.../binary/BinaryArrayIdentityResolver.java | 27 +-
.../internal/binary/BinaryClassDescriptor.java | 4 +-
.../ignite/internal/binary/BinaryObjectImpl.java | 27 +-
.../ignite/internal/binary/BinaryReaderExImpl.java | 7 +-
.../internal/binary/BinaryReaderHandles.java | 5 +
.../internal/binary/BinaryReaderHandlesHolder.java | 3 +
.../binary/BinaryReaderHandlesHolderImpl.java | 5 +
.../apache/ignite/internal/binary/BinaryUtils.java | 27 +-
.../internal/binary/BinaryWriterSchemaHolder.java | 4 +-
.../binary/CrossObjectReferenceResolver.java | 358 +++++++++++++
.../ignite/internal/binary/ObjectDetachHelper.java | 151 ++++++
.../internal/binary/RawBinaryObjectExtractor.java | 337 ++++++++++++
.../binary/streams/BinaryAbstractOutputStream.java | 7 +
.../binary/streams/BinaryOutputStream.java | 9 +
.../platform/memory/PlatformOutputStreamImpl.java | 5 +
.../CrossObjectReferenceSerializationTest.java | 596 +++++++++++++++++++++
.../binary/RawBinaryObjectExtractorTest.java | 262 +++++++++
.../binary/mutabletest/GridBinaryTestClasses.java | 75 +++
.../IgnitePdsBinarySortObjectFieldsTest.java | 27 +-
.../testsuites/IgniteBinaryObjectsTestSuite.java | 5 +
20 files changed, 1909 insertions(+), 32 deletions(-)
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryArrayIdentityResolver.java
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryArrayIdentityResolver.java
index 14792e5abd5..13eaab2c234 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryArrayIdentityResolver.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryArrayIdentityResolver.java
@@ -50,30 +50,29 @@ public class BinaryArrayIdentityResolver extends
BinaryAbstractIdentityResolver
/** {@inheritDoc} */
@Override protected int hashCode0(BinaryObject obj) {
- int hash = 1;
-
if (obj instanceof BinaryObjectExImpl) {
BinaryObjectExImpl ex = (BinaryObjectExImpl)obj;
int start = ex.dataStartOffset();
int end = ex.footerStartOffset();
- if (ex.hasArray()) {
- // Handle heap object.
- byte[] data = ex.array();
-
- for (int i = start; i < end; i++)
- hash = 31 * hash + data[i];
- }
+ if (ex.hasArray())
+ return hashCode(ex.array(), start, end);
else {
// Handle offheap object.
+ int hash = 1;
+
long ptr = ex.offheapAddress();
for (int i = start; i < end; i++)
hash = 31 * hash + BinaryPrimitives.readByte(ptr, i);
+
+ return hash;
}
}
else if (obj instanceof BinaryEnumObjectImpl) {
+ int hash = 1;
+
int ord = obj.enumOrdinal();
// Construct hash as if it was an int serialized in little-endian
form.
@@ -81,10 +80,20 @@ public class BinaryArrayIdentityResolver extends
BinaryAbstractIdentityResolver
hash = 31 * hash + (ord & 0x0000FF00);
hash = 31 * hash + (ord & 0x00FF0000);
hash = 31 * hash + (ord & 0xFF000000);
+
+ return hash;
}
else
throw new BinaryObjectException("Array identity resolver cannot be
used with provided BinaryObject " +
"implementation: " + obj.getClass().getName());
+ }
+
+ /** */
+ public int hashCode(byte[] data, int startPos, int endPos) {
+ int hash = 1;
+
+ for (int i = startPos; i < endPos; i++)
+ hash = 31 * hash + data[i];
return hash;
}
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryClassDescriptor.java
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryClassDescriptor.java
index be1ffc3cb57..b0fde3168b6 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryClassDescriptor.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryClassDescriptor.java
@@ -335,12 +335,12 @@ public class BinaryClassDescriptor {
if (BinaryUtils.FIELDS_SORTED_ORDER) {
fields0 = new TreeMap<>();
- stableFieldsMeta = metaDataEnabled ? new
TreeMap<String, BinaryFieldMetadata>() : null;
+ stableFieldsMeta = metaDataEnabled ? new TreeMap<>() :
null;
}
else {
fields0 = new LinkedHashMap<>();
- stableFieldsMeta = metaDataEnabled ? new
LinkedHashMap<String, BinaryFieldMetadata>() : null;
+ stableFieldsMeta = metaDataEnabled ? new
LinkedHashMap<>() : null;
}
Set<String> duplicates = duplicateFields(cls);
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectImpl.java
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectImpl.java
index e10b33168fb..e8ef88c09cc 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectImpl.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectImpl.java
@@ -35,6 +35,8 @@ import org.apache.ignite.binary.BinaryType;
import org.apache.ignite.internal.GridDirectTransient;
import org.apache.ignite.internal.IgniteCodeGeneratingFail;
import org.apache.ignite.internal.binary.streams.BinaryHeapInputStream;
+import org.apache.ignite.internal.binary.streams.BinaryHeapOutputStream;
+import org.apache.ignite.internal.binary.streams.BinaryOutputStream;
import org.apache.ignite.internal.processors.cache.CacheObject;
import org.apache.ignite.internal.processors.cache.CacheObjectAdapter;
import org.apache.ignite.internal.processors.cache.CacheObjectContext;
@@ -282,16 +284,35 @@ public final class BinaryObjectImpl extends
BinaryObjectExImpl implements Extern
* @return Detached binary object.
*/
public BinaryObjectImpl detach() {
+ return detach(false);
+ }
+
+ /**
+ * @return Detached binary object.
+ */
+ public BinaryObjectImpl detach(boolean checkCrossObjReferences) {
if (!detachAllowed || detached())
return this;
int len = length();
- byte[] arr0 = new byte[len];
+ if (checkCrossObjReferences) {
+ ObjectDetachHelper detachHelper = ObjectDetachHelper.create(arr,
start);
+
+ if (detachHelper.isCrossObjectReferencesDetected()) {
+ try (BinaryOutputStream out = new BinaryHeapOutputStream(2 *
len)) {
+ detachHelper.detach(out);
+
+ return new BinaryObjectImpl(ctx, out.arrayCopy(), 0);
+ }
+ }
+ }
+
+ byte[] detachedData = new byte[len];
- U.arrayCopy(arr, start, arr0, 0, len);
+ U.arrayCopy(arr, start, detachedData, 0, len);
- return new BinaryObjectImpl(ctx, arr0, 0);
+ return new BinaryObjectImpl(ctx, detachedData, 0);
}
/**
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderExImpl.java
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderExImpl.java
index a88ed7902a4..57e6d88198a 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderExImpl.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderExImpl.java
@@ -1338,7 +1338,7 @@ public class BinaryReaderExImpl implements BinaryReader,
BinaryRawReaderEx, Bina
/** {@inheritDoc} */
@Nullable @Override public Object readObjectDetached(boolean deserialize)
throws BinaryObjectException {
- return BinaryUtils.unmarshal(in, ctx, ldr, this, true, deserialize);
+ return BinaryUtils.unmarshal(in, ctx, ldr, new
BinaryReaderHandlesHolderImpl(), true, deserialize);
}
/** {@inheritDoc} */
@@ -2369,6 +2369,11 @@ public class BinaryReaderExImpl implements BinaryReader,
BinaryRawReaderEx, Bina
return in.remaining();
}
+ /** {@inheritDoc} */
+ @Override public boolean isEmpty() {
+ return hnds == null || hnds.isEmpty();
+ }
+
/** {@inheritDoc} */
@Override public void close() throws IOException {
// No-op.
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderHandles.java
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderHandles.java
index 72d054502f4..9582177dcf3 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderHandles.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderHandles.java
@@ -103,4 +103,9 @@ public class BinaryReaderHandles {
data0.put(pos, obj);
}
}
+
+ /** */
+ public boolean isEmpty() {
+ return mode == MODE_EMPTY;
+ }
}
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderHandlesHolder.java
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderHandlesHolder.java
index 48c9e8ebdd9..a60276ced82 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderHandlesHolder.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderHandlesHolder.java
@@ -43,4 +43,7 @@ public interface BinaryReaderHandlesHolder {
* @return Handles.
*/
public BinaryReaderHandles handles();
+
+ /** */
+ public boolean isEmpty();
}
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderHandlesHolderImpl.java
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderHandlesHolderImpl.java
index 95e5ff52d79..a1ee6a91a16 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderHandlesHolderImpl.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryReaderHandlesHolderImpl.java
@@ -41,4 +41,9 @@ public class BinaryReaderHandlesHolderImpl implements
BinaryReaderHandlesHolder
return hnds;
}
+
+ /** {@inheritDoc} */
+ @Override public boolean isEmpty() {
+ return hnds == null || hnds.isEmpty();
+ }
}
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java
index 72790b94317..c4d925de429 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java
@@ -154,7 +154,7 @@ public class BinaryUtils {
!IgniteSystemProperties.getBoolean(IgniteSystemProperties.IGNITE_BINARY_DONT_WRAP_TREE_STRUCTURES);
/** Whether to sort field in binary objects (doesn't affect
Binarylizable). */
- public static final boolean FIELDS_SORTED_ORDER =
+ public static boolean FIELDS_SORTED_ORDER =
IgniteSystemProperties.getBoolean(IgniteSystemProperties.IGNITE_BINARY_SORT_OBJECT_FIELDS);
/** Field type names. */
@@ -817,6 +817,20 @@ public class BinaryUtils {
return in.readIntPositioned(start +
GridBinaryMarshaller.TOTAL_LEN_POS);
}
+ /** */
+ public static int dataStartRelative(BinaryPositionReadable in, int start) {
+ int typeId = in.readIntPositioned(start +
GridBinaryMarshaller.TYPE_ID_POS);
+
+ if (typeId == GridBinaryMarshaller.UNREGISTERED_TYPE_ID) {
+ // Gets the length of the type name which is stored as string.
+ int len = in.readIntPositioned(start +
GridBinaryMarshaller.DFLT_HDR_LEN + /** object type */1);
+
+ return GridBinaryMarshaller.DFLT_HDR_LEN + /** object type */1 +
/** string length */ 4 + len;
+ }
+ else
+ return GridBinaryMarshaller.DFLT_HDR_LEN;
+ }
+
/**
* Get footer start of the object.
*
@@ -1910,10 +1924,11 @@ public class BinaryUtils {
BinaryObjectExImpl po;
if (detach) {
- // In detach mode we simply copy object's content.
- in.position(start);
+ BinaryObjectImpl binObj = new BinaryObjectImpl(ctx,
in.array(), start);
+
+ binObj.detachAllowed(true);
- po = new BinaryObjectImpl(ctx, in.readByteArray(len), 0);
+ po = binObj.detach(!handles.isEmpty());
}
else {
if (in.offheapPointer() == 0)
@@ -1921,10 +1936,10 @@ public class BinaryUtils {
else
po = new BinaryObjectOffheapImpl(ctx,
in.offheapPointer(), start,
in.remaining() + in.position());
-
- in.position(start + po.length());
}
+ in.position(start + len);
+
handles.setHandle(po, start);
return po;
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryWriterSchemaHolder.java
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryWriterSchemaHolder.java
index 038a34e3edf..b1d372a6196 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryWriterSchemaHolder.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryWriterSchemaHolder.java
@@ -71,7 +71,9 @@ public class BinaryWriterSchemaHolder {
}
/**
- * Write collected frames and pop them.
+ * Write collected frames.
+ * Note, that after writing collected frames, you must call {@link
BinaryWriterSchemaHolder#pop(int)} method with
+ * the total number of written frames as an argument.
*
* @param out Output stream.
* @param fieldCnt Count.
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/binary/CrossObjectReferenceResolver.java
b/modules/core/src/main/java/org/apache/ignite/internal/binary/CrossObjectReferenceResolver.java
new file mode 100644
index 00000000000..ce5fd0c70e9
--- /dev/null
+++
b/modules/core/src/main/java/org/apache/ignite/internal/binary/CrossObjectReferenceResolver.java
@@ -0,0 +1,358 @@
+/*
+ * 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.ignite.internal.binary;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.ignite.internal.binary.streams.BinaryOutputStream;
+
+import static
org.apache.ignite.internal.binary.BinaryUtils.FLAG_OFFSET_ONE_BYTE;
+import static
org.apache.ignite.internal.binary.BinaryUtils.FLAG_OFFSET_TWO_BYTES;
+import static org.apache.ignite.internal.binary.BinaryUtils.OFFSET_1;
+import static org.apache.ignite.internal.binary.BinaryUtils.OFFSET_2;
+import static org.apache.ignite.internal.binary.BinaryUtils.dataStartRelative;
+import static org.apache.ignite.internal.binary.BinaryUtils.fieldOffsetLength;
+import static
org.apache.ignite.internal.binary.BinaryUtils.footerStartAbsolute;
+import static org.apache.ignite.internal.binary.BinaryUtils.hasRaw;
+import static org.apache.ignite.internal.binary.BinaryUtils.hasSchema;
+import static org.apache.ignite.internal.binary.BinaryUtils.isCompactFooter;
+import static org.apache.ignite.internal.binary.BinaryUtils.length;
+import static org.apache.ignite.internal.binary.BinaryUtils.rawOffsetAbsolute;
+import static
org.apache.ignite.internal.binary.GridBinaryMarshaller.DFLT_HDR_LEN;
+import static org.apache.ignite.internal.binary.GridBinaryMarshaller.FLAGS_POS;
+import static
org.apache.ignite.internal.binary.GridBinaryMarshaller.HASH_CODE_POS;
+import static
org.apache.ignite.internal.binary.GridBinaryMarshaller.SCHEMA_OR_RAW_OFF_POS;
+import static
org.apache.ignite.internal.binary.GridBinaryMarshaller.TOTAL_LEN_POS;
+
+/** */
+public class CrossObjectReferenceResolver {
+ /** */
+ private final RawBinaryObjectExtractor in;
+
+ /** */
+ private final BinaryOutputStream out;
+
+ /** */
+ private final int inRootObjStartPos;
+
+ /** */
+ private final Map<Integer, Integer> objPosTranslation = new HashMap<>();
+
+ /** */
+ private final BinaryWriterSchemaHolder schema = new
BinaryWriterSchemaHolder();
+
+ /** */
+ private CrossObjectReferenceResolver(RawBinaryObjectExtractor in,
BinaryOutputStream out) {
+ this.in = in;
+
+ inRootObjStartPos = in.position();
+
+ this.out = out;
+ }
+
+ /** */
+ static void copyObject(RawBinaryObjectExtractor in, BinaryOutputStream
out) {
+ CrossObjectReferenceResolver resolver = new
CrossObjectReferenceResolver(in, out);
+
+ resolver.reassembleNextObject();
+ }
+
+ /** */
+ private void reassembleNextObject() {
+ int inObjStartPos = in.position();
+ int outObjStartPos = out.position();
+
+ byte objType = in.readBytePositioned(inObjStartPos);
+
+ switch (objType) {
+ case GridBinaryMarshaller.OBJ: {
+ doObjectProcessing();
+
+ break;
+ }
+
+ case GridBinaryMarshaller.HANDLE: {
+ doHandleProcessing();
+
+ break;
+ }
+
+ case GridBinaryMarshaller.OBJ_ARR: {
+ objPosTranslation.put(inObjStartPos, outObjStartPos);
+
+ copyBytes(Byte.BYTES); // Object type.
+
+ in.copyTypeId(out);
+
+ int size = copyInt();
+
+ reassembleNextCortege(size);
+
+ break;
+ }
+
+ case GridBinaryMarshaller.COL: {
+ objPosTranslation.put(inObjStartPos, outObjStartPos);
+
+ copyBytes(Byte.BYTES); // Object type.
+
+ int size = copyInt();
+
+ copyBytes(Byte.BYTES); // Collection type.
+
+ reassembleNextCortege(size);
+
+ break;
+ }
+
+ case GridBinaryMarshaller.MAP: {
+ objPosTranslation.put(inObjStartPos, outObjStartPos);
+
+ copyBytes(Byte.BYTES); // Object type.
+
+ int size = copyInt() * 2;
+
+ copyBytes(Byte.BYTES); // Map type.
+
+ reassembleNextCortege(size);
+
+ break;
+ }
+
+ default:
+ in.copyObject(out);
+ }
+ }
+
+ /** */
+ private void doObjectProcessing() {
+ int inObjStartPos = in.position();
+ int outObjStartPos = out.position();
+
+ objPosTranslation.put(inObjStartPos, outObjStartPos);
+
+ BinaryObjectDescriptor inObjDesc = BinaryObjectDescriptor.parse(in,
inObjStartPos);
+
+ int fieldsCnt = 0;
+
+ try {
+ // Copy object's header. Update with actual data length and field
offsets will be performed later.
+ copyBytes(dataStartRelative(in, inObjStartPos));
+
+ // Process object fields.
+ while (in.position() < inObjDesc.rawDataStartPos)
+ reassembleField(fieldsCnt++, offset(outObjStartPos,
out.position()), inObjDesc);
+
+ int outRawDataStartPos = -1;
+
+ if (inObjDesc.hasRaw) {
+ outRawDataStartPos = out.position();
+
+ copyBytes(inObjDesc.footerStartPos - in.position());
+ }
+
+ int outFooterStartPos = out.position();
+
+ int schemaOrRawOffsetPos;
+ int footerFieldOffsetLen;
+
+ // Write footer and raw data offset if required.
+ if (inObjDesc.hasSchema) {
+ schemaOrRawOffsetPos = offset(outObjStartPos,
outFooterStartPos);
+ footerFieldOffsetLen = schema.write(out, fieldsCnt,
inObjDesc.isCompactFooter);
+
+ if (inObjDesc.hasRaw)
+ out.writeInt(offset(outObjStartPos, outRawDataStartPos));
+ }
+ else {
+ schemaOrRawOffsetPos = inObjDesc.hasRaw ?
offset(outObjStartPos, outRawDataStartPos) : DFLT_HDR_LEN;
+ footerFieldOffsetLen = 0;
+ }
+
+ // Update header to reflect changes after cross object references
are resolved.
+ overrideHeader(
+ outObjStartPos,
+ /** flags */ setFieldOffsetFlag(inObjDesc.flags,
footerFieldOffsetLen),
+ /** hash */
BinaryArrayIdentityResolver.instance().hashCode(out.array(), outObjStartPos +
DFLT_HDR_LEN, outFooterStartPos),
+ /** total length */ out.position() - outObjStartPos,
+ schemaOrRawOffsetPos
+ );
+
+ in.position(inObjDesc.endPos);
+ }
+ finally {
+ schema.pop(fieldsCnt);
+ }
+ }
+
+ /** */
+ private void doHandleProcessing() {
+ int inObjStartPos = in.position();
+
+ in.skipBytes(1); // Object type.
+
+ int offset = in.readInt();
+
+ int inHandleObjPos = inObjStartPos - offset;
+
+ Integer outHandleObjPos = objPosTranslation.get(inHandleObjPos);
+
+ if (outHandleObjPos != null) {
+ int outObjStartPos = out.position();
+
+ out.writeByte(GridBinaryMarshaller.HANDLE);
+ out.writeInt(offset(outHandleObjPos, outObjStartPos));
+ }
+ else {
+ assert inHandleObjPos < inRootObjStartPos;
+
+ objPosTranslation.put(inHandleObjPos, out.position());
+
+ copyObjectPositioned(inHandleObjPos);
+ }
+ }
+
+ /** */
+ private void overrideHeader(int writeObjStartPos, short flags, int
hashCode, int totalLen, int schemaOrRawOffsetPos) {
+ out.unsafeWriteShort(writeObjStartPos + FLAGS_POS, flags);
+ out.unsafeWriteInt(writeObjStartPos + HASH_CODE_POS, hashCode);
+ out.unsafeWriteInt(writeObjStartPos + TOTAL_LEN_POS, totalLen);
+ out.unsafeWriteInt(writeObjStartPos + SCHEMA_OR_RAW_OFF_POS,
schemaOrRawOffsetPos);
+ }
+
+ /** */
+ private short setFieldOffsetFlag(short flags, int fieldOffsetLength) {
+ if (fieldOffsetLength == 0)
+ return flags;
+
+ flags &= ~FLAG_OFFSET_ONE_BYTE;
+ flags &= ~FLAG_OFFSET_TWO_BYTES;
+
+ if (fieldOffsetLength == OFFSET_1)
+ flags |= FLAG_OFFSET_ONE_BYTE;
+ else if (fieldOffsetLength == OFFSET_2)
+ flags |= FLAG_OFFSET_TWO_BYTES;
+
+ return flags;
+ }
+
+ /** */
+ private void reassembleField(int fieldOrder, int fieldOffset,
BinaryObjectDescriptor binObjDesc) {
+ int fieldId = binObjDesc.isCompactFooter
+ ? -1
+ : in.readIntPositioned(binObjDesc.fieldIdPosition(fieldOrder));
+
+ schema.push(fieldId, fieldOffset);
+
+ reassembleNextObject();
+ }
+
+ /** */
+ private void reassembleNextCortege(int cortegeSize) {
+ for (int elemIdx = 0; elemIdx < cortegeSize; elemIdx++)
+ reassembleNextObject();
+ }
+
+ /** */
+ private void copyObjectPositioned(int inPos) {
+ int inRetPos = in.position();
+
+ in.position(inPos);
+
+ ObjectDetachHelper detachHelper =
ObjectDetachHelper.create(in.array(), inPos);
+
+ if (detachHelper.isCrossObjectReferencesDetected())
+ detachHelper.detach(out);
+ else
+ in.copyObject(out);
+
+ in.position(inRetPos);
+ }
+
+ /** */
+ private int copyInt() {
+ int res = in.peekInt();
+
+ copyBytes(Integer.BYTES);
+
+ return res;
+ }
+
+ /** */
+ private void copyBytes(int cnt) {
+ in.copyBytes(cnt, out);
+ }
+
+ /** */
+ private int offset(int startPos, int pos) {
+ assert pos - startPos >= 0;
+
+ return pos - startPos;
+ }
+
+ /** */
+ private static class BinaryObjectDescriptor {
+ /** */
+ private final int rawDataStartPos;
+
+ /** */
+ private final int footerStartPos;
+
+ /** */
+ private final int endPos;
+
+ /** */
+ private final short flags;
+
+ /** */
+ private final boolean hasRaw;
+
+ /** */
+ private final boolean hasSchema;
+
+ /** */
+ private final boolean isCompactFooter;
+
+ /** */
+ private final int fieldOffsetLength;
+
+ /** */
+ private BinaryObjectDescriptor(BinaryPositionReadable in, int
startPos) {
+ rawDataStartPos = rawOffsetAbsolute(in, startPos);
+ footerStartPos = footerStartAbsolute(in, startPos);
+ endPos = startPos + length(in, startPos);
+
+ flags = in.readShortPositioned(startPos + FLAGS_POS);
+
+ hasRaw = hasRaw(flags);
+ hasSchema = hasSchema(flags);
+ isCompactFooter = isCompactFooter(flags);
+ fieldOffsetLength = fieldOffsetLength(flags);
+ }
+
+ /** */
+ private static BinaryObjectDescriptor parse(BinaryPositionReadable in,
int startPos) {
+ return new BinaryObjectDescriptor(in, startPos);
+ }
+
+ /** */
+ private int fieldIdPosition(int fieldOrder) {
+ return footerStartPos + (/** field ID */ 4 + fieldOffsetLength) *
fieldOrder;
+ }
+ }
+}
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/binary/ObjectDetachHelper.java
b/modules/core/src/main/java/org/apache/ignite/internal/binary/ObjectDetachHelper.java
new file mode 100644
index 00000000000..3b7ca117156
--- /dev/null
+++
b/modules/core/src/main/java/org/apache/ignite/internal/binary/ObjectDetachHelper.java
@@ -0,0 +1,151 @@
+/*
+ * 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.ignite.internal.binary;
+
+import org.apache.ignite.internal.binary.streams.BinaryHeapInputStream;
+import org.apache.ignite.internal.binary.streams.BinaryInputStream;
+import org.apache.ignite.internal.binary.streams.BinaryOutputStream;
+
+import static org.apache.ignite.internal.binary.BinaryUtils.dataStartRelative;
+import static org.apache.ignite.internal.binary.BinaryUtils.length;
+import static org.apache.ignite.internal.binary.BinaryUtils.rawOffsetAbsolute;
+
+/** */
+class ObjectDetachHelper {
+ /** */
+ private final RawBinaryObjectExtractor reader;
+
+ /** */
+ private final int rootObjStartPos;
+
+ /** */
+ private boolean isCrossObjReferenceDetected;
+
+ /** */
+ private ObjectDetachHelper(BinaryInputStream in) {
+ reader = new RawBinaryObjectExtractor(in);
+
+ rootObjStartPos = in.position();
+ }
+
+ /** */
+ static ObjectDetachHelper create(byte[] data, int offset) {
+ ObjectDetachHelper res = new
ObjectDetachHelper(BinaryHeapInputStream.create(data, offset));
+
+ res.findCrossObjectReferences();
+
+ return res;
+ }
+
+ /** */
+ public boolean isCrossObjectReferencesDetected() {
+ return isCrossObjReferenceDetected;
+ }
+
+ /** */
+ public void detach(BinaryOutputStream out) {
+ reader.position(rootObjStartPos);
+
+ CrossObjectReferenceResolver.copyObject(reader, out);
+ }
+
+ /** */
+ private void findCrossObjectReferences() {
+ isCrossObjReferenceDetected = findInNextObject();
+ }
+
+ /** */
+ private boolean findInNextObject() {
+ int objStartPos = reader.position();
+
+ byte objType = reader.readBytePositioned(objStartPos);
+
+ switch (objType) {
+ case GridBinaryMarshaller.OBJ: {
+ int objDataStartPos = objStartPos + dataStartRelative(reader,
objStartPos);
+ int objDataEndPos = rawOffsetAbsolute(reader, objStartPos);
+ int objEndPos = objStartPos + length(reader, objStartPos);
+
+ reader.position(objDataStartPos);
+
+ while (reader.position() < objDataEndPos) {
+ if (findInNextObject())
+ return true;
+ }
+
+ reader.position(objEndPos);
+
+ return false;
+ }
+
+ case GridBinaryMarshaller.HANDLE: {
+ reader.skipBytes(1); // Object type.
+
+ int offset = reader.readInt();
+
+ return objStartPos - offset < rootObjStartPos;
+ }
+
+ case GridBinaryMarshaller.OBJ_ARR: {
+ reader.skipBytes(1); // Object type.
+
+ reader.skipTypeId();
+
+ int size = reader.readInt();
+
+ return findInNextCortege(size);
+ }
+
+ case GridBinaryMarshaller.COL: {
+ reader.skipBytes(1); // Object type.
+
+ int size = reader.readInt();
+
+ reader.skipBytes(1); // Collection type.
+
+ return findInNextCortege(size);
+ }
+
+ case GridBinaryMarshaller.MAP: {
+ reader.skipBytes(1); // Object type.
+
+ int size = reader.readInt() * 2;
+
+ reader.skipBytes(1); // Map type.
+
+ return findInNextCortege(size);
+ }
+
+ default: {
+ reader.skipObject();
+
+ return false;
+ }
+ }
+ }
+
+ /** */
+ private boolean findInNextCortege(int cortegeSize) {
+ for (int elemIdx = 0; elemIdx < cortegeSize; elemIdx++) {
+ if (findInNextObject())
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/binary/RawBinaryObjectExtractor.java
b/modules/core/src/main/java/org/apache/ignite/internal/binary/RawBinaryObjectExtractor.java
new file mode 100644
index 00000000000..b75f6a53d9c
--- /dev/null
+++
b/modules/core/src/main/java/org/apache/ignite/internal/binary/RawBinaryObjectExtractor.java
@@ -0,0 +1,337 @@
+/*
+ * 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.ignite.internal.binary;
+
+import org.apache.ignite.binary.BinaryObjectException;
+import org.apache.ignite.internal.binary.streams.BinaryInputStream;
+import org.apache.ignite.internal.binary.streams.BinaryOutputStream;
+
+/** */
+class RawBinaryObjectExtractor implements BinaryPositionReadable {
+ /** */
+ private final BinaryInputStream in;
+
+ /** */
+ RawBinaryObjectExtractor(BinaryInputStream in) {
+ this.in = in;
+ }
+
+ /** */
+ byte[] extractObject() {
+ int startPos = in.position();
+
+ skipObject();
+
+ int endPos = in.position();
+
+ in.position(startPos);
+
+ return in.readByteArray(endPos - startPos);
+ }
+
+ /** */
+ void copyObject(BinaryOutputStream out) {
+ int startPos = in.position();
+
+ skipObject();
+
+ out.writeByteArray(in.array(), startPos, in.position() - startPos);
+ }
+
+ /** */
+ void copyTypeId(BinaryOutputStream out) {
+ int startPos = in.position();
+
+ skipTypeId();
+
+ out.writeByteArray(in.array(), startPos, in.position() - startPos);
+ }
+
+ /** */
+ void copyBytes(int cnt, BinaryOutputStream out) {
+ assert cnt >= 0;
+
+ if (cnt == 0)
+ return;
+
+ out.writeByteArray(in.array(), in.position(), cnt);
+
+ skipBytes(cnt);
+ }
+
+ /** */
+ int readInt() {
+ return in.readInt();
+ }
+
+ /** */
+ void skipObject() {
+ int objStartPos = in.position();
+
+ byte type = in.readByte();
+
+ switch (type) {
+ case GridBinaryMarshaller.NULL:
+ break;
+
+ case GridBinaryMarshaller.OBJ:
+ skipBytes(BinaryUtils.length(in, objStartPos) - /** Object
type. */ Byte.BYTES);
+
+ break;
+
+ case GridBinaryMarshaller.BINARY_OBJ:
+ skipBytes(in.readInt());
+
+ skipBytes(Integer.BYTES); // Offset.
+
+ break;
+
+ case GridBinaryMarshaller.BYTE:
+ case GridBinaryMarshaller.BOOLEAN:
+ skipBytes(Byte.BYTES);
+
+ break;
+
+ case GridBinaryMarshaller.CHAR:
+ skipBytes(Character.BYTES);
+
+ break;
+
+ case GridBinaryMarshaller.SHORT:
+ skipBytes(Short.BYTES);
+
+ break;
+
+ case GridBinaryMarshaller.FLOAT:
+ skipBytes(Float.BYTES);
+
+ break;
+
+ case GridBinaryMarshaller.HANDLE:
+ case GridBinaryMarshaller.INT:
+ skipBytes(Integer.BYTES);
+
+ break;
+
+ case GridBinaryMarshaller.ENUM:
+ case GridBinaryMarshaller.BINARY_ENUM: {
+ skipTypeId();
+
+ skipBytes(Integer.BYTES); // Ordinal.
+
+ break;
+ }
+
+ case GridBinaryMarshaller.LONG:
+ case GridBinaryMarshaller.DATE:
+ case GridBinaryMarshaller.TIME:
+ skipBytes(Long.BYTES);
+
+ break;
+
+ case GridBinaryMarshaller.DOUBLE:
+ skipBytes(Double.BYTES);
+
+ break;
+
+ case GridBinaryMarshaller.OPTM_MARSH:
+ case GridBinaryMarshaller.STRING:
+ skipBytes(in.readInt());
+
+ break;
+
+ case GridBinaryMarshaller.DECIMAL:
+ skipBytes(Integer.BYTES); // Scale.
+ skipBytes(in.readInt());
+
+ break;
+
+ case GridBinaryMarshaller.UUID:
+ skipBytes(Long.BYTES + Long.BYTES);
+
+ break;
+
+ case GridBinaryMarshaller.TIMESTAMP:
+ skipBytes(Long.BYTES + Integer.BYTES);
+
+ break;
+
+ case GridBinaryMarshaller.BYTE_ARR:
+ case GridBinaryMarshaller.BOOLEAN_ARR:
+ skipBytes(in.readInt() * Byte.BYTES);
+
+ break;
+
+ case GridBinaryMarshaller.CHAR_ARR:
+ skipBytes(in.readInt() * Character.BYTES);
+
+ break;
+
+ case GridBinaryMarshaller.SHORT_ARR:
+ skipBytes(in.readInt() * Short.BYTES);
+
+ break;
+
+ case GridBinaryMarshaller.INT_ARR:
+ skipBytes(in.readInt() * Integer.BYTES);
+
+ break;
+
+ case GridBinaryMarshaller.FLOAT_ARR:
+ skipBytes(in.readInt() * Float.BYTES);
+
+ break;
+
+ case GridBinaryMarshaller.LONG_ARR:
+ skipBytes(in.readInt() * Long.BYTES);
+
+ break;
+
+ case GridBinaryMarshaller.DOUBLE_ARR:
+ skipBytes(in.readInt() * Double.BYTES);
+
+ break;
+
+ case GridBinaryMarshaller.DECIMAL_ARR:
+ case GridBinaryMarshaller.DATE_ARR:
+ case GridBinaryMarshaller.TIMESTAMP_ARR:
+ case GridBinaryMarshaller.TIME_ARR:
+ case GridBinaryMarshaller.UUID_ARR:
+ case GridBinaryMarshaller.STRING_ARR: {
+ skipCortege();
+
+ break;
+ }
+
+ case GridBinaryMarshaller.ENUM_ARR:
+ case GridBinaryMarshaller.OBJ_ARR: {
+ skipTypeId();
+
+ skipCortege();
+
+ break;
+ }
+
+ case GridBinaryMarshaller.COL: {
+ int size = in.readInt();
+
+ skipBytes(Byte.BYTES); // Collection type.
+
+ skipCortege(size);
+
+ break;
+ }
+
+ case GridBinaryMarshaller.MAP: {
+ int size = in.readInt() * 2;
+
+ skipBytes(Byte.BYTES); // Map type.
+
+ skipCortege(size);
+
+ break;
+ }
+
+ case GridBinaryMarshaller.CLASS: {
+ skipTypeId();
+
+ break;
+ }
+
+ case GridBinaryMarshaller.PROXY: {
+ int size = in.readInt();
+
+ for (int i = 0; i < size; i++)
+ skipTypeId(); // Interface type.
+
+ skipObject();
+
+ break;
+ }
+
+ default:
+ throw new BinaryObjectException("Unsupported binary type
[type=" + type + ']');
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte readBytePositioned(int pos) {
+ return in.readBytePositioned(pos);
+ }
+
+ /** {@inheritDoc} */
+ @Override public short readShortPositioned(int pos) {
+ return in.readShortPositioned(pos);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int readIntPositioned(int pos) {
+ return in.readIntPositioned(pos);
+ }
+
+ /** */
+ int position() {
+ return in.position();
+ }
+
+ /** */
+ void position(int position) {
+ in.position(position);
+ }
+
+ /** */
+ byte peekByte() {
+ return in.readBytePositioned(in.position());
+ }
+
+ /** */
+ int peekInt() {
+ return in.readIntPositioned(in.position());
+ }
+
+ /** */
+ void skipBytes(int count) {
+ int curPos = in.position();
+
+ in.position(curPos + count);
+ }
+
+ /** */
+ void skipTypeId() {
+ int typeId = in.readInt();
+
+ if (typeId == GridBinaryMarshaller.UNREGISTERED_TYPE_ID)
+ skipObject();
+ }
+
+ /** */
+ byte[] array() {
+ return in.array();
+ }
+
+ /** */
+ private void skipCortege() {
+ skipCortege(in.readInt());
+ }
+
+ /** */
+ private void skipCortege(int size) {
+ for (int i = 0; i < size; i++)
+ skipObject();
+ }
+}
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/binary/streams/BinaryAbstractOutputStream.java
b/modules/core/src/main/java/org/apache/ignite/internal/binary/streams/BinaryAbstractOutputStream.java
index e87309dab1a..165bb8801e2 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/binary/streams/BinaryAbstractOutputStream.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/binary/streams/BinaryAbstractOutputStream.java
@@ -52,6 +52,13 @@ public abstract class BinaryAbstractOutputStream extends
BinaryAbstractStream
copyAndShift(val, GridUnsafe.BYTE_ARR_OFF, val.length);
}
+ /** {@inheritDoc} */
+ @Override public void writeByteArray(byte[] val, int off, int len) {
+ ensureCapacity(pos + len);
+
+ copyAndShift(val, GridUnsafe.BYTE_ARR_OFF + off, len);
+ }
+
/** {@inheritDoc} */
@Override public void writeBoolean(boolean val) {
writeByte(val ? BYTE_ONE : BYTE_ZERO);
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/binary/streams/BinaryOutputStream.java
b/modules/core/src/main/java/org/apache/ignite/internal/binary/streams/BinaryOutputStream.java
index 84941203acd..16423e2153e 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/binary/streams/BinaryOutputStream.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/binary/streams/BinaryOutputStream.java
@@ -35,6 +35,15 @@ public interface BinaryOutputStream extends BinaryStream,
AutoCloseable {
*/
public void writeByteArray(byte[] val);
+ /**
+ * Write byte array.
+ *
+ * @param val Byte array.
+ * @param off Offset.
+ * @param len Array length.
+ */
+ public void writeByteArray(byte[] val, int off, int len);
+
/**
* Write boolean value.
*
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/memory/PlatformOutputStreamImpl.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/memory/PlatformOutputStreamImpl.java
index 3f5e1d29356..3fa70540d9e 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/memory/PlatformOutputStreamImpl.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/memory/PlatformOutputStreamImpl.java
@@ -59,6 +59,11 @@ public class PlatformOutputStreamImpl implements
PlatformOutputStream {
copyAndShift(val, GridUnsafe.BYTE_ARR_OFF, val.length);
}
+ /** {@inheritDoc} */
+ @Override public void writeByteArray(byte[] val, int off, int len) {
+ copyAndShift(val, GridUnsafe.BYTE_ARR_OFF + off, len);
+ }
+
/** {@inheritDoc} */
@Override public void writeBoolean(boolean val) {
writeByte(val ? (byte)1 : (byte)0);
diff --git
a/modules/core/src/test/java/org/apache/ignite/internal/binary/CrossObjectReferenceSerializationTest.java
b/modules/core/src/test/java/org/apache/ignite/internal/binary/CrossObjectReferenceSerializationTest.java
new file mode 100644
index 00000000000..ec169a993af
--- /dev/null
+++
b/modules/core/src/test/java/org/apache/ignite/internal/binary/CrossObjectReferenceSerializationTest.java
@@ -0,0 +1,596 @@
+/*
+ * 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.ignite.internal.binary;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.binary.BinaryObjectException;
+import org.apache.ignite.binary.BinaryRawReader;
+import org.apache.ignite.binary.BinaryRawWriter;
+import org.apache.ignite.binary.BinaryReader;
+import org.apache.ignite.binary.BinaryWriter;
+import org.apache.ignite.binary.Binarylizable;
+import org.apache.ignite.client.ClientCache;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.BinaryConfiguration;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import
org.apache.ignite.internal.binary.mutabletest.GridBinaryTestClasses.TestObjectAllTypes;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/** */
+@RunWith(Parameterized.class)
+public class CrossObjectReferenceSerializationTest extends
GridCommonAbstractTest {
+ /** */
+ private static boolean prevFieldsSortedOrderFlag;
+
+ /** */
+ private static Ignite srv;
+
+ /** */
+ private static IgniteClient cli;
+
+ /** */
+ private static IgniteCache<Object, Object> srvCache;
+
+ /** */
+ private static ClientCache<Object, Object> cliCache;
+
+ /** */
+ @Parameterized.Parameter
+ public ObjectType innerObjType;
+
+ /** */
+ @Parameterized.Parameter(1)
+ public ObjectType outerObjType;
+
+ /** */
+ @Parameterized.Parameter(2)
+ public boolean isCompactFooterEnabled;
+
+ /** */
+ @Parameterized.Parameter(3)
+ public SerializationMode serializationMode;
+
+ /** Test parameters. */
+ @Parameterized.Parameters(name = "innerObjectType={0},
outerObjectType={1}, isCompactFooterEnabled={2}, serializationMode={3}")
+ public static Iterable<Object[]> parameters() {
+ List<Object[]> res = new ArrayList<>();
+
+ for (ObjectType innerObjType : ObjectType.values()) {
+ for (ObjectType outerObjType : ObjectType.values()) {
+ for (boolean isCompactFooterEnabled : new boolean[] {true,
false}) {
+ for (SerializationMode serializationMode :
SerializationMode.values())
+ res.add(new Object[] {innerObjType, outerObjType,
isCompactFooterEnabled, serializationMode});
+ }
+ }
+ }
+
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration getConfiguration(String
igniteInstanceName) throws Exception {
+ return super.getConfiguration(igniteInstanceName)
+ .setCacheConfiguration(new
CacheConfiguration<>(DEFAULT_CACHE_NAME))
+ .setBinaryConfiguration(new BinaryConfiguration()
+ .setCompactFooter(isCompactFooterEnabled));
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ super.beforeTestsStarted();
+
+ srv = startGrid(0);
+ cli = Ignition.startClient(new ClientConfiguration()
+ .setAddresses("127.0.0.1:10800")
+ .setBinaryConfiguration(new BinaryConfiguration()
+ .setCompactFooter(isCompactFooterEnabled)));
+
+ srvCache = srv.cache(DEFAULT_CACHE_NAME);
+ cliCache = cli.cache(DEFAULT_CACHE_NAME);
+
+ prevFieldsSortedOrderFlag = BinaryUtils.FIELDS_SORTED_ORDER;
+
+ BinaryUtils.FIELDS_SORTED_ORDER = true;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTestsStopped() throws Exception {
+ BinaryUtils.FIELDS_SORTED_ORDER = prevFieldsSortedOrderFlag;
+
+ super.afterTestsStopped();
+
+ cli.close();
+ srv.close();
+ }
+
+ /** */
+ @Test
+ public void testArray() {
+ Object outerObj = createObject(outerObjType);
+
+ Object[] arr = new Object[] {createReferencesHolder(outerObj),
createReferencesHolder(outerObj)};
+
+ checkPutGetRemove(arr, arr);
+ }
+
+ /** */
+ @Test
+ public void testInnerArray() {
+ Object outerObj = createObject(outerObjType);
+
+ Object[] innerArr = new Object[] {new TestObject(),
createReferencesHolder(outerObj)};
+
+ Object[] arr = new Object[] {createReferencesHolder(outerObj),
innerArr};
+
+ checkPutGetRemove(arr, arr);
+ }
+
+ /** */
+ @Test
+ public void testCollection() {
+ Object outerObj = createObject(outerObjType);
+
+ Collection<Object> col = new ArrayList<>();
+
+ col.add(createReferencesHolder(outerObj));
+ col.add(createReferencesHolder(outerObj));
+
+ checkPutGetRemove(col, col);
+ }
+
+ /** */
+ @Test
+ public void testInnerCollection() {
+ Object outerObj = createObject(outerObjType);
+
+ Collection<Object> col = new ArrayList<>();
+
+ Collection<Object> innerCol = new ArrayList<>();
+
+ innerCol.add(new TestObject());
+ innerCol.add(createReferencesHolder(outerObj));
+
+ col.add(createReferencesHolder(outerObj));
+ col.add(innerCol);
+
+ checkPutGetRemove(col, col);
+ }
+
+ /** */
+ @Test
+ public void testMapReferenceBetweenKeyAndValue() {
+ Object outerObj = createObject(outerObjType);
+
+ Map<Object, Object> map = new HashMap<>();
+
+ map.put(createReferencesHolder(outerObj),
createReferencesHolder(outerObj));
+
+ checkPutGetRemove(map, map);
+ }
+
+ /** */
+ @Test
+ public void testMapReferenceBetweenEntries() {
+ Object outerObj = createObject(outerObjType);
+
+ Map<Object, Object> map = new HashMap<>();
+
+ map.put(0, createReferencesHolder(outerObj));
+ map.put(1, createReferencesHolder(outerObj));
+
+ checkPutGetRemove(map, map);
+ }
+
+ /** */
+ @Test
+ public void testMapInnerCollection() {
+ Object outerObj = createObject(outerObjType);
+
+ Collection<Object> col = new ArrayList<>();
+
+ col.add(createReferencesHolder(outerObj));
+ col.add(createReferencesHolder(outerObj));
+
+ Map<Object, Object> map = new HashMap<>();
+
+ map.put(0, col);
+
+ checkPutGetRemove(map, map);
+ }
+
+ /** */
+ @Test
+ public void testMapInnerArray() {
+ Object outerObj = createObject(outerObjType);
+
+ Map<Object, Object> map = new HashMap<>();
+
+ map.put(0, new Object[] {createReferencesHolder(outerObj),
createReferencesHolder(outerObj)});
+
+ checkPutGetRemove(0, map);
+ }
+
+ /** */
+ @Test
+ public void testConsecutiveCrossObjectReferences() {
+ Object outerObj = createObject(outerObjType);
+
+ Object holder = createReferencesHolder(outerObj);
+
+ Object enclosingHolder = createReferencesHolder(holder);
+
+ Object doubleEnclosingHolder = createReferencesHolder(enclosingHolder);
+
+ Object[] arr = new Object[] {createReferencesHolder(outerObj), holder,
enclosingHolder, doubleEnclosingHolder};
+
+ checkPutGetRemove(arr, arr);
+ }
+
+ /** */
+ @Test
+ public void testMultipleCrossObjectReferences() {
+ Object firstOuterObj = createObject(outerObjType);
+ Object secondOuterObj = createObject(outerObjType);
+
+ Object[] arr = new Object[] {
+ createReferencesHolder(new Object[] {firstOuterObj,
secondOuterObj}),
+ createReferencesHolder(new Object[] {firstOuterObj,
secondOuterObj})
+ };
+
+ checkPutGetRemove(arr, arr);
+ }
+
+ /** */
+ private Object createReferencesHolder(Object outerObj) {
+ switch (serializationMode) {
+ case RAW:
+ return new RawObject(createObject(innerObjType), outerObj);
+ case MIXED:
+ return new MixedObject(createObject(innerObjType), outerObj);
+ case SCHEMA:
+ return new SchemaObject(createObject(innerObjType), outerObj);
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ /** */
+ private Object createObject(ObjectType type) {
+ switch (type) {
+ case OBJECT: {
+ return new TestObject();
+ }
+
+ case ARRAY: {
+ TestObjectAllTypes obj = new TestObject();
+
+ return new Object[] {obj, obj};
+ }
+
+ case COLLECTION: {
+ TestObjectAllTypes obj = new TestObject();
+
+ Collection<Object> col = new ArrayList<>();
+
+ col.add(obj);
+ col.add(obj);
+
+ return col;
+ }
+
+ case MAP: {
+ TestObjectAllTypes obj = new TestObject();
+
+ Map<Object, Object> map = new HashMap<>();
+
+ map.put(0, obj);
+ map.put(1, obj);
+
+ return map;
+ }
+
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ /** */
+ private void checkPutGetRemove(Object key, Object val) {
+ srvCache.put(key, val);
+
+ assertDeepEquals(val, srvCache.get(key));
+ assertDeepEquals(val, cliCache.get(key));
+
+ srvCache.remove(key);
+
+ assertNull(srvCache.get(key));
+ assertNull(cliCache.get(key));
+
+ cliCache.put(key, val);
+
+ assertDeepEquals(val, cliCache.get(key));
+ assertDeepEquals(val, srvCache.get(key));
+
+ cliCache.remove(key);
+
+ assertNull(srvCache.get(key));
+ assertNull(cliCache.get(key));
+ }
+
+ /** */
+ private static void assertDeepEquals(Object exp, Object actual) {
+ assertTrue(deepEquals(exp, actual));
+ }
+
+ /** */
+ private static boolean deepEquals(Object lhs, Object rhs) {
+ if (lhs instanceof Map && rhs instanceof Map) {
+ Map<Object, Object> lhsMap = (Map<Object, Object>)lhs;
+ Map<Object, Object> rhsMap = (Map<Object, Object>)rhs;
+
+ assertEquals(lhsMap.size(), rhsMap.size());
+
+ return lhsMap.entrySet().stream().allMatch(e ->
deepEquals(e.getValue(), rhsMap.get(e.getKey())));
+ }
+ else if (lhs instanceof List && rhs instanceof List) {
+ List<Object> lhsList = (List<Object>)lhs;
+ List<Object> rhsList = (List<Object>)rhs;
+
+ assertEquals(lhsList.size(), rhsList.size());
+
+ boolean res = true;
+
+ for (int i = 0; i < lhsList.size(); i++) {
+ if (!deepEquals(lhsList.get(i), rhsList.get(i))) {
+ res = false;
+
+ break;
+ }
+ }
+
+ return res;
+ }
+ else
+ return Objects.deepEquals(lhs, rhs);
+ }
+
+ /** */
+ private static int hashCodeArraysAware(Object obj) {
+ return obj != null && obj.getClass().isArray() ?
Arrays.deepHashCode((Object[])obj) : Objects.hash(obj);
+ }
+
+ /** */
+ public enum ObjectType {
+ /** */
+ OBJECT,
+
+ /** */
+ ARRAY,
+
+ /** */
+ COLLECTION,
+
+ /** */
+ MAP
+ }
+
+ /** */
+ public enum SerializationMode {
+ /** */
+ RAW,
+
+ /** */
+ MIXED,
+
+ /** */
+ SCHEMA
+ }
+
+ /** */
+ private static class MixedObject extends SchemaObject implements
Binarylizable {
+ /** */
+ public MixedObject() {
+ // No-op.
+ }
+
+ /** */
+ public MixedObject(Object innerObj, Object outerObj) {
+ super(innerObj, outerObj);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeBinary(BinaryWriter writer) throws
BinaryObjectException {
+ writer.writeObject("aWrapperOfOuterRefToReplaceWithObj",
aWrapperOfOuterRefToReplaceWithObj);
+ writer.writeObject("bInnerObj", bInnerObj);
+ writer.writeObject("cRefToOuterObjToReplaceWithInnerRef",
cRefToOuterObjToReplaceWithInnerRef);
+ writer.writeObject("dRefToInnerObjToRecalculate",
dRefToInnerObjToRecalculate);
+
+ BinaryRawWriter rawWriter = writer.rawWriter();
+
+ rawWriter.writeByte(eInnerBytePrimitive);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void readBinary(BinaryReader reader) throws
BinaryObjectException {
+ aWrapperOfOuterRefToReplaceWithObj =
reader.readObject("aWrapperOfOuterRefToReplaceWithObj");
+ bInnerObj = reader.readObject("bInnerObj");
+ cRefToOuterObjToReplaceWithInnerRef =
reader.readObject("cRefToOuterObjToReplaceWithInnerRef");
+ dRefToInnerObjToRecalculate =
reader.readObject("dRefToInnerObjToRecalculate");
+
+ BinaryRawReader rawReader = reader.rawReader();
+
+ eInnerBytePrimitive = rawReader.readByte();
+ }
+ }
+
+ /** */
+ private static class RawObject extends SchemaObject implements
Binarylizable {
+ /** */
+ public RawObject() {
+ // No-op.
+ }
+
+ /** */
+ public RawObject(Object innerObj, Object outerObj) {
+ super(innerObj, outerObj);
+
+ // Reference recalculation for objects written with Raw Binary
Writer currently is not supported.
+ aWrapperOfOuterRefToReplaceWithObj = null;
+ cRefToOuterObjToReplaceWithInnerRef = null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeBinary(BinaryWriter writer) throws
BinaryObjectException {
+ BinaryRawWriter rawWriter = writer.rawWriter();
+
+ rawWriter.writeObject(aWrapperOfOuterRefToReplaceWithObj);
+ rawWriter.writeObject(bInnerObj);
+ rawWriter.writeObject(cRefToOuterObjToReplaceWithInnerRef);
+ rawWriter.writeObject(dRefToInnerObjToRecalculate);
+ rawWriter.writeByte(eInnerBytePrimitive);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void readBinary(BinaryReader reader) throws
BinaryObjectException {
+ BinaryRawReader rawReader = reader.rawReader();
+
+ aWrapperOfOuterRefToReplaceWithObj = rawReader.readObject();
+ bInnerObj = rawReader.readObject();
+ cRefToOuterObjToReplaceWithInnerRef = rawReader.readObject();
+ dRefToInnerObjToRecalculate = rawReader.readObject();
+ eInnerBytePrimitive = rawReader.readByte();
+ }
+ }
+
+ /** */
+ private static class SchemaObject {
+ /** */
+ protected Object aWrapperOfOuterRefToReplaceWithObj;
+
+ /** */
+ protected Object bInnerObj;
+
+ /** */
+ protected Object cRefToOuterObjToReplaceWithInnerRef;
+
+ /** */
+ protected Object dRefToInnerObjToRecalculate;
+
+ /** */
+ protected byte eInnerBytePrimitive;
+
+ /** */
+ public SchemaObject() {
+ // No-op.
+ }
+
+ /** */
+ public SchemaObject(Object innerObj, Object outerObj) {
+ aWrapperOfOuterRefToReplaceWithObj = new ComplexWrapper(outerObj);
+
+ bInnerObj = innerObj;
+
+ cRefToOuterObjToReplaceWithInnerRef = outerObj;
+
+ dRefToInnerObjToRecalculate = innerObj;
+
+ eInnerBytePrimitive = 127;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (!(o instanceof SchemaObject))
+ return false;
+
+ SchemaObject that = (SchemaObject)o;
+
+ return deepEquals(aWrapperOfOuterRefToReplaceWithObj,
that.aWrapperOfOuterRefToReplaceWithObj)
+ && deepEquals(bInnerObj, that.bInnerObj)
+ && deepEquals(cRefToOuterObjToReplaceWithInnerRef,
that.cRefToOuterObjToReplaceWithInnerRef)
+ && deepEquals(dRefToInnerObjToRecalculate,
that.dRefToInnerObjToRecalculate)
+ && eInnerBytePrimitive == that.eInnerBytePrimitive;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ int res = 1;
+
+ res = 31 * res +
hashCodeArraysAware(aWrapperOfOuterRefToReplaceWithObj);
+ res = 31 * res + hashCodeArraysAware(bInnerObj);
+ res = 31 * res +
hashCodeArraysAware(cRefToOuterObjToReplaceWithInnerRef);
+ res = 31 * res + hashCodeArraysAware(dRefToInnerObjToRecalculate);
+ res = 31 * res + hashCodeArraysAware(eInnerBytePrimitive);
+
+ return res;
+ }
+ }
+
+ /** */
+ private static class TestObject extends TestObjectAllTypes {
+ /** */
+ public TestObject() {
+ setDefaultData();
+ }
+ }
+
+ /** */
+ public static class ComplexWrapper extends TestObject {
+ /** */
+ private final Object data;
+
+ /** */
+ public ComplexWrapper(Object data) {
+ this.data = data;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (!(o instanceof ComplexWrapper))
+ return false;
+
+ ComplexWrapper that = (ComplexWrapper)o;
+
+ return super.equals(o) && deepEquals(data, that.data);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return Objects.hash(super.hashCode(), hashCodeArraysAware(data));
+ }
+ }
+}
diff --git
a/modules/core/src/test/java/org/apache/ignite/internal/binary/RawBinaryObjectExtractorTest.java
b/modules/core/src/test/java/org/apache/ignite/internal/binary/RawBinaryObjectExtractorTest.java
new file mode 100644
index 00000000000..186cbef0ad3
--- /dev/null
+++
b/modules/core/src/test/java/org/apache/ignite/internal/binary/RawBinaryObjectExtractorTest.java
@@ -0,0 +1,262 @@
+/*
+ * 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.ignite.internal.binary;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import org.apache.ignite.binary.BinaryObject;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.binary.builder.BinaryObjectBuilderImpl;
+import
org.apache.ignite.internal.binary.mutabletest.GridBinaryTestClasses.TestObjectAllTypes;
+import org.apache.ignite.internal.binary.streams.BinaryHeapInputStream;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgnitePredicate;
+import org.apache.ignite.marshaller.MarshallerContext;
+import org.apache.ignite.marshaller.jdk.JdkMarshaller;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+
+/** */
+public class RawBinaryObjectExtractorTest extends GridCommonAbstractTest {
+ /** */
+ @Test
+ public void test() throws Exception {
+ BinaryContext ctx = createTestBinaryContext();
+
+ Collection<Object> testObjects = createObjectsOfAllTypes(ctx);
+
+ byte[] serializedTestObjectsBytes;
+
+ try (BinaryWriterExImpl writer = new BinaryWriterExImpl(ctx)) {
+ testObjects.forEach(writer::writeObject);
+
+ serializedTestObjectsBytes = writer.array();
+ }
+
+ RawBinaryObjectExtractor rawReader = new
RawBinaryObjectExtractor(BinaryHeapInputStream.create(serializedTestObjectsBytes,
0));
+
+ for (Object testObj : testObjects) {
+ byte[] objRawBytes = rawReader.extractObject();
+
+ try (BinaryReaderExImpl binReader = new BinaryReaderExImpl(ctx,
BinaryHeapInputStream.create(objRawBytes, 0), null, false)) {
+ Object deserializedObj = binReader.readObject();
+
+ if (testObj instanceof Proxy)
+ assertEquals(String.valueOf(testObj),
String.valueOf(deserializedObj));
+ else
+ assertEqualsArraysAware(testObj, deserializedObj);
+
+ assertEquals(objRawBytes.length, binReader.in().position());
+ }
+ }
+
+ assertEquals(serializedTestObjectsBytes.length, rawReader.position());
+ }
+
+ /** */
+ public static BinaryContext createTestBinaryContext() {
+ BinaryContext ctx = new
BinaryContext(BinaryCachingMetadataHandler.create(), new IgniteConfiguration(),
null);
+
+ BinaryMarshaller marsh = new BinaryMarshaller();
+
+ marsh.setContext(new TestMarshallerContext());
+
+ ctx.configure(marsh);
+
+ return ctx;
+ }
+
+ /** */
+ private static Collection<Object> createObjectsOfAllTypes(BinaryContext
ctx) throws IllegalAccessException {
+ Collection<Object> res = new ArrayList<>();
+
+ TestObjectAllTypesEx allTypesObj = new TestObjectAllTypesEx(ctx);
+
+ for (Field field : TestObjectAllTypesEx.class.getFields())
+ res.add(field.get(allTypesObj));
+
+ return res;
+ }
+
+ /** */
+ private Object createTestObject() {
+ TestObjectAllTypes res = new TestObjectAllTypes();
+
+ res.setDefaultData();
+
+ return res;
+ }
+
+ /** */
+ private interface RegisteredClass { }
+
+ /** */
+ private interface UnregisteredClass { }
+
+ /** */
+ private static class TestMarshallerContext implements MarshallerContext {
+ /** */
+ Map<Integer, String> clsNamesByTypeId = new HashMap<>();
+
+ /** {@inheritDoc} */
+ @Override public boolean registerClassName(
+ byte platformId,
+ int typeId,
+ String clsName
+ ) {
+ if (Objects.equals(clsName, UnregisteredClass.class.getName()))
+ return false;
+
+ clsNamesByTypeId.put(typeId, clsName);
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean registerClassNameLocally(byte platformId, int
typeId, String clsName) {
+ return registerClassName(platformId, typeId, clsName);
+ }
+
+ /** {@inheritDoc} */
+ @Override public Class<?> getClass(int typeId, ClassLoader ldr) throws
ClassNotFoundException {
+ return U.forName(clsNamesByTypeId.get(typeId), ldr);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String getClassName(byte platformId, int typeId) {
+ return clsNamesByTypeId.get(typeId);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isSystemType(String typeName) {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgnitePredicate<String> classNameFilter() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public JdkMarshaller jdkMarshaller() {
+ return new JdkMarshaller();
+ }
+ }
+
+ /** */
+ public static class TestInvocationHandler implements InvocationHandler {
+ /** */
+ private final String proxyClsName;
+
+ /** */
+ public TestInvocationHandler(Class<?> proxyCls) {
+ proxyClsName = proxyCls.getName();
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object invoke(Object p, Method method, Object[]
methodArgs) throws Throwable {
+ if ("toString".equals(method.getName()))
+ return proxyClsName;
+
+ throw new IllegalStateException();
+ }
+ }
+
+ /** */
+ public static class TestObjectAllTypesEx extends TestObjectAllTypes {
+ /** */
+ public Object nullObj;
+
+ /** */
+ public Object obj;
+
+ /** */
+ public Object[] emptyArr;
+
+ /** */
+ public Object[] objArr;
+
+ /** */
+ public Collection<Object> col;
+
+ /** */
+ public Map<Object, Object> map;
+
+ /** */
+ public Class<?> registeredClass;
+
+ /** */
+ public Class<?> unregisteredClass;
+
+ /** */
+ public Object registeredClsProxy;
+
+ /** */
+ public Object unregisteredClsProxy;
+
+ /** */
+ public BinaryObject binObj;
+
+ /** */
+ public TestObjectAllTypesEx(BinaryContext ctx) {
+ setDefaultData();
+
+ nullObj = null;
+ obj = new TestObjectAllTypes();
+
+ emptyArr = new Object[] {};
+ objArr = new Object[] {"test", new TestObjectAllTypes(), new
Object[]{new TestObjectAllTypes()}};
+
+ col = new ArrayList<>();
+
+ col.add(0);
+ col.add(new TestObjectAllTypes());
+ col.add(new ArrayList<>());
+ col.add(new HashMap<>());
+
+ map = new HashMap<>();
+
+ map.put(0, 0);
+ map.put(1, new TestObjectAllTypes());
+ map.put(2, new ArrayList<>());
+ map.put(3, new HashMap<>());
+
+ registeredClass = RegisteredClass.class;
+ unregisteredClass = UnregisteredClass.class;
+
+ registeredClsProxy = Proxy.newProxyInstance(
+ RawBinaryObjectExtractorTest.class.getClassLoader(),
+ new Class[] { RegisteredClass.class },
+ new TestInvocationHandler(RegisteredClass.class));
+
+ unregisteredClsProxy = Proxy.newProxyInstance(
+ RawBinaryObjectExtractorTest.class.getClassLoader(),
+ new Class[] { UnregisteredClass.class },
+ new TestInvocationHandler(UnregisteredClass.class));
+
+ binObj = new BinaryObjectBuilderImpl(ctx,
"TestBinaryType").setField("test-field", "test-value").build();
+ }
+ }
+}
diff --git
a/modules/core/src/test/java/org/apache/ignite/internal/binary/mutabletest/GridBinaryTestClasses.java
b/modules/core/src/test/java/org/apache/ignite/internal/binary/mutabletest/GridBinaryTestClasses.java
index 067866c094a..8d1ff662782 100644
---
a/modules/core/src/test/java/org/apache/ignite/internal/binary/mutabletest/GridBinaryTestClasses.java
+++
b/modules/core/src/test/java/org/apache/ignite/internal/binary/mutabletest/GridBinaryTestClasses.java
@@ -26,9 +26,11 @@ import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.TreeMap;
import java.util.UUID;
import com.google.common.base.Throwables;
@@ -317,6 +319,79 @@ public class GridBinaryTestClasses {
entry = new GridMapEntry<>(1, "a");
}
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (!(o instanceof TestObjectAllTypes))
+ return false;
+
+ TestObjectAllTypes allTypesObj = (TestObjectAllTypes)o;
+
+ return b == allTypesObj.b
+ && s == allTypesObj.s
+ && i == allTypesObj.i
+ && l == allTypesObj.l
+ && Float.compare(f, allTypesObj.f) == 0
+ && Double.compare(d, allTypesObj.d) == 0
+ && c == allTypesObj.c
+ && z == allTypesObj.z
+ && Objects.equals(b_, allTypesObj.b_)
+ && Objects.equals(s_, allTypesObj.s_)
+ && Objects.equals(i_, allTypesObj.i_)
+ && Objects.equals(bi_, allTypesObj.bi_)
+ && Objects.equals(l_, allTypesObj.l_)
+ && Objects.equals(f_, allTypesObj.f_)
+ && Objects.equals(d_, allTypesObj.d_)
+ && (bd_ == null ? allTypesObj.bd_ == null :
bd_.compareTo(allTypesObj.bd_) == 0)
+ && Objects.equals(c_, allTypesObj.c_)
+ && Objects.equals(z_, allTypesObj.z_)
+ && Objects.equals(str, allTypesObj.str)
+ && Objects.equals(uuid, allTypesObj.uuid)
+ && Objects.equals(date, allTypesObj.date)
+ && Objects.equals(ts, allTypesObj.ts)
+ && Arrays.equals(bArr, allTypesObj.bArr)
+ && Arrays.equals(sArr, allTypesObj.sArr)
+ && Arrays.equals(iArr, allTypesObj.iArr)
+ && Arrays.equals(lArr, allTypesObj.lArr)
+ && Arrays.equals(fArr, allTypesObj.fArr)
+ && Arrays.equals(dArr, allTypesObj.dArr)
+ && Arrays.equals(cArr, allTypesObj.cArr)
+ && Arrays.equals(zArr, allTypesObj.zArr)
+ && Arrays.equals(bdArr, allTypesObj.bdArr)
+ && Arrays.equals(strArr, allTypesObj.strArr)
+ && Arrays.equals(uuidArr, allTypesObj.uuidArr)
+ && Arrays.equals(dateArr, allTypesObj.dateArr)
+ && Arrays.equals(tsArr, allTypesObj.tsArr)
+ && anEnum == allTypesObj.anEnum
+ && Arrays.equals(enumArr, allTypesObj.enumArr)
+ && Objects.equals(entry, allTypesObj.entry);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ int res =
+ Objects.hash(b_, s_, i_, bi_, l_, f_, d_, bd_, c_, z_, b, s,
i, l, f, d, c, z, str, uuid, date, ts, anEnum, entry);
+
+ res = 31 * res + Arrays.hashCode(bArr);
+ res = 31 * res + Arrays.hashCode(sArr);
+ res = 31 * res + Arrays.hashCode(iArr);
+ res = 31 * res + Arrays.hashCode(lArr);
+ res = 31 * res + Arrays.hashCode(fArr);
+ res = 31 * res + Arrays.hashCode(dArr);
+ res = 31 * res + Arrays.hashCode(cArr);
+ res = 31 * res + Arrays.hashCode(zArr);
+ res = 31 * res + Arrays.hashCode(bdArr);
+ res = 31 * res + Arrays.hashCode(strArr);
+ res = 31 * res + Arrays.hashCode(uuidArr);
+ res = 31 * res + Arrays.hashCode(dateArr);
+ res = 31 * res + Arrays.hashCode(tsArr);
+ res = 31 * res + Arrays.hashCode(enumArr);
+
+ return res;
+ }
}
/**
diff --git
a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsBinarySortObjectFieldsTest.java
b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsBinarySortObjectFieldsTest.java
index b3d2e60a4cf..4103b22df88 100644
---
a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsBinarySortObjectFieldsTest.java
+++
b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsBinarySortObjectFieldsTest.java
@@ -19,7 +19,6 @@ package
org.apache.ignite.internal.processors.cache.persistence;
import java.util.concurrent.TimeUnit;
import org.apache.ignite.IgniteCache;
-import org.apache.ignite.IgniteSystemProperties;
import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
import org.apache.ignite.cluster.ClusterState;
import org.apache.ignite.configuration.CacheConfiguration;
@@ -27,8 +26,8 @@ import
org.apache.ignite.configuration.DataRegionConfiguration;
import org.apache.ignite.configuration.DataStorageConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.binary.BinaryUtils;
import org.apache.ignite.testframework.GridTestUtils;
-import org.apache.ignite.testframework.junits.WithSystemProperty;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.junit.Test;
@@ -78,19 +77,27 @@ public class IgnitePdsBinarySortObjectFieldsTest extends
GridCommonAbstractTest
* @throws Exception if failed.
*/
@Test
- @WithSystemProperty(key =
IgniteSystemProperties.IGNITE_BINARY_SORT_OBJECT_FIELDS, value = "true")
public void testGivenCacheWithPojoValueAndPds_WhenPut_ThenNoHangup()
throws Exception {
- IgniteEx ignite = startGrid(0);
+ boolean prev = BinaryUtils.FIELDS_SORTED_ORDER;
- ignite.cluster().state(ClusterState.ACTIVE);
+ BinaryUtils.FIELDS_SORTED_ORDER = true;
- final IgniteCache<Long, Value> cache = ignite.getOrCreateCache(
- new CacheConfiguration<Long, Value>(CACHE_NAME)
- .setAffinity(new
RendezvousAffinityFunction().setPartitions(32)));
+ try {
+ IgniteEx ignite = startGrid(0);
- GridTestUtils.assertTimeout(5, TimeUnit.SECONDS, () -> cache.put(1L,
new Value(1L)));
+ ignite.cluster().state(ClusterState.ACTIVE);
- assertEquals(1, cache.size());
+ final IgniteCache<Long, Value> cache = ignite.getOrCreateCache(
+ new CacheConfiguration<Long, Value>(CACHE_NAME)
+ .setAffinity(new
RendezvousAffinityFunction().setPartitions(32)));
+
+ GridTestUtils.assertTimeout(5, TimeUnit.SECONDS, () ->
cache.put(1L, new Value(1L)));
+
+ assertEquals(1, cache.size());
+ }
+ finally {
+ BinaryUtils.FIELDS_SORTED_ORDER = prev;
+ }
}
/**
diff --git
a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBinaryObjectsTestSuite.java
b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBinaryObjectsTestSuite.java
index b52df8fe875..9dc3ec83c00 100644
---
a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBinaryObjectsTestSuite.java
+++
b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBinaryObjectsTestSuite.java
@@ -41,11 +41,13 @@ import
org.apache.ignite.internal.binary.BinaryObjectTypeCompatibilityTest;
import
org.apache.ignite.internal.binary.BinarySerialiedFieldComparatorSelfTest;
import org.apache.ignite.internal.binary.BinarySimpleNameTestPropertySelfTest;
import org.apache.ignite.internal.binary.BinaryTreeSelfTest;
+import org.apache.ignite.internal.binary.CrossObjectReferenceSerializationTest;
import org.apache.ignite.internal.binary.GridBinaryAffinityKeySelfTest;
import
org.apache.ignite.internal.binary.GridBinaryMarshallerCtxDisabledSelfTest;
import org.apache.ignite.internal.binary.GridBinaryWildcardsSelfTest;
import
org.apache.ignite.internal.binary.GridDefaultBinaryMappersBinaryMetaDataSelfTest;
import
org.apache.ignite.internal.binary.GridSimpleLowerCaseBinaryMappersBinaryMetaDataSelfTest;
+import org.apache.ignite.internal.binary.RawBinaryObjectExtractorTest;
import
org.apache.ignite.internal.binary.noncompact.BinaryFieldsHeapNonCompactSelfTest;
import
org.apache.ignite.internal.binary.noncompact.BinaryFieldsOffheapNonCompactSelfTest;
import
org.apache.ignite.internal.binary.noncompact.BinaryFooterOffsetsHeapNonCompactSelfTest;
@@ -179,6 +181,9 @@ import org.junit.runners.Suite;
BinaryMetadataMoveLegacyFolderTest.class,
BinaryContextPredefinedTypesTest.class,
+
+ RawBinaryObjectExtractorTest.class,
+ CrossObjectReferenceSerializationTest.class,
})
public class IgniteBinaryObjectsTestSuite {
}