From e3169fb9db8ceb8f5c750d971c9f3fe6c135db3f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Apr 2026 00:07:34 +0000 Subject: [PATCH 01/33] Update killbill-oss-parent to 0.147.0-e4efed1-SNAPSHOT, remove version properties and targetJdk Agent-Logs-Url: https://github.com/killbill/killbill-commons/sessions/0cd0a140-7cb2-4780-9385-7e0741ebdf50 Co-authored-by: xsalefter <510438+xsalefter@users.noreply.github.com> --- pom.xml | 122 +------------------------------------------------------- 1 file changed, 1 insertion(+), 121 deletions(-) diff --git a/pom.xml b/pom.xml index fa694a84..ff9b0f38 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ org.kill-bill.billing killbill-oss-parent - 0.146.63 + 0.147.0-12af62b-SNAPSHOT org.kill-bill.commons killbill-commons @@ -66,108 +66,10 @@ true - - 1.12.0 - - 7.0.0 - - 3.0.6 - - 2.13.4 - - 2.1.0 - - 2.1.1 - - 2.0.1 - - 5.0.0 - - 3.0.2 - - 3.0.0 - - 3.30.2-GA - - 4.0.0 - - 4.0.0 - - 3.0.18 - - 11.0.24 - 21 - - 4.9.8.3 -Xmx${build.jvmsize} --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED - - com.fasterxml.jackson.jakarta.rs - jackson-jakarta-rs-json-provider - ${jackson.jakarta.rs.version} - - - com.google.inject - guice - ${guice.version} - - - com.google.inject.extensions - guice-servlet - ${guice.version} - - - jakarta.activation - jakarta.activation-api - ${jakarta.activation-api.version} - - - jakarta.inject - jakarta.inject-api - ${jakarta.inject-api.version} - - - jakarta.xml.bind - jakarta.xml.bind-api - ${jaxb-api.version} - - - org.eclipse.jetty - jetty-alpn-server - ${jetty.version} - - - org.eclipse.jetty.http2 - http2-server - ${jetty.version} - - - org.eclipse.jetty.websocket - websocket-jetty-api - ${jetty.version} - - - org.glassfish.jaxb - jaxb-runtime - ${jaxb-runtime.version} - - - - com.sun.activation - jakarta.activation - - - - - org.javassist - javassist - ${javassist.version} - org.kill-bill.commons killbill-automaton @@ -281,26 +183,4 @@ - - - - com.github.spotbugs - spotbugs-maven-plugin - ${spotbugs-maven-plugin.version} - - - org.apache.maven.plugins - maven-enforcer-plugin - - - - org.codehaus.mojo - extra-enforcer-rules - ${extra-enforcer-rules.version} - - - - - From b7f5514f7e41e2c14761c09c9622458013a09c10 Mon Sep 17 00:00:00 2001 From: xsalefter Date: Wed, 22 Apr 2026 13:49:08 +0700 Subject: [PATCH 02/33] add netty-commons dependencies explicitly --- clock/pom.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/clock/pom.xml b/clock/pom.xml index f6c3d091..21e07470 100644 --- a/clock/pom.xml +++ b/clock/pom.xml @@ -33,6 +33,13 @@ guice runtime + + + io.netty + netty-common + ${netty.version} + test + it.ozimov embedded-redis @@ -51,6 +58,12 @@ org.redisson redisson test + + + io.netty + netty-common + + org.slf4j From 5b5f72820b6c29d11724ce3685a5f3194c0dddee Mon Sep 17 00:00:00 2001 From: xsalefter Date: Fri, 24 Apr 2026 17:50:26 +0700 Subject: [PATCH 03/33] update release.yml to use latest actions --- .github/workflows/release.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6d31c914..805b9a37 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,11 +27,11 @@ jobs: steps: - name: Checkout code if: github.event.inputs.perform_version == '' - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Checkout full repository # Required when performing an existing release. if: github.event.inputs.perform_version != '' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: '0' - name: Setup git user @@ -43,7 +43,7 @@ jobs: git config --global user.name "Kill Bill core team" git config --global url."https://${BUILD_USER}:${BUILD_TOKEN}@github.com/".insteadOf "git@github.com:" - name: Configure Java - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: 21 distribution: temurin @@ -65,7 +65,7 @@ jobs: # Will be pushed as part of the release process, only if the release is successful git commit -m "pom.xml: update killbill-oss-parent to ${{ github.event.inputs.parent_version }}" - name: Configure settings.xml for release - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: 21 distribution: temurin @@ -102,4 +102,3 @@ jobs: run: | PROJECT_VERSION=$(git describe --abbrev=0 | cut -d '-' -f 3) gh workflow -R killbill/killbill-oss-parent run release.yml -f commons_version=${PROJECT_VERSION} - From fdcb53c802de9364e4c0f71f8e85a6463a79772a Mon Sep 17 00:00:00 2001 From: xsalefter Date: Fri, 24 Apr 2026 17:51:49 +0700 Subject: [PATCH 04/33] update killbill-oss-parent to 0.147.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ff9b0f38..87b33d84 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ org.kill-bill.billing killbill-oss-parent - 0.147.0-12af62b-SNAPSHOT + 0.147.0 org.kill-bill.commons killbill-commons From 6af61763cd53cbb4d4e5353e413b2555685768cc Mon Sep 17 00:00:00 2001 From: xsalefter Date: Wed, 27 May 2026 02:51:05 +0700 Subject: [PATCH 05/33] update oss-parent version to use latest snapshot --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 87b33d84..3691b576 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ org.kill-bill.billing killbill-oss-parent - 0.147.0 + 0.147.0-f3fd77e-SNAPSHOT org.kill-bill.commons killbill-commons @@ -30,11 +30,11 @@ pom killbill-commons Kill Bill reusable Java components - http://github.com/killbill/killbill-commons + https://github.com/killbill/killbill-commons Apache License 2.0 - http://www.apache.org/licenses/LICENSE-2.0.html + https://www.apache.org/licenses/LICENSE-2.0.html repo From 340d12099cfaff5d08d0c05830971bc391915de7 Mon Sep 17 00:00:00 2001 From: xsalefter Date: Wed, 27 May 2026 02:51:35 +0700 Subject: [PATCH 06/33] add dependency to killbill-utils --- jooby/pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jooby/pom.xml b/jooby/pom.xml index 8e837dcd..70c480cf 100644 --- a/jooby/pom.xml +++ b/jooby/pom.xml @@ -177,6 +177,10 @@ 4.5.14 test + + org.kill-bill.commons + killbill-utils + From 82a83917f49d8a86088962cc59f267533c14908a Mon Sep 17 00:00:00 2001 From: xsalefter Date: Wed, 27 May 2026 04:36:36 +0700 Subject: [PATCH 07/33] initial commit for Closeables.java --- .../killbill/commons/utils/io/Closeables.java | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 utils/src/main/java/org/killbill/commons/utils/io/Closeables.java diff --git a/utils/src/main/java/org/killbill/commons/utils/io/Closeables.java b/utils/src/main/java/org/killbill/commons/utils/io/Closeables.java new file mode 100644 index 00000000..56606568 --- /dev/null +++ b/utils/src/main/java/org/killbill/commons/utils/io/Closeables.java @@ -0,0 +1,131 @@ +/* + * Copyright 2020-2026 Equinix, Inc + * Copyright 2014-2026 The Billing Project, LLC + * + * The Billing Project 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.killbill.commons.utils.io; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jakarta.annotation.Nullable; + +/** + * Verbatim copy of com.google.common.io.Closeables. Changes: + * - Use SLF4J instead of JUL + * - add .debug() when closable is null + */ +public final class Closeables { + + private static final Logger logger = LoggerFactory.getLogger(Closeables.class.getName()); + + private Closeables() {} + + /** + * Closes a {@link Closeable}, with control over whether an {@code IOException} may be thrown. + * This is primarily useful in a finally block, where a thrown exception needs to be logged but + * not propagated (otherwise the original exception will be lost). + * + *

If {@code swallowIOException} is true then we never throw {@code IOException} but merely log + * it. + * + *

Example: + * + *

{@code
+     * public void useStreamNicely() throws IOException {
+     *   SomeStream stream = new SomeStream("foo");
+     *   boolean threw = true;
+     *   try {
+     *     // ... code which does something with the stream ...
+     *     threw = false;
+     *   } finally {
+     *     // If an exception occurs, rethrow it only if threw==false:
+     *     Closeables.close(stream, threw);
+     *   }
+     * }
+     * }
+ * + * @param closeable the {@code Closeable} object to be closed, or null, in which case this method + * does nothing + * @param swallowIOException if true, don't propagate IO exceptions thrown by the {@code close} + * methods + * @throws IOException if {@code swallowIOException} is false and {@code close} throws an {@code + * IOException}. + */ + public static void close(@Nullable Closeable closeable, boolean swallowIOException) + throws IOException { + if (closeable == null) { + logger.debug("Closeable is null; skip closing."); + return; + } + try { + closeable.close(); + } catch (IOException e) { + if (swallowIOException) { + logger.warn("IOException thrown while closing Closeable.", e); + } else { + throw e; + } + } + } + + /** + * Closes the given {@link InputStream}, logging any {@code IOException} that's thrown rather than + * propagating it. + * + *

While it's not safe in the general case to ignore exceptions that are thrown when closing an + * I/O resource, it should generally be safe in the case of a resource that's being used only for + * reading, such as an {@code InputStream}. Unlike with writable resources, there's no chance that + * a failure that occurs when closing the stream indicates a meaningful problem such as a failure + * to flush all bytes to the underlying resource. + * + * @param inputStream the input stream to be closed, or {@code null} in which case this method + * does nothing + * @since 17.0 + */ + public static void closeQuietly(@Nullable InputStream inputStream) { + try { + close(inputStream, true); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + /** + * Closes the given {@link Reader}, logging any {@code IOException} that's thrown rather than + * propagating it. + * + *

While it's not safe in the general case to ignore exceptions that are thrown when closing an + * I/O resource, it should generally be safe in the case of a resource that's being used only for + * reading, such as a {@code Reader}. Unlike with writable resources, there's no chance that a + * failure that occurs when closing the reader indicates a meaningful problem such as a failure to + * flush all bytes to the underlying resource. + * + * @param reader the reader to be closed, or {@code null} in which case this method does nothing + * @since 17.0 + */ + public static void closeQuietly(@Nullable Reader reader) { + try { + close(reader, true); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } +} From d932e1310c33b243df0f75c3a0a3117c53bd0428 Mon Sep 17 00:00:00 2001 From: xsalefter Date: Wed, 27 May 2026 04:36:45 +0700 Subject: [PATCH 08/33] add jakarta.annotation-api dependency --- utils/pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/utils/pom.xml b/utils/pom.xml index 7d7b2bb7..e1ac9d2c 100644 --- a/utils/pom.xml +++ b/utils/pom.xml @@ -32,14 +32,14 @@ - - com.google.code.findbugs - jsr305 - org.slf4j slf4j-api + + jakarta.annotation + jakarta.annotation-api + org.slf4j slf4j-simple From aaf40ad070c5bb257a91218303c906e39bf919d9 Mon Sep 17 00:00:00 2001 From: xsalefter Date: Wed, 27 May 2026 02:52:22 +0700 Subject: [PATCH 09/33] replace guava classes with JDK API --- jooby/src/main/java/org/jooby/Asset.java | 6 +- jooby/src/main/java/org/jooby/Env.java | 15 +- jooby/src/main/java/org/jooby/Jooby.java | 25 ++-- jooby/src/main/java/org/jooby/MediaType.java | 38 +++-- jooby/src/main/java/org/jooby/Request.java | 7 +- jooby/src/main/java/org/jooby/Response.java | 5 +- jooby/src/main/java/org/jooby/Result.java | 16 +- jooby/src/main/java/org/jooby/Route.java | 40 +++-- jooby/src/main/java/org/jooby/Sse.java | 5 +- .../main/java/org/jooby/handlers/Cors.java | 7 +- .../java/org/jooby/handlers/CsrfHandler.java | 4 +- .../org/jooby/internal/BuiltinParser.java | 82 +++++----- .../org/jooby/internal/HttpHandlerImpl.java | 3 +- .../java/org/jooby/internal/MutantImpl.java | 3 +- .../java/org/jooby/internal/RequestImpl.java | 9 +- .../java/org/jooby/internal/ResponseImpl.java | 3 +- .../org/jooby/internal/RouteMetadata.java | 3 +- .../java/org/jooby/internal/SessionImpl.java | 5 +- .../java/org/jooby/internal/URLAsset.java | 4 +- .../org/jooby/internal/WebSocketImpl.java | 5 +- .../internal/WebSocketRendererContext.java | 4 +- .../org/jooby/internal/WsBinaryMessage.java | 7 +- .../org/jooby/internal/mvc/MvcRoutes.java | 35 +++-- .../org/jooby/internal/mvc/RequestParam.java | 8 +- .../mvc/RequestParamProviderImpl.java | 8 +- .../org/jooby/internal/parser/BeanParser.java | 4 +- .../jooby/internal/parser/ParserBuilder.java | 8 +- .../jooby/internal/parser/ParserExecutor.java | 5 +- .../org/jooby/internal/ssl/PemReader.java | 6 +- .../jooby/servlet/ServletServletRequest.java | 9 +- .../jooby/servlet/ServletServletResponse.java | 3 +- .../java/org/jooby/servlet/ServletUpload.java | 3 +- .../main/java/org/jooby/test/MockRouter.java | 14 +- .../test/java/org/jooby/CookieCodecTest.java | 21 +-- jooby/src/test/java/org/jooby/CorsTest.java | 10 +- .../java/org/jooby/DefaultErrHandlerTest.java | 9 +- jooby/src/test/java/org/jooby/EnvTest.java | 5 +- .../java/org/jooby/RequestForwardingTest.java | 11 +- .../org/jooby/ResponseForwardingTest.java | 17 ++- jooby/src/test/java/org/jooby/ResultTest.java | 9 +- .../java/org/jooby/RouteDefinitionTest.java | 4 +- jooby/src/test/java/org/jooby/SseTest.java | 7 +- jooby/src/test/java/org/jooby/ViewTest.java | 9 +- .../internal/AbstractRendererContextTest.java | 3 +- .../org/jooby/internal/AppPrinterTest.java | 34 ++--- .../org/jooby/internal/BuiltinParserTest.java | 140 +++++++++++++++++- .../internal/CookieSessionManagerTest.java | 8 +- .../org/jooby/internal/FallbackRouteTest.java | 10 +- .../org/jooby/internal/MutantImplTest.java | 111 +++++++------- .../jooby/internal/ParamConverterTest.java | 23 ++- .../internal/ParamReferenceImplTest.java | 17 +-- .../org/jooby/internal/RequestImplTest.java | 42 +++--- .../org/jooby/internal/WebSocketImplTest.java | 7 +- .../WebSocketRendererContextTest.java | 7 +- .../jooby/internal/WsBinaryMessageTest.java | 4 +- .../internal/handlers/HeadHandlerTest.java | 11 +- .../internal/handlers/OptionsHandlerTest.java | 9 +- .../jooby/internal/mvc/MvcWebSocketTest.java | 5 +- .../jooby/internal/parser/BeanPlanTest.java | 12 +- .../internal/reqparam/ParserExecutorTest.java | 5 +- .../servlet/ServletServletRequestTest.java | 18 +-- .../test/java/org/jooby/test/MockUnit.java | 12 +- 62 files changed, 547 insertions(+), 432 deletions(-) diff --git a/jooby/src/main/java/org/jooby/Asset.java b/jooby/src/main/java/org/jooby/Asset.java index 8f8465b6..578984b3 100644 --- a/jooby/src/main/java/org/jooby/Asset.java +++ b/jooby/src/main/java/org/jooby/Asset.java @@ -20,9 +20,9 @@ import java.io.InputStream; import java.net.URISyntaxException; import java.net.URL; +import java.nio.ByteBuffer; import com.google.common.io.BaseEncoding; -import com.google.common.primitives.Longs; import org.jooby.funzy.Throwing; import jakarta.annotation.Nonnull; @@ -141,8 +141,8 @@ default String etag() { BaseEncoding b64 = BaseEncoding.base64(); int lhash = resource().toURI().hashCode(); - b.append(b64.encode(Longs.toByteArray(lastModified() ^ lhash))); - b.append(b64.encode(Longs.toByteArray(length() ^ lhash))); + b.append(b64.encode(ByteBuffer.allocate(Long.BYTES).putLong(lastModified() ^ lhash).array())); + b.append(b64.encode(ByteBuffer.allocate(Long.BYTES).putLong(length() ^ lhash).array())); b.append('"'); return b.toString(); } catch (URISyntaxException x) { diff --git a/jooby/src/main/java/org/jooby/Env.java b/jooby/src/main/java/org/jooby/Env.java index 432f3d70..05a41fe0 100644 --- a/jooby/src/main/java/org/jooby/Env.java +++ b/jooby/src/main/java/org/jooby/Env.java @@ -16,7 +16,6 @@ package org.jooby; import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableList; import com.google.inject.Key; import com.google.inject.name.Names; import com.typesafe.config.Config; @@ -25,6 +24,8 @@ import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; + +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -332,11 +333,11 @@ default Env build(final Config config) { String name = config.hasPath("application.env") ? config.getString("application.env") : "dev"; return new Env() { - private ImmutableList.Builder> start = ImmutableList.builder(); + private List> start = new ArrayList<>(); - private ImmutableList.Builder> started = ImmutableList.builder(); + private List> started = new ArrayList<>(); - private ImmutableList.Builder> shutdown = ImmutableList.builder(); + private List> shutdown = new ArrayList<>(); private Map> xss = new HashMap<>(); @@ -393,7 +394,7 @@ public String toString() { @Override public List> stopTasks() { - return shutdown.build(); + return Collections.unmodifiableList(shutdown); } @Override @@ -416,12 +417,12 @@ public LifeCycle onStarted(final Throwing.Consumer task) { @Override public List> startTasks() { - return this.start.build(); + return Collections.unmodifiableList(this.start); } @Override public List> startedTasks() { - return this.started.build(); + return Collections.unmodifiableList(this.started); } @Override diff --git a/jooby/src/main/java/org/jooby/Jooby.java b/jooby/src/main/java/org/jooby/Jooby.java index aeb0ad1e..2d26546c 100644 --- a/jooby/src/main/java/org/jooby/Jooby.java +++ b/jooby/src/main/java/org/jooby/Jooby.java @@ -18,9 +18,6 @@ import com.google.common.base.Joiner; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import com.google.common.escape.Escaper; import com.google.common.html.HtmlEscapers; import com.google.common.net.UrlEscapers; @@ -118,6 +115,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; @@ -513,7 +511,7 @@ static class MvcClass implements Route.Props { String path; - ImmutableMap.Builder attrs = ImmutableMap.builder(); + Map attrs = new LinkedHashMap<>(); private List consumes; @@ -583,7 +581,7 @@ public MvcClass renderer(final String name) { } public Route.Definition apply(final Route.Definition route) { - attrs.build().forEach(route::attr); + attrs.forEach(route::attr); if (name != null) { route.name(name); } @@ -2822,9 +2820,9 @@ private Injector bootstrap(final Config args, // clear bag and freeze it this.bag.clear(); - this.bag = ImmutableSet.of(); + this.bag = Collections.emptySet(); this.executors.clear(); - this.executors = ImmutableList.of(); + this.executors = Collections.emptyList(); return injector; } @@ -3024,7 +3022,11 @@ private Config buildConfig(final Config source, final Config args, // set module config Config moduleStack = ConfigFactory.empty(); - for (Config module : ImmutableList.copyOf(modules).reverse()) { + // FIXME: Java 21 has better .reverse() support. + // Not using it yet since I don't think we really need hard 'Java 21' minimum, yet + List reversedModules = new ArrayList<>(modules); + Collections.reverse(reversedModules); + for (Config module : reversedModules) { moduleStack = moduleStack.withFallback(module); } @@ -3162,7 +3164,7 @@ private Config defaultConfig(final Config conf, final String cpath) { if (!conf.hasPath("application.lang")) { locales = Optional.ofNullable(this.languages) .map(langs -> LocaleUtils.parse(Joiner.on(",").join(langs))) - .orElse(ImmutableList.of(Locale.getDefault())); + .orElse(List.of(Locale.getDefault())); } else { locales = LocaleUtils.parse(conf.getString("application.lang")); } @@ -3303,7 +3305,7 @@ static String logback(final Config conf) { logback = conf.getString("logback.configurationFile"); } else { String env = conf.hasPath("application.env") ? conf.getString("application.env") : null; - ImmutableList.Builder files = ImmutableList.builder(); + List files = new ArrayList<>(); // TODO: sanitization of arguments File userdir = new File(System.getProperty("user.dir")); File confdir = new File(userdir, "conf"); @@ -3313,8 +3315,7 @@ static String logback(final Config conf) { } files.add(new File(userdir, "logback.xml")); files.add(new File(confdir, "logback.xml")); - logback = files.build() - .stream() + logback = files.stream() .filter(File::exists) .map(File::getAbsolutePath) .findFirst() diff --git a/jooby/src/main/java/org/jooby/MediaType.java b/jooby/src/main/java/org/jooby/MediaType.java index c23881b7..a9d1773a 100644 --- a/jooby/src/main/java/org/jooby/MediaType.java +++ b/jooby/src/main/java/org/jooby/MediaType.java @@ -28,8 +28,6 @@ import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; @@ -79,7 +77,7 @@ public static class Matcher { * @return True if the matcher matches the given media type. */ public boolean matches(final MediaType candidate) { - return doFirst(ImmutableList.of(candidate)).isPresent(); + return doFirst(List.of(candidate)).isPresent(); } /** @@ -117,7 +115,7 @@ public boolean matches(final List candidates) { * @return A first most relevant media type or an empty optional. */ public Optional first(final MediaType candidate) { - return first(ImmutableList.of(candidate)); + return first(List.of(candidate)); } /** @@ -160,10 +158,10 @@ public Optional first(final List candidates) { */ public List filter(final List types) { checkArgument(types != null && types.size() > 0, "Media types are required"); - ImmutableList.Builder result = ImmutableList.builder(); + List result = new ArrayList<>(); final List sortedTypes; if (types.size() == 1) { - sortedTypes = ImmutableList.of(types.get(0)); + sortedTypes = List.of(types.get(0)); } else { sortedTypes = new ArrayList<>(types); Collections.sort(sortedTypes); @@ -175,7 +173,7 @@ public List filter(final List types) { } } } - return result.build(); + return Collections.unmodifiableList(result); } /** @@ -202,7 +200,7 @@ private Optional doFirst(final List candidates) { /** * Default parameters. */ - private static final Map DEFAULT_PARAMS = ImmutableMap.of("q", "1"); + private static final Map DEFAULT_PARAMS = Map.of("q", "1"); /** * A JSON media type. @@ -247,7 +245,7 @@ private Optional doFirst(final List candidates) { public static final MediaType all = new MediaType("*", "*"); /** Any media type. */ - public static final List ALL = ImmutableList.of(MediaType.all); + public static final List ALL = List.of(MediaType.all); /** Form multipart-data media type. */ public static final MediaType multipart = new MediaType("multipart", "form-data"); @@ -300,15 +298,15 @@ private Optional doFirst(final List candidates) { private static final ConcurrentHashMap> cache = new ConcurrentHashMap<>(); static { - cache.put("html", ImmutableList.of(html)); - cache.put("json", ImmutableList.of(json)); - cache.put("css", ImmutableList.of(css)); - cache.put("js", ImmutableList.of(js)); - cache.put("octetstream", ImmutableList.of(octetstream)); - cache.put("form", ImmutableList.of(form)); - cache.put("multipart", ImmutableList.of(multipart)); - cache.put("xml", ImmutableList.of(xml)); - cache.put("plain", ImmutableList.of(plain)); + cache.put("html", List.of(html)); + cache.put("json", List.of(json)); + cache.put("css", List.of(css)); + cache.put("js", List.of(js)); + cache.put("octetstream", List.of(octetstream)); + cache.put("form", List.of(form)); + cache.put("multipart", List.of(multipart)); + cache.put("xml", List.of(xml)); + cache.put("plain", List.of(plain)); cache.put("*", ALL); } @@ -326,7 +324,7 @@ private Optional doFirst(final List candidates) { private MediaType(final String type, final String subtype, final Map parameters) { this.type = requireNonNull(type, "A mime type is required."); this.subtype = requireNonNull(subtype, "A mime subtype is required."); - this.params = ImmutableMap.copyOf(requireNonNull(parameters, "Parameters are required.")); + this.params = Collections.unmodifiableMap(new LinkedHashMap<>(requireNonNull(parameters, "Parameters are required."))); this.wildcardType = "*".equals(type); this.wildcardSubtype = "*".equals(subtype); this.name = type + "/" + subtype; @@ -576,7 +574,7 @@ public static List parse(final String value) throws Err.BadMediaType * @return A media type matcher. */ public static Matcher matcher(final MediaType acceptable) { - return matcher(ImmutableList.of(acceptable)); + return matcher(List.of(acceptable)); } /** diff --git a/jooby/src/main/java/org/jooby/Request.java b/jooby/src/main/java/org/jooby/Request.java index 470a6000..35e183d4 100644 --- a/jooby/src/main/java/org/jooby/Request.java +++ b/jooby/src/main/java/org/jooby/Request.java @@ -15,8 +15,6 @@ */ package org.jooby; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import com.google.common.net.UrlEscapers; import com.google.inject.Key; import com.google.inject.TypeLiteral; @@ -27,6 +25,7 @@ import jakarta.annotation.Nullable; import java.io.IOException; import java.nio.charset.Charset; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Locale.LanguageRange; @@ -680,7 +679,7 @@ default boolean is(final List types) { */ @Nonnull default Optional accepts(final MediaType... types) { - return accepts(ImmutableList.copyOf(types)); + return accepts(List.of(types)); } /** @@ -1310,7 +1309,7 @@ default Request set(final TypeLiteral type, final Object value) { */ @Nonnull default Request push(final String path) { - return push(path, ImmutableMap.of()); + return push(path, Collections.emptyMap()); } /** diff --git a/jooby/src/main/java/org/jooby/Response.java b/jooby/src/main/java/org/jooby/Response.java index fd520350..f043e77b 100644 --- a/jooby/src/main/java/org/jooby/Response.java +++ b/jooby/src/main/java/org/jooby/Response.java @@ -21,12 +21,11 @@ import java.io.FileInputStream; import java.io.InputStream; import java.nio.charset.Charset; +import java.util.List; import java.util.Optional; import org.jooby.Cookie.Definition; -import com.google.common.collect.ImmutableList; - import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -384,7 +383,7 @@ default Response cookie(final String name, final String value) { */ @Nonnull default Response header(final String name, final Object... values) { - return header(name, ImmutableList.builder().add(values).build()); + return header(name, List.of(values)); } /** diff --git a/jooby/src/main/java/org/jooby/Result.java b/jooby/src/main/java/org/jooby/Result.java index 78d229eb..d54bc455 100644 --- a/jooby/src/main/java/org/jooby/Result.java +++ b/jooby/src/main/java/org/jooby/Result.java @@ -17,6 +17,7 @@ import static java.util.Objects.requireNonNull; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -24,8 +25,6 @@ import java.util.function.Supplier; import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -107,7 +106,7 @@ public Optional ifGet(final List types) { public T get(final List types) { Supplier provider = MediaType .matcher(types) - .first(ImmutableList.copyOf(data.keySet())) + .first(List.copyOf(data.keySet())) .map(it -> data.remove(it)) .orElseThrow( () -> new Err(Status.NOT_ACCEPTABLE, Joiner.on(", ").join(types))); @@ -131,7 +130,7 @@ protected Result clone() { } - private static Map NO_HEADERS = ImmutableMap.of(); + private static Map NO_HEADERS = Collections.emptyMap(); /** Response headers. */ protected Map headers = NO_HEADERS; @@ -326,7 +325,7 @@ public Result header(final String name, final Object... values) { requireNonNull(name, "Header's name is required."); requireNonNull(values, "Header's values are required."); - return header(name, ImmutableList.copyOf(values)); + return header(name, List.of(values)); } /** @@ -356,10 +355,9 @@ protected Result clone() { } private void setHeader(final String name, final Object val) { - headers = ImmutableMap. builder() - .putAll(headers) - .put(name, val) - .build(); + var newMap = new LinkedHashMap<>(headers); + newMap.put(name, val); + headers = Collections.unmodifiableMap(newMap); } } diff --git a/jooby/src/main/java/org/jooby/Route.java b/jooby/src/main/java/org/jooby/Route.java index 68ddaa78..7b6f5fbc 100644 --- a/jooby/src/main/java/org/jooby/Route.java +++ b/jooby/src/main/java/org/jooby/Route.java @@ -18,9 +18,6 @@ import com.google.common.base.CaseFormat; import static com.google.common.base.Preconditions.checkArgument; import com.google.common.base.Strings; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; import com.google.common.primitives.Primitives; import com.google.inject.Key; import com.google.inject.TypeLiteral; @@ -723,7 +720,7 @@ class Definition implements Props { private List excludes = Collections.emptyList(); - private Map attributes = ImmutableMap.of(); + private Map attributes = Collections.emptyMap(); private Mapper mapper; @@ -928,10 +925,9 @@ public Definition attr(final String name, final Object value) { requireNonNull(value, "Attribute value is required."); if (valid(value)) { - attributes = ImmutableMap.builder() - .putAll(attributes) - .put(name, value) - .build(); + var newMap = new java.util.LinkedHashMap<>(attributes); + newMap.put(name, value); + attributes = Collections.unmodifiableMap(newMap); } return this; } @@ -1102,10 +1098,10 @@ public boolean canProduce(final String... types) { public Definition consumes(final List types) { checkArgument(types != null && types.size() > 0, "Consumes types are required"); if (types.size() > 1) { - this.consumes = Lists.newLinkedList(types); + this.consumes = new java.util.LinkedList<>(types); Collections.sort(this.consumes); } else { - this.consumes = ImmutableList.of(types.get(0)); + this.consumes = List.of(types.get(0)); } return this; } @@ -1114,10 +1110,10 @@ public Definition consumes(final List types) { public Definition produces(final List types) { checkArgument(types != null && types.size() > 0, "Produces types are required"); if (types.size() > 1) { - this.produces = Lists.newLinkedList(types); + this.produces = new java.util.LinkedList<>(types); Collections.sort(this.produces); } else { - this.produces = ImmutableList.of(types.get(0)); + this.produces = List.of(types.get(0)); } return this; } @@ -2034,17 +2030,15 @@ default void next(final Request req, final Response rsp) throws Throwable { /** * Well known HTTP methods. */ - List METHODS = ImmutableList.builder() - .add(GET, - POST, - PUT, - DELETE, - PATCH, - HEAD, - CONNECT, - OPTIONS, - TRACE) - .build(); + List METHODS = List.of(GET, + POST, + PUT, + DELETE, + PATCH, + HEAD, + CONNECT, + OPTIONS, + TRACE); /** * @return Current request path. diff --git a/jooby/src/main/java/org/jooby/Sse.java b/jooby/src/main/java/org/jooby/Sse.java index fb34fcf5..b5e3d8e2 100644 --- a/jooby/src/main/java/org/jooby/Sse.java +++ b/jooby/src/main/java/org/jooby/Sse.java @@ -15,7 +15,6 @@ */ package org.jooby; -import com.google.common.collect.ImmutableList; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.TypeLiteral; @@ -535,7 +534,7 @@ public Sse() { protected void handshake(final Request req, final Runnable handler) throws Exception { this.injector = req.require(Injector.class); - this.renderers = ImmutableList.copyOf(injector.getInstance(Renderer.KEY)); + this.renderers = List.copyOf(injector.getInstance(Renderer.KEY)); this.produces = req.route().produces(); this.locals = req.attributes(); this.lastEventId = req.header("Last-Event-ID"); @@ -869,7 +868,7 @@ protected boolean shouldClose(final Throwable ex) { } private CompletableFuture> send(final Event event) { - List produces = event.type().>map(ImmutableList::of) + List produces = event.type().>map(List::of) .orElse(this.produces); SseRenderer ctx = new SseRenderer(renderers, produces, StandardCharsets.UTF_8, locale, locals); return Try.apply(() -> { diff --git a/jooby/src/main/java/org/jooby/handlers/Cors.java b/jooby/src/main/java/org/jooby/handlers/Cors.java index ad6e1fa0..d056d744 100644 --- a/jooby/src/main/java/org/jooby/handlers/Cors.java +++ b/jooby/src/main/java/org/jooby/handlers/Cors.java @@ -28,7 +28,6 @@ import jakarta.inject.Inject; import jakarta.inject.Named; -import com.google.common.collect.ImmutableList; import com.typesafe.config.Config; /** @@ -69,7 +68,7 @@ private static class Matcher implements Predicate { private boolean wild; public Matcher(final List values, final Predicate predicate) { - this.values = ImmutableList.copyOf(values); + this.values = List.copyOf(values); this.predicate = predicate; this.wild = values.contains("*"); } @@ -281,7 +280,7 @@ public boolean anyHeader() { * @return True if a header is allowed. */ public boolean allowHeader(final String header) { - return allowHeaders(ImmutableList.of(header)); + return allowHeaders(List.of(header)); } /** @@ -384,7 +383,7 @@ public Cors withMaxAge(final int preflightMaxAge) { @SuppressWarnings({"unchecked", "rawtypes" }) private List list(final Object value) { - return value instanceof List ? (List) value : ImmutableList.of(value.toString()); + return value instanceof List ? (List) value : List.of(value.toString()); } private static Matcher> allMatch(final List values) { diff --git a/jooby/src/main/java/org/jooby/handlers/CsrfHandler.java b/jooby/src/main/java/org/jooby/handlers/CsrfHandler.java index 3635a470..848f4bf9 100644 --- a/jooby/src/main/java/org/jooby/handlers/CsrfHandler.java +++ b/jooby/src/main/java/org/jooby/handlers/CsrfHandler.java @@ -29,8 +29,6 @@ import org.jooby.Session; import org.jooby.Status; -import com.google.common.collect.ImmutableSet; - /** *

Cross Site Request Forgery handler

