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
commit f017e678a31da6f2a7aa20e52640612d1e7ac788 Author: JamesBognar <[email protected]> AuthorDate: Fri Sep 25 09:49:17 2020 -0400 REST API refactoring. --- .../annotation/RestMethod_ClientVersion_Test.java | 6 +- .../main/java/org/apache/juneau/rest/RestCall.java | 22 +++ .../java/org/apache/juneau/rest/RestContext.java | 151 ++++++++++----------- .../org/apache/juneau/rest/RestMethodContext.java | 94 ++++++++++--- 4 files changed, 169 insertions(+), 104 deletions(-) diff --git a/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/annotation/RestMethod_ClientVersion_Test.java b/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/annotation/RestMethod_ClientVersion_Test.java index 6d2b052..fac19aa 100644 --- a/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/annotation/RestMethod_ClientVersion_Test.java +++ b/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/annotation/RestMethod_ClientVersion_Test.java @@ -55,13 +55,13 @@ public class RestMethod_ClientVersion_Test { RestClient a = MockRestClient.build(A.class); a.get("/").run().assertBody().is("no-version"); for (String s : "1, 1.0, 1.0.0, 1.0.1".split("\\s*,\\s*")) { - a.get("/").clientVersion(s).run().assertBody().is("[1.0,1.0]"); + a.get("/").clientVersion(s).run().assertBody().msg("s=[{0}]",s).is("[1.0,1.0]"); } for (String s : "1.1, 1.1.1, 1.2, 1.9.9".split("\\s*,\\s*")) { - a.get("/").clientVersion(s).run().assertBody().is("[1.1,2)"); + a.get("/").clientVersion(s).run().assertBody().msg("s=[{0}]").is("[1.1,2)"); } for (String s : "2, 2.0, 2.1, 9, 9.9".split("\\s*,\\s*")) { - a.get("/").clientVersion(s).run().assertBody().is("2"); + a.get("/").clientVersion(s).run().assertBody().msg("s=[{0}]").is("2"); } } 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 fc79cad..aa034d6 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 @@ -43,6 +43,8 @@ public class RestCall { private RestCallLogger logger; private RestCallLoggerConfig loggerConfig; + private UrlPathPatternMatch urlPathPatternMatch; + /** * Constructor. * @@ -303,6 +305,26 @@ public class RestCall { return this; } + /** + * Sets the URL path pattern match on this call. + * + * @param urlPathPatternMatch The match pattern. + * @return This object (for method chaining). + */ + public RestCall urlPathPatternMatch(UrlPathPatternMatch urlPathPatternMatch) { + this.urlPathPatternMatch = urlPathPatternMatch; + return this; + } + + /** + * Returns the URL path pattern match on this call. + * + * @return The URL path pattern match on this call. + */ + public UrlPathPatternMatch getUrlPathPatternMatch() { + return urlPathPatternMatch; + } + //------------------------------------------------------------------------------------------------------------------ // Lifecycle methods. //------------------------------------------------------------------------------------------------------------------ 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 762264f..582c481 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 @@ -3911,17 +3911,15 @@ public class RestContext extends BeanContext { sm = new RestMethodContext(smb) { @Override - int invoke(RestCall call) throws Throwable { + void invoke(RestCall call) throws Throwable { - int rc = super.invoke(call); - if (rc != SC_OK) - return rc; + super.invoke(call); final Object o = call.getOutput(); if ("GET".equals(call.getMethod())) { call.output(rim.getMethodsByPath().keySet()); - return SC_OK; + return; } else if ("POST".equals(call.getMethod())) { String pip = call.getUrlPathInfo().getPath(); @@ -3945,13 +3943,13 @@ public class RestContext extends BeanContext { } Object output = m.invoke(o, args); call.output(output); - return SC_OK; + return; } catch (Exception e) { throw toHttpException(e, InternalServerError.class); } } } - return SC_NOT_FOUND; + throw new NotFound(); } }; @@ -4109,32 +4107,6 @@ public class RestContext extends BeanContext { } } - static class MethodMapBuilder { - TreeMap<String,TreeSet<RestMethodContext>> map = new TreeMap<>(); - Set<RestMethodContext> list = ASet.of(); - - - MethodMapBuilder add(String httpMethodName, RestMethodContext mc) { - httpMethodName = httpMethodName.toUpperCase(); - if (! map.containsKey(httpMethodName)) - map.put(httpMethodName, new TreeSet<>()); - map.get(httpMethodName).add(mc); - list.add(mc); - return this; - } - - Map<String,List<RestMethodContext>> getMap() { - Map<String,List<RestMethodContext>> m = new LinkedHashMap<>(); - for (Map.Entry<String,TreeSet<RestMethodContext>> e : map.entrySet()) - m.put(e.getKey(), AList.of(e.getValue())); - return Collections.unmodifiableMap(m); - } - - List<RestMethodContext> getList() { - return AList.of(list); - } - } - /** * Returns the resource resolver associated with this context. * @@ -5223,18 +5195,13 @@ public class RestContext extends BeanContext { } else { // If the specified method has been defined in a subclass, invoke it. - int rc = findMethodAndInvoke(call); - - // Should be 404 if URL pattern didn't match. - if (rc == 0) - rc = SC_NOT_FOUND; - - // If not invoked above, see if it's an OPTIONs request - if (rc != SC_OK) - handleNotFound(call.status(rc)); - - if (call.getStatus() == 0) - call.status(rc); + try { + findMethod(call).invoke(call); + } catch (NotFound e) { + if (call.getStatus() == 0) + call.status(404); + handleNotFound(call); + } } if (call.hasOutput()) { @@ -5254,50 +5221,44 @@ public class RestContext extends BeanContext { finishCall(call); } - private int findMethodAndInvoke(RestCall call) throws Throwable { + private RestMethodContext findMethod(RestCall call) throws Throwable { String m = call.getMethod(); int rc = 0; - if (methodMap.containsKey(m)) - rc = invoke(methodMap.get(m), call); - - if ((rc == 0 || rc == 404) && methodMap.containsKey("*")) - rc = invoke(methodMap.get("*"), call); - - // Should be 405 if the URL pattern matched but HTTP method did not. - if (rc == 0) - for (List<RestMethodContext> rcc : methodMap.values()) - if (matches(rcc, call)) - rc = SC_METHOD_NOT_ALLOWED; - - return rc; - } - - private boolean matches(List<RestMethodContext> mc, RestCall call) throws Throwable { - UrlPathInfo pi = call.getUrlPathInfo(); - for (RestMethodContext m : mc) - if (m.matches(pi)) - return true; - return false; - } + if (methodMap.containsKey(m)) { + for (RestMethodContext mc : methodMap.get(m)) { + int mrc = mc.match(call); + if (mrc == 2) + return mc; + rc = Math.max(rc, mrc); + } + } - private int invoke(List<RestMethodContext> mc, RestCall call) throws Throwable { - if (mc.size() == 1) { - call.restMethodContext(mc.get(0)); - return mc.get(0).invoke(call); + if (methodMap.containsKey("*")) { + for (RestMethodContext mc : methodMap.get("*")) { + int mrc = mc.match(call); + if (mrc == 2) + return mc; + rc = Math.max(rc, mrc); + } } - int maxRc = 0; - for (RestMethodContext m : mc) { - int rc = m.invoke(call); - if (rc == SC_OK) { - call.restMethodContext(m); - return SC_OK; + // If no paths matched, see if the path matches any other methods. + // Note that we don't want to match against "/*" patterns such as getOptions(). + if (rc == 0) { + for (RestMethodContext mc : methods) { + if (! mc.getPathPattern().endsWith("/*")) { + int mrc = mc.match(call); + if (mrc == 2) + throw new MethodNotAllowed(); + } } - maxRc = Math.max(maxRc, rc); } - return maxRc; + if (rc == 1) + throw new PreconditionFailed("Method ''{0}'' not found on resource on path ''{1}'' with matching matcher.", m, call.getPathInfo()); + + throw new NotFound(); } private boolean isDebug(RestCall call) { @@ -5743,4 +5704,34 @@ public class RestContext extends BeanContext { .a("useClasspathResourceCaching", useClasspathResourceCaching) ); } + + //----------------------------------------------------------------------------------------------------------------- + // Helpers. + //----------------------------------------------------------------------------------------------------------------- + + static class MethodMapBuilder { + TreeMap<String,TreeSet<RestMethodContext>> map = new TreeMap<>(); + Set<RestMethodContext> set = ASet.of(); + + + MethodMapBuilder add(String httpMethodName, RestMethodContext mc) { + httpMethodName = httpMethodName.toUpperCase(); + if (! map.containsKey(httpMethodName)) + map.put(httpMethodName, new TreeSet<>()); + map.get(httpMethodName).add(mc); + set.add(mc); + return this; + } + + Map<String,List<RestMethodContext>> getMap() { + AMap<String,List<RestMethodContext>> m = AMap.of(); + for (Map.Entry<String,TreeSet<RestMethodContext>> e : map.entrySet()) + m.put(e.getKey(), AList.of(e.getValue())); + return m.unmodifiable(); + } + + List<RestMethodContext> getList() { + return AList.of(set).unmodifiable(); + } + } } 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 25b20cc..f980e58 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 @@ -13,7 +13,6 @@ package org.apache.juneau.rest; import static org.apache.juneau.rest.Enablement.*; -import static javax.servlet.http.HttpServletResponse.*; import static org.apache.juneau.internal.ClassUtils.*; import static org.apache.juneau.internal.ObjectUtils.*; import static org.apache.juneau.internal.StringUtils.*; @@ -929,21 +928,87 @@ public class RestMethodContext extends BeanContext implements Comparable<RestMet } /** - * Workhorse method. + * Identifies if this method can process the specified call. * - * @param pathInfo The value of {@link HttpServletRequest#getPathInfo()} (sorta) - * @return The HTTP response code. + * <p> + * To process the call, the following must be true: + * <ul> + * <li>Path pattern must match. + * <li>Matchers (if any) must match. + * </ul> + * + * @param call The call to check. + * @return + * One of the following values: + * <ul> + * <li><c>0</c> - Path doesn't match. + * <li><c>1</c> - Path matched but matchers did not. + * <li><c>2</c> - Matches. + * </ul> */ - int invoke(RestCall call) throws Throwable { + protected int match(RestCall call) { - UrlPathPatternMatch pm = null; + UrlPathPatternMatch pm = matchPattern(call); + + if (pm == null) + return 0; + + if (requiredMatchers.length == 0 && optionalMatchers.length == 0) { + call.urlPathPatternMatch(pm); // Cache so we don't have to recalculate. + return 2; + } + + try { + RestRequest req = call.getRestRequest(); + RestResponse res = call.getRestResponse(); + + @SuppressWarnings("deprecation") + RequestProperties requestProperties = new RequestProperties(req.getVarResolverSession(), properties); + + req.init(this, requestProperties); + res.init(this, requestProperties); + + // If the method implements matchers, test them. + for (RestMatcher m : requiredMatchers) + if (! m.matches(req)) + return 1; + if (optionalMatchers.length > 0) { + boolean matches = false; + for (RestMatcher m : optionalMatchers) + matches |= m.matches(req); + if (! matches) + return 1; + } + call.urlPathPatternMatch(pm); // Cache so we don't have to recalculate. + return 2; + } catch (Exception e) { + throw new InternalServerError(e); + } + } + + private UrlPathPatternMatch matchPattern(RestCall call) { + UrlPathPatternMatch pm = null; for (UrlPathPattern pp : pathPatterns) if (pm == null) pm = pp.match(call.getUrlPathInfo()); + return pm; + } + + /** + * Workhorse method. + * + * @param pathInfo The value of {@link HttpServletRequest#getPathInfo()} (sorta) + */ + void invoke(RestCall call) throws Throwable { + + UrlPathPatternMatch pm = call.getUrlPathPatternMatch(); if (pm == null) - return SC_NOT_FOUND; + pm = matchPattern(call); + + if (pm == null) + throw new NotFound(); RestRequest req = call.getRestRequest(); RestResponse res = call.getRestResponse(); @@ -960,18 +1025,6 @@ public class RestMethodContext extends BeanContext implements Comparable<RestMet req.init(this, requestProperties); res.init(this, requestProperties); - // If the method implements matchers, test them. - for (RestMatcher m : requiredMatchers) - if (! m.matches(req)) - return SC_PRECONDITION_FAILED; - if (optionalMatchers.length > 0) { - boolean matches = false; - for (RestMatcher m : optionalMatchers) - matches |= m.matches(req); - if (! matches) - return SC_PRECONDITION_FAILED; - } - context.preCall(call); call.loggerConfig(callLoggerConfig); @@ -1006,7 +1059,7 @@ public class RestMethodContext extends BeanContext implements Comparable<RestMet for (RestGuard guard : guards) if (! guard.guard(req, res)) - return SC_OK; + return; Object output; try { @@ -1055,7 +1108,6 @@ public class RestMethodContext extends BeanContext implements Comparable<RestMet } catch (InvocationTargetException e) { throw e.getTargetException(); } - return SC_OK; } // // protected void addStatusCode(int code) {
