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 3aeae88  RestClient tests
3aeae88 is described below

commit 3aeae88610af3628f4ce96b1587c8c867ce23c09
Author: JamesBognar <[email protected]>
AuthorDate: Thu Jun 18 13:34:27 2020 -0400

    RestClient tests
---
 .../org/apache/juneau/utils/MethodInvokerTest.java |   5 +-
 .../org/apache/juneau/mstat/MethodExecStats.java   |   3 +-
 .../juneau/serializer/SerializerSession.java       |   2 +-
 .../org/apache/juneau/utils/MethodExecStats.java   | 181 ------------
 .../org/apache/juneau/utils/MethodInvoker.java     |   2 +
 .../apache/juneau/rest/client2/MarshallsTest.java  |  83 ++++++
 .../apache/juneau/rest/client2/RestClientTest.java | 314 +++++++++++++++++++++
 .../rest/client2/BasicHttpEntityRequestBase.java}  |  56 ++--
 .../juneau/rest/client2/BasicHttpRequestBase.java} |  54 +---
 .../apache/juneau/rest/client2/RestRequest.java    |  58 ++--
 .../apache/juneau/rest/client2/RestResponse.java   |   9 +
 .../apache/juneau/rest/mock2/MockRestRequest.java  |   1 +
 .../java/org/apache/juneau/rest/RestContext.java   |   1 +
 .../org/apache/juneau/rest/RestContextStats.java   |   2 +-
 14 files changed, 465 insertions(+), 306 deletions(-)

diff --git 
a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/utils/MethodInvokerTest.java
 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/utils/MethodInvokerTest.java
index d904e7c..fe3512d 100644
--- 
a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/utils/MethodInvokerTest.java
+++ 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/utils/MethodInvokerTest.java
@@ -18,6 +18,7 @@ import static org.junit.runners.MethodSorters.*;
 
 import java.lang.reflect.*;
 
+import org.apache.juneau.mstat.*;
 import org.junit.*;
 
 @FixMethodOrder(NAME_ASCENDING)
@@ -62,7 +63,7 @@ public class MethodInvokerTest {
                        mi.invoke(a);
                } catch (Exception e) {}
 
-               
assertObjectMatches("{method:'A.bar',runs:3,running:0,errors:3,minTime:*,maxTime:*,avgTime:*,totalTime:*,exceptions:[{exception:'RuntimeException',hash:'*',count:3}]}",
 mes);
+               
assertObjectMatches("{method:'A.bar',runs:3,running:0,errors:3,minTime:*,maxTime:*,avgTime:*,totalTime:*,exceptions:[{hash:'*',count:3,exceptionClass:*,message:*,stackTrace:*}]}",
 mes);
        }
 
        @Test
@@ -83,7 +84,7 @@ public class MethodInvokerTest {
                        mi.invoke(a, 1, "x");
                } catch (Exception e) {}
 
-               
assertObjectMatches("{method:'A.baz',runs:3,running:0,errors:3,minTime:*,maxTime:*,avgTime:*,totalTime:*,exceptions:[{exception:'IllegalArgumentException',hash:'*',count:3}]}",
 mes);
