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/04 14:14:01 UTC
[juneau] branch master updated: RestClient tests
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 ada5eef RestClient tests
ada5eef is described below
commit ada5eef63907c073887bf108cdfd3c509f679cdc
Author: JamesBognar <ja...@apache.org>
AuthorDate: Sat Apr 4 10:13:42 2020 -0400
RestClient tests
---
.../org/apache/juneau/BasicAssertionError.java | 63 ++++
.../apache/juneau/http/annotation/FormData.java | 17 +
.../org/apache/juneau/http/annotation/Header.java | 17 +
.../org/apache/juneau/http/annotation/Query.java | 17 +
juneau-doc/docs/ReleaseNotes/8.1.4.html | 7 +
.../27.OpenApiDetails/02.Serializers.html | 8 +-
.../juneau/rest/test/client/RestClientTest.java | 4 +-
.../juneau/rest/client2/RestClientBuilderTest.java | 76 ++--
.../apache/juneau/rest/client2/RestResponse.java | 14 +-
.../juneau/rest/client2/RestResponseBody.java | 40 +-
.../juneau/rest/client2/RestResponseHeader.java | 47 ++-
juneau-rest/juneau-rest-server/pom.xml | 4 +
.../apache/juneau/rest/BasicRestCallHandler.java | 3 +
.../org/apache/juneau/rest/RequestFormData.java | 419 ++++++++++++++++++---
.../org/apache/juneau/rest/RequestHeader.java} | 133 +++----
.../org/apache/juneau/rest/RequestHeaders.java | 147 +++++++-
.../java/org/apache/juneau/rest/RequestPath.java | 10 +-
.../java/org/apache/juneau/rest/RequestQuery.java | 14 +-
.../main/java/org/apache/juneau/rest/RestCall.java | 11 +
.../java/org/apache/juneau/rest/RestContext.java | 18 +-
.../org/apache/juneau/rest/RestParamDefaults.java | 51 ++-
.../java/org/apache/juneau/rest/RestRequest.java | 8 +
.../java/org/apache/juneau/rest/RestResponse.java | 62 ++-
.../juneau/rest/reshandlers/DefaultHandler.java | 2 +
pom.xml | 6 +
25 files changed, 945 insertions(+), 253 deletions(-)
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BasicAssertionError.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BasicAssertionError.java
new file mode 100644
index 0000000..63a6ba5
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BasicAssertionError.java
@@ -0,0 +1,63 @@
+// ***************************************************************************************************************************
+// * 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;
+
+import static org.apache.juneau.internal.StringUtils.*;
+
+import java.text.*;
+
+/**
+ * An extension of {@link AssertionError} with helper constructors for messages with message-style arguments.
+ */
+public class BasicAssertionError extends AssertionError {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * @param message The {@link MessageFormat}-style message.
+ * @param args Optional {@link MessageFormat}-style arguments.
+ */
+ public BasicAssertionError(String message, Object...args) {
+ super(format(message, args));
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param cause The cause of this exception.
+ * @param message The {@link MessageFormat}-style message.
+ * @param args Optional {@link MessageFormat}-style arguments.
+ */
+ public BasicAssertionError(Throwable cause, String message, Object...args) {
+ this(getMessage(cause, message, null), args);
+ initCause(cause);
+ }
+
+ /**
+ * Finds the message.
+ *
+ * @param cause The cause.
+ * @param msg The message.
+ * @param def The default value if both above are <jk>null</jk>.
+ * @return The resolved message.
+ */
+ protected static final String getMessage(Throwable cause, String msg, String def) {
+ if (msg != null)
+ return msg;
+ if (cause != null)
+ return cause.getMessage();
+ return def;
+ }
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/FormData.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/FormData.java
index 8c2be4d..d3e3d31 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/FormData.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/FormData.java
@@ -147,6 +147,23 @@ public @interface FormData {
*/
Class<? extends HttpPartParser> parser() default HttpPartParser.Null.class;
+ /**
+ * Denotes a multi-part parameter (e.g. multiple entries with the same name).
+ *
+ * <h5 class='figure'>Example</h5>
+ * <jk>public void</jk> doPost(
+ * <ja>@FormData</ja>(name=<js>"beans"</js>, multi=<jk>true</jk>) MyBean[] beans
+ * ) {
+ *
+ * <ul class='notes'>
+ * <li>
+ * Meant to be used on multi-part parameters (e.g. <js>"key=1&key=2&key=3"</js> instead of <js>"key=@(1,2,3)"</js>)
+ * <li>
+ * The data type must be a collection or array type.
+ * </ul>
+ */
+ boolean multi() default false;
+
//=================================================================================================================
// Attributes common to all Swagger Parameter objects
//=================================================================================================================
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Header.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Header.java
index 3aa1388..45dc38c 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Header.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Header.java
@@ -120,6 +120,23 @@ public @interface Header {
*/
Class<? extends HttpPartParser> parser() default HttpPartParser.Null.class;
+ /**
+ * Denotes a multi-part parameter (e.g. multiple entries with the same name).
+ *
+ * <h5 class='figure'>Example</h5>
+ * <jk>public void</jk> doPost(
+ * <ja>@Header</ja>(name=<js>"Beans"</js>, multi=<jk>true</jk>) MyBean[] beans
+ * ) {
+ *
+ * <ul class='notes'>
+ * <li>
+ * Meant to be used on multi-part parameters (e.g. <js>"Header: h1"</js>, <js>"Header: h2"</js> instead of <js>"Header: @(h1,h2)"</js>)
+ * <li>
+ * The data type must be a collection or array type.
+ * </ul>
+ */
+ boolean multi() default false;
+
//=================================================================================================================
// Attributes common to all Swagger Parameter objects
//=================================================================================================================
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Query.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Query.java
index a7a1db7..b7704a7 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Query.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Query.java
@@ -128,6 +128,23 @@ public @interface Query {
*/
Class<? extends HttpPartParser> parser() default HttpPartParser.Null.class;
+ /**
+ * Denotes a multi-part parameter (e.g. multiple entries with the same name).
+ *
+ * <h5 class='figure'>Example</h5>
+ * <jk>public void</jk> doPost(
+ * <ja>@Query</ja>(name=<js>"beans"</js>, multi=<jk>true</jk>) MyBean[] beans
+ * ) {
+ *
+ * <ul class='notes'>
+ * <li>
+ * Meant to be used on multi-part parameters (e.g. <js>"key=1&key=2&key=3"</js> instead of <js>"key=@(1,2,3)"</js>)
+ * <li>
+ * The data type must be a collection or array type.
+ * </ul>
+ */
+ boolean multi() default false;
+
//=================================================================================================================
// Attributes common to all Swagger Parameter objects
//=================================================================================================================
diff --git a/juneau-doc/docs/ReleaseNotes/8.1.4.html b/juneau-doc/docs/ReleaseNotes/8.1.4.html
index 984bb0d..49a3475 100644
--- a/juneau-doc/docs/ReleaseNotes/8.1.4.html
+++ b/juneau-doc/docs/ReleaseNotes/8.1.4.html
@@ -176,6 +176,13 @@
}
</p>
<li>
+ New annotations for multi-part support:
+ <ul>
+ <li class='jm'>{@link oaj.http.annotation.Header#multi()}
+ <li class='jm'>{@link oaj.http.annotation.Query#multi()}
+ <li class='jm'>{@link oaj.http.annotation.FormData#multi()}
+ </ul>
+ <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-doc/docs/Topics/02.juneau-marshall/27.OpenApiDetails/02.Serializers.html b/juneau-doc/docs/Topics/02.juneau-marshall/27.OpenApiDetails/02.Serializers.html
index 29a268e..2bca333 100644
--- a/juneau-doc/docs/Topics/02.juneau-marshall/27.OpenApiDetails/02.Serializers.html
+++ b/juneau-doc/docs/Topics/02.juneau-marshall/27.OpenApiDetails/02.Serializers.html
@@ -51,14 +51,12 @@ OpenAPI Serializers
Under-the-covers, this gets converted to the following schema object:
</p>
<p class='bpcode w800'>
- <jk>import static</jk> org.apache.juneau.httppart.HttpPartSchema.*;
-
- HttpPartSchema schema = <jsm>create</jsm>()
+ HttpPartSchema schema = HttpPartSchema.<jsm>create</jsm>()
.items(
- <jsm>create</jsm>()
+ HttpPartSchema.<jsm>create</jsm>()
.collectionFormat(<js>"pipes"</js>)
.items(
- <jsm>create</jsm>()
+ HttpPartSchema.<jsm>create</jsm>()
.collectionFormat(<js>"csv"</js>)
.type(<js>"integer"</js>)
.format(<js>"int64"</js>)
diff --git a/juneau-microservice/juneau-microservice-ftest/src/test/java/org/apache/juneau/rest/test/client/RestClientTest.java b/juneau-microservice/juneau-microservice-ftest/src/test/java/org/apache/juneau/rest/test/client/RestClientTest.java
index fe1ca75..17779fe 100644
--- a/juneau-microservice/juneau-microservice-ftest/src/test/java/org/apache/juneau/rest/test/client/RestClientTest.java
+++ b/juneau-microservice/juneau-microservice-ftest/src/test/java/org/apache/juneau/rest/test/client/RestClientTest.java
@@ -43,7 +43,7 @@ public class RestClientTest extends RestTestcase {
try {
c.post(URL, new StringEntity("xxxFAILURExxx")).run().getBody().assertContains("SUCCESS");
fail();
- } catch (RestCallException e) {
+ } catch (AssertionError e) {
assertTrue(e.getLocalizedMessage().contains("Response did not have the expected substring for body."));
}
}
@@ -64,7 +64,7 @@ public class RestClientTest extends RestTestcase {
try {
c.post(URL, new StringEntity("xxxFAILURExxx")).run().getBody().assertValue(x -> ! x.contains("FAILURE"));
fail();
- } catch (RestCallException e) {
+ } catch (AssertionError e) {
assertTrue(e.getLocalizedMessage().contains("Response did not have the expected value for body."));
}
}
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 07d020f..7faca72 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
@@ -33,6 +33,7 @@ import org.apache.juneau.*;
import org.apache.juneau.collections.*;
import org.apache.juneau.http.*;
import org.apache.juneau.http.annotation.*;
+import org.apache.juneau.http.annotation.Header;
import org.apache.juneau.http.exception.*;
import org.apache.juneau.http.response.*;
import org.apache.juneau.httppart.*;
@@ -2182,30 +2183,57 @@ public class RestClientBuilderTest {
}
}
-// @Test
-// public void k17_restClient_partParserClass() throws Exception {
-// RestClient rc = MockRestClient
-// .create(A.class)
-// .simpleJson()
-// .serializers(XmlSerializer.DEFAULT,JsonSerializer.DEFAULT)
-// .parsers(XmlParser.DEFAULT,JsonParser.DEFAULT)
-// .build();
-//
-// }
-//// public RestClientBuilder partParser(Class<? extends HttpPartParser> value) {
-//
-// @Test
-// public void k18_restClient_partParserObject() throws Exception { fail(); }
-//// public RestClientBuilder partParser(HttpPartParser value) {
-//
-// @Test
-// public void k19_restClient_partSerializerClass() throws Exception { fail(); }
-//// public RestClientBuilder partSerializer(Class<? extends HttpPartSerializer> value) {
-//
-// @Test
-// public void k20_restClient_partSerializerObject() throws Exception { fail(); }
-//// public RestClientBuilder partSerializer(HttpPartSerializer value) {
-//
+ @Rest(partSerializer=XPartSerializer.class, partParser=XPartParser.class,debug="true")
+ public static class K extends BasicRest {
+ @RestMethod(path="/")
+ public Ok get(@Header(name="Foo",multi=true) Bean[] foo, org.apache.juneau.rest.RestRequest req, org.apache.juneau.rest.RestResponse res) throws Exception {
+ assertEquals(2, foo.length);
+ assertObjectEquals("['x{f:1}','x{f:1}']", req.getHeaders().getAll("Foo", String[].class));
+ assertEquals("{f:1}", foo[0].toString());
+ assertEquals("{f:1}", foo[1].toString());
+ res.header("Foo", bean);
+ return Ok.OK;
+ }
+ }
+
+ @Test
+ public void k17_restClient_partSerializer_partParser_Class() throws Exception {
+ RestClient rc = MockRestClient
+ .create(K.class)
+ .simpleJson()
+ .header("Foo",bean)
+ .partSerializer(XPartSerializer.class)
+ .partParser(XPartParser.class)
+ .header("")
+ .build();
+ Bean b = rc
+ .get("/")
+ .header("Foo",bean)
+ .run()
+ .getHeader("Foo").assertValue("x{f:1}")
+ .getHeader("Foo").as(Bean.class);
+ assertEquals("{f:1}", b.toString());
+ }
+
+ @Test
+ public void k18_restClient_partSerializer_partParser_Object() throws Exception {
+ RestClient rc = MockRestClient
+ .create(K.class)
+ .simpleJson()
+ .header("Foo",bean)
+ .partSerializer(new XPartSerializer())
+ .partParser(new XPartParser())
+ .header("")
+ .build();
+ Bean b = rc
+ .get("/")
+ .header("Foo",bean)
+ .run()
+ .getHeader("Foo").assertValue("x{f:1}")
+ .getHeader("Foo").as(Bean.class);
+ assertEquals("{f:1}", b.toString());
+ }
+
// @Test
// public void k21_restClient_serializerClass() throws Exception { fail(); }
//// public RestClientBuilder serializer(Class<? extends Serializer> value) {
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 3618bc4..e2fb349 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
@@ -154,14 +154,15 @@ public final class RestResponse implements HttpResponse {
*
* @param validCodes The list of valid codes.
* @return This object (for method chaining).
- * @throws RestCallException If assertion fails.
+ * @throws RestCallException If REST call failed.
+ * @throws AssertionError If assertion failed.
*/
- public RestResponse assertStatusCode(int...validCodes) throws RestCallException {
+ public RestResponse assertStatusCode(int...validCodes) throws RestCallException, AssertionError {
int sc = getStatusCode();
for (int c : validCodes)
if (c == sc)
return this;
- throw new RestCallException("Response did not have the expected status code.\n\tExpected=[{0}]\n\tActual=[{1}]", validCodes, sc);
+ throw new BasicAssertionError("Response did not have the expected status code.\n\tExpected=[{0}]\n\tActual=[{1}]", validCodes, sc);
}
/**
@@ -169,13 +170,14 @@ public final class RestResponse implements HttpResponse {
*
* @param test The test.
* @return This object (for method chaining).
- * @throws RestCallException If assertion fails.
+ * @throws RestCallException If REST call failed.
+ * @throws AssertionError If assertion failed.
*/
- public RestResponse assertStatusCode(Predicate<Integer> test) throws RestCallException {
+ public RestResponse assertStatusCode(Predicate<Integer> test) throws RestCallException, AssertionError {
int sc = getStatusCode();
if (test.test(sc))
return this;
- throw new RestCallException("Response did not have the expected status code.\n\tActual=[{0}]", sc);
+ throw new BasicAssertionError("Response did not have the expected status code.\n\tActual=[{0}]", sc);
}
//------------------------------------------------------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestResponseBody.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestResponseBody.java
index 4ddbd30..5472c3e 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestResponseBody.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestResponseBody.java
@@ -1542,12 +1542,13 @@ public class RestResponseBody implements HttpEntity {
*
* @param value The value to check against.
* @return The response object (for method chaining).
- * @throws RestCallException If assertion fails.
+ * @throws RestCallException If REST call failed.
+ * @throws AssertionError If assertion failed.
*/
- public RestResponse assertValue(String value) throws RestCallException {
+ public RestResponse assertValue(String value) throws RestCallException, AssertionError {
String text = asString();
if (! StringUtils.isEquals(value, text))
- throw new RestCallException("Response did not have the expected value for body.\n\tExpected=[{0}]\n\tActual=[{1}]", value, text);
+ throw new BasicAssertionError("Response did not have the expected value for body.\n\tExpected=[{0}]\n\tActual=[{1}]", value, text);
return response;
}
@@ -1576,13 +1577,14 @@ public class RestResponseBody implements HttpEntity {
*
* @param values The values to check against.
* @return The response object (for method chaining).
- * @throws RestCallException If assertion fails.
+ * @throws AssertionError If assertion failed.
+ * @throws RestCallException If REST call failed.
*/
- public RestResponse assertContains(String...values) throws RestCallException {
+ public RestResponse assertContains(String...values) throws RestCallException, AssertionError {
String text = asString();
for (String substring : values)
if (! StringUtils.contains(text, substring))
- throw new RestCallException("Response did not have the expected substring for body.\n\tExpected=[{0}]\n\tBody=[{1}]", substring, text);
+ throw new BasicAssertionError("Response did not have the expected substring for body.\n\tExpected=[{0}]\n\tBody=[{1}]", substring, text);
return response;
}
@@ -1611,12 +1613,13 @@ public class RestResponseBody implements HttpEntity {
*
* @param test The predicate to use to test the body context.
* @return The response object (for method chaining).
- * @throws RestCallException If assertion fails.
+ * @throws RestCallException If REST call failed.
+ * @throws AssertionError If assertion failed.
*/
- public RestResponse assertValue(Predicate<String> test) throws RestCallException {
+ public RestResponse assertValue(Predicate<String> test) throws RestCallException, AssertionError {
String text = asString();
if (! test.test(text))
- throw new RestCallException("Response did not have the expected value for body.\n\tActual=[{0}]", text);
+ throw new BasicAssertionError("Response did not have the expected value for body.\n\tActual=[{0}]", text);
return response;
}
@@ -1645,9 +1648,10 @@ public class RestResponseBody implements HttpEntity {
*
* @param regex The pattern to test for.
* @return The response object (for method chaining).
- * @throws RestCallException If assertion fails.
+ * @throws RestCallException If REST call failed.
+ * @throws AssertionError If assertion failed.
*/
- public RestResponse assertMatches(String regex) throws RestCallException {
+ public RestResponse assertMatches(String regex) throws RestCallException, AssertionError {
return assertMatches(regex, 0);
}
@@ -1677,13 +1681,14 @@ public class RestResponseBody implements HttpEntity {
* @param regex The pattern to test for.
* @param flags Pattern match flags. See {@link Pattern#compile(String, int)}.
* @return The response object (for method chaining).
- * @throws RestCallException If assertion fails.
+ * @throws RestCallException If REST call failed.
+ * @throws AssertionError If assertion failed.
*/
- public RestResponse assertMatches(String regex, int flags) throws RestCallException {
+ public RestResponse assertMatches(String regex, int flags) throws RestCallException, AssertionError {
String text = asString();
Pattern p = Pattern.compile(regex, flags);
if (! p.matcher(text).matches())
- throw new RestCallException("Response did not match expected pattern.\n\tpattern=[{0}]\n\tBody=[{1}]", regex, text);
+ throw new BasicAssertionError("Response did not match expected pattern.\n\tpattern=[{0}]\n\tBody=[{1}]", regex, text);
return response;
}
@@ -1713,12 +1718,13 @@ public class RestResponseBody implements HttpEntity {
*
* @param pattern The pattern to test for.
* @return The response object (for method chaining).
- * @throws RestCallException If assertion fails.
+ * @throws RestCallException If REST call failed.
+ * @throws AssertionError If assertion failed.
*/
- public RestResponse assertMatches(Pattern pattern) throws RestCallException {
+ public RestResponse assertMatches(Pattern pattern) throws RestCallException, AssertionError {
String text = asString();
if (! pattern.matcher(text).matches())
- throw new RestCallException("Response did not match expected pattern.\n\tpattern=[{0}]\n\tBody=[{1}]", pattern.pattern(), text);
+ throw new BasicAssertionError("Response did not match expected pattern.\n\tpattern=[{0}]\n\tBody=[{1}]", pattern.pattern(), text);
return response;
}
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestResponseHeader.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestResponseHeader.java
index 0023f46..650bbe7 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestResponseHeader.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestResponseHeader.java
@@ -516,11 +516,12 @@ public class RestResponseHeader implements Header {
* </p>
*
* @return The response object (for method chaining).
- * @throws RestCallException If assertion failed.
+ * @throws RestCallException If REST call failed.
+ * @throws AssertionError If assertion failed.
*/
- public RestResponse assertExists() throws RestCallException {
+ public RestResponse assertExists() throws RestCallException, AssertionError {
if (! exists())
- throw new RestCallException("Response did not have the expected header {0}.", getName());
+ throw new BasicAssertionError("Response did not have the expected header {0}.", getName());
return response;
}
@@ -538,11 +539,12 @@ public class RestResponseHeader implements Header {
*
* @param value The value to test for.
* @return The response object (for method chaining).
- * @throws RestCallException If assertion failed.
+ * @throws RestCallException If REST call failed.
+ * @throws AssertionError If assertion failed.
*/
- public RestResponse assertValue(String value) throws RestCallException {
+ public RestResponse assertValue(String value) throws RestCallException, AssertionError {
if (! StringUtils.isEquals(value, asString()))
- throw new RestCallException("Response did not have the expected value for header {0}.\n\tExpected=[{1}]\n\tActual=[{2}]", getName(), value, asString());
+ throw new BasicAssertionError("Response did not have the expected value for header {0}.\n\tExpected=[{1}]\n\tActual=[{2}]", getName(), value, asString());
return response;
}
@@ -560,12 +562,13 @@ public class RestResponseHeader implements Header {
*
* @param test The predicate to test for.
* @return The response object (for method chaining).
- * @throws RestCallException If assertion failed.
+ * @throws RestCallException If REST call failed.
+ * @throws AssertionError If assertion failed.
*/
- public RestResponse assertValue(Predicate<String> test) throws RestCallException {
+ public RestResponse assertValue(Predicate<String> test) throws RestCallException, AssertionError {
String text = asString();
if (! test.test(text))
- throw new RestCallException("Response did not have the expected value for header {0}.\n\tActual=[{1}]", getName(), text);
+ throw new BasicAssertionError("Response did not have the expected value for header {0}.\n\tActual=[{1}]", getName(), text);
return response;
}
@@ -583,13 +586,14 @@ public class RestResponseHeader implements Header {
*
* @param values The substrings to test for.
* @return The response object (for method chaining).
- * @throws RestCallException If assertion failed.
+ * @throws RestCallException If REST call failed.
+ * @throws AssertionError If assertion failed.
*/
- public RestResponse assertContains(String...values) throws RestCallException {
+ public RestResponse assertContains(String...values) throws RestCallException, AssertionError {
String text = asString();
for (String substring : values)
if (! StringUtils.contains(text, substring))
- throw new RestCallException("Response did not have the expected substring in header {0}.\n\tExpected=[{1}]\n\tHeader=[{2}]", getName(), substring, text);
+ throw new BasicAssertionError("Response did not have the expected substring in header {0}.\n\tExpected=[{1}]\n\tHeader=[{2}]", getName(), substring, text);
return response;
}
@@ -607,9 +611,10 @@ public class RestResponseHeader implements Header {
*
* @param regex The pattern to test for.
* @return The response object (for method chaining).
- * @throws RestCallException If assertion failed.
+ * @throws RestCallException If REST call failed.
+ * @throws AssertionError If assertion failed.
*/
- public RestResponse assertMatches(String regex) throws RestCallException {
+ public RestResponse assertMatches(String regex) throws RestCallException, AssertionError {
return assertMatches(regex, 0);
}
@@ -628,12 +633,13 @@ public class RestResponseHeader implements Header {
* @param regex The pattern to test for.
* @param flags Pattern match flags. See {@link Pattern#compile(String, int)}.
* @return The response object (for method chaining).
- * @throws RestCallException If assertion failed.
+ * @throws RestCallException If REST call failed.
+ * @throws AssertionError If assertion failed.
*/
- public RestResponse assertMatches(String regex, int flags) throws RestCallException {
+ public RestResponse assertMatches(String regex, int flags) throws RestCallException, AssertionError {
String text = asString();
if (! Pattern.compile(regex, flags).matcher(text).matches())
- throw new RestCallException("Response did not match expected pattern in header {0}.\n\tpattern=[{1}]\n\tHeader=[{2}]", getName(), regex, text);
+ throw new BasicAssertionError("Response did not match expected pattern in header {0}.\n\tpattern=[{1}]\n\tHeader=[{2}]", getName(), regex, text);
return response;
}
@@ -655,12 +661,13 @@ public class RestResponseHeader implements Header {
*
* @param pattern The pattern to test for.
* @return The response object (for method chaining).
- * @throws RestCallException If assertion failed.
+ * @throws RestCallException If REST call failed.
+ * @throws AssertionError If assertion failed.
*/
- public RestResponse assertMatches(Pattern pattern) throws RestCallException {
+ public RestResponse assertMatches(Pattern pattern) throws RestCallException, AssertionError {
String text = asString();
if (! pattern.matcher(text).matches())
- throw new RestCallException("Response did not match expected pattern in header {0}.\n\tpattern=[{1}]\n\tHeader=[{2}]", getName(), pattern.pattern(), text);
+ throw new BasicAssertionError("Response did not match expected pattern in header {0}.\n\tpattern=[{1}]\n\tHeader=[{2}]", getName(), pattern.pattern(), text);
return response;
}
diff --git a/juneau-rest/juneau-rest-server/pom.xml b/juneau-rest/juneau-rest-server/pom.xml
index dc03d7c..ecacb46 100644
--- a/juneau-rest/juneau-rest-server/pom.xml
+++ b/juneau-rest/juneau-rest-server/pom.xml
@@ -60,6 +60,10 @@
<groupId>com.sun.activation</groupId>
<artifactId>javax.activation</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpcore</artifactId>
+ </dependency>
</dependencies>
<properties>
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
index 3c5a964..262e868 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
@@ -344,6 +344,9 @@ public class BasicRestCallHandler implements RestCallHandler {
call.exception(e);
+ if (call.isDebug())
+ e.printStackTrace();
+
int occurrence = context == null ? 0 : context.getStackTraceOccurrence(e);
int code = 500;
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestFormData.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestFormData.java
index 3fef82e..949b7eb 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestFormData.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestFormData.java
@@ -16,6 +16,7 @@ import static org.apache.juneau.internal.ArrayUtils.*;
import static org.apache.juneau.internal.StringUtils.*;
import java.lang.reflect.*;
+import java.lang.reflect.Type;
import java.util.*;
import javax.servlet.http.*;
@@ -120,20 +121,12 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
/**
* Returns a form-data parameter value.
*
- * <p>
- * Parameter lookup is case-insensitive (consistent with WAS, but differs from Tomcat).
- *
* <ul class='notes'>
* <li>
+ * Parameter lookup is case-insensitive (consistent with WAS, but differs from Tomcat).
+ * <li>
* This method returns the raw unparsed value, and differs from calling
- * <code>get(name, String.<jk>class</jk>)</code> which will convert the value from UON
- * notation:
- * <ul>
- * <li><js>"null"</js> => <jk>null</jk>
- * <li><js>"'null'"</js> => <js>"null"</js>
- * <li><js>"'foo bar'"</js> => <js>"foo bar"</js>
- * <li><js>"foo~~bar"</js> => <js>"foo~bar"</js>
- * </ul>
+ * <code>get(name, String.<jk>class</jk>)</code> which uses the {@link HttpPartParser} for parsing the value.
* </ul>
*
* @param name The form-data parameter name.
@@ -154,7 +147,15 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
}
/**
- * Same as {@link #getString(String)} except returns a default value if <jk>null</jk> or empty.
+ * Returns a form-data parameter value.
+ *
+ * <ul class='notes'>
+ * <li>
+ * Parameter lookup is case-insensitive (consistent with WAS, but differs from Tomcat).
+ * <li>
+ * This method returns the raw unparsed value, and differs from calling
+ * <code>get(name, String.<jk>class</jk>)</code> which uses the {@link HttpPartParser} for parsing the value.
+ * </ul>
*
* @param name The form-data parameter name.
* @param def The default value.
@@ -166,7 +167,15 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
}
/**
- * Same as {@link #getString(String)} but converts the value to an integer.
+ * Returns a form-data parameter value as an integer.
+ *
+ * <ul class='notes'>
+ * <li>
+ * Parameter lookup is case-insensitive (consistent with WAS, but differs from Tomcat).
+ * <li>
+ * This method returns the raw unparsed value, and differs from calling
+ * <code>get(name, Integer.<jk>class</jk>)</code> which uses the {@link HttpPartParser} for parsing the value.
+ * </ul>
*
* @param name The form-data parameter name.
* @return The parameter value, or <c>0</c> if parameter does not exist or is <jk>null</jk> or empty.
@@ -176,7 +185,15 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
}
/**
- * Same as {@link #getString(String,String)} but converts the value to an integer.
+ * Returns a form-data parameter value as an integer.
+ *
+ * <ul class='notes'>
+ * <li>
+ * Parameter lookup is case-insensitive (consistent with WAS, but differs from Tomcat).
+ * <li>
+ * This method returns the raw unparsed value, and differs from calling
+ * <code>get(name, Integer.<jk>class</jk>)</code> which uses the {@link HttpPartParser} for parsing the value.
+ * </ul>
*
* @param name The form-data parameter name.
* @param def The default value.
@@ -188,7 +205,15 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
}
/**
- * Same as {@link #getString(String)} but converts the value to a boolean.
+ * Returns a form-data parameter value as a boolean.
+ *
+ * <ul class='notes'>
+ * <li>
+ * Parameter lookup is case-insensitive (consistent with WAS, but differs from Tomcat).
+ * <li>
+ * This method returns the raw unparsed value, and differs from calling
+ * <code>get(name, Boolean.<jk>class</jk>)</code> which uses the {@link HttpPartParser} for parsing the value.
+ * </ul>
*
* @param name The form-data parameter name.
* @return The parameter value, or <jk>false</jk> if parameter does not exist or is <jk>null</jk> or empty.
@@ -198,7 +223,15 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
}
/**
- * Same as {@link #getString(String,String)} but converts the value to a boolean.
+ * Returns a form-data parameter value as a boolean.
+ *
+ * <ul class='notes'>
+ * <li>
+ * Parameter lookup is case-insensitive (consistent with WAS, but differs from Tomcat).
+ * <li>
+ * This method returns the raw unparsed value, and differs from calling
+ * <code>get(name, Boolean.<jk>class</jk>)</code> which uses the {@link HttpPartParser} for parsing the value.
+ * </ul>
*
* @param name The form-data parameter name.
* @param def The default value.
@@ -246,7 +279,84 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
}
/**
- * Same as {@link #get(String, Object, Class)} but allows you to override the part parser.
+ * Returns the specified form-data parameter value converted to a POJO using the {@link HttpPartParser} registered with the resource.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <jc>// Pipe-delimited list of comma-delimited numbers</jc>
+ * HttpPartSchema schema = HttpPartSchema.<jsm>create</jsm>()
+ * .items(
+ * HttpPartSchema.<jsm>create</jsm>()
+ * .collectionFormat(<js>"pipes"</js>)
+ * .items(
+ * HttpPartSchema.<jsm>create</jsm>()
+ * .collectionFormat(<js>"csv"</js>)
+ * .type(<js>"integer"</js>)
+ * .format(<js>"int64"</js>)
+ * .minimum(<js>"0"</js>)
+ * .maximum(<js>"100"</js>)
+ * .minLength(1)
+ * .maxLength=(10)
+ * )
+ * )
+ * .build();
+ *
+ * <jc>// Parse into a 2d long array.</jc>
+ * <jk>long</jk>[][] myparams = formData.get(schema, <js>"myparam"</js>, <jk>long</jk>[][].<jk>class</jk>);
+ * </p>
+ *
+ * <ul class='seealso'>
+ * <li class='jf'>{@link RestContext#REST_partParser}
+ * </ul>
+ *
+ * @param schema
+ * The schema object that defines the format of the input.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the parser.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}).
+ * @param name The parameter name.
+ * @param type The class type to convert the parameter value to.
+ * @param <T> The class type to convert the parameter value to.
+ * @return The parameter value converted to the specified class type.
+ * @throws BadRequest Thrown if input could not be parsed.
+ * @throws InternalServerError Thrown if any other exception occurs.
+ */
+ public <T> T get(HttpPartSchema schema, String name, Class<T> type) throws BadRequest, InternalServerError {
+ return getInner(null, schema, name, null, getClassMeta(type));
+ }
+
+ /**
+ * Returns the specified form-data parameter value converted to a POJO using the specified {@link HttpPartParser}.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <jc>// Pipe-delimited list of comma-delimited numbers</jc>
+ * HttpPartSchema schema = HttpPartSchema.<jsm>create</jsm>()
+ * .items(
+ * HttpPartSchema.<jsm>create</jsm>()
+ * .collectionFormat(<js>"pipes"</js>)
+ * .items(
+ * HttpPartSchema.<jsm>create</jsm>()
+ * .collectionFormat(<js>"csv"</js>)
+ * .type(<js>"integer"</js>)
+ * .format(<js>"int64"</js>)
+ * .minimum(<js>"0"</js>)
+ * .maximum(<js>"100"</js>)
+ * .minLength(1)
+ * .maxLength=(10)
+ * )
+ * )
+ * .build();
+ *
+ * HttpPartParserSession parser = OpenApiParser.<jsf>DEFAULT</jsf>.createSession();
+ *
+ * <jc>// Parse into a 2d long array.</jc>
+ * <jk>long</jk>[][] myparams = formData.get(parser, schema, <js>"myparam"</js>, <jk>long</jk>[][].<jk>class</jk>);
+ * </p>
+ *
+ * <ul class='seealso'>
+ * <li class='jf'>{@link RestContext#REST_partParser}
+ * </ul>
*
* @param parser
* The parser to use for parsing the string value.
@@ -268,7 +378,29 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
}
/**
- * Same as {@link #get(String, Class)} except returns a default value if not specified.
+ * Returns the specified form-data parameter value converted to a POJO using the {@link HttpPartParser} registered with the resource.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <jc>// Parse into an integer.</jc>
+ * <jk>int</jk> myparam = formData.get(<js>"myparam"</js>, -1, <jk>int</jk>.<jk>class</jk>);
+ *
+ * <jc>// Parse into an int array.</jc>
+ * <jk>int</jk>[] myparam = formData.get(<js>"myparam"</js>, <jk>new int</jk>[0], <jk>int</jk>[].<jk>class</jk>);
+
+ * <jc>// Parse into a bean.</jc>
+ * MyBean myparam = formData.get(<js>"myparam"</js>, <jk>new</jk> MyBean(), MyBean.<jk>class</jk>);
+ *
+ * <jc>// Parse into a linked-list of objects.</jc>
+ * List myparam = formData.get(<js>"myparam"</js>, Collections.<jsm>emptyList</jsm>(), LinkedList.<jk>class</jk>);
+ *
+ * <jc>// Parse into a map of object keys/values.</jc>
+ * Map myparam = formData.get(<js>"myparam"</js>, Collections.<jsm>emptyMap</jsm>(), TreeMap.<jk>class</jk>);
+ * </p>
+ *
+ * <ul class='seealso'>
+ * <li class='jf'>{@link RestContext#REST_partParser}
+ * </ul>
*
* @param name The parameter name.
* @param def The default value if the parameter was not specified or is <jk>null</jk>.
@@ -283,11 +415,36 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
}
/**
- * Same as {@link #get(String, Object, Class)} but allows you to override the part parser.
+ * Returns the specified form-data parameter value converted to a POJO using the {@link HttpPartParser} registered with the resource.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <jc>// Pipe-delimited list of comma-delimited numbers</jc>
+ * HttpPartSchema schema = HttpPartSchema.<jsm>create</jsm>()
+ * .items(
+ * HttpPartSchema.<jsm>create</jsm>()
+ * .collectionFormat(<js>"pipes"</js>)
+ * .items(
+ * HttpPartSchema.<jsm>create</jsm>()
+ * .collectionFormat(<js>"csv"</js>)
+ * .type(<js>"integer"</js>)
+ * .format(<js>"int64"</js>)
+ * .minimum(<js>"0"</js>)
+ * .maximum(<js>"100"</js>)
+ * .minLength(1)
+ * .maxLength=(10)
+ * )
+ * )
+ * .build();
+ *
+ * <jc>// Parse into a 2d long array.</jc>
+ * <jk>long</jk>[][] myparams = formData.get(schema, <js>"myparam"</js>, <jk>new long</jk>[][0], <jk>long</jk>[][].<jk>class</jk>);
+ * </p>
+ *
+ * <ul class='seealso'>
+ * <li class='jf'>{@link RestContext#REST_partParser}
+ * </ul>
*
- * @param parser
- * The parser to use for parsing the string value.
- * <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
* @param schema
* The schema object that defines the format of the input.
* <br>If <jk>null</jk>, defaults to the schema defined on the parser.
@@ -298,35 +455,45 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
* @param type The class type to convert the parameter value to.
* @param <T> The class type to convert the parameter value to.
* @return The parameter value converted to the specified class type.
- * @throws BadRequest Thrown if input could not be parsed or fails schema validation.
- * @throws InternalServerError Thrown if any other exception occurs.
- */
- public <T> T get(HttpPartParserSession parser, HttpPartSchema schema, String name, T def, Class<T> type) throws BadRequest, InternalServerError {
- return getInner(parser, schema, name, def, getClassMeta(type));
- }
-
- /**
- * Same as {@link #get(String, Class)} except for use on multi-part parameters
- * (e.g. <js>"key=1&key=2&key=3"</js> instead of <js>"key=@(1,2,3)"</js>)
- *
- * <p>
- * This method must only be called when parsing into classes of type Collection or array.
- *
- * @param name The parameter name.
- * @param type The class type to convert the parameter value to.
- * @return The parameter value converted to the specified class type.
* @throws BadRequest Thrown if input could not be parsed.
* @throws InternalServerError Thrown if any other exception occurs.
*/
- public <T> T getAll(String name, Class<T> type) throws BadRequest, InternalServerError {
- return getAllInner(null, null, name, null, getClassMeta(type));
+ public <T> T get(HttpPartSchema schema, String name, T def, Class<T> type) throws BadRequest, InternalServerError {
+ return getInner(null, schema, name, def, getClassMeta(type));
}
/**
- * Same as {@link #getAll(String, Class)} but allows you to override the part parser.
+ * Returns the specified form-data parameter value converted to a POJO using the specified {@link HttpPartParser}.
*
- * <p>
- * This method must only be called when parsing into classes of type Collection or array.
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <jc>// Pipe-delimited list of comma-delimited numbers</jc>
+ * HttpPartSchema schema = HttpPartSchema.<jsm>create</jsm>()
+ * .items(
+ * HttpPartSchema.<jsm>create</jsm>()
+ * .collectionFormat(<js>"pipes"</js>)
+ * .items(
+ * HttpPartSchema.<jsm>create</jsm>()
+ * .collectionFormat(<js>"csv"</js>)
+ * .type(<js>"integer"</js>)
+ * .format(<js>"int64"</js>)
+ * .minimum(<js>"0"</js>)
+ * .maximum(<js>"100"</js>)
+ * .minLength(1)
+ * .maxLength=(10)
+ * )
+ * )
+ * .build();
+ *
+ * HttpPartParserSession parser = OpenApiParser.<jsf>DEFAULT</jsf>.createSession();
+ *
+ * <jc>// Parse into a 2d long array.</jc>
+ * <jk>long</jk>[][] myparams = formData.get(parser, schema, <js>"myparam"</js>, <jk>new long</jk>[][0], <jk>long</jk>[][].<jk>class</jk>);
+ * </p>
+ *
+ * <ul class='seealso'>
+ * <li class='jf'>{@link RestContext#REST_partParser}
+ * </ul>
*
* @param parser
* The parser to use for parsing the string value.
@@ -337,13 +504,15 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
* <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
* <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}).
* @param name The parameter name.
+ * @param def The default value if the parameter was not specified or is <jk>null</jk>.
* @param type The class type to convert the parameter value to.
+ * @param <T> The class type to convert the parameter value to.
* @return The parameter value converted to the specified class type.
* @throws BadRequest Thrown if input could not be parsed or fails schema validation.
* @throws InternalServerError Thrown if any other exception occurs.
*/
- public <T> T getAll(HttpPartParserSession parser, HttpPartSchema schema, String name, Class<T> type) throws BadRequest, InternalServerError {
- return getAllInner(parser, schema, name, null, getClassMeta(type));
+ public <T> T get(HttpPartParserSession parser, HttpPartSchema schema, String name, T def, Class<T> type) throws BadRequest, InternalServerError {
+ return getInner(parser, schema, name, def, getClassMeta(type));
}
/**
@@ -393,7 +562,7 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
public <T> T get(String name, Type type, Type...args) throws BadRequest, InternalServerError {
return getInner(null, null, name, null, this.<T>getClassMeta(type, args));
}
-
+
/**
* Same as {@link #get(String, Type, Type...)} but allows you to override the part parser.
*
@@ -472,6 +641,148 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
}
/**
+ * Returns the specified form-data parameter values converted POJO using the {@link HttpPartParser} registered with the resource.
+ *
+ * <p>
+ * Meant to be used on multi-part parameters (e.g. <js>"key=1&key=2&key=3"</js> instead of <js>"key=@(1,2,3)"</js>)
+ *
+ * <p>
+ * This method must only be called when parsing into classes of type Collection or array.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <jc>// Parse into multiple integers.</jc>
+ * <jk>int</jk>[] myparam = formData.getAll(<js>"myparam"</js>, <jk>int</jk>[].<jk>class</jk>);
+ *
+ * <jc>// Parse into multiple int arrays.</jc>
+ * <jk>int</jk>[][] myparam = formData.getAll(<js>"myparam"</js>, <jk>int</jk>[][].<jk>class</jk>);
+
+ * <jc>// Parse into multiple beans.</jc>
+ * MyBean[] myparam = formData.getAll(<js>"myparam"</js>, MyBean[].<jk>class</jk>);
+ *
+ * <jc>// Parse into multiple linked-lists of objects.</jc>
+ * List[] myparam = formData.getAll(<js>"myparam"</js>, LinkedList[].<jk>class</jk>);
+ *
+ * <jc>// Parse into multiple maps of object keys/values.</jc>
+ * Map[] myparam = formData.getAll(<js>"myparam"</js>, TreeMap[].<jk>class</jk>);
+ * </p>
+ *
+ * <ul class='seealso'>
+ * <li class='jf'>{@link RestContext#REST_partParser}
+ * </ul>
+ *
+ * @param name The parameter name.
+ * @param type The class type to convert the parameter value to.
+ * @return The parameter value converted to the specified class type.
+ * @throws BadRequest Thrown if input could not be parsed.
+ * @throws InternalServerError Thrown if any other exception occurs.
+ */
+ public <T> T getAll(String name, Class<T> type) throws BadRequest, InternalServerError {
+ return getAllInner(null, null, name, getClassMeta(type));
+ }
+
+ /**
+ * Returns the specified form-data parameter values converted to POJOs using the {@link HttpPartParser} registered with the resource.
+ *
+ * <p>
+ * Meant to be used on multi-part parameters (e.g. <js>"key=1&key=2&key=3"</js> instead of <js>"key=@(1,2,3)"</js>)
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <jc>// Pipe-delimited list of comma-delimited numbers</jc>
+ * HttpPartSchema schema = HttpPartSchema.<jsm>create</jsm>()
+ * .items(
+ * HttpPartSchema.<jsm>create</jsm>()
+ * .collectionFormat(<js>"pipes"</js>)
+ * .items(
+ * HttpPartSchema.<jsm>create</jsm>()
+ * .collectionFormat(<js>"csv"</js>)
+ * .type(<js>"integer"</js>)
+ * .format(<js>"int64"</js>)
+ * .minimum(<js>"0"</js>)
+ * .maximum(<js>"100"</js>)
+ * .minLength(1)
+ * .maxLength=(10)
+ * )
+ * )
+ * .build();
+ *
+ * <jc>// Parse into multiple 2d long arrays.</jc>
+ * <jk>long</jk>[][][] myparams = formData.getAll(schema, <js>"myparam"</js>, <jk>long</jk>[][][].<jk>class</jk>);
+ * </p>
+ *
+ * <ul class='seealso'>
+ * <li class='jf'>{@link RestContext#REST_partParser}
+ * </ul>
+ *
+ * @param schema
+ * The schema object that defines the format of the input.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the parser.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}).
+ * @param name The parameter name.
+ * @param type The class type to convert the parameter value to.
+ * @return The parameter value converted to the specified class type.
+ * @throws BadRequest Thrown if input could not be parsed.
+ * @throws InternalServerError Thrown if any other exception occurs.
+ */
+ public <T> T getAll(HttpPartSchema schema, String name, Class<T> type) throws BadRequest, InternalServerError {
+ return getAllInner(null, schema, name, getClassMeta(type));
+ }
+
+ /**
+ * Returns the specified form-data parameter values converted to POJOs using the specified {@link HttpPartParser}.
+ *
+ * <p>
+ * Meant to be used on multi-part parameters (e.g. <js>"key=1&key=2&key=3"</js> instead of <js>"key=@(1,2,3)"</js>)
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <jc>// Pipe-delimited list of comma-delimited numbers</jc>
+ * HttpPartSchema schema = HttpPartSchema.<jsm>create</jsm>()
+ * .items(
+ * HttpPartSchema.<jsm>create</jsm>()
+ * .collectionFormat(<js>"pipes"</js>)
+ * .items(
+ * HttpPartSchema.<jsm>create</jsm>()
+ * .collectionFormat(<js>"csv"</js>)
+ * .type(<js>"integer"</js>)
+ * .format(<js>"int64"</js>)
+ * .minimum(<js>"0"</js>)
+ * .maximum(<js>"100"</js>)
+ * .minLength(1)
+ * .maxLength=(10)
+ * )
+ * )
+ * .build();
+ *
+ * <jc>// Parse into multiple 2d long arrays.</jc>
+ * <jk>long</jk>[][][] myparams = formData.getAll(schema, <js>"myparam"</js>, <jk>long</jk>[][][].<jk>class</jk>);
+ * </p>
+ *
+ * <ul class='seealso'>
+ * <li class='jf'>{@link RestContext#REST_partParser}
+ * </ul>
+ *
+ * @param parser
+ * The parser to use for parsing the string value.
+ * <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
+ * @param schema
+ * The schema object that defines the format of the input.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the parser.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}).
+ * @param name The parameter name.
+ * @param type The class type to convert the parameter value to.
+ * @return The parameter value converted to the specified class type.
+ * @throws BadRequest Thrown if input could not be parsed or fails schema validation.
+ * @throws InternalServerError Thrown if any other exception occurs.
+ */
+ public <T> T getAll(HttpPartParserSession parser, HttpPartSchema schema, String name, Class<T> type) throws BadRequest, InternalServerError {
+ return getAllInner(parser, schema, name, getClassMeta(type));
+ }
+
+ /**
* Same as {@link #get(String, Type, Type...)} except for use on multi-part parameters
* (e.g. <js>"key=1&key=2&key=3"</js> instead of <js>"key=@(1,2,3)"</js>)
*
@@ -491,7 +802,7 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
* @throws InternalServerError Thrown if any other exception occurs.
*/
public <T> T getAll(String name, Type type, Type...args) throws BadRequest, InternalServerError {
- return getAllInner(null, null, name, null, this.<T>getClassMeta(type, args));
+ return getAllInner(null, null, name, this.<T>getClassMeta(type, args));
}
/**
@@ -518,11 +829,13 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
* @throws InternalServerError Thrown if any other exception occurs.
*/
public <T> T getAll(HttpPartParserSession parser, HttpPartSchema schema, String name, Type type, Type...args) throws BadRequest, InternalServerError {
- return getAllInner(parser, schema, name, null, this.<T>getClassMeta(type, args));
+ return getAllInner(parser, schema, name, this.<T>getClassMeta(type, args));
}
/* Workhorse method */
private <T> T getInner(HttpPartParserSession parser, HttpPartSchema schema, String name, T def, ClassMeta<T> cm) throws BadRequest, InternalServerError {
+ if (parser == null)
+ parser = req.getPartParser();
try {
if (cm.isMapOrBean() && isOneOf(name, "*", "")) {
OMap m = new OMap();
@@ -531,7 +844,7 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
HttpPartSchema pschema = schema == null ? null : schema.getProperty(k);
ClassMeta<?> cm2 = cm.getValueType();
if (cm.getValueType().isCollectionOrArray())
- m.put(k, getAllInner(parser, pschema, k, null, cm2));
+ m.put(k, getAllInner(parser, pschema, k, cm2));
else
m.put(k, getInner(parser, pschema, k, null, cm2));
}
@@ -550,10 +863,8 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
/* Workhorse method */
@SuppressWarnings("rawtypes")
- <T> T getAllInner(HttpPartParserSession parser, HttpPartSchema schema, String name, T def, ClassMeta<T> cm) throws BadRequest, InternalServerError {
+ <T> T getAllInner(HttpPartParserSession parser, HttpPartSchema schema, String name, ClassMeta<T> cm) throws BadRequest, InternalServerError {
String[] p = get(name);
- if (p == null)
- return def;
if (schema == null)
schema = HttpPartSchema.DEFAULT;
try {
@@ -575,7 +886,7 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
} catch (Exception e) {
throw new InternalServerError(e, "Could not parse form-data parameter ''{0}''.", name) ;
}
- throw new InternalServerError("Invalid call to getParameters(String, ClassMeta). Class type must be a Collection or array.");
+ throw new InternalServerError("Invalid call to getAll(String, ClassMeta). Class type must be a Collection or array.");
}
private <T> T parse(HttpPartParserSession parser, HttpPartSchema schema, String val, ClassMeta<T> c) throws SchemaValidationException, ParseException {
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestResponseHeader.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeader.java
similarity index 82%
copy from juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestResponseHeader.java
copy to juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeader.java
index 0023f46..6f76f60 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestResponseHeader.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeader.java
@@ -10,7 +10,7 @@
// * "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.client2;
+package org.apache.juneau.rest;
import static org.apache.juneau.httppart.HttpPartType.*;
import java.lang.reflect.*;
@@ -28,17 +28,8 @@ import org.apache.juneau.utils.*;
/**
* Represents a single header on an HTTP response.
- *
- * <p>
- * An extension of an HttpClient {@link Header} that provides various support for converting the header to POJOs and
- * other convenience methods.
- *
- * <ul class='seealso'>
- * <li class='jc'>{@link RestClient}
- * <li class='link'>{@doc juneau-rest-client}
- * </ul>
*/
-public class RestResponseHeader implements Header {
+public class RequestHeader implements Header {
static final Header NULL_HEADER = new Header() {
@@ -71,7 +62,7 @@ public class RestResponseHeader implements Header {
* @param response The response object.
* @param header The wrapped header. Can be <jk>null</jk>.
*/
- public RestResponseHeader(RestRequest request, RestResponse response, Header header) {
+ public RequestHeader(RestRequest request, RestResponse response, Header header) {
this.request = request;
this.response = response;
this.header = header == null ? NULL_HEADER : header;
@@ -92,7 +83,7 @@ public class RestResponseHeader implements Header {
* The part schema.
* @return This object (for method chaining).
*/
- public RestResponseHeader schema(HttpPartSchema value) {
+ public RequestHeader schema(HttpPartSchema value) {
this.schema = value;
return this;
}
@@ -101,14 +92,14 @@ public class RestResponseHeader implements Header {
* Specifies the part parser to use for this header.
*
* <p>
- * If not specified, uses the part parser defined on the client by calling {@link RestClientBuilder#partParser(Class)}.
+ * If not specified, uses the part parser defined on the client by calling {@link RestContextBuilder#partParser(Class)}.
*
* @param value
* The new part parser to use for this header.
* <br>If <jk>null</jk>, {@link SimplePartParser#DEFAULT} will be used.
* @return This object (for method chaining).
*/
- public RestResponseHeader parser(HttpPartParserSession value) {
+ public RequestHeader parser(HttpPartParserSession value) {
this.parser = value == null ? SimplePartParser.DEFAULT_SESSION : value;
return this;
}
@@ -184,9 +175,9 @@ public class RestResponseHeader implements Header {
* @param type The type to convert to.
* @param args The type parameters.
* @return The converted type, or <jk>null</jk> if header is not present.
- * @throws RestCallException If value could not be parsed.
+ * @throws ParseException If value could not be parsed.
*/
- public <T> T as(Type type, Type...args) throws RestCallException {
+ public <T> T as(Type type, Type...args) throws ParseException {
return as(request.getClassMeta(type, args));
}
@@ -198,9 +189,9 @@ public class RestResponseHeader implements Header {
* @param type The type to convert to.
* @param args The type parameters.
* @return The response object (for method chaining).
- * @throws RestCallException If value could not be parsed.
+ * @throws ParseException If value could not be parsed.
*/
- public <T> RestResponse as(Mutable<T> m, Type type, Type...args) throws RestCallException {
+ public <T> RestResponse as(Mutable<T> m, Type type, Type...args) throws ParseException {
m.set(as(type, args));
return response;
}
@@ -211,9 +202,9 @@ public class RestResponseHeader implements Header {
* @param <T> The type to convert to.
* @param type The type to convert to.
* @return The converted type, or <jk>null</jk> if header is not present.
- * @throws RestCallException If value could not be parsed.
+ * @throws ParseException If value could not be parsed.
*/
- public <T> T as(Class<T> type) throws RestCallException {
+ public <T> T as(Class<T> type) throws ParseException {
return as(request.getClassMeta(type));
}
@@ -224,9 +215,9 @@ public class RestResponseHeader implements Header {
* @param <T> The type to convert to.
* @param type The type to convert to.
* @return The response object (for method chaining).
- * @throws RestCallException If value could not be parsed.
+ * @throws ParseException If value could not be parsed.
*/
- public <T> RestResponse as(Mutable<T> m, Class<T> type) throws RestCallException {
+ public <T> RestResponse as(Mutable<T> m, Class<T> type) throws ParseException {
m.set(as(type));
return response;
}
@@ -237,14 +228,10 @@ public class RestResponseHeader implements Header {
* @param <T> The type to convert to.
* @param type The type to convert to.
* @return The converted type, or <jk>null</jk> if header is not present.
- * @throws RestCallException If value could not be parsed.
+ * @throws ParseException If value could not be parsed.
*/
- public <T> T as(ClassMeta<T> type) throws RestCallException {
- try {
- return parser.parse(HEADER, schema, asString(), type);
- } catch (ParseException e) {
- throw new RestCallException(e);
- }
+ public <T> T as(ClassMeta<T> type) throws ParseException {
+ return parser.parse(HEADER, schema, asString(), type);
}
/**
@@ -254,9 +241,9 @@ public class RestResponseHeader implements Header {
* @param <T> The type to convert to.
* @param type The type to convert to.
* @return The response object (for method chaining).
- * @throws RestCallException If value could not be parsed.
+ * @throws ParseException If value could not be parsed.
*/
- public <T> RestResponse as(Mutable<T> m, ClassMeta<T> type) throws RestCallException {
+ public <T> RestResponse as(Mutable<T> m, ClassMeta<T> type) throws ParseException {
m.set(as(type));
return response;
}
@@ -268,9 +255,9 @@ public class RestResponseHeader implements Header {
* @param type The type to convert to.
* @param args The type parameters.
* @return The parsed value as an {@link Optional}, or an empty optional if header was not present.
- * @throws RestCallException If value could not be parsed.
+ * @throws ParseException If value could not be parsed.
*/
- public <T> Optional<T> asOptional(Type type, Type...args) throws RestCallException {
+ public <T> Optional<T> asOptional(Type type, Type...args) throws ParseException {
return Optional.ofNullable(as(type, args));
}
@@ -282,9 +269,9 @@ public class RestResponseHeader implements Header {
* @param type The type to convert to.
* @param args The type parameters.
* @return The response object (for method chaining).
- * @throws RestCallException If value could not be parsed.
+ * @throws ParseException If value could not be parsed.
*/
- public <T> RestResponse asOptional(Mutable<Optional<T>> m, Type type, Type...args) throws RestCallException {
+ public <T> RestResponse asOptional(Mutable<Optional<T>> m, Type type, Type...args) throws ParseException {
m.set(asOptional(type, args));
return response;
}
@@ -295,9 +282,9 @@ public class RestResponseHeader implements Header {
* @param <T> The type to convert to.
* @param type The type to convert to.
* @return The parsed value as an {@link Optional}, or an empty optional if header was not present.
- * @throws RestCallException If value could not be parsed.
+ * @throws ParseException If value could not be parsed.
*/
- public <T> Optional<T> asOptional(Class<T> type) throws RestCallException {
+ public <T> Optional<T> asOptional(Class<T> type) throws ParseException {
return Optional.ofNullable(as(type));
}
@@ -308,9 +295,9 @@ public class RestResponseHeader implements Header {
* @param <T> The type to convert to.
* @param type The type to convert to.
* @return The response object (for method chaining).
- * @throws RestCallException If value could not be parsed.
+ * @throws ParseException If value could not be parsed.
*/
- public <T> RestResponse asOptional(Mutable<Optional<T>> m, Class<T> type) throws RestCallException {
+ public <T> RestResponse asOptional(Mutable<Optional<T>> m, Class<T> type) throws ParseException {
m.set(asOptional(type));
return response;
}
@@ -321,9 +308,9 @@ public class RestResponseHeader implements Header {
* @param <T> The type to convert to.
* @param type The type to convert to.
* @return The parsed value as an {@link Optional}, or an empty optional if header was not present.
- * @throws RestCallException If value could not be parsed.
+ * @throws ParseException If value could not be parsed.
*/
- public <T> Optional<T> asOptional(ClassMeta<T> type) throws RestCallException {
+ public <T> Optional<T> asOptional(ClassMeta<T> type) throws ParseException {
return Optional.ofNullable(as(type));
}
@@ -334,9 +321,9 @@ public class RestResponseHeader implements Header {
* @param <T> The type to convert to.
* @param type The type to convert to.
* @return The response object (for method chaining).
- * @throws RestCallException If value could not be parsed.
+ * @throws ParseException If value could not be parsed.
*/
- public <T> RestResponse asOptional(Mutable<Optional<T>> m, ClassMeta<T> type) throws RestCallException {
+ public <T> RestResponse asOptional(Mutable<Optional<T>> m, ClassMeta<T> type) throws ParseException {
m.set(asOptional(type));
return response;
}
@@ -358,9 +345,8 @@ public class RestResponseHeader implements Header {
*
* @param pattern The regular expression pattern to match.
* @return The matcher.
- * @throws RestCallException If a connection error occurred.
*/
- public Matcher asMatcher(Pattern pattern) throws RestCallException {
+ public Matcher asMatcher(Pattern pattern) {
return pattern.matcher(asString());
}
@@ -383,9 +369,8 @@ public class RestResponseHeader implements Header {
* @param m The mutable to set the value in.
* @param pattern The regular expression pattern to match.
* @return The response object (for method chaining).
- * @throws RestCallException If a connection error occurred.
*/
- public RestResponse asMatcher(Mutable<Matcher> m, Pattern pattern) throws RestCallException {
+ public RestResponse asMatcher(Mutable<Matcher> m, Pattern pattern) {
m.set(pattern.matcher(asString()));
return response;
}
@@ -407,9 +392,8 @@ public class RestResponseHeader implements Header {
*
* @param regex The regular expression pattern to match.
* @return The matcher.
- * @throws RestCallException If a connection error occurred.
*/
- public Matcher asMatcher(String regex) throws RestCallException {
+ public Matcher asMatcher(String regex) {
return asMatcher(regex, 0);
}
@@ -432,9 +416,8 @@ public class RestResponseHeader implements Header {
* @param m The mutable to set the value in.
* @param regex The regular expression pattern to match.
* @return The response object (for method chaining).
- * @throws RestCallException If a connection error occurred.
*/
- public RestResponse asMatcher(Mutable<Matcher> m, String regex) throws RestCallException {
+ public RestResponse asMatcher(Mutable<Matcher> m, String regex) {
asMatcher(regex, 0);
return response;
}
@@ -457,9 +440,8 @@ public class RestResponseHeader implements Header {
* @param regex The regular expression pattern to match.
* @param flags Pattern match flags. See {@link Pattern#compile(String, int)}.
* @return The matcher.
- * @throws RestCallException If a connection error occurred.
*/
- public Matcher asMatcher(String regex, int flags) throws RestCallException {
+ public Matcher asMatcher(String regex, int flags) {
return asMatcher(Pattern.compile(regex, flags));
}
@@ -483,9 +465,8 @@ public class RestResponseHeader implements Header {
* @param regex The regular expression pattern to match.
* @param flags Pattern match flags. See {@link Pattern#compile(String, int)}.
* @return The response object (for method chaining).
- * @throws RestCallException If a connection error occurred.
*/
- public RestResponse asMatcher(Mutable<Matcher> m, String regex, int flags) throws RestCallException {
+ public RestResponse asMatcher(Mutable<Matcher> m, String regex, int flags) {
asMatcher(Pattern.compile(regex, flags));
return response;
}
@@ -516,11 +497,11 @@ public class RestResponseHeader implements Header {
* </p>
*
* @return The response object (for method chaining).
- * @throws RestCallException If assertion failed.
+ * @throws AssertionError If assertion failed.
*/
- public RestResponse assertExists() throws RestCallException {
+ public RestResponse assertExists() throws AssertionError {
if (! exists())
- throw new RestCallException("Response did not have the expected header {0}.", getName());
+ throw new BasicAssertionError("Response did not have the expected header {0}.", getName());
return response;
}
@@ -538,11 +519,11 @@ public class RestResponseHeader implements Header {
*
* @param value The value to test for.
* @return The response object (for method chaining).
- * @throws RestCallException If assertion failed.
+ * @throws AssertionError If assertion failed.
*/
- public RestResponse assertValue(String value) throws RestCallException {
+ public RestResponse assertValue(String value) throws AssertionError {
if (! StringUtils.isEquals(value, asString()))
- throw new RestCallException("Response did not have the expected value for header {0}.\n\tExpected=[{1}]\n\tActual=[{2}]", getName(), value, asString());
+ throw new BasicAssertionError("Response did not have the expected value for header {0}.\n\tExpected=[{1}]\n\tActual=[{2}]", getName(), value, asString());
return response;
}
@@ -560,12 +541,12 @@ public class RestResponseHeader implements Header {
*
* @param test The predicate to test for.
* @return The response object (for method chaining).
- * @throws RestCallException If assertion failed.
+ * @throws AssertionError If assertion failed.
*/
- public RestResponse assertValue(Predicate<String> test) throws RestCallException {
+ public RestResponse assertValue(Predicate<String> test) throws AssertionError {
String text = asString();
if (! test.test(text))
- throw new RestCallException("Response did not have the expected value for header {0}.\n\tActual=[{1}]", getName(), text);
+ throw new BasicAssertionError("Response did not have the expected value for header {0}.\n\tActual=[{1}]", getName(), text);
return response;
}
@@ -583,13 +564,13 @@ public class RestResponseHeader implements Header {
*
* @param values The substrings to test for.
* @return The response object (for method chaining).
- * @throws RestCallException If assertion failed.
+ * @throws AssertionError If assertion failed.
*/
- public RestResponse assertContains(String...values) throws RestCallException {
+ public RestResponse assertContains(String...values) throws AssertionError {
String text = asString();
for (String substring : values)
if (! StringUtils.contains(text, substring))
- throw new RestCallException("Response did not have the expected substring in header {0}.\n\tExpected=[{1}]\n\tHeader=[{2}]", getName(), substring, text);
+ throw new BasicAssertionError("Response did not have the expected substring in header {0}.\n\tExpected=[{1}]\n\tHeader=[{2}]", getName(), substring, text);
return response;
}
@@ -607,9 +588,9 @@ public class RestResponseHeader implements Header {
*
* @param regex The pattern to test for.
* @return The response object (for method chaining).
- * @throws RestCallException If assertion failed.
+ * @throws AssertionError If assertion failed.
*/
- public RestResponse assertMatches(String regex) throws RestCallException {
+ public RestResponse assertMatches(String regex) throws AssertionError {
return assertMatches(regex, 0);
}
@@ -628,12 +609,12 @@ public class RestResponseHeader implements Header {
* @param regex The pattern to test for.
* @param flags Pattern match flags. See {@link Pattern#compile(String, int)}.
* @return The response object (for method chaining).
- * @throws RestCallException If assertion failed.
+ * @throws AssertionError If assertion failed.
*/
- public RestResponse assertMatches(String regex, int flags) throws RestCallException {
+ public RestResponse assertMatches(String regex, int flags) throws AssertionError {
String text = asString();
if (! Pattern.compile(regex, flags).matcher(text).matches())
- throw new RestCallException("Response did not match expected pattern in header {0}.\n\tpattern=[{1}]\n\tHeader=[{2}]", getName(), regex, text);
+ throw new BasicAssertionError("Response did not match expected pattern in header {0}.\n\tpattern=[{1}]\n\tHeader=[{2}]", getName(), regex, text);
return response;
}
@@ -655,12 +636,12 @@ public class RestResponseHeader implements Header {
*
* @param pattern The pattern to test for.
* @return The response object (for method chaining).
- * @throws RestCallException If assertion failed.
+ * @throws AssertionError If assertion failed.
*/
- public RestResponse assertMatches(Pattern pattern) throws RestCallException {
+ public RestResponse assertMatches(Pattern pattern) throws AssertionError {
String text = asString();
if (! pattern.matcher(text).matches())
- throw new RestCallException("Response did not match expected pattern in header {0}.\n\tpattern=[{1}]\n\tHeader=[{2}]", getName(), pattern.pattern(), text);
+ throw new BasicAssertionError("Response did not match expected pattern in header {0}.\n\tpattern=[{1}]\n\tHeader=[{2}]", getName(), pattern.pattern(), text);
return response;
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java
index 68e8577..da94030 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java
@@ -165,17 +165,27 @@ public class RequestHeaders extends TreeMap<String,String[]> {
}
/**
- * Same as {@link #getString(String)} but converts the value to an integer.
+ * Returns the specified header value as an integer.
+ *
+ * <ul class='notes'>
+ * <li>
+ * If {@code allowHeaderParams} init parameter is <jk>true</jk>, then first looks for {@code &HeaderName=x} in the URL query string.
+ * </ul>
*
* @param name The HTTP header name.
- * @return The header value, or the default value if the header isn't present.
+ * @return The header value, or <code>0</code> value if the header isn't present.
*/
public int getInt(String name) {
return getInt(name, 0);
}
/**
- * Same as {@link #getString(String,String)} but converts the value to an integer.
+ * Returns the specified header value as an integer.
+ *
+ * <ul class='notes'>
+ * <li>
+ * If {@code allowHeaderParams} init parameter is <jk>true</jk>, then first looks for {@code &HeaderName=x} in the URL query string.
+ * </ul>
*
* @param name The HTTP header name.
* @param def The default value to return if the header value isn't found.
@@ -187,7 +197,12 @@ public class RequestHeaders extends TreeMap<String,String[]> {
}
/**
- * Same as {@link #getString(String)} but converts the value to a boolean.
+ * Returns the specified header value as a boolean.
+ *
+ * <ul class='notes'>
+ * <li>
+ * If {@code allowHeaderParams} init parameter is <jk>true</jk>, then first looks for {@code &HeaderName=x} in the URL query string.
+ * </ul>
*
* @param name The HTTP header name.
* @return The header value, or the default value if the header isn't present.
@@ -197,7 +212,12 @@ public class RequestHeaders extends TreeMap<String,String[]> {
}
/**
- * Same as {@link #getString(String,String)} but converts the value to a boolean.
+ * Returns the specified header value as a boolean.
+ *
+ * <ul class='notes'>
+ * <li>
+ * If {@code allowHeaderParams} init parameter is <jk>true</jk>, then first looks for {@code &HeaderName=x} in the URL query string.
+ * </ul>
*
* @param name The HTTP header name.
* @param def The default value to return if the header value isn't found.
@@ -227,10 +247,10 @@ public class RequestHeaders extends TreeMap<String,String[]> {
* <h5 class='section'>Examples:</h5>
* <p class='bcode w800'>
* <jc>// Parse into an integer.</jc>
- * <jk>int</jk> myheader = req.getHeader(<js>"My-Header"</js>, <jk>int</jk>.<jk>class</jk>);
+ * <jk>int</jk> myheader = req.getHeaders().get(<js>"My-Header"</js>, <jk>int</jk>.<jk>class</jk>);
*
* <jc>// Parse a UUID.</jc>
- * UUID myheader = req.getHeader(<js>"My-Header"</js>, UUID.<jk>class</jk>);
+ * UUID myheader = req.getHeaders().get(<js>"My-Header"</js>, UUID.<jk>class</jk>);
* </p>
*
* <ul class='notes'>
@@ -254,7 +274,59 @@ public class RequestHeaders extends TreeMap<String,String[]> {
}
/**
- * Same as {@link #get(String, Class)} but allows you to override the part parser used.
+ * Returns all headers with the specified name converted to a POJO using the {@link HttpPartParser} registered with the resource.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <jc>// Parse into an integer.</jc>
+ * <jk>int</jk>[] myheaders = req.getHeaders().getAll(<js>"My-Header"</js>, <jk>int</jk>[].<jk>class</jk>);
+ *
+ * <jc>// Parse a UUID.</jc>
+ * UUID[] myheaders = req.getHeaders().getAll(<js>"My-Header"</js>, UUID[].<jk>class</jk>);
+ * </p>
+ *
+ * <ul class='notes'>
+ * <li>
+ * If {@code allowHeaderParams} init parameter is <jk>true</jk>, then first looks for {@code &HeaderName=x} in the URL query string.
+ * <li>
+ * The class must be an array or collection type.
+ * </ul>
+ *
+ * <ul class='seealso'>
+ * <li class='jf'>{@link RestContext#REST_partParser}
+ * </ul>
+ *
+ * @param name The HTTP header name.
+ * @param type The class type to convert the header value to.
+ * @param <T> The class type to convert the header value to.
+ * @return The parameter value converted to the specified class type.
+ * @throws BadRequest Thrown if input could not be parsed.
+ * @throws InternalServerError Thrown if any other exception occurs.
+ */
+ public <T> T getAll(String name, Class<T> type) throws BadRequest, InternalServerError {
+ return getAllInner(null, null, name, getClassMeta(type));
+ }
+
+ /**
+ * Returns the specified header value converted to a POJO using the specified part parser.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <jc>// Parse into an integer.</jc>
+ * <jk>int</jk> myheader = req.getHeaders().get(<js>"My-Header"</js>, <jk>int</jk>.<jk>class</jk>);
+ *
+ * <jc>// Parse a UUID.</jc>
+ * UUID myheader = req.getHeaders().get(<js>"My-Header"</js>, UUID.<jk>class</jk>);
+ * </p>
+ *
+ * <ul class='notes'>
+ * <li>
+ * If {@code allowHeaderParams} init parameter is <jk>true</jk>, then first looks for {@code &HeaderName=x} in the URL query string.
+ * </ul>
+ *
+ * <ul class='seealso'>
+ * <li class='jf'>{@link RestContext#REST_partParser}
+ * </ul>
*
* @param parser
* The parser to use for parsing the string header.
@@ -384,8 +456,39 @@ public class RequestHeaders extends TreeMap<String,String[]> {
return getInner(parser, schema, name, null, this.<T>getClassMeta(type, args));
}
+ /**
+ * Converts all the headers with the specified name to the specified type.
+ *
+ * @param parser
+ * The parser to use for parsing the string header.
+ * <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
+ * @param schema
+ * The schema object that defines the format of the input.
+ * <br>If <jk>null</jk>, defaults to the schema defined on the parser.
+ * <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
+ * <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}).
+ * @param name
+ * The HTTP header name.
+ * @param type
+ * The type of object to create.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+ * @param args
+ * The type arguments of the class if it's a collection or map.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+ * <br>Ignored if the main type is not a map or collection.
+ * @param <T> The class type to convert the header value to.
+ * @return The parameter value converted to the specified class type.
+ * @throws BadRequest Thrown if input could not be parsed or fails schema validation.
+ * @throws InternalServerError Thrown if any other exception occurs.
+ */
+ public <T> T getAll(HttpPartParserSession parser, HttpPartSchema schema, String name, Type type, Type...args) throws BadRequest, InternalServerError {
+ return getAllInner(parser, schema, name, this.<T>getClassMeta(type, args));
+ }
+
/* Workhorse method */
private <T> T getInner(HttpPartParserSession parser, HttpPartSchema schema, String name, T def, ClassMeta<T> cm) throws BadRequest, InternalServerError {
+ if (parser == null)
+ parser = req.getPartParser();
try {
if (cm.isMapOrBean() && isOneOf(name, "*", "")) {
OMap m = new OMap();
@@ -409,6 +512,34 @@ public class RequestHeaders extends TreeMap<String,String[]> {
}
/* Workhorse method */
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ <T> T getAllInner(HttpPartParserSession parser, HttpPartSchema schema, String name, ClassMeta<T> cm) throws BadRequest, InternalServerError {
+ String[] p = get(name);
+ if (schema == null)
+ schema = HttpPartSchema.DEFAULT;
+ try {
+ if (cm.isArray()) {
+ List c = new ArrayList();
+ for (int i = 0; i < p.length; i++)
+ c.add(parse(parser, schema.getItems(), p[i], cm.getElementType()));
+ return (T)toArray(c, cm.getElementType().getInnerClass());
+ } else if (cm.isCollection()) {
+ Collection c = (Collection)(cm.canCreateNewInstance() ? cm.newInstance() : new OList());
+ for (int i = 0; i < p.length; i++)
+ c.add(parse(parser, schema.getItems(), p[i], cm.getElementType()));
+ return (T)c;
+ }
+ } catch (SchemaValidationException e) {
+ throw new BadRequest(e, "Validation failed on header ''{0}''. ", name);
+ } catch (ParseException e) {
+ throw new BadRequest(e, "Could not parse header ''{0}''.", name) ;
+ } catch (Exception e) {
+ throw new InternalServerError(e, "Could not parse header ''{0}''.", name);
+ }
+ throw new InternalServerError("Invalid call to getAll(String, ClassMeta). Class type must be a Collection or array.");
+ }
+
+ /* Workhorse method */
private <T> T parse(HttpPartParserSession parser, HttpPartSchema schema, String val, ClassMeta<T> cm) throws SchemaValidationException, ParseException {
if (parser == null)
parser = this.parser;
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPath.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPath.java
index 1ce2c9d..d2dae59 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPath.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPath.java
@@ -147,7 +147,7 @@ public class RequestPath extends TreeMap<String,String> {
* @throws InternalServerError Thrown if any other exception occurs.
*/
public <T> T get(String name, Class<T> type) throws BadRequest, InternalServerError {
- return getInner(null, null, name, null, this.<T>getClassMeta(type));
+ return getInner(null, null, name, null, getClassMeta(type));
}
/**
@@ -169,7 +169,7 @@ public class RequestPath extends TreeMap<String,String> {
* @throws InternalServerError Thrown if any other exception occurs.
*/
public <T> T get(HttpPartParserSession parser, HttpPartSchema schema, String name, Class<T> type) throws BadRequest, InternalServerError {
- return getInner(parser, schema, name, null, this.<T>getClassMeta(type));
+ return getInner(parser, schema, name, null, getClassMeta(type));
}
/**
@@ -221,7 +221,7 @@ public class RequestPath extends TreeMap<String,String> {
* @throws InternalServerError Thrown if any other exception occurs.
*/
public <T> T get(String name, Type type, Type...args) throws BadRequest, InternalServerError {
- return getInner(null, null, name, null, this.<T>getClassMeta(type, args));
+ return getInner(null, null, name, null, getClassMeta(type, args));
}
/**
@@ -249,11 +249,13 @@ public class RequestPath extends TreeMap<String,String> {
* @throws InternalServerError Thrown if any other exception occurs.
*/
public <T> T get(HttpPartParserSession parser, HttpPartSchema schema, String name, Type type, Type...args) throws BadRequest, InternalServerError {
- return getInner(parser, schema, name, null, this.<T>getClassMeta(type, args));
+ return getInner(parser, schema, name, null, getClassMeta(type, args));
}
/* Workhorse method */
private <T> T getInner(HttpPartParserSession parser, HttpPartSchema schema, String name, T def, ClassMeta<T> cm) throws BadRequest, InternalServerError {
+ if (parser == null)
+ parser = req.getPartParser();
try {
if (cm.isMapOrBean() && isOneOf(name, "*", "")) {
OMap m = new OMap();
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQuery.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQuery.java
index 5ade3a8..c8dfd04 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQuery.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQuery.java
@@ -482,7 +482,7 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
* @throws InternalServerError Thrown if any other exception occurs.
*/
public <T> T getAll(String name, Class<T> c) throws BadRequest, InternalServerError {
- return getAllInner(null, null, name, null, getClassMeta(c));
+ return getAllInner(null, null, name, getClassMeta(c));
}
/**
@@ -506,7 +506,7 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
* @throws InternalServerError Thrown if any other exception occurs.
*/
public <T> T getAll(String name, Type type, Type...args) throws BadRequest, InternalServerError {
- return getAllInner(null, null, name, null, (ClassMeta<T>)getClassMeta(type, args));
+ return getAllInner(null, null, name, (ClassMeta<T>)getClassMeta(type, args));
}
/**
@@ -534,7 +534,7 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
* @throws InternalServerError Thrown if any other exception occurs.
*/
public <T> T getAll(HttpPartParserSession parser, HttpPartSchema schema, String name, Type type, Type...args) throws BadRequest, InternalServerError {
- return getAllInner(parser, schema, name, null, (ClassMeta<T>)getClassMeta(type, args));
+ return getAllInner(parser, schema, name, getClassMeta(type, args));
}
/**
@@ -616,6 +616,8 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
/* Workhorse method */
private <T> T getInner(HttpPartParserSession parser, HttpPartSchema schema, String name, T def, ClassMeta<T> cm) throws BadRequest, InternalServerError {
+ if (parser == null)
+ parser = req.getPartParser();
try {
if (cm.isMapOrBean() && isOneOf(name, "*", "")) {
OMap m = new OMap();
@@ -624,7 +626,7 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
HttpPartSchema pschema = schema == null ? null : schema.getProperty(k);
ClassMeta<?> cm2 = cm.getValueType();
if (cm.getValueType().isCollectionOrArray())
- m.put(k, getAllInner(parser, pschema, k, null, cm2));
+ m.put(k, getAllInner(parser, pschema, k, cm2));
else
m.put(k, getInner(parser, pschema, k, null, cm2));
}
@@ -643,10 +645,8 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
/* Workhorse method */
@SuppressWarnings("rawtypes")
- private <T> T getAllInner(HttpPartParserSession parser, HttpPartSchema schema, String name, T def, ClassMeta<T> cm) throws BadRequest, InternalServerError {
+ private <T> T getAllInner(HttpPartParserSession parser, HttpPartSchema schema, String name, ClassMeta<T> cm) throws BadRequest, InternalServerError {
String[] p = get(name);
- if (p == null)
- return def;
if (schema == null)
schema = HttpPartSchema.DEFAULT;
try {
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCall.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCall.java
index f897a42..3aed6db 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCall.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCall.java
@@ -366,4 +366,15 @@ public class RestCall {
return rres.getOutput();
return null;
}
+
+ /**
+ * Shortcut for calling <c>getRestRequest().isDebug()</c>.
+ *
+ * @return <jk>true</jk> if debug is enabled for this request.
+ */
+ public boolean isDebug() {
+ if (rreq != null)
+ return rreq.isDebug();
+ return false;
+ }
}
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 2e43461..cd4bcc4 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
@@ -3816,22 +3816,8 @@ public final class RestContext extends BeanContext {
.create()
.append(getInstanceArrayProperty(REST_parsers, Parser.class, new Parser[0], resourceResolver, resource, ps))
.build();
- partSerializer =
- (HttpPartSerializer)
- SerializerGroup
- .create()
- .append(getInstanceProperty(REST_partSerializer, HttpPartSerializer.class, OpenApiSerializer.class, resourceResolver, resource, ps))
- .build()
- .getSerializers()
- .get(0);
- partParser =
- (HttpPartParser)
- ParserGroup
- .create()
- .append(getInstanceProperty(REST_partParser, HttpPartParser.class, OpenApiParser.class, resourceResolver, resource, ps))
- .build()
- .getParsers()
- .get(0);
+ partSerializer = getInstanceProperty(REST_partSerializer, HttpPartSerializer.class, OpenApiSerializer.class, resourceResolver, resource, ps);
+ partParser = getInstanceProperty(REST_partParser, HttpPartParser.class, OpenApiParser.class, resourceResolver, resource, ps);
jsonSchemaGenerator =
JsonSchemaGenerator
.create()
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
index 43ed079..24df668 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
@@ -258,11 +258,16 @@ class RestParamDefaults {
static final class HeaderObject extends RestMethodParam {
private final HttpPartParser partParser;
private final HttpPartSchema schema;
+ private final boolean multi;
protected HeaderObject(ParamInfo mpi, PropertyStore ps) {
super(HEADER, mpi, getName(mpi));
this.schema = HttpPartSchema.create(Header.class, mpi);
this.partParser = createPartParser(schema.getParser(), ps);
+ this.multi = getMulti(mpi);
+
+ if (multi && ! isCollection(type))
+ throw new InternalServerError("Use of multipart flag on @Header parameter that's not an array or Collection on method ''{0}''", mpi.getMethod());
}
private static String getName(ParamInfo mpi) {
@@ -278,10 +283,18 @@ class RestParamDefaults {
return n;
}
+ private static boolean getMulti(ParamInfo mpi) {
+ for (Header h : mpi.getAnnotations(Header.class))
+ if (h.multi())
+ return true;
+ return false;
+ }
+
@Override /* RestMethodParam */
public Object resolve(RestRequest req, RestResponse res) throws Exception {
HttpPartParserSession ps = partParser == null ? req.getPartParser() : partParser.createPartSession(req.getParserSessionArgs());
- return req.getHeaders().get(ps, schema, name, type);
+ RequestHeaders rh = req.getHeaders();
+ return multi ? rh.getAll(ps, schema, name, type) : rh.get(ps, schema, name, type);
}
}
@@ -436,7 +449,7 @@ class RestParamDefaults {
}
static final class FormDataObject extends RestMethodParam {
- private final boolean multiPart;
+ private final boolean multi;
private final HttpPartParser partParser;
private final HttpPartSchema schema;
@@ -444,9 +457,9 @@ class RestParamDefaults {
super(FORM_DATA, mpi, getName(mpi));
this.schema = HttpPartSchema.create(FormData.class, mpi);
this.partParser = createPartParser(schema.getParser(), ps);
- this.multiPart = schema.getCollectionFormat() == HttpPartSchema.CollectionFormat.MULTI;
+ this.multi = getMulti(mpi) || schema.getCollectionFormat() == HttpPartSchema.CollectionFormat.MULTI;
- if (multiPart && ! isCollection(type))
+ if (multi && ! isCollection(type))
throw new InternalServerError("Use of multipart flag on @FormData parameter that's not an array or Collection on method ''{0}''", mpi.getMethod());
}
@@ -463,17 +476,23 @@ class RestParamDefaults {
return n;
}
+ private static boolean getMulti(ParamInfo mpi) {
+ for (FormData f : mpi.getAnnotations(FormData.class))
+ if (f.multi())
+ return true;
+ return false;
+ }
+
@Override /* RestMethodParam */
public Object resolve(RestRequest req, RestResponse res) throws Exception {
HttpPartParserSession ps = partParser == null ? req.getPartParser() : partParser.createPartSession(req.getParserSessionArgs());
- if (multiPart)
- return req.getFormData().getAll(ps, schema, name, type);
- return req.getFormData().get(ps, schema, name, type);
+ RequestFormData fd = req.getFormData();
+ return multi ? fd.getAll(ps, schema, name, type) : fd.get(ps, schema, name, type);
}
}
static final class QueryObject extends RestMethodParam {
- private final boolean multiPart;
+ private final boolean multi;
private final HttpPartParser partParser;
private final HttpPartSchema schema;
@@ -481,9 +500,9 @@ class RestParamDefaults {
super(QUERY, mpi, getName(mpi));
this.schema = HttpPartSchema.create(Query.class, mpi);
this.partParser = createPartParser(schema.getParser(), ps);
- this.multiPart = schema.getCollectionFormat() == HttpPartSchema.CollectionFormat.MULTI;
+ this.multi = getMulti(mpi) || schema.getCollectionFormat() == HttpPartSchema.CollectionFormat.MULTI;
- if (multiPart && ! isCollection(type))
+ if (multi && ! isCollection(type))
throw new InternalServerError("Use of multipart flag on @Query parameter that's not an array or Collection on method ''{0}''", mpi.getMethod());
}
@@ -500,12 +519,18 @@ class RestParamDefaults {
return n;
}
+ private static boolean getMulti(ParamInfo mpi) {
+ for (Query q : mpi.getAnnotations(Query.class))
+ if (q.multi())
+ return true;
+ return false;
+ }
+
@Override /* RestMethodParam */
public Object resolve(RestRequest req, RestResponse res) throws Exception {
HttpPartParserSession ps = partParser == null ? req.getPartParser() : partParser.createPartSession(req.getParserSessionArgs());
- if (multiPart)
- return req.getQuery().getAll(ps, schema, name, type);
- return req.getQuery().get(ps, schema, name, type);
+ RequestQuery rq = req.getQuery();
+ return multi ? rq.getAll(ps, schema, name, type) : rq.get(ps, schema, name, type);
}
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
index 18e5574..4efb852 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
@@ -1860,6 +1860,14 @@ public final class RestRequest extends HttpServletRequestWrapper {
return inner;
}
+ <T> ClassMeta<T> getClassMeta(Type type, Type[] args) {
+ return beanSession.getClassMeta(type, args);
+ }
+
+ <T> ClassMeta<T> getClassMeta(Class<T> type) {
+ return beanSession.getClassMeta(type);
+ }
+
//-----------------------------------------------------------------------------------------------------------------
// Utility methods
//-----------------------------------------------------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
index f76fc9e..672482a 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
@@ -13,6 +13,7 @@
package org.apache.juneau.rest;
import static org.apache.juneau.internal.StringUtils.*;
+import static org.apache.juneau.httppart.HttpPartType.*;
import java.io.*;
import java.nio.charset.*;
@@ -88,7 +89,7 @@ public final class RestResponse extends HttpServletResponseWrapper {
String passThroughHeaders = req.getHeader("x-response-headers");
if (passThroughHeaders != null) {
HttpPartParser p = context.getPartParser();
- OMap m = p.createPartSession(req.getParserSessionArgs()).parse(HttpPartType.HEADER, null, passThroughHeaders, context.getClassMeta(OMap.class));
+ OMap m = p.createPartSession(req.getParserSessionArgs()).parse(HEADER, null, passThroughHeaders, context.getClassMeta(OMap.class));
for (Map.Entry<String,Object> e : m.entrySet())
setHeaderSafe(e.getKey(), e.getValue().toString());
}
@@ -587,6 +588,65 @@ public final class RestResponse extends HttpServletResponseWrapper {
}
/**
+ * Sets a header on the request.
+ *
+ * @param name The header name.
+ * @param value The header value.
+ * <ul>
+ * <li>Can be any POJO.
+ * <li>Converted to a string using the specified part serializer.
+ * </ul>
+ * @return This object (for method chaining).
+ * @throws SchemaValidationException Header failed schema validation.
+ * @throws SerializeException Header could not be serialized.
+ */
+ public RestResponse header(String name, Object value) throws SchemaValidationException, SerializeException {
+ return header(null, null, name, value);
+ }
+
+ /**
+ * Sets a header on the request.
+ *
+ * @param schema
+ * The schema to use to serialize the header, or <jk>null</jk> to use the default schema.
+ * @param name The header name.
+ * @param value The header value.
+ * <ul>
+ * <li>Can be any POJO.
+ * <li>Converted to a string using the specified part serializer.
+ * </ul>
+ * @return This object (for method chaining).
+ * @throws SchemaValidationException Header failed schema validation.
+ * @throws SerializeException Header could not be serialized.
+ */
+ public RestResponse header(HttpPartSchema schema, String name, Object value) throws SchemaValidationException, SerializeException {
+ return header(null, schema, name, value);
+ }
+
+ /**
+ * Sets a header on the request.
+ * @param serializer
+ * The serializer to use to serialize the header, or <jk>null</jk> to use the part serializer on the request.
+ * @param schema
+ * The schema to use to serialize the header, or <jk>null</jk> to use the default schema.
+ * @param name The header name.
+ * @param value The header value.
+ * <ul>
+ * <li>Can be any POJO.
+ * <li>Converted to a string using the specified part serializer.
+ * </ul>
+ * @return This object (for method chaining).
+ * @throws SchemaValidationException Header failed schema validation.
+ * @throws SerializeException Header could not be serialized.
+ */
+ public RestResponse header(HttpPartSerializerSession serializer, HttpPartSchema schema, String name, Object value) throws SchemaValidationException, SerializeException {
+ if (serializer == null)
+ serializer = request.getPartSerializer();
+ setHeader(name, serializer.serialize(HEADER, schema, value));
+ return this;
+ }
+
+ /**
* Same as {@link #setHeader(String, String)} but header is defined as a response part
*
* @param h Header to set.
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java
index e924609..83b50ed 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java
@@ -69,6 +69,8 @@ public class DefaultHandler implements ResponseHandler {
if (isThrowable) {
res.setHeaderSafe("Exception-Name", rm.getClassMeta().getName());
res.setHeaderSafe("Exception-Message", ((Throwable)o).getMessage());
+ if (req.isDebug())
+ ((Throwable)o).printStackTrace();
}
ResponseBeanPropertyMeta stm = rm.getStatusMethod();
diff --git a/pom.xml b/pom.xml
index 04fb109..1409956 100644
--- a/pom.xml
+++ b/pom.xml
@@ -40,6 +40,7 @@
<junit.version>4.11</junit.version>
<jaxrs.version>1.1.1</jaxrs.version>
<servlet.version>3.1.0</servlet.version>
+ <httpcore.version>4.4.13</httpcore.version>
<httpclient.version>4.5.6</httpclient.version>
<jetty.version>9.4.13.v20181111</jetty.version>
<juneau.compare.version>8.0.0</juneau.compare.version>
@@ -90,6 +91,11 @@
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpcore</artifactId>
+ <version>${httpcore.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>${httpclient.version}</version>
</dependency>