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