+               
assertObjectMatches("{method:'A.baz',runs:3,running:0,errors:3,minTime:*,maxTime:*,avgTime:*,totalTime:*,exceptions:[{hash:'*',count:3,exceptionClass:*,message:*,stackTrace:*}]}",
 mes);
        }
 
        @Test
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/mstat/MethodExecStats.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/mstat/MethodExecStats.java
index 7d81ba4..598c53a 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/mstat/MethodExecStats.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/mstat/MethodExecStats.java
@@ -149,7 +149,8 @@ public class MethodExecStats implements 
Comparable<MethodExecStats> {
         * @return The average execution time in milliseconds.
         */
        public int getAvgTime() {
-               return (int)(getTotalTime() / getRuns());
+               int runs = getRuns();
+               return runs == 0 ? 0 : (int)(getTotalTime() / runs);
        }
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerSession.java
index 463e5dd..599ab26 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerSession.java
@@ -264,7 +264,7 @@ public abstract class SerializerSession extends 
BeanTraverseSession {
                        p.getName(), p.getBeanMeta().getClassMeta(), 
t.getLocalizedMessage());
                if (! isIgnoreInvocationExceptionsOnGetters())
                        throw new SerializeException(this, "{0}Could not call 
getValue() on property ''{1}'' of class ''{2}'', exception = {3}", prefix,
-                               p.getName(), p.getBeanMeta().getClassMeta(), 
t.getLocalizedMessage());
+                               p.getName(), p.getBeanMeta().getClassMeta(), 
t.getLocalizedMessage()).initCause(t);
        }
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MethodExecStats.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MethodExecStats.java
deleted file mode 100644
index f27c6a3..0000000
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MethodExecStats.java
+++ /dev/null
@@ -1,181 +0,0 @@
-// 
***************************************************************************************************************************
-// * 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.juneau.utils;
-
-import java.lang.reflect.*;
-import java.util.*;
-import java.util.concurrent.atomic.*;
-
-import org.apache.juneau.annotation.*;
-import org.apache.juneau.marshall.*;
-
-/**
- * Basic timing information.
- *
- * Keeps track of number of starts/finishes on tasks and keeps an average run 
time.
- */
-@Bean(bpi="method,runs,running,errors,minTime,maxTime,avgTime,totalTime,exceptions")
-public class MethodExecStats implements Comparable<MethodExecStats> {
-
-       private String method;
-       private volatile int minTime = -1, maxTime;
-
-       private AtomicInteger
-               starts = new AtomicInteger(),
-               finishes = new AtomicInteger(),
-               errors = new AtomicInteger();
-
-       private AtomicLong
-               totalTime = new AtomicLong();
-
-       private StackTraceDatabase stackTraceDb;
-
-       /**
-        * Constructor.
-        *
-        * @param method Arbitrary label.  Should be kept to less than 50 
characters.
-        * @param stackTraceStopClass Don't calculate stack traces when this 
class is encountered.
-        */
-       public MethodExecStats(Method method, Class<?> stackTraceStopClass) {
-               this.method = method.getDeclaringClass().getSimpleName() + "." 
+ method.getName();
-               this.stackTraceDb = new StackTraceDatabase(-1, 
stackTraceStopClass);
-       }
-
-       /**
-        * Constructor.
-        *
-        * @param method Arbitrary label.  Should be kept to less than 50 
characters.
-        */
-       public MethodExecStats(Method method) {
-               this(method, MethodInvoker.class);
-       }
-
-       /**
-        * Call when task is started.
-        */
-       public void started() {
-               starts.incrementAndGet();
-       }
-
-       /**
-        * Call when task is finished.
-        * @param nanoTime The execution time of the task in nanoseconds.
-        */
-       public void finished(long nanoTime) {
-               finishes.incrementAndGet();
-               int milliTime = (int)(nanoTime/1_000_000);
-               totalTime.addAndGet(nanoTime);
-               minTime = minTime == -1 ? milliTime : Math.min(minTime, 
milliTime);
-               maxTime = Math.max(maxTime, milliTime);
-       }
-
-       /**
-        * Call when an error occurs.
-        * @param e The exception thrown.  Can be <jk>null</jk>.
-        */
-       public void error(Throwable e) {
-               errors.incrementAndGet();
-               stackTraceDb.add(e);
-       }
-
-       /**
-        * Returns the method name of these stats.
-        *
-        * @return The method name of these stats.
-        */
-       public String getMethod() {
-               return method;
-       }
-
-       /**
-        * Returns the number of times the {@link #started()} method was called.
-        *
-        * @return The number of times the {@link #started()} method was called.
-        */
-       public int getRuns() {
-               return starts.get();
-       }
-
-       /**
-        * Returns the number currently running method invocations.
-        *
-        * @return The number of currently running method invocations.
-        */
-       public int getRunning() {
-               return starts.get() - finishes.get();
-       }
-
-       /**
-        * Returns the number of times the {@link #error(Throwable)} method was 
called.
-        *
-        * @return The number of times the {@link #error(Throwable)} method was 
called.
-        */
-       public int getErrors() {
-               return errors.get();
-       }
-
-       /**
-        * Returns the max execution time.
-        *
-        * @return The average execution time in milliseconds.
-        */
-       public int getMinTime() {
-               return minTime == -1 ? 0 : minTime;
-       }
-
-       /**
-        * Returns the max execution time.
-        *
-        * @return The average execution time in milliseconds.
-        */
-       public int getMaxTime() {
-               return maxTime;
-       }
-
-       /**
-        * Returns the average execution time.
-        *
-        * @return The average execution time in milliseconds.
-        */
-       public int getAvgTime() {
-               return (int)(getTotalTime() / getRuns());
-       }
-
-       /**
-        * Returns the total execution time.
-        *
-        * @return The total execution time in milliseconds.
-        */
-       public long getTotalTime() {
-               return totalTime.get() / 1_000_000;
-       }
-
-       /**
-        * Returns information on all stack traces of all exceptions 
encountered.
-        *
-        * @return Information on all stack traces of all exceptions 
encountered.
-        */
-       public List<StackTraceInfo> getExceptions() {
-               return stackTraceDb.getClonedStackTraceInfos();
-       }
-
-       @Override /* Object */
-       public String toString() {
-               return SimpleJson.DEFAULT.toString(this);
-       }
-
-       @Override /* Comparable */
-       public int compareTo(MethodExecStats o) {
-               return Long.compare(o.getTotalTime(), getTotalTime());
-       }
-}
\ No newline at end of file
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MethodInvoker.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MethodInvoker.java
index 393128f..d89206a 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MethodInvoker.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MethodInvoker.java
@@ -14,6 +14,8 @@ package org.apache.juneau.utils;
 
 import java.lang.reflect.*;
 
