You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2020/04/06 14:22:11 UTC

[juneau] branch master updated: ignoreRecursions should not depend on detectRecursions.

This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 1a827ef  ignoreRecursions should not depend on detectRecursions.
1a827ef is described below

commit 1a827efdcf57a5661db2d350c3536435cbda60e3
Author: JamesBognar <ja...@apache.org>
AuthorDate: Mon Apr 6 10:21:51 2020 -0400

    ignoreRecursions should not depend on detectRecursions.
---
 .../org/apache/juneau/BeanTraverseBuilder.java     | 115 ++++++++++++-
 .../org/apache/juneau/BeanTraverseContext.java     |  56 +++---
 .../apache/juneau/http/SerializedHttpEntity.java   |   3 +-
 juneau-doc/docs/ReleaseNotes/8.1.4.html            |   3 +
 .../juneau/rest/client2/RestClientBuilderTest.java | 191 +++++++++++++++------
 .../apache/juneau/rest/client2/RestRequest.java    |   2 +-
 6 files changed, 282 insertions(+), 88 deletions(-)

diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanTraverseBuilder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanTraverseBuilder.java
index 6330c07..03d81f0 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanTraverseBuilder.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanTraverseBuilder.java
@@ -54,11 +54,35 @@ public class BeanTraverseBuilder extends BeanContextBuilder {
 	 * <p>
 	 * Specifies that recursions should be checked for during traversal.
 	 *
+	 * <p>
+	 * Recursions can occur when traversing models that aren't true trees but rather contain loops.
+	 * <br>In general, unchecked recursions cause stack-overflow-errors.
+	 * <br>These show up as {@link BeanRecursionException BeanRecursionException} with the message <js>"Depth too deep.  Stack overflow occurred."</js>.
+	 *
 	 * <ul class='notes'>
 	 * 	<li>
 	 * 		Checking for recursion can cause a small performance penalty.
 	 * </ul>
 	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<jc>// Create a serializer that never adds _type to nodes.</jc>
+	 * 	WriterSerializer s = JsonSerializer
+	 * 		.<jsm>create</jsm>()
+	 * 		.detectRecursions(<jk>true</jk>)
+	 * 		.build();
+	 *
+	 * 	<jc>// Create a POJO model with a recursive loop.</jc>
+	 * 	<jk>public class</jk> A {
+	 * 		<jk>public</jk> Object <jf>f</jf>;
+	 * 	}
+	 * 	A a = <jk>new</jk> A();
+	 * 	a.<jf>f</jf> = a;
+	 *
+	 * 	<jc>// Throws a SerializeException</jc>
+	 * 	String json = s.serialize(a);
+	 * </p>
+	 *
 	 * <ul class='seealso'>
 	 * 	<li class='jf'>{@link BeanTraverseContext#BEANTRAVERSE_detectRecursions}
 	 * </ul>
@@ -77,7 +101,36 @@ public class BeanTraverseBuilder extends BeanContextBuilder {
 	 * Configuration property:  Automatically detect POJO recursions.
 	 *
 	 * <p>
-	 * Shortcut for calling <code>detectRecursions(<jk>true</jk>)</code>.
+	 * Specifies that recursions should be checked for during traversal.
+	 *
+	 * <p>
+	 * Recursions can occur when traversing models that aren't true trees but rather contain loops.
+	 * <br>In general, unchecked recursions cause stack-overflow-errors.
+	 * <br>These show up as {@link BeanRecursionException BeanRecursionException} with the message <js>"Depth too deep.  Stack overflow occurred."</js>.
+	 *
+	 * <ul class='notes'>
+	 * 	<li>
+	 * 		Checking for recursion can cause a small performance penalty.
+	 * </ul>
+	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<jc>// Create a serializer that never adds _type to nodes.</jc>
+	 * 	WriterSerializer s = JsonSerializer
+	 * 		.<jsm>create</jsm>()
+	 * 		.detectRecursions()
+	 * 		.build();
+	 *
+	 * 	<jc>// Create a POJO model with a recursive loop.</jc>
+	 * 	<jk>public class</jk> A {
+	 * 		<jk>public</jk> Object <jf>f</jf>;
+	 * 	}
+	 * 	A a = <jk>new</jk> A();
+	 * 	a.<jf>f</jf> = a;
+	 *
+	 * 	<jc>// Throws a SerializeException</jc>
+	 * 	String json = s.serialize(a);
+	 * </p>
 	 *
 	 * <ul class='seealso'>
 	 * 	<li class='jf'>{@link BeanTraverseContext#BEANTRAVERSE_detectRecursions}
@@ -95,12 +148,33 @@ public class BeanTraverseBuilder extends BeanContextBuilder {
 	 *
 	 * <p>
 	 * If <jk>true</jk>, when we encounter the same object when traversing a tree, we set the value to <jk>null</jk>.
-	 * Otherwise, an exception is thrown.
 	 *
-	 * <ul class='notes'>
-	 * 	<li>
-	 * 		Checking for recursion can cause a small performance penalty.
-	 * </ul>
+	 * <p>
+	 * For example, if a model contains the links A-&gt;B-&gt;C-&gt;A, then the JSON generated will look like
+	 * 	the following when <jsf>BEANTRAVERSE_ignoreRecursions</jsf> is <jk>true</jk>...
+	 *
+	 * <p class='bcode w800'>
+	 * 	{A:{B:{C:<jk>null</jk>}}}
+	 * </p>
+	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<jc>// Create a serializer that never adds _type to nodes.</jc>
+	 * 	WriterSerializer s = JsonSerializer
+	 * 		.<jsm>create</jsm>()
+	 * 		.ignoreRecursions(<jk>true</jk>)
+	 * 		.build();
+	 *
+	 * 	<jc>// Create a POJO model with a recursive loop.</jc>
+	 * 	<jk>public class</jk> A {
+	 * 		<jk>public</jk> Object <jf>f</jf>;
+	 * 	}
+	 * 	A a = <jk>new</jk> A();
+	 * 	a.<jf>f</jf> = a;
+	 *
+	 * 	<jc>// Produces "{f:null}"</jc>
+	 * 	String json = s.serialize(a);
+	 * </p>
 	 *
 	 * <ul class='seealso'>
 	 * 	<li class='jf'>{@link BeanTraverseContext#BEANTRAVERSE_ignoreRecursions}
@@ -120,7 +194,34 @@ public class BeanTraverseBuilder extends BeanContextBuilder {
 	 * Configuration property:  Ignore recursion errors.
 	 *
 	 * <p>
-	 * Shortcut for calling <code>ignoreRecursions(<jk>true</jk>)</code>.
+	 * When enabled, when we encounter the same object when traversing a tree, we set the value to <jk>null</jk>.
+	 *
+	 * <p>
+	 * For example, if a model contains the links A-&gt;B-&gt;C-&gt;A, then the JSON generated will look like
+	 * 	the following when <jsf>BEANTRAVERSE_ignoreRecursions</jsf> is <jk>true</jk>...
+	 *
+	 * <p class='bcode w800'>
+	 * 	{A:{B:{C:<jk>null</jk>}}}
+	 * </p>
+	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<jc>// Create a serializer that never adds _type to nodes.</jc>
+	 * 	WriterSerializer s = JsonSerializer
+	 * 		.<jsm>create</jsm>()
+	 * 		.ignoreRecursions()
+	 * 		.build();
+	 *
+	 * 	<jc>// Create a POJO model with a recursive loop.</jc>
+	 * 	<jk>public class</jk> A {
+	 * 		<jk>public</jk> Object <jf>f</jf>;
+	 * 	}
+	 * 	A a = <jk>new</jk> A();
+	 * 	a.<jf>f</jf> = a;
+	 *
+	 * 	<jc>// Produces "{f:null}"</jc>
+	 * 	String json = s.serialize(a);
+	 * </p>
 	 *
 	 * <ul class='seealso'>
 	 * 	<li class='jf'>{@link BeanTraverseContext#BEANTRAVERSE_ignoreRecursions}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanTraverseContext.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanTraverseContext.java
index ee5cb95..dcad339 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanTraverseContext.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanTraverseContext.java
@@ -15,7 +15,6 @@ package org.apache.juneau;
 
 import org.apache.juneau.annotation.*;
 import org.apache.juneau.collections.*;
-import org.apache.juneau.parser.*;
 
 /**
  * Parent class for all classes that traverse POJOs.
@@ -63,18 +62,7 @@ public abstract class BeanTraverseContext extends BeanContext {
 	 * <p>
 	 * Recursions can occur when traversing models that aren't true trees but rather contain loops.
 	 * <br>In general, unchecked recursions cause stack-overflow-errors.
-	 * <br>These show up as {@link ParseException ParseExceptions} with the message <js>"Depth too deep.  Stack overflow occurred."</js>.
-	 *
-	 * <p>
-	 * The behavior when recursions are detected depends on the value for {@link #BEANTRAVERSE_ignoreRecursions}.
-	 *
-	 * <p>
-	 * For example, if a model contains the links A-&gt;B-&gt;C-&gt;A, then the JSON generated will look like
-	 * 	the following when <jsf>BEANTRAVERSE_ignoreRecursions</jsf> is <jk>true</jk>...
-	 *
-	 * <p class='bcode w800'>
-	 * 	{A:{B:{C:<jk>null</jk>}}}
-	 * </p>
+	 * <br>These show up as {@link BeanRecursionException BeanRecursionException} with the message <js>"Depth too deep.  Stack overflow occurred."</js>.
 	 *
 	 * <ul class='notes'>
 	 * 	<li>
@@ -86,15 +74,7 @@ public abstract class BeanTraverseContext extends BeanContext {
 	 * 	<jc>// Create a serializer that never adds _type to nodes.</jc>
 	 * 	WriterSerializer s = JsonSerializer
 	 * 		.<jsm>create</jsm>()
-	 * 		.detectRecursions()
-	 * 		.ignoreRecursions()
-	 * 		.build();
-	 *
-	 * 	<jc>// Same, but use property.</jc>
-	 * 	WriterSerializer s = JsonSerializer
-	 * 		.<jsm>create</jsm>()
 	 * 		.set(<jsf>BEANTRAVERSE_detectRecursions</jsf>, <jk>true</jk>)
-	 * 		.set(<jsf>BEANTRAVERSE_ignoreRecursions</jsf>, <jk>true</jk>)
 	 * 		.build();
 	 *
 	 * 	<jc>// Create a POJO model with a recursive loop.</jc>
@@ -104,7 +84,7 @@ public abstract class BeanTraverseContext extends BeanContext {
 	 * 	A a = <jk>new</jk> A();
 	 * 	a.<jf>f</jf> = a;
 	 *
-	 * 	<jc>// Produces "{f:null}"</jc>
+	 * 	<jc>// Throws a SerializeException</jc>
 	 * 	String json = s.serialize(a);
 	 * </p>
 	 */
@@ -135,12 +115,34 @@ public abstract class BeanTraverseContext extends BeanContext {
 	 *
 	 * <h5 class='section'>Description:</h5>
 	 * <p>
-	 * Used in conjunction with {@link #BEANTRAVERSE_detectRecursions}.
-	 * <br>Setting is ignored if <jsf>BEANTRAVERSE_detectRecursions</jsf> is <jk>false</jk>.
+	 * If <jk>true</jk>, when we encounter the same object when traversing a tree, we set the value to <jk>null</jk>.
 	 *
 	 * <p>
-	 * If <jk>true</jk>, when we encounter the same object when traversing a tree, we set the value to <jk>null</jk>.
-	 * <br>Otherwise, a {@link BeanRecursionException} is thrown with the message <js>"Recursion occurred, stack=..."</js>.
+	 * For example, if a model contains the links A-&gt;B-&gt;C-&gt;A, then the JSON generated will look like
+	 * 	the following when <jsf>BEANTRAVERSE_ignoreRecursions</jsf> is <jk>true</jk>...
+	 *
+	 * <p class='bcode w800'>
+	 * 	{A:{B:{C:<jk>null</jk>}}}
+	 * </p>
+	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<jc>// Create a serializer that never adds _type to nodes.</jc>
+	 * 	WriterSerializer s = JsonSerializer
+	 * 		.<jsm>create</jsm>()
+	 * 		.set(<jsf>BEANTRAVERSE_ignoreRecursions</jsf>, <jk>true</jk>)
+	 * 		.build();
+	 *
+	 * 	<jc>// Create a POJO model with a recursive loop.</jc>
+	 * 	<jk>public class</jk> A {
+	 * 		<jk>public</jk> Object <jf>f</jf>;
+	 * 	}
+	 * 	A a = <jk>new</jk> A();
+	 * 	a.<jf>f</jf> = a;
+	 *
+	 * 	<jc>// Produces "{f:null}"</jc>
+	 * 	String json = s.serialize(a);
+	 * </p>
 	 */
 	public static final String BEANTRAVERSE_ignoreRecursions = PREFIX + ".ignoreRecursions.b";
 
@@ -257,8 +259,8 @@ public abstract class BeanTraverseContext extends BeanContext {
 
 		maxDepth = getIntegerProperty(BEANTRAVERSE_maxDepth, 100);
 		initialDepth = getIntegerProperty(BEANTRAVERSE_initialDepth, 0);
-		detectRecursions = getBooleanProperty(BEANTRAVERSE_detectRecursions, false);
 		ignoreRecursions = getBooleanProperty(BEANTRAVERSE_ignoreRecursions, false);
+		detectRecursions = getBooleanProperty(BEANTRAVERSE_detectRecursions, ignoreRecursions);
 	}
 
 	@Override /* Context */
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/SerializedHttpEntity.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/SerializedHttpEntity.java
index 0026a12..b0bdefb 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/SerializedHttpEntity.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/SerializedHttpEntity.java
@@ -17,6 +17,7 @@ import static org.apache.juneau.internal.IOUtils.*;
 import java.io.*;
 
 import org.apache.http.entity.*;
+import org.apache.juneau.*;
 import org.apache.juneau.httppart.*;
 import org.apache.juneau.internal.*;
 import org.apache.juneau.serializer.*;
@@ -69,7 +70,7 @@ public class SerializedHttpEntity extends BasicHttpEntity {
 					}
 				}
 			} catch (SerializeException e) {
-				throw new IOException(e);
+				throw new BasicRuntimeException(e, "Serialization error on request body.");
 			}
 		}
 	}
diff --git a/juneau-doc/docs/ReleaseNotes/8.1.4.html b/juneau-doc/docs/ReleaseNotes/8.1.4.html
index 49a3475..80557d1 100644
--- a/juneau-doc/docs/ReleaseNotes/8.1.4.html
+++ b/juneau-doc/docs/ReleaseNotes/8.1.4.html
@@ -183,6 +183,9 @@
 			<li class='jm'>{@link oaj.http.annotation.FormData#multi()}
 		</ul>
 	<li>
+		{@link oaj.BeanContext#BEAN_ignoreRecursions} setting no longer requires {@link oaj.BeanContext#BEAN_detectRecursions} 
+		to be enabled.
+	<li>
 		HTML-Schema support is being deprecated due to low-use and difficulty in maintaining.  It will be removed in 9.0.
 </ul>
 
diff --git a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RestClientBuilderTest.java b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RestClientBuilderTest.java
index e5ccd22..72798fc 100644
--- a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RestClientBuilderTest.java
+++ b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RestClientBuilderTest.java
@@ -2233,58 +2233,145 @@ public class RestClientBuilderTest {
 		assertEquals("{f:1}", b.toString());
 	}
 
-//	@Test
-//	public void k21_restClient_serializerClass() throws Exception { fail(); }
-////	public RestClientBuilder serializer(Class<? extends Serializer> value) {
-//
-//	@Test
-//	public void k22_restClient_serializerObject() throws Exception { fail(); }
-////	public RestClientBuilder serializer(Serializer value) {
-//
-//	@Test
-//	public void k23_restClient_serializersClasses() throws Exception { fail(); }
-////	public RestClientBuilder serializers(Class<? extends Serializer>...value) {
-//
-//	@Test
-//	public void k24_restClient_serializersObjects() throws Exception { fail(); }
-////	public RestClientBuilder serializers(Serializer...value) {
-//
-//	//-----------------------------------------------------------------------------------------------------------------
-//	// Serializer properties
-//	//-----------------------------------------------------------------------------------------------------------------
-//
-//	@Test
-//	public void l01_serializer_addBeanTypesBoolean() throws Exception { fail(); }
-////	public RestClientBuilder addBeanTypes(boolean value) {
-//
-//	@Test
-//	public void l02_serializer_addBeanTypes() throws Exception { fail(); }
-////	public RestClientBuilder addBeanTypes() {
-//
-//	@Test
-//	public void l03_serializer_addRootTypeBoolean() throws Exception { fail(); }
-////	public RestClientBuilder addRootType(boolean value) {
-//
-//	@Test
-//	public void l04_serializer_addRootType() throws Exception { fail(); }
-////	public RestClientBuilder addRootType() {
-//
-//	@Test
-//	public void l05_serializer_detectRecursionsBoolean() throws Exception { fail(); }
-////	public RestClientBuilder detectRecursions(boolean value) {
-//
-//	@Test
-//	public void l06_serializer_detectRecursions() throws Exception { fail(); }
-////	public RestClientBuilder detectRecursions() {
-//
-//	@Test
-//	public void l07_serializer_ignoreRecursionsBoolean() throws Exception { fail(); }
-////	public RestClientBuilder ignoreRecursions(boolean value) {
-//
-//	@Test
-//	public void l08_serializer_ignoreRecursions() throws Exception { fail(); }
-////	public RestClientBuilder ignoreRecursions() {
-//
+	//-----------------------------------------------------------------------------------------------------------------
+	// Serializer properties
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Rest
+	public static class L extends BasicRest {
+		@RestMethod(path="/")
+		public Reader post(org.apache.juneau.rest.RestRequest req) throws IOException {
+			return req.getBody().getReader();
+		}
+	}
+
+	public static class L1 {
+		public Object f1;
+
+		public static L1 create() {
+			L1 l = new L1();
+			l.f1 = L2.create();
+			return l;
+		}
+	}
+
+	@org.apache.juneau.annotation.Bean(typeName="L")
+	public static class L2 {
+		public int f2;
+
+		public static L2 create() {
+			L2 l = new L2();
+			l.f2 = 1;
+			return l;
+		}
+	}
+
+	@Test
+	public void l01a_serializer_addBeanTypes() throws Exception {
+		RestClient rc = MockRestClient
+			.create(L.class)
+			.simpleJson()
+			.addBeanTypes(true)
+			.build();
+		rc.post("", L1.create()).run().getBody().assertValue("{f1:{_type:'L',f2:1}}");
+
+		rc = MockRestClient
+			.create(L.class)
+			.simpleJson()
+			.addBeanTypes(false)
+			.build();
+		rc.post("", L1.create()).run().getBody().assertValue("{f1:{f2:1}}");
+
+		rc = MockRestClient
+			.create(L.class)
+			.simpleJson()
+			.addBeanTypes()
+			.build();
+		rc.post("", L1.create()).run().getBody().assertValue("{f1:{_type:'L',f2:1}}");
+	}
+
+	@Test
+	public void l03_serializer_addRootType() throws Exception {
+		RestClient rc = MockRestClient
+			.create(L.class)
+			.simpleJson()
+			.addRootType(true)
+			.build();
+		rc.post("", L2.create()).run().getBody().assertValue("{f2:1}");
+
+		rc = MockRestClient
+			.create(L.class)
+			.simpleJson()
+			.addBeanTypes()
+			.addRootType(false)
+			.build();
+		rc.post("", L2.create()).run().getBody().assertValue("{f2:1}");
+
+		rc = MockRestClient
+			.create(L.class)
+			.simpleJson()
+			.addBeanTypes()
+			.addRootType(true)
+			.build();
+		rc.post("", L2.create()).run().getBody().assertValue("{_type:'L',f2:1}");
+
+		rc = MockRestClient
+			.create(L.class)
+			.simpleJson()
+			.addBeanTypes()
+			.addRootType()
+			.build();
+		rc.post("", L2.create()).run().getBody().assertValue("{_type:'L',f2:1}");
+	}
+
+	@Test
+	public void l05_serializer_detectRecursions() throws Exception {
+		L1 l1 = new L1();
+		l1.f1 = l1;
+
+		RestClient rc = MockRestClient
+			.create(L.class)
+			.simpleJson()
+			.detectRecursions()
+			.build();
+		try {
+			rc.post("", l1).run();
+		} catch (RestCallException e) {
+			assertTrue(e.getCause().getCause().getMessage().startsWith("Recursion occurred"));
+		}
+
+		rc = MockRestClient
+			.create(L.class)
+			.simpleJson()
+			.detectRecursions(true)
+			.build();
+		try {
+			rc.post("", l1).run();
+		} catch (RestCallException e) {
+			assertTrue(e.getCause().getCause().getMessage().startsWith("Recursion occurred"));
+		}
+	}
+
+	@Test
+	public void l07_serializer_ignoreRecursions() throws Exception {
+		L1 l1 = new L1();
+		l1.f1 = l1;
+
+		RestClient rc = MockRestClient
+			.create(L.class)
+			.simpleJson()
+			.ignoreRecursions()
+			.build();
+		rc.post("", l1).run().getBody().assertValue("{}");
+
+		rc = MockRestClient
+			.create(L.class)
+			.simpleJson()
+			.ignoreRecursions(true)
+			.build();
+		rc.post("", l1).run().getBody().assertValue("{}");
+	}
+
 //	@Test
 //	public void l09_serializer_initialDepth() throws Exception { fail(); }
 ////	public RestClientBuilder initialDepth(int value) {
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestRequest.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestRequest.java
index 4daf6c5..b13ae8a 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestRequest.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestRequest.java
@@ -2378,7 +2378,7 @@ public final class RestRequest extends BeanSession implements HttpUriRequest, Co
 		} catch (Exception e) {
 			if (response != null)
 				response.close();
-			throw e instanceof RestCallException ? (RestCallException)e : new RestCallException(e).setRestResponse(response);
+			throw RestCallException.create(e).setRestResponse(response);
 		}
 
 		return this.response;