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 2a4acb91f0 Clean up juneau-rest-server
2a4acb91f0 is described below

commit 2a4acb91f08226766e3484ce6aae69f7c9f41eac
Author: James Bognar <[email protected]>
AuthorDate: Thu Jan 1 11:32:15 2026 -0500

    Clean up juneau-rest-server
---
 README.md                                          |  14 +
 .../rest/OverrideableHttpServletRequest.java       |  61 ----
 .../java/org/apache/juneau/rest/RestContext.java   |  34 +-
 .../java/org/apache/juneau/rest/RestOpContext.java |   6 +-
 .../java/org/apache/juneau/rest/RestSession.java   |   2 +-
 .../org/apache/juneau/rest/util/RestUtils.java     | 353 +++------------------
 .../apache/juneau/rest/util/RestUtils_Test.java    | 101 ------
 7 files changed, 96 insertions(+), 475 deletions(-)

diff --git a/README.md b/README.md
index 45bd4100fb..67e61575e2 100644
--- a/README.md
+++ b/README.md
@@ -313,6 +313,20 @@ Juneau is packed with features that may not be obvious at 
first. Users are encou
    * juneau-rest-client - Apache HttpClient 4.5+.
 * Built on top of Servlet and Apache HttpClient APIs that allow you to use the 
newest HTTP/2 features such as request/response multiplexing and server push.
 
