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/03/19 18:17:48 UTC
[juneau] branch master updated: JUNEAU-204
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 1113cde JUNEAU-204
1113cde is described below
commit 1113cded0c0b8c169ca83b606e47b7d2ffb9aa13
Author: JamesBognar <ja...@apache.org>
AuthorDate: Thu Mar 19 14:17:31 2020 -0400
JUNEAU-204
Add @RestMethod(paths) to allow matching multiple paths.
---
juneau-doc/docs/ReleaseNotes/8.1.4.html | 30 ++++++++++
.../rest/annotation2/PathAnnotationTest.java | 33 ++++++++++
.../org/apache/juneau/rest/RestMethodContext.java | 70 +++++++++++++++++++---
.../apache/juneau/rest/annotation/RestMethod.java | 24 ++++++++
.../rest/annotation/RestMethodConfigApply.java | 4 +-
5 files changed, 151 insertions(+), 10 deletions(-)
diff --git a/juneau-doc/docs/ReleaseNotes/8.1.4.html b/juneau-doc/docs/ReleaseNotes/8.1.4.html
index a80fb9b..e8875f5 100644
--- a/juneau-doc/docs/ReleaseNotes/8.1.4.html
+++ b/juneau-doc/docs/ReleaseNotes/8.1.4.html
@@ -311,6 +311,36 @@
</ul>
<li>
New {@link oaj.http.annotation.Path#required() @Path(required)} annotation support.
+ <br>A path can be marked as not-required when the path variable is resolved by a parent resource like so:
+ <p class='bpcode w800'>
+ <ja>@Rest</ja>(path=<js>"/parent/{p1}"</js>,children=Child.<jk>class</jk>)
+ <jk>public class</jk> Parent {
+ ...
+ }
+
+ <ja>@Rest</ja>(path="/child")
+ <jk>public class</jk> Child {
+
+ <ja>@RestMethod</ja>(path="/")
+ <jk>public</jk> String doGet(<ja>@Path</ja>(name=<js>"p1"</js>,required=<jk>false</jk>) String p1) {
+ <jc>// p1 will be null when accessed via "/child"</jc>
+ <jc>// p1 will be non-null when accessed via "/parent/p1/child".</jc>
+ }
+ ...
+ }
+ </p>
+ <br>This allows the child resource to be mapped to multiple parents that may resolve various different path variables.
+ <li>
+ New {@link oajr.annotation.RestMethod#paths() @RestMethod(paths)} annotation that allows you to map multiple
+ paths to the same Java method.
+ <br>Example:
+ <p class='bpcode w800'>
+ <ja>@RestMethod</ja>(
+ name=<jsf>GET</jsf>,
+ paths={<js>"/"</js>,<js>"/{foo}"</js>}
+ )
+ <jk>public</jk> String doGet(<ja>@Path</ja>(name=<js>"foo"</js>,required=<jk>false</jk>) String foo) {...}
+ </p>
</ul>
<h5 class='topic w800'>juneau-rest-server-springboot</h5>
diff --git a/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/annotation2/PathAnnotationTest.java b/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/annotation2/PathAnnotationTest.java
index 81ab56d..efbc375 100644
--- a/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/annotation2/PathAnnotationTest.java
+++ b/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/annotation2/PathAnnotationTest.java
@@ -1135,4 +1135,37 @@ public class PathAnnotationTest {
u2.get("/foo/xxx").execute().assertStatus(200).assertBody("nil,xxx");
}
+ //=================================================================================================================
+ // Multiple paths
+ //=================================================================================================================
+
+ @Rest(path="/v1/{v1}",children=V2.class)
+ public static class V1 {
+ }
+ @Rest(path="/v2")
+ public static class V2 {
+ @RestMethod(paths={"/","/{foo}"})
+ public String doGet(@Path(name="v1",required=false) String v1, @Path(name="foo",required=false) String foo) {
+ return "1," + (v1 == null ? "nil" : v1) + "," + (foo == null ? "nil" : foo);
+ }
+ @RestMethod(paths={"/foo","/foo/{foo}"})
+ public String doGet2(@Path(name="v1",required=false) String v1, @Path(name="foo",required=false) String foo) {
+ return "2," + (v1 == null ? "nil" : v1) + "," + (foo == null ? "nil" : foo);
+ }
+ }
+
+ static MockRest v1 = MockRest.build(V1.class, null);
+ static MockRest v2 = MockRest.build(V2.class, null);
+
+ @Test
+ public void v01_multiplePaths() throws Exception {
+ v1.get("/v1/v1foo/v2").execute().assertStatus(200).assertBody("1,v1foo,nil");
+ v1.get("/v1/v1foo/v2/v2foo").execute().assertStatus(200).assertBody("1,v1foo,v2foo");
+ v1.get("/v1/v1foo/v2/foo").execute().assertStatus(200).assertBody("2,v1foo,nil");
+ v1.get("/v1/v1foo/v2/foo/v2foo").execute().assertStatus(200).assertBody("2,v1foo,v2foo");
+ v2.get("/v2").execute().assertStatus(200).assertBody("1,nil,nil");
+ v2.get("/v2/v2foo").execute().assertStatus(200).assertBody("1,nil,v2foo");
+ v2.get("/v2/foo").execute().assertStatus(200).assertBody("2,nil,nil");
+ v2.get("/v2/foo/v2foo").execute().assertStatus(200).assertBody("2,nil,v2foo");
+ }
}
\ No newline at end of file
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodContext.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodContext.java
index 669d6d3..879e0b4 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodContext.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodContext.java
@@ -431,10 +431,46 @@ public class RestMethodContext extends BeanContext implements Comparable<RestMet
* Slashes are trimmed from the path ends.
* <br>As a convention, you may want to start your path with <js>'/'</js> simple because it make it easier to read.
* </ul>
+ * @deprecated Use {@link #RESTMETHOD_paths}
*/
+ @Deprecated
public static final String RESTMETHOD_path = PREFIX + ".path.s";
/**
+ * Configuration property: Resource method paths.
+ *
+ * <h5 class='section'>Property:</h5>
+ * <ul class='spaced-list'>
+ * <li><b>ID:</b> {@link org.apache.juneau.rest.RestMethodContext#RESTMETHOD_paths RESTMETHOD_paths}
+ * <li><b>Name:</b> <js>"RestMethodContext.path.ls"</js>
+ * <li><b>Data type:</b> <c>String[]</c>
+ * <li><b>System property:</b> <c>RestMethodContext.paths</c>
+ * <li><b>Environment variable:</b> <c>RESTMETHODCONTEXT_PATHS</c>
+ * <li><b>Default:</b> <jk>null</jk>
+ * <li><b>Session property:</b> <jk>false</jk>
+ * <li><b>Annotations:</b>
+ * <ul>
+ * <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#path()}
+ * <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#paths()}
+ * </ul>
+ * </ul>
+ *
+ * <h5 class='section'>Description:</h5>
+ * <p>
+ * Identifies the URL subpath relative to the servlet class.
+ *
+ * <p>
+ * <ul class='notes'>
+ * <li>
+ * This method is only applicable for Java methods.
+ * <li>
+ * Slashes are trimmed from the path ends.
+ * <br>As a convention, you may want to start your path with <js>'/'</js> simple because it make it easier to read.
+ * </ul>
+ */
+ public static final String RESTMETHOD_paths = PREFIX + ".paths.ls";
+
+ /**
* Configuration property: Priority.
*
* <h5 class='section'>Property:</h5>
@@ -556,7 +592,7 @@ public class RestMethodContext extends BeanContext implements Comparable<RestMet
//-------------------------------------------------------------------------------------------------------------------
private final String httpMethod;
- private final UrlPathPattern pathPattern;
+ private final UrlPathPattern[] pathPatterns;
final RestMethodParam[] methodParams;
private final RestGuard[] guards;
private final RestMatcher[] optionalMatchers;
@@ -656,9 +692,15 @@ public class RestMethodContext extends BeanContext implements Comparable<RestMet
this.responseMeta = ResponseBeanMeta.create(mi, ps);
- this.pathPattern = new UrlPathPattern(getProperty(RESTMETHOD_path, String.class, HttpUtils.detectHttpPath(method, true)));
+ List<UrlPathPattern> pathPatterns = new ArrayList<>();
+ for (String p : getArrayProperty(RESTMETHOD_paths, String.class))
+ pathPatterns.add(new UrlPathPattern(p));
+ if (pathPatterns.isEmpty())
+ pathPatterns.add(new UrlPathPattern(HttpUtils.detectHttpPath(method, true)));
+
+ this.pathPatterns = pathPatterns.toArray(new UrlPathPattern[pathPatterns.size()]);
- this.methodParams = context.findParams(mi, false, pathPattern);
+ this.methodParams = context.findParams(mi, false, this.pathPatterns[this.pathPatterns.length-1]);
this.converters = getInstanceArrayProperty(REST_converters, RestConverter.class, new RestConverter[0], rr, r, this);
@@ -846,7 +888,7 @@ public class RestMethodContext extends BeanContext implements Comparable<RestMet
* Returns the path pattern for this method.
*/
String getPathPattern() {
- return pathPattern.toString();
+ return pathPatterns[0].toString();
}
/**
@@ -862,7 +904,10 @@ public class RestMethodContext extends BeanContext implements Comparable<RestMet
}
boolean matches(UrlPathInfo pathInfo) {
- return pathPattern.match(pathInfo) != null;
+ for (UrlPathPattern p : pathPatterns)
+ if (p.match(pathInfo) != null)
+ return true;
+ return false;
}
/**
@@ -873,7 +918,12 @@ public class RestMethodContext extends BeanContext implements Comparable<RestMet
*/
int invoke(RestCall call) throws Throwable {
- UrlPathPatternMatch pm = pathPattern.match(call.getUrlPathInfo());
+ UrlPathPatternMatch pm = null;
+
+ for (UrlPathPattern pp : pathPatterns)
+ if (pm == null)
+ pm = pp.match(call.getUrlPathInfo());
+
if (pm == null)
return SC_NOT_FOUND;
@@ -1021,9 +1071,11 @@ public class RestMethodContext extends BeanContext implements Comparable<RestMet
if (c != 0)
return c;
- c = pathPattern.compareTo(o.pathPattern);
- if (c != 0)
- return c;
+ for (int i = 0; i < Math.min(pathPatterns.length, o.pathPatterns.length); i++) {
+ c = pathPatterns[i].compareTo(o.pathPatterns[i]);
+ if (c != 0)
+ return c;
+ }
c = compare(o.hierarchyDepth, hierarchyDepth);
if (c != 0)
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java
index 5f9b0f8..8bd8727 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java
@@ -22,6 +22,7 @@ import org.apache.juneau.annotation.*;
import org.apache.juneau.rest.*;
import org.apache.juneau.remote.*;
import org.apache.juneau.html.annotation.*;
+import org.apache.juneau.http.annotation.*;
/**
* Identifies a REST Java method on a {@link RestServlet} implementation class.
@@ -586,6 +587,29 @@ public @interface RestMethod {
String path() default "";
/**
+ * Same as {@link #path()} but allows you to specify multiple path patterns for the same method.
+ *
+ * <p>
+ * The path can contain variables that get resolved to {@link org.apache.juneau.http.annotation.Path @Path} parameters.
+ * <br>When variables are not defined on all paths, the {@link Path#required @Path(required)} annotation can be set
+ * to <jk>false</jk> to make it optional.
+ *
+ * <h5 class='figure'>Example:</h5>
+ * <p class='bcode w800'>
+ * <ja>@RestMethod</ja>(
+ * name=<jsf>GET</jsf>,
+ * paths={<js>"/"</js>,<js>"/{foo}"</js>}
+ * )
+ * <jk>public</jk> String doGet(<ja>@Path</ja>(name=<js>"foo"</js>,required=<jk>false</jk>) String foo) {...}
+ * </p>
+ *
+ * <ul class='seealso'>
+ * <li class='ja'>{@link org.apache.juneau.http.annotation.Path}
+ * </ul>
+ */
+ String[] paths() default {};
+
+ /**
* Sets the POJO swaps for the serializers and parsers defined on this method.
*
* <div class='warn'>
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethodConfigApply.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethodConfigApply.java
index 9e5dad6..7e84695 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethodConfigApply.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethodConfigApply.java
@@ -159,7 +159,9 @@ public class RestMethodConfigApply extends ConfigApply<RestMethod> {
psb.set(REST_maxInput, string(a.maxInput()));
if (! a.path().isEmpty())
- psb.set(RESTMETHOD_path, string(a.path()));
+ psb.addTo(RESTMETHOD_paths, string(a.path()));
+ for (String p : a.paths())
+ psb.addTo(RESTMETHOD_paths, string(p));
if (! a.rolesDeclared().isEmpty())
psb.addTo(REST_rolesDeclared, strings(a.rolesDeclared()));