+import org.apache.juneau.mstat.*;
+
 /**
  * A wrapper around a {@link Method#invoke(Object, Object...)} method that 
allows for basic instrumentation.
  */
diff --git 
a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/MarshallsTest.java
 
b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/MarshallsTest.java
index c664ca2..13b9d8f 100644
--- 
a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/MarshallsTest.java
+++ 
b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/MarshallsTest.java
@@ -66,6 +66,8 @@ public class MarshallsTest {
        // Basic tests - Single language support
        
//------------------------------------------------------------------------------------------------------------------
 
+       private static RestClient a1 = MockRestClient.create(A.class).build();
+
        private static RestClient a1a = 
MockRestClient.create(A.class).simpleJson().build();
        private static RestClient a1b = 
MockRestClient.create(A.class).json().build();
        private static RestClient a1c = 
MockRestClient.create(A.class).xml().build();
@@ -149,6 +151,87 @@ public class MarshallsTest {
        }
 
        @Test
+       public void a01a_singleLanguages_perRequest() throws Exception {
+               a1.post("/a01", bean)
+                       .header("X-Accept", "application/json+simple")
+                       .header("X-Content-Type", "application/json+simple")
+                       .simpleJson()
+                       .run()
+                       .assertStatus().is(200)
+                       .getBody().as(Bean.class).check();
+               a1.post("/a01", bean)
+                       .header("X-Accept", "application/json")
+                       .header("X-Content-Type", "application/json")
+                       .json()
+                       .run()
+                       .assertStatus().is(200)
+                       .getBody().as(Bean.class).check();
+               a1.post("/a01", bean)
+                       .header("X-Accept", "text/xml")
+                       .header("X-Content-Type", "text/xml")
+                       .xml()
+                       .run()
+                       .assertStatus().is(200)
+                       .getBody().as(Bean.class).check();
+               a1.post("/a01", bean)
+                       .header("X-Accept", "text/html")
+                       .header("X-Content-Type", "text/html")
+                       .html()
+                       .run()
+                       .assertStatus().is(200)
+                       .getBody().as(Bean.class).check();
+               a1.post("/a01", bean)
+                       .header("X-Accept", "text/plain")
+                       .header("X-Content-Type", "text/plain")
+                       .plainText()
+                       .run()
+                       .assertStatus().is(200)
+                       .getBody().as(Bean.class).check();
+               a1.post("/a01", bean)
+                       .header("X-Accept", "octal/msgpack")
+                       .header("X-Content-Type", "octal/msgpack")
+                       .msgPack()
+                       .run()
+                       .assertStatus().is(200)
+                       .getBody().as(Bean.class).check();
+               a1.post("/a01", bean)
+                       .header("X-Accept", "text/uon")
+                       .header("X-Content-Type", "text/uon")
+                       .uon()
+                       .run()
+                       .assertStatus().is(200)
+                       .getBody().as(Bean.class).check();
+               a1.post("/a01", bean)
+                       .header("X-Accept", "application/x-www-form-urlencoded")
+                       .header("X-Content-Type", 
"application/x-www-form-urlencoded")
+                       .urlEnc()
+                       .run()
+                       .assertStatus().is(200)
+                       .getBody().as(Bean.class).check();
+               a1.post("/a01", bean)
+                       .header("X-Accept", "text/openapi")
+                       .header("X-Content-Type", "text/openapi")
+                       .openApi()
+                       .run()
+                       .assertStatus().is(200)
+                       .getBody().as(Bean.class).check();
+               a1.post("/a01", bean)
+                       .header("X-Accept", "text/html")
+                       .header("X-Content-Type", "text/html")
+                       .htmlDoc()
+                       .run()
+                       .assertStatus().is(200)
+                       .getBody().as(Bean.class).check();
+               a1.post("/a01", bean)
+                       .header("X-Accept", "text/html")
+                       .header("X-Content-Type", "text/html+stripped")
+                       .htmlStrippedDoc()
+                       .run()
+                       .assertStatus().is(200)
+                       .getBody().as(Bean.class).check();
+       }
+
+       @Test
        public void a02_singleLanguagesWithOverride() throws Exception {
                a1a.post("/a01", bean)
                        .header("Accept", "application/json")
diff --git 
a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RestClientTest.java
 
b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RestClientTest.java
index 8414385..fb8b0b5 100644
--- 
a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RestClientTest.java
+++ 
b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RestClientTest.java
@@ -30,6 +30,7 @@ import org.apache.http.HttpException;
 import org.apache.http.HttpResponse;
 import org.apache.http.auth.*;
 import org.apache.http.client.*;
+import org.apache.http.client.config.*;
 import org.apache.http.client.methods.*;
 import org.apache.http.client.utils.*;
 import org.apache.http.entity.*;
@@ -1366,6 +1367,70 @@ public class RestClientTest {
                assertEquals("{f:1}", res);
        }
 
+       @Test
+       public void c14_miscellaneous_requestConfig() throws Exception {
+               MockRestClient
+                       .create(A.class)
+                       .simpleJson()
+                       .build()
+                       .get("/bean")
+                       
.requestConfig(RequestConfig.custom().setMaxRedirects(1).build())
+                       .run()
+                       .assertBody().is("{f:1}");
+       }
+
+       @Test
+       public void c15_miscellaneous_RestClient_toString() throws Exception {
+               String s = MockRestClient
+                       .create(A.class)
+                       .simpleJson()
+                       .rootUrl("foo")
+                       .build()
+                       .toString();
+               assertTrue(s.contains("rootUri: 'foo'"));
+       }
+
+       @Test
+       public void c15_miscellaneous_request_target() throws Exception {
+               MockRestClient
+                       .create(A.class)
+                       .simpleJson()
+                       .build()
+                       .get("/bean")
+                       .target(new HttpHost("localhost"))
+                       .run()
+                       .assertBody().is("{f:1}");
+       }
+
+       @Test
+       public void c16_miscellaneous_request_context() throws Exception {
+               MockRestClient
+                       .create(A.class)
+                       .simpleJson()
+                       .build()
+                       .get("/bean")
+                       .context(new BasicHttpContext())
+                       .run()
+                       .assertBody().is("{f:1}");
+       }
+
+       @Test
+       public void c18_miscellaneous_request_scheme() throws Exception {
+               java.net.URI uri = MockRestClient
+                       .create(A.class)
+                       .simpleJson()
+                       .build()
+                       .get("/bean")
+                       .scheme("http")
+                       .host("localhost")
+                       .port(8080)
+                       .userInfo("foo:bar")
+                       .run()
+                       .assertBody().is("{f:1}")
+                       .getRequest().getURI();
+               assertEquals("http://foo:bar@localhost:8080/bean";, 
uri.toString());
+       }
+
        
//------------------------------------------------------------------------------------------------------------------
        // Pooled connections
        
//------------------------------------------------------------------------------------------------------------------
@@ -2067,6 +2132,19 @@ public class RestClientTest {
                        .assertBody().is("['foo']");
        }
 
+       @Test
+       public void f48_headers_onRequest_debug() throws Exception {
+               MockRestClient
+               .create(A.class)
+               .simpleJson()
+               .header("Check", "Debug")
+               .build()
+               .get("/checkHeader")
+               .debug()
+               .run()
+               .assertBody().is("['true']");
+       }
+
        
//------------------------------------------------------------------------------------------------------------------
        // Header Beans
        
//------------------------------------------------------------------------------------------------------------------
@@ -3179,6 +3257,23 @@ public class RestClientTest {
        }
 
        @Test
+       public void k03a_restClient_errorCodes_perRequest() throws Exception {
+               try {
+                       MockRestClient
+                               .create(A.class)
+                               .simpleJson()
+                               .ignoreErrors(false)
+                               .build()
+                               .get("/echo")
+                               .errorCodes(x -> x == 200)
+                               .run();
+                       fail("Exception expected.");
+               } catch (RestCallException e) {
+                       assertEquals(200, e.getResponseCode());
+               }
+       }
+
+       @Test
        public void k04_restClient_executorService() throws Exception {
                ExecutorService es = new ThreadPoolExecutor(1, 1, 30, 
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10));
                RestClient rc = MockRestClient
@@ -3303,6 +3398,225 @@ public class RestClientTest {
                assertEquals(111, XRestCallInterceptor.x);
        }
 
+
+       @Test
+       public void k08a_restClient_interceptorsObjects_perRequest() throws 
Exception {
+               MockRestClient
+                       .create(A.class)
+                       .simpleJson()
+                       .header("Foo","f1")
+                       .build()
+                       .get("/checkHeader")
+                       .interceptors(new XRestCallInterceptor())
+                       .header("Check","foo")
+                       .header("Foo","f3")
+                       .run()
+                       .assertBody().is("['f1','f2','f3']")
+                       .assertHeader("Bar").is("b1");
+               assertEquals(111, XRestCallInterceptor.x);
+       }
+
+       public static class K08a extends BasicRestCallInterceptor {
+               @Override
+               public void onInit(RestRequest req) throws Exception {
+                       throw new RuntimeException("foo");
+               }
+
+               @Override
+               public void onConnect(RestRequest req, RestResponse res) throws 
Exception {
+               }
+
+               @Override
+               public void onClose(RestRequest req, RestResponse res) throws 
Exception {
+               }
+       }
+
+       public static class K08b extends BasicRestCallInterceptor {
+               @Override
+               public void onInit(RestRequest req) throws Exception {
+               }
+
+               @Override
+               public void onConnect(RestRequest req, RestResponse res) throws 
Exception {
+                       throw new RuntimeException("foo");
+               }
+
+               @Override
+               public void onClose(RestRequest req, RestResponse res) throws 
Exception {
+               }
+       }
+
+       public static class K08c extends BasicRestCallInterceptor {
+               @Override
+               public void onInit(RestRequest req) throws Exception {
+               }
+
+               @Override
+               public void onConnect(RestRequest req, RestResponse res) throws 
Exception {
+               }
+
+               @Override
+               public void onClose(RestRequest req, RestResponse res) throws 
Exception {
+                       throw new RuntimeException("foo");
+               }
+       }
+
+       @Test
+       public void k08_restClient_interceptorsClasses_exceptions() throws 
Exception {
+               try {
+                       MockRestClient
+                               .create(A.class)
+                               .simpleJson()
+                               .header("Foo","f1")
+                               .interceptors(K08a.class)
+                               .build()
+                               .get("/checkHeader")
+                               .header("Check","foo")
+                               .header("Foo","f3")
+                               .run();
+                       fail();
+               } catch (RestCallException e) {
+                       assertEquals("foo", 
e.getCause(RuntimeException.class).getMessage());
+               }
+
+               try {
+                       MockRestClient
+                               .create(A.class)
+                               .simpleJson()
+                               .header("Foo","f1")
+                               .interceptors(K08b.class)
+                               .build()
+                               .get("/checkHeader")
+                               .header("Check","foo")
+                               .header("Foo","f3")
+                               .run();
+                       fail();
+               } catch (RestCallException e) {
+                       assertEquals("foo", 
e.getCause(RuntimeException.class).getMessage());
+               }
+
+               try {
+                       MockRestClient
+                               .create(A.class)
+                               .simpleJson()
+                               .header("Foo","f1")
+                               .interceptors(K08c.class)
+                               .build()
+                               .get("/checkHeader")
+                               .header("Check","foo")
+                               .header("Foo","f3")
+                               .run()
+                               .close();
+                       fail();
+               } catch (RestCallException e) {
+                       assertEquals("foo", 
e.getCause(RuntimeException.class).getMessage());
+               }
+       }
+
+       @Test
+       public void k08_restClient_interceptorsObjects_exceptions() throws 
Exception {
+               try {
+                       MockRestClient
+                               .create(A.class)
+                               .simpleJson()
+                               .header("Foo","f1")
+                               .interceptors(new K08a())
+                               .build()
+                               .get("/checkHeader")
+                               .header("Check","foo")
+                               .header("Foo","f3")
+                               .run();
+                       fail();
+               } catch (RestCallException e) {
+                       assertEquals("foo", 
e.getCause(RuntimeException.class).getMessage());
+               }
+
+               try {
+                       MockRestClient
+                               .create(A.class)
+                               .simpleJson()
+                               .header("Foo","f1")
+                               .interceptors(new K08b())
+                               .build()
+                               .get("/checkHeader")
+                               .header("Check","foo")
+                               .header("Foo","f3")
+                               .run();
+                       fail();
+               } catch (RestCallException e) {
+                       assertEquals("foo", 
e.getCause(RuntimeException.class).getMessage());
+               }
+
+               try {
+                       MockRestClient
+                               .create(A.class)
+                               .simpleJson()
+                               .header("Foo","f1")
+                               .interceptors(new K08c())
+                               .build()
+                               .get("/checkHeader")
+                               .header("Check","foo")
+                               .header("Foo","f3")
+                               .run()
+                               .close();
+                       fail();
+               } catch (RestCallException e) {
+                       assertEquals("foo", 
e.getCause(RuntimeException.class).getMessage());
+               }
+       }
+
+       @Test
+       public void k08_restClient_interceptorsObjects_perRequest_exceptions() 
throws Exception {
+               try {
+                       MockRestClient
+                               .create(A.class)
+                               .simpleJson()
+                               .header("Foo","f1")
+                               .build()
+                               .get("/checkHeader")
+                               .interceptors(new K08a())
+                               .header("Check","foo")
+                               .header("Foo","f3")
+                               .run();
+                       fail();
+               } catch (RestCallException e) {
+                       assertEquals("foo", 
e.getCause(RuntimeException.class).getMessage());
+               }
+
+               try {
+                       MockRestClient
+                               .create(A.class)
+                               .simpleJson()
+                               .header("Foo","f1")
+                               .build()
+                               .get("/checkHeader")
+                               .interceptors(new K08b())
+                               .header("Check","foo")
+                               .header("Foo","f3")
+                               .run();
+                       fail();
+               } catch (RestCallException e) {
+                       assertEquals("foo", 
e.getCause(RuntimeException.class).getMessage());
+               }
+
+               try {
+                       MockRestClient
+                               .create(A.class)
+                               .simpleJson()
+                               .header("Foo","f1")
+                               .build()
+                               .get("/checkHeader")
+                               .interceptors(new K08c())
+                               .header("Check","foo")
+                               .header("Foo","f3")
+                               .run()
+                               .close();
+                       fail();
+               } catch (RestCallException e) {
+                       assertEquals("foo", 
e.getCause(RuntimeException.class).getMessage());
+               }
+       }
+
        public static class K09RestClient extends RestClient {
                private static String lastMessage;
 
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextStats.java
 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/BasicHttpEntityRequestBase.java
similarity index 53%
copy from 
juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextStats.java
copy to 
juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/BasicHttpEntityRequestBase.java
index 1f0664c..eac3fc8 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextStats.java
+++ 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/BasicHttpEntityRequestBase.java
@@ -10,54 +10,30 @@
 // * "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.juneau.rest;
+package org.apache.juneau.rest.client2;
 
-import java.time.*;
-import java.util.*;
-
-import org.apache.juneau.annotation.*;
-import org.apache.juneau.transforms.*;
-import org.apache.juneau.utils.*;
+import org.apache.http.client.methods.*;
 
 /**
- * A snapshot of execution statistics for REST resource classes.
+ * Inner request class of {@link RestRequest} for requests with bodies.
  */
-@Bean(bpi="startTime,upTime,methodStats")
-public class RestContextStats {
-       private final Instant startTime;
-       private final List<MethodExecStats> methodStats;
+class BasicHttpEntityRequestBase extends HttpEntityEnclosingRequestBase 
implements RestRequestCreated {
 
-       RestContextStats(Instant startTime, List<MethodExecStats> methodStats) {
-               this.startTime = startTime;
-               this.methodStats = methodStats;
-       }
+       final String method;
+       final RestRequest restRequest;
 
-       /**
-        * Returns the time this REST resource class was started.
-        *
-        * @return The time this REST resource class was started.
-        */
-       @Swap(TemporalSwap.IsoInstant.class)
-       public Instant getStartTime() {
-               return startTime;
+       BasicHttpEntityRequestBase(RestRequest restRequest, String method) {
+               this.restRequest = restRequest;
+               this.method = method;
        }
 
-       /**
-        * Returns the time in milliseconds that this REST resource class has 
been running.
-        *
-        * @return The time in milliseconds that this REST resource class has 
been running.
-        */
-       public String getUpTime() {
-               long s = Duration.between(startTime, 
Instant.now()).getSeconds();
-               return String.format("%dh:%02dm:%02ds", s / 3600, (s % 3600) / 
60, (s % 60));
+       @Override /* RestRequestCreated */
+       public RestRequest getRestRequest() {
+               return this.restRequest;
        }
 
-       /**
-        * Returns statistics on all method executions.
-        *
-        * @return Statistics on all method executions.
-        */
-       public Collection<MethodExecStats> getMethodStats() {
-               return methodStats;
+       @Override /* HttpRequestBase */
+       public String getMethod() {
+               return method;
        }
-}
+}
\ No newline at end of file
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextStats.java
 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/BasicHttpRequestBase.java
similarity index 53%
copy from 
juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextStats.java
copy to 
juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/BasicHttpRequestBase.java
index 1f0664c..c1efb09 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextStats.java
+++ 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/BasicHttpRequestBase.java
@@ -10,54 +10,30 @@
 // * "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.juneau.rest;
+package org.apache.juneau.rest.client2;
 
-import java.time.*;
-import java.util.*;
-
-import org.apache.juneau.annotation.*;
-import org.apache.juneau.transforms.*;
-import org.apache.juneau.utils.*;
+import org.apache.http.client.methods.*;
 
 /**
- * A snapshot of execution statistics for REST resource classes.
+ * Inner request class of {@link RestRequest} for requests without bodies.
  */
-@Bean(bpi="startTime,upTime,methodStats")
-public class RestContextStats {
-       private final Instant startTime;
-       private final List<MethodExecStats> methodStats;
+class BasicHttpRequestBase extends HttpRequestBase implements 
RestRequestCreated {
 
-       RestContextStats(Instant startTime, List<MethodExecStats> methodStats) {
-               this.startTime = startTime;
-               this.methodStats = methodStats;
-       }
+       final String method;
+       final RestRequest restRequest;
 
-       /**
-        * Returns the time this REST resource class was started.
-        *
-        * @return The time this REST resource class was started.
-        */
-       @Swap(TemporalSwap.IsoInstant.class)
-       public Instant getStartTime() {
-               return startTime;
+       BasicHttpRequestBase(RestRequest restRequest, String method) {
+               this.method = method;
+               this.restRequest = restRequest;
        }
 
-       /**
-        * Returns the time in milliseconds that this REST resource class has 
been running.
-        *
-        * @return The time in milliseconds that this REST resource class has 
been running.
-        */
-       public String getUpTime() {
-               long s = Duration.between(startTime, 
Instant.now()).getSeconds();
-               return String.format("%dh:%02dm:%02ds", s / 3600, (s % 3600) / 
60, (s % 60));
+       @Override /* RestRequestCreated */
+       public RestRequest getRestRequest() {
+               return restRequest;
        }
 
-       /**
-        * Returns statistics on all method executions.
-        *
-        * @return Statistics on all method executions.
-        */
-       public Collection<MethodExecStats> getMethodStats() {
-               return methodStats;
+       @Override /* HttpRequestBase */
+       public String getMethod() {
+               return method;
        }
 }
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 a5c87cc..3f4f2af 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
@@ -99,53 +99,30 @@ public class RestRequest extends BeanSession implements 
HttpUriRequest, Configur
        protected RestRequest(RestClient client, URI uri, String method, 
boolean hasBody) throws RestCallException {
                super(client, BeanSessionArgs.DEFAULT);
                this.client = client;
-               if (hasBody) {
-                       this.request = new EntityRequest(method);
-               } else {
-                       this.request = new Request(method);
-               }
-               this.request.setURI(uri);
+               this.request = createInnerRequest(method, uri, hasBody);
                this.errorCodes = client.errorCodes;
                this.partSerializer = client.getPartSerializerSession();
                this.uriBuilder = new URIBuilder(request.getURI());
                this.ignoreErrors = client.ignoreErrors;
        }
 
-       private class Request extends HttpRequestBase implements 
RestRequestCreated {
-               private final String method;
-
-               Request(String method) {
-                       this.method = method;
-               }
-
-               @Override /* RestRequestCreated */
-               public RestRequest getRestRequest() {
-                       return RestRequest.this;
-               }
-
-               @Override /* HttpRequestBase */
-               public String getMethod() {
-                       return method;
-               }
+       /**
+        * Constructs the {@link HttpRequestBase} object that ends up being 
passed to the client execute method.
+        *
+        * <p>
+        * Subclasses can override this method to create their own request base 
objects.
+        *
+        * @param method The HTTP method.
+        * @param uri The HTTP URI.
+        * @param hasBody Whether the HTTP request has a body.
+        * @return A new {@link HttpRequestBase} object.
+        */
+       protected HttpRequestBase createInnerRequest(String method, URI uri, 
boolean hasBody) {
+               HttpRequestBase req = hasBody ? new 
BasicHttpEntityRequestBase(this, method) : new BasicHttpRequestBase(this, 
method);
+               req.setURI(uri);
+               return req;
        }
 
-       private class EntityRequest extends HttpEntityEnclosingRequestBase 
implements RestRequestCreated {
-               private final String method;
-
-               EntityRequest(String method) {
-                       this.method = method;
-               }
-
-               @Override /* RestRequestCreated */
-               public RestRequest getRestRequest() {
-                       return RestRequest.this;
-               }
-
-               @Override /* HttpRequestBase */
-               public String getMethod() {
-                       return method;
-               }
-       }
 
        
//------------------------------------------------------------------------------------------------------------------
        // Configuration
@@ -793,8 +770,7 @@ public class RestRequest extends BeanSession implements 
HttpUriRequest, Configur
         * @throws RestCallException Invalid URI syntax detected.
         */
        public RestRequest uri(Object uri) throws RestCallException {
-               if (uri != null)
-                       uriBuilder = new URIBuilder(client.toURI(uri));
+               uriBuilder = new URIBuilder(client.toURI(uri));
                return this;
        }
 
diff --git 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestResponse.java
 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestResponse.java
index a027d76..77d0714 100644
--- 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestResponse.java
+++ 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestResponse.java
@@ -70,6 +70,15 @@ public class RestResponse implements HttpResponse {
                this.partParser = client.getPartParserSession();
        }
 
+       /**
+        * Returns the request object that created this response object.
+        *
+        * @return The request object that created this response object.
+        */
+       public RestRequest getRequest() {
+               return request;
+       }
+
        
//------------------------------------------------------------------------------------------------------------------
        // Setters
        
//------------------------------------------------------------------------------------------------------------------
diff --git 
a/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockRestRequest.java
 
b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockRestRequest.java
index 2e259cf..1f1aa2b 100644
--- 
a/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockRestRequest.java
+++ 
b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockRestRequest.java
@@ -228,6 +228,7 @@ public class MockRestRequest extends 
org.apache.juneau.rest.client2.RestRequest
         */
        @Override
        public MockRestRequest scheme(String value) {
+               super.scheme(value);
                this.scheme = value;
                return this;
        }
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index eb6aefc..35cf920 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -56,6 +56,7 @@ import org.apache.juneau.httppart.bean.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.jsonschema.*;
 import org.apache.juneau.msgpack.*;
+import org.apache.juneau.mstat.*;
 import org.apache.juneau.oapi.*;
 import org.apache.juneau.parser.*;
 import org.apache.juneau.plaintext.*;
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextStats.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextStats.java
index 1f0664c..3ea0569 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextStats.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextStats.java
@@ -16,8 +16,8 @@ import java.time.*;
 import java.util.*;
 
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.mstat.*;
 import org.apache.juneau.transforms.*;
-import org.apache.juneau.utils.*;
 
 /**
  * A snapshot of execution statistics for REST resource classes.

Reply via email to