+## Repository Structure
+
+This repository uses multiple branches to separate different concerns:
+
+* **`master`** - Contains the main source code for Apache Juneau
+* **`docs`** - Contains the Docusaurus-based documentation site
+* **`asf-staging`** - Contains the staging/preview version of the website
+* **`asf-site`** - Contains the production version of the website
+
+When working with the repository, ensure you're on the correct branch for your 
task:
+- For code changes, work in the `master` branch
+- For documentation updates, work in the `docs` branch
+- The `asf-staging` and `asf-site` branches are automatically updated during 
the release process
+
 ## Building
 Building requires:
 * [Apache Maven](https://maven.apache.org/)
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/OverrideableHttpServletRequest.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/OverrideableHttpServletRequest.java
deleted file mode 100644
index efe6d63d4c..0000000000
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/OverrideableHttpServletRequest.java
+++ /dev/null
@@ -1,61 +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.rest.util.RestUtils.*;
-
-import jakarta.servlet.http.*;
-
-/**
- * A wrapper class that allows you to override basic fields.
- */
-class OverrideableHttpServletRequest extends HttpServletRequestWrapper {
-
-       private String pathInfo, servletPath;
-
-       /**
-        * Constructor.
-        *
-        * @param request The wrapped servlet request.
-        */
-       public OverrideableHttpServletRequest(HttpServletRequest request) {
-               super(request);
-       }
-
-       @Override /* Overridden from HttpServletRequest */
-       public String getPathInfo() {
-               // Note that pathInfo can never be empty.
-               return pathInfo == null ? super.getPathInfo() : 
pathInfo.charAt(0) == (char)0 ? null : pathInfo;
-       }
-
-       @Override /* Overridden from HttpServletRequest */
-       public String getServletPath() { return servletPath == null ? 
super.getServletPath() : servletPath; }
-
-       public OverrideableHttpServletRequest pathInfo(String value) {
-               validatePathInfo(value);
-               if (value == null)
-                       value = "\u0000";
-               pathInfo = value;
-               return this;
-       }
-
-       public OverrideableHttpServletRequest servletPath(String value) {
-               validateServletPath(value);
-               servletPath = value;
-               return this;
-       }
-}
\ No newline at end of file
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 436b9dade7..ff5b4dc7d9 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
@@ -28,6 +28,7 @@ import static org.apache.juneau.commons.utils.Utils.*;
 import static org.apache.juneau.http.HttpHeaders.*;
 import static org.apache.juneau.rest.annotation.RestOpAnnotation.*;
 import static org.apache.juneau.rest.processor.ResponseProcessor.*;
+import static org.apache.juneau.rest.util.RestUtils.*;
 
 import java.io.*;
 import java.lang.annotation.*;
@@ -5170,10 +5171,21 @@ public class RestContext extends Context {
                                var pi = sb.getPathInfoUndecoded();
                                var upi2 = UrlPath.of(pi == null ? sp : sp + 
pi);
                                var uppm = pathMatcher.match(upi2);
-                               if (nn(uppm) && ! uppm.hasEmptyVars()) {
-                                       sb.pathVars(uppm.getVars());
-                                       sb.req(new 
OverrideableHttpServletRequest(sb.req()).pathInfo(nullIfEmpty(urlDecode(uppm.getSuffix()))).servletPath(uppm.getPrefix()));
-                               } else {
+                       if (nn(uppm) && ! uppm.hasEmptyVars()) {
+                               sb.pathVars(uppm.getVars());
+                               var pathInfo = 
opt(validatePathInfo(nullIfEmpty(urlDecode(uppm.getSuffix())))).orElse("\u0000");
+                               var servletPath = 
validateServletPath(uppm.getPrefix());
+                               sb.req(new HttpServletRequestWrapper(sb.req()) {
+                                       @Override
+                                       public String getPathInfo() {
+                                               return pathInfo.charAt(0) == 
(char)0 ? null : pathInfo;
+                                       }
+                                       @Override
+                                       public String getServletPath() {
+                                               return servletPath;
+                                       }
+                               });
+                       } else {
                                        var call = sb.build();
                                        
call.debug(isDebug(call)).status(SC_NOT_FOUND).finish();
                                        return;
@@ -5187,8 +5199,18 @@ public class RestContext extends Context {
                                var rc = childMatch.get().getChildContext();
                                if (! uppm.hasEmptyVars()) {
                                        sb.pathVars(uppm.getVars());
-                                       var childRequest = new 
OverrideableHttpServletRequest(sb.req()).pathInfo(nullIfEmpty(urlDecode(uppm.getSuffix())))
-                                               
.servletPath(sb.req().getServletPath() + uppm.getPrefix());
+                                       var pathInfo = 
opt(validatePathInfo(nullIfEmpty(urlDecode(uppm.getSuffix())))).orElse("\u0000");
+                                       var servletPath = 
validateServletPath(sb.req().getServletPath() + uppm.getPrefix());
+                                       var childRequest = new 
HttpServletRequestWrapper(sb.req()) {
+                                               @Override
+                                               public String getPathInfo() {
+                                                       return 
pathInfo.charAt(0) == (char)0 ? null : pathInfo;
+                                               }
+                                               @Override
+                                               public String getServletPath() {
+                                                       return servletPath;
+                                               }
+                                       };
                                        rc.execute(rc.getResource(), 
childRequest, sb.res());
                                } else {
                                        var call = sb.build();
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOpContext.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOpContext.java
index 10d1417443..b89612f087 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOpContext.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOpContext.java
@@ -2057,7 +2057,7 @@ public class RestOpContext extends Context implements 
Comparable<RestOpContext>
                                        if (a instanceof Header a2) {
                                                if (nn(def)) {
                                                        try {
-                                                               
defaultRequestHeaders().set(basicHeader(firstNonEmpty(a2.name(), a2.value()), 
parseAnything(def)));
+                                                               
defaultRequestHeaders().set(basicHeader(firstNonEmpty(a2.name(), a2.value()), 
parseIfJson(def)));
                                                        } catch (ParseException 
e) {
                                                                throw new 
ConfigException(e, "Malformed @Header annotation");
                                                        }
@@ -2066,7 +2066,7 @@ public class RestOpContext extends Context implements 
Comparable<RestOpContext>
                                        if (a instanceof Query a2) {
                                                if (nn(def)) {
                                                        try {
-                                                               
defaultRequestQueryData().setDefault(basicPart(firstNonEmpty(a2.name(), 
a2.value()), parseAnything(def)));
+                                                               
defaultRequestQueryData().setDefault(basicPart(firstNonEmpty(a2.name(), 
a2.value()), parseIfJson(def)));
                                                        } catch (ParseException 
e) {
                                                                throw new 
ConfigException(e, "Malformed @Query annotation");
                                                        }
@@ -2075,7 +2075,7 @@ public class RestOpContext extends Context implements 
Comparable<RestOpContext>
                                        if (a instanceof FormData a2) {
                                                if (nn(def)) {
                                                        try {
-                                                               
defaultRequestFormData().setDefault(basicPart(firstNonEmpty(a2.name(), 
a2.value()), parseAnything(def)));
+                                                               
defaultRequestFormData().setDefault(basicPart(firstNonEmpty(a2.name(), 
a2.value()), parseIfJson(def)));
                                                        } catch (ParseException 
e) {
                                                                throw new 
ConfigException(e, "Malformed @FormData annotation");
                                                        }
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestSession.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestSession.java
index f17ca79836..7963dcf407 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestSession.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestSession.java
@@ -402,7 +402,7 @@ public class RestSession extends ContextSession {
        public Map<String,String[]> getQueryParams() {
                if (queryParams == null) {
                        if (req.getMethod().equalsIgnoreCase("POST"))
-                               queryParams = 
RestUtils.parseQuery(req.getQueryString(), map());
+                               queryParams = 
RestUtils.parseQuery(req.getQueryString());
                        else
                                queryParams = req.getParameterMap();
                }
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/RestUtils.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/RestUtils.java
index f2ff9ee9f5..c3e7621ec2 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/RestUtils.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/RestUtils.java
@@ -90,20 +90,6 @@ public class RestUtils {
 
        private static final Pattern INDEXED_LINK_PATTERN = 
Pattern.compile("(?s)(\\S*)\\[(\\d+)\\]\\:(.*)");
 
-       /**
-        * Normalizes the {@link RestOp#path()} value.
-        *
-        * @param path The path to normalize.
-        * @return The normalized path.
-        */
-       public static String fixMethodPath(String path) {
-               if (path == null)
-                       return null;
-               if (path.equals("/"))
-                       return path;
-               return trimTrailingSlashes(path);
-       }
-
        /**
         * Returns readable text for an HTTP response code.
         *
@@ -121,119 +107,22 @@ public class RestUtils {
         * @return The un-decoded path info.
         */
        public static String getPathInfoUndecoded(HttpServletRequest req) {
-               String requestURI = req.getRequestURI();
-               String contextPath = req.getContextPath();
-               String servletPath = req.getServletPath();
-               int l = contextPath.length() + servletPath.length();
-               if (requestURI.length() == l)
-                       return null;
-               return requestURI.substring(l);
-       }
-
-       /**
-        * Returns <jk>true</jk> if the specified value is a valid context path.
-        *
-        * The path must start with a "/" character but not end with a "/" 
character.
-        * For servlets in the default (root) context, the value should be "".
-        *
-        * @param value The value to test.
-        * @return <jk>true</jk> if the specified value is a valid context path.
-        */
-       public static boolean isValidContextPath(String value) {
-               if (value == null)
-                       return false;
-               if (value.isEmpty())
-                       return true;
-               if (value.charAt(value.length() - 1) == '/' || value.charAt(0) 
!= '/')
-                       return false;
-               return true;
-       }
-
-       /**
-        * Returns <jk>true</jk> if the specified value is a valid path-info 
path.
-        *
-        * The extra path information follows the servlet path but precedes the 
query string and will start with a "/" character.
-        * The value should be null if there was no extra path information.
-        *
-        * @param value The value to test.
-        * @return <jk>true</jk> if the specified value is a valid path-info 
path.
-        */
-       public static boolean isValidPathInfo(String value) {
-               if (value == null)
-                       return true;
-               if (value.isEmpty() || value.charAt(0) != '/')
-                       return false;
-               return true;
-       }
-
-       /**
-        * Returns <jk>true</jk> if the specified value is a valid servlet path.
-        *
-        * This path must with a "/" character and includes either the servlet 
name or a path to the servlet,
-        * but does not include any extra path information or a query string.
-        * Should be an empty string ("") if the servlet used to process this 
request was matched using the "/*" pattern.
-        *
-        * @param value The value to test.
-        * @return <jk>true</jk> if the specified value is a valid servlet path.
-        */
-       public static boolean isValidServletPath(String value) {
-               if (value == null)
-                       return false;
-               if (value.isEmpty())
-                       return true;
-               if (value.equals("/") || value.charAt(value.length() - 1) == 
'/' || value.charAt(0) != '/')
-                       return false;
-               return true;
+               var requestURI = req.getRequestURI();
+               var contextPath = req.getContextPath();
+               var servletPath = req.getServletPath();
+               var l = contextPath.length() + servletPath.length();
+               return requestURI.length() == l ? null : 
requestURI.substring(l);
        }
 
        /**
         * Parses a string that can consist of a simple string or JSON 
object/array.
         *
-        * @param s The string to parse.
+        * @param value The string to parse.
         * @return The parsed value, or <jk>null</jk> if the input is null.
         * @throws ParseException Invalid JSON in string.
         */
-       public static Object parseAnything(String s) throws ParseException {
-               if (isProbablyJson(s))
-                       return JsonParser.DEFAULT.parse(s, Object.class);
-               return s;
-       }
-
-       /**
-        * Parses HTTP header.
-        *
-        * @param s The string to parse.
-        * @return The parsed string.
-        */
-       public static String[] parseHeader(String s) {
-               var i = s.indexOf(':');
-               if (i == -1)
-                       i = s.indexOf('=');
-               if (i == -1)
-                       return null;
-               var name = s.substring(0, i).trim().toLowerCase(Locale.ENGLISH);
-               String val = s.substring(i + 1).trim();
-               return a(name, val);
-       }
-
-       /**
-        * Parses key/value pairs separated by either : or =
-        *
-        * @param s The string to parse.
-        * @return The parsed string.
-        */
-       public static String[] parseKeyValuePair(String s) {
-               var i = -1;
-               for (var j = 0; j < s.length() && i < 0; j++) {
-                       var c = s.charAt(j);
-                       if (c == '=' || c == ':')
-                               i = j;
-               }
-               if (i == -1)
-                       return null;
-               String name = s.substring(0, i).trim();
-               String val = s.substring(i + 1).trim();
-               return a(name, val);
+       public static Object parseIfJson(String value) throws ParseException {
+               return isProbablyJson(value) ? JsonParser.DEFAULT.parse(value, 
Object.class) : value;
        }
 
        /**
@@ -243,27 +132,14 @@ public class RestUtils {
         * @return A new map containing the parsed query.
         */
        public static Map<String,String[]> parseQuery(Object qs) {
-               return parseQuery(qs, null);
-       }
-
-       /**
-        * Same as {@link #parseQuery(Object)} but allows you to specify the 
map to insert values into.
-        *
-        * @param qs A reader containing the query string to parse.
-        * @param map The map to pass the values into.
-        * @return The same map passed in, or a new map if it was <jk>null</jk>.
-        */
-       public static Map<String,String[]> parseQuery(Object qs, 
Map<String,String[]> map) {
 
                try {
-                       Map<String,String[]> m = map;
-                       if (m == null)
-                               m = map();
+                       var m = CollectionUtils.<String,String[]>map();
 
                        if (qs == null || ((qs instanceof CharSequence) && 
isEmpty(s(qs))))
                                return m;
 
-                       try (ParserPipe p = new ParserPipe(qs)) {
+                       try (var p = new ParserPipe(qs)) {
 
                                // S1: Looking for attrName start.
                                // S2: Found attrName start, looking for = or & 
or end.
@@ -271,7 +147,7 @@ public class RestUtils {
                                // S4: Found valStart, looking for & or end.
 
                                try (var r = new UonReader(p, true)) {
-                                       int c = r.peekSkipWs();
+                                       var c = r.peekSkipWs();
                                        if (c == '?')
                                                r.read();
 
@@ -289,7 +165,7 @@ public class RestUtils {
                                                        if (c == -1) {
                                                                add(m, 
r.getMarked(), null);
                                                        } else if (c == 
'\u0001') {
-                                                               
m.put(r.getMarked(0, -1), null);
+                                                               add(m, 
r.getMarked(0, -1), null);
                                                                state = S1;
                                                        } else if (c == 
'\u0002') {
                                                                currAttr = 
r.getMarked(0, -1);
@@ -350,192 +226,63 @@ public class RestUtils {
        }
 
        /**
-        * If the specified path-info starts with the specified context path, 
trims the context path from the path info.
+        * Validates that the specified value is a valid path-info path and 
returns it.
         *
-        * @param contextPath The context path.
-        * @param path The URL path.
-        * @return The path following the context path, or the original path.
-        */
-       public static String trimContextPath(String contextPath, String path) {
-               if (path == null)
-                       return null;
-               if (path.isEmpty() || path.equals("/") || contextPath.isEmpty() 
|| contextPath.equals("/"))
-                       return path;
-               String op = path;
-               if (path.charAt(0) == '/')
-                       path = path.substring(1);
-               if (contextPath.charAt(0) == '/')
-                       contextPath = contextPath.substring(1);
-               if (path.startsWith(contextPath)) {
-                       if (path.length() == contextPath.length())
-                               return "/";
-                       path = path.substring(contextPath.length());
-                       if (path.isEmpty() || path.charAt(0) == '/')
-                               return path;
-               }
-               return op;
-       }
-
-       /**
-        * Efficiently trims the path info part from a request URI.
+        * <p>
+        * A valid path-info path must be:
+        * <ul>
+        *      <li><jk>null</jk> (valid, indicates no extra path 
information)</li>
+        *      <li>Non-empty and starting with <c>/</c> (e.g., 
<c>/users/123</c>)</li>
+        * </ul>
         *
         * <p>
-        * The result is the URI of the servlet itself.
+        * The path-info follows the servlet path but precedes the query string.
         *
-        * @param requestURI The value returned by {@link 
HttpServletRequest#getRequestURL()}
-        * @param contextPath The value returned by {@link 
HttpServletRequest#getContextPath()}
-        * @param servletPath The value returned by {@link 
HttpServletRequest#getServletPath()}
-        * @return The same StringBuilder with remainder trimmed.
+        * @param value The value to validate.
+        * @return The validated value (may be <jk>null</jk>).
+        * @throws RuntimeException If the value is not a valid path-info path.
         */
-       public static StringBuffer trimPathInfo(StringBuffer requestURI, String 
contextPath, String servletPath) {
-               if (servletPath.equals("/"))
-                       servletPath = "";
-               if (contextPath.equals("/"))
-                       contextPath = "";
-
-               try {
-                       // Given URL:  http://hostname:port/servletPath/extra
-                       // We want:    http://hostname:port/servletPath
-                       var sc = 0;
-                       for (var i = 0; i < requestURI.length(); i++) {
-                               var c = requestURI.charAt(i);
-                               if (c == '/') {
-                                       sc++;
-                                       if (sc == 3) {
-                                               if (servletPath.isEmpty()) {
-                                                       requestURI.setLength(i);
-                                                       return requestURI;
-                                               }
-
-                                               // Make sure context path 
follows the authority.
-                                               for (var j = 0; j < 
contextPath.length(); i++, j++)
-                                                       if 
(requestURI.charAt(i) != contextPath.charAt(j))
-                                                               throw new 
Exception("case=1");
-
-                                               // Make sure servlet path 
follows the authority.
-                                               for (var j = 0; j < 
servletPath.length(); i++, j++)
-                                                       if 
(requestURI.charAt(i) != servletPath.charAt(j))
-                                                               throw new 
Exception("case=2");
-
-                                               // Make sure servlet path isn't 
a false match (e.g. /foo2 should not match /foo)
-                                               c = (requestURI.length() == i ? 
'/' : requestURI.charAt(i));
-                                               if (c == '/' || c == '?') {
-                                                       requestURI.setLength(i);
-                                                       return requestURI;
-                                               }
-
-                                               throw new Exception("case=3");
-                                       }
-                               } else if (c == '?') {
-                                       if (sc != 2)
-                                               throw new Exception("case=4");
-                                       if (servletPath.isEmpty()) {
-                                               requestURI.setLength(i);
-                                               return requestURI;
-                                       }
-                                       throw new Exception("case=5");
-                               }
-                       }
-                       if (servletPath.isEmpty())
-                               return requestURI;
-                       throw new Exception("case=6");
-               } catch (Exception e) {
-                       throw rex(e, "Could not find servlet path in request 
URI.  URI=''{0}'', servletPath=''{1}''", requestURI, servletPath);
-               }
+       public static String validatePathInfo(String value) {
+               if (value != null && (value.isEmpty() || value.charAt(0) != 
'/'))
+                       throw rex("Value is not a valid path-info path: [{0}]", 
value);
+               return value;
        }
 
        /**
-        * Throws a {@link RuntimeException} if the method {@link 
#isValidContextPath(String)} returns <jk>false</jk> for the specified value.
+        * Validates that the specified value is a valid servlet path and 
returns it.
         *
-        * @param value The value to test.
-        */
-       public static void validateContextPath(String value) {
-               if (! isValidContextPath(value))
-                       throw rex("Value is not a valid context path: [{0}]", 
value);
-       }
-
-       /**
-        * Throws a {@link RuntimeException} if the method {@link 
#isValidPathInfo(String)} returns <jk>false</jk> for the specified value.
+        * <p>
+        * A valid servlet path must be:
+        * <ul>
+        *      <li>An empty string <c>""</c> (valid, indicates servlet matched 
using <c>/*</c> pattern)</li>
+        *      <li>Non-empty, starting with <c>/</c>, not ending with 
<c>/</c>, and not exactly <c>/</c> (e.g., <c>/api</c>, <c>/api/users</c>)</li>
+        * </ul>
         *
-        * @param value The value to test.
-        */
-       public static void validatePathInfo(String value) {
-               if (! isValidPathInfo(value))
-                       throw rex("Value is not a valid path-info path: [{0}]", 
value);
-       }
-
-       /**
-        * Throws a {@link RuntimeException} if the method {@link 
#isValidServletPath(String)} returns <jk>false</jk> for the specified value.
+        * <p>
+        * The servlet path includes either the servlet name or a path to the 
servlet, but does not include any extra path information or a query string.
         *
-        * @param value The value to test.
+        * @param value The value to validate.
+        * @return The validated value (never <jk>null</jk>).
+        * @throws RuntimeException If the value is <jk>null</jk> or not a 
valid servlet path.
         */
-       public static void validateServletPath(String value) {
-               if (! isValidServletPath(value))
+       public static String validateServletPath(String value) {
+               if (value == null)
+                       throw rex("Value is not a valid servlet path: [{0}]", 
value);
+               if (! value.isEmpty() && (value.equals("/") || 
value.charAt(value.length() - 1) == '/' || value.charAt(0) != '/'))
                        throw rex("Value is not a valid servlet path: [{0}]", 
value);
+               return value;
        }
 
        private static void add(Map<String,String[]> m, String key, String val) 
{
-               boolean b = m.containsKey(key);
                if (val == null) {
-                       if (! b)
+                       if (! m.containsKey(key))
                                m.put(key, null);
-               } else if (b && nn(m.get(key))) {
-                       m.put(key, addAll(m.get(key), val));
                } else {
-                       m.put(key, a(val));
-               }
-       }
-
-       static String[] resolveContent(String[] content, String[] 
parentContent) {
-               if (content.length == 0)
-                       return parentContent;
-
-               List<String> list = list();
-               for (var l : content) {
-                       if ("INHERIT".equals(l)) {
-                               addAll(list, parentContent);
-                       } else if ("NONE".equals(l)) {
-                               return new String[0];
-                       } else {
-                               list.add(l);
-                       }
-               }
-               return array(list, String.class);
-       }
-
-       static String[] resolveLinks(String[] links, String[] parentLinks) {
-               if (links.length == 0)
-                       return parentLinks;
-
-               List<String> list = list();
-               for (var l : links) {
-                       if ("INHERIT".equals(l))
-                               addAll(list, parentLinks);
-                       else if (l.indexOf('[') != -1 && 
INDEXED_LINK_PATTERN.matcher(l).matches()) {
-                               Matcher lm = INDEXED_LINK_PATTERN.matcher(l);
-                               lm.matches();
-                               String key = lm.group(1);
-                               int index = Math.min(list.size(), 
Integer.parseInt(lm.group(2)));
-                               String remainder = lm.group(3);
-                               list.add(index, key.isEmpty() ? remainder : key 
+ ":" + remainder);
-                       } else {
-                               list.add(l);
-                       }
-               }
-               return array(list, String.class);
-       }
-
-       static String resolveNewlineSeparatedAnnotation(String[] value, String 
fromParent) {
-               if (value.length == 0)
-                       return fromParent;
-
-               List<String> l = list();
-               for (var v : value) {
-                       if (! "INHERIT".equals(v))
-                               l.add(v);
-                       else if (nn(fromParent))
-                               l.add(fromParent);
+                       m.compute(key, (k, existing) -> {
+                               if (existing != null)
+                                       return addAll(existing, val);
+                               return a(val);
+                       });
                }
-               return StringUtils.join(l, '\n');
        }
 }
\ No newline at end of file
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/util/RestUtils_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/rest/util/RestUtils_Test.java
index c54f3a3088..4f3b878388 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/rest/util/RestUtils_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/rest/util/RestUtils_Test.java
@@ -49,63 +49,6 @@ class RestUtils_Test extends TestBase {
                assertEquals("%2F", urlEncode("/"));
        }
 
-       
//------------------------------------------------------------------------------------------------------------------
-       // trimPathInfo(String,String)
-       
//------------------------------------------------------------------------------------------------------------------
-
-       @Test void c01_testGetServletURI() {
-               var e = "http://hostname";;
-               var sp = "";
-               var cp = "";
-
-               for (var s : a("http://hostname";, "http://hostname/foo";, 
"http://hostname?foo";, "http://hostname/?foo";))
-                       assertEquals(e, trimPathInfo(new StringBuffer(s), cp, 
sp).toString());
-
-               for (var s : a("http:/hostname?foo")) {
-                       assertThrows(Exception.class, ()->trimPathInfo(new 
StringBuffer(s), "", ""));
-               }
-
-               e = "http://hostname";;
-               sp = "/";
-               cp = "/";
-
-               for (var s : a("http://hostname";, "http://hostname/foo";, 
"http://hostname?foo";, "http://hostname/?foo";))
-                       assertEquals(e, trimPathInfo(new StringBuffer(s), cp, 
sp).toString());
-
-               e = "http://hostname/foo";;
-               sp = "/foo";
-               cp = "/";
-
-               for (var s : a("http://hostname/foo";, 
"http://hostname/foo/bar";, "http://hostname/foo?bar";))
-                       assertEquals(e, trimPathInfo(new StringBuffer(s), cp, 
sp).toString());
-
-               for (var s : a("http://hostname/foo2";, "http://hostname/fo2";, 
"http://hostname?foo";, "http://hostname/fo?bar";, "http:/hostname/foo")) {
-                       assertThrows(Exception.class, ()->trimPathInfo(new 
StringBuffer(s), "/", "/foo"));
-               }
-
-               e = "http://hostname/foo/bar";;
-               sp = "/foo/bar";
-               cp = "/";
-
-               for (var s : a("http://hostname/foo/bar";, 
"http://hostname/foo/bar/baz";, "http://hostname/foo/bar?baz";))
-                       assertEquals(e, trimPathInfo(new StringBuffer(s), cp, 
sp).toString());
-
-               for (var s : a("http://hostname/foo2/bar";, 
"http://hostname/foo/bar2";)) {
-                       assertThrows(Exception.class, ()->trimPathInfo(new 
StringBuffer(s), "/foo/bar", "/foo/bar"));
-               }
-
-               e = "http://hostname/foo/bar";;
-               sp = "/bar";
-               cp = "/foo";
-
-               for (var s : a("http://hostname/foo/bar";, 
"http://hostname/foo/bar/baz";, "http://hostname/foo/bar?baz";))
-                       assertEquals(e, trimPathInfo(new StringBuffer(s), cp, 
sp).toString());
-
-               for (var s : a("http://hostname/foo2/bar";, 
"http://hostname/foo/bar2";)) {
-                       assertThrows(Exception.class, ()->trimPathInfo(new 
StringBuffer(s), "/foo", "/bar"));
-               }
-       }
-
        
//------------------------------------------------------------------------------------------------------------------
        // trimSlashes(String)
        
//------------------------------------------------------------------------------------------------------------------
@@ -185,48 +128,4 @@ class RestUtils_Test extends TestBase {
        public static class B {
                public String f1 = "f1";
        }
-
-       
//------------------------------------------------------------------------------------------------------------------
-       // Other tests
-       
//------------------------------------------------------------------------------------------------------------------
-
-       @Test void i01_testTrimContextPath() {
-               assertEquals("/bar", trimContextPath("/foo", "/bar"));
-               assertEquals("/", trimContextPath("/foo", "/"));
-               assertEquals("", trimContextPath("/foo", ""));
-               assertEquals(null, trimContextPath("/foo", null));
-
-               assertEquals("/bar", trimContextPath("/foo", "/foo/bar"));
-               assertEquals("/bar/baz", trimContextPath("/foo", 
"/foo/bar/baz"));
-               assertEquals("/bar/", trimContextPath("/foo", "/foo/bar/"));
-               assertEquals("/", trimContextPath("/foo", "/foo/"));
-               assertEquals("/", trimContextPath("/foo", "/foo"));
-       }
-
-       @Test void i02_testIsValidContextPath() {
-               assertTrue(isValidContextPath(""));
-               assertTrue(isValidContextPath("/foo"));
-               assertFalse(isValidContextPath("/"));
-               assertFalse(isValidContextPath("/foo/"));
-               assertFalse(isValidContextPath(null));
-               assertFalse(isValidContextPath("foo"));
-       }
-
-       @Test void i03_testIsValidServletPath() {
-               assertTrue(isValidServletPath(""));
-               assertTrue(isValidServletPath("/foo"));
-               assertFalse(isValidServletPath("/"));
-               assertFalse(isValidServletPath("/foo/"));
-               assertFalse(isValidServletPath(null));
-               assertFalse(isValidServletPath("foo"));
-       }
-
-       @Test void i04_testIsValidPathInfo() {
-               assertFalse(isValidPathInfo(""));
-               assertTrue(isValidPathInfo("/foo"));
-               assertTrue(isValidPathInfo("/"));
-               assertTrue(isValidPathInfo("/foo/"));
-               assertTrue(isValidPathInfo(null));
-               assertFalse(isValidPathInfo("foo"));
-       }
 }
\ No newline at end of file

Reply via email to