Skip to content

Commit

Permalink
Introduce quarkus-classloader-commons and related benchmarks infrastr…
Browse files Browse the repository at this point in the history
…ucture
  • Loading branch information
Sanne committed May 1, 2024
1 parent 52b538b commit aed7581
Show file tree
Hide file tree
Showing 11 changed files with 281 additions and 18 deletions.
91 changes: 91 additions & 0 deletions independent-projects/bootstrap/benchmarks/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>quarkus-bootstrap-parent</artifactId>
<groupId>io.quarkus</groupId>
<version>999-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

<artifactId>benchmarks</artifactId>
<name>Quarkus - Bootstrap - JMH Benchmarks</name>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-bootstrap-bom</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-bootstrap-bom-test</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>${jmh.version}</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-classloader-commons</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<finalName>benchmark</finalName>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.openjdk.jmh.Main</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -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);
}

}
5 changes: 5 additions & 0 deletions independent-projects/bootstrap/bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<artifactId>commons-lang3</artifactId>
<version>${commons-lang.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-classloader-commons</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-bootstrap-core</artifactId>
Expand Down
43 changes: 43 additions & 0 deletions independent-projects/bootstrap/classloader-commons/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>quarkus-bootstrap-parent</artifactId>
<groupId>io.quarkus</groupId>
<version>999-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

<artifactId>quarkus-classloader-commons</artifactId>
<name>Quarkus - Bootstrap - Classloader common utilities</name>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-bootstrap-bom</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-bootstrap-bom-test</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -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);
}
}

}
Original file line number Diff line number Diff line change
@@ -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
}

}
4 changes: 4 additions & 0 deletions independent-projects/bootstrap/core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
</dependencyManagement>

<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-classloader-commons</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-bootstrap-app-model</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -295,7 +287,7 @@ private ClassLoaderState getState() {
Map<String, List<ClassPathElement>> 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);
}
Expand Down
3 changes: 3 additions & 0 deletions independent-projects/bootstrap/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
<version.enforcer.plugin>3.2.1</version.enforcer.plugin>
<version.surefire.plugin>3.2.5</version.surefire.plugin>
<jandex.version>3.1.7</jandex.version>
<jmh.version>1.37</jmh.version>

<!-- Dependency versions -->
<assertj.version>3.25.3</assertj.version>
Expand Down Expand Up @@ -89,6 +90,8 @@
<module>core</module>
<module>runner</module>
<module>gradle-resolver</module>
<module>classloader-commons</module>
<module>benchmarks</module>
</modules>
<build>
<pluginManagement>
Expand Down
4 changes: 4 additions & 0 deletions independent-projects/bootstrap/runner/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
</dependencyManagement>

<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-classloader-commons</artifactId>
</dependency>
<dependency>
<groupId>io.smallrye.common</groupId>
<artifactId>smallrye-common-io</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit aed7581

Please sign in to comment.