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 bd06ea3 REST refactoring.
bd06ea3 is described below
commit bd06ea39d2dd2f1731c2200d655ed6be9b3bc136
Author: JamesBognar <[email protected]>
AuthorDate: Sun Feb 21 11:51:39 2021 -0500
REST refactoring.
---
.../java/org/apache/juneau/rest/RequestPath.java | 357 --------------
.../org/apache/juneau/rest/RequestPathParam.java | 293 ++++++++++++
.../org/apache/juneau/rest/RequestPathParams.java | 512 +++++++++++++++++++++
.../apache/juneau/rest/RestOperationContext.java | 13 -
.../java/org/apache/juneau/rest/RestRequest.java | 59 +--
.../apache/juneau/rest/annotation/HookEvent.java | 2 +-
.../org/apache/juneau/rest/annotation/Rest.java | 2 +-
.../java/org/apache/juneau/rest/args/PathArg.java | 12 +-
.../apache/juneau/rest/args/RequestPathArg.java | 10 +-
.../apache/juneau/rest/converters/Traversable.java | 2 +-
.../apache/juneau/rest/vars/RequestPathVar.java | 6 +-
.../http/remote/Remote_PathAnnotation_Test.java | 10 +-
.../java/org/apache/juneau/rest/Paths_Test.java | 4 +-
.../org/apache/juneau/rest/RestOp_Params_Test.java | 2 +-
.../apache/juneau/rest/annotation/Path_Test.java | 28 +-
15 files changed, 869 insertions(+), 443 deletions(-)
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
deleted file mode 100644
index fa15203..0000000
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPath.java
+++ /dev/null
@@ -1,357 +0,0 @@
-//
***************************************************************************************************************************
-// * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file *
-// * distributed with this work for additional information regarding copyright
ownership. The ASF licenses this file *
-// * to you under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance *
-// * with the License. You may obtain a copy of the License at
*
-// *
*
-// * http://www.apache.org/licenses/LICENSE-2.0
*
-// *
*
-// * Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an *
-// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or implied. See the License for the *
-// * specific language governing permissions and limitations under the
License. *
-//
***************************************************************************************************************************
-package org.apache.juneau.rest;
-
-import static org.apache.juneau.internal.StringUtils.*;
-
-import java.lang.reflect.*;
-import java.util.*;
-
-import org.apache.juneau.*;
-import org.apache.juneau.collections.*;
-import org.apache.juneau.httppart.*;
-import org.apache.juneau.oapi.*;
-import org.apache.juneau.parser.*;
-import org.apache.juneau.http.exception.*;
-
-/**
- * Contains information about the matched path on the HTTP request.
- *
- * <p>
- * Provides access to the matched path variables and path match remainder.
- *
- * <ul class='seealso'>
- * <li class='link'>{@doc RestmRequestPathMatch}
- * </ul>
- */
-public class RequestPath extends TreeMap<String,String> {
- private static final long serialVersionUID = 1L;
-
- private final RestRequest req;
- private HttpPartParserSession parser;
-
- RequestPath(RestRequest req) {
- super(String.CASE_INSENSITIVE_ORDER);
- this.req = req;
- }
-
- RequestPath parser(HttpPartParserSession parser) {
- this.parser = parser;
- return this;
- }
-
- RequestPath remainder(String remainder) {
- put("/**", remainder);
- put("/*", urlDecode(remainder));
- return this;
- }
-
- /**
- * Sets a request query parameter value.
- *
- * @param name The parameter name.
- * @param value The parameter value.
- */
- public void put(String name, Object value) {
- super.put(name, value.toString());
- }
-
- /**
- * Returns the specified path parameter converted to a String.
- *
- * @param name The path variable name.
- * @return The parameter value.
- * @throws BadRequest Thrown if input could not be parsed.
- * @throws InternalServerError Thrown if any other exception occurs.
- */
- public String getString(String name) throws BadRequest,
InternalServerError {
- return getInner(parser, null, name, null,
req.getBeanSession().string());
- }
-
- /**
- * Returns the specified path parameter converted to an integer.
- *
- * @param name The path variable name.
- * @return The parameter value.
- * @throws BadRequest Thrown if input could not be parsed.
- * @throws InternalServerError Thrown if any other exception occurs.
- */
- public int getInt(String name) throws BadRequest, InternalServerError {
- return getInner(parser, null, name, null,
getClassMeta(int.class));
- }
-
- /**
- * Returns the specified path parameter converted to a boolean.
- *
- * @param name The path variable name.
- * @return The parameter value.
- * @throws BadRequest Thrown if input could not be parsed.
- * @throws InternalServerError Thrown if any other exception occurs.
- */
- public boolean getBoolean(String name) throws BadRequest,
InternalServerError {
- return getInner(null, null, name, null,
getClassMeta(boolean.class));
- }
-
- /**
- * Returns the specified path 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> <jv>myparam</jv> =
<jv>path</jv>.get(<js>"myparam"</js>, <jk>int</jk>.<jk>class</jk>);
- *
- * <jc>// Parse into an int array.</jc>
- * <jk>int</jk>[] <jv>myparam</jv> =
<jv>path</jv>.get(<js>"myparam"</js>, <jk>int</jk>[].<jk>class</jk>);
-
- * <jc>// Parse into a bean.</jc>
- * MyBean <jv>myparam</jv> = <jv>path</jv>.get(<js>"myparam"</js>,
MyBean.<jk>class</jk>);
- *
- * <jc>// Parse into a linked-list of objects.</jc>
- * List <jv>myparam</jv> = <jv>path</jv>.get(<js>"myparam"</js>,
LinkedList.<jk>class</jk>);
- *
- * <jc>// Parse into a map of object keys/values.</jc>
- * Map <jv>myparam</jv> = <jv>path</jv>.get(<js>"myparam"</js>,
TreeMap.<jk>class</jk>);
- * </p>
- *
- * <ul class='seealso'>
- * <li class='jf'>{@link RestContext#REST_partParser}
- * </ul>
- *
- * @param name The attribute name.
- * @param type The class type to convert the attribute value to.
- * @param <T> The class type to convert the attribute value to.
- * @return The attribute 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(String name, Class<T> type) throws BadRequest,
InternalServerError {
- return getInner(null, null, name, null, getClassMeta(type));
- }
-
- /**
- * Same as {@link #get(String, Class)} but allows you to override the
part parser.
- *
- * @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 attribute name.
- * @param type The class type to convert the attribute value to.
- * @param <T> The class type to convert the attribute value to.
- * @return The attribute 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, Class<T> type) throws BadRequest, InternalServerError {
- return getInner(parser, schema, name, null, getClassMeta(type));
- }
-
- /**
- * Returns the specified query parameter value converted to a POJO
using the {@link HttpPartParser} registered with the resource.
- *
- * <p>
- * Similar to {@link #get(String,Class)} but allows for complex
collections of POJOs to be created.
- *
- * <p>
- * Use this method if you want to parse into a parameterized
<c>Map</c>/<c>Collection</c> object.
- *
- * <h5 class='section'>Examples:</h5>
- * <p class='bcode w800'>
- * <jc>// Parse into a linked-list of strings.</jc>
- * List<String> <jv>myparam</jv> =
<jv>req</jv>.getPathParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>,
String.<jk>class</jk>);
- *
- * <jc>// Parse into a linked-list of linked-lists of strings.</jc>
- * List<List<String>> <jv>myparam</jv> =
<jv>req</jv>.getPathParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>,
LinkedList.<jk>class</jk>, String.<jk>class</jk>);
- *
- * <jc>// Parse into a map of string keys/values.</jc>
- * Map<String,String> <jv>myparam</jv> =
<jv>req</jv>.getPathParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>,
String.<jk>class</jk>, String.<jk>class</jk>);
- *
- * <jc>// Parse into a map containing string keys and values of
lists containing beans.</jc>
- * Map<String,List<MyBean>> <jv>myparam</jv> =
<jv>req</jv>.getPathParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>,
String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
- * </p>
- *
- * <ul class='notes'>
- * <li>
- * <c>Collections</c> must be followed by zero or one
parameter representing the value type.
- * <li>
- * <c>Maps</c> must be followed by zero or two parameters
representing the key and value types.
- * </ul>
- *
- * <ul class='seealso'>
- * <li class='jf'>{@link RestContext#REST_partParser}
- * </ul>
- *
- * @param name The attribute 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 attribute value to.
- * @return The attribute 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(String name, Type type, Type...args) throws
BadRequest, InternalServerError {
- return getInner(null, null, name, null, getClassMeta(type,
args));
- }
-
- /**
- * Same as {@link #get(String, Type, Type...)} but allows you to
override the part parser.
- *
- * @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 attribute 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 attribute value to.
- * @return The attribute 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, Type type, Type...args) throws BadRequest, InternalServerError {
- 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.getPartParserSession();
- try {
- if (cm.isMapOrBean() && isOneOf(name, "*", "")) {
- OMap m = new OMap();
- for (Map.Entry<String,String> e :
this.entrySet()) {
- String k = e.getKey();
- HttpPartSchema pschema = schema == null
? null : schema.getProperty(k);
- ClassMeta<?> cm2 = cm.getValueType();
- m.put(k, getInner(parser, pschema, k,
null, cm2));
- }
- return req.getBeanSession().convertToType(m,
cm);
- }
- T t = parse(parser, schema, get(name), cm);
- return (t == null ? def : t);
- } catch (SchemaValidationException e) {
- throw new BadRequest(e, "Validation failed on path
parameter ''{0}''. ", name);
- } catch (ParseException e) {
- throw new BadRequest(e, "Could not parse path parameter
''{0}''.", name) ;
- } catch (Exception e) {
- throw new InternalServerError(e, "Could not parse path
parameter ''{0}''.", name) ;
- }
- }
-
- /* Workhorse method */
- private <T> T parse(HttpPartParserSession parser, HttpPartSchema
schema, String val, ClassMeta<T> cm) throws SchemaValidationException,
ParseException {
- if (parser == null)
- parser = this.parser;
- return parser.parse(HttpPartType.PATH, schema, val, cm);
- }
-
- /**
- * Returns the decoded remainder of the URL following any path pattern
matches.
- *
- * <p>
- * The behavior of path remainder is shown below given the path pattern
"/foo/*":
- * <table class='styled'>
- * <tr>
- * <th>URL</th>
- * <th>Path Remainder</th>
- * </tr>
- * <tr>
- * <td><c>/foo</c></td>
- * <td><jk>null</jk></td>
- * </tr>
- * <tr>
- * <td><c>/foo/</c></td>
- * <td><js>""</js></td>
- * </tr>
- * <tr>
- * <td><c>/foo//</c></td>
- * <td><js>"/"</js></td>
- * </tr>
- * <tr>
- * <td><c>/foo///</c></td>
- * <td><js>"//"</js></td>
- * </tr>
- * <tr>
- * <td><c>/foo/a/b</c></td>
- * <td><js>"a/b"</js></td>
- * </tr>
- * <tr>
- * <td><c>/foo//a/b/</c></td>
- * <td><js>"/a/b/"</js></td>
- * </tr>
- * <tr>
- * <td><c>/foo/a%2Fb</c></td>
- * <td><js>"a/b"</js></td>
- * </tr>
- * </table>
- *
- * <h5 class='section'>Example:</h5>
- * <p class='bcode w800'>
- * <jc>// REST method</jc>
- * <ja>@RestGet</ja>(<js>"/foo/{bar}/*"</js>)
- * <jk>public</jk> String doGetById(RequestPathMatch
<jv>path</jv>, <jk>int</jk> <jv>bar</jv>) {
- * <jk>return</jk> <jv>path</jv>.getRemainder();
- * }
- * </p>
- *
- * <p>
- * The remainder can also be retrieved by calling
<code>get(<js>"/*"</js>)</code>.
- *
- * @return The path remainder string.
- */
- public String getRemainder() {
- return get("/*");
- }
-
- /**
- * Same as {@link #getRemainder()} but doesn't decode characters.
- *
- * <p>
- * The undecoded remainder can also be retrieved by calling
<code>get(<js>"/**"</js>)</code>.
- *
- * @return The un-decoded path remainder.
- */
- public String getRemainderUndecoded() {
- return get("/**");
- }
-
-
//-----------------------------------------------------------------------------------------------------------------
- // Helper methods
-
//-----------------------------------------------------------------------------------------------------------------
-
- private <T> ClassMeta<T> getClassMeta(Type type, Type...args) {
- return req.getBeanSession().getClassMeta(type, args);
- }
-
- private <T> ClassMeta<T> getClassMeta(Class<T> type) {
- return req.getBeanSession().getClassMeta(type);
- }
-}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPathParam.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPathParam.java
new file mode 100644
index 0000000..b8c404d
--- /dev/null
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPathParam.java
@@ -0,0 +1,293 @@
+//
***************************************************************************************************************************
+// * 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.rest;
+
+import static org.apache.juneau.httppart.HttpPartType.*;
+
+import java.time.*;
+import java.util.*;
+
+import org.apache.http.*;
+import org.apache.juneau.*;
+import org.apache.juneau.assertions.*;
+import org.apache.juneau.http.*;
+import org.apache.juneau.http.pair.*;
+import org.apache.juneau.httppart.*;
+import org.apache.juneau.reflect.*;
+
+/**
+ * Represents a single path parameter on an HTTP request.
+ */
+public class RequestPathParam extends RequestHttpPart implements NameValuePair
{
+
+ private final String value;
+
+ /**
+ * Constructor.
+ *
+ * @param request The request object.
+ * @param name The parameter name.
+ * @param value The parameter value.
+ */
+ public RequestPathParam(RestRequest request, String name, String value)
{
+ super(PATH, request, name);
+ this.value = value;
+ }
+
+
//------------------------------------------------------------------------------------------------------------------
+ // Retrievers
+
//------------------------------------------------------------------------------------------------------------------
+
+ @Override /* RequestHttpPart */
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * Returns the value of this parameter as an integer.
+ *
+ * @return The value of this parameter as an integer, or {@link
Optional#empty()} if the parameter was not present.
+ */
+ public Optional<Integer> asInteger() {
+ return asNamedInteger().asInteger();
+ }
+
+ /**
+ * Returns the value of this parameter as a boolean.
+ *
+ * @return The value of this parameter as a boolean, or {@link
Optional#empty()} if the parameter was not present.
+ */
+ public Optional<Boolean> asBoolean() {
+ return asNamedBoolean().asBoolean();
+ }
+
+ /**
+ * Returns the value of this parameter as a long.
+ *
+ * @return The value of this parameter as a long, or {@link
Optional#empty()} if the parameter was not present.
+ */
+ public Optional<Long> asLong() {
+ return asNamedLong().asLong();
+ }
+
+ /**
+ * Returns the value of this parameter as a date.
+ *
+ * @return The value of this parameter as a date, or {@link
Optional#empty()} if the parameter was not present.
+ */
+ public Optional<ZonedDateTime> asDate() {
+ return asNamedDate().asZonedDateTime();
+ }
+
+ /**
+ * Returns the value of this parameter as a list from a comma-delimited
string.
+ *
+ * @return The value of this parameter as a list from a comma-delimited
string, or {@link Optional#empty()} if the parameter was not present.
+ */
+ public Optional<List<String>> asCsvArray() {
+ return asNamedCsvArray().asList();
+ }
+
+ /**
+ * Returns the value of this parameter as a {@link BasicNameValuePair}.
+ *
+ * @param c The subclass of {@link BasicNameValuePair} to instantiate.
+ * @param <T> The subclass of {@link BasicNameValuePair} to instantiate.
+ * @return The value of this parameter as a string, never <jk>null</jk>.
+ */
+ public <T extends BasicNameValuePair> T asNameValuePair(Class<T> c) {
+ try {
+ ClassInfo ci = ClassInfo.of(c);
+ ConstructorInfo cc =
ci.getConstructor(Visibility.PUBLIC, String.class);
+ if (cc != null)
+ return cc.invoke(orElse(null));
+ cc = ci.getConstructor(Visibility.PUBLIC, String.class,
String.class);
+ if (cc != null)
+ return cc.invoke(getName(), orElse(null));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ throw new BasicRuntimeException("Could not determine a method
to construct type {0}", c.getClass().getName());
+ }
+
+ /**
+ * Returns the value of this parameter as a {@link BasicNamedCsvArray}.
+ *
+ * @return The value of this parameter as a {@link BasicNamedCsvArray},
never <jk>null</jk>.
+ */
+ public BasicNamedCsvArray asNamedCsvArray() {
+ return new BasicNamedCsvArray(getName(), getValue());
+ }
+
+ /**
+ * Returns the value of this parameter as a {@link BasicNamedDate}.
+ *
+ * @return The value of this parameter as a {@link BasicNamedDate},
never <jk>null</jk>.
+ */
+ public BasicNamedDate asNamedDate() {
+ return new BasicNamedDate(getName(), getValue());
+ }
+
+ /**
+ * Returns the value of this parameter as a {@link BasicNamedInteger}.
+ *
+ * @return The value of this parameter as a {@link BasicNamedInteger},
never <jk>null</jk>.
+ */
+ public BasicNamedInteger asNamedInteger() {
+ return new BasicNamedInteger(getName(), getValue());
+ }
+
+ /**
+ * Returns the value of this parameter as a {@link BasicNamedBoolean}.
+ *
+ * @return The value of this parameter as a {@link BasicNamedBoolean},
never <jk>null</jk>.
+ */
+ public BasicNamedBoolean asNamedBoolean() {
+ return new BasicNamedBoolean(getName(), getValue());
+ }
+
+ /**
+ * Returns the value of this parameter as a {@link BasicNamedLong}.
+ *
+ * @return The value of this parameter as a {@link BasicNamedLong},
never <jk>null</jk>.
+ */
+ public BasicNamedLong asNamedLong() {
+ return new BasicNamedLong(getName(), getValue());
+ }
+
+ /**
+ * Returns the value of this parameter as a {@link BasicNamedString}.
+ *
+ * @return The value of this parameter as a {@link BasicNamedString},
never <jk>null</jk>.
+ */
+ public BasicNamedString asNamedString() {
+ return new BasicNamedString(getName(), getValue());
+ }
+
+
//------------------------------------------------------------------------------------------------------------------
+ // Assertions.
+
//------------------------------------------------------------------------------------------------------------------
+
+ /**
+ * Provides the ability to perform fluent-style assertions on this
parameter.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <jv>request</jv>
+ * .getPathParam(<js>"foo"</js>)
+ * .assertString().contains(<js>"bar"</js>);
+ * </p>
+ *
+ * <p>
+ * The assertion test returns the original object allowing you to chain
multiple requests like so:
+ * <p class='bcode w800'>
+ * String <jv>foo</jv> = <jv>request</jv>
+ * .getPathParam(<js>"foo"</js>)
+ * .assertString().contains(<js>"bar"</js>)
+ * .asString().get();
+ * </p>
+ *
+ * @return A new fluent assertion object.
+ */
+ public FluentStringAssertion<RequestPathParam> assertString() {
+ return new FluentStringAssertion<>(orElse(null), this);
+ }
+
+ /**
+ * Provides the ability to perform fluent-style assertions on an
integer parameter.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <jv>request</jv>
+ * .getPathParam(<js>"age"</js>)
+ * .assertInteger().isGreaterThan(1);
+ * </p>
+ *
+ * @return A new fluent assertion object.
+ */
+ public FluentIntegerAssertion<RequestPathParam> assertInteger() {
+ return new
FluentIntegerAssertion<>(asNamedInteger().asInteger().orElse(null), this);
+ }
+
+ /**
+ * Provides the ability to perform fluent-style assertions on a long
parameter.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <jv>request</jv>
+ * .getPathParam(<js>"length"</js>)
+ * .assertLong().isLessThan(100000);
+ * </p>
+ *
+ * @return A new fluent assertion object.
+ */
+ public FluentLongAssertion<RequestPathParam> assertLong() {
+ return new
FluentLongAssertion<>(asNamedLong().asLong().orElse(null), this);
+ }
+
+ /**
+ * Provides the ability to perform fluent-style assertions on a date
parameter.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <jv>request</jv>
+ * .getPathParam(<js>"time"</js>)
+ * .assertDate().isAfterNow();
+ * </p>
+ *
+ * @return A new fluent assertion object.
+ */
+ public FluentZonedDateTimeAssertion<RequestPathParam> assertDate() {
+ return new
FluentZonedDateTimeAssertion<>(asNamedDate().asZonedDateTime().orElse(null),
this);
+ }
+
+ /**
+ * Provides the ability to perform fluent-style assertions on
comma-separated string parameters.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * <jv>request</jv>
+ * .getPathParam(<js>"allow"</js>)
+ * .assertCsvArray().contains(<js>"GET"</js>);
+ * </p>
+ *
+ * @return A new fluent assertion object.
+ */
+ public FluentListAssertion<RequestPathParam> assertCsvArray() {
+ return new
FluentListAssertion<>(asNamedCsvArray().asList().orElse(null), this);
+ }
+
+
//------------------------------------------------------------------------------------------------------------------
+ // Header passthrough methods.
+
//------------------------------------------------------------------------------------------------------------------
+
+ @Override /* Object */
+ public String toString() {
+ return getName() + "=" + getValue();
+ }
+
+ // <FluentSetters>
+
+ @Override /* GENERATED */
+ public RequestPathParam schema(HttpPartSchema value) {
+ super.schema(value);
+ return this;
+ }
+
+ @Override /* GENERATED */
+ public RequestPathParam parser(HttpPartParserSession value) {
+ super.parser(value);
+ return this;
+ }
+ // </FluentSetters>
+}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPathParams.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPathParams.java
new file mode 100644
index 0000000..9811e77
--- /dev/null
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPathParams.java
@@ -0,0 +1,512 @@
+//
***************************************************************************************************************************
+// * 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.rest;
+
+import static org.apache.juneau.internal.StringUtils.*;
+import static java.util.Collections.*;
+import static org.apache.juneau.assertions.Assertions.*;
+
+import java.time.*;
+import java.util.*;
+
+import org.apache.http.*;
+import org.apache.juneau.httppart.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.rest.util.*;
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.collections.*;
+
+/**
+ * Represents the path parameters in an HTTP request.
+ */
+@BeanIgnore
+public class RequestPathParams {
+
+ private final RestCall call;
+ private final RestRequest req;
+ private final boolean caseSensitive;
+ private HttpPartParserSession parser;
+
+ private List<RequestPathParam> list = new LinkedList<>();
+ private Map<String,List<RequestPathParam>> map = new TreeMap<>();
+
+ RequestPathParams(RestCall call, RestRequest req, boolean
caseSensitive) {
+ this.call = call;
+ this.req = req;
+ this.caseSensitive = caseSensitive;
+
+ // Add parameters from parent context if any.
+ @SuppressWarnings("unchecked")
+ Map<String,String> parentVars =
(Map<String,String>)req.getAttribute("juneau.pathVars").orElse(null);
+ if (parentVars != null)
+ for (Map.Entry<String,String> e : parentVars.entrySet())
+ add(e.getKey(), e.getValue());
+
+ UrlPathMatch pm = call.getUrlPathMatch();
+ if (pm != null) {
+ for (Map.Entry<String,String> e :
pm.getVars().entrySet())
+ add(e.getKey(), e.getValue());
+ String r = pm.getRemainder();
+ if (r != null) {
+ add("/**", r);
+ add("/*", urlDecode(r));
+ }
+ }
+ }
+
+ /**
+ * Copy constructor.
+ */
+ private RequestPathParams(RequestPathParams copyFrom) {
+ call = copyFrom.call;
+ req = copyFrom.req;
+ caseSensitive = copyFrom.caseSensitive;
+ parser = copyFrom.parser;
+ list.addAll(copyFrom.list);
+ map.putAll(copyFrom.map);
+ }
+
+ RequestPathParams parser(HttpPartParserSession parser) {
+ this.parser = parser;
+ for (RequestPathParam p : list)
+ p.parser(parser);
+ return this;
+ }
+
+
//-----------------------------------------------------------------------------------------------------------------
+ // Basic operations.
+
//-----------------------------------------------------------------------------------------------------------------
+
+ /**
+ * Adds default entries to these parameters.
+ *
+ * <p>
+ * Similar to {@link #set(String, Object)} but doesn't override
existing values.
+ *
+ * @param pairs
+ * The default entries.
+ * <br>Can be <jk>null</jk>.
+ * @return This object (for method chaining).
+ */
+ public RequestPathParams addDefault(List<NameValuePair> pairs) {
+ for (NameValuePair p : pairs) {
+ String name = p.getName();
+ String key = key(name);
+ List<RequestPathParam> l = map.get(key);
+ boolean hasAllBlanks = l != null &&
l.stream().allMatch(x -> StringUtils.isEmpty(x.getValue()));
+ if (l == null || hasAllBlanks) {
+ if (hasAllBlanks)
+ list.removeAll(l);
+ RequestPathParam x = new RequestPathParam(req,
name, p.getValue());
+ list.add(x);
+ map.put(key, AList.of(x));
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Returns all the parameters with the specified name.
+ *
+ * @param name The parameter name.
+ * @return The list of all parameters with the specified name, or an
empty list if none are found.
+ */
+ public List<RequestPathParam> getAll(String name) {
+ assertArgNotNull("name", name);
+ List<RequestPathParam> l = map.get(key(name));
+ return unmodifiableList(l == null ? emptyList() : l);
+ }
+
+ /**
+ * Returns all the parameters on this request.
+ *
+ * @return All the parameters on this request.
+ */
+ public List<RequestPathParam> getAll() {
+ return unmodifiableList(list);
+ }
+
+ /**
+ * Returns <jk>true</jk> if the parameters with the specified names are
present.
+ *
+ * @param names The parameter names. Must not be <jk>null</jk>.
+ * @return <jk>true</jk> if the parameters with the specified names are
present.
+ */
+ public boolean contains(String...names) {
+ assertArgNotNull("names", names);
+ for (String n : names)
+ if (! map.containsKey(key(n)))
+ return false;
+ return true;
+ }
+
+ /**
+ * Returns <jk>true</jk> if the parameter with any of the specified
names are present.
+ *
+ * @param names The parameter names. Must not be <jk>null</jk>.
+ * @return <jk>true</jk> if the parameter with any of the specified
names are present.
+ */
+ public boolean containsAny(String...names) {
+ assertArgNotNull("names", names);
+ for (String n : names)
+ if (map.containsKey(key(n)))
+ return true;
+ return false;
+ }
+
+ /**
+ * Returns <jk>true</jk> if these parameters are empty.
+ *
+ * @return <jk>true</jk> if these parameters are empty.
+ */
+ public boolean isEmpty() {
+ return list.isEmpty();
+ }
+
+ /**
+ * Adds a parameter value.
+ *
+ * <p>
+ * Parameter is added to the end.
+ * <br>Existing parameter with the same name are not changed.
+ *
+ * @param name The parameter name. Must not be <jk>null</jk>.
+ * @param value The parameter value.
+ * @return This object (for method chaining).
+ */
+ public RequestPathParams add(String name, Object value) {
+ assertArgNotNull("name", name);
+ String key = key(name);
+ RequestPathParam h = new RequestPathParam(req, name,
stringify(value)).parser(parser);
+ if (map.containsKey(key))
+ map.get(key).add(h);
+ else
+ map.put(key, AList.of(h));
+ list.add(h);
+ return this;
+ }
+
+ /**
+ * Adds request parameter values.
+ *
+ * <p>
+ * Parameters are added to the end.
+ * <br>Existing parameters with the same name are not changed.
+ *
+ * @param parameters The parameter objects. Must not be <jk>null</jk>.
+ * @return This object (for method chaining).
+ */
+ public RequestPathParams add(NameValuePair...parameters) {
+ assertArgNotNull("parameters", parameters);
+ for (NameValuePair p : parameters) {
+ if (p != null)
+ add(p.getName(), p.getValue());
+ }
+ return this;
+ }
+
+ /**
+ * Sets a parameter value.
+ *
+ * <p>
+ * Parameter is added to the end.
+ * <br>Any previous parameters with the same name are removed.
+ *
+ * @param name The parameter name. Must not be <jk>null</jk>.
+ * @param value
+ * The parameter value.
+ * <br>Converted to a string using {@link Object#toString()}.
+ * <br>Can be <jk>null</jk>.
+ * @return This object (for method chaining).
+ */
+ public RequestPathParams set(String name, Object value) {
+ assertArgNotNull("name", name);
+ String key = key(name);
+ RequestPathParam p = new RequestPathParam(req, name,
stringify(value)).parser(parser);
+ if (map.containsKey(key))
+
list.removeIf(x->caseSensitive?x.getName().equals(name):x.getName().equalsIgnoreCase(name));
+ list.add(p);
+ map.put(key, AList.of(p));
+ return this;
+ }
+
+
+ /**
+ * Sets request header values.
+ *
+ * <p>
+ * Parameters are added to the end of the headers.
+ * <br>Any previous parameters with the same name are removed.
+ *
+ * @param parameters The parameters to set. Must not be <jk>null</jk>
or contain <jk>null</jk>.
+ * @return This object (for method chaining).
+ */
+ public RequestPathParams set(NameValuePair...parameters) {
+ assertArgNotNull("headers", parameters);
+ for (NameValuePair p : parameters)
+ remove(p);
+ for (NameValuePair p : parameters)
+ add(p);
+ return this;
+ }
+
+ /**
+ * Remove parameters.
+ *
+ * @param name The parameter names. Must not be <jk>null</jk>.
+ * @return This object (for method chaining).
+ */
+ public RequestPathParams remove(String...name) {
+ assertArgNotNull("name", name);
+ for (String n : name) {
+ String key = key(n);
+ if (map.containsKey(key))
+ list.removeAll(map.get(key));
+ map.remove(key);
+ }
+ return this;
+ }
+
+ /**
+ * Remove parameters.
+ *
+ * @param parameters The parameters to remove. Must not be
<jk>null</jk>.
+ * @return This object (for method chaining).
+ */
+ public RequestPathParams remove(NameValuePair...parameters) {
+ for (NameValuePair p : parameters)
+ remove(p.getName());
+ return this;
+ }
+
+
//-----------------------------------------------------------------------------------------------------------------
+ // Convenience getters.
+
//-----------------------------------------------------------------------------------------------------------------
+
+ /**
+ * Returns the first parameter with the specified name.
+ *
+ * <p>
+ * Note that this method never returns <jk>null</jk> and that {@link
RequestPathParam#isPresent()} can be used
+ * to test for the existence of the parameter.
+ *
+ * @param name The parameter name.
+ * @return The parameter. Never <jk>null</jk>.
+ */
+ public RequestPathParam getFirst(String name) {
+ assertArgNotNull("name", name);
+ List<RequestPathParam> l = map.get(key(name));
+ return (l == null || l.isEmpty() ? new RequestPathParam(req,
name, null).parser(parser) : l.get(0));
+ }
+
+ /**
+ * Returns the last parameter with the specified name.
+ *
+ * <p>
+ * Note that this method never returns <jk>null</jk> and that {@link
RequestPathParam#isPresent()} can be used
+ * to test for the existence of the parameter.
+ *
+ * @param name The parameter name.
+ * @return The parameter. Never <jk>null</jk>.
+ */
+ public RequestPathParam getLast(String name) {
+ assertArgNotNull("name", name);
+ List<RequestPathParam> l = map.get(key(name));
+ return (l == null || l.isEmpty() ? new RequestPathParam(req,
name, null).parser(parser) : l.get(l.size()-1));
+ }
+
+ /**
+ * Returns the last parameter with the specified name.
+ *
+ * <p>
+ * This is equivalent to {@link #getLast(String)}.
+ *
+ * @param name The parameter name.
+ * @return The parameter value, or {@link Optional#empty()} if it
doesn't exist.
+ */
+ public RequestPathParam get(String name) {
+ return getLast(name);
+ }
+
+ /**
+ * Returns the last parameter with the specified name as a string.
+ *
+ * @param name The parameter name.
+ * @return The parameter value, or {@link Optional#empty()} if it
doesn't exist.
+ */
+ public Optional<String> getString(String name) {
+ return getLast(name).asString();
+ }
+
+ /**
+ * Returns the last parameter with the specified name as an integer.
+ *
+ * @param name The parameter name.
+ * @return The parameter value, or {@link Optional#empty()} if it
doesn't exist.
+ */
+ public Optional<Integer> getInteger(String name) {
+ return getLast(name).asInteger();
+ }
+
+ /**
+ * Returns the last parameter with the specified name as a boolean.
+ *
+ * @param name The parameter name.
+ * @return The parameter value, or {@link Optional#empty()} if it
doesn't exist.
+ */
+ public Optional<Boolean> getBoolean(String name) {
+ return getLast(name).asBoolean();
+ }
+
+ /**
+ * Returns the last parameter with the specified name as a list from a
comma-delimited string.
+ *
+ * @param name The parameter name.
+ * @return The parameter value, or {@link Optional#empty()} if it
doesn't exist.
+ */
+ public Optional<List<String>> getCsvArray(String name) {
+ return getLast(name).asCsvArray();
+ }
+
+ /**
+ * Returns the last parameter with the specified name as a long.
+ *
+ * @param name The parameter name.
+ * @return The parameter value, or {@link Optional#empty()} if it
doesn't exist.
+ */
+ public Optional<Long> getLong(String name) {
+ return getLast(name).asLong();
+ }
+
+ /**
+ * Returns the last parameter with the specified name as a boolean.
+ *
+ * @param name The parameter name.
+ * @return The parameter value, or {@link Optional#empty()} if it
doesn't exist.
+ */
+ public Optional<ZonedDateTime> getDate(String name) {
+ return getLast(name).asDate();
+ }
+
+
//-----------------------------------------------------------------------------------------------------------------
+ // Other methods.
+
//-----------------------------------------------------------------------------------------------------------------
+
+ /**
+ * Makes a copy of these parameters.
+ *
+ * @return A new parameters object.
+ */
+ public RequestPathParams copy() {
+ return new RequestPathParams(this);
+ }
+
+ /**
+ * Returns the decoded remainder of the URL following any path pattern
matches.
+ *
+ * <p>
+ * The behavior of path remainder is shown below given the path pattern
"/foo/*":
+ * <table class='styled'>
+ * <tr>
+ * <th>URL</th>
+ * <th>Path Remainder</th>
+ * </tr>
+ * <tr>
+ * <td><c>/foo</c></td>
+ * <td><jk>null</jk></td>
+ * </tr>
+ * <tr>
+ * <td><c>/foo/</c></td>
+ * <td><js>""</js></td>
+ * </tr>
+ * <tr>
+ * <td><c>/foo//</c></td>
+ * <td><js>"/"</js></td>
+ * </tr>
+ * <tr>
+ * <td><c>/foo///</c></td>
+ * <td><js>"//"</js></td>
+ * </tr>
+ * <tr>
+ * <td><c>/foo/a/b</c></td>
+ * <td><js>"a/b"</js></td>
+ * </tr>
+ * <tr>
+ * <td><c>/foo//a/b/</c></td>
+ * <td><js>"/a/b/"</js></td>
+ * </tr>
+ * <tr>
+ * <td><c>/foo/a%2Fb</c></td>
+ * <td><js>"a/b"</js></td>
+ * </tr>
+ * </table>
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode w800'>
+ * <jc>// REST method</jc>
+ * <ja>@RestGet</ja>(<js>"/foo/{bar}/*"</js>)
+ * <jk>public</jk> String doGetById(RequestPathParams
<jv>path</jv>, <jk>int</jk> <jv>bar</jv>) {
+ * <jk>return</jk>
<jv>path</jv>.remainder().orElse(<jk>null</jk>);
+ * }
+ * </p>
+ *
+ * <p>
+ * The remainder can also be retrieved by calling
<code>get(<js>"/**"</js>)</code>.
+ *
+ * @return The path remainder string.
+ */
+ public RequestPathParam getRemainder() {
+ return get("/*");
+
+ }
+
+ /**
+ * Same as {@link #getRemainder()} but doesn't decode characters.
+ *
+ * <p>
+ * The undecoded remainder can also be retrieved by calling
<code>get(<js>"/*"</js>)</code>.
+ *
+ * @return The un-decoded path remainder.
+ */
+ public RequestPathParam getRemainderUndecoded() {
+ return get("/**");
+ }
+
+ /**
+ * Converts the parameters to a readable string.
+ *
+ * @param sorted Sort the parameters by name.
+ * @return A JSON string containing the contents of the parameters.
+ */
+ public String toString(boolean sorted) {
+ OMap m = OMap.create();
+ if (sorted) {
+ for (List<RequestPathParam> p1 : map.values())
+ for (RequestPathParam p2 : p1)
+ m.append(p2.getName(), p2.getValue());
+ } else {
+ for (RequestPathParam p : list)
+ m.append(p.getName(), p.getValue());
+ }
+ return m.toString();
+ }
+
+ private String key(String name) {
+ return caseSensitive ? name : name.toLowerCase();
+ }
+
+ @Override /* Object */
+ public String toString() {
+ return toString(false);
+ }
+}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOperationContext.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOperationContext.java
index 040de8f..324b8d6 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOperationContext.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOperationContext.java
@@ -1838,22 +1838,9 @@ public class RestOperationContext extends BeanContext
implements Comparable<Rest
call.restOperationContext(this);
- UrlPathMatch pm = call.getUrlPathMatch();
- if (pm == null)
- pm = matchPattern(call);
-
- if (pm == null)
- throw new NotFound();
-
RestRequest req = call.getRestRequest();
RestResponse res = call.getRestResponse();
- RequestPath rp = req.getPathMatch();
- for (Map.Entry<String,String> e : pm.getVars().entrySet())
- rp.put(e.getKey(), e.getValue());
- if (pm.getRemainder() != null)
- rp.remainder(pm.getRemainder());
-
context.preCall(call);
call.logger(callLogger);
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 3fc81ec..f585979 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
@@ -115,7 +115,7 @@ public final class RestRequest {
private final RequestBody body;
private final BeanSession beanSession;
private final RequestQueryParams queryParams;
- private final RequestPath pathParams;
+ private final RequestPathParams pathParams;
private final RequestHeaders headers;
private final RequestAttributes attrs;
private final HttpPartSerializerSession partSerializerSession;
@@ -159,8 +159,7 @@ public final class RestRequest {
}
}
- pathParams = new RequestPath(this);
- pathParams.putAll(call.getPathVars());
+ pathParams = new RequestPathParams(call, this, true);
beanSession = opContext.createSession();
@@ -1202,10 +1201,10 @@ public final class RestRequest {
//-----------------------------------------------------------------------------------------------------------------
/**
- * Request path match.
+ * Path parameters.
*
* <p>
- * Returns a {@link RequestPath} object that encapsulates access to
everything related to the URL path.
+ * Returns a {@link RequestPathParams} object that encapsulates access
to URL path parameters.
*
* <h5 class='section'>Example:</h5>
* <p class='bcode w800'>
@@ -1213,57 +1212,47 @@ public final class RestRequest {
* <jk>public void</jk> doGet(RestRequest <jv>req</jv>) {
*
* <jc>// Get access to path data.</jc>
- * RequestPathMatch <jv>pathMatch</jv> =
<jv>req</jv>.getPathMatch();
+ * RequestPathParams <jv>pathParams</jv> =
<jv>req</jv>.getPathParams();
*
* <jc>// Example URL: /123/qux/true/quux</jc>
*
- * <jk>int</jk> <jv>foo</jv> =
<jv>pathMatch</jv>.getInt(<js>"foo"</js>); <jc>// =123</jc>
- * String <jv>bar</jv> =
<jv>pathMatch</jv>.getString(<js>"bar"</js>); <jc>// =qux</jc>
- * <jk>boolean</jk> <jv>baz</jv> =
<jv>pathMatch</jv>.getBoolean(<js>"baz"</js>); <jc>// =true</jc>
- * String <jv>remainder</jv> =
<jv>pathMatch</jv>.getRemainder(); <jc>// =quux</jc>
+ * <jk>int</jk> <jv>foo</jv> =
<jv>pathParams</jv>.get(<js>"foo"</js>).asInteger().orElse(-1); <jc>//
=123</jc>
+ * String <jv>bar</jv> =
<jv>pathParams</jv>.get(<js>"bar"</js>).orElse(null); <jc>// =qux</jc>
+ * <jk>boolean</jk> <jv>baz</jv> =
<jv>pathParams</jv>.get(<js>"baz"</js>).asBoolean().orElse(<jk>false</jk>);
<jc>// =true</jc>
+ * String <jv>remainder</jv> =
<jv>pathParams</jv>.getRemainder().orElse(<jk>null</jk>); <jc>// =quux</jc>
* }
* </p>
*
* <ul class='notes'>
* <li>
* This object is modifiable.
- * <li>
- * Values are converted from strings using the registered
{@link RestContext#REST_partParser part-parser} on the resource class.
- * <li>
- * The {@link RequestPath} object can also be passed as a
parameter on the method.
- * <li>
- * The {@link Path @Path} annotation can be used to access
individual values.
- * </ul>
- *
- * <ul class='seealso'>
- * <li class='link'>{@doc RestmRequestPathMatch}
* </ul>
*
* @return
- * The path data from the URL.
+ * The path parameters.
* <br>Never <jk>null</jk>.
*/
- public RequestPath getPathMatch() {
+ public RequestPathParams getPathParams() {
return pathParams;
}
/**
- * Shortcut for calling <c>getPathMatch().get(name)</c>.
+ * Shortcut for calling <c>getPathParams().get(<jv>name</jv>)</c>.
*
- * @param name The path variable name.
- * @return The path variable value, or <jk>null</jk> if not found.
+ * @param name The path parameter name.
+ * @return The path parameter, never <jk>null</jk>.
*/
- public String getPath(String name) {
- return getPathMatch().get(name);
+ public RequestPathParam getPathParam(String name) {
+ return pathParams.get(name);
}
/**
- * Shortcut for calling <c>getPathMatch().getRemainder()</c>.
+ * Shortcut for calling <c>getPathParams().getRemainder()</c>.
*
- * @return The path remainder value, or <jk>null</jk> if not found.
+ * @return The path remainder value, never <jk>null</jk>.
*/
- public String getPathRemainder() {
- return getPathMatch().getRemainder();
+ public RequestPathParam getPathRemainder() {
+ return pathParams.getRemainder();
}
//-----------------------------------------------------------------------------------------------------------------
@@ -1959,13 +1948,13 @@ public final class RestRequest {
if (pt ==
HttpPartType.BODY)
return
getBody().schema(schema).asType(type);
if (pt == QUERY)
- return
getQueryParams().get(name).parser(pp).schema(schema).asType(type);
+ return
getQueryParam(name).parser(pp).schema(schema).asType(type).orElse(null);
if (pt == FORMDATA)
- return
getFormParams().get(name).parser(pp).schema(schema).asType(type);
+ return
getFormParam(name).parser(pp).schema(schema).asType(type).orElse(null);
if (pt == HEADER)
- return
getHeader(name).parser(pp).schema(schema).asType(type);
+ return
getHeader(name).parser(pp).schema(schema).asType(type).orElse(null);
if (pt == PATH)
- return
getPathMatch().get(pp, schema, name, type);
+ return
getPathParam(name).parser(pp).schema(schema).asType(type).orElse(null);
}
return null;
}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/HookEvent.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/HookEvent.java
index f0a908d..5c3f4d3 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/HookEvent.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/HookEvent.java
@@ -154,12 +154,12 @@ public enum HookEvent {
* <li>{@link RequestHeaders}
* <li>{@link RequestQueryParams}
* <li>{@link RequestFormParams}
+ * <li>{@link RequestPathParams}
* <li>{@link Logger}
* <li>{@link RestContext}
* <li>{@link org.apache.juneau.parser.Parser}
* <li>{@link Locale}
* <li>{@link Swagger}
- * <li>{@link RequestPath}
* <li>{@link RequestBody}
* <li>{@link Config}
* <li>{@link UriContext}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Rest.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Rest.java
index f7bcdf0..257a80b 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Rest.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Rest.java
@@ -831,7 +831,7 @@ public @interface Rest {
* <h5 class='topic'>Path variables</h5>
* <p>
* The path can contain variables that get resolved to {@link
org.apache.juneau.http.annotation.Path @Path} parameters
- * or access through the {@link RestRequest#getPathMatch()} method.
+ * or access through the {@link RestRequest#getPathParams()} method.
*
* <h5 class='figure'>Example:</h5>
* <p class='bcode'>
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/args/PathArg.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/args/PathArg.java
index e24e85d..0f8f752 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/args/PathArg.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/args/PathArg.java
@@ -18,6 +18,7 @@ import static org.apache.juneau.internal.StringUtils.*;
import java.lang.reflect.*;
import org.apache.juneau.*;
+import org.apache.juneau.collections.*;
import org.apache.juneau.http.annotation.*;
import org.apache.juneau.httppart.*;
import org.apache.juneau.internal.*;
@@ -28,10 +29,6 @@ import org.apache.juneau.rest.util.*;
/**
* Resolves method parameters and parameter types annotated with {@link Path}
on {@link RestOp}-annotated Java methods.
- *
- * <p>
- * The parameter value is resolved using <c><jv>call</jv>.{@link
RestCall#getRestRequest() getRestRequest}().{@link RestRequest#getPathMatch()
getPathMatch}().{@link
RequestPath#get(HttpPartParserSession,HttpPartSchema,String,Type,Type...)
get}(<jv>parserSession<jv>, <jv>schema</jv>, <jv>name</jv>, <jv>type</jv>)</c>
- * with a {@link HttpPartSchema schema} derived from the {@link Path}
annotation.
*/
public class PathArg implements RestOperationArg {
private final HttpPartParser partParser;
@@ -102,7 +99,12 @@ public class PathArg implements RestOperationArg {
@Override /* RestOperationArg */
public Object resolve(RestCall call) throws Exception {
RestRequest req = call.getRestRequest();
+ if (name.equals("*")) {
+ OMap m = new OMap();
+
call.getRestRequest().getPathParams().getAll().stream().forEach(x ->
m.put(x.getName(), x.getValue()));
+ return req.getBeanSession().convertToType(m, type);
+ }
HttpPartParserSession ps = partParser == null ?
req.getPartParserSession() :
partParser.createPartSession(req.getParserSessionArgs());
- return call.getRestRequest().getPathMatch().get(ps, schema,
name, type);
+ return
call.getRestRequest().getPathParams().get(name).parser(ps).schema(schema).asType(type).orElse(null);
}
}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/args/RequestPathArg.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/args/RequestPathArg.java
index 014cfb0..f26f79b 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/args/RequestPathArg.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/args/RequestPathArg.java
@@ -17,10 +17,10 @@ import org.apache.juneau.rest.*;
import org.apache.juneau.rest.annotation.*;
/**
- * Resolves method of type with {@link RequestPath} on {@link
RestOp}-annotated Java methods.
+ * Resolves method of type with {@link RequestPathParams} on {@link
RestOp}-annotated Java methods.
*
* <p>
- * The parameter value is resolved using <c><jv>call</jv>.{@link
RestCall#getRestRequest() getRestRequest}().{@link RestRequest#getPathMatch()
getPathMatch}()</c>.
+ * The parameter value is resolved using <c><jv>call</jv>.{@link
RestCall#getRestRequest() getRestRequest}().{@link RestRequest#getPathParams()
getPathParams}()</c>.
*/
public class RequestPathArg extends SimpleRestOperationArg {
@@ -28,10 +28,10 @@ public class RequestPathArg extends SimpleRestOperationArg {
* Static creator.
*
* @param paramInfo The Java method parameter being resolved.
- * @return A new {@link RequestPathArg}, or <jk>null</jk> if the
parameter type is not {@link RequestPath}.
+ * @return A new {@link RequestPathArg}, or <jk>null</jk> if the
parameter type is not {@link RequestPathParams}.
*/
public static RequestPathArg create(ParamInfo paramInfo) {
- if (paramInfo.isType(RequestPath.class))
+ if (paramInfo.isType(RequestPathParams.class))
return new RequestPathArg();
return null;
}
@@ -40,6 +40,6 @@ public class RequestPathArg extends SimpleRestOperationArg {
* Constructor.
*/
protected RequestPathArg() {
- super((c)->c.getRestRequest().getPathMatch());
+ super((c)->c.getRestRequest().getPathParams());
}
}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/converters/Traversable.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/converters/Traversable.java
index 0f587b5..0d6e8f5 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/converters/Traversable.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/converters/Traversable.java
@@ -49,7 +49,7 @@ public final class Traversable implements RestConverter {
if (o == null)
return null;
- String pathRemainder = req.getPathMatch().getRemainder();
+ String pathRemainder = req.getPathRemainder().orElse(null);
if (pathRemainder != null) {
try {
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/RequestPathVar.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/RequestPathVar.java
index 2a35fc9..ff2b4f2 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/RequestPathVar.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/RequestPathVar.java
@@ -23,7 +23,7 @@ import org.apache.juneau.svl.*;
* The format for this var is <js>"$RP{key1[,key2...]}"</js>.
*
* <p>
- * Used to resolve values returned by {@link RestRequest#getPath(String)}.
+ * Used to resolve values returned by {@link RestRequest#getPathParam(String)}.
* <br>When multiple keys are used, returns the first non-null/empty value.
*
* <h5 class='section'>Example:</h5>
@@ -70,8 +70,8 @@ public class RequestPathVar extends MultipartResolvingVar {
public String resolve(VarResolverSession session, String key) {
RestRequest req =
session.getBean(RestRequest.class).orElseThrow(InternalServerError::new);
if ("REMAINDER".equals(key))
- return req.getPathRemainder();
- return req.getPath(key);
+ return req.getPathParams().getRemainder().orElse(null);
+ return req.getPathParam(key).orElse(null);
}
@Override /* Var */
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/http/remote/Remote_PathAnnotation_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/http/remote/Remote_PathAnnotation_Test.java
index d0aa05f..278dce6 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/http/remote/Remote_PathAnnotation_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/http/remote/Remote_PathAnnotation_Test.java
@@ -410,12 +410,12 @@ public class Remote_PathAnnotation_Test {
assertEquals("{x:'1|2'}",x.getX1("1","2"));
assertThrown(()->x.getX1()).contains("Minimum number of items
not met");
assertThrown(()->x.getX1("1","2","3")).contains("Maximum number
of items exceeded");
- assertEquals("{x:null}",x.getX1((String)null));
+ assertEquals("{x:'null'}",x.getX1((String)null));
assertEquals("{x:'1'}",x.getX2(new String[]{"1"}));
assertEquals("{x:'1|2'}",x.getX2(new String[]{"1","2"}));
assertThrown(()->x.getX2(new String[]{})).contains("Minimum
number of items not met");
assertThrown(()->x.getX2(new
String[]{"1","2","3"})).contains("Maximum number of items exceeded");
- assertEquals("{x:null}",x.getX2(new String[]{null}));
+ assertEquals("{x:'null'}",x.getX2(new String[]{null}));
assertEquals("{x:'1|1'}",x.getX3("1","1"));
assertEquals("{x:'1|1'}",x.getX4(new String[]{"1","1"}));
assertEquals("{x:'1|2'}",x.getX5("1","2"));
@@ -463,12 +463,12 @@ public class Remote_PathAnnotation_Test {
assertThrown(()->x.getX3("bar")).contains("Value does not match
one of the expected values. Must be one of the following: ['foo']");
assertEquals("{x:'foo'}",x.getX4("foo"));
assertThrown(()->x.getX4("bar")).contains("Value does not match
one of the expected values. Must be one of the following: ['foo']");
- assertEquals("{x:null}",x.getX4((String)null));
+ assertEquals("{x:'null'}",x.getX4((String)null));
assertEquals("{x:'foo123'}",x.getX5("foo123"));
assertThrown(()->x.getX5("bar")).contains("Value does not match
expected pattern");
assertEquals("{x:'foo123'}",x.getX6("foo123"));
assertThrown(()->x.getX6("foo")).contains("Value does not match
expected pattern");
- assertEquals("{x:null}",x.getX6((String)null));
+ assertEquals("{x:'null'}",x.getX6((String)null));
}
//-----------------------------------------------------------------------------------------------------------------
@@ -586,7 +586,7 @@ public class Remote_PathAnnotation_Test {
public static class K {
@RestOp(path="/*")
public String get(RestRequest req) throws Exception {
- return req.getPathMatch().getRemainder();
+ return req.getPathParams().getRemainder().orElse(null);
}
}
diff --git a/juneau-utest/src/test/java/org/apache/juneau/rest/Paths_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/rest/Paths_Test.java
index 049b3d8..cf55322 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/rest/Paths_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/rest/Paths_Test.java
@@ -30,8 +30,8 @@ public class Paths_Test {
static OMap getPaths(RestRequest req) {
return OMap.create()
.a("pathInfo", req.getPathInfo())
- .a("pathRemainder", req.getPathMatch().getRemainder())
- .a("pathRemainderUndecoded",
req.getPathMatch().getRemainderUndecoded())
+ .a("pathRemainder",
req.getPathParams().getRemainder().orElse(null))
+ .a("pathRemainderUndecoded",
req.getPathParams().getRemainderUndecoded().orElse(null))
.a("requestURI", req.getRequestURI())
.a("requestParentURI",
req.getUriContext().getRootRelativePathInfoParent())
.a("requestURL", req.getRequestURL())
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/rest/RestOp_Params_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/rest/RestOp_Params_Test.java
index 5ea032c..8fcc5bb 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/rest/RestOp_Params_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/rest/RestOp_Params_Test.java
@@ -109,7 +109,7 @@ public class RestOp_Params_Test {
return t != null;
}
@RestGet
- public boolean r(RequestPath t) {
+ public boolean r(RequestPathParams t) {
return t != null;
}
@RestGet
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/Path_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/Path_Test.java
index 13900cb..5e753f7 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/Path_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/Path_Test.java
@@ -20,8 +20,8 @@ import java.util.*;
import org.apache.juneau.collections.*;
import org.apache.juneau.http.annotation.*;
+import org.apache.juneau.internal.*;
import org.apache.juneau.json.*;
-import org.apache.juneau.marshall.*;
import org.apache.juneau.rest.*;
import org.apache.juneau.rest.RestResponse;
import org.apache.juneau.rest.client.*;
@@ -383,31 +383,31 @@ public class Path_Test {
@Rest(path="/f/{a}/{b}")
public static class F {
@RestGet(path="/")
- public String a(RequestPath path) {
- return format("a: {0}", path);
+ public String a(RequestPathParams path) {
+ return format("a: {0}", path.toString(true));
}
@RestGet(path="/*")
- public String b(RequestPath path) {
- return format("b: {0}", path);
+ public String b(RequestPathParams path) {
+ return format("b: {0}", path.toString(true));
}
@RestGet(path="/fc")
- public String c(RequestPath path) {
- return format("c: {0}", path);
+ public String c(RequestPathParams path) {
+ return format("c: {0}", path.toString(true));
}
@RestGet(path="/fd/{c}/{d}")
- public String d(RequestPath path) {
- return format("d: {0}", path);
+ public String d(RequestPathParams path) {
+ return format("d: {0}", path.toString(true));
}
@RestGet(path="/fe/{a}/{b}")
- public String e(RequestPath path) {
- return format("e: {0}", path);
+ public String e(RequestPathParams path) {
+ return format("e: {0}", path.toString(true));
}
@RestGet(path="/ff/{c}/{d}/*")
- public String f(RequestPath path) {
- return format("f: {0}", path);
+ public String f(RequestPathParams path) {
+ return format("f: {0}", path.toString(true));
}
private String format(String msg, Object...args) {
- return SimpleJson.DEFAULT.format(msg, args);
+ return StringUtils.format(msg, args);
}
}