IGNITE-3635 - Fixed StackOverflowError thrown from BinaryObject.toString()
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/c0b2b479 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/c0b2b479 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/c0b2b479 Branch: refs/heads/master Commit: c0b2b4797be4f250f6f1304ff27d45c72154608a Parents: 98914fe Author: Valentin Kulichenko <valentin.luliche...@gmail.com> Authored: Fri Sep 16 14:59:35 2016 -0700 Committer: Valentin Kulichenko <valentin.luliche...@gmail.com> Committed: Fri Sep 16 14:59:35 2016 -0700 ---------------------------------------------------------------------- .../internal/binary/BinaryObjectExImpl.java | 161 +++++++++++++------ .../binary/BinaryObjectToStringSelfTest.java | 75 +++++++++ .../IgniteBinaryObjectsTestSuite.java | 2 + 3 files changed, 190 insertions(+), 48 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/c0b2b479/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectExImpl.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectExImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectExImpl.java index b4e909e..e6df407 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectExImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectExImpl.java @@ -20,14 +20,16 @@ package org.apache.ignite.internal.binary; import java.math.BigDecimal; import java.util.Arrays; import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.Map; import org.apache.ignite.IgniteException; +import org.apache.ignite.binary.BinaryObject; import org.apache.ignite.binary.BinaryObjectBuilder; +import org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.binary.BinaryType; import org.apache.ignite.internal.binary.builder.BinaryObjectBuilderImpl; import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeMemory; import org.apache.ignite.internal.util.typedef.internal.SB; -import org.apache.ignite.binary.BinaryObjectException; -import org.apache.ignite.binary.BinaryType; -import org.apache.ignite.binary.BinaryObject; import org.jetbrains.annotations.Nullable; /** @@ -164,6 +166,20 @@ public abstract class BinaryObjectExImpl implements BinaryObjectEx { } } + /** {@inheritDoc} */ + @Override public String toString() { + try { + BinaryReaderHandles ctx = new BinaryReaderHandles(); + + ctx.put(start(), this); + + return toString(ctx, new IdentityHashMap<BinaryObject, Integer>()); + } + catch (BinaryObjectException e) { + throw new IgniteException("Failed to create string representation of binary object.", e); + } + } + /** * @param ctx Reader context. * @param handles Handles for already traversed objects. @@ -197,43 +213,7 @@ public abstract class BinaryObjectExImpl implements BinaryObjectEx { buf.a(", ").a(name).a('='); - if (val instanceof byte[]) - buf.a(Arrays.toString((byte[]) val)); - else if (val instanceof short[]) - buf.a(Arrays.toString((short[])val)); - else if (val instanceof int[]) - buf.a(Arrays.toString((int[])val)); - else if (val instanceof long[]) - buf.a(Arrays.toString((long[])val)); - else if (val instanceof float[]) - buf.a(Arrays.toString((float[])val)); - else if (val instanceof double[]) - buf.a(Arrays.toString((double[])val)); - else if (val instanceof char[]) - buf.a(Arrays.toString((char[])val)); - else if (val instanceof boolean[]) - buf.a(Arrays.toString((boolean[]) val)); - else if (val instanceof BigDecimal[]) - buf.a(Arrays.toString((BigDecimal[])val)); - else { - if (val instanceof BinaryObjectExImpl) { - BinaryObjectExImpl po = (BinaryObjectExImpl)val; - - Integer idHash0 = handles.get(val); - - if (idHash0 != null) { // Circular reference. - BinaryType meta0 = po.rawType(); - - assert meta0 != null; - - buf.a(meta0.typeName()).a(" [hash=").a(idHash0).a(", ...]"); - } - else - buf.a(po.toString(ctx, handles)); - } - else - buf.a(val); - } + appendValue(val, buf, ctx, handles); } buf.a(']'); @@ -242,17 +222,102 @@ public abstract class BinaryObjectExImpl implements BinaryObjectEx { return buf.toString(); } - /** {@inheritDoc} */ - @Override public String toString() { - try { - BinaryReaderHandles ctx = new BinaryReaderHandles(); + /** + * @param val Value to append. + * @param buf Buffer to append to. + * @param ctx Reader context. + * @param handles Handles for already traversed objects. + */ + private void appendValue(Object val, SB buf, BinaryReaderHandles ctx, + IdentityHashMap<BinaryObject, Integer> handles) { + if (val instanceof byte[]) + buf.a(Arrays.toString((byte[]) val)); + else if (val instanceof short[]) + buf.a(Arrays.toString((short[])val)); + else if (val instanceof int[]) + buf.a(Arrays.toString((int[])val)); + else if (val instanceof long[]) + buf.a(Arrays.toString((long[])val)); + else if (val instanceof float[]) + buf.a(Arrays.toString((float[])val)); + else if (val instanceof double[]) + buf.a(Arrays.toString((double[])val)); + else if (val instanceof char[]) + buf.a(Arrays.toString((char[])val)); + else if (val instanceof boolean[]) + buf.a(Arrays.toString((boolean[]) val)); + else if (val instanceof BigDecimal[]) + buf.a(Arrays.toString((BigDecimal[])val)); + else if (val instanceof BinaryObjectExImpl) { + BinaryObjectExImpl po = (BinaryObjectExImpl)val; + + Integer idHash0 = handles.get(val); + + if (idHash0 != null) { // Circular reference. + BinaryType meta0 = po.rawType(); + + assert meta0 != null; + + buf.a(meta0.typeName()).a(" [hash=").a(idHash0).a(", ...]"); + } + else + buf.a(po.toString(ctx, handles)); + } + else if (val instanceof Object[]) { + Object[] arr = (Object[])val; - ctx.put(start(), this); + buf.a('['); - return toString(ctx, new IdentityHashMap<BinaryObject, Integer>()); + for (int i = 0; i < arr.length; i++) { + Object o = arr[i]; + + appendValue(o, buf, ctx, handles); + + if (i < arr.length - 1) + buf.a(", "); + } } - catch (BinaryObjectException e) { - throw new IgniteException("Failed to create string representation of binary object.", e); + else if (val instanceof Iterable) { + Iterable<Object> col = (Iterable<Object>)val; + + buf.a(col.getClass().getSimpleName()).a(" {"); + + Iterator it = col.iterator(); + + while (it.hasNext()) { + Object o = it.next(); + + appendValue(o, buf, ctx, handles); + + if (it.hasNext()) + buf.a(", "); + } + + buf.a('}'); + } + else if (val instanceof Map) { + Map<Object, Object> map = (Map<Object, Object>)val; + + buf.a(map.getClass().getSimpleName()).a(" {"); + + Iterator<Map.Entry<Object, Object>> it = map.entrySet().iterator(); + + while (it.hasNext()) { + Map.Entry<Object, Object> e = it.next(); + + appendValue(e.getKey(), buf, ctx, handles); + + buf.a('='); + + appendValue(e.getValue(), buf, ctx, handles); + + if (it.hasNext()) + buf.a(", "); + } + + buf.a('}'); } + else + buf.a(val); } } http://git-wip-us.apache.org/repos/asf/ignite/blob/c0b2b479/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryObjectToStringSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryObjectToStringSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryObjectToStringSelfTest.java new file mode 100644 index 0000000..cc6cf8b --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryObjectToStringSelfTest.java @@ -0,0 +1,75 @@ +package org.apache.ignite.internal.binary; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import org.apache.ignite.binary.BinaryObject; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi; +import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder; +import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; + +/** + * Tests for {@code BinaryObject.toString()}. + */ +public class BinaryObjectToStringSelfTest extends GridCommonAbstractTest { + /** */ + private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true); + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(gridName); + + cfg.setMarshaller(new BinaryMarshaller()); + cfg.setDiscoverySpi(new TcpDiscoverySpi().setIpFinder(IP_FINDER)); + + return cfg; + } + + /** {@inheritDoc} */ + @Override protected void beforeTest() throws Exception { + startGrid(); + } + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + stopAllGrids(); + } + + /** + * @throws Exception If failed. + */ + @SuppressWarnings("unchecked") + public void testToString() throws Exception { + MyObject obj = new MyObject(); + + obj.arr = new Object[] {111, "aaa", obj}; + obj.col = Arrays.asList(222, "bbb", obj); + + obj.map = new HashMap(); + + obj.map.put(10, 333); + obj.map.put(20, "ccc"); + obj.map.put(30, obj); + + BinaryObject bo = grid().binary().toBinary(obj); + + // Check that toString() doesn't fail with StackOverflowError or other exceptions. + bo.toString(); + } + + /** + */ + private static class MyObject { + /** Object array. */ + private Object[] arr; + + /** Collection. */ + private Collection col; + + /** Map. */ + private Map map; + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/c0b2b479/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBinaryObjectsTestSuite.java ---------------------------------------------------------------------- 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 dc0540d..c1d9974 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 @@ -30,6 +30,7 @@ import org.apache.ignite.internal.binary.BinaryMarshallerSelfTest; import org.apache.ignite.internal.binary.BinaryObjectBuilderAdditionalSelfTest; import org.apache.ignite.internal.binary.BinaryObjectBuilderDefaultMappersSelfTest; import org.apache.ignite.internal.binary.BinaryObjectBuilderSimpleNameLowerCaseMappersSelfTest; +import org.apache.ignite.internal.binary.BinaryObjectToStringSelfTest; import org.apache.ignite.internal.binary.BinarySimpleNameTestPropertySelfTest; import org.apache.ignite.internal.binary.BinaryTreeSelfTest; import org.apache.ignite.internal.binary.GridBinaryAffinityKeySelfTest; @@ -102,6 +103,7 @@ public class IgniteBinaryObjectsTestSuite extends TestSuite { suite.addTestSuite(GridSimpleLowerCaseBinaryMappersBinaryMetaDataSelfTest.class); suite.addTestSuite(GridBinaryAffinityKeySelfTest.class); suite.addTestSuite(GridBinaryWildcardsSelfTest.class); + suite.addTestSuite(BinaryObjectToStringSelfTest.class); // Tests for objects with non-compact footers. suite.addTestSuite(BinaryMarshallerNonCompactSelfTest.class);