* @@ -82,7 +80,7 @@ */ public class CsrfHandler implements Route.Filter { - private final Set REQUIRE_ON = ImmutableSet.of("POST", "PUT", "DELETE", "PATCH"); + private final Set REQUIRE_ON = Set.of("POST", "PUT", "DELETE", "PATCH"); private String name; diff --git a/jooby/src/main/java/org/jooby/internal/BuiltinParser.java b/jooby/src/main/java/org/jooby/internal/BuiltinParser.java index 7ebc11f6..99ac3f02 100644 --- a/jooby/src/main/java/org/jooby/internal/BuiltinParser.java +++ b/jooby/src/main/java/org/jooby/internal/BuiltinParser.java @@ -22,50 +22,46 @@ import java.time.LocalDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.SortedSet; +import java.util.TreeSet; import java.util.function.Function; -import java.util.function.Supplier; import org.jooby.Parser; -import com.google.common.collect.ImmutableCollection; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSortedSet; import com.google.inject.TypeLiteral; @SuppressWarnings({"unchecked", "rawtypes" }) public enum BuiltinParser implements Parser { Basic { - private final Map, Function> parsers = ImmutableMap - ., Function> builder() - .put(BigDecimal.class, NOT_EMPTY.andThen(BigDecimal::new)) - .put(BigInteger.class, NOT_EMPTY.andThen(BigInteger::new)) - .put(Byte.class, NOT_EMPTY.andThen(Byte::valueOf)) - .put(byte.class, NOT_EMPTY.andThen(Byte::valueOf)) - .put(Double.class, NOT_EMPTY.andThen(Double::valueOf)) - .put(double.class, NOT_EMPTY.andThen(Double::valueOf)) - .put(Float.class, NOT_EMPTY.andThen(Float::valueOf)) - .put(float.class, NOT_EMPTY.andThen(Float::valueOf)) - .put(Integer.class, NOT_EMPTY.andThen(Integer::valueOf)) - .put(int.class, NOT_EMPTY.andThen(Integer::valueOf)) - .put(Long.class, NOT_EMPTY.andThen(this::toLong)) - .put(long.class, NOT_EMPTY.andThen(this::toLong)) - .put(Short.class, NOT_EMPTY.andThen(Short::valueOf)) - .put(short.class, NOT_EMPTY.andThen(Short::valueOf)) - .put(Boolean.class, NOT_EMPTY.andThen(this::toBoolean)) - .put(boolean.class, NOT_EMPTY.andThen(this::toBoolean)) - .put(Character.class, NOT_EMPTY.andThen(this::toCharacter)) - .put(char.class, NOT_EMPTY.andThen(this::toCharacter)) - .put(String.class, this::toString) - .build(); + private final Map, Function> parsers = Map.ofEntries( + Map.entry(BigDecimal.class, NOT_EMPTY.andThen(BigDecimal::new)), + Map.entry(BigInteger.class, NOT_EMPTY.andThen(BigInteger::new)), + Map.entry(Byte.class, NOT_EMPTY.andThen(Byte::valueOf)), + Map.entry(byte.class, NOT_EMPTY.andThen(Byte::valueOf)), + Map.entry(Double.class, NOT_EMPTY.andThen(Double::valueOf)), + Map.entry(double.class, NOT_EMPTY.andThen(Double::valueOf)), + Map.entry(Float.class, NOT_EMPTY.andThen(Float::valueOf)), + Map.entry(float.class, NOT_EMPTY.andThen(Float::valueOf)), + Map.entry(Integer.class, NOT_EMPTY.andThen(Integer::valueOf)), + Map.entry(int.class, NOT_EMPTY.andThen(Integer::valueOf)), + Map.entry(Long.class, NOT_EMPTY.andThen(this::toLong)), + Map.entry(long.class, NOT_EMPTY.andThen(this::toLong)), + Map.entry(Short.class, NOT_EMPTY.andThen(Short::valueOf)), + Map.entry(short.class, NOT_EMPTY.andThen(Short::valueOf)), + Map.entry(Boolean.class, NOT_EMPTY.andThen(this::toBoolean)), + Map.entry(boolean.class, NOT_EMPTY.andThen(this::toBoolean)), + Map.entry(Character.class, NOT_EMPTY.andThen(this::toCharacter)), + Map.entry(char.class, NOT_EMPTY.andThen(this::toCharacter)), + Map.entry(String.class, (Function) this::toString) + ); @Override public Object parse(final TypeLiteral type, final Parser.Context ctx) throws Throwable { @@ -112,14 +108,10 @@ private Long toLong(final String value) { }, Collection { - private final Map, Supplier>> parsers = ImmutableMap., Supplier>> builder() - .put(List.class, ImmutableList.Builder::new) - .put(Set.class, ImmutableSet.Builder::new) - .put(SortedSet.class, ImmutableSortedSet::naturalOrder) - .build(); + private static final Set> SUPPORTED = Set.of(List.class, Set.class, SortedSet.class); private boolean matches(final TypeLiteral toType) { - return parsers.containsKey(toType.getRawType()) + return SUPPORTED.contains(toType.getRawType()) && toType.getType() instanceof ParameterizedType; } @@ -127,13 +119,27 @@ private boolean matches(final TypeLiteral toType) { public Object parse(final TypeLiteral type, final Parser.Context ctx) throws Throwable { if (matches(type)) { return ctx.param(values -> { - ImmutableCollection.Builder builder = parsers.get(type.getRawType()).get(); + Class rawType = type.getRawType(); + java.util.Collection result; + if (SortedSet.class.isAssignableFrom(rawType)) { + result = new TreeSet<>(); + } else if (Set.class.isAssignableFrom(rawType)) { + result = new java.util.LinkedHashSet<>(); + } else { + result = new ArrayList<>(); + } TypeLiteral paramType = TypeLiteral.get(((ParameterizedType) type.getType()) .getActualTypeArguments()[0]); for (Object value : values) { - builder.add(ctx.next(paramType, value)); + result.add(ctx.next(paramType, value)); + } + if (SortedSet.class.isAssignableFrom(rawType)) { + return Collections.unmodifiableSortedSet((SortedSet) result); + } else if (Set.class.isAssignableFrom(rawType)) { + return Collections.unmodifiableSet((Set) result); + } else { + return Collections.unmodifiableList((List) result); } - return builder.build(); }); } else { return ctx.next(); @@ -196,7 +202,7 @@ Object toEnum(final Class type, final String value) { Bytes { @Override public Object parse(final TypeLiteral type, final Parser.Context ctx) throws Throwable { - if (type.getRawType() == byte[].class) { + if (byte[].class.equals(type.getRawType())) { return ctx.body(body -> body.bytes()); } return ctx.next(); diff --git a/jooby/src/main/java/org/jooby/internal/HttpHandlerImpl.java b/jooby/src/main/java/org/jooby/internal/HttpHandlerImpl.java index 931ad7cb..946665b4 100644 --- a/jooby/src/main/java/org/jooby/internal/HttpHandlerImpl.java +++ b/jooby/src/main/java/org/jooby/internal/HttpHandlerImpl.java @@ -20,7 +20,6 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import com.google.common.collect.Sets; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.name.Names; @@ -484,7 +483,7 @@ private static Err handle405(final Set routeDefs, final String private static List alternative(final Set routeDefs, final String verb, final String uri) { List routes = new LinkedList<>(); - Set verbs = Sets.newHashSet(Route.METHODS); + Set verbs = new java.util.HashSet<>(Route.METHODS); verbs.remove(verb); for (String alt : verbs) { findRoutes(routeDefs, alt, uri, MediaType.all, MediaType.ALL) diff --git a/jooby/src/main/java/org/jooby/internal/MutantImpl.java b/jooby/src/main/java/org/jooby/internal/MutantImpl.java index 56ef6594..5a97b9a7 100644 --- a/jooby/src/main/java/org/jooby/internal/MutantImpl.java +++ b/jooby/src/main/java/org/jooby/internal/MutantImpl.java @@ -15,7 +15,6 @@ */ package org.jooby.internal; -import com.google.common.collect.ImmutableMap; import com.google.inject.TypeLiteral; import org.jooby.Err; import org.jooby.MediaType; @@ -95,7 +94,7 @@ public Map toMap() { if (data instanceof Map) { return (Map) data; } - return ImmutableMap.of((String) md()[0], this); + return Map.of((String) md()[0], this); } @SuppressWarnings("rawtypes") diff --git a/jooby/src/main/java/org/jooby/internal/RequestImpl.java b/jooby/src/main/java/org/jooby/internal/RequestImpl.java index 50ed8cf8..431b4c58 100644 --- a/jooby/src/main/java/org/jooby/internal/RequestImpl.java +++ b/jooby/src/main/java/org/jooby/internal/RequestImpl.java @@ -15,7 +15,6 @@ */ package org.jooby.internal; -import com.google.common.collect.ImmutableList; import com.google.inject.Injector; import com.google.inject.Key; import com.typesafe.config.Config; @@ -248,8 +247,8 @@ public Map headers() { public Mutant cookie(final String name) { List values = req.cookies().stream().filter(c -> c.name().equalsIgnoreCase(name)) .findFirst() - .map(cookie -> ImmutableList.of(cookie.value().orElse(""))) - .orElse(ImmutableList.of()); + .map(cookie -> List.of(cookie.value().orElse(""))) + .orElse(Collections.emptyList()); return new MutantImpl(require(ParserExecutor.class), new StrParamReferenceImpl("cookie", name, values)); @@ -298,12 +297,12 @@ public long length() { public List locales( final BiFunction, List, List> filter) { return lang.map(h -> filter.apply(LocaleUtils.range(h), locales)) - .orElseGet(() -> filter.apply(ImmutableList.of(), locales)); + .orElseGet(() -> filter.apply(Collections.emptyList(), locales)); } @Override public Locale locale(final BiFunction, List, Locale> filter) { - Supplier def = () -> filter.apply(ImmutableList.of(), locales); + Supplier def = () -> filter.apply(Collections.emptyList(), locales); // don't fail on bad Accept-Language header, just fallback to default locale. return lang.map(h -> Try.apply(() -> filter.apply(LocaleUtils.range(h), locales)).orElseGet(def)) .orElseGet(def); diff --git a/jooby/src/main/java/org/jooby/internal/ResponseImpl.java b/jooby/src/main/java/org/jooby/internal/ResponseImpl.java index 67515f2f..01fd282d 100644 --- a/jooby/src/main/java/org/jooby/internal/ResponseImpl.java +++ b/jooby/src/main/java/org/jooby/internal/ResponseImpl.java @@ -15,7 +15,6 @@ */ package org.jooby.internal; -import com.google.common.collect.ImmutableList; import static java.util.Objects.requireNonNull; import org.jooby.Asset; import org.jooby.Cookie; @@ -347,7 +346,7 @@ public void send(final Result result) throws Throwable { /** * Do we need to figure it out Content-Length? */ - List produces = this.type == null ? route.produces() : ImmutableList.of(type); + List produces = this.type == null ? route.produces() : List.of(type); Object value = finalResult.get(produces); if (value != null) { diff --git a/jooby/src/main/java/org/jooby/internal/RouteMetadata.java b/jooby/src/main/java/org/jooby/internal/RouteMetadata.java index 3ee0a058..de0ec395 100644 --- a/jooby/src/main/java/org/jooby/internal/RouteMetadata.java +++ b/jooby/src/main/java/org/jooby/internal/RouteMetadata.java @@ -25,6 +25,7 @@ import org.jooby.Env; import org.jooby.funzy.Try; +import org.killbill.commons.utils.io.Closeables; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Label; @@ -32,11 +33,9 @@ import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; -import com.google.common.base.Throwables; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import com.google.common.io.Closeables; import com.google.common.io.Resources; import com.google.common.util.concurrent.UncheckedExecutionException; diff --git a/jooby/src/main/java/org/jooby/internal/SessionImpl.java b/jooby/src/main/java/org/jooby/internal/SessionImpl.java index 5dbb6401..bc92031e 100644 --- a/jooby/src/main/java/org/jooby/internal/SessionImpl.java +++ b/jooby/src/main/java/org/jooby/internal/SessionImpl.java @@ -29,7 +29,6 @@ */ package org.jooby.internal; -import com.google.common.collect.ImmutableList; import static java.util.Objects.requireNonNull; import org.jooby.Mutant; import org.jooby.Session; @@ -153,7 +152,7 @@ public long expiryAt() { @Override public Mutant get(final String name) { String value = attributes.get(name); - List values = value == null ? Collections.emptyList() : ImmutableList.of(value); + List values = value == null ? Collections.emptyList() : List.of(value); return new MutantImpl(resolver, new StrParamReferenceImpl("session attribute", name, values)); } @@ -181,7 +180,7 @@ public Mutant unset(final String name) { String value = attributes.remove(name); List values = Collections.emptyList(); if (value != null) { - values = ImmutableList.of(value); + values = List.of(value); dirty = true; } return new MutantImpl(resolver, new StrParamReferenceImpl("session attribute", name, values)); diff --git a/jooby/src/main/java/org/jooby/internal/URLAsset.java b/jooby/src/main/java/org/jooby/internal/URLAsset.java index 5bf057aa..d883f033 100644 --- a/jooby/src/main/java/org/jooby/internal/URLAsset.java +++ b/jooby/src/main/java/org/jooby/internal/URLAsset.java @@ -27,9 +27,7 @@ import org.jooby.Asset; import org.jooby.MediaType; - -import com.google.common.io.Closeables; -import org.jooby.funzy.Try; +import org.killbill.commons.utils.io.Closeables; public class URLAsset implements Asset { diff --git a/jooby/src/main/java/org/jooby/internal/WebSocketImpl.java b/jooby/src/main/java/org/jooby/internal/WebSocketImpl.java index 269f4646..b2698546 100644 --- a/jooby/src/main/java/org/jooby/internal/WebSocketImpl.java +++ b/jooby/src/main/java/org/jooby/internal/WebSocketImpl.java @@ -15,7 +15,6 @@ */ package org.jooby.internal; -import com.google.common.collect.ImmutableList; import com.google.inject.Injector; import com.google.inject.Key; @@ -208,7 +207,7 @@ public void connect(final Injector injector, final Request req, final NativeWebS this.injector = requireNonNull(injector, "Injector required."); this.ws = requireNonNull(ws, "WebSocket is required."); this.locale = req.locale(); - renderers = ImmutableList.copyOf(injector.getInstance(Renderer.KEY)); + renderers = List.copyOf(injector.getInstance(Renderer.KEY)); /** * Bind callbacks @@ -220,7 +219,7 @@ public void connect(final Injector injector, final Request req, final NativeWebS ws.onTextMessage(message -> Try .run(sync(() -> messageCallback.onMessage( new MutantImpl(injector.getInstance(ParserExecutor.class), consumes, - new StrParamReferenceImpl("body", "message", ImmutableList.of(message)))))) + new StrParamReferenceImpl("body", "message", List.of(message)))))) .onFailure(this::handleErr)); ws.onCloseMessage((code, reason) -> { diff --git a/jooby/src/main/java/org/jooby/internal/WebSocketRendererContext.java b/jooby/src/main/java/org/jooby/internal/WebSocketRendererContext.java index b6f9b229..f503ea9e 100644 --- a/jooby/src/main/java/org/jooby/internal/WebSocketRendererContext.java +++ b/jooby/src/main/java/org/jooby/internal/WebSocketRendererContext.java @@ -29,8 +29,6 @@ import org.jooby.WebSocket.SuccessCallback; import org.jooby.spi.NativeWebSocket; -import com.google.common.collect.ImmutableList; - public class WebSocketRendererContext extends AbstractRendererContext { private NativeWebSocket ws; @@ -44,7 +42,7 @@ public class WebSocketRendererContext extends AbstractRendererContext { public WebSocketRendererContext(final List renderers, final NativeWebSocket ws, final MediaType type, final Charset charset, Locale locale, final SuccessCallback success, final OnError err) { - super(renderers, ImmutableList.of(type), charset, locale, Collections.emptyMap()); + super(renderers, List.of(type), charset, locale, Collections.emptyMap()); this.ws = ws; this.type = type; this.success = success; diff --git a/jooby/src/main/java/org/jooby/internal/WsBinaryMessage.java b/jooby/src/main/java/org/jooby/internal/WsBinaryMessage.java index 1a1c2907..2a28eb23 100644 --- a/jooby/src/main/java/org/jooby/internal/WsBinaryMessage.java +++ b/jooby/src/main/java/org/jooby/internal/WsBinaryMessage.java @@ -31,8 +31,7 @@ import org.jooby.Mutant; import org.jooby.Status; -import com.google.common.base.Charsets; -import com.google.common.collect.ImmutableMap; +import java.nio.charset.StandardCharsets; import com.google.inject.TypeLiteral; public class WsBinaryMessage implements Mutant { @@ -132,14 +131,14 @@ public T to(final TypeLiteral type, final MediaType mtype) { return (T) new ByteArrayInputStream(buffer.array()); } if (rawType == Reader.class) { - return (T) new InputStreamReader(new ByteArrayInputStream(buffer.array()), Charsets.UTF_8); + return (T) new InputStreamReader(new ByteArrayInputStream(buffer.array()), StandardCharsets.UTF_8); } throw typeError(rawType); } @Override public Map toMap() { - return ImmutableMap.of("message", this); + return Map.of("message", this); } @Override diff --git a/jooby/src/main/java/org/jooby/internal/mvc/MvcRoutes.java b/jooby/src/main/java/org/jooby/internal/mvc/MvcRoutes.java index 35b4aef1..d8240123 100644 --- a/jooby/src/main/java/org/jooby/internal/mvc/MvcRoutes.java +++ b/jooby/src/main/java/org/jooby/internal/mvc/MvcRoutes.java @@ -16,7 +16,6 @@ package org.jooby.internal.mvc; import com.google.common.base.CaseFormat; -import com.google.common.collect.ImmutableSet; import org.jooby.Env; import org.jooby.MediaType; import org.jooby.Route; @@ -43,8 +42,10 @@ import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -57,17 +58,29 @@ public class MvcRoutes { private static final String[] EMPTY = new String[0]; - private static final Set> VERBS = ImmutableSet.of(GET.class, - POST.class, PUT.class, DELETE.class, PATCH.class, HEAD.class, OPTIONS.class, TRACE.class, - CONNECT.class); + private static final Set> VERBS; + static { + Set> verbs = new LinkedHashSet<>(); + verbs.add(GET.class); + verbs.add(POST.class); + verbs.add(PUT.class); + verbs.add(DELETE.class); + verbs.add(PATCH.class); + verbs.add(HEAD.class); + verbs.add(OPTIONS.class); + verbs.add(TRACE.class); + verbs.add(CONNECT.class); + VERBS = Collections.unmodifiableSet(verbs); + } - private static final Set> IGNORE = ImmutableSet - .>builder() - .addAll(VERBS) - .add(Path.class) - .add(Produces.class) - .add(Consumes.class) - .build(); + private static final Set> IGNORE; + static { + Set> ignore = new java.util.LinkedHashSet<>(VERBS); + ignore.add(Path.class); + ignore.add(Produces.class); + ignore.add(Consumes.class); + IGNORE = Collections.unmodifiableSet(ignore); + } public static List routes(final Env env, final RouteMetadata classInfo, final String rpath, boolean caseSensitiveRouting, final Class routeClass) { diff --git a/jooby/src/main/java/org/jooby/internal/mvc/RequestParam.java b/jooby/src/main/java/org/jooby/internal/mvc/RequestParam.java index d4ce8a87..bf670fd4 100644 --- a/jooby/src/main/java/org/jooby/internal/mvc/RequestParam.java +++ b/jooby/src/main/java/org/jooby/internal/mvc/RequestParam.java @@ -16,8 +16,6 @@ package org.jooby.internal.mvc; import com.google.common.base.Strings; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap.Builder; import com.google.inject.TypeLiteral; import com.google.inject.util.Types; import org.jooby.Cookie; @@ -38,6 +36,8 @@ import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Parameter; import java.lang.reflect.Type; +import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -62,7 +62,7 @@ private interface GetValue { private static final Map injector; static { - Builder builder = ImmutableMap. builder(); + Map builder = new LinkedHashMap<>(); /** * Body */ @@ -144,7 +144,7 @@ private interface GetValue { return param.optional ? req.ifFlash(param.name) : req.flash(param.name); }); - injector = builder.build(); + injector = Collections.unmodifiableMap(builder); } public final String name; diff --git a/jooby/src/main/java/org/jooby/internal/mvc/RequestParamProviderImpl.java b/jooby/src/main/java/org/jooby/internal/mvc/RequestParamProviderImpl.java index 1788bd0a..fab7577b 100644 --- a/jooby/src/main/java/org/jooby/internal/mvc/RequestParamProviderImpl.java +++ b/jooby/src/main/java/org/jooby/internal/mvc/RequestParamProviderImpl.java @@ -19,12 +19,10 @@ import java.lang.reflect.Executable; import java.lang.reflect.Parameter; +import java.util.ArrayList; import java.util.Collections; import java.util.List; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList.Builder; - public class RequestParamProviderImpl implements RequestParamProvider { private RequestParamNameProviderImpl provider; @@ -40,11 +38,11 @@ public List parameters(final Executable exec) { return Collections.emptyList(); } - Builder builder = ImmutableList.builder(); + List builder = new ArrayList<>(); for (Parameter parameter : parameters) { builder.add(new RequestParam(parameter, provider.name(parameter))); } - return builder.build(); + return Collections.unmodifiableList(builder); } } diff --git a/jooby/src/main/java/org/jooby/internal/parser/BeanParser.java b/jooby/src/main/java/org/jooby/internal/parser/BeanParser.java index fd9280e4..9c05c251 100644 --- a/jooby/src/main/java/org/jooby/internal/parser/BeanParser.java +++ b/jooby/src/main/java/org/jooby/internal/parser/BeanParser.java @@ -16,7 +16,6 @@ package org.jooby.internal.parser; import com.google.common.primitives.Primitives; -import com.google.common.reflect.Reflection; import com.google.inject.TypeLiteral; import org.jooby.Err; import org.jooby.Mutant; @@ -29,6 +28,7 @@ import org.jooby.internal.parser.bean.BeanPlan; import org.jooby.funzy.Try; +import java.lang.reflect.Proxy; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -94,7 +94,7 @@ private Object newBean(final Request req, final Response rsp, final Route.Chain private Object newBeanInterface(final Request req, final Response rsp, final Route.Chain chain, final Class beanType) { - return Reflection.newProxy(beanType, (proxy, method, args) -> { + return Proxy.newProxyInstance(beanType.getClassLoader(), new Class[]{beanType}, (proxy, method, args) -> { StringBuilder name = new StringBuilder(method.getName() .replace("get", "") .replace("is", "")); diff --git a/jooby/src/main/java/org/jooby/internal/parser/ParserBuilder.java b/jooby/src/main/java/org/jooby/internal/parser/ParserBuilder.java index ba3bc77a..59a6375d 100644 --- a/jooby/src/main/java/org/jooby/internal/parser/ParserBuilder.java +++ b/jooby/src/main/java/org/jooby/internal/parser/ParserBuilder.java @@ -15,6 +15,8 @@ */ package org.jooby.internal.parser; +import java.util.Collections; +import java.util.LinkedHashMap; import java.util.Map; import org.jooby.Mutant; @@ -25,14 +27,12 @@ import org.jooby.internal.EmptyBodyReference; import org.jooby.internal.StrParamReferenceImpl; -import com.google.common.collect.ImmutableMap; import com.google.inject.TypeLiteral; @SuppressWarnings("rawtypes") public class ParserBuilder implements Parser.Builder { - private ImmutableMap.Builder, Parser.Callback> strategies = ImmutableMap - .builder(); + private Map, Parser.Callback> strategies = new LinkedHashMap<>(); public final TypeLiteral toType; @@ -92,7 +92,7 @@ public Builder ifparams(final Callback> callback) { @SuppressWarnings("unchecked") public Object parse() throws Throwable { - Map, Callback> map = strategies.build(); + Map, Callback> map = Collections.unmodifiableMap(strategies); Callback callback = map.get(type); if (callback == null) { return ctx.next(toType, value); diff --git a/jooby/src/main/java/org/jooby/internal/parser/ParserExecutor.java b/jooby/src/main/java/org/jooby/internal/parser/ParserExecutor.java index 3dd8811a..f3b1fa3e 100644 --- a/jooby/src/main/java/org/jooby/internal/parser/ParserExecutor.java +++ b/jooby/src/main/java/org/jooby/internal/parser/ParserExecutor.java @@ -32,7 +32,6 @@ import org.jooby.internal.StatusCodeProvider; import org.jooby.internal.StrParamReferenceImpl; -import com.google.common.collect.ImmutableList; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.TypeLiteral; @@ -51,7 +50,7 @@ public class ParserExecutor { public ParserExecutor(final Injector injector, final Set parsers, final StatusCodeProvider sc) { this.injector = injector; - this.parsers = ImmutableList.copyOf(parsers); + this.parsers = List.copyOf(parsers); this.sc = sc; } @@ -155,7 +154,7 @@ private Object wrap(final Object nextval, final Object value) { if (nextval instanceof String) { ParamReference pref = (ParamReference) value; return new StrParamReferenceImpl(pref.type(), pref.name(), - ImmutableList.of((String) nextval)); + List.of((String) nextval)); } return nextval; } diff --git a/jooby/src/main/java/org/jooby/internal/ssl/PemReader.java b/jooby/src/main/java/org/jooby/internal/ssl/PemReader.java index 0aa867e9..37465987 100644 --- a/jooby/src/main/java/org/jooby/internal/ssl/PemReader.java +++ b/jooby/src/main/java/org/jooby/internal/ssl/PemReader.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.security.KeyException; import java.security.KeyStore; import java.security.cert.CertificateException; @@ -28,7 +29,6 @@ import java.util.regex.Pattern; import com.google.common.io.BaseEncoding; -import com.google.common.io.Files; /** * Reads a PEM file and converts it into a list of DERs so that they are imported into a @@ -53,7 +53,7 @@ final class PemReader { static List readCertificates(final File file) throws CertificateException, IOException { - String content = Files.toString(file, StandardCharsets.US_ASCII); + String content = Files.readString(file.toPath(), StandardCharsets.US_ASCII); BaseEncoding base64 = base64(); List certs = new ArrayList(); @@ -78,7 +78,7 @@ private static BaseEncoding base64() { } static ByteBuffer readPrivateKey(final File file) throws KeyException, IOException { - String content = Files.toString(file, StandardCharsets.US_ASCII); + String content = Files.readString(file.toPath(), StandardCharsets.US_ASCII); Matcher m = KEY_PATTERN.matcher(content); if (!m.find()) { diff --git a/jooby/src/main/java/org/jooby/servlet/ServletServletRequest.java b/jooby/src/main/java/org/jooby/servlet/ServletServletRequest.java index ca75d8a2..200100f3 100644 --- a/jooby/src/main/java/org/jooby/servlet/ServletServletRequest.java +++ b/jooby/src/main/java/org/jooby/servlet/ServletServletRequest.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URLDecoder; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; @@ -40,8 +41,6 @@ import org.jooby.spi.NativeUpload; import com.google.common.base.Strings; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList.Builder; public class ServletServletRequest implements NativeRequest { @@ -107,11 +106,11 @@ public List paramNames() { } private List toList(final Enumeration enumeration) { - Builder result = ImmutableList.builder(); + List result = new ArrayList<>(); while (enumeration.hasMoreElements()) { result.add(enumeration.nextElement()); } - return result.build(); + return Collections.unmodifiableList(result); } @Override @@ -152,7 +151,7 @@ public List headerNames() { public List cookies() { jakarta.servlet.http.Cookie[] cookies = req.getCookies(); if (cookies == null) { - return ImmutableList.of(); + return Collections.emptyList(); } return Arrays.stream(cookies) .map(c -> { diff --git a/jooby/src/main/java/org/jooby/servlet/ServletServletResponse.java b/jooby/src/main/java/org/jooby/servlet/ServletServletResponse.java index 7abb8877..20e7cde4 100644 --- a/jooby/src/main/java/org/jooby/servlet/ServletServletResponse.java +++ b/jooby/src/main/java/org/jooby/servlet/ServletServletResponse.java @@ -15,7 +15,6 @@ */ package org.jooby.servlet; -import com.google.common.collect.ImmutableList; import com.google.common.io.ByteStreams; import static java.util.Objects.requireNonNull; @@ -55,7 +54,7 @@ public List headers(final String name) { if (headers == null || headers.size() == 0) { return Collections.emptyList(); } - return ImmutableList.copyOf(headers); + return List.copyOf(headers); } @Override diff --git a/jooby/src/main/java/org/jooby/servlet/ServletUpload.java b/jooby/src/main/java/org/jooby/servlet/ServletUpload.java index 8f3c0c2d..a1fcd6e3 100644 --- a/jooby/src/main/java/org/jooby/servlet/ServletUpload.java +++ b/jooby/src/main/java/org/jooby/servlet/ServletUpload.java @@ -27,7 +27,6 @@ import org.jooby.spi.NativeUpload; -import com.google.common.collect.ImmutableList; public class ServletUpload implements NativeUpload { @@ -61,7 +60,7 @@ public List headers(final String name) { if (headers == null) { return Collections.emptyList(); } - return ImmutableList.copyOf(headers); + return List.copyOf(headers); } @Override diff --git a/jooby/src/main/java/org/jooby/test/MockRouter.java b/jooby/src/main/java/org/jooby/test/MockRouter.java index 0d16bd2e..789bed7e 100644 --- a/jooby/src/main/java/org/jooby/test/MockRouter.java +++ b/jooby/src/main/java/org/jooby/test/MockRouter.java @@ -15,9 +15,6 @@ */ package org.jooby.test; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; -import com.google.common.reflect.Reflection; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.name.Names; @@ -37,8 +34,10 @@ import org.jooby.funzy.Try; import java.lang.reflect.Field; +import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -251,7 +250,7 @@ public class MockRouter { @Override public List routes() { - return ImmutableList.of(); + return Collections.emptyList(); } @Override @@ -435,7 +434,7 @@ private Jooby hackInjector(final Jooby app) { @SuppressWarnings("rawtypes") private static Injector proxyInjector(final ClassLoader loader, final Map registry) { - return Reflection.newProxy(Injector.class, (proxy, method, args) -> { + return (Injector) Proxy.newProxyInstance(Injector.class.getClassLoader(), new Class[]{Injector.class}, (proxy, method, args) -> { if (method.getName().equals("getInstance")) { Key key = (Key) args[0]; Object value = registry.get(key); @@ -445,7 +444,7 @@ private static Injector proxyInjector(final ClassLoader loader, final Map { StackTraceElement[] stacktrace = iex.getStackTrace(); - return Lists.newArrayList(stacktrace).subList(CLEAN_STACK, stacktrace.length); + return new ArrayList<>(Arrays.asList(stacktrace)).subList(CLEAN_STACK, stacktrace.length); }).onSuccess(stacktrace -> iex .setStackTrace(stacktrace.toArray(new StackTraceElement[stacktrace.size()]))); throw iex; @@ -465,8 +464,9 @@ private void traverse(final Class type, final Consumer set) { } } + @SuppressWarnings({"rawtypes", "unchecked"}) private static T empty(final Class type) { - return Reflection.newProxy(type, (proxy, method, args) -> { + return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, (proxy, method, args) -> { throw new UnsupportedOperationException(method.toString()); }); } diff --git a/jooby/src/test/java/org/jooby/CookieCodecTest.java b/jooby/src/test/java/org/jooby/CookieCodecTest.java index 26a4b90f..54965218 100644 --- a/jooby/src/test/java/org/jooby/CookieCodecTest.java +++ b/jooby/src/test/java/org/jooby/CookieCodecTest.java @@ -19,28 +19,31 @@ import org.junit.Test; -import com.google.common.collect.ImmutableMap; +import java.util.LinkedHashMap; +import java.util.Map; public class CookieCodecTest { @Test public void encode() { - assertEquals("success=OK", Cookie.URL_ENCODER.apply(ImmutableMap.of("success", "OK"))); + assertEquals("success=OK", Cookie.URL_ENCODER.apply(Map.of("success", "OK"))); assertEquals("success=semi%3Bcolon", - Cookie.URL_ENCODER.apply(ImmutableMap.of("success", "semi;colon"))); + Cookie.URL_ENCODER.apply(Map.of("success", "semi;colon"))); assertEquals("success=eq%3Duals", - Cookie.URL_ENCODER.apply(ImmutableMap.of("success", "eq=uals"))); + Cookie.URL_ENCODER.apply(Map.of("success", "eq=uals"))); - assertEquals("success=OK&error=404", - Cookie.URL_ENCODER.apply(ImmutableMap.of("success", "OK", "error", "404"))); + Map map = new LinkedHashMap<>(); + map.put("success", "OK"); + map.put("error", "404"); + assertEquals("success=OK&error=404", Cookie.URL_ENCODER.apply(map)); } @Test public void decode() { - assertEquals(ImmutableMap.of("success", "OK"), Cookie.URL_DECODER.apply("success=OK")); - assertEquals(ImmutableMap.of("success", "OK", "foo", "bar"), + assertEquals(Map.of("success", "OK"), Cookie.URL_DECODER.apply("success=OK")); + assertEquals(Map.of("success", "OK", "foo", "bar"), Cookie.URL_DECODER.apply("success=OK&foo=bar")); - assertEquals(ImmutableMap.of("semicolon", "semi;colon"), + assertEquals(Map.of("semicolon", "semi;colon"), Cookie.URL_DECODER.apply("semicolon=semi%3Bcolon")); } } diff --git a/jooby/src/test/java/org/jooby/CorsTest.java b/jooby/src/test/java/org/jooby/CorsTest.java index 2c5fe28e..2b2c8173 100644 --- a/jooby/src/test/java/org/jooby/CorsTest.java +++ b/jooby/src/test/java/org/jooby/CorsTest.java @@ -19,13 +19,13 @@ import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; +import java.util.ArrayList; import java.util.Arrays; import java.util.function.Consumer; import org.jooby.handlers.Cors; import org.junit.Test; -import com.google.common.collect.Lists; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; @@ -138,10 +138,10 @@ private Config baseconf() { .withValue("enabled", fromAnyRef(true)) .withValue("credentials", fromAnyRef(true)) .withValue("maxAge", fromAnyRef("30m")) - .withValue("origin", fromAnyRef(Lists.newArrayList())) - .withValue("exposedHeaders", fromAnyRef(Lists.newArrayList("X"))) - .withValue("allowedMethods", fromAnyRef(Lists.newArrayList())) - .withValue("allowedHeaders", fromAnyRef(Lists.newArrayList())); + .withValue("origin", fromAnyRef(new ArrayList<>())) + .withValue("exposedHeaders", fromAnyRef(new ArrayList<>(Arrays.asList("X")))) + .withValue("allowedMethods", fromAnyRef(new ArrayList<>())) + .withValue("allowedHeaders", fromAnyRef(new ArrayList<>())); return config; } diff --git a/jooby/src/test/java/org/jooby/DefaultErrHandlerTest.java b/jooby/src/test/java/org/jooby/DefaultErrHandlerTest.java index c35792ca..a5de0d7b 100644 --- a/jooby/src/test/java/org/jooby/DefaultErrHandlerTest.java +++ b/jooby/src/test/java/org/jooby/DefaultErrHandlerTest.java @@ -15,8 +15,6 @@ */ package org.jooby; -import com.google.common.collect.ImmutableList; -import com.google.common.escape.Escapers; import com.google.common.html.HtmlEscapers; import com.typesafe.config.Config; import static org.mockito.ArgumentMatchers.any; @@ -32,6 +30,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; @@ -58,7 +57,7 @@ public void handleNoErrMessage() throws Exception { new Err.DefHandler().handle(req, rsp, ex); }, unit -> { Result result = capturedResult.get(); - View view = (View) result.ifGet(ImmutableList.of(MediaType.html)).get(); + View view = (View) result.ifGet(List.of(MediaType.html)).get(); assertEquals("err", view.name()); checkErr(stacktrace, "Server Error(500)", (Map) view.model() .get("err")); @@ -120,7 +119,7 @@ public void handleWithErrMessage() throws Exception { }, unit -> { Result result = capturedResult.get(); - View view = (View) result.ifGet(ImmutableList.of(MediaType.html)).get(); + View view = (View) result.ifGet(List.of(MediaType.html)).get(); assertEquals("err", view.name()); checkErr(stacktrace, "Server Error(500): Something something dark", (Map) view.model() @@ -151,7 +150,7 @@ public void handleWithHtmlErrMessage() throws Exception { }, unit -> { Result result = capturedResult.get(); - View view = (View) result.ifGet(ImmutableList.of(MediaType.html)).get(); + View view = (View) result.ifGet(List.of(MediaType.html)).get(); assertEquals("err", view.name()); checkErr(stacktrace, "Server Error(500): Something something <em>dark</em>", (Map) view.model() diff --git a/jooby/src/test/java/org/jooby/EnvTest.java b/jooby/src/test/java/org/jooby/EnvTest.java index 3eeef1ca..ce5ca379 100644 --- a/jooby/src/test/java/org/jooby/EnvTest.java +++ b/jooby/src/test/java/org/jooby/EnvTest.java @@ -15,7 +15,6 @@ */ package org.jooby; -import com.google.common.collect.ImmutableMap; import com.google.inject.Key; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; @@ -97,7 +96,7 @@ public void resolveMap() { Config config = ConfigFactory.empty(); Env env = Env.DEFAULT.build(config); - assertEquals("foo.bar - foo.bar", env.resolver().source(ImmutableMap.of("var", "foo.bar")) + assertEquals("foo.bar - foo.bar", env.resolver().source(Map.of("var", "foo.bar")) .resolve("${var} - ${var}")); } @@ -107,7 +106,7 @@ public void resolveMapIgnore() { Env env = Env.DEFAULT.build(config); assertEquals("${varx} - ${varx}", - env.resolver().ignoreMissing().source(ImmutableMap.of("var", "foo.bar")) + env.resolver().ignoreMissing().source(Map.of("var", "foo.bar")) .resolve("${varx} - ${varx}")); } diff --git a/jooby/src/test/java/org/jooby/RequestForwardingTest.java b/jooby/src/test/java/org/jooby/RequestForwardingTest.java index 327a1feb..022f295d 100644 --- a/jooby/src/test/java/org/jooby/RequestForwardingTest.java +++ b/jooby/src/test/java/org/jooby/RequestForwardingTest.java @@ -31,8 +31,7 @@ import org.jooby.test.MockUnit; import org.junit.Test; -import com.google.common.base.Charsets; -import com.google.common.collect.ImmutableMap; +import java.nio.charset.StandardCharsets; import com.google.inject.Key; import com.google.inject.TypeLiteral; @@ -415,10 +414,10 @@ public void charset() throws Exception { new MockUnit(Request.class) .expect(unit -> { Request req = unit.get(Request.class); - when(req.charset()).thenReturn(Charsets.UTF_8); + when(req.charset()).thenReturn(StandardCharsets.UTF_8); }) .run(unit -> { - assertEquals(Charsets.UTF_8, new Request.Forwarding(unit.get(Request.class)).charset()); + assertEquals(StandardCharsets.UTF_8, new Request.Forwarding(unit.get(Request.class)).charset()); }); } @@ -675,11 +674,11 @@ public void push() throws Exception { new MockUnit(Request.class) .expect(unit -> { Request req = unit.get(Request.class); - when(req.push("/path", ImmutableMap.of("k", "v"))).thenReturn(req); + when(req.push("/path", Map.of("k", "v"))).thenReturn(req); }) .run(unit -> { Forwarding req = new Request.Forwarding(unit.get(Request.class)); - assertEquals(req, req.push("/path", ImmutableMap.of("k", "v"))); + assertEquals(req, req.push("/path", Map.of("k", "v"))); }); } diff --git a/jooby/src/test/java/org/jooby/ResponseForwardingTest.java b/jooby/src/test/java/org/jooby/ResponseForwardingTest.java index 81fc9008..82dd203c 100644 --- a/jooby/src/test/java/org/jooby/ResponseForwardingTest.java +++ b/jooby/src/test/java/org/jooby/ResponseForwardingTest.java @@ -23,14 +23,15 @@ import java.io.ByteArrayInputStream; import java.io.File; import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.Optional; import org.jooby.test.MockUnit; import org.junit.Test; -import com.google.common.base.Charsets; -import com.google.common.collect.Lists; +import java.nio.charset.StandardCharsets; public class ResponseForwardingTest { @@ -173,15 +174,15 @@ public void charset() throws Exception { new MockUnit(Response.class) .expect(unit -> { Response rsp = unit.get(Response.class); - when(rsp.charset()).thenReturn(Charsets.UTF_8); + when(rsp.charset()).thenReturn(StandardCharsets.UTF_8); - when(rsp.charset(Charsets.US_ASCII)).thenReturn(null); + when(rsp.charset(StandardCharsets.US_ASCII)).thenReturn(null); }) .run(unit -> { Response rsp = new Response.Forwarding(unit.get(Response.class)); - assertEquals(Charsets.UTF_8, rsp.charset()); + assertEquals(StandardCharsets.UTF_8, rsp.charset()); - assertEquals(rsp, rsp.charset(Charsets.US_ASCII)); + assertEquals(rsp, rsp.charset(StandardCharsets.US_ASCII)); }); } @@ -337,12 +338,12 @@ public void listHeader() throws Exception { .expect(unit -> { Response rsp = unit.get(Response.class); - when(rsp.header("h", Lists. newArrayList("v1", 2))).thenReturn(rsp); + when(rsp.header("h", new ArrayList<>(Arrays.asList("v1", 2)))).thenReturn(rsp); }) .run(unit -> { Response rsp = new Response.Forwarding(unit.get(Response.class)); - assertEquals(rsp, rsp.header("h", Lists. newArrayList("v1", 2))); + assertEquals(rsp, rsp.header("h", new ArrayList<>(Arrays.asList("v1", 2)))); }); } diff --git a/jooby/src/test/java/org/jooby/ResultTest.java b/jooby/src/test/java/org/jooby/ResultTest.java index b1411dc6..22269972 100644 --- a/jooby/src/test/java/org/jooby/ResultTest.java +++ b/jooby/src/test/java/org/jooby/ResultTest.java @@ -17,13 +17,14 @@ import static org.junit.Assert.assertEquals; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; +import java.util.List; import java.util.Optional; import org.junit.Test; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; public class ResultTest { @@ -134,7 +135,7 @@ public void header() { assertEquals(7.0f, result.headers().get("float")); assertEquals(8.0d, result.headers().get("double")); assertEquals(date, result.headers().get("date")); - assertEquals(Lists.newArrayList(1, 2, 3), result.headers().get("list")); + assertEquals(new ArrayList<>(Arrays.asList(1, 2, 3)), result.headers().get("list")); } @Test @@ -214,7 +215,7 @@ public void whenGet() { .when(MediaType.all, () -> value); Result clone = result.clone(); assertEquals(json, result.get()); - assertEquals(value, clone.get(ImmutableList.of(MediaType.html))); + assertEquals(value, clone.get(List.of(MediaType.html))); } @Test diff --git a/jooby/src/test/java/org/jooby/RouteDefinitionTest.java b/jooby/src/test/java/org/jooby/RouteDefinitionTest.java index cd332cdf..9e20ab0c 100644 --- a/jooby/src/test/java/org/jooby/RouteDefinitionTest.java +++ b/jooby/src/test/java/org/jooby/RouteDefinitionTest.java @@ -15,7 +15,6 @@ */ package org.jooby; -import com.google.common.collect.ImmutableMap; import issues.RouteSourceLocation; import org.jooby.Route.Definition; import org.jooby.internal.RouteImpl; @@ -28,6 +27,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.function.Function; @@ -315,7 +315,7 @@ public void reverse() throws Exception { assertEquals("/cat/5", route.apply("/{type}/{id}").reverse("cat", 5)); assertEquals("/ccat/1", - route.apply("/c{type}/{id}").reverse(ImmutableMap.of("type", "cat", "id", 1))); + route.apply("/c{type}/{id}").reverse(Map.of("type", "cat", "id", 1))); assertEquals("/cat/tom", route.apply("/cat/tom").reverse("cat", 1)); } diff --git a/jooby/src/test/java/org/jooby/SseTest.java b/jooby/src/test/java/org/jooby/SseTest.java index cae84fe1..418e57d7 100644 --- a/jooby/src/test/java/org/jooby/SseTest.java +++ b/jooby/src/test/java/org/jooby/SseTest.java @@ -15,8 +15,6 @@ */ package org.jooby; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Sets; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.TypeLiteral; @@ -38,6 +36,7 @@ import java.nio.channels.ClosedChannelException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -59,10 +58,10 @@ public class SseTest { when(request.require(Injector.class)).thenReturn(injector); when(request.route()).thenReturn(route); - when(request.attributes()).thenReturn(ImmutableMap.of()); + when(request.attributes()).thenReturn(Map.of()); when(request.header("Last-Event-ID")).thenReturn(lastEventId); - when(injector.getInstance(Renderer.KEY)).thenReturn(Sets.newHashSet()); + when(injector.getInstance(Renderer.KEY)).thenReturn(new HashSet<>()); }; private Block locale = unit -> { diff --git a/jooby/src/test/java/org/jooby/ViewTest.java b/jooby/src/test/java/org/jooby/ViewTest.java index 2298d8e7..f12f5fc4 100644 --- a/jooby/src/test/java/org/jooby/ViewTest.java +++ b/jooby/src/test/java/org/jooby/ViewTest.java @@ -19,7 +19,8 @@ import org.junit.Test; -import com.google.common.collect.ImmutableMap; +import java.util.Map; + public class ViewTest { @@ -64,15 +65,15 @@ public void viewBuildModel() { @Test public void viewBuildModelMap() { - View view = Results.html("v").put("m", ImmutableMap.of("k", "v")); + View view = Results.html("v").put("m", Map.of("k", "v")); assertEquals("v", view.name()); assertEquals(1, view.model().size()); - assertEquals(ImmutableMap.of("k", "v"), view.model().get("m")); + assertEquals(Map.of("k", "v"), view.model().get("m")); } @Test public void viewPutMap() { - View view = Results.html("v").put(ImmutableMap.of("k", "v")); + View view = Results.html("v").put(Map.of("k", "v")); assertEquals("v", view.name()); assertEquals(1, view.model().size()); assertEquals("v", view.model().get("k")); diff --git a/jooby/src/test/java/org/jooby/internal/AbstractRendererContextTest.java b/jooby/src/test/java/org/jooby/internal/AbstractRendererContextTest.java index d44cd6cf..97e7bf23 100644 --- a/jooby/src/test/java/org/jooby/internal/AbstractRendererContextTest.java +++ b/jooby/src/test/java/org/jooby/internal/AbstractRendererContextTest.java @@ -32,14 +32,13 @@ import org.jooby.test.MockUnit; import org.junit.Test; -import com.google.common.collect.ImmutableList; public class AbstractRendererContextTest { @Test(expected = Err.class) public void norenderer() throws Throwable { List renderers = new ArrayList<>(); - List produces = ImmutableList.of(MediaType.json); + List produces = List.of(MediaType.json); View value = Results.html("view"); new MockUnit() .run(unit -> { diff --git a/jooby/src/test/java/org/jooby/internal/AppPrinterTest.java b/jooby/src/test/java/org/jooby/internal/AppPrinterTest.java index 52f04f77..4f233110 100644 --- a/jooby/src/test/java/org/jooby/internal/AppPrinterTest.java +++ b/jooby/src/test/java/org/jooby/internal/AppPrinterTest.java @@ -18,6 +18,7 @@ import static org.junit.Assert.assertEquals; import java.util.Arrays; +import java.util.LinkedHashSet; import org.jooby.Route; import org.jooby.Route.Before; @@ -25,7 +26,6 @@ import org.junit.Test; import org.slf4j.LoggerFactory; -import com.google.common.collect.Sets; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import com.typesafe.config.ConfigValueFactory; @@ -38,9 +38,9 @@ public class AppPrinterTest { @Test public void print() { String setup = new AppPrinter( - Sets.newLinkedHashSet( + new LinkedHashSet<>( Arrays.asList(before("/"), beforeSend("/"), after("/"), route("/"), route("/home"))), - Sets.newLinkedHashSet(Arrays.asList(socket("/ws"))), config("/")) + new LinkedHashSet<>(Arrays.asList(socket("/ws"))), config("/")) .toString(); assertEquals(" GET {before}/ [*/*] [*/*] (/anonymous)\n" + " GET {after}/ [*/*] [*/*] (/anonymous)\n" + @@ -56,9 +56,9 @@ public void print() { @Test public void printConfig() { AppPrinter printer = new AppPrinter( - Sets.newLinkedHashSet( + new LinkedHashSet<>( Arrays.asList(before("/"), beforeSend("/"), after("/"), route("/"), route("/home"))), - Sets.newLinkedHashSet(Arrays.asList(socket("/ws"))), config("/")); + new LinkedHashSet<>(Arrays.asList(socket("/ws"))), config("/")); Logger log = (Logger) LoggerFactory.getLogger(AppPrinterTest.class); log.setLevel(Level.DEBUG); printer.printConf(log, config("/")); @@ -67,8 +67,8 @@ public void printConfig() { @Test public void printHttps() { String setup = new AppPrinter( - Sets.newLinkedHashSet(Arrays.asList(route("/"), route("/home"))), - Sets.newLinkedHashSet(Arrays.asList(socket("/ws"))), + new LinkedHashSet<>(Arrays.asList(route("/"), route("/home"))), + new LinkedHashSet<>(Arrays.asList(socket("/ws"))), config("/").withValue("application.securePort", ConfigValueFactory.fromAnyRef(8443))) .toString(); assertEquals(" GET / [*/*] [*/*] (/anonymous)\n" + @@ -83,8 +83,8 @@ public void printHttps() { @Test public void printHttp2() { String setup = new AppPrinter( - Sets.newLinkedHashSet(Arrays.asList(route("/"), route("/home"))), - Sets.newLinkedHashSet(Arrays.asList(socket("/ws"))), + new LinkedHashSet<>(Arrays.asList(route("/"), route("/home"))), + new LinkedHashSet<>(Arrays.asList(socket("/ws"))), config("/") .withValue("server.http2.enabled", ConfigValueFactory.fromAnyRef(true)) .withValue("application.securePort", ConfigValueFactory.fromAnyRef(8443))) @@ -101,8 +101,8 @@ public void printHttp2() { @Test public void printHttp2Https() { String setup = new AppPrinter( - Sets.newLinkedHashSet(Arrays.asList(route("/"), route("/home"))), - Sets.newLinkedHashSet(Arrays.asList(socket("/ws"))), + new LinkedHashSet<>(Arrays.asList(route("/"), route("/home"))), + new LinkedHashSet<>(Arrays.asList(socket("/ws"))), config("/") .withValue("server.http2.cleartext", ConfigValueFactory.fromAnyRef(false)) .withValue("server.http2.enabled", ConfigValueFactory.fromAnyRef(true)) @@ -120,8 +120,8 @@ public void printHttp2Https() { @Test public void printHttp2ClearText() { String setup = new AppPrinter( - Sets.newLinkedHashSet(Arrays.asList(route("/"), route("/home"))), - Sets.newLinkedHashSet(Arrays.asList(socket("/ws"))), + new LinkedHashSet<>(Arrays.asList(route("/"), route("/home"))), + new LinkedHashSet<>(Arrays.asList(socket("/ws"))), config("/") .withValue("server.http2.cleartext", ConfigValueFactory.fromAnyRef(true)) .withValue("server.http2.enabled", ConfigValueFactory.fromAnyRef(true))) @@ -146,8 +146,8 @@ private Config config(final String path) { @Test public void printWithPath() { String setup = new AppPrinter( - Sets.newLinkedHashSet(Arrays.asList(route("/"), route("/home"))), - Sets.newLinkedHashSet(Arrays.asList(socket("/ws"))), config("/app")) + new LinkedHashSet<>(Arrays.asList(route("/"), route("/home"))), + new LinkedHashSet<>(Arrays.asList(socket("/ws"))), config("/app")) .toString(); assertEquals(" GET / [*/*] [*/*] (/anonymous)\n" + " GET /home [*/*] [*/*] (/anonymous)\n" + @@ -160,8 +160,8 @@ public void printWithPath() { @Test public void printNoSockets() { String setup = new AppPrinter( - Sets.newLinkedHashSet(Arrays.asList(route("/"), route("/home"))), - Sets.newLinkedHashSet(), config("/app")) + new LinkedHashSet<>(Arrays.asList(route("/"), route("/home"))), + new LinkedHashSet<>(), config("/app")) .toString(); assertEquals(" GET / [*/*] [*/*] (/anonymous)\n" + " GET /home [*/*] [*/*] (/anonymous)\n" + diff --git a/jooby/src/test/java/org/jooby/internal/BuiltinParserTest.java b/jooby/src/test/java/org/jooby/internal/BuiltinParserTest.java index 8593f76f..4c83ddd4 100644 --- a/jooby/src/test/java/org/jooby/internal/BuiltinParserTest.java +++ b/jooby/src/test/java/org/jooby/internal/BuiltinParserTest.java @@ -15,20 +15,152 @@ */ package org.jooby.internal; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.fail; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.jooby.Parser; +import org.jooby.internal.parser.ParserExecutor; +import org.junit.Before; import org.junit.Test; +import com.google.inject.TypeLiteral; +import com.google.inject.util.Types; +import com.typesafe.config.ConfigFactory; + public class BuiltinParserTest { + private ParserExecutor executor; + + public enum Color { + RED, + GREEN, + BLUE + } + + @Before + public void setup() { + executor = parser(BuiltinParser.Basic, BuiltinParser.Collection, BuiltinParser.Optional, + BuiltinParser.Enum, BuiltinParser.Bytes); + } + + @Test + public void valuesAreRegisteredInParserOrder() { + assertArrayEquals(new BuiltinParser[]{ + BuiltinParser.Basic, + BuiltinParser.Collection, + BuiltinParser.Optional, + BuiltinParser.Enum, + BuiltinParser.Bytes }, BuiltinParser.values()); + } + @Test - public void values() { - assertEquals(5, BuiltinParser.values().length); + public void bytesParserName() { + assertEquals("byte[]", BuiltinParser.Bytes.toString()); } @Test - public void bytesValueOf() { - assertEquals(BuiltinParser.Bytes, BuiltinParser.valueOf("Bytes")); + public void bytesParserReadsBody() throws Throwable { + byte[] bytes = "hello".getBytes(StandardCharsets.UTF_8); + + assertArrayEquals(bytes, executor.convert(TypeLiteral.get(byte[].class), + new BodyReferenceImpl(bytes.length, StandardCharsets.UTF_8, + new File("target/BuiltinParserTest/body.tmp"), + new ByteArrayInputStream(bytes), bytes.length + 1L))); + } + + @Test + public void longParserAcceptsHttpDate() throws Throwable { + LocalDateTime dateTime = LocalDateTime.of(2024, 1, 15, 10, 30, 0); + + assertEquals(dateTime.toInstant(ZoneOffset.UTC).toEpochMilli(), + (long) executor.convert(TypeLiteral.get(long.class), data(dateTime.format(Headers.fmt)))); + } + + @Test + public void basicParserAcceptsEmptyStringButRejectsEmptyScalarValues() throws Throwable { + assertEquals("", executor.convert(TypeLiteral.get(String.class), data(""))); + + try { + executor.convert(TypeLiteral.get(int.class), data("")); + fail("Expected NoSuchElementException"); + } catch (NoSuchElementException expected) { + // expected + } + } + + @Test + public void collectionParserBuildsUnmodifiableCollections() throws Throwable { + List list = executor.convert(TypeLiteral.get(Types.listOf(String.class)), + data("b", "a", "b")); + assertEquals(Arrays.asList("b", "a", "b"), list); + assertUnsupported(() -> list.add("c")); + + Set set = executor.convert(TypeLiteral.get(Types.setOf(String.class)), + data("b", "a", "b")); + assertEquals(new LinkedHashSet<>(Arrays.asList("b", "a")), set); + assertEquals(2, set.size()); + assertUnsupported(() -> set.add("c")); + + SortedSet sorted = executor.convert( + TypeLiteral.get(Types.newParameterizedType(SortedSet.class, String.class)), + data("b", "a", "b")); + assertEquals(new TreeSet<>(Arrays.asList("a", "b")), sorted); + assertUnsupported(() -> sorted.add("c")); + } + + @Test + public void collectionAndOptionalParsersIgnoreRawOrUnsupportedTypes() throws Throwable { + ParserExecutor collectionOnly = parser(BuiltinParser.Collection); + assertSame(ParserExecutor.NO_PARSER, + collectionOnly.convert(TypeLiteral.get(List.class), data("a"))); + assertSame(ParserExecutor.NO_PARSER, + collectionOnly.convert(TypeLiteral.get(Types.mapOf(String.class, String.class)), data("a"))); + + ParserExecutor optionalOnly = parser(BuiltinParser.Optional); + assertSame(ParserExecutor.NO_PARSER, + optionalOnly.convert(TypeLiteral.get(Optional.class), data("a"))); + } + + @Test + public void enumParserIsCaseInsensitive() throws Throwable { + assertEquals(Color.RED, executor.convert(TypeLiteral.get(Color.class), data("red"))); + assertEquals(Color.GREEN, executor.convert(TypeLiteral.get(Color.class), data("Green"))); + assertEquals(Color.BLUE, executor.convert(TypeLiteral.get(Color.class), data("bLuE"))); + } + + private Object data(final String... values) { + return new StrParamReferenceImpl("parameter", "p", new ArrayList<>(Arrays.asList(values))); + } + + private ParserExecutor parser(final Parser... parsers) { + return new ParserExecutor(null, new LinkedHashSet<>(Arrays.asList(parsers)), + new StatusCodeProvider(ConfigFactory.empty())); + } + + private static void assertUnsupported(final Runnable mutation) { + try { + mutation.run(); + fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException expected) { + // expected + } } } diff --git a/jooby/src/test/java/org/jooby/internal/CookieSessionManagerTest.java b/jooby/src/test/java/org/jooby/internal/CookieSessionManagerTest.java index bc717e15..99731a1e 100644 --- a/jooby/src/test/java/org/jooby/internal/CookieSessionManagerTest.java +++ b/jooby/src/test/java/org/jooby/internal/CookieSessionManagerTest.java @@ -19,6 +19,7 @@ import static org.junit.Assert.assertNotNull; import static org.mockito.Mockito.when; +import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; @@ -37,7 +38,6 @@ import static org.mockito.Mockito.doAnswer; -import com.google.common.collect.ImmutableMap; import com.typesafe.config.Config; public class CookieSessionManagerTest { @@ -138,7 +138,7 @@ public void saveAfter() throws Exception { .expect(unit -> { SessionImpl session = unit.get(SessionImpl.class); - when(session.attributes()).thenReturn(ImmutableMap.of("foo", "2")); + when(session.attributes()).thenReturn(Map.of("foo", "2")); Request req = unit.get(Request.class); when(req.ifSession()).thenReturn(Optional.of(session)); @@ -214,7 +214,7 @@ public void saveAfterTouchSession() throws Exception { .expect(unit -> { SessionImpl session = unit.get(SessionImpl.class); - when(session.attributes()).thenReturn(ImmutableMap.of("foo", "1")); + when(session.attributes()).thenReturn(Map.of("foo", "1")); Request req = unit.get(Request.class); when(req.ifSession()).thenReturn(Optional.of(session)); @@ -302,7 +302,7 @@ public void getSession() throws Exception { .expect(sessionBuilder(Session.COOKIE_SESSION, false, -1)) .expect(unit -> { Session.Builder builder = unit.get(Session.Builder.class); - when(builder.set(ImmutableMap.of("foo", "1"))).thenReturn(builder); + when(builder.set(Map.of("foo", "1"))).thenReturn(builder); when(builder.build()).thenReturn(unit.get(SessionImpl.class)); }) .expect(push) diff --git a/jooby/src/test/java/org/jooby/internal/FallbackRouteTest.java b/jooby/src/test/java/org/jooby/internal/FallbackRouteTest.java index ba970e2e..0ab68d42 100644 --- a/jooby/src/test/java/org/jooby/internal/FallbackRouteTest.java +++ b/jooby/src/test/java/org/jooby/internal/FallbackRouteTest.java @@ -17,6 +17,8 @@ import static org.junit.Assert.assertEquals; +import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import org.jooby.MediaType; @@ -24,8 +26,6 @@ import org.jooby.Route.Filter; import org.junit.Test; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; public class FallbackRouteTest { @@ -35,7 +35,7 @@ public void props() throws Throwable { Filter filter = (req, rsp, chain) -> { handled.set(true); }; - FallbackRoute route = new FallbackRoute("foo", "GET", "/x", ImmutableList.of(MediaType.json), + FallbackRoute route = new FallbackRoute("foo", "GET", "/x", List.of(MediaType.json), filter); assertEquals(true, route.apply(null)); @@ -46,8 +46,8 @@ public void props() throws Throwable { assertEquals("foo", route.name()); assertEquals("/x", route.path()); assertEquals("/x", route.pattern()); - assertEquals(ImmutableList.of(MediaType.json), route.produces()); - assertEquals("/x", route.reverse(ImmutableMap.of())); + assertEquals(List.of(MediaType.json), route.produces()); + assertEquals("/x", route.reverse(Map.of())); assertEquals("/x", route.reverse("a", "b")); assertEquals(Route.Source.BUILTIN, route.source()); route.handle(null, null, null); diff --git a/jooby/src/test/java/org/jooby/internal/MutantImplTest.java b/jooby/src/test/java/org/jooby/internal/MutantImplTest.java index d5a3ba77..87c61697 100644 --- a/jooby/src/test/java/org/jooby/internal/MutantImplTest.java +++ b/jooby/src/test/java/org/jooby/internal/MutantImplTest.java @@ -21,6 +21,7 @@ import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; import java.util.Set; @@ -36,10 +37,6 @@ import org.jooby.internal.parser.StringConstructorParser; import org.junit.Test; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSortedSet; -import com.google.common.collect.Sets; import com.google.inject.Injector; import com.google.inject.TypeLiteral; import com.typesafe.config.ConfigFactory; @@ -67,32 +64,32 @@ public void asBoolean() throws Exception { @Test public void asBooleanList() throws Exception { - assertEquals(ImmutableList.of(Boolean.TRUE, Boolean.FALSE), + assertEquals(List.of(Boolean.TRUE, Boolean.FALSE), newMutant("true", "false").toList(boolean.class)); - assertEquals(ImmutableList.of(Boolean.TRUE, Boolean.FALSE), + assertEquals(List.of(Boolean.TRUE, Boolean.FALSE), newMutant("true", "false").toList(Boolean.class)); - assertEquals(ImmutableList.of(Boolean.TRUE, Boolean.FALSE), + assertEquals(List.of(Boolean.TRUE, Boolean.FALSE), newMutant("true", "false").to(new TypeLiteral>() { })); } @Test public void asBooleanSet() throws Exception { - assertEquals(ImmutableSet.of(Boolean.TRUE, Boolean.FALSE), + assertEquals(Set.of(Boolean.TRUE, Boolean.FALSE), newMutant("true", "false").toSet(boolean.class)); - assertEquals(ImmutableSet.of(Boolean.TRUE, Boolean.FALSE), + assertEquals(Set.of(Boolean.TRUE, Boolean.FALSE), newMutant("true", "false").toSet(Boolean.class)); } @Test public void asBooleanSortedSet() throws Exception { - assertEquals(ImmutableSet.of(Boolean.TRUE, Boolean.FALSE), + assertEquals(Set.of(Boolean.TRUE, Boolean.FALSE), newMutant("false", "true").toSortedSet(boolean.class)); - assertEquals(ImmutableSet.of(Boolean.TRUE, Boolean.FALSE), + assertEquals(Set.of(Boolean.TRUE, Boolean.FALSE), newMutant("false", "true").toSortedSet(Boolean.class)); } @@ -138,28 +135,28 @@ public void byteOverflow() throws Exception { @Test public void asByteList() throws Exception { - assertEquals(ImmutableList.of((byte) 1, (byte) 2, (byte) 3), + assertEquals(List.of((byte) 1, (byte) 2, (byte) 3), newMutant("1", "2", "3").toList(byte.class)); - assertEquals(ImmutableList.of((byte) 1, (byte) 2, (byte) 3), + assertEquals(List.of((byte) 1, (byte) 2, (byte) 3), newMutant("1", "2", "3").toList(Byte.class)); } @Test public void asByteSet() throws Exception { - assertEquals(ImmutableSet.of((byte) 1, (byte) 2, (byte) 3), + assertEquals(Set.of((byte) 1, (byte) 2, (byte) 3), newMutant("1", "2", "3").toSet(byte.class)); - assertEquals(ImmutableSet.of((byte) 1, (byte) 2, (byte) 3), + assertEquals(Set.of((byte) 1, (byte) 2, (byte) 3), newMutant("1", "2", "3").toSet(Byte.class)); } @Test public void asByteSortedSet() throws Exception { - assertEquals(ImmutableSortedSet.of((byte) 1, (byte) 2, (byte) 3), + assertEquals(Set.of((byte) 1, (byte) 2, (byte) 3), newMutant("1", "2", "3").toSortedSet(byte.class)); - assertEquals(ImmutableSortedSet.of((byte) 1, (byte) 2, (byte) 3), + assertEquals(Set.of((byte) 1, (byte) 2, (byte) 3), newMutant("1", "2", "3").toSortedSet(Byte.class)); } @@ -191,28 +188,28 @@ public void shortOverflow() throws Exception { @Test public void asShortList() throws Exception { - assertEquals(ImmutableList.of((short) 1, (short) 2, (short) 3), + assertEquals(List.of((short) 1, (short) 2, (short) 3), newMutant("1", "2", "3").toList(short.class)); - assertEquals(ImmutableList.of((short) 1, (short) 2, (short) 3), + assertEquals(List.of((short) 1, (short) 2, (short) 3), newMutant("1", "2", "3").toList(Short.class)); } @Test public void asShortSet() throws Exception { - assertEquals(ImmutableSet.of((short) 1, (short) 2, (short) 3), + assertEquals(Set.of((short) 1, (short) 2, (short) 3), newMutant("1", "2", "3").toSet(short.class)); - assertEquals(ImmutableSet.of((short) 1, (short) 2, (short) 3), + assertEquals(Set.of((short) 1, (short) 2, (short) 3), newMutant("1", "2", "3").toSet(Short.class)); } @Test public void asShortSortedSet() throws Exception { - assertEquals(ImmutableSortedSet.of((short) 1, (short) 2, (short) 3), + assertEquals(Set.of((short) 1, (short) 2, (short) 3), newMutant("1", "2", "3").toSortedSet(short.class)); - assertEquals(ImmutableSortedSet.of((short) 1, (short) 2, (short) 3), + assertEquals(Set.of((short) 1, (short) 2, (short) 3), newMutant("1", "2", "3").toSortedSet(Short.class)); } @@ -236,32 +233,32 @@ public void asInt() throws Exception { @Test public void asIntList() throws Exception { - assertEquals(ImmutableList.of(1, 2, 3), + assertEquals(List.of(1, 2, 3), newMutant("1", "2", "3").toList(int.class)); - assertEquals(ImmutableList.of(1, 2, 3), + assertEquals(List.of(1, 2, 3), newMutant("1", "2", "3").toList(Integer.class)); } @Test public void asIntSet() throws Exception { - assertEquals(ImmutableSet.of(1, 2, 3), + assertEquals(Set.of(1, 2, 3), newMutant("1", "2", "3").toSet(int.class)); - assertEquals(ImmutableSet.of(1, 2, 3), + assertEquals(Set.of(1, 2, 3), newMutant("1", "2", "3").toSet(Integer.class)); - assertEquals(ImmutableSet.of(1, 2, 3), + assertEquals(Set.of(1, 2, 3), newMutant("1", "2", "3").to(new TypeLiteral>() { })); } @Test public void asIntSortedSet() throws Exception { - assertEquals(ImmutableSortedSet.of(1, 2, 3), + assertEquals(Set.of(1, 2, 3), newMutant("1", "2", "3").toSortedSet(int.class)); - assertEquals(ImmutableSet.of(1, 2, 3), + assertEquals(Set.of(1, 2, 3), newMutant("1", "2", "3").toSortedSet(Integer.class)); } @@ -295,34 +292,34 @@ public void notALong() throws Exception { @Test public void asLongList() throws Exception { - assertEquals(ImmutableList.of(1l, 2l, 3l), + assertEquals(List.of(1l, 2l, 3l), newMutant("1", "2", "3").toList(long.class)); - assertEquals(ImmutableList.of(1l, 2l, 3l), + assertEquals(List.of(1l, 2l, 3l), newMutant("1", "2", "3").toList(Long.class)); } @Test public void asMediaTypeList() throws Exception { - assertEquals(ImmutableList.of(MediaType.valueOf("application/json")), + assertEquals(List.of(MediaType.valueOf("application/json")), newMutant("application/json").toList(MediaType.class)); } @Test public void asLongSet() throws Exception { - assertEquals(ImmutableSet.of(1l, 2l, 3l), + assertEquals(Set.of(1l, 2l, 3l), newMutant("1", "2", "3").toSet(long.class)); - assertEquals(ImmutableSet.of(1l, 2l, 3l), + assertEquals(Set.of(1l, 2l, 3l), newMutant("1", "2", "3").toSet(Long.class)); } @Test public void asLongSortedSet() throws Exception { - assertEquals(ImmutableSortedSet.of(1l, 2l, 3l), + assertEquals(Set.of(1l, 2l, 3l), newMutant("1", "2", "3").toSortedSet(long.class)); - assertEquals(ImmutableSortedSet.of(1l, 2l, 3l), + assertEquals(Set.of(1l, 2l, 3l), newMutant("1", "2", "3").toSortedSet(Long.class)); } @@ -349,29 +346,29 @@ public void notAFloat() throws Exception { @Test public void asFloatList() throws Exception { - assertEquals(ImmutableList.of(1f, 2f, 3f), + assertEquals(List.of(1f, 2f, 3f), newMutant("1", "2", "3").toList(float.class)); - assertEquals(ImmutableList.of(1f, 2f, 3f), + assertEquals(List.of(1f, 2f, 3f), newMutant("1", "2", "3").toList(Float.class)); } @Test public void asFloatSet() throws Exception { - assertEquals(ImmutableSet.of(1f, 2f, 3f), + assertEquals(Set.of(1f, 2f, 3f), newMutant("1", "2", "3").toSet(float.class)); Set asSet = newMutant("1", "2", "3").toSet(Float.class); - assertEquals(ImmutableSet.of(1f, 2f, 3f), + assertEquals(Set.of(1f, 2f, 3f), asSet); } @Test public void asFloatSortedSet() throws Exception { - assertEquals(ImmutableSortedSet.of(1f, 2f, 3f), + assertEquals(Set.of(1f, 2f, 3f), newMutant("1", "2", "3").toSortedSet(float.class)); - assertEquals(ImmutableSortedSet.of(1f, 2f, 3f), + assertEquals(Set.of(1f, 2f, 3f), newMutant("1", "2", "3").toSortedSet(Float.class)); } @@ -398,28 +395,28 @@ public void notADouble() throws Exception { @Test public void asDoubleList() throws Exception { - assertEquals(ImmutableList.of(1d, 2d, 3d), + assertEquals(List.of(1d, 2d, 3d), newMutant("1", "2", "3").toList(double.class)); - assertEquals(ImmutableList.of(1d, 2d, 3d), + assertEquals(List.of(1d, 2d, 3d), newMutant("1", "2", "3").toList(Double.class)); } @Test public void asDoubleSet() throws Exception { - assertEquals(ImmutableSet.of(1d, 2d, 3d), + assertEquals(Set.of(1d, 2d, 3d), newMutant("1", "2", "3").toSet(double.class)); - assertEquals(ImmutableSet.of(1d, 2d, 3d), + assertEquals(Set.of(1d, 2d, 3d), newMutant("1", "2", "3").toSet(Double.class)); } @Test public void asDoubleSortedSet() throws Exception { - assertEquals(ImmutableSortedSet.of(1d, 2d, 3d), + assertEquals(Set.of(1d, 2d, 3d), newMutant("1", "2", "3").toSortedSet(double.class)); - assertEquals(ImmutableSortedSet.of(1d, 2d, 3d), + assertEquals(Set.of(1d, 2d, 3d), newMutant("1", "2", "3").toSortedSet(Double.class)); } @@ -443,19 +440,19 @@ public void asEnum() throws Exception { @Test public void asEnumList() throws Exception { - assertEquals(ImmutableList.of(LETTER.A, LETTER.B), + assertEquals(List.of(LETTER.A, LETTER.B), newMutant("A", "B").toList(LETTER.class)); } @Test public void asEnumSet() throws Exception { - assertEquals(ImmutableSet.of(LETTER.A, LETTER.B), + assertEquals(Set.of(LETTER.A, LETTER.B), newMutant("A", "B").toSet(LETTER.class)); } @Test public void asEnumSortedSet() throws Exception { - assertEquals(ImmutableSortedSet.of(LETTER.A, LETTER.B), + assertEquals(Set.of(LETTER.A, LETTER.B), newMutant("A", "B").toSortedSet(LETTER.class)); } @@ -486,7 +483,7 @@ public void asString() throws Exception { @Test public void asStringList() throws Exception { - assertEquals(ImmutableList.of("aa", "bb"), + assertEquals(List.of("aa", "bb"), newMutant("aa", "bb").toList(String.class)); assertEquals("[aa, bb]", newMutant("aa", "bb").toString()); @@ -494,13 +491,13 @@ public void asStringList() throws Exception { @Test public void asStringSet() throws Exception { - assertEquals(ImmutableSet.of("aa", "bb"), + assertEquals(Set.of("aa", "bb"), newMutant("aa", "bb", "bb").toSet()); } @Test public void asStringSortedSet() throws Exception { - assertEquals(ImmutableSortedSet.of("aa", "bb"), + assertEquals(Set.of("aa", "bb"), newMutant("aa", "bb", "bb").toSortedSet()); } @@ -537,13 +534,13 @@ private Mutant newMutant(final String... values) { private Mutant newMutant(final String value) { StrParamReferenceImpl reference = new StrParamReferenceImpl("parameter", "test", value == null ? Collections.emptyList() - : ImmutableList.of(value)); + : List.of(value)); return new MutantImpl(newConverter(), reference); } private ParserExecutor newConverter() { return new ParserExecutor(mock(Injector.class), - Sets.newLinkedHashSet( + new LinkedHashSet<>( Arrays.asList( BuiltinParser.Basic, BuiltinParser.Collection, diff --git a/jooby/src/test/java/org/jooby/internal/ParamConverterTest.java b/jooby/src/test/java/org/jooby/internal/ParamConverterTest.java index 75345073..7b08d4ce 100644 --- a/jooby/src/test/java/org/jooby/internal/ParamConverterTest.java +++ b/jooby/src/test/java/org/jooby/internal/ParamConverterTest.java @@ -15,9 +15,6 @@ */ package org.jooby.internal; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; import com.google.inject.Injector; import com.google.inject.TypeLiteral; import com.google.inject.util.Types; @@ -128,7 +125,7 @@ public void shouldConvertToDateFromString() throws Throwable { } private Object data(final String... value) { - return new StrParamReferenceImpl("parameter", "test", ImmutableList.copyOf(value)); + return new StrParamReferenceImpl("parameter", "test", new ArrayList<>(Arrays.asList(value))); } @Test @@ -171,7 +168,7 @@ public void shouldConvertBeanWithStringConstructor() throws Throwable { @Test public void shouldConvertListOfBeanWithStringConstructor() throws Throwable { ParserExecutor resolver = newParser(); - assertEquals(Lists.newArrayList(new StringBean("231")), + assertEquals(new ArrayList<>(Arrays.asList(new StringBean("231"))), resolver.convert(TypeLiteral.get(Types.listOf(StringBean.class)), data("231"))); } @@ -293,14 +290,14 @@ public void shouldConvertToByte() throws Throwable { @Test public void shouldConvertToListOfBytes() throws Throwable { ParserExecutor resolver = newParser(); - assertEquals(Lists.newArrayList((byte) 23, (byte) 45), + assertEquals(new ArrayList<>(Arrays.asList((byte) 23, (byte) 45)), resolver.convert(TypeLiteral.get(Types.listOf(Byte.class)), data("23", "45"))); } @Test public void shouldConvertToSetOfBytes() throws Throwable { ParserExecutor resolver = newParser(); - assertEquals(Sets.newHashSet((byte) 23, (byte) 45), + assertEquals(new HashSet<>(Arrays.asList((byte) 23, (byte) 45)), resolver.convert(TypeLiteral.get(Types.setOf(Byte.class)), data("23", "45", "23"))); } @@ -343,11 +340,11 @@ public void shouldConvertToSortedSet() throws Throwable { @Test public void shouldConvertToListOfBoolean() throws Throwable { ParserExecutor resolver = newParser(); - assertEquals(Lists.newArrayList(true, false), + assertEquals(new ArrayList<>(Arrays.asList(true, false)), resolver.convert(TypeLiteral.get(Types.listOf(Boolean.class)), data("true", "false"))); - assertEquals(Lists.newArrayList(false, false), + assertEquals(new ArrayList<>(Arrays.asList(false, false)), resolver.convert(TypeLiteral.get(Types.listOf(Boolean.class)), data("false", "false"))); } @@ -355,11 +352,11 @@ public void shouldConvertToListOfBoolean() throws Throwable { @Test public void shouldConvertToSetOfBoolean() throws Throwable { ParserExecutor resolver = newParser(); - assertEquals(Sets.newHashSet(true, false), + assertEquals(new HashSet<>(Arrays.asList(true, false)), resolver.convert(TypeLiteral.get(Types.setOf(Boolean.class)), data("true", "false"))); - assertEquals(Sets.newHashSet(false), + assertEquals(new HashSet<>(Collections.singletonList(false)), resolver.convert(TypeLiteral.get(Types.setOf(Boolean.class)), data("false", "false"))); } @@ -388,14 +385,14 @@ public void shouldConvertToOptionalBoolean() throws Throwable { @Test public void shouldConvertToMediaType() throws Throwable { ParserExecutor resolver = newParser(); - assertEquals(Lists.newArrayList(MediaType.valueOf("text/html")), + assertEquals(new ArrayList<>(Arrays.asList(MediaType.valueOf("text/html"))), resolver.convert(TypeLiteral.get(Types.listOf(MediaType.class)), data("text/html"))); } private ParserExecutor newParser() { return new ParserExecutor(mock(Injector.class), - Sets.newLinkedHashSet( + new LinkedHashSet<>( Arrays.asList( BuiltinParser.Basic, BuiltinParser.Collection, diff --git a/jooby/src/test/java/org/jooby/internal/ParamReferenceImplTest.java b/jooby/src/test/java/org/jooby/internal/ParamReferenceImplTest.java index 19f2fd7f..efa9720e 100644 --- a/jooby/src/test/java/org/jooby/internal/ParamReferenceImplTest.java +++ b/jooby/src/test/java/org/jooby/internal/ParamReferenceImplTest.java @@ -26,7 +26,6 @@ import org.jooby.test.MockUnit; import org.junit.Test; -import com.google.common.collect.ImmutableList; public class ParamReferenceImplTest { @@ -43,7 +42,7 @@ public void first() throws Exception { new MockUnit() .run(unit -> { assertEquals("first", - new StrParamReferenceImpl("parameter", "name", ImmutableList.of("first")).first()); + new StrParamReferenceImpl("parameter", "name", List.of("first")).first()); }); } @@ -52,7 +51,7 @@ public void last() throws Exception { new MockUnit() .run(unit -> { assertEquals("last", - new StrParamReferenceImpl("parameter", "name", ImmutableList.of("last")).last()); + new StrParamReferenceImpl("parameter", "name", List.of("last")).last()); }); } @@ -61,9 +60,9 @@ public void get() throws Exception { new MockUnit() .run(unit -> { assertEquals("0", - new StrParamReferenceImpl("parameter", "name", ImmutableList.of("0")).get(0)); + new StrParamReferenceImpl("parameter", "name", List.of("0")).get(0)); assertEquals("1", - new StrParamReferenceImpl("parameter", "name", ImmutableList.of("0", "1")).get(1)); + new StrParamReferenceImpl("parameter", "name", List.of("0", "1")).get(1)); }); } @@ -71,7 +70,7 @@ public void get() throws Exception { public void missing() throws Exception { new MockUnit() .run(unit -> { - new StrParamReferenceImpl("parameter", "name", ImmutableList.of("0")).get(1); + new StrParamReferenceImpl("parameter", "name", List.of("0")).get(1); }); } @@ -79,7 +78,7 @@ public void missing() throws Exception { public void missingLowIndex() throws Exception { new MockUnit() .run(unit -> { - new StrParamReferenceImpl("parameter", "name", ImmutableList.of("0")).get(-1); + new StrParamReferenceImpl("parameter", "name", List.of("0")).get(-1); }); } @@ -88,9 +87,9 @@ public void size() throws Exception { new MockUnit() .run(unit -> { assertEquals(1, - new StrParamReferenceImpl("parameter", "name", ImmutableList.of("0")).size()); + new StrParamReferenceImpl("parameter", "name", List.of("0")).size()); assertEquals(2, - new StrParamReferenceImpl("parameter", "name", ImmutableList.of("0", "1")).size()); + new StrParamReferenceImpl("parameter", "name", List.of("0", "1")).size()); }); } diff --git a/jooby/src/test/java/org/jooby/internal/RequestImplTest.java b/jooby/src/test/java/org/jooby/internal/RequestImplTest.java index d4339f59..aa90c70e 100644 --- a/jooby/src/test/java/org/jooby/internal/RequestImplTest.java +++ b/jooby/src/test/java/org/jooby/internal/RequestImplTest.java @@ -21,7 +21,9 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Optional; import org.jooby.Err; @@ -31,8 +33,6 @@ import org.jooby.test.MockUnit.Block; import org.junit.Test; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import com.google.inject.Injector; public class RequestImplTest { @@ -60,8 +60,8 @@ public void defaults() throws Exception { .expect(contentType) .run(unit -> { new RequestImpl(unit.get(Injector.class), unit.get(NativeRequest.class), "/", 8080, - unit.get(Route.class), StandardCharsets.UTF_8, ImmutableList.of(Locale.ENGLISH), - ImmutableMap.of(), ImmutableMap.of(), 1L); + unit.get(Route.class), StandardCharsets.UTF_8, List.of(Locale.ENGLISH), + Map.of(), Map.of(), 1L); }); } @@ -79,9 +79,9 @@ public void matches() throws Exception { .run(unit -> { RequestImpl req = new RequestImpl(unit.get(Injector.class), unit.get(NativeRequest.class), "/", 8080, - unit.get(Route.class), StandardCharsets.UTF_8, ImmutableList.of(Locale.ENGLISH), - ImmutableMap.of(), - ImmutableMap.of(), 1L); + unit.get(Route.class), StandardCharsets.UTF_8, List.of(Locale.ENGLISH), + Map.of(), + Map.of(), 1L); assertEquals(true, req.matches("/path/**")); }); } @@ -98,9 +98,9 @@ public void lang() throws Exception { .run(unit -> { RequestImpl req = new RequestImpl(unit.get(Injector.class), unit.get(NativeRequest.class), "/", 8080, - unit.get(Route.class), StandardCharsets.UTF_8, ImmutableList.of(Locale.ENGLISH), - ImmutableMap.of(), - ImmutableMap.of(), 1L); + unit.get(Route.class), StandardCharsets.UTF_8, List.of(Locale.ENGLISH), + Map.of(), + Map.of(), 1L); assertEquals(Locale.ENGLISH, req.locale()); }); } @@ -119,9 +119,9 @@ public void files() throws Exception { .run(unit -> { try { new RequestImpl(unit.get(Injector.class), unit.get(NativeRequest.class), "/", 8080, - unit.get(Route.class), StandardCharsets.UTF_8, ImmutableList.of(Locale.ENGLISH), - ImmutableMap.of(), - ImmutableMap.of(), 1L).file("f"); + unit.get(Route.class), StandardCharsets.UTF_8, List.of(Locale.ENGLISH), + Map.of(), + Map.of(), 1L).file("f"); fail("expecting error"); } catch (IOException ex) { assertEquals(cause, ex); @@ -138,7 +138,7 @@ public void paramNames() throws Exception { .expect(contentType) .expect(unit -> { Route route = unit.get(Route.class); - when(route.vars()).thenReturn(ImmutableMap.of()); + when(route.vars()).thenReturn(Map.of()); NativeRequest req = unit.get(NativeRequest.class); when(req.paramNames()).thenThrow(cause); @@ -146,9 +146,9 @@ public void paramNames() throws Exception { .run(unit -> { try { new RequestImpl(unit.get(Injector.class), unit.get(NativeRequest.class), "/", 8080, - unit.get(Route.class), StandardCharsets.UTF_8, ImmutableList.of(Locale.ENGLISH), - ImmutableMap.of(), - ImmutableMap.of(), 1L).params(); + unit.get(Route.class), StandardCharsets.UTF_8, List.of(Locale.ENGLISH), + Map.of(), + Map.of(), 1L).params(); fail("expecting error"); } catch (Err ex) { assertEquals(400, ex.statusCode()); @@ -166,7 +166,7 @@ public void params() throws Exception { .expect(contentType) .expect(unit -> { Route route = unit.get(Route.class); - when(route.vars()).thenReturn(ImmutableMap.of()); + when(route.vars()).thenReturn(Map.of()); NativeRequest req = unit.get(NativeRequest.class); when(req.params("p")).thenThrow(cause); @@ -174,9 +174,9 @@ public void params() throws Exception { .run(unit -> { try { new RequestImpl(unit.get(Injector.class), unit.get(NativeRequest.class), "/", 8080, - unit.get(Route.class), StandardCharsets.UTF_8, ImmutableList.of(Locale.ENGLISH), - ImmutableMap.of(), - ImmutableMap.of(), 1L).param("p"); + unit.get(Route.class), StandardCharsets.UTF_8, List.of(Locale.ENGLISH), + Map.of(), + Map.of(), 1L).param("p"); fail("expecting error"); } catch (Err ex) { assertEquals(400, ex.statusCode()); diff --git a/jooby/src/test/java/org/jooby/internal/WebSocketImplTest.java b/jooby/src/test/java/org/jooby/internal/WebSocketImplTest.java index 972769df..0c59c88e 100644 --- a/jooby/src/test/java/org/jooby/internal/WebSocketImplTest.java +++ b/jooby/src/test/java/org/jooby/internal/WebSocketImplTest.java @@ -15,7 +15,6 @@ */ package org.jooby.internal; -import com.google.common.collect.ImmutableMap; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.TypeLiteral; @@ -291,16 +290,16 @@ public void attributes() throws Exception { .run(unit -> { WebSocketImpl ws = new WebSocketImpl( unit.get(WebSocket.OnOpen1.class), path, pattern, vars, consumes, produces); - assertEquals(ImmutableMap.of(), ws.attributes()); + assertEquals(Map.of(), ws.attributes()); ws.set("foo", "bar"); assertEquals("bar", ws.get("foo")); assertEquals(Optional.empty(), ws.ifGet("bar")); assertEquals(Optional.of("bar"), ws.unset("foo")); - assertEquals(ImmutableMap.of(), ws.attributes()); + assertEquals(Map.of(), ws.attributes()); ws.set("foo", "bar"); ws.unset(); - assertEquals(ImmutableMap.of(), ws.attributes()); + assertEquals(Map.of(), ws.attributes()); try { ws.get("foo"); diff --git a/jooby/src/test/java/org/jooby/internal/WebSocketRendererContextTest.java b/jooby/src/test/java/org/jooby/internal/WebSocketRendererContextTest.java index 743416b7..8c5d82cb 100644 --- a/jooby/src/test/java/org/jooby/internal/WebSocketRendererContextTest.java +++ b/jooby/src/test/java/org/jooby/internal/WebSocketRendererContextTest.java @@ -17,6 +17,8 @@ import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; @@ -33,7 +35,6 @@ import org.jooby.test.MockUnit; import org.junit.Test; -import com.google.common.collect.Lists; public class WebSocketRendererContextTest { @@ -44,7 +45,7 @@ public void fileChannel() throws Exception { WebSocket.OnError.class) .run(unit -> { WebSocketRendererContext ctx = new WebSocketRendererContext( - Lists.newArrayList(unit.get(Renderer.class)), + new ArrayList<>(Collections.singletonList(unit.get(Renderer.class))), unit.get(NativeWebSocket.class), produces, StandardCharsets.UTF_8, @@ -62,7 +63,7 @@ public void inputStream() throws Exception { WebSocket.OnError.class, InputStream.class) .run(unit -> { WebSocketRendererContext ctx = new WebSocketRendererContext( - Lists.newArrayList(unit.get(Renderer.class)), + new ArrayList<>(Collections.singletonList(unit.get(Renderer.class))), unit.get(NativeWebSocket.class), produces, StandardCharsets.UTF_8, diff --git a/jooby/src/test/java/org/jooby/internal/WsBinaryMessageTest.java b/jooby/src/test/java/org/jooby/internal/WsBinaryMessageTest.java index b6d601c3..73eba666 100644 --- a/jooby/src/test/java/org/jooby/internal/WsBinaryMessageTest.java +++ b/jooby/src/test/java/org/jooby/internal/WsBinaryMessageTest.java @@ -35,7 +35,7 @@ import org.junit.Test; import org.mockito.Mockito; -import com.google.common.base.Charsets; +import java.nio.charset.StandardCharsets; public class WsBinaryMessageTest { @@ -82,7 +82,7 @@ public void toReader() throws Exception { new Class[]{byte[].class }, bytes); unit.mockConstructor(InputStreamReader.class, new Class[]{ - InputStream.class, Charset.class }, null, Charsets.UTF_8); + InputStream.class, Charset.class }, null, StandardCharsets.UTF_8); }) .run(unit -> { Reader result = new WsBinaryMessage(buffer).to(Reader.class); diff --git a/jooby/src/test/java/org/jooby/internal/handlers/HeadHandlerTest.java b/jooby/src/test/java/org/jooby/internal/handlers/HeadHandlerTest.java index 2d6bc58b..02e3d853 100644 --- a/jooby/src/test/java/org/jooby/internal/handlers/HeadHandlerTest.java +++ b/jooby/src/test/java/org/jooby/internal/handlers/HeadHandlerTest.java @@ -17,6 +17,8 @@ import static org.mockito.Mockito.when; +import java.util.Collections; +import java.util.HashSet; import java.util.Optional; import java.util.Set; @@ -31,7 +33,6 @@ import org.jooby.test.MockUnit.Block; import org.junit.Test; -import com.google.common.collect.Sets; public class HeadHandlerTest { @@ -68,7 +69,7 @@ public void handle() throws Exception { }) .expect(len) .run(unit -> { - Set routes = Sets.newHashSet(unit.get(Route.Definition.class)); + Set routes = new HashSet<>(Collections.singleton(unit.get(Route.Definition.class))); new HeadHandler(routes) .handle(unit.get(Request.class), unit.get(Response.class), unit.get(Route.Chain.class)); @@ -89,7 +90,7 @@ public void noRoute() throws Exception { }) .expect(next) .run(unit -> { - Set routes = Sets.newHashSet(unit.get(Route.Definition.class)); + Set routes = new HashSet<>(Collections.singleton(unit.get(Route.Definition.class))); new HeadHandler(routes) .handle(unit.get(Request.class), unit.get(Response.class), unit.get(Route.Chain.class)); @@ -106,7 +107,7 @@ public void ignoreGlob() throws Exception { }) .expect(next) .run(unit -> { - Set routes = Sets.newHashSet(unit.get(Route.Definition.class)); + Set routes = new HashSet<>(Collections.singleton(unit.get(Route.Definition.class))); new HeadHandler(routes) .handle(unit.get(Request.class), unit.get(Response.class), unit.get(Route.Chain.class)); @@ -119,7 +120,7 @@ public void noroutes() throws Exception { .expect(path) .expect(next) .run(unit -> { - Set routes = Sets.newHashSet(); + Set routes = new HashSet<>(); new HeadHandler(routes) .handle(unit.get(Request.class), unit.get(Response.class), unit.get(Route.Chain.class)); diff --git a/jooby/src/test/java/org/jooby/internal/handlers/OptionsHandlerTest.java b/jooby/src/test/java/org/jooby/internal/handlers/OptionsHandlerTest.java index baa95193..311a537e 100644 --- a/jooby/src/test/java/org/jooby/internal/handlers/OptionsHandlerTest.java +++ b/jooby/src/test/java/org/jooby/internal/handlers/OptionsHandlerTest.java @@ -17,6 +17,8 @@ import static org.mockito.Mockito.when; +import java.util.Collections; +import java.util.HashSet; import java.util.Optional; import java.util.Set; @@ -32,7 +34,6 @@ import org.jooby.test.MockUnit.Block; import org.junit.Test; -import com.google.common.collect.Sets; public class OptionsHandlerTest { @@ -68,7 +69,7 @@ public void handle() throws Exception { when(rsp.status(Status.OK)).thenReturn(rsp); }) .run(unit -> { - Set routes = Sets.newHashSet(unit.get(Route.Definition.class)); + Set routes = new HashSet<>(Collections.singleton(unit.get(Route.Definition.class))); new OptionsHandler(routes) .handle(unit.get(Request.class), unit.get(Response.class), unit.get(Route.Chain.class)); @@ -98,7 +99,7 @@ public void handleSome() throws Exception { when(rsp.status(Status.OK)).thenReturn(rsp); }) .run(unit -> { - Set routes = Sets.newHashSet(unit.get(Route.Definition.class)); + Set routes = new HashSet<>(Collections.singleton(unit.get(Route.Definition.class))); new OptionsHandler(routes) .handle(unit.get(Request.class), unit.get(Response.class), unit.get(Route.Chain.class)); @@ -111,7 +112,7 @@ public void handleNone() throws Exception { .expect(next) .expect(allow(true)) .run(unit -> { - Set routes = Sets.newHashSet(unit.get(Route.Definition.class)); + Set routes = new HashSet<>(Collections.singleton(unit.get(Route.Definition.class))); new OptionsHandler(routes) .handle(unit.get(Request.class), unit.get(Response.class), unit.get(Route.Chain.class)); diff --git a/jooby/src/test/java/org/jooby/internal/mvc/MvcWebSocketTest.java b/jooby/src/test/java/org/jooby/internal/mvc/MvcWebSocketTest.java index f035f5c3..6d03dde5 100644 --- a/jooby/src/test/java/org/jooby/internal/mvc/MvcWebSocketTest.java +++ b/jooby/src/test/java/org/jooby/internal/mvc/MvcWebSocketTest.java @@ -28,7 +28,6 @@ import org.jooby.test.MockUnit.Block; import org.junit.Test; -import com.google.common.collect.ImmutableList; import com.google.inject.Binder; import com.google.inject.Injector; import com.google.inject.Module; @@ -168,10 +167,10 @@ public void onListPojoMessage() throws Exception { Pojo pojo = new Pojo(); new MockUnit(WebSocket.class, Injector.class, PojoListSocket.class, Binder.class, Mutant.class) .expect(childInjector(PojoListSocket.class)) - .expect(mutant(TypeLiteral.get(Types.listOf(Pojo.class)), ImmutableList.of(pojo))) + .expect(mutant(TypeLiteral.get(Types.listOf(Pojo.class)), List.of(pojo))) .expect(unit -> { PojoListSocket socket = unit.get(PojoListSocket.class); - socket.onMessage(ImmutableList.of(pojo)); + socket.onMessage(List.of(pojo)); }) .run(unit -> { new MvcWebSocket(unit.get(WebSocket.class), PojoListSocket.class) diff --git a/jooby/src/test/java/org/jooby/internal/parser/BeanPlanTest.java b/jooby/src/test/java/org/jooby/internal/parser/BeanPlanTest.java index 5fd8cf52..819f5c0c 100644 --- a/jooby/src/test/java/org/jooby/internal/parser/BeanPlanTest.java +++ b/jooby/src/test/java/org/jooby/internal/parser/BeanPlanTest.java @@ -19,12 +19,14 @@ import jakarta.inject.Inject; +import java.util.Arrays; +import java.util.HashSet; + import org.jooby.internal.ParameterNameProvider; import org.jooby.internal.parser.bean.BeanPlan; import org.jooby.test.MockUnit; import org.junit.Test; -import com.google.common.collect.Sets; public class BeanPlanTest { @@ -105,7 +107,7 @@ public void shouldFindMemberOnSuperclass() throws Exception { new MockUnit(ParameterNameProvider.class) .run(unit -> { BeanPlan plan = new BeanPlan(unit.get(ParameterNameProvider.class), Ext.class); - Ext bean = (Ext) plan.newBean(p -> p.name, Sets.newHashSet("foo", "bar")); + Ext bean = (Ext) plan.newBean(p -> p.name, new HashSet<>(Arrays.asList("foo", "bar"))); assertEquals("foo", bean.foo); assertEquals("bar", bean.bar); }); @@ -116,7 +118,7 @@ public void shouldFavorSetterLikeMethod() throws Exception { new MockUnit(ParameterNameProvider.class) .run(unit -> { BeanPlan plan = new BeanPlan(unit.get(ParameterNameProvider.class), SetterLike.class); - SetterLike bean = (SetterLike) plan.newBean(p -> p.name, Sets.newHashSet("bar")); + SetterLike bean = (SetterLike) plan.newBean(p -> p.name, new HashSet<>(Arrays.asList("bar"))); assertEquals("^bar", bean.bar); }); } @@ -126,7 +128,7 @@ public void shouldIgnoreSetterMethodWithZeroOrMoreArg() throws Exception { new MockUnit(ParameterNameProvider.class) .run(unit -> { BeanPlan plan = new BeanPlan(unit.get(ParameterNameProvider.class), BadSetter.class); - BadSetter bean = (BadSetter) plan.newBean(p -> p.name, Sets.newHashSet("bar")); + BadSetter bean = (BadSetter) plan.newBean(p -> p.name, new HashSet<>(Arrays.asList("bar"))); assertEquals("bar", bean.bar); }); } @@ -136,7 +138,7 @@ public void shouldTraverseGraphMethod() throws Exception { new MockUnit(ParameterNameProvider.class) .run(unit -> { BeanPlan plan = new BeanPlan(unit.get(ParameterNameProvider.class), GraphMethod.class); - GraphMethod bean = (GraphMethod) plan.newBean(p -> p.name, Sets.newHashSet("base[foo]")); + GraphMethod bean = (GraphMethod) plan.newBean(p -> p.name, new HashSet<>(Arrays.asList("base[foo]"))); assertEquals("base[foo]", bean.base.foo); }); } diff --git a/jooby/src/test/java/org/jooby/internal/reqparam/ParserExecutorTest.java b/jooby/src/test/java/org/jooby/internal/reqparam/ParserExecutorTest.java index 5dc6f7ce..70bd74b6 100644 --- a/jooby/src/test/java/org/jooby/internal/reqparam/ParserExecutorTest.java +++ b/jooby/src/test/java/org/jooby/internal/reqparam/ParserExecutorTest.java @@ -17,7 +17,9 @@ import static org.junit.Assert.assertEquals; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -27,7 +29,6 @@ import org.jooby.test.MockUnit; import org.junit.Test; -import com.google.common.collect.Sets; import com.google.inject.Injector; import com.google.inject.TypeLiteral; import com.typesafe.config.ConfigFactory; @@ -38,7 +39,7 @@ public class ParserExecutorTest { public void params() throws Exception { new MockUnit(Injector.class) .run(unit -> { - Set parsers = Sets.newHashSet((Parser) (type, ctx) -> ctx.params(up -> "p")); + Set parsers = new HashSet<>(Collections.singleton((Parser) (type, ctx) -> ctx.params(up -> "p"))); Object converted = new ParserExecutor(unit.get(Injector.class), parsers, new StatusCodeProvider(ConfigFactory.empty())) .convert(TypeLiteral.get(Map.class), new HashMap<>()); diff --git a/jooby/src/test/java/org/jooby/servlet/ServletServletRequestTest.java b/jooby/src/test/java/org/jooby/servlet/ServletServletRequestTest.java index 2aecca0e..a6081f91 100644 --- a/jooby/src/test/java/org/jooby/servlet/ServletServletRequestTest.java +++ b/jooby/src/test/java/org/jooby/servlet/ServletServletRequestTest.java @@ -19,7 +19,10 @@ import static org.junit.Assert.assertEquals; import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.Map; import java.util.UUID; import jakarta.servlet.ServletException; @@ -29,9 +32,6 @@ import org.jooby.test.MockUnit; import org.junit.Test; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterators; -import com.google.common.collect.Lists; public class ServletServletRequestTest { @@ -159,11 +159,11 @@ public void paramNames() throws IOException, Exception { when(req.getContentType()).thenReturn("text/html"); when(req.getPathInfo()).thenReturn("/"); when(req.getParameterNames()).thenReturn( - Iterators.asEnumeration(Lists.newArrayList("p1", "p2").iterator())); + Collections.enumeration(Arrays.asList("p1", "p2"))); when(req.getContextPath()).thenReturn(""); }) .run(unit -> { - assertEquals(Lists.newArrayList("p1", "p2"), + assertEquals(new ArrayList<>(Arrays.asList("p1", "p2")), new ServletServletRequest(unit.get(HttpServletRequest.class), tmpdir) .paramNames()); }); @@ -182,7 +182,7 @@ public void params() throws IOException, Exception { when(req.getContextPath()).thenReturn(""); }) .run(unit -> { - assertEquals(Lists.newArrayList("a", "b"), + assertEquals(new ArrayList<>(Arrays.asList("a", "b")), new ServletServletRequest(unit.get(HttpServletRequest.class), tmpdir) .params("x")); }); @@ -201,7 +201,7 @@ public void noparams() throws IOException, Exception { when(req.getContextPath()).thenReturn(""); }) .run(unit -> { - assertEquals(Lists.newArrayList(), + assertEquals(new ArrayList<>(), new ServletServletRequest(unit.get(HttpServletRequest.class), tmpdir) .params("x")); }); @@ -223,7 +223,7 @@ public void attributes() throws Exception { when(req.getAttribute("server.attribute")).thenReturn(serverAttribute); }) .run(unit -> { - assertEquals(ImmutableMap.of("server.attribute", serverAttribute), + assertEquals(Map.of("server.attribute", serverAttribute), new ServletServletRequest(unit.get(HttpServletRequest.class), tmpdir) .attributes()); }); @@ -278,7 +278,7 @@ public void noupgrade() throws IOException, Exception { when(req.getContextPath()).thenReturn(""); }) .run(unit -> { - assertEquals(Lists.newArrayList(), + assertEquals(new ArrayList<>(), new ServletServletRequest(unit.get(HttpServletRequest.class), tmpdir) .upgrade(ServletServletRequest.class)); }); diff --git a/jooby/src/test/java/org/jooby/test/MockUnit.java b/jooby/src/test/java/org/jooby/test/MockUnit.java index 92d9abca..10cbd4d2 100644 --- a/jooby/src/test/java/org/jooby/test/MockUnit.java +++ b/jooby/src/test/java/org/jooby/test/MockUnit.java @@ -15,8 +15,6 @@ */ package org.jooby.test; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Multimap; import static java.util.Objects.requireNonNull; import org.jooby.funzy.Try; import org.mockito.ArgumentCaptor; @@ -96,7 +94,7 @@ public interface Block { private List mocks = new LinkedList<>(); - private Multimap globalMock = ArrayListMultimap.create(); + private Map> globalMock = new LinkedHashMap<>(); private Map>> captures = new LinkedHashMap<>(); @@ -228,18 +226,18 @@ public T mock(final Class type, final boolean strict) { public T registerMock(final Class type) { T mock = mock(type); - globalMock.put(type, mock); + globalMock.computeIfAbsent(type, k -> new ArrayList<>()).add(mock); return mock; } public T registerMock(final Class type, final T mock) { - globalMock.put(type, mock); + globalMock.computeIfAbsent(type, k -> new ArrayList<>()).add(mock); return mock; } public T get(final Class type) { try { - List collection = (List) requireNonNull(globalMock.get(type)); + List collection = requireNonNull(globalMock.get(type), "Mock not found: " + type); Object result = collection.get(collection.size() - 1); // If this is a pre-mock that has been replaced by a construction mock, return the latter Object constructed = preMockToConstructed.get(result); @@ -250,7 +248,7 @@ public T get(final Class type) { } public T first(final Class type) { - List collection = (List) requireNonNull(globalMock.get(type), + List collection = requireNonNull(globalMock.get(type), "Mock not found: " + type); Object result = collection.get(0); Object constructed = preMockToConstructed.get(result); From 098c9ee8a5a3309c7d4f58f0dd3164ffe4754b45 Mon Sep 17 00:00:00 2001 From: xsalefter Date: Thu, 28 May 2026 02:31:49 +0700 Subject: [PATCH 10/33] add jakarta.annotation-api and remove remove com.google.code.findbugs --- automaton/pom.xml | 4 ++-- concurrent/pom.xml | 4 ++-- embeddeddb/postgresql/pom.xml | 4 ++-- jdbi/pom.xml | 4 ++-- metrics/pom.xml | 4 ++-- queue/pom.xml | 4 ++-- skeleton/pom.xml | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/automaton/pom.xml b/automaton/pom.xml index 988ac7c6..b4688a9f 100644 --- a/automaton/pom.xml +++ b/automaton/pom.xml @@ -32,8 +32,8 @@ - com.google.code.findbugs - jsr305 + jakarta.annotation + jakarta.annotation-api jakarta.activation diff --git a/concurrent/pom.xml b/concurrent/pom.xml index 8b5eeb4d..09d4c131 100644 --- a/concurrent/pom.xml +++ b/concurrent/pom.xml @@ -34,8 +34,8 @@ test - com.google.code.findbugs - jsr305 + jakarta.annotation + jakarta.annotation-api com.google.inject diff --git a/embeddeddb/postgresql/pom.xml b/embeddeddb/postgresql/pom.xml index 046a06ee..0d00a3b5 100644 --- a/embeddeddb/postgresql/pom.xml +++ b/embeddeddb/postgresql/pom.xml @@ -29,8 +29,8 @@ Kill Bill library of embedded dbs: PostgreSQL - com.google.code.findbugs - jsr305 + jakarta.annotation + jakarta.annotation-api io.airlift diff --git a/jdbi/pom.xml b/jdbi/pom.xml index 30d36c90..d587c82b 100644 --- a/jdbi/pom.xml +++ b/jdbi/pom.xml @@ -38,8 +38,8 @@ classmate - com.google.code.findbugs - jsr305 + jakarta.annotation + jakarta.annotation-api com.h2database diff --git a/metrics/pom.xml b/metrics/pom.xml index 25e1ed8f..7e473862 100644 --- a/metrics/pom.xml +++ b/metrics/pom.xml @@ -41,8 +41,8 @@ jackson-databind - com.google.code.findbugs - jsr305 + jakarta.annotation + jakarta.annotation-api com.google.inject diff --git a/queue/pom.xml b/queue/pom.xml index d845ed2a..2252ad8e 100644 --- a/queue/pom.xml +++ b/queue/pom.xml @@ -48,8 +48,8 @@ jackson-datatype-joda - com.google.code.findbugs - jsr305 + jakarta.annotation + jakarta.annotation-api com.google.inject diff --git a/skeleton/pom.xml b/skeleton/pom.xml index a1724a57..a9c1c1d7 100644 --- a/skeleton/pom.xml +++ b/skeleton/pom.xml @@ -55,8 +55,8 @@ true - com.google.code.findbugs - jsr305 + jakarta.annotation + jakarta.annotation-api com.google.inject From 50a8d2ce771545da2d4620636e11ba6baab779ed Mon Sep 17 00:00:00 2001 From: xsalefter Date: Thu, 28 May 2026 02:43:28 +0700 Subject: [PATCH 11/33] move from javax.annotation to jakarta.annotation --- automaton/pom.xml | 8 ++++---- .../killbill/automaton/dot/DOTBuilder.java | 2 +- concurrent/pom.xml | 8 ++++---- .../commons/profiling/ProfilingData.java | 2 +- embeddeddb/postgresql/pom.xml | 8 ++++---- .../KillBillTestingPostgreSqlServer.java | 2 +- jdbi/pom.xml | 8 ++++---- .../commons/jdbi/guice/DBIProvider.java | 2 +- .../jdbi/guice/DataSourceProvider.java | 2 +- metrics/pom.xml | 8 ++++---- .../metrics/guice/CountedListener.java | 2 +- .../guice/DeclaredMethodsTypeListener.java | 2 +- .../guice/DeclaringClassMetricNamer.java | 2 +- .../guice/ExceptionMeteredListener.java | 2 +- .../guice/GaugeInstanceClassMetricNamer.java | 2 +- .../metrics/guice/MeteredListener.java | 2 +- .../commons/metrics/guice/MetricNamer.java | 2 +- .../guice/MetricsInstrumentationModule.java | 2 +- .../commons/metrics/guice/TimedListener.java | 2 +- .../guice/annotation/AnnotationResolver.java | 4 ++-- .../annotation/ClassAnnotationResolver.java | 4 ++-- .../annotation/ListAnnotationResolver.java | 4 ++-- .../annotation/MethodAnnotationResolver.java | 4 ++-- .../killbill/bus/DefaultPersistentBus.java | 2 +- .../killbill/bus/dao/PersistentBusSqlDao.java | 2 +- .../DefaultNotificationQueue.java | 2 +- .../org/killbill/queue/dao/QueueSqlDao.java | 2 +- skeleton/pom.xml | 8 ++++---- .../skeleton/metrics/ResourceTimer.java | 2 +- utils/pom.xml | 8 ++++---- .../killbill/commons/eventbus/Subscriber.java | 4 ++-- .../commons/eventbus/SubscriberRegistry.java | 4 ++-- .../org/killbill/commons/utils/Joiner.java | 6 +++--- .../killbill/commons/utils/Preconditions.java | 19 +++++++++---------- .../commons/utils/StandardSystemProperty.java | 4 ++-- .../org/killbill/commons/utils/Strings.java | 4 ++-- .../utils/collect/AbstractIterator.java | 10 +++++----- .../utils/collect/ConcatenatedIterator.java | 10 +++++----- .../commons/utils/collect/Iterators.java | 4 ++-- .../concurrent/ThreadFactoryBuilder.java | 12 ++++++------ .../reflect/AbstractInvocationHandler.java | 10 +++++----- .../commons/eventbus/StringCatcher.java | 2 +- 42 files changed, 99 insertions(+), 100 deletions(-) diff --git a/automaton/pom.xml b/automaton/pom.xml index b4688a9f..02874c53 100644 --- a/automaton/pom.xml +++ b/automaton/pom.xml @@ -31,15 +31,15 @@ spotbugs-exclude.xml - - jakarta.annotation - jakarta.annotation-api - jakarta.activation jakarta.activation-api runtime + + jakarta.annotation + jakarta.annotation-api + jakarta.xml.bind jakarta.xml.bind-api diff --git a/automaton/src/main/java/org/killbill/automaton/dot/DOTBuilder.java b/automaton/src/main/java/org/killbill/automaton/dot/DOTBuilder.java index 32b13869..1a3646b5 100644 --- a/automaton/src/main/java/org/killbill/automaton/dot/DOTBuilder.java +++ b/automaton/src/main/java/org/killbill/automaton/dot/DOTBuilder.java @@ -22,7 +22,7 @@ import java.util.HashMap; import java.util.Map; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import org.killbill.commons.utils.MapJoiner; diff --git a/concurrent/pom.xml b/concurrent/pom.xml index 09d4c131..5eb8ab48 100644 --- a/concurrent/pom.xml +++ b/concurrent/pom.xml @@ -33,15 +33,15 @@ reload4j test - - jakarta.annotation - jakarta.annotation-api - com.google.inject guice runtime + + jakarta.annotation + jakarta.annotation-api + org.slf4j slf4j-api diff --git a/concurrent/src/main/java/org/killbill/commons/profiling/ProfilingData.java b/concurrent/src/main/java/org/killbill/commons/profiling/ProfilingData.java index a4a2ddde..675b91bd 100644 --- a/concurrent/src/main/java/org/killbill/commons/profiling/ProfilingData.java +++ b/concurrent/src/main/java/org/killbill/commons/profiling/ProfilingData.java @@ -25,7 +25,7 @@ import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; public class ProfilingData { diff --git a/embeddeddb/postgresql/pom.xml b/embeddeddb/postgresql/pom.xml index 0d00a3b5..f8456a2b 100644 --- a/embeddeddb/postgresql/pom.xml +++ b/embeddeddb/postgresql/pom.xml @@ -28,10 +28,6 @@ killbill-embeddeddb-postgresql Kill Bill library of embedded dbs: PostgreSQL - - jakarta.annotation - jakarta.annotation-api - io.airlift command @@ -47,6 +43,10 @@ embedded-postgres test + + jakarta.annotation + jakarta.annotation-api + org.kill-bill.commons killbill-embeddeddb-common diff --git a/embeddeddb/postgresql/src/test/java/org/killbill/commons/embeddeddb/postgresql/KillBillTestingPostgreSqlServer.java b/embeddeddb/postgresql/src/test/java/org/killbill/commons/embeddeddb/postgresql/KillBillTestingPostgreSqlServer.java index 83d71baa..6963b8c6 100644 --- a/embeddeddb/postgresql/src/test/java/org/killbill/commons/embeddeddb/postgresql/KillBillTestingPostgreSqlServer.java +++ b/embeddeddb/postgresql/src/test/java/org/killbill/commons/embeddeddb/postgresql/KillBillTestingPostgreSqlServer.java @@ -25,7 +25,7 @@ import java.sql.SQLException; import java.sql.Statement; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import org.killbill.commons.utils.Preconditions; import org.slf4j.Logger; diff --git a/jdbi/pom.xml b/jdbi/pom.xml index d587c82b..7f6f6b03 100644 --- a/jdbi/pom.xml +++ b/jdbi/pom.xml @@ -37,10 +37,6 @@ com.fasterxml classmate - - jakarta.annotation - jakarta.annotation-api - com.h2database h2 @@ -58,6 +54,10 @@ + + jakarta.annotation + jakarta.annotation-api + jakarta.inject jakarta.inject-api diff --git a/jdbi/src/main/java/org/killbill/commons/jdbi/guice/DBIProvider.java b/jdbi/src/main/java/org/killbill/commons/jdbi/guice/DBIProvider.java index bfa9bc80..c082ad75 100644 --- a/jdbi/src/main/java/org/killbill/commons/jdbi/guice/DBIProvider.java +++ b/jdbi/src/main/java/org/killbill/commons/jdbi/guice/DBIProvider.java @@ -22,7 +22,7 @@ import java.util.LinkedHashSet; import java.util.Set; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import jakarta.inject.Inject; import jakarta.inject.Provider; import javax.sql.DataSource; diff --git a/jdbi/src/main/java/org/killbill/commons/jdbi/guice/DataSourceProvider.java b/jdbi/src/main/java/org/killbill/commons/jdbi/guice/DataSourceProvider.java index a7911459..b7679b6b 100644 --- a/jdbi/src/main/java/org/killbill/commons/jdbi/guice/DataSourceProvider.java +++ b/jdbi/src/main/java/org/killbill/commons/jdbi/guice/DataSourceProvider.java @@ -24,7 +24,7 @@ import java.sql.SQLException; import java.util.concurrent.TimeUnit; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import jakarta.inject.Inject; import jakarta.inject.Provider; import javax.sql.DataSource; diff --git a/metrics/pom.xml b/metrics/pom.xml index 7e473862..3fb0f5b7 100644 --- a/metrics/pom.xml +++ b/metrics/pom.xml @@ -40,10 +40,6 @@ com.fasterxml.jackson.core jackson-databind - - jakarta.annotation - jakarta.annotation-api - com.google.inject guice @@ -56,6 +52,10 @@ io.dropwizard.metrics metrics-core + + jakarta.annotation + jakarta.annotation-api + jakarta.inject jakarta.inject-api diff --git a/metrics/src/main/java/org/killbill/commons/metrics/guice/CountedListener.java b/metrics/src/main/java/org/killbill/commons/metrics/guice/CountedListener.java index 6a9c9fe7..27457faa 100644 --- a/metrics/src/main/java/org/killbill/commons/metrics/guice/CountedListener.java +++ b/metrics/src/main/java/org/killbill/commons/metrics/guice/CountedListener.java @@ -19,7 +19,7 @@ import java.lang.reflect.Method; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import org.aopalliance.intercept.MethodInterceptor; import org.killbill.commons.metrics.api.Counter; diff --git a/metrics/src/main/java/org/killbill/commons/metrics/guice/DeclaredMethodsTypeListener.java b/metrics/src/main/java/org/killbill/commons/metrics/guice/DeclaredMethodsTypeListener.java index c6c92cab..b3f21e41 100644 --- a/metrics/src/main/java/org/killbill/commons/metrics/guice/DeclaredMethodsTypeListener.java +++ b/metrics/src/main/java/org/killbill/commons/metrics/guice/DeclaredMethodsTypeListener.java @@ -19,7 +19,7 @@ import java.lang.reflect.Method; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import org.aopalliance.intercept.MethodInterceptor; diff --git a/metrics/src/main/java/org/killbill/commons/metrics/guice/DeclaringClassMetricNamer.java b/metrics/src/main/java/org/killbill/commons/metrics/guice/DeclaringClassMetricNamer.java index e7634c7b..afe7c9f2 100644 --- a/metrics/src/main/java/org/killbill/commons/metrics/guice/DeclaringClassMetricNamer.java +++ b/metrics/src/main/java/org/killbill/commons/metrics/guice/DeclaringClassMetricNamer.java @@ -19,7 +19,7 @@ import java.lang.reflect.Method; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import org.killbill.commons.metrics.api.annotation.Counted; import org.killbill.commons.metrics.api.annotation.ExceptionMetered; diff --git a/metrics/src/main/java/org/killbill/commons/metrics/guice/ExceptionMeteredListener.java b/metrics/src/main/java/org/killbill/commons/metrics/guice/ExceptionMeteredListener.java index 173a7de2..a0a53c21 100644 --- a/metrics/src/main/java/org/killbill/commons/metrics/guice/ExceptionMeteredListener.java +++ b/metrics/src/main/java/org/killbill/commons/metrics/guice/ExceptionMeteredListener.java @@ -19,7 +19,7 @@ import java.lang.reflect.Method; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import org.aopalliance.intercept.MethodInterceptor; import org.killbill.commons.metrics.api.Meter; diff --git a/metrics/src/main/java/org/killbill/commons/metrics/guice/GaugeInstanceClassMetricNamer.java b/metrics/src/main/java/org/killbill/commons/metrics/guice/GaugeInstanceClassMetricNamer.java index 9c35c4a1..0099b9bc 100644 --- a/metrics/src/main/java/org/killbill/commons/metrics/guice/GaugeInstanceClassMetricNamer.java +++ b/metrics/src/main/java/org/killbill/commons/metrics/guice/GaugeInstanceClassMetricNamer.java @@ -19,7 +19,7 @@ import java.lang.reflect.Method; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import org.killbill.commons.metrics.api.annotation.Gauge; diff --git a/metrics/src/main/java/org/killbill/commons/metrics/guice/MeteredListener.java b/metrics/src/main/java/org/killbill/commons/metrics/guice/MeteredListener.java index 43ef843b..794cfb47 100644 --- a/metrics/src/main/java/org/killbill/commons/metrics/guice/MeteredListener.java +++ b/metrics/src/main/java/org/killbill/commons/metrics/guice/MeteredListener.java @@ -19,7 +19,7 @@ import java.lang.reflect.Method; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import org.aopalliance.intercept.MethodInterceptor; import org.killbill.commons.metrics.api.Meter; diff --git a/metrics/src/main/java/org/killbill/commons/metrics/guice/MetricNamer.java b/metrics/src/main/java/org/killbill/commons/metrics/guice/MetricNamer.java index 543492bb..7f7ba81f 100644 --- a/metrics/src/main/java/org/killbill/commons/metrics/guice/MetricNamer.java +++ b/metrics/src/main/java/org/killbill/commons/metrics/guice/MetricNamer.java @@ -19,7 +19,7 @@ import java.lang.reflect.Method; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import org.killbill.commons.metrics.api.annotation.Counted; import org.killbill.commons.metrics.api.annotation.ExceptionMetered; diff --git a/metrics/src/main/java/org/killbill/commons/metrics/guice/MetricsInstrumentationModule.java b/metrics/src/main/java/org/killbill/commons/metrics/guice/MetricsInstrumentationModule.java index e5626854..537c4810 100644 --- a/metrics/src/main/java/org/killbill/commons/metrics/guice/MetricsInstrumentationModule.java +++ b/metrics/src/main/java/org/killbill/commons/metrics/guice/MetricsInstrumentationModule.java @@ -17,7 +17,7 @@ package org.killbill.commons.metrics.guice; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import org.killbill.commons.metrics.api.MetricRegistry; import org.killbill.commons.metrics.api.annotation.Counted; diff --git a/metrics/src/main/java/org/killbill/commons/metrics/guice/TimedListener.java b/metrics/src/main/java/org/killbill/commons/metrics/guice/TimedListener.java index 5b107017..9079ebb7 100644 --- a/metrics/src/main/java/org/killbill/commons/metrics/guice/TimedListener.java +++ b/metrics/src/main/java/org/killbill/commons/metrics/guice/TimedListener.java @@ -19,7 +19,7 @@ import java.lang.reflect.Method; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import org.aopalliance.intercept.MethodInterceptor; import org.killbill.commons.metrics.api.MetricRegistry; diff --git a/metrics/src/main/java/org/killbill/commons/metrics/guice/annotation/AnnotationResolver.java b/metrics/src/main/java/org/killbill/commons/metrics/guice/annotation/AnnotationResolver.java index 704af3e3..9b7ad811 100644 --- a/metrics/src/main/java/org/killbill/commons/metrics/guice/annotation/AnnotationResolver.java +++ b/metrics/src/main/java/org/killbill/commons/metrics/guice/annotation/AnnotationResolver.java @@ -20,8 +20,8 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; /** * Finds annotations, if any, pertaining to a particular Method. Extension point for customizing annotation lookup. diff --git a/metrics/src/main/java/org/killbill/commons/metrics/guice/annotation/ClassAnnotationResolver.java b/metrics/src/main/java/org/killbill/commons/metrics/guice/annotation/ClassAnnotationResolver.java index bb42d4dc..012562d4 100644 --- a/metrics/src/main/java/org/killbill/commons/metrics/guice/annotation/ClassAnnotationResolver.java +++ b/metrics/src/main/java/org/killbill/commons/metrics/guice/annotation/ClassAnnotationResolver.java @@ -20,8 +20,8 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; /** * Looks for annotations on the enclosing class of the method. diff --git a/metrics/src/main/java/org/killbill/commons/metrics/guice/annotation/ListAnnotationResolver.java b/metrics/src/main/java/org/killbill/commons/metrics/guice/annotation/ListAnnotationResolver.java index 7134a2fe..8be25d6f 100644 --- a/metrics/src/main/java/org/killbill/commons/metrics/guice/annotation/ListAnnotationResolver.java +++ b/metrics/src/main/java/org/killbill/commons/metrics/guice/annotation/ListAnnotationResolver.java @@ -22,8 +22,8 @@ import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; /** * Delegates to the provided list of resolvers, applying each resolver in turn. diff --git a/metrics/src/main/java/org/killbill/commons/metrics/guice/annotation/MethodAnnotationResolver.java b/metrics/src/main/java/org/killbill/commons/metrics/guice/annotation/MethodAnnotationResolver.java index 75057f5d..a698bb09 100644 --- a/metrics/src/main/java/org/killbill/commons/metrics/guice/annotation/MethodAnnotationResolver.java +++ b/metrics/src/main/java/org/killbill/commons/metrics/guice/annotation/MethodAnnotationResolver.java @@ -20,8 +20,8 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; /** * Looks for annotations on the method itself. diff --git a/queue/src/main/java/org/killbill/bus/DefaultPersistentBus.java b/queue/src/main/java/org/killbill/bus/DefaultPersistentBus.java index e2cace83..14b6a387 100644 --- a/queue/src/main/java/org/killbill/bus/DefaultPersistentBus.java +++ b/queue/src/main/java/org/killbill/bus/DefaultPersistentBus.java @@ -30,7 +30,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import jakarta.inject.Inject; import jakarta.inject.Named; import javax.sql.DataSource; diff --git a/queue/src/main/java/org/killbill/bus/dao/PersistentBusSqlDao.java b/queue/src/main/java/org/killbill/bus/dao/PersistentBusSqlDao.java index cfc4842e..e151ac8e 100644 --- a/queue/src/main/java/org/killbill/bus/dao/PersistentBusSqlDao.java +++ b/queue/src/main/java/org/killbill/bus/dao/PersistentBusSqlDao.java @@ -23,7 +23,7 @@ import java.util.Iterator; import java.util.List; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import org.joda.time.DateTime; import org.killbill.commons.jdbi.statement.SmartFetchSize; diff --git a/queue/src/main/java/org/killbill/notificationq/DefaultNotificationQueue.java b/queue/src/main/java/org/killbill/notificationq/DefaultNotificationQueue.java index 37225b86..7d0b27eb 100644 --- a/queue/src/main/java/org/killbill/notificationq/DefaultNotificationQueue.java +++ b/queue/src/main/java/org/killbill/notificationq/DefaultNotificationQueue.java @@ -29,7 +29,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import org.joda.time.DateTime; import org.killbill.CreatorName; diff --git a/queue/src/main/java/org/killbill/queue/dao/QueueSqlDao.java b/queue/src/main/java/org/killbill/queue/dao/QueueSqlDao.java index 3d9c493e..942c6874 100644 --- a/queue/src/main/java/org/killbill/queue/dao/QueueSqlDao.java +++ b/queue/src/main/java/org/killbill/queue/dao/QueueSqlDao.java @@ -23,7 +23,7 @@ import java.util.Date; import java.util.List; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import org.killbill.commons.jdbi.binder.SmartBindBean; import org.killbill.commons.jdbi.template.KillBillSqlDaoStringTemplate; diff --git a/skeleton/pom.xml b/skeleton/pom.xml index a9c1c1d7..e8631104 100644 --- a/skeleton/pom.xml +++ b/skeleton/pom.xml @@ -54,10 +54,6 @@ jackson-jakarta-rs-json-provider true - - jakarta.annotation - jakarta.annotation-api - com.google.inject guice @@ -73,6 +69,10 @@ metrics-core test + + jakarta.annotation + jakarta.annotation-api + jakarta.inject jakarta.inject-api diff --git a/skeleton/src/main/java/org/killbill/commons/skeleton/metrics/ResourceTimer.java b/skeleton/src/main/java/org/killbill/commons/skeleton/metrics/ResourceTimer.java index a9093265..1a419832 100644 --- a/skeleton/src/main/java/org/killbill/commons/skeleton/metrics/ResourceTimer.java +++ b/skeleton/src/main/java/org/killbill/commons/skeleton/metrics/ResourceTimer.java @@ -25,7 +25,7 @@ import java.util.Map; import java.util.concurrent.TimeUnit; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import org.killbill.commons.metrics.api.MetricRegistry; import org.killbill.commons.metrics.api.Timer; diff --git a/utils/pom.xml b/utils/pom.xml index e1ac9d2c..3416226d 100644 --- a/utils/pom.xml +++ b/utils/pom.xml @@ -32,14 +32,14 @@ - - org.slf4j - slf4j-api - jakarta.annotation jakarta.annotation-api + + org.slf4j + slf4j-api + org.slf4j slf4j-simple diff --git a/utils/src/main/java/org/killbill/commons/eventbus/Subscriber.java b/utils/src/main/java/org/killbill/commons/eventbus/Subscriber.java index 14656cce..5e7db178 100644 --- a/utils/src/main/java/org/killbill/commons/eventbus/Subscriber.java +++ b/utils/src/main/java/org/killbill/commons/eventbus/Subscriber.java @@ -22,7 +22,7 @@ import java.lang.reflect.Method; import java.util.concurrent.Executor; -import javax.annotation.CheckForNull; +import jakarta.annotation.Nullable; import org.killbill.commons.utils.Preconditions; import org.killbill.commons.utils.annotation.VisibleForTesting; @@ -129,7 +129,7 @@ public final int hashCode() { } @Override - public final boolean equals(@CheckForNull final Object obj) { + public final boolean equals(@Nullable final Object obj) { if (obj instanceof Subscriber) { final Subscriber that = (Subscriber) obj; // Use == so that different equal instances will still receive events. diff --git a/utils/src/main/java/org/killbill/commons/eventbus/SubscriberRegistry.java b/utils/src/main/java/org/killbill/commons/eventbus/SubscriberRegistry.java index be3fe697..1c9fc03f 100644 --- a/utils/src/main/java/org/killbill/commons/eventbus/SubscriberRegistry.java +++ b/utils/src/main/java/org/killbill/commons/eventbus/SubscriberRegistry.java @@ -34,7 +34,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArraySet; -import javax.annotation.CheckForNull; +import jakarta.annotation.Nullable; import org.killbill.commons.utils.Preconditions; import org.killbill.commons.utils.Primitives; @@ -232,7 +232,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull final Object o) { + public boolean equals(@Nullable final Object o) { if (o instanceof MethodIdentifier) { final MethodIdentifier ident = (MethodIdentifier) o; return name.equals(ident.name) && parameterTypes.equals(ident.parameterTypes); diff --git a/utils/src/main/java/org/killbill/commons/utils/Joiner.java b/utils/src/main/java/org/killbill/commons/utils/Joiner.java index b01a4fe9..81e2f5b4 100644 --- a/utils/src/main/java/org/killbill/commons/utils/Joiner.java +++ b/utils/src/main/java/org/killbill/commons/utils/Joiner.java @@ -21,7 +21,7 @@ import java.util.AbstractList; import java.util.Iterator; -import javax.annotation.CheckForNull; +import jakarta.annotation.Nullable; import static java.util.Objects.requireNonNull; @@ -106,7 +106,7 @@ CharSequence toString(final Object part) { * Returns a string containing the string representation of each argument, using the previously * configured separator between each. */ - public String join(@CheckForNull final Object first, @CheckForNull final Object second, final Object... rest) { + public String join(@Nullable final Object first, @Nullable final Object second, final Object... rest) { return join(iterable(first, second, rest)); } @@ -122,7 +122,7 @@ public int size() { } @Override - @CheckForNull + @Nullable public Object get(final int index) { switch (index) { case 0: diff --git a/utils/src/main/java/org/killbill/commons/utils/Preconditions.java b/utils/src/main/java/org/killbill/commons/utils/Preconditions.java index c86506d6..c7c898fc 100644 --- a/utils/src/main/java/org/killbill/commons/utils/Preconditions.java +++ b/utils/src/main/java/org/killbill/commons/utils/Preconditions.java @@ -17,8 +17,7 @@ package org.killbill.commons.utils; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,7 +38,7 @@ public final class Preconditions { * string using {@link String#valueOf(Object)} * @throws IllegalStateException if {@code expression} is false */ - public static void checkState(final boolean expression, @CheckForNull final Object errorMessage) { + public static void checkState(final boolean expression, @Nullable final Object errorMessage) { if (!expression) { throw new IllegalStateException(String.valueOf(errorMessage)); } @@ -79,9 +78,9 @@ public static void checkState(final boolean expression, final String errorMessag * @return the non-null reference that was validated * @throws NullPointerException if {@code reference} is null */ - public static T checkNotNull(@CheckForNull final T reference, + public static T checkNotNull(@Nullable final T reference, final String errorMessageTemplate, - @CheckForNull @Nullable final Object... errorMessageArgs) { + @Nullable final Object... errorMessageArgs) { if (reference == null) { throw new NullPointerException(lenientFormat(errorMessageTemplate, errorMessageArgs)); } @@ -94,7 +93,7 @@ public static T checkNotNull(@CheckForNull final T reference, * *

See {@link #checkState(boolean, String, Object...)} for details. */ - public static void checkState(final boolean b, final String errorMessageTemplate, @CheckForNull final Object p1) { + public static void checkState(final boolean b, final String errorMessageTemplate, @Nullable final Object p1) { if (!b) { throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1)); } @@ -124,7 +123,7 @@ public static T checkNotNull(final T reference) { * @return the non-null reference that was validated * @throws NullPointerException if {@code reference} is null */ - public static T checkNotNull(@CheckForNull final T reference, @CheckForNull final Object errorMessage) { + public static T checkNotNull(@Nullable final T reference, @Nullable final Object errorMessage) { if (reference == null) { throw new NullPointerException(String.valueOf(errorMessage)); } @@ -136,14 +135,14 @@ public static T checkNotNull(@CheckForNull final T reference, @CheckForNull * *

See {@link #checkNotNull(Object, String, Object...)} for details. */ - public static T checkNotNull(@CheckForNull final T obj, final String errorMessageTemplate, @CheckForNull final Object p1) { + public static T checkNotNull(@Nullable final T obj, final String errorMessageTemplate, @Nullable final Object p1) { if (obj == null) { throw new NullPointerException(lenientFormat(errorMessageTemplate, p1)); } return obj; } - private static String lenientFormat(@CheckForNull String template, @CheckForNull Object... args) { + private static String lenientFormat(@Nullable String template, @Nullable Object... args) { template = String.valueOf(template); // null -> "null" if (args == null) { @@ -183,7 +182,7 @@ private static String lenientFormat(@CheckForNull String template, @CheckForNull return builder.toString(); } - private static String lenientToString(@CheckForNull final Object o) { + private static String lenientToString(@Nullable final Object o) { if (o == null) { return "null"; } diff --git a/utils/src/main/java/org/killbill/commons/utils/StandardSystemProperty.java b/utils/src/main/java/org/killbill/commons/utils/StandardSystemProperty.java index 117a535d..7cf6859a 100644 --- a/utils/src/main/java/org/killbill/commons/utils/StandardSystemProperty.java +++ b/utils/src/main/java/org/killbill/commons/utils/StandardSystemProperty.java @@ -18,7 +18,7 @@ package org.killbill.commons.utils; -import javax.annotation.CheckForNull; +import jakarta.annotation.Nullable; public enum StandardSystemProperty { @@ -148,7 +148,7 @@ public String key() { *

  • {@code jdk.module.*} (added in Java 9, optional) * */ - @CheckForNull + @Nullable public String value() { return System.getProperty(key); } diff --git a/utils/src/main/java/org/killbill/commons/utils/Strings.java b/utils/src/main/java/org/killbill/commons/utils/Strings.java index dde4fbc2..519f2dba 100644 --- a/utils/src/main/java/org/killbill/commons/utils/Strings.java +++ b/utils/src/main/java/org/killbill/commons/utils/Strings.java @@ -26,7 +26,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.annotation.CheckForNull; +import jakarta.annotation.Nullable; /** * Verbatim copy to guava's Strings (v.31.0.1). See more @@ -69,7 +69,7 @@ public static List split(final String string, final String separator) { * @param string the string to test and possibly return * @return {@code string} itself if it is non-null; {@code ""} if it is null */ - public static String nullToEmpty(@CheckForNull final String string) { + public static String nullToEmpty(@Nullable final String string) { return (string == null) ? "" : string; } diff --git a/utils/src/main/java/org/killbill/commons/utils/collect/AbstractIterator.java b/utils/src/main/java/org/killbill/commons/utils/collect/AbstractIterator.java index 08506610..da9994d3 100644 --- a/utils/src/main/java/org/killbill/commons/utils/collect/AbstractIterator.java +++ b/utils/src/main/java/org/killbill/commons/utils/collect/AbstractIterator.java @@ -20,7 +20,7 @@ import java.util.Iterator; import java.util.NoSuchElementException; -import javax.annotation.CheckForNull; +import jakarta.annotation.Nullable; import org.killbill.commons.utils.Preconditions; @@ -47,7 +47,7 @@ private enum State { FAILED, } - @CheckForNull + @Nullable private T next; /** @@ -74,7 +74,7 @@ private enum State { * this method. Any further attempts to use the iterator will result in an {@link * IllegalStateException}. */ - @CheckForNull + @Nullable protected abstract T computeNext(); /** @@ -84,7 +84,7 @@ private enum State { * @return {@code null}; a convenience so your {@code computeNext} implementation can use the * simple statement {@code return endOfData();} */ - @CheckForNull + @Nullable protected final T endOfData() { state = State.DONE; return null; @@ -143,7 +143,7 @@ public final T peek() { /** * See Guava's NullnessCasts javadoc. */ - static T uncheckedCastNullableTToT(@CheckForNull final T t) { + static T uncheckedCastNullableTToT(@Nullable final T t) { return t; } } diff --git a/utils/src/main/java/org/killbill/commons/utils/collect/ConcatenatedIterator.java b/utils/src/main/java/org/killbill/commons/utils/collect/ConcatenatedIterator.java index 8e5ebff4..999ba8e8 100644 --- a/utils/src/main/java/org/killbill/commons/utils/collect/ConcatenatedIterator.java +++ b/utils/src/main/java/org/killbill/commons/utils/collect/ConcatenatedIterator.java @@ -23,7 +23,7 @@ import java.util.Iterator; import java.util.NoSuchElementException; -import javax.annotation.CheckForNull; +import jakarta.annotation.Nullable; import org.killbill.commons.utils.Preconditions; @@ -32,7 +32,7 @@ */ class ConcatenatedIterator implements Iterator { /* The last iterator to return an element. Calls to remove() go to this iterator. */ - @CheckForNull + @Nullable private Iterator toRemove; /* The iterator currently returning elements. */ @@ -45,11 +45,11 @@ class ConcatenatedIterator implements Iterator { * operation O(1). */ - @CheckForNull + @Nullable private Iterator> topMetaIterator; // Only becomes nonnull if we encounter nested concatenations. - @CheckForNull + @Nullable private Deque>> metaIterators; ConcatenatedIterator(final Iterator> metaIterator) { @@ -58,7 +58,7 @@ class ConcatenatedIterator implements Iterator { } // Returns a nonempty meta-iterator or, if all meta-iterators are empty, null. - @CheckForNull + @Nullable private Iterator> getTopMetaIterator() { while (topMetaIterator == null || !topMetaIterator.hasNext()) { if (metaIterators != null && !metaIterators.isEmpty()) { diff --git a/utils/src/main/java/org/killbill/commons/utils/collect/Iterators.java b/utils/src/main/java/org/killbill/commons/utils/collect/Iterators.java index 5effecf9..69cb1515 100644 --- a/utils/src/main/java/org/killbill/commons/utils/collect/Iterators.java +++ b/utils/src/main/java/org/killbill/commons/utils/collect/Iterators.java @@ -29,7 +29,7 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; -import javax.annotation.CheckForNull; +import jakarta.annotation.Nullable; import org.killbill.commons.utils.Preconditions; @@ -132,7 +132,7 @@ public static Stream toStream(final Iterator iterator) { } /** Returns {@code true} if {@code iterator} contains {@code element}. */ - public static boolean contains(final Iterator iterator, @CheckForNull final Object element) { + public static boolean contains(final Iterator iterator, @Nullable final Object element) { if (element == null) { while (iterator.hasNext()) { if (iterator.next() == null) { diff --git a/utils/src/main/java/org/killbill/commons/utils/concurrent/ThreadFactoryBuilder.java b/utils/src/main/java/org/killbill/commons/utils/concurrent/ThreadFactoryBuilder.java index bfe03265..15a8c3c8 100644 --- a/utils/src/main/java/org/killbill/commons/utils/concurrent/ThreadFactoryBuilder.java +++ b/utils/src/main/java/org/killbill/commons/utils/concurrent/ThreadFactoryBuilder.java @@ -24,7 +24,7 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicLong; -import javax.annotation.CheckForNull; +import jakarta.annotation.Nullable; import org.killbill.commons.utils.Preconditions; @@ -51,12 +51,12 @@ * @since 4.0 */ public final class ThreadFactoryBuilder { - @CheckForNull + @Nullable private String nameFormat = null; - @CheckForNull private Boolean daemon = null; - @CheckForNull private Integer priority = null; - @CheckForNull private UncaughtExceptionHandler uncaughtExceptionHandler = null; - @CheckForNull private ThreadFactory backingThreadFactory = null; + @Nullable private Boolean daemon = null; + @Nullable private Integer priority = null; + @Nullable private UncaughtExceptionHandler uncaughtExceptionHandler = null; + @Nullable private ThreadFactory backingThreadFactory = null; /** Creates a new {@link ThreadFactory} builder. */ public ThreadFactoryBuilder() {} diff --git a/utils/src/main/java/org/killbill/commons/utils/reflect/AbstractInvocationHandler.java b/utils/src/main/java/org/killbill/commons/utils/reflect/AbstractInvocationHandler.java index e67190e7..dd84676d 100644 --- a/utils/src/main/java/org/killbill/commons/utils/reflect/AbstractInvocationHandler.java +++ b/utils/src/main/java/org/killbill/commons/utils/reflect/AbstractInvocationHandler.java @@ -23,7 +23,7 @@ import java.lang.reflect.Proxy; import java.util.Arrays; -import javax.annotation.CheckForNull; +import jakarta.annotation.Nullable; /** * Verbatim copy of Guava's {@code com.google.common.reflect.AbstractInvocationHandler}. @@ -64,8 +64,8 @@ public abstract class AbstractInvocationHandler implements InvocationHandler { * */ @Override - @CheckForNull - public final Object invoke(final Object proxy, final Method method, @CheckForNull Object[] args) throws Throwable { + @Nullable + public final Object invoke(final Object proxy, final Method method, @Nullable Object[] args) throws Throwable { if (args == null) { args = NO_ARGS; } @@ -99,7 +99,7 @@ public final Object invoke(final Object proxy, final Method method, @CheckForNul *

    Unlike {@link #invoke}, {@code args} will never be null. When the method has no parameter, * an empty array is passed in. */ - @CheckForNull + @Nullable protected abstract Object handleInvocation(Object proxy, Method method, Object[] args) throws Throwable; /** @@ -114,7 +114,7 @@ public final Object invoke(final Object proxy, final Method method, @CheckForNul *

    Subclasses can override this method to provide custom equality. */ @Override - public boolean equals(@CheckForNull final Object obj) { + public boolean equals(@Nullable final Object obj) { return super.equals(obj); } diff --git a/utils/src/test/java/org/killbill/commons/eventbus/StringCatcher.java b/utils/src/test/java/org/killbill/commons/eventbus/StringCatcher.java index 6e6fb26f..7f22f7f2 100644 --- a/utils/src/test/java/org/killbill/commons/eventbus/StringCatcher.java +++ b/utils/src/test/java/org/killbill/commons/eventbus/StringCatcher.java @@ -21,7 +21,7 @@ import java.util.ArrayList; import java.util.List; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import org.testng.Assert; From 21aa3a86ac03940fec1adab84d3ddd3d5d396a79 Mon Sep 17 00:00:00 2001 From: xsalefter Date: Thu, 28 May 2026 02:45:21 +0700 Subject: [PATCH 12/33] override @toString() and update docs --- .../commons/utils/concurrent/DirectExecutor.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/utils/src/main/java/org/killbill/commons/utils/concurrent/DirectExecutor.java b/utils/src/main/java/org/killbill/commons/utils/concurrent/DirectExecutor.java index 3f6ab09a..2af48dd0 100644 --- a/utils/src/main/java/org/killbill/commons/utils/concurrent/DirectExecutor.java +++ b/utils/src/main/java/org/killbill/commons/utils/concurrent/DirectExecutor.java @@ -24,6 +24,9 @@ * to avoid circular dependency. * * An {@link Executor} that runs each task in the thread that invokes {@link Executor#execute}. + * + * Changes: + * - Guava implements toString() as "MoreExecutors.directExecutor()", while this enum as "DirectExecutor.INSTANCE" */ public enum DirectExecutor implements Executor { @@ -33,4 +36,10 @@ public enum DirectExecutor implements Executor { public void execute(final Runnable command) { command.run(); } + + @Override + public String toString() { + return "DirectExecutor.INSTANCE"; + } + } From c75b2ee6cdb47aba64d6796d296944c35bb6f86a Mon Sep 17 00:00:00 2001 From: xsalefter Date: Thu, 28 May 2026 02:46:50 +0700 Subject: [PATCH 13/33] replace guava classes with killbill-utils --- jooby/src/main/java/org/jooby/Cookie.java | 2 +- jooby/src/main/java/org/jooby/Jooby.java | 12 ++++++------ jooby/src/main/java/org/jooby/MediaType.java | 2 +- jooby/src/main/java/org/jooby/Result.java | 2 +- jooby/src/main/java/org/jooby/Route.java | 4 ++-- jooby/src/main/java/org/jooby/WebSocket.java | 2 +- .../main/java/org/jooby/handlers/AssetHandler.java | 2 +- .../main/java/org/jooby/handlers/CorsHandler.java | 2 +- .../src/main/java/org/jooby/handlers/SSIHandler.java | 2 +- .../org/jooby/internal/AbstractRendererContext.java | 2 +- .../src/main/java/org/jooby/internal/AppPrinter.java | 2 +- .../main/java/org/jooby/internal/AssetSource.java | 2 +- .../java/org/jooby/internal/HttpHandlerImpl.java | 2 +- .../org/jooby/internal/ServerExecutorProvider.java | 6 +++--- .../org/jooby/internal/handlers/OptionsHandler.java | 2 +- .../java/org/jooby/internal/mvc/RequestParam.java | 2 +- .../org/jooby/servlet/ServletServletRequest.java | 2 +- .../src/test/java/org/jooby/servlet/WebXmlTest.java | 2 +- jooby/src/test/java/org/jooby/test/JoobySuite.java | 7 +++---- .../src/test/java/org/jooby/test/ServerFeature.java | 4 ++-- 20 files changed, 31 insertions(+), 32 deletions(-) diff --git a/jooby/src/main/java/org/jooby/Cookie.java b/jooby/src/main/java/org/jooby/Cookie.java index 36cfa0c7..a41d51ef 100644 --- a/jooby/src/main/java/org/jooby/Cookie.java +++ b/jooby/src/main/java/org/jooby/Cookie.java @@ -16,7 +16,7 @@ package org.jooby; import com.google.common.base.Splitter; -import com.google.common.base.Strings; +import org.killbill.commons.utils.Strings; import com.google.common.io.BaseEncoding; import static java.util.Objects.requireNonNull; import org.jooby.funzy.Throwing; diff --git a/jooby/src/main/java/org/jooby/Jooby.java b/jooby/src/main/java/org/jooby/Jooby.java index 2d26546c..21908f04 100644 --- a/jooby/src/main/java/org/jooby/Jooby.java +++ b/jooby/src/main/java/org/jooby/Jooby.java @@ -15,13 +15,13 @@ */ package org.jooby; -import com.google.common.base.Joiner; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; import com.google.common.escape.Escaper; import com.google.common.html.HtmlEscapers; import com.google.common.net.UrlEscapers; -import com.google.common.util.concurrent.MoreExecutors; +import org.killbill.commons.utils.Joiner; +import static org.killbill.commons.utils.Preconditions.checkArgument; +import static org.killbill.commons.utils.Preconditions.checkState; +import org.killbill.commons.utils.concurrent.DirectExecutor; import com.google.inject.Binder; import com.google.inject.Guice; import com.google.inject.Injector; @@ -2638,9 +2638,9 @@ private Injector bootstrap(final Config args, /** executors: */ if (!defaultExecSet) { // default executor - executor(MoreExecutors.directExecutor()); + executor(DirectExecutor.INSTANCE); } - executor("direct", MoreExecutors.directExecutor()); + executor("direct", DirectExecutor.INSTANCE); executor("server", ServerExecutorProvider.class); /** Some basic xss functions. */ diff --git a/jooby/src/main/java/org/jooby/MediaType.java b/jooby/src/main/java/org/jooby/MediaType.java index a9d1773a..cebf8113 100644 --- a/jooby/src/main/java/org/jooby/MediaType.java +++ b/jooby/src/main/java/org/jooby/MediaType.java @@ -15,7 +15,7 @@ */ package org.jooby; -import static com.google.common.base.Preconditions.checkArgument; +import static org.killbill.commons.utils.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import java.io.File; diff --git a/jooby/src/main/java/org/jooby/Result.java b/jooby/src/main/java/org/jooby/Result.java index d54bc455..c632cd71 100644 --- a/jooby/src/main/java/org/jooby/Result.java +++ b/jooby/src/main/java/org/jooby/Result.java @@ -24,7 +24,7 @@ import java.util.Optional; import java.util.function.Supplier; -import com.google.common.base.Joiner; +import org.killbill.commons.utils.Joiner; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; diff --git a/jooby/src/main/java/org/jooby/Route.java b/jooby/src/main/java/org/jooby/Route.java index 7b6f5fbc..27339f02 100644 --- a/jooby/src/main/java/org/jooby/Route.java +++ b/jooby/src/main/java/org/jooby/Route.java @@ -16,9 +16,9 @@ package org.jooby; import com.google.common.base.CaseFormat; -import static com.google.common.base.Preconditions.checkArgument; -import com.google.common.base.Strings; import com.google.common.primitives.Primitives; +import org.killbill.commons.utils.Strings; +import static org.killbill.commons.utils.Preconditions.checkArgument; import com.google.inject.Key; import com.google.inject.TypeLiteral; import static java.util.Objects.requireNonNull; diff --git a/jooby/src/main/java/org/jooby/WebSocket.java b/jooby/src/main/java/org/jooby/WebSocket.java index e808cf2d..79e55780 100644 --- a/jooby/src/main/java/org/jooby/WebSocket.java +++ b/jooby/src/main/java/org/jooby/WebSocket.java @@ -15,7 +15,7 @@ */ package org.jooby; -import com.google.common.base.Preconditions; +import org.killbill.commons.utils.Preconditions; import com.google.inject.Key; import com.google.inject.TypeLiteral; import static java.util.Objects.requireNonNull; diff --git a/jooby/src/main/java/org/jooby/handlers/AssetHandler.java b/jooby/src/main/java/org/jooby/handlers/AssetHandler.java index d1d8d309..49fd6cfd 100644 --- a/jooby/src/main/java/org/jooby/handlers/AssetHandler.java +++ b/jooby/src/main/java/org/jooby/handlers/AssetHandler.java @@ -15,7 +15,7 @@ */ package org.jooby.handlers; -import com.google.common.base.Strings; +import org.killbill.commons.utils.Strings; import com.typesafe.config.ConfigFactory; import com.typesafe.config.ConfigValueFactory; import org.jooby.Asset; diff --git a/jooby/src/main/java/org/jooby/handlers/CorsHandler.java b/jooby/src/main/java/org/jooby/handlers/CorsHandler.java index d11cf840..bae8150f 100644 --- a/jooby/src/main/java/org/jooby/handlers/CorsHandler.java +++ b/jooby/src/main/java/org/jooby/handlers/CorsHandler.java @@ -29,7 +29,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Joiner; +import org.killbill.commons.utils.Joiner; import com.google.common.base.Splitter; /** diff --git a/jooby/src/main/java/org/jooby/handlers/SSIHandler.java b/jooby/src/main/java/org/jooby/handlers/SSIHandler.java index d5d81ca2..47522792 100644 --- a/jooby/src/main/java/org/jooby/handlers/SSIHandler.java +++ b/jooby/src/main/java/org/jooby/handlers/SSIHandler.java @@ -27,7 +27,7 @@ import org.jooby.Response; import org.jooby.Route; -import com.google.common.io.CharStreams; +import org.killbill.commons.utils.io.CharStreams; /** *

    server side include

    diff --git a/jooby/src/main/java/org/jooby/internal/AbstractRendererContext.java b/jooby/src/main/java/org/jooby/internal/AbstractRendererContext.java index 43503514..1b31e901 100644 --- a/jooby/src/main/java/org/jooby/internal/AbstractRendererContext.java +++ b/jooby/src/main/java/org/jooby/internal/AbstractRendererContext.java @@ -35,7 +35,7 @@ import org.jooby.Status; import org.jooby.View; -import com.google.common.base.Joiner; +import org.killbill.commons.utils.Joiner; public abstract class AbstractRendererContext implements Renderer.Context { diff --git a/jooby/src/main/java/org/jooby/internal/AppPrinter.java b/jooby/src/main/java/org/jooby/internal/AppPrinter.java index 373a1b50..f4cc40e8 100644 --- a/jooby/src/main/java/org/jooby/internal/AppPrinter.java +++ b/jooby/src/main/java/org/jooby/internal/AppPrinter.java @@ -15,10 +15,10 @@ */ package org.jooby.internal; -import com.google.common.base.Strings; import com.typesafe.config.Config; import org.jooby.Route; import org.jooby.WebSocket; +import org.killbill.commons.utils.Strings; import org.slf4j.Logger; import java.util.Set; diff --git a/jooby/src/main/java/org/jooby/internal/AssetSource.java b/jooby/src/main/java/org/jooby/internal/AssetSource.java index 8e19d866..ec805a03 100644 --- a/jooby/src/main/java/org/jooby/internal/AssetSource.java +++ b/jooby/src/main/java/org/jooby/internal/AssetSource.java @@ -15,7 +15,7 @@ */ package org.jooby.internal; -import com.google.common.base.Strings; +import org.killbill.commons.utils.Strings; import java.net.MalformedURLException; import java.net.URL; diff --git a/jooby/src/main/java/org/jooby/internal/HttpHandlerImpl.java b/jooby/src/main/java/org/jooby/internal/HttpHandlerImpl.java index 946665b4..92a982d2 100644 --- a/jooby/src/main/java/org/jooby/internal/HttpHandlerImpl.java +++ b/jooby/src/main/java/org/jooby/internal/HttpHandlerImpl.java @@ -15,7 +15,7 @@ */ package org.jooby.internal; -import com.google.common.base.Strings; +import org.killbill.commons.utils.Strings; import com.google.common.base.Throwables; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; diff --git a/jooby/src/main/java/org/jooby/internal/ServerExecutorProvider.java b/jooby/src/main/java/org/jooby/internal/ServerExecutorProvider.java index 1592d231..74c4b490 100644 --- a/jooby/src/main/java/org/jooby/internal/ServerExecutorProvider.java +++ b/jooby/src/main/java/org/jooby/internal/ServerExecutorProvider.java @@ -15,7 +15,7 @@ */ package org.jooby.internal; -import com.google.common.util.concurrent.MoreExecutors; +import org.killbill.commons.utils.concurrent.DirectExecutor; import com.google.inject.Inject; import com.google.inject.Provider; import org.jooby.spi.Server; @@ -34,8 +34,8 @@ public ServerExecutorProvider(final ServerHolder serverHolder) { requireNonNull(serverHolder, "Server holder is required."); executor = (serverHolder.server != null) ? - serverHolder.server.executor().orElse(MoreExecutors.directExecutor()) : - MoreExecutors.directExecutor(); + serverHolder.server.executor().orElse(DirectExecutor.INSTANCE) : + DirectExecutor.INSTANCE; } @Override diff --git a/jooby/src/main/java/org/jooby/internal/handlers/OptionsHandler.java b/jooby/src/main/java/org/jooby/internal/handlers/OptionsHandler.java index 6390a0c2..50909e28 100644 --- a/jooby/src/main/java/org/jooby/internal/handlers/OptionsHandler.java +++ b/jooby/src/main/java/org/jooby/internal/handlers/OptionsHandler.java @@ -27,7 +27,7 @@ import org.jooby.Route.Definition; import org.jooby.Status; -import com.google.common.base.Joiner; +import org.killbill.commons.utils.Joiner; import com.google.inject.Inject; public class OptionsHandler implements Route.Handler { diff --git a/jooby/src/main/java/org/jooby/internal/mvc/RequestParam.java b/jooby/src/main/java/org/jooby/internal/mvc/RequestParam.java index bf670fd4..f90bfaa9 100644 --- a/jooby/src/main/java/org/jooby/internal/mvc/RequestParam.java +++ b/jooby/src/main/java/org/jooby/internal/mvc/RequestParam.java @@ -15,7 +15,7 @@ */ package org.jooby.internal.mvc; -import com.google.common.base.Strings; +import org.killbill.commons.utils.Strings; import com.google.inject.TypeLiteral; import com.google.inject.util.Types; import org.jooby.Cookie; diff --git a/jooby/src/main/java/org/jooby/servlet/ServletServletRequest.java b/jooby/src/main/java/org/jooby/servlet/ServletServletRequest.java index 200100f3..57431672 100644 --- a/jooby/src/main/java/org/jooby/servlet/ServletServletRequest.java +++ b/jooby/src/main/java/org/jooby/servlet/ServletServletRequest.java @@ -40,7 +40,7 @@ import org.jooby.spi.NativeRequest; import org.jooby.spi.NativeUpload; -import com.google.common.base.Strings; +import org.killbill.commons.utils.Strings; public class ServletServletRequest implements NativeRequest { diff --git a/jooby/src/test/java/org/jooby/servlet/WebXmlTest.java b/jooby/src/test/java/org/jooby/servlet/WebXmlTest.java index 6438fb23..f5f1ff48 100644 --- a/jooby/src/test/java/org/jooby/servlet/WebXmlTest.java +++ b/jooby/src/test/java/org/jooby/servlet/WebXmlTest.java @@ -23,7 +23,7 @@ import org.junit.Test; -import com.google.common.io.CharStreams; +import org.killbill.commons.utils.io.CharStreams; public class WebXmlTest { diff --git a/jooby/src/test/java/org/jooby/test/JoobySuite.java b/jooby/src/test/java/org/jooby/test/JoobySuite.java index df74451a..d792ec0a 100644 --- a/jooby/src/test/java/org/jooby/test/JoobySuite.java +++ b/jooby/src/test/java/org/jooby/test/JoobySuite.java @@ -24,8 +24,7 @@ import org.junit.runners.Suite; import org.junit.runners.model.InitializationError; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; +import java.util.function.Predicate; /** * JUnit suite for Jooby. Internal use only. @@ -49,7 +48,7 @@ public JoobySuite(final Class klass) throws InitializationError { @SuppressWarnings("rawtypes") private List runners(final Class klass) throws InitializationError { List runners = new ArrayList<>(); - Predicate filter = Predicates.alwaysTrue(); + Predicate filter = c -> true; OnServer onserver = klass.getAnnotation(OnServer.class); if (onserver != null) { List> server = Arrays.asList(onserver.value()); @@ -60,7 +59,7 @@ private List runners(final Class klass) throws InitializationError { for (String server : servers) { try { Class serverClass = getClass().getClassLoader().loadClass(server); - if (filter.apply(serverClass)) { + if (filter.test(serverClass)) { runners.add(new JoobyRunner(getTestClass().getJavaClass(), serverClass)); } } catch (ClassNotFoundException ex) { diff --git a/jooby/src/test/java/org/jooby/test/ServerFeature.java b/jooby/src/test/java/org/jooby/test/ServerFeature.java index 295973fa..daef0732 100644 --- a/jooby/src/test/java/org/jooby/test/ServerFeature.java +++ b/jooby/src/test/java/org/jooby/test/ServerFeature.java @@ -15,7 +15,7 @@ */ package org.jooby.test; -import static com.google.common.base.Preconditions.checkState; +import static org.killbill.commons.utils.Preconditions.checkState; import java.io.IOException; @@ -25,7 +25,7 @@ import org.junit.Rule; import org.junit.runner.RunWith; -import com.google.common.base.Joiner; +import org.killbill.commons.utils.Joiner; /** * Internal use only. From 0464c1d1cb0236317ce840382688b6af55525eda Mon Sep 17 00:00:00 2001 From: xsalefter Date: Thu, 28 May 2026 03:04:52 +0700 Subject: [PATCH 14/33] add new method: padStart() and padEnd() --- .../org/killbill/commons/utils/Strings.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/utils/src/main/java/org/killbill/commons/utils/Strings.java b/utils/src/main/java/org/killbill/commons/utils/Strings.java index 519f2dba..20c6041d 100644 --- a/utils/src/main/java/org/killbill/commons/utils/Strings.java +++ b/utils/src/main/java/org/killbill/commons/utils/Strings.java @@ -184,4 +184,38 @@ public static String toSnakeCase(final String str) { } return result.toString(); } + + /** + * Verbatim copy of Guava's Strings.padEnd(string, int, char). See: + * javadoc + */ + public static String padEnd(final String string, final int minLength, final char padChar) { + Preconditions.checkNotNull(string); // eager for GWT. + if (string.length() >= minLength) { + return string; + } + StringBuilder sb = new StringBuilder(minLength); + sb.append(string); + for (int i = string.length(); i < minLength; i++) { + sb.append(padChar); + } + return sb.toString(); + } + + /** + * Verbatim copy of Guava's Strings.padStart(string, int, char). See: + * javadoc + */ + public static String padStart(String string, int minLength, char padChar) { + Preconditions.checkNotNull(string); // eager for GWT. + if (string.length() >= minLength) { + return string; + } + StringBuilder sb = new StringBuilder(minLength); + for (int i = string.length(); i < minLength; i++) { + sb.append(padChar); + } + sb.append(string); + return sb.toString(); + } } From 90de41a15e4fd668d8f935a15e3f74117babe72c Mon Sep 17 00:00:00 2001 From: xsalefter Date: Thu, 28 May 2026 03:05:05 +0700 Subject: [PATCH 15/33] add new method: join(Object[]) --- utils/src/main/java/org/killbill/commons/utils/Joiner.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/utils/src/main/java/org/killbill/commons/utils/Joiner.java b/utils/src/main/java/org/killbill/commons/utils/Joiner.java index 81e2f5b4..cebe70af 100644 --- a/utils/src/main/java/org/killbill/commons/utils/Joiner.java +++ b/utils/src/main/java/org/killbill/commons/utils/Joiner.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.util.AbstractList; +import java.util.Arrays; import java.util.Iterator; import jakarta.annotation.Nullable; @@ -64,6 +65,10 @@ public String join(final Iterator parts) { return appendTo(new StringBuilder(), parts).toString(); } + public String join(final Object[] obj) { + return join(Arrays.asList(obj)); + } + /** * Appends the string representation of each of {@code parts}, using the previously configured * separator between each, to {@code builder}. From 12e82f1ded3de9ba6464920c3fb793c8cd917d4a Mon Sep 17 00:00:00 2001 From: xsalefter Date: Thu, 28 May 2026 06:19:11 +0700 Subject: [PATCH 16/33] exclude Subscriber, SubscriberRegistry and AbstractInvocationHandler --- utils/spotbugs-exclude.xml | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/utils/spotbugs-exclude.xml b/utils/spotbugs-exclude.xml index eb23b139..58f45ce3 100644 --- a/utils/spotbugs-exclude.xml +++ b/utils/spotbugs-exclude.xml @@ -15,4 +15,33 @@ ~ under the License. --> - + + + + + + + + + + + + + + + + + + + + + From 5b7c12732bc73e6d7911b57c772c2a713c1d315f Mon Sep 17 00:00:00 2001 From: xsalefter Date: Fri, 29 May 2026 06:21:39 +0700 Subject: [PATCH 17/33] add port more guava classes and methods to killbill-utils --- utils/spotbugs-exclude.xml | 7 + .../org/killbill/commons/utils/Ascii.java | 153 +++++++++++++++++ .../killbill/commons/utils/CaseFormat.java | 161 ++++++++++++++++++ .../killbill/commons/utils/CharMatcher.java | 105 ++++++++++++ .../killbill/commons/utils/Preconditions.java | 22 +++ .../killbill/commons/utils/Primitives.java | 14 ++ .../killbill/commons/utils/Throwables.java | 41 +++++ .../commons/utils/io/ByteStreams.java | 106 +++++++++++- .../killbill/commons/utils/io/Resources.java | 12 ++ 9 files changed, 620 insertions(+), 1 deletion(-) create mode 100644 utils/src/main/java/org/killbill/commons/utils/Ascii.java create mode 100644 utils/src/main/java/org/killbill/commons/utils/CaseFormat.java create mode 100644 utils/src/main/java/org/killbill/commons/utils/CharMatcher.java create mode 100644 utils/src/main/java/org/killbill/commons/utils/Throwables.java diff --git a/utils/spotbugs-exclude.xml b/utils/spotbugs-exclude.xml index 58f45ce3..2d8a1953 100644 --- a/utils/spotbugs-exclude.xml +++ b/utils/spotbugs-exclude.xml @@ -44,4 +44,11 @@ + + + + + + diff --git a/utils/src/main/java/org/killbill/commons/utils/Ascii.java b/utils/src/main/java/org/killbill/commons/utils/Ascii.java new file mode 100644 index 00000000..f2cccb42 --- /dev/null +++ b/utils/src/main/java/org/killbill/commons/utils/Ascii.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed 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.killbill.commons.utils; + +/** + * Verbatim copy of guava's com.google.common.base.Ascii (v.31.0.1) — case-conversion methods only. + * See More. + * + *

    Static methods pertaining to ASCII characters (those in the range of values {@code 0x00} + * through {@code 0x7F}), and to strings containing such characters. + * + * @author Catherine Berry + * @author Gregory Kick + * @since 7.0 + */ +public final class Ascii { + + private Ascii() {} + + /** + * The difference between the ASCII values of an uppercase character and its lowercase + * counterpart. The value is 0x20 (decimal: 32). + */ + private static final char CASE_MASK = 0x20; + + /** + * Returns a copy of the input string in which all {@linkplain #isUpperCase(char) uppercase ASCII + * characters} have been converted to lowercase. All other characters are copied without + * modification. + */ + public static String toLowerCase(String string) { + int length = string.length(); + for (int i = 0; i < length; i++) { + if (isUpperCase(string.charAt(i))) { + char[] chars = string.toCharArray(); + for (; i < length; i++) { + char c = chars[i]; + if (isUpperCase(c)) { + chars[i] = (char) (c ^ CASE_MASK); + } + } + return String.valueOf(chars); + } + } + return string; + } + + /** + * Returns a copy of the input character sequence in which all {@linkplain #isUpperCase(char) + * uppercase ASCII characters} have been converted to lowercase. All other characters are copied + * without modification. + * + * @since 14.0 + */ + public static String toLowerCase(CharSequence chars) { + if (chars instanceof String) { + return toLowerCase((String) chars); + } + char[] newChars = new char[chars.length()]; + for (int i = 0; i < newChars.length; i++) { + newChars[i] = toLowerCase(chars.charAt(i)); + } + return String.valueOf(newChars); + } + + /** + * If the argument is an {@linkplain #isUpperCase(char) uppercase ASCII character}, returns the + * lowercase equivalent. Otherwise returns the argument. + */ + public static char toLowerCase(char c) { + return isUpperCase(c) ? (char) (c ^ CASE_MASK) : c; + } + + /** + * Returns a copy of the input string in which all {@linkplain #isLowerCase(char) lowercase ASCII + * characters} have been converted to uppercase. All other characters are copied without + * modification. + */ + public static String toUpperCase(String string) { + int length = string.length(); + for (int i = 0; i < length; i++) { + if (isLowerCase(string.charAt(i))) { + char[] chars = string.toCharArray(); + for (; i < length; i++) { + char c = chars[i]; + if (isLowerCase(c)) { + chars[i] = (char) (c ^ CASE_MASK); + } + } + return String.valueOf(chars); + } + } + return string; + } + + /** + * Returns a copy of the input character sequence in which all {@linkplain #isLowerCase(char) + * lowercase ASCII characters} have been converted to uppercase. All other characters are copied + * without modification. + * + * @since 14.0 + */ + public static String toUpperCase(CharSequence chars) { + if (chars instanceof String) { + return toUpperCase((String) chars); + } + char[] newChars = new char[chars.length()]; + for (int i = 0; i < newChars.length; i++) { + newChars[i] = toUpperCase(chars.charAt(i)); + } + return String.valueOf(newChars); + } + + /** + * If the argument is a {@linkplain #isLowerCase(char) lowercase ASCII character}, returns the + * uppercase equivalent. Otherwise returns the argument. + */ + public static char toUpperCase(char c) { + return isLowerCase(c) ? (char) (c ^ CASE_MASK) : c; + } + + /** + * Indicates whether {@code c} is one of the twenty-six lowercase ASCII alphabetic characters + * between {@code 'a'} and {@code 'z'} inclusive. All others (including non-ASCII characters) + * return {@code false}. + */ + public static boolean isLowerCase(char c) { + // Note: This was benchmarked against the alternate expression "(char)(c - 'a') < 26" (Nov '13) + // and found to perform at least as well, or better. + return (c >= 'a') && (c <= 'z'); + } + + /** + * Indicates whether {@code c} is one of the twenty-six uppercase ASCII alphabetic characters + * between {@code 'A'} and {@code 'Z'} inclusive. All others (including non-ASCII characters) + * return {@code false}. + */ + public static boolean isUpperCase(char c) { + return (c >= 'A') && (c <= 'Z'); + } +} diff --git a/utils/src/main/java/org/killbill/commons/utils/CaseFormat.java b/utils/src/main/java/org/killbill/commons/utils/CaseFormat.java new file mode 100644 index 00000000..35ff8dcf --- /dev/null +++ b/utils/src/main/java/org/killbill/commons/utils/CaseFormat.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2006 The Guava Authors + * + * Licensed 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.killbill.commons.utils; + +import static java.util.Objects.requireNonNull; + +/** + * Verbatim copy of guava's com.google.common.base.CaseFormat (v.31.0.1). + * See More. + * + *

    Utility class for converting between various ASCII case formats. Behavior is undefined for + * non-ASCII input. + * + * @author Mike Bostock + * @since 1.0 + */ +public enum CaseFormat { + /** Hyphenated variable naming convention, e.g., "lower-hyphen". */ + LOWER_HYPHEN(CharMatcher.is('-'), "-") { + @Override + String normalizeWord(String word) { + return Ascii.toLowerCase(word); + } + + @Override + String convert(CaseFormat format, String s) { + if (format == LOWER_UNDERSCORE) { + return s.replace('-', '_'); + } + if (format == UPPER_UNDERSCORE) { + return Ascii.toUpperCase(s.replace('-', '_')); + } + return super.convert(format, s); + } + }, + + /** C++ variable naming convention, e.g., "lower_underscore". */ + LOWER_UNDERSCORE(CharMatcher.is('_'), "_") { + @Override + String normalizeWord(String word) { + return Ascii.toLowerCase(word); + } + + @Override + String convert(CaseFormat format, String s) { + if (format == LOWER_HYPHEN) { + return s.replace('_', '-'); + } + if (format == UPPER_UNDERSCORE) { + return Ascii.toUpperCase(s); + } + return super.convert(format, s); + } + }, + + /** Java variable naming convention, e.g., "lowerCamel". */ + LOWER_CAMEL(CharMatcher.inRange('A', 'Z'), "") { + @Override + String normalizeWord(String word) { + return firstCharOnlyToUpper(word); + } + + @Override + String normalizeFirstWord(String word) { + return Ascii.toLowerCase(word); + } + }, + + /** Java and C++ class naming convention, e.g., "UpperCamel". */ + UPPER_CAMEL(CharMatcher.inRange('A', 'Z'), "") { + @Override + String normalizeWord(String word) { + return firstCharOnlyToUpper(word); + } + }, + + /** Java and C++ constant naming convention, e.g., "UPPER_UNDERSCORE". */ + UPPER_UNDERSCORE(CharMatcher.is('_'), "_") { + @Override + String normalizeWord(String word) { + return Ascii.toUpperCase(word); + } + + @Override + String convert(CaseFormat format, String s) { + if (format == LOWER_HYPHEN) { + return Ascii.toLowerCase(s.replace('_', '-')); + } + if (format == LOWER_UNDERSCORE) { + return Ascii.toLowerCase(s); + } + return super.convert(format, s); + } + }; + + private final CharMatcher wordBoundary; + private final String wordSeparator; + + CaseFormat(CharMatcher wordBoundary, String wordSeparator) { + this.wordBoundary = wordBoundary; + this.wordSeparator = wordSeparator; + } + + /** + * Converts the specified {@code String str} from this format to the specified {@code format}. A + * "best effort" approach is taken; if {@code str} does not conform to the assumed format, then + * the behavior of this method is undefined but we make a reasonable effort at converting anyway. + */ + public final String to(CaseFormat format, String str) { + Preconditions.checkNotNull(format); + Preconditions.checkNotNull(str); + return (format == this) ? str : convert(format, str); + } + + /** Enum values can override for performance reasons. */ + String convert(CaseFormat format, String s) { + // deal with camel conversion + StringBuilder out = null; + int i = 0; + int j = -1; + while ((j = wordBoundary.indexIn(s, ++j)) != -1) { + if (i == 0) { + // include some extra space for separators + out = new StringBuilder(s.length() + 4 * format.wordSeparator.length()); + out.append(format.normalizeFirstWord(s.substring(i, j))); + } else { + requireNonNull(out).append(format.normalizeWord(s.substring(i, j))); + } + out.append(format.wordSeparator); + i = j + wordSeparator.length(); + } + return (i == 0) + ? format.normalizeFirstWord(s) + : requireNonNull(out).append(format.normalizeWord(s.substring(i))).toString(); + } + + abstract String normalizeWord(String word); + + String normalizeFirstWord(String word) { + return normalizeWord(word); + } + + private static String firstCharOnlyToUpper(String word) { + return word.isEmpty() + ? word + : Ascii.toUpperCase(word.charAt(0)) + Ascii.toLowerCase(word.substring(1)); + } +} + diff --git a/utils/src/main/java/org/killbill/commons/utils/CharMatcher.java b/utils/src/main/java/org/killbill/commons/utils/CharMatcher.java new file mode 100644 index 00000000..1c19cfd8 --- /dev/null +++ b/utils/src/main/java/org/killbill/commons/utils/CharMatcher.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed 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.killbill.commons.utils; + +/** + * Verbatim copy of guava's com.google.common.base.CharMatcher (v.31.0.1) — minimal subset used by + * CaseFormat. See More. + * + *

    Determines a true or false value for any Java {@code char} value, just as {@link + * java.util.function.Predicate} does for any {@link Object}. + * + * @author Kevin Bourrillion + * @since 1.0 + */ +public abstract class CharMatcher { + + /** Returns a {@code char} matcher that matches only one specified BMP character. */ + public static CharMatcher is(final char match) { + return new Is(match); + } + + /** + * Returns a {@code char} matcher that matches any character in a given BMP range (both endpoints + * are inclusive). For example, to match any lowercase letter of the English alphabet, use {@code + * CharMatcher.inRange('a', 'z')}. + * + * @throws IllegalArgumentException if {@code endInclusive < startInclusive} + */ + public static CharMatcher inRange(final char startInclusive, final char endInclusive) { + return new InRange(startInclusive, endInclusive); + } + + /** Determines a true or false value for the given character. */ + public abstract boolean matches(char c); + + /** + * Returns the index of the first matching character in a character sequence, starting from a + * given position, or {@code -1} if no character matches after that position. + */ + public int indexIn(CharSequence sequence, int start) { + int length = sequence.length(); + Preconditions.checkPositionIndex(start, length); + for (int i = start; i < length; i++) { + if (matches(sequence.charAt(i))) { + return i; + } + } + return -1; + } + + // Implementation of is(char) + private static final class Is extends CharMatcher { + + private final char match; + + Is(char match) { + this.match = match; + } + + @Override + public boolean matches(char c) { + return c == match; + } + + @Override + public String toString() { + return "CharMatcher.is('" + match + "')"; + } + } + + // Implementation of inRange(char, char) + private static final class InRange extends CharMatcher { + + private final char startInclusive; + private final char endInclusive; + + InRange(char startInclusive, char endInclusive) { + Preconditions.checkArgument(endInclusive >= startInclusive); + this.startInclusive = startInclusive; + this.endInclusive = endInclusive; + } + + @Override + public boolean matches(char c) { + return startInclusive <= c && c <= endInclusive; + } + + @Override + public String toString() { + return "CharMatcher.inRange('" + startInclusive + "', '" + endInclusive + "')"; + } + } +} diff --git a/utils/src/main/java/org/killbill/commons/utils/Preconditions.java b/utils/src/main/java/org/killbill/commons/utils/Preconditions.java index c7c898fc..d197bc82 100644 --- a/utils/src/main/java/org/killbill/commons/utils/Preconditions.java +++ b/utils/src/main/java/org/killbill/commons/utils/Preconditions.java @@ -257,4 +257,26 @@ public static void checkArgument(final boolean expression, final String errorMes throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, errorMessageArgs)); } } + + public static void checkArgument(final boolean expression) { + if (!expression) { + throw new IllegalArgumentException(); + } + } + + /** + * Ensures that {@code index} specifies a valid position in an array, list or string of + * size {@code size}. A position index may range from zero to {@code size}, inclusive. + * + * @param index a user-supplied index identifying a position in an array, list or string + * @param size the size of that array, list or string + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is greater than {@code size} + */ + public static int checkPositionIndex(int index, int size) { + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException("index (" + index + ") must not be negative and must not be greater than size (" + size + ")"); + } + return index; + } } diff --git a/utils/src/main/java/org/killbill/commons/utils/Primitives.java b/utils/src/main/java/org/killbill/commons/utils/Primitives.java index 59a29e45..1f20f6df 100644 --- a/utils/src/main/java/org/killbill/commons/utils/Primitives.java +++ b/utils/src/main/java/org/killbill/commons/utils/Primitives.java @@ -30,6 +30,9 @@ public final class Primitives { /** A map from primitive types to their corresponding wrapper types. */ private static final Map, Class> PRIMITIVE_TO_WRAPPER_TYPE; + /** A map from wrapper types to their corresponding primitive types. */ + private static final Map, Class> WRAPPER_TO_PRIMITIVE_TYPE; + static { final Map, Class> primToWrap = new LinkedHashMap<>(16); final Map, Class> wrapToPrim = new LinkedHashMap<>(16); @@ -45,6 +48,7 @@ public final class Primitives { add(primToWrap, wrapToPrim, void.class, Void.class); PRIMITIVE_TO_WRAPPER_TYPE = Collections.unmodifiableMap(primToWrap); + WRAPPER_TO_PRIMITIVE_TYPE = Collections.unmodifiableMap(wrapToPrim); } private static void add(final Map, Class> forward, final Map, Class> backward, final Class key, final Class value) { @@ -70,4 +74,14 @@ public static Class wrap(final Class type) { @SuppressWarnings("unchecked") final Class wrapped = (Class) PRIMITIVE_TO_WRAPPER_TYPE.get(type); return (wrapped == null) ? type : wrapped; } + + /** + * Returns {@code true} if {@code type} is one of the nine primitive-wrapper types, such as + * {@link Integer}. + * + * @see Class#isPrimitive + */ + public static boolean isWrapperType(final Class type) { + return WRAPPER_TO_PRIMITIVE_TYPE.containsKey(Preconditions.checkNotNull(type)); + } } diff --git a/utils/src/main/java/org/killbill/commons/utils/Throwables.java b/utils/src/main/java/org/killbill/commons/utils/Throwables.java new file mode 100644 index 00000000..82d15206 --- /dev/null +++ b/utils/src/main/java/org/killbill/commons/utils/Throwables.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.killbill.commons.utils; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * Verbatim copy of guava's com.google.common.base.Throwables (v.31.0.1). + * Only includes the methods used within killbill. + * + * @see Guava removal tracking + */ +public final class Throwables { + + private Throwables() {} + + /** + * Returns a string containing the result of {@link Throwable#toString() toString()}, followed by + * the full, recursive stack trace of {@code throwable}. Note that you probably should not be + * parsing the resulting string; if you need programmatic access to the stack frames, you can call + * {@link Throwable#getStackTrace()}. + */ + public static String getStackTraceAsString(final Throwable throwable) { + final StringWriter stringWriter = new StringWriter(); + throwable.printStackTrace(new PrintWriter(stringWriter)); + return stringWriter.toString(); + } +} diff --git a/utils/src/main/java/org/killbill/commons/utils/io/ByteStreams.java b/utils/src/main/java/org/killbill/commons/utils/io/ByteStreams.java index ed259250..431f92b3 100644 --- a/utils/src/main/java/org/killbill/commons/utils/io/ByteStreams.java +++ b/utils/src/main/java/org/killbill/commons/utils/io/ByteStreams.java @@ -17,8 +17,10 @@ package org.killbill.commons.utils.io; +import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.util.ArrayDeque; import java.util.Queue; @@ -26,7 +28,7 @@ import org.killbill.commons.utils.math.IntMath; /** - * Contains verbatim copy to guava's Joiner (v.31.0.1). See More. + * Contains verbatim copy to guava's ByteStreams (v.31.0.1). See More. */ public final class ByteStreams { @@ -95,4 +97,106 @@ private static byte[] combineBuffers(final Queue bufs, final int totalLe } return result; } + + /** + * Copies all bytes from the input stream to the output stream. Does not close or flush either + * stream. + * + * Keep the method signature exist for Jooby's sake, but using `inputStream.transferTo()` as implementation. + * Note that, all other killbill codebase (notably in killbill-platform) use inputStream.transferTo() directly. + * + * @param from the input stream to read from + * @param to the output stream to write to + * @return the number of bytes copied + * @throws IOException if an I/O error occurs + */ + public static long copy(final InputStream from, final OutputStream to) throws IOException { + Preconditions.checkNotNull(from); + Preconditions.checkNotNull(to); + return from.transferTo(to); + } + + /** + * Wraps a {@link InputStream}, limiting the number of bytes which can be read. + * + * @param in the input stream to be wrapped + * @param limit the maximum number of bytes to be read from {@code in} + * @return a length-limited {@link InputStream} + */ + public static InputStream limit(final InputStream in, final long limit) { + return new LimitedInputStream(in, limit); + } + + private static final class LimitedInputStream extends FilterInputStream { + + private long left; + private long mark = -1; + + LimitedInputStream(InputStream in, long limit) { + super(in); + Preconditions.checkNotNull(in); + Preconditions.checkArgument(limit >= 0, "limit must be non-negative"); + left = limit; + } + + @Override + public int available() throws IOException { + return (int) Math.min(in.available(), left); + } + + // it's okay to mark even if mark isn't supported, as reset won't work + @Override + public synchronized void mark(int readLimit) { + in.mark(readLimit); + mark = left; + } + + @Override + public int read() throws IOException { + if (left == 0) { + return -1; + } + + int result = in.read(); + if (result != -1) { + --left; + } + return result; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (left == 0) { + return -1; + } + + len = (int) Math.min(len, left); + int result = in.read(b, off, len); + if (result != -1) { + left -= result; + } + return result; + } + + @Override + public synchronized void reset() throws IOException { + if (!in.markSupported()) { + throw new IOException("Mark not supported"); + } + if (mark == -1) { + throw new IOException("Mark not set"); + } + + in.reset(); + left = mark; + } + + @Override + public long skip(long n) throws IOException { + n = Math.min(n, left); + long skipped = in.skip(n); + left -= skipped; + return skipped; + } + } } diff --git a/utils/src/main/java/org/killbill/commons/utils/io/Resources.java b/utils/src/main/java/org/killbill/commons/utils/io/Resources.java index 4c0e6faf..9e5e1909 100644 --- a/utils/src/main/java/org/killbill/commons/utils/io/Resources.java +++ b/utils/src/main/java/org/killbill/commons/utils/io/Resources.java @@ -42,4 +42,16 @@ public static URL getResource(final String resourceName) { Preconditions.checkArgument(url != null, "resource %s not found.", resourceName); return url; } + + /** + * Given a {@code resourceName} that is relative to {@code contextClass}, returns a {@code URL} + * pointing to the named resource. + * + * @throws IllegalArgumentException if the resource is not found + */ + public static URL getResource(final Class contextClass, final String resourceName) { + final URL url = contextClass.getResource(resourceName); + Preconditions.checkArgument(url != null, "resource %s relative to %s not found.", resourceName, contextClass.getName()); + return url; + } } From 38ae425c68276a49a058ac559d5469811cc8dc45 Mon Sep 17 00:00:00 2001 From: xsalefter Date: Fri, 29 May 2026 06:22:25 +0700 Subject: [PATCH 18/33] replace guava classes with new killbill-utils classes from previous commit --- jooby/src/main/java/org/jooby/Asset.java | 8 +- jooby/src/main/java/org/jooby/Cookie.java | 7 +- jooby/src/main/java/org/jooby/Err.java | 2 +- jooby/src/main/java/org/jooby/Mutant.java | 2 +- jooby/src/main/java/org/jooby/Renderer.java | 2 +- jooby/src/main/java/org/jooby/Route.java | 4 +- jooby/src/main/java/org/jooby/Session.java | 4 +- .../org/jooby/internal/BodyReferenceImpl.java | 2 +- .../org/jooby/internal/HttpHandlerImpl.java | 2 +- .../jooby/internal/HttpRendererContext.java | 2 +- .../org/jooby/internal/RouteMetadata.java | 2 +- .../internal/StaticMethodTypeConverter.java | 2 +- .../StringConstructTypeConverter.java | 2 +- .../org/jooby/internal/jetty/JettyServer.java | 2 +- .../org/jooby/internal/mvc/MvcRoutes.java | 2 +- .../org/jooby/internal/parser/BeanParser.java | 2 +- .../org/jooby/internal/ssl/PemReader.java | 16 +-- .../jooby/servlet/ServletServletResponse.java | 2 +- jooby/src/test/java/jetty/H2Jetty.java | 2 +- jooby/src/test/java/org/jooby/AssetTest.java | 122 ++++++++++++++++++ .../jooby/internal/BodyReferenceImplTest.java | 6 +- .../org/jooby/internal/RouteMetadataTest.java | 2 +- .../java/org/jooby/internal/URLAssetTest.java | 2 +- .../servlet/ServletServletResponseTest.java | 2 +- 24 files changed, 159 insertions(+), 42 deletions(-) create mode 100644 jooby/src/test/java/org/jooby/AssetTest.java diff --git a/jooby/src/main/java/org/jooby/Asset.java b/jooby/src/main/java/org/jooby/Asset.java index 578984b3..d255c11b 100644 --- a/jooby/src/main/java/org/jooby/Asset.java +++ b/jooby/src/main/java/org/jooby/Asset.java @@ -21,8 +21,8 @@ import java.net.URISyntaxException; import java.net.URL; import java.nio.ByteBuffer; +import java.util.Base64; -import com.google.common.io.BaseEncoding; import org.jooby.funzy.Throwing; import jakarta.annotation.Nonnull; @@ -138,11 +138,11 @@ default String etag() { StringBuilder b = new StringBuilder(32); b.append("W/\""); - BaseEncoding b64 = BaseEncoding.base64(); + Base64.Encoder b64 = Base64.getEncoder(); int lhash = resource().toURI().hashCode(); - b.append(b64.encode(ByteBuffer.allocate(Long.BYTES).putLong(lastModified() ^ lhash).array())); - b.append(b64.encode(ByteBuffer.allocate(Long.BYTES).putLong(length() ^ lhash).array())); + b.append(b64.encodeToString(ByteBuffer.allocate(Long.BYTES).putLong(lastModified() ^ lhash).array())); + b.append(b64.encodeToString(ByteBuffer.allocate(Long.BYTES).putLong(length() ^ lhash).array())); b.append('"'); return b.toString(); } catch (URISyntaxException x) { diff --git a/jooby/src/main/java/org/jooby/Cookie.java b/jooby/src/main/java/org/jooby/Cookie.java index a41d51ef..5be2e4fe 100644 --- a/jooby/src/main/java/org/jooby/Cookie.java +++ b/jooby/src/main/java/org/jooby/Cookie.java @@ -17,7 +17,6 @@ import com.google.common.base.Splitter; import org.killbill.commons.utils.Strings; -import com.google.common.io.BaseEncoding; import static java.util.Objects.requireNonNull; import org.jooby.funzy.Throwing; import org.jooby.internal.CookieImpl; @@ -35,7 +34,6 @@ import java.util.Objects; import java.util.Optional; import java.util.function.Function; -import java.util.regex.Pattern; import java.util.stream.Collectors; /** @@ -417,9 +415,6 @@ public Optional maxAge() { */ public class Signature { - /** Remove trailing '='. */ - private static final Pattern EQ = Pattern.compile("=+$"); - /** Algorithm name. */ public static final String HMAC_SHA256 = "HmacSHA256"; @@ -448,7 +443,7 @@ public static String sign(final String value, final String secret) { Mac mac = Mac.getInstance(HMAC_SHA256); mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), HMAC_SHA256)); byte[] bytes = mac.doFinal(value.getBytes(StandardCharsets.UTF_8)); - return EQ.matcher(BaseEncoding.base64().encode(bytes)).replaceAll("") + SEP + value; + return java.util.Base64.getEncoder().withoutPadding().encodeToString(bytes) + SEP + value; } catch (Exception ex) { throw new IllegalArgumentException("Can't sign value", ex); } diff --git a/jooby/src/main/java/org/jooby/Err.java b/jooby/src/main/java/org/jooby/Err.java index 9640bb0e..0d4fdd7c 100644 --- a/jooby/src/main/java/org/jooby/Err.java +++ b/jooby/src/main/java/org/jooby/Err.java @@ -25,7 +25,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Throwables; +import org.killbill.commons.utils.Throwables; import jakarta.annotation.Nullable; diff --git a/jooby/src/main/java/org/jooby/Mutant.java b/jooby/src/main/java/org/jooby/Mutant.java index b6925c87..6e52dbac 100644 --- a/jooby/src/main/java/org/jooby/Mutant.java +++ b/jooby/src/main/java/org/jooby/Mutant.java @@ -21,7 +21,7 @@ import java.util.Set; import java.util.SortedSet; -import com.google.common.primitives.Primitives; +import org.killbill.commons.utils.Primitives; import com.google.inject.TypeLiteral; import com.google.inject.util.Types; diff --git a/jooby/src/main/java/org/jooby/Renderer.java b/jooby/src/main/java/org/jooby/Renderer.java index bd03c534..f0e4fe1b 100644 --- a/jooby/src/main/java/org/jooby/Renderer.java +++ b/jooby/src/main/java/org/jooby/Renderer.java @@ -25,7 +25,7 @@ import java.util.Map; import java.util.Set; -import com.google.common.base.CaseFormat; +import org.killbill.commons.utils.CaseFormat; import com.google.inject.Key; import com.google.inject.TypeLiteral; diff --git a/jooby/src/main/java/org/jooby/Route.java b/jooby/src/main/java/org/jooby/Route.java index 27339f02..89d0f3c6 100644 --- a/jooby/src/main/java/org/jooby/Route.java +++ b/jooby/src/main/java/org/jooby/Route.java @@ -15,8 +15,8 @@ */ package org.jooby; -import com.google.common.base.CaseFormat; -import com.google.common.primitives.Primitives; +import org.killbill.commons.utils.CaseFormat; +import org.killbill.commons.utils.Primitives; import org.killbill.commons.utils.Strings; import static org.killbill.commons.utils.Preconditions.checkArgument; import com.google.inject.Key; diff --git a/jooby/src/main/java/org/jooby/Session.java b/jooby/src/main/java/org/jooby/Session.java index 6248e4d5..50225e6b 100644 --- a/jooby/src/main/java/org/jooby/Session.java +++ b/jooby/src/main/java/org/jooby/Session.java @@ -15,7 +15,7 @@ */ package org.jooby; -import com.google.common.io.BaseEncoding; +import java.util.Base64; import static java.util.Objects.requireNonNull; import jakarta.annotation.Nonnull; @@ -240,7 +240,7 @@ interface Store { default String generateID() { byte[] bytes = new byte[30]; rnd.nextBytes(bytes); - return BaseEncoding.base64Url().encode(bytes); + return Base64.getUrlEncoder().encodeToString(bytes); } } diff --git a/jooby/src/main/java/org/jooby/internal/BodyReferenceImpl.java b/jooby/src/main/java/org/jooby/internal/BodyReferenceImpl.java index 992c30fd..3d5cb46a 100644 --- a/jooby/src/main/java/org/jooby/internal/BodyReferenceImpl.java +++ b/jooby/src/main/java/org/jooby/internal/BodyReferenceImpl.java @@ -26,7 +26,7 @@ import org.jooby.Parser; -import com.google.common.io.ByteStreams; +import org.killbill.commons.utils.io.ByteStreams; import org.jooby.funzy.Try; public class BodyReferenceImpl implements Parser.BodyReference { diff --git a/jooby/src/main/java/org/jooby/internal/HttpHandlerImpl.java b/jooby/src/main/java/org/jooby/internal/HttpHandlerImpl.java index 92a982d2..6d707da5 100644 --- a/jooby/src/main/java/org/jooby/internal/HttpHandlerImpl.java +++ b/jooby/src/main/java/org/jooby/internal/HttpHandlerImpl.java @@ -16,7 +16,7 @@ package org.jooby.internal; import org.killbill.commons.utils.Strings; -import com.google.common.base.Throwables; +import org.killbill.commons.utils.Throwables; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; diff --git a/jooby/src/main/java/org/jooby/internal/HttpRendererContext.java b/jooby/src/main/java/org/jooby/internal/HttpRendererContext.java index faf64831..80965a89 100644 --- a/jooby/src/main/java/org/jooby/internal/HttpRendererContext.java +++ b/jooby/src/main/java/org/jooby/internal/HttpRendererContext.java @@ -15,7 +15,7 @@ */ package org.jooby.internal; -import com.google.common.io.ByteStreams; +import org.killbill.commons.utils.io.ByteStreams; import org.jooby.Err; import org.jooby.MediaType; import org.jooby.Renderer; diff --git a/jooby/src/main/java/org/jooby/internal/RouteMetadata.java b/jooby/src/main/java/org/jooby/internal/RouteMetadata.java index de0ec395..e94672af 100644 --- a/jooby/src/main/java/org/jooby/internal/RouteMetadata.java +++ b/jooby/src/main/java/org/jooby/internal/RouteMetadata.java @@ -36,7 +36,7 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import com.google.common.io.Resources; +import org.killbill.commons.utils.io.Resources; import com.google.common.util.concurrent.UncheckedExecutionException; public class RouteMetadata implements ParameterNameProvider { diff --git a/jooby/src/main/java/org/jooby/internal/StaticMethodTypeConverter.java b/jooby/src/main/java/org/jooby/internal/StaticMethodTypeConverter.java index 94ceab79..be717121 100644 --- a/jooby/src/main/java/org/jooby/internal/StaticMethodTypeConverter.java +++ b/jooby/src/main/java/org/jooby/internal/StaticMethodTypeConverter.java @@ -17,7 +17,7 @@ import org.jooby.internal.parser.StaticMethodParser; -import com.google.common.primitives.Primitives; +import org.killbill.commons.utils.Primitives; import com.google.inject.TypeLiteral; import com.google.inject.matcher.AbstractMatcher; import com.google.inject.spi.TypeConverter; diff --git a/jooby/src/main/java/org/jooby/internal/StringConstructTypeConverter.java b/jooby/src/main/java/org/jooby/internal/StringConstructTypeConverter.java index 9278991e..c2a17887 100644 --- a/jooby/src/main/java/org/jooby/internal/StringConstructTypeConverter.java +++ b/jooby/src/main/java/org/jooby/internal/StringConstructTypeConverter.java @@ -19,7 +19,7 @@ import org.jooby.internal.parser.StringConstructorParser; -import com.google.common.primitives.Primitives; +import org.killbill.commons.utils.Primitives; import com.google.inject.TypeLiteral; import com.google.inject.matcher.AbstractMatcher; import com.google.inject.spi.TypeConverter; diff --git a/jooby/src/main/java/org/jooby/internal/jetty/JettyServer.java b/jooby/src/main/java/org/jooby/internal/jetty/JettyServer.java index 04861918..fbfd1089 100644 --- a/jooby/src/main/java/org/jooby/internal/jetty/JettyServer.java +++ b/jooby/src/main/java/org/jooby/internal/jetty/JettyServer.java @@ -15,7 +15,7 @@ */ package org.jooby.internal.jetty; -import com.google.common.primitives.Primitives; +import org.killbill.commons.utils.Primitives; import com.typesafe.config.Config; import com.typesafe.config.ConfigException; import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory; diff --git a/jooby/src/main/java/org/jooby/internal/mvc/MvcRoutes.java b/jooby/src/main/java/org/jooby/internal/mvc/MvcRoutes.java index d8240123..1b7a4815 100644 --- a/jooby/src/main/java/org/jooby/internal/mvc/MvcRoutes.java +++ b/jooby/src/main/java/org/jooby/internal/mvc/MvcRoutes.java @@ -15,7 +15,7 @@ */ package org.jooby.internal.mvc; -import com.google.common.base.CaseFormat; +import org.killbill.commons.utils.CaseFormat; import org.jooby.Env; import org.jooby.MediaType; import org.jooby.Route; diff --git a/jooby/src/main/java/org/jooby/internal/parser/BeanParser.java b/jooby/src/main/java/org/jooby/internal/parser/BeanParser.java index 9c05c251..b94dc536 100644 --- a/jooby/src/main/java/org/jooby/internal/parser/BeanParser.java +++ b/jooby/src/main/java/org/jooby/internal/parser/BeanParser.java @@ -15,7 +15,7 @@ */ package org.jooby.internal.parser; -import com.google.common.primitives.Primitives; +import org.killbill.commons.utils.Primitives; import com.google.inject.TypeLiteral; import org.jooby.Err; import org.jooby.Mutant; diff --git a/jooby/src/main/java/org/jooby/internal/ssl/PemReader.java b/jooby/src/main/java/org/jooby/internal/ssl/PemReader.java index 37465987..cb61f8e2 100644 --- a/jooby/src/main/java/org/jooby/internal/ssl/PemReader.java +++ b/jooby/src/main/java/org/jooby/internal/ssl/PemReader.java @@ -28,7 +28,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.google.common.io.BaseEncoding; +import java.util.Base64; /** * Reads a PEM file and converts it into a list of DERs so that they are imported into a @@ -51,16 +51,18 @@ final class PemReader { "-+END\\s+.*PRIVATE\\s+KEY[^-]*-+", // Footer Pattern.CASE_INSENSITIVE); + private static final Base64.Decoder BASE64_DECODER = Base64.getMimeDecoder(); + static List readCertificates(final File file) throws CertificateException, IOException { String content = Files.readString(file.toPath(), StandardCharsets.US_ASCII); - BaseEncoding base64 = base64(); - List certs = new ArrayList(); + // Originally, we have local variables: base64 = return BaseEncoding.base64().withSeparator("\n", '\n'); + List certs = new ArrayList<>(); Matcher m = CERT_PATTERN.matcher(content); int start = 0; while (m.find(start)) { - ByteBuffer buffer = ByteBuffer.wrap(base64.decode(m.group(1))); + ByteBuffer buffer = ByteBuffer.wrap(BASE64_DECODER.decode(m.group(1))); certs.add(buffer); start = m.end(); @@ -73,10 +75,6 @@ static List readCertificates(final File file) return certs; } - private static BaseEncoding base64() { - return BaseEncoding.base64().withSeparator("\n", '\n'); - } - static ByteBuffer readPrivateKey(final File file) throws KeyException, IOException { String content = Files.readString(file.toPath(), StandardCharsets.US_ASCII); @@ -86,7 +84,7 @@ static ByteBuffer readPrivateKey(final File file) throws KeyException, IOExcepti } String value = m.group(1); - return ByteBuffer.wrap(base64().decode(value)); + return ByteBuffer.wrap(BASE64_DECODER.decode(value)); } private PemReader() { diff --git a/jooby/src/main/java/org/jooby/servlet/ServletServletResponse.java b/jooby/src/main/java/org/jooby/servlet/ServletServletResponse.java index 20e7cde4..11ee84c3 100644 --- a/jooby/src/main/java/org/jooby/servlet/ServletServletResponse.java +++ b/jooby/src/main/java/org/jooby/servlet/ServletServletResponse.java @@ -15,7 +15,7 @@ */ package org.jooby.servlet; -import com.google.common.io.ByteStreams; +import org.killbill.commons.utils.io.ByteStreams; import static java.util.Objects.requireNonNull; import org.jooby.funzy.Try; diff --git a/jooby/src/test/java/jetty/H2Jetty.java b/jooby/src/test/java/jetty/H2Jetty.java index 7335dbd2..451751e7 100644 --- a/jooby/src/test/java/jetty/H2Jetty.java +++ b/jooby/src/test/java/jetty/H2Jetty.java @@ -15,7 +15,7 @@ */ package jetty; -import com.google.common.io.ByteStreams; +import org.killbill.commons.utils.io.ByteStreams; import org.jooby.Jooby; import org.jooby.MediaType; import org.jooby.Results; diff --git a/jooby/src/test/java/org/jooby/AssetTest.java b/jooby/src/test/java/org/jooby/AssetTest.java new file mode 100644 index 00000000..691b04c0 --- /dev/null +++ b/jooby/src/test/java/org/jooby/AssetTest.java @@ -0,0 +1,122 @@ +/* + * Copyright 2026 The Billing Project, LLC + * + * The Billing Project 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.jooby; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.InputStream; +import java.net.URL; + +import org.junit.Test; + +public class AssetTest { + + private Asset createAsset(final URL resource, final long lastModified, final long length) { + return new Asset() { + @Override + public String path() { + return resource.getPath(); + } + + @Override + public URL resource() { + return resource; + } + + @Override + public long lastModified() { + return lastModified; + } + + @Override + public long length() { + return length; + } + + @Override + public InputStream stream() { + return null; + } + + @Override + public MediaType type() { + return MediaType.js; + } + }; + } + + @Test + public void etagFormat() throws Exception { + URL url = new URL("file:///assets/app.js"); + Asset asset = createAsset(url, 1700000000000L, 4096L); + + String etag = asset.etag(); + // Weak etag format: W/"" + assertTrue("Should start with W/\"", etag.startsWith("W/\"")); + assertTrue("Should end with \"", etag.endsWith("\"")); + } + + @Test + public void etagDeterministic() throws Exception { + URL url = new URL("file:///assets/app.js"); + Asset a1 = createAsset(url, 1700000000000L, 4096L); + Asset a2 = createAsset(url, 1700000000000L, 4096L); + + assertEquals("Same inputs must produce same etag", a1.etag(), a2.etag()); + } + + @Test + public void etagSensitiveToLastModified() throws Exception { + URL url = new URL("file:///assets/app.js"); + Asset a1 = createAsset(url, 1700000000000L, 4096L); + Asset a2 = createAsset(url, 1700000001000L, 4096L); + + assertNotEquals("Different lastModified must produce different etag", a1.etag(), a2.etag()); + } + + @Test + public void etagSensitiveToLength() throws Exception { + URL url = new URL("file:///assets/app.js"); + Asset a1 = createAsset(url, 1700000000000L, 4096L); + Asset a2 = createAsset(url, 1700000000000L, 8192L); + + assertNotEquals("Different length must produce different etag", a1.etag(), a2.etag()); + } + + @Test + public void etagSensitiveToResource() throws Exception { + URL url1 = new URL("file:///assets/app.js"); + URL url2 = new URL("file:///assets/vendor.js"); + Asset a1 = createAsset(url1, 1700000000000L, 4096L); + Asset a2 = createAsset(url2, 1700000000000L, 4096L); + + assertNotEquals("Different resource must produce different etag", a1.etag(), a2.etag()); + } + + @Test + public void etagExactValue() throws Exception { + // Pin a specific input and verify exact output to detect regressions. + // This value was captured from the original Guava (BaseEncoding + Longs) implementation. + URL url = new URL("file:///assets/app.js"); + Asset asset = createAsset(url, 1700000000000L, 4096L); + + String expected = "W/\"///+dC0YuPc=/////+L9wPc=\""; + assertEquals(expected, asset.etag()); + } +} diff --git a/jooby/src/test/java/org/jooby/internal/BodyReferenceImplTest.java b/jooby/src/test/java/org/jooby/internal/BodyReferenceImplTest.java index c1771485..84cfb806 100644 --- a/jooby/src/test/java/org/jooby/internal/BodyReferenceImplTest.java +++ b/jooby/src/test/java/org/jooby/internal/BodyReferenceImplTest.java @@ -17,6 +17,8 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.when; @@ -34,7 +36,7 @@ import org.jooby.test.MockUnit.Block; import org.junit.Test; -import com.google.common.io.ByteStreams; +import org.killbill.commons.utils.io.ByteStreams; public class BodyReferenceImplTest { @@ -77,7 +79,7 @@ public void inErr() throws Exception { .expect(unit -> { InputStream in = unit.get(InputStream.class); - when(in.read(unit.capture(byte[].class))).thenThrow(new IOException()); + when(in.transferTo(any(OutputStream.class))).thenThrow(new IOException()); OutputStream out = unit.get(ByteArrayOutputStream.class); diff --git a/jooby/src/test/java/org/jooby/internal/RouteMetadataTest.java b/jooby/src/test/java/org/jooby/internal/RouteMetadataTest.java index 43f155ca..16689bfd 100644 --- a/jooby/src/test/java/org/jooby/internal/RouteMetadataTest.java +++ b/jooby/src/test/java/org/jooby/internal/RouteMetadataTest.java @@ -35,7 +35,7 @@ import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; -import com.google.common.io.Resources; +import org.killbill.commons.utils.io.Resources; import com.typesafe.config.Config; public class RouteMetadataTest { diff --git a/jooby/src/test/java/org/jooby/internal/URLAssetTest.java b/jooby/src/test/java/org/jooby/internal/URLAssetTest.java index a5dce848..097cd5df 100644 --- a/jooby/src/test/java/org/jooby/internal/URLAssetTest.java +++ b/jooby/src/test/java/org/jooby/internal/URLAssetTest.java @@ -30,7 +30,7 @@ import org.jooby.test.MockUnit; import org.junit.Test; -import com.google.common.io.ByteStreams; +import org.killbill.commons.utils.io.ByteStreams; public class URLAssetTest { diff --git a/jooby/src/test/java/org/jooby/servlet/ServletServletResponseTest.java b/jooby/src/test/java/org/jooby/servlet/ServletServletResponseTest.java index 4572b84e..83a53329 100644 --- a/jooby/src/test/java/org/jooby/servlet/ServletServletResponseTest.java +++ b/jooby/src/test/java/org/jooby/servlet/ServletServletResponseTest.java @@ -15,7 +15,7 @@ */ package org.jooby.servlet; -import com.google.common.io.ByteStreams; +import org.killbill.commons.utils.io.ByteStreams; import static org.mockito.Mockito.when; import org.jooby.funzy.Throwing; import org.jooby.test.MockUnit; From 5f312300a8362c57b27b512a331029cb52278719 Mon Sep 17 00:00:00 2001 From: xsalefter Date: Sun, 31 May 2026 03:53:10 +0700 Subject: [PATCH 19/33] jooby SseRenderer use killbill-utils' (modified) ByteSource instead of Guava --- .../java/org/jooby/internal/SseRenderer.java | 2 +- .../killbill/commons/utils/io/ByteSource.java | 120 +++++++++++ .../commons/utils/io/ByteSourceTest.java | 201 ++++++++++++++++++ 3 files changed, 322 insertions(+), 1 deletion(-) create mode 100644 utils/src/main/java/org/killbill/commons/utils/io/ByteSource.java create mode 100644 utils/src/test/java/org/killbill/commons/utils/io/ByteSourceTest.java diff --git a/jooby/src/main/java/org/jooby/internal/SseRenderer.java b/jooby/src/main/java/org/jooby/internal/SseRenderer.java index 1ee18acf..b5510c36 100644 --- a/jooby/src/main/java/org/jooby/internal/SseRenderer.java +++ b/jooby/src/main/java/org/jooby/internal/SseRenderer.java @@ -15,7 +15,7 @@ */ package org.jooby.internal; -import com.google.common.io.ByteSource; +import org.killbill.commons.utils.io.ByteSource; import org.jooby.MediaType; import org.jooby.Renderer; import org.jooby.Sse; diff --git a/utils/src/main/java/org/killbill/commons/utils/io/ByteSource.java b/utils/src/main/java/org/killbill/commons/utils/io/ByteSource.java new file mode 100644 index 00000000..d80e6cd7 --- /dev/null +++ b/utils/src/main/java/org/killbill/commons/utils/io/ByteSource.java @@ -0,0 +1,120 @@ +/* + * Copyright 2014-2026 The Billing Project, LLC + * + * The Billing Project 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.killbill.commons.utils.io; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; + +/** + * A lightweight byte source abstraction for lazily concatenating and slicing byte arrays. + * + *

    Inspired by Guava's {@code com.google.common.io.ByteSource}, but intentionally minimal: + * only the subset used by jooby's SseRenderer (wrap, concat, empty, slice, read) is implemented. + * Internally backed by byte arrays and {@link ByteArrayOutputStream} — no streaming, no IO + * dependency tree.

    + * + * @see + * Original Guava ByteSource + */ +public abstract class ByteSource { + + private static final ByteSource EMPTY = new ByteSource() { + @Override + public byte[] read() { + return new byte[0]; + } + + @Override + public String toString() { + return "ByteSource.empty()"; + } + }; + + /** + * Returns a {@code ByteSource} that wraps the given byte array. + */ + public static ByteSource wrap(final byte[] bytes) { + return new ByteSource() { + @Override + public byte[] read() { + return bytes.clone(); + } + + @Override + public String toString() { + return "ByteSource.wrap(" + bytes.length + " bytes)"; + } + }; + } + + /** + * Returns an empty {@code ByteSource}. + */ + public static ByteSource empty() { + return EMPTY; + } + + /** + * Returns a {@code ByteSource} that concatenates all given sources. + * Bytes are materialized on {@link #read()}. + */ + public static ByteSource concat(final ByteSource... sources) { + return new ByteSource() { + @Override + public byte[] read() throws IOException { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + for (final ByteSource source : sources) { + out.write(source.read()); + } + return out.toByteArray(); + } + + @Override + public String toString() { + return "ByteSource.concat(" + sources.length + " sources)"; + } + }; + } + + /** + * Returns a view of a sub-range of this source. + * If offset + length exceeds actual size, returns only available bytes from offset. + */ + public ByteSource slice(final long offset, final long length) { + final ByteSource parent = this; + return new ByteSource() { + @Override + public byte[] read() throws IOException { + final byte[] all = parent.read(); + final int start = (int) Math.min(offset, all.length); + final int end = (int) Math.min(offset + length, all.length); + return Arrays.copyOfRange(all, start, end); + } + + @Override + public String toString() { + return parent.toString() + ".slice(" + offset + ", " + length + ")"; + } + }; + } + + /** + * Reads the full contents as a byte array. + */ + public abstract byte[] read() throws IOException; +} diff --git a/utils/src/test/java/org/killbill/commons/utils/io/ByteSourceTest.java b/utils/src/test/java/org/killbill/commons/utils/io/ByteSourceTest.java new file mode 100644 index 00000000..d4b53af0 --- /dev/null +++ b/utils/src/test/java/org/killbill/commons/utils/io/ByteSourceTest.java @@ -0,0 +1,201 @@ +/* + * Copyright 2014-2026 The Billing Project, LLC + * + * The Billing Project 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.killbill.commons.utils.io; + +import java.io.IOException; +import java.util.Arrays; + +import org.testng.Assert; +import org.testng.annotations.Test; + +public class ByteSourceTest { + + @Test + public void testWrap() throws IOException { + byte[] data = {1, 2, 3, 4, 5}; + ByteSource source = ByteSource.wrap(data); + Assert.assertEquals(source.read(), data); + } + + @Test + public void testWrap_returnsClone() throws IOException { + byte[] data = {10, 20, 30}; + ByteSource source = ByteSource.wrap(data); + byte[] first = source.read(); + byte[] second = source.read(); + Assert.assertEquals(first, second); + // modifying returned array should not affect future reads + first[0] = 99; + Assert.assertEquals(source.read(), new byte[]{10, 20, 30}); + } + + @Test + public void testWrap_emptyArray() throws IOException { + ByteSource source = ByteSource.wrap(new byte[0]); + Assert.assertEquals(source.read(), new byte[0]); + } + + @Test + public void testWrap_toString() { + ByteSource source = ByteSource.wrap(new byte[]{1, 2, 3}); + Assert.assertTrue(source.toString().contains("3 bytes")); + } + + @Test + public void testEmpty() throws IOException { + ByteSource empty = ByteSource.empty(); + Assert.assertEquals(empty.read(), new byte[0]); + } + + @Test + public void testEmpty_sameInstance() { + Assert.assertSame(ByteSource.empty(), ByteSource.empty()); + } + + @Test + public void testEmpty_toString() { + Assert.assertEquals(ByteSource.empty().toString(), "ByteSource.empty()"); + } + + @Test + public void testConcat_multipleSources() throws IOException { + ByteSource a = ByteSource.wrap(new byte[]{1, 2}); + ByteSource b = ByteSource.wrap(new byte[]{3, 4}); + ByteSource c = ByteSource.wrap(new byte[]{5}); + ByteSource concat = ByteSource.concat(a, b, c); + Assert.assertEquals(concat.read(), new byte[]{1, 2, 3, 4, 5}); + } + + @Test + public void testConcat_singleSource() throws IOException { + ByteSource a = ByteSource.wrap(new byte[]{7, 8, 9}); + ByteSource concat = ByteSource.concat(a); + Assert.assertEquals(concat.read(), new byte[]{7, 8, 9}); + } + + @Test + public void testConcat_noSources() throws IOException { + ByteSource concat = ByteSource.concat(); + Assert.assertEquals(concat.read(), new byte[0]); + } + + @Test + public void testConcat_withEmpty() throws IOException { + ByteSource a = ByteSource.wrap(new byte[]{1, 2}); + ByteSource concat = ByteSource.concat(ByteSource.empty(), a, ByteSource.empty()); + Assert.assertEquals(concat.read(), new byte[]{1, 2}); + } + + @Test + public void testConcat_toString() { + ByteSource concat = ByteSource.concat( + ByteSource.wrap(new byte[1]), + ByteSource.wrap(new byte[2])); + Assert.assertTrue(concat.toString().contains("2 sources")); + } + + @Test + public void testSlice_middle() throws IOException { + byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + ByteSource source = ByteSource.wrap(data); + ByteSource slice = source.slice(3, 4); + Assert.assertEquals(slice.read(), new byte[]{3, 4, 5, 6}); + } + + @Test + public void testSlice_fromStart() throws IOException { + byte[] data = {10, 20, 30, 40, 50}; + ByteSource source = ByteSource.wrap(data); + ByteSource slice = source.slice(0, 3); + Assert.assertEquals(slice.read(), new byte[]{10, 20, 30}); + } + + @Test + public void testSlice_toEnd() throws IOException { + byte[] data = {1, 2, 3, 4, 5}; + ByteSource source = ByteSource.wrap(data); + ByteSource slice = source.slice(2, 100); + Assert.assertEquals(slice.read(), new byte[]{3, 4, 5}); + } + + @Test + public void testSlice_offsetBeyondEnd() throws IOException { + byte[] data = {1, 2, 3}; + ByteSource source = ByteSource.wrap(data); + ByteSource slice = source.slice(10, 5); + Assert.assertEquals(slice.read(), new byte[0]); + } + + @Test + public void testSlice_zeroLength() throws IOException { + byte[] data = {1, 2, 3}; + ByteSource source = ByteSource.wrap(data); + ByteSource slice = source.slice(1, 0); + Assert.assertEquals(slice.read(), new byte[0]); + } + + @Test + public void testSlice_ofSlice() throws IOException { + byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + ByteSource source = ByteSource.wrap(data); + ByteSource slice = source.slice(2, 6).slice(1, 3); + Assert.assertEquals(slice.read(), new byte[]{3, 4, 5}); + } + + @Test + public void testSlice_toString() { + ByteSource source = ByteSource.wrap(new byte[10]); + ByteSource slice = source.slice(2, 5); + String str = slice.toString(); + Assert.assertTrue(str.contains("slice")); + Assert.assertTrue(str.contains("2")); + Assert.assertTrue(str.contains("5")); + } + + @Test + public void testConcat_thenSlice() throws IOException { + ByteSource a = ByteSource.wrap(new byte[]{1, 2, 3}); + ByteSource b = ByteSource.wrap(new byte[]{4, 5, 6}); + ByteSource concat = ByteSource.concat(a, b); + ByteSource slice = concat.slice(2, 3); + Assert.assertEquals(slice.read(), new byte[]{3, 4, 5}); + } + + @Test + public void testConcat_nestedConcat() throws IOException { + ByteSource a = ByteSource.wrap(new byte[]{1}); + ByteSource b = ByteSource.wrap(new byte[]{2}); + ByteSource c = ByteSource.wrap(new byte[]{3}); + ByteSource nested = ByteSource.concat(ByteSource.concat(a, b), c); + Assert.assertEquals(nested.read(), new byte[]{1, 2, 3}); + } + + @Test + public void testSlice_ofEmpty() throws IOException { + ByteSource slice = ByteSource.empty().slice(0, 10); + Assert.assertEquals(slice.read(), new byte[0]); + } + + @Test + public void testWrap_largeArray() throws IOException { + byte[] data = new byte[10000]; + Arrays.fill(data, (byte) 42); + ByteSource source = ByteSource.wrap(data); + Assert.assertEquals(source.read().length, 10000); + Assert.assertEquals(source.read()[9999], (byte) 42); + } +} From f7c6e5be4d7896cfa33c42d24f449365a67bac95 Mon Sep 17 00:00:00 2001 From: xsalefter Date: Sun, 31 May 2026 04:07:10 +0700 Subject: [PATCH 20/33] add CharMatcher#anyOf() and its implementation --- .../killbill/commons/utils/CharMatcher.java | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/utils/src/main/java/org/killbill/commons/utils/CharMatcher.java b/utils/src/main/java/org/killbill/commons/utils/CharMatcher.java index 1c19cfd8..eb68f07a 100644 --- a/utils/src/main/java/org/killbill/commons/utils/CharMatcher.java +++ b/utils/src/main/java/org/killbill/commons/utils/CharMatcher.java @@ -16,7 +16,7 @@ /** * Verbatim copy of guava's com.google.common.base.CharMatcher (v.31.0.1) — minimal subset used by - * CaseFormat. See More. + * CaseFormat and Splitter. * *

    Determines a true or false value for any Java {@code char} value, just as {@link * java.util.function.Predicate} does for any {@link Object}. @@ -31,6 +31,14 @@ public static CharMatcher is(final char match) { return new Is(match); } + /** + * Returns a {@code char} matcher that matches any character present in the given character + * sequence. + */ + public static CharMatcher anyOf(final CharSequence sequence) { + return new AnyOf(sequence.toString()); + } + /** * Returns a {@code char} matcher that matches any character in a given BMP range (both endpoints * are inclusive). For example, to match any lowercase letter of the English alphabet, use {@code @@ -102,4 +110,24 @@ public String toString() { return "CharMatcher.inRange('" + startInclusive + "', '" + endInclusive + "')"; } } + + // Implementation of anyOf(CharSequence) + private static final class AnyOf extends CharMatcher { + + private final String chars; + + AnyOf(String chars) { + this.chars = chars; + } + + @Override + public boolean matches(char c) { + return chars.indexOf(c) >= 0; + } + + @Override + public String toString() { + return "CharMatcher.anyOf(\"" + chars + "\")"; + } + } } From 8e40e4cb19bd54360e3d8bf15e2a5dab46fa0dd8 Mon Sep 17 00:00:00 2001 From: xsalefter Date: Sun, 31 May 2026 04:08:48 +0700 Subject: [PATCH 21/33] port (sub-set) of Guava's Splitter and apply to various jobby classes --- jooby/src/main/java/org/jooby/Cookie.java | 2 +- jooby/src/main/java/org/jooby/Env.java | 2 +- .../java/org/jooby/handlers/CorsHandler.java | 2 +- .../java/org/jooby/internal/ByteRange.java | 2 +- .../jooby/internal/parser/bean/BeanPlan.java | 4 +- .../org/killbill/commons/utils/Splitter.java | 122 +++++++++++++ .../killbill/commons/utils/SplitterTest.java | 163 ++++++++++++++++++ 7 files changed, 291 insertions(+), 6 deletions(-) create mode 100644 utils/src/main/java/org/killbill/commons/utils/Splitter.java create mode 100644 utils/src/test/java/org/killbill/commons/utils/SplitterTest.java diff --git a/jooby/src/main/java/org/jooby/Cookie.java b/jooby/src/main/java/org/jooby/Cookie.java index 5be2e4fe..79d656be 100644 --- a/jooby/src/main/java/org/jooby/Cookie.java +++ b/jooby/src/main/java/org/jooby/Cookie.java @@ -15,7 +15,7 @@ */ package org.jooby; -import com.google.common.base.Splitter; +import org.killbill.commons.utils.Splitter; import org.killbill.commons.utils.Strings; import static java.util.Objects.requireNonNull; import org.jooby.funzy.Throwing; diff --git a/jooby/src/main/java/org/jooby/Env.java b/jooby/src/main/java/org/jooby/Env.java index 05a41fe0..ba8e9beb 100644 --- a/jooby/src/main/java/org/jooby/Env.java +++ b/jooby/src/main/java/org/jooby/Env.java @@ -15,7 +15,7 @@ */ package org.jooby; -import com.google.common.base.Splitter; +import org.killbill.commons.utils.Splitter; import com.google.inject.Key; import com.google.inject.name.Names; import com.typesafe.config.Config; diff --git a/jooby/src/main/java/org/jooby/handlers/CorsHandler.java b/jooby/src/main/java/org/jooby/handlers/CorsHandler.java index bae8150f..128b248c 100644 --- a/jooby/src/main/java/org/jooby/handlers/CorsHandler.java +++ b/jooby/src/main/java/org/jooby/handlers/CorsHandler.java @@ -30,7 +30,7 @@ import org.slf4j.LoggerFactory; import org.killbill.commons.utils.Joiner; -import com.google.common.base.Splitter; +import org.killbill.commons.utils.Splitter; /** * Handle preflight and simple CORS requests. CORS options are set via: {@link Cors}. diff --git a/jooby/src/main/java/org/jooby/internal/ByteRange.java b/jooby/src/main/java/org/jooby/internal/ByteRange.java index 335b0383..82c6987b 100644 --- a/jooby/src/main/java/org/jooby/internal/ByteRange.java +++ b/jooby/src/main/java/org/jooby/internal/ByteRange.java @@ -15,7 +15,7 @@ */ package org.jooby.internal; -import com.google.common.base.Splitter; +import org.killbill.commons.utils.Splitter; import org.jooby.Err; import org.jooby.Status; diff --git a/jooby/src/main/java/org/jooby/internal/parser/bean/BeanPlan.java b/jooby/src/main/java/org/jooby/internal/parser/bean/BeanPlan.java index d9a9c0f2..9aa896d3 100644 --- a/jooby/src/main/java/org/jooby/internal/parser/bean/BeanPlan.java +++ b/jooby/src/main/java/org/jooby/internal/parser/bean/BeanPlan.java @@ -15,8 +15,8 @@ */ package org.jooby.internal.parser.bean; -import com.google.common.base.CharMatcher; -import com.google.common.base.Splitter; +import org.killbill.commons.utils.CharMatcher; +import org.killbill.commons.utils.Splitter; import com.google.inject.TypeLiteral; import org.jooby.Request; import org.jooby.internal.ParameterNameProvider; diff --git a/utils/src/main/java/org/killbill/commons/utils/Splitter.java b/utils/src/main/java/org/killbill/commons/utils/Splitter.java new file mode 100644 index 00000000..9d090a5a --- /dev/null +++ b/utils/src/main/java/org/killbill/commons/utils/Splitter.java @@ -0,0 +1,122 @@ +/* + * Copyright 2014-2026 The Billing Project, LLC + * + * The Billing Project 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.killbill.commons.utils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A string splitter with fluent API, inspired by Guava's {@code Splitter}. + * + *

    Like Guava's implementation, this does NOT use regex internally. Splitting is done by + * scanning for separator characters directly. This avoids regex compilation overhead and + * eliminates escaping pitfalls (e.g. splitting on '.' with regex would match any character).

    + * + *

    Only the subset used by jooby is implemented: {@link #on(char)}, {@link #on(CharMatcher)}, + * {@link #trimResults()}, {@link #omitEmptyStrings()}, {@link #split(CharSequence)}, + * and {@link #splitToList(CharSequence)}.

    + * + * @see + * Original Guava Splitter + */ +public final class Splitter { + + private final CharMatcher separatorMatcher; + private final boolean trimResults; + private final boolean omitEmpty; + + private Splitter(final CharMatcher separatorMatcher, final boolean trimResults, final boolean omitEmpty) { + this.separatorMatcher = separatorMatcher; + this.trimResults = trimResults; + this.omitEmpty = omitEmpty; + } + + /** + * Returns a splitter that splits on the given single character. + * No regex is used — the character is matched literally, consistent with Guava's behavior. + */ + public static Splitter on(final char separator) { + return new Splitter(new CharMatcher() { + @Override + public boolean matches(final char c) { + return c == separator; + } + }, false, false); + } + + /** + * Returns a splitter that splits on any character matched by the given {@link CharMatcher}. + * No regex is used — each character is tested against the matcher directly. + */ + public static Splitter on(final CharMatcher separatorMatcher) { + return new Splitter(separatorMatcher, false, false); + } + + /** + * Returns a splitter that trims whitespace from the beginning and end of each resulting part. + */ + public Splitter trimResults() { + return new Splitter(this.separatorMatcher, true, this.omitEmpty); + } + + /** + * Returns a splitter that omits empty strings from the results (after trimming, if applicable). + */ + public Splitter omitEmptyStrings() { + return new Splitter(this.separatorMatcher, this.trimResults, true); + } + + /** + * Splits the given sequence into parts and returns them as an {@link Iterable}. + */ + public Iterable split(final CharSequence sequence) { + return splitToList(sequence); + } + + /** + * Splits the given sequence into parts and returns them as an unmodifiable {@link List}. + */ + public List splitToList(final CharSequence sequence) { + final List raw = splitRaw(sequence); + final List result = new ArrayList<>(); + for (final String part : raw) { + String value = trimResults ? part.trim() : part; + if (omitEmpty && value.isEmpty()) { + continue; + } + result.add(value); + } + return Collections.unmodifiableList(result); + } + + /** + * Splits the sequence by scanning for separator characters. No regex involved. + */ + private List splitRaw(final CharSequence sequence) { + final List parts = new ArrayList<>(); + int start = 0; + for (int i = 0; i < sequence.length(); i++) { + if (separatorMatcher.matches(sequence.charAt(i))) { + parts.add(sequence.subSequence(start, i).toString()); + start = i + 1; + } + } + parts.add(sequence.subSequence(start, sequence.length()).toString()); + return parts; + } +} diff --git a/utils/src/test/java/org/killbill/commons/utils/SplitterTest.java b/utils/src/test/java/org/killbill/commons/utils/SplitterTest.java new file mode 100644 index 00000000..6a61866b --- /dev/null +++ b/utils/src/test/java/org/killbill/commons/utils/SplitterTest.java @@ -0,0 +1,163 @@ +/* + * Copyright 2014-2026 The Billing Project, LLC + * + * The Billing Project 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.killbill.commons.utils; + +import java.util.Iterator; +import java.util.List; + +import org.testng.Assert; +import org.testng.annotations.Test; + +public class SplitterTest { + + @Test + public void testOnChar_basic() { + List result = Splitter.on(',').splitToList("a,b,c"); + Assert.assertEquals(result, List.of("a", "b", "c")); + } + + @Test + public void testOnChar_singleElement() { + List result = Splitter.on(',').splitToList("hello"); + Assert.assertEquals(result, List.of("hello")); + } + + @Test + public void testOnChar_emptyParts() { + List result = Splitter.on(',').splitToList("a,,b,"); + Assert.assertEquals(result, List.of("a", "", "b", "")); + } + + @Test + public void testOnChar_emptyInput() { + List result = Splitter.on(',').splitToList(""); + Assert.assertEquals(result, List.of("")); + } + + @Test + public void testTrimResults() { + List result = Splitter.on(',').trimResults().splitToList(" a , b , c "); + Assert.assertEquals(result, List.of("a", "b", "c")); + } + + @Test + public void testOmitEmptyStrings() { + List result = Splitter.on(',').omitEmptyStrings().splitToList("a,,b,,c"); + Assert.assertEquals(result, List.of("a", "b", "c")); + } + + @Test + public void testTrimResults_andOmitEmpty() { + List result = Splitter.on(',').trimResults().omitEmptyStrings() + .splitToList(" a , , b , , "); + Assert.assertEquals(result, List.of("a", "b")); + } + + @Test + public void testOmitEmpty_andTrimResults_orderDoesNotMatter() { + List result = Splitter.on(',').omitEmptyStrings().trimResults() + .splitToList(" a , , b , , "); + Assert.assertEquals(result, List.of("a", "b")); + } + + @Test + public void testOnCharMatcher_anyOf() { + List result = Splitter.on(CharMatcher.anyOf("[].")).trimResults() + .omitEmptyStrings().splitToList("foo[0].bar"); + Assert.assertEquals(result, List.of("foo", "0", "bar")); + } + + @Test + public void testOnCharMatcher_anyOf_multipleDelimiters() { + List result = Splitter.on(CharMatcher.anyOf("&=")).splitToList("a=1&b=2"); + Assert.assertEquals(result, List.of("a", "1", "b", "2")); + } + + @Test + public void testSplit_returnsIterable() { + Iterable result = Splitter.on('&').trimResults().omitEmptyStrings() + .split("a & b & c"); + Iterator it = result.iterator(); + Assert.assertTrue(it.hasNext()); + Assert.assertEquals(it.next(), "a"); + Assert.assertEquals(it.next(), "b"); + Assert.assertEquals(it.next(), "c"); + Assert.assertFalse(it.hasNext()); + } + + @Test + public void testSplitToList_unmodifiable() { + List result = Splitter.on(',').splitToList("a,b"); + try { + result.add("c"); + Assert.fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException expected) { + // ok + } + } + + @Test + public void testOnNewline() { + List result = Splitter.on('\n').splitToList("line1\nline2\nline3"); + Assert.assertEquals(result, List.of("line1", "line2", "line3")); + } + + @Test + public void testOnEquals() { + List result = Splitter.on('=').trimResults().omitEmptyStrings() + .split("key=value").iterator().next().isEmpty() ? List.of() : null; + // More direct test: + Iterator it = Splitter.on('=').trimResults().omitEmptyStrings() + .split("key=value").iterator(); + Assert.assertEquals(it.next(), "key"); + Assert.assertEquals(it.next(), "value"); + Assert.assertFalse(it.hasNext()); + } + + @Test + public void testOnAmpersand_cookiePattern() { + // Matches Cookie.java usage: Splitter.on('&').trimResults().omitEmptyStrings() + List result = Splitter.on('&').trimResults().omitEmptyStrings() + .splitToList("name=val&other=x&"); + Assert.assertEquals(result, List.of("name=val", "other=x")); + } + + @Test + public void testOnComma_corsPattern() { + // Matches CorsHandler.java: Splitter.on(',').trimResults().omitEmptyStrings() + List result = Splitter.on(',').trimResults().omitEmptyStrings() + .splitToList("GET, POST, PUT"); + Assert.assertEquals(result, List.of("GET", "POST", "PUT")); + } + + @Test + public void testImmutability_trimDoesNotModifyOriginal() { + Splitter base = Splitter.on(','); + Splitter trimmed = base.trimResults(); + // base should still not trim + Assert.assertEquals(base.splitToList(" a , b "), List.of(" a ", " b ")); + Assert.assertEquals(trimmed.splitToList(" a , b "), List.of("a", "b")); + } + + @Test + public void testImmutability_omitDoesNotModifyOriginal() { + Splitter base = Splitter.on(','); + Splitter omitting = base.omitEmptyStrings(); + Assert.assertEquals(base.splitToList("a,,b"), List.of("a", "", "b")); + Assert.assertEquals(omitting.splitToList("a,,b"), List.of("a", "b")); + } +} From 80d97da8a06873c71d0f8cec51aff42bbd5a8956 Mon Sep 17 00:00:00 2001 From: xsalefter Date: Sun, 31 May 2026 06:31:32 +0700 Subject: [PATCH 22/33] port (sub-set) various Guava's Escapers and apply to various jobby classes --- jooby/src/main/java/org/jooby/Jooby.java | 6 +- jooby/src/main/java/org/jooby/Request.java | 2 +- .../java/org/jooby/DefaultErrHandlerTest.java | 2 +- jooby/src/test/java/org/jooby/JoobyTest.java | 6 +- .../commons/utils/escape/Escaper.java | 37 ++++++ .../commons/utils/html/HtmlEscapers.java | 66 ++++++++++ .../commons/utils/net/UrlEscapers.java | 122 ++++++++++++++++++ .../commons/utils/escape/EscaperTest.java | 37 ++++++ .../commons/utils/html/HtmlEscapersTest.java | 65 ++++++++++ .../commons/utils/net/UrlEscapersTest.java | 106 +++++++++++++++ 10 files changed, 441 insertions(+), 8 deletions(-) create mode 100644 utils/src/main/java/org/killbill/commons/utils/escape/Escaper.java create mode 100644 utils/src/main/java/org/killbill/commons/utils/html/HtmlEscapers.java create mode 100644 utils/src/main/java/org/killbill/commons/utils/net/UrlEscapers.java create mode 100644 utils/src/test/java/org/killbill/commons/utils/escape/EscaperTest.java create mode 100644 utils/src/test/java/org/killbill/commons/utils/html/HtmlEscapersTest.java create mode 100644 utils/src/test/java/org/killbill/commons/utils/net/UrlEscapersTest.java diff --git a/jooby/src/main/java/org/jooby/Jooby.java b/jooby/src/main/java/org/jooby/Jooby.java index 21908f04..14f70ded 100644 --- a/jooby/src/main/java/org/jooby/Jooby.java +++ b/jooby/src/main/java/org/jooby/Jooby.java @@ -15,9 +15,9 @@ */ package org.jooby; -import com.google.common.escape.Escaper; -import com.google.common.html.HtmlEscapers; -import com.google.common.net.UrlEscapers; +import org.killbill.commons.utils.escape.Escaper; +import org.killbill.commons.utils.html.HtmlEscapers; +import org.killbill.commons.utils.net.UrlEscapers; import org.killbill.commons.utils.Joiner; import static org.killbill.commons.utils.Preconditions.checkArgument; import static org.killbill.commons.utils.Preconditions.checkState; diff --git a/jooby/src/main/java/org/jooby/Request.java b/jooby/src/main/java/org/jooby/Request.java index 35e183d4..fce58a66 100644 --- a/jooby/src/main/java/org/jooby/Request.java +++ b/jooby/src/main/java/org/jooby/Request.java @@ -15,7 +15,7 @@ */ package org.jooby; -import com.google.common.net.UrlEscapers; +import org.killbill.commons.utils.net.UrlEscapers; import com.google.inject.Key; import com.google.inject.TypeLiteral; import static java.util.Objects.requireNonNull; diff --git a/jooby/src/test/java/org/jooby/DefaultErrHandlerTest.java b/jooby/src/test/java/org/jooby/DefaultErrHandlerTest.java index a5de0d7b..d9aeb79c 100644 --- a/jooby/src/test/java/org/jooby/DefaultErrHandlerTest.java +++ b/jooby/src/test/java/org/jooby/DefaultErrHandlerTest.java @@ -15,7 +15,7 @@ */ package org.jooby; -import com.google.common.html.HtmlEscapers; +import org.killbill.commons.utils.html.HtmlEscapers; import com.typesafe.config.Config; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; diff --git a/jooby/src/test/java/org/jooby/JoobyTest.java b/jooby/src/test/java/org/jooby/JoobyTest.java index 22d7a445..959ca790 100644 --- a/jooby/src/test/java/org/jooby/JoobyTest.java +++ b/jooby/src/test/java/org/jooby/JoobyTest.java @@ -15,9 +15,9 @@ */ package org.jooby; -import com.google.common.escape.Escaper; -import com.google.common.html.HtmlEscapers; -import com.google.common.net.UrlEscapers; +import org.killbill.commons.utils.escape.Escaper; +import org.killbill.commons.utils.html.HtmlEscapers; +import org.killbill.commons.utils.net.UrlEscapers; import com.google.inject.Binder; import com.google.inject.Guice; import com.google.inject.Injector; diff --git a/utils/src/main/java/org/killbill/commons/utils/escape/Escaper.java b/utils/src/main/java/org/killbill/commons/utils/escape/Escaper.java new file mode 100644 index 00000000..5c2e3f80 --- /dev/null +++ b/utils/src/main/java/org/killbill/commons/utils/escape/Escaper.java @@ -0,0 +1,37 @@ +/* + * Copyright 2014-2026 The Billing Project, LLC + * + * The Billing Project 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.killbill.commons.utils.escape; + +/** + * An object that converts literal text into a format safe for inclusion in a particular context + * (such as an XML document or a URL). Typically, the reverse process of "unescaping" is performed by + * a separate interface. + * + *

    Replaces Guava's {@code com.google.common.escape.Escaper}. Made a functional interface + * so that {@code escaper::escape} method references work naturally.

    + * + * @see + * Original Guava Escaper + */ +@FunctionalInterface +public interface Escaper { + + /** + * Returns the escaped form of the given input string. + */ + String escape(String input); +} diff --git a/utils/src/main/java/org/killbill/commons/utils/html/HtmlEscapers.java b/utils/src/main/java/org/killbill/commons/utils/html/HtmlEscapers.java new file mode 100644 index 00000000..4631b6ae --- /dev/null +++ b/utils/src/main/java/org/killbill/commons/utils/html/HtmlEscapers.java @@ -0,0 +1,66 @@ +/* + * Copyright 2014-2026 The Billing Project, LLC + * + * The Billing Project 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.killbill.commons.utils.html; + +import org.killbill.commons.utils.escape.Escaper; + +/** + * Provides HTML escaping. Replaces the 5 characters that have special meaning in HTML + * ({@code & < > " '}) with their corresponding HTML entities. + * + * @see + * Original Guava HtmlEscapers + */ +public final class HtmlEscapers { + + private HtmlEscapers() {} + + private static final Escaper HTML_ESCAPER = input -> { + final StringBuilder sb = new StringBuilder(input.length()); + for (int i = 0; i < input.length(); i++) { + final char c = input.charAt(i); + switch (c) { + case '&': + sb.append("&"); + break; + case '<': + sb.append("<"); + break; + case '>': + sb.append(">"); + break; + case '"': + sb.append("""); + break; + case '\'': + sb.append("'"); + break; + default: + sb.append(c); + } + } + return sb.toString(); + }; + + /** + * Returns an {@link Escaper} that replaces special HTML characters with their entity + * equivalents. + */ + public static Escaper htmlEscaper() { + return HTML_ESCAPER; + } +} diff --git a/utils/src/main/java/org/killbill/commons/utils/net/UrlEscapers.java b/utils/src/main/java/org/killbill/commons/utils/net/UrlEscapers.java new file mode 100644 index 00000000..169d69da --- /dev/null +++ b/utils/src/main/java/org/killbill/commons/utils/net/UrlEscapers.java @@ -0,0 +1,122 @@ +/* + * Copyright 2026 The Billing Project, LLC + * + * The Billing Project 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.killbill.commons.utils.net; + +import org.killbill.commons.utils.escape.Escaper; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.BitSet; + +/** + * Provides URL escaping (percent-encoding) for different URL components. + * + *

    Each escaper percent-encodes characters that are not allowed in its respective URL component, + * as defined by RFC 3986.

    + * + * @see + * Original Guava UrlEscapers + */ +public final class UrlEscapers { + + private UrlEscapers() {} + + // RFC 3986 unreserved characters: ALPHA / DIGIT / "-" / "." / "_" / "~" + private static final BitSet UNRESERVED = new BitSet(128); + // sub-delims: "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" + private static final BitSet SUB_DELIMS = new BitSet(128); + + static { + // unreserved + for (char c = 'A'; c <= 'Z'; c++) UNRESERVED.set(c); + for (char c = 'a'; c <= 'z'; c++) UNRESERVED.set(c); + for (char c = '0'; c <= '9'; c++) UNRESERVED.set(c); + UNRESERVED.set('-'); + UNRESERVED.set('.'); + UNRESERVED.set('_'); + UNRESERVED.set('~'); + + // sub-delims + String subDelims = "!$&'()*+,;="; + for (int i = 0; i < subDelims.length(); i++) { + SUB_DELIMS.set(subDelims.charAt(i)); + } + } + + // Path segment safe: unreserved + sub-delims + ":" + "@" + private static final BitSet PATH_SEGMENT_SAFE = new BitSet(128); + + static { + PATH_SEGMENT_SAFE.or(UNRESERVED); + PATH_SEGMENT_SAFE.or(SUB_DELIMS); + PATH_SEGMENT_SAFE.set(':'); + PATH_SEGMENT_SAFE.set('@'); + } + + // Fragment safe: path segment safe + "/" + "?" + private static final BitSet FRAGMENT_SAFE = new BitSet(128); + + static { + FRAGMENT_SAFE.or(PATH_SEGMENT_SAFE); + FRAGMENT_SAFE.set('/'); + FRAGMENT_SAFE.set('?'); + } + + /** + * Returns an {@link Escaper} for URL fragment encoding. Characters not allowed in a URI + * fragment are percent-encoded. + * + *

    Safe characters: unreserved + sub-delims + {@code : @ / ?}

    + */ + public static Escaper urlFragmentEscaper() { + return input -> percentEncode(input, FRAGMENT_SAFE); + } + + /** + * Returns an {@link Escaper} for URL form parameter encoding (application/x-www-form-urlencoded). + * Space is encoded as {@code +}, other unsafe characters as percent-encoded. + */ + public static Escaper urlFormParameterEscaper() { + return input -> URLEncoder.encode(input, StandardCharsets.UTF_8); + } + + /** + * Returns an {@link Escaper} for URL path segment encoding. Characters not allowed in a path + * segment are percent-encoded. + * + *

    Safe characters: unreserved + sub-delims + {@code : @}

    + */ + public static Escaper urlPathSegmentEscaper() { + return input -> percentEncode(input, PATH_SEGMENT_SAFE); + } + + private static String percentEncode(final String input, final BitSet safeChars) { + final StringBuilder sb = new StringBuilder(input.length()); + final byte[] bytes = input.getBytes(StandardCharsets.UTF_8); + for (final byte b : bytes) { + final int unsigned = b & 0xFF; + if (unsigned < 128 && safeChars.get(unsigned)) { + sb.append((char) unsigned); + } else { + sb.append('%'); + sb.append(Character.toUpperCase(Character.forDigit(unsigned >> 4, 16))); + sb.append(Character.toUpperCase(Character.forDigit(unsigned & 0xF, 16))); + } + } + return sb.toString(); + } +} diff --git a/utils/src/test/java/org/killbill/commons/utils/escape/EscaperTest.java b/utils/src/test/java/org/killbill/commons/utils/escape/EscaperTest.java new file mode 100644 index 00000000..294780e5 --- /dev/null +++ b/utils/src/test/java/org/killbill/commons/utils/escape/EscaperTest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2026 The Billing Project, LLC + * + * The Billing Project 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.killbill.commons.utils.escape; + +import org.killbill.commons.utils.html.HtmlEscapers; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class EscaperTest { + + @Test + public void testFunctionalInterface() { + Escaper escaper = String::toUpperCase; + Assert.assertEquals(escaper.escape("hello"), "HELLO"); + } + + @Test + public void testMethodReference() { + Escaper html = HtmlEscapers.htmlEscaper(); + java.util.function.Function fn = html::escape; + Assert.assertEquals(fn.apply(""), "<b>"); + } +} diff --git a/utils/src/test/java/org/killbill/commons/utils/html/HtmlEscapersTest.java b/utils/src/test/java/org/killbill/commons/utils/html/HtmlEscapersTest.java new file mode 100644 index 00000000..66ca6525 --- /dev/null +++ b/utils/src/test/java/org/killbill/commons/utils/html/HtmlEscapersTest.java @@ -0,0 +1,65 @@ +/* + * Copyright 2026 The Billing Project, LLC + * + * The Billing Project 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.killbill.commons.utils.html; + +import org.testng.Assert; +import org.testng.annotations.Test; + +public class HtmlEscapersTest { + + @Test + public void testAmpersand() { + Assert.assertEquals(HtmlEscapers.htmlEscaper().escape("a&b"), "a&b"); + } + + @Test + public void testLessThan() { + Assert.assertEquals(HtmlEscapers.htmlEscaper().escape("