diff --git a/independent-projects/bootstrap/benchmarks/pom.xml b/independent-projects/bootstrap/benchmarks/pom.xml new file mode 100644 index 0000000000000..3e7b7d754c161 --- /dev/null +++ b/independent-projects/bootstrap/benchmarks/pom.xml @@ -0,0 +1,91 @@ + + + 4.0.0 + + quarkus-bootstrap-parent + io.quarkus + 999-SNAPSHOT + ../pom.xml + + + benchmarks + Quarkus - Bootstrap - JMH Benchmarks + + + + + io.quarkus + quarkus-bootstrap-bom + ${project.version} + pom + import + + + io.quarkus + quarkus-bootstrap-bom-test + ${project.version} + pom + import + + + + + + + org.openjdk.jmh + jmh-core + ${jmh.version} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + provided + + + io.quarkus + quarkus-classloader-commons + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + package + + shade + + + benchmark + + + org.openjdk.jmh.Main + + + + + + + + maven-compiler-plugin + + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + + + + + + + + \ No newline at end of file diff --git a/independent-projects/bootstrap/benchmarks/src/main/java/io/quarkus/commons/benchmarks/BenchmarkClassnameSanitize.java b/independent-projects/bootstrap/benchmarks/src/main/java/io/quarkus/commons/benchmarks/BenchmarkClassnameSanitize.java new file mode 100644 index 0000000000000..f6ea0a1be7054 --- /dev/null +++ b/independent-projects/bootstrap/benchmarks/src/main/java/io/quarkus/commons/benchmarks/BenchmarkClassnameSanitize.java @@ -0,0 +1,65 @@ +package io.quarkus.commons.benchmarks; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import io.quarkus.commons.classloading.ClassloadHelper; + +@State(Scope.Benchmark) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@Warmup(iterations = 10, time = 200, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 20, time = 100, timeUnit = TimeUnit.MILLISECONDS) +@Fork(2) +public class BenchmarkClassnameSanitize { + + @Param({ "true", "false" }) + public boolean worseCase; + + private static final String TEST_CLASS_NAME_BAD = "/some/odd/package/and/ClassName/"; + private static final String TEST_CLASS_NAME_GOOD = "some/odd/package/and/ClassName"; + + private String TESTINPUT; + + @Setup + public void setup() { + TESTINPUT = worseCase ? TEST_CLASS_NAME_BAD : TEST_CLASS_NAME_GOOD; + } + + @Benchmark + public String sanitizeNameOptimised() { + return ClassloadHelper.sanitizeName(TESTINPUT); + } + + @Benchmark + public String sanitizeNameStandard() { + return legacySanitize(TESTINPUT); + } + + private String legacySanitize(String name) { + if (name.startsWith("/")) { + name = name.substring(1); + } + if (name.endsWith("/")) { + name = name.substring(0, name.length() - 1); + } + return name; + } + + public static void main(String[] args) throws IOException { + org.openjdk.jmh.Main.main(args); + } + +} diff --git a/independent-projects/bootstrap/bom/pom.xml b/independent-projects/bootstrap/bom/pom.xml index 5db6112b194ad..2143268dbc458 100644 --- a/independent-projects/bootstrap/bom/pom.xml +++ b/independent-projects/bootstrap/bom/pom.xml @@ -47,6 +47,11 @@ commons-lang3 ${commons-lang.version} + + io.quarkus + quarkus-classloader-commons + ${project.version} + io.quarkus quarkus-bootstrap-core diff --git a/independent-projects/bootstrap/classloader-commons/pom.xml b/independent-projects/bootstrap/classloader-commons/pom.xml new file mode 100644 index 0000000000000..15f3c98893323 --- /dev/null +++ b/independent-projects/bootstrap/classloader-commons/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + + quarkus-bootstrap-parent + io.quarkus + 999-SNAPSHOT + ../pom.xml + + + quarkus-classloader-commons + Quarkus - Bootstrap - Classloader common utilities + + + + + io.quarkus + quarkus-bootstrap-bom + ${project.version} + pom + import + + + io.quarkus + quarkus-bootstrap-bom-test + ${project.version} + pom + import + + + + + + + org.junit.jupiter + junit-jupiter + test + + + + \ No newline at end of file diff --git a/independent-projects/bootstrap/classloader-commons/src/main/java/io/quarkus/commons/classloading/ClassloadHelper.java b/independent-projects/bootstrap/classloader-commons/src/main/java/io/quarkus/commons/classloading/ClassloadHelper.java new file mode 100644 index 0000000000000..cfe0dd6049b5e --- /dev/null +++ b/independent-projects/bootstrap/classloader-commons/src/main/java/io/quarkus/commons/classloading/ClassloadHelper.java @@ -0,0 +1,34 @@ +package io.quarkus.commons.classloading; + +public final class ClassloadHelper { + + private ClassloadHelper() { + //Not meant to be instantiated + } + + /** + * Sanitizes the string passed as argument, by removing any "/" + * symbol at the end or at the beginning. + * + * @param name the string to be sanitized + * @return a new String with the offending characters removed, or potentially the same String + * if no modifications were required. + */ + public static String sanitizeName(final String name) { + final int length = name.length(); + if (length == 0) { + return name; + } + final int beginIndex = (name.charAt(0) == '/') ? 1 : 0; + final int endIndex = (name.charAt(length - 1) == '/') ? length - 1 : length; + if (beginIndex == 0 && endIndex == length) { + return name; + } else { + if (beginIndex > endIndex) { + return ""; + } + return name.substring(beginIndex, endIndex); + } + } + +} diff --git a/independent-projects/bootstrap/classloader-commons/src/test/java/io/quarkus/commons/classloading/ClassloadHelperTest.java b/independent-projects/bootstrap/classloader-commons/src/test/java/io/quarkus/commons/classloading/ClassloadHelperTest.java new file mode 100644 index 0000000000000..a6bb841d8cb4f --- /dev/null +++ b/independent-projects/bootstrap/classloader-commons/src/test/java/io/quarkus/commons/classloading/ClassloadHelperTest.java @@ -0,0 +1,27 @@ +package io.quarkus.commons.classloading; + +import static io.quarkus.commons.classloading.ClassloadHelper.sanitizeName; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ClassloadHelperTest { + + @Test + public void testSanitizeNameSpec() { + Assertions.assertEquals("", sanitizeName("")); + Assertions.assertEquals("", sanitizeName("/")); + Assertions.assertEquals("", sanitizeName("//")); + Assertions.assertEquals("a", sanitizeName("/a/")); + Assertions.assertEquals("a", sanitizeName("/a")); + Assertions.assertEquals("a", sanitizeName("a/")); + Assertions.assertEquals("abcd", sanitizeName("/abcd/")); + Assertions.assertEquals("ab/cd", sanitizeName("/ab/cd/")); + Assertions.assertEquals("ab/cd/something/something", sanitizeName("/ab/cd/something/something")); + //The following cases are left out intentionally as they would IMO + //fall in the cathegory of illegal input, and not worth sanitising for: + // "/abcd//" : multiple slashes at end[begin] + // "/abcd//abds/" :multiple slashes in the middle + } + +} diff --git a/independent-projects/bootstrap/core/pom.xml b/independent-projects/bootstrap/core/pom.xml index fe05e4cb1f81b..1fd1e4571a880 100644 --- a/independent-projects/bootstrap/core/pom.xml +++ b/independent-projects/bootstrap/core/pom.xml @@ -33,6 +33,10 @@ + + io.quarkus + quarkus-classloader-commons + io.quarkus quarkus-bootstrap-app-model diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java index e1b20fe85d657..7ce1cdece5cba 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java @@ -1,5 +1,7 @@ package io.quarkus.bootstrap.classloading; +import static io.quarkus.commons.classloading.ClassloadHelper.sanitizeName; + import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.IOException; @@ -153,16 +155,6 @@ public static Builder builder(String name, ClassLoader parent, boolean parentFir return new Builder(name, parent, parentFirst); } - private String sanitizeName(String name) { - if (name.startsWith("/")) { - name = name.substring(1); - } - if (name.endsWith("/")) { - name = name.substring(0, name.length() - 1); - } - return name; - } - /** * Returns true if the supplied class is a class that would be loaded parent-first */ @@ -295,7 +287,7 @@ private ClassLoaderState getState() { Map> elementMap = new HashMap<>(); for (ClassPathElement element : elements) { for (String i : element.getProvidedResources()) { - if (i.startsWith("/")) { + if (i.charAt(0) == '/') { throw new RuntimeException( "Resources cannot start with /, " + i + " is incorrect provided by " + element); } diff --git a/independent-projects/bootstrap/pom.xml b/independent-projects/bootstrap/pom.xml index c2f192de80593..db44c42aa19be 100644 --- a/independent-projects/bootstrap/pom.xml +++ b/independent-projects/bootstrap/pom.xml @@ -38,6 +38,7 @@ 3.2.1 3.2.5 3.1.7 + 1.37 3.25.3 @@ -89,6 +90,8 @@ core runner gradle-resolver + classloader-commons + benchmarks diff --git a/independent-projects/bootstrap/runner/pom.xml b/independent-projects/bootstrap/runner/pom.xml index f32872fa86a74..8549283dd2c6d 100644 --- a/independent-projects/bootstrap/runner/pom.xml +++ b/independent-projects/bootstrap/runner/pom.xml @@ -38,6 +38,10 @@ + + io.quarkus + quarkus-classloader-commons + io.smallrye.common smallrye-common-io diff --git a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/RunnerClassLoader.java b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/RunnerClassLoader.java index 62c02e8fe08d3..872cdf9e4bb4b 100644 --- a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/RunnerClassLoader.java +++ b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/RunnerClassLoader.java @@ -1,5 +1,7 @@ package io.quarkus.bootstrap.runner; +import static io.quarkus.commons.classloading.ClassloadHelper.sanitizeName; + import java.net.URL; import java.util.ArrayList; import java.util.Collections; @@ -208,13 +210,6 @@ protected URL findResource(String name) { return null; } - private String sanitizeName(final String name) { - if (name.length() > 0 && name.charAt(0) == '/') { - return name.substring(1); - } - return name; - } - private ClassLoadingResource[] getClassLoadingResources(final String name) { ClassLoadingResource[] resources = directlyIndexedResourcesIndexMap.get(name); if (resources != null) {