diff --git a/apache-maven/src/assembly/component.xml b/apache-maven/src/assembly/component.xml
index 5f55a310c8bd..ae86c5639f6b 100644
--- a/apache-maven/src/assembly/component.xml
+++ b/apache-maven/src/assembly/component.xml
@@ -23,14 +23,25 @@ under the License.
false
boot
- org.codehaus.plexus:plexus-classworlds
+ org.apache.maven:maven-api-classworlds
+ org.apache.maven:maven-classworlds
+
+
+ false
+ lib/modules
+
+ org.jline:*
+
+
+
false
lib
- org.codehaus.plexus:plexus-classworlds
+ org.apache.maven:maven-api-classworlds
+ org.apache.maven:maven-classworlds
diff --git a/apache-maven/src/assembly/maven/bin/m2.conf b/apache-maven/src/assembly/maven/bin/m2.conf
index b91431dea5f9..58aeb83a02a7 100644
--- a/apache-maven/src/assembly/maven/bin/m2.conf
+++ b/apache-maven/src/assembly/maven/bin/m2.conf
@@ -23,6 +23,8 @@ main is ${maven.mainClass} from plexus.core
set maven.conf default ${maven.home}/conf
set maven.installation.conf default ${maven.conf}
+module ${maven.home}/lib/modules/*.jar
+
[plexus.core]
load ${maven.conf}/logging
optionally ${maven.home}/lib/ext/redisson/*.jar
diff --git a/apache-maven/src/assembly/maven/bin/mvn b/apache-maven/src/assembly/maven/bin/mvn
index 04b149010b0e..d1da8cedf99f 100755
--- a/apache-maven/src/assembly/maven/bin/mvn
+++ b/apache-maven/src/assembly/maven/bin/mvn
@@ -226,17 +226,34 @@ fi
if [ -n "$MAVEN_DEBUG_SCRIPT" ]; then
echo "[DEBUG] Final MAVEN_OPTS: $MAVEN_OPTS" >&2
fi
-LAUNCHER_JAR=`echo "$MAVEN_HOME"/boot/plexus-classworlds-*.jar`
+LAUNCHER_JAR=""
+for _jar in "$MAVEN_HOME"/boot/*.jar; do
+ [ -f "$_jar" ] && LAUNCHER_JAR="${LAUNCHER_JAR:+$LAUNCHER_JAR:}$_jar"
+done
LAUNCHER_CLASS=org.codehaus.plexus.classworlds.launcher.Launcher
+MODULES_DIR="$MAVEN_HOME/lib/modules"
+
+# Module-path and native access flags require a JDK that can read the module jars.
+# The probe includes --module-path so it fails on JDKs that cannot read the module descriptors.
+USE_MODULE_PATH=false
+if "$JAVACMD" --module-path "$MODULES_DIR" --add-modules ALL-MODULE-PATH --enable-native-access=org.jline.terminal.ffm -version >/dev/null 2>&1; then
+ USE_MODULE_PATH=true
+fi
# For Cygwin and MinGW, switch paths to Windows format before running java(1) command
if $cygwin || $mingw ; then
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --windows "$JAVA_HOME"`
- LAUNCHER_JAR=`cygpath --windows "$LAUNCHER_JAR"`
+ LAUNCHER_JAR=`cygpath --windows --path "$LAUNCHER_JAR"`
CLASSWORLDS_CONF=`cygpath --windows "$CLASSWORLDS_CONF"`
MAVEN_HOME=`cygpath --windows "$MAVEN_HOME"`
MAVEN_PROJECTBASEDIR=`cygpath --windows "$MAVEN_PROJECTBASEDIR"`
+ MODULES_DIR=`cygpath --windows "$MODULES_DIR"`
+fi
+
+MAVEN_MODULE_OPTS=""
+if $USE_MODULE_PATH; then
+ MAVEN_MODULE_OPTS="--module-path \"$MODULES_DIR\" --add-modules ALL-MODULE-PATH --enable-native-access=org.jline.terminal.ffm,org.jline.nativ"
fi
handle_args() {
@@ -279,7 +296,7 @@ MAVEN_MAIN_CLASS=${MAVEN_MAIN_CLASS:=org.apache.maven.cling.MavenCling}
cmd="\"$JAVACMD\" \
$MAVEN_OPTS \
$MAVEN_DEBUG_OPTS \
- --enable-native-access=ALL-UNNAMED \
+ $MAVEN_MODULE_OPTS \
-classpath \"$LAUNCHER_JAR\" \
\"-Dclassworlds.conf=$CLASSWORLDS_CONF\" \
\"-Dmaven.home=$MAVEN_HOME\" \
diff --git a/apache-maven/src/assembly/maven/bin/mvn.cmd b/apache-maven/src/assembly/maven/bin/mvn.cmd
index f25f85858f7a..795db16d5947 100644
--- a/apache-maven/src/assembly/maven/bin/mvn.cmd
+++ b/apache-maven/src/assembly/maven/bin/mvn.cmd
@@ -266,13 +266,22 @@ goto processArgs
:endHandleArgs
call :processArgs %*
-for %%i in ("%MAVEN_HOME%"\boot\plexus-classworlds-*) do set LAUNCHER_JAR="%%i"
+set LAUNCHER_JAR="%MAVEN_HOME%\boot\*"
set LAUNCHER_CLASS=org.codehaus.plexus.classworlds.launcher.Launcher
+set MODULES_DIR="%MAVEN_HOME%\lib\modules"
if "%MAVEN_MAIN_CLASS%"=="" @set MAVEN_MAIN_CLASS=org.apache.maven.cling.MavenCling
+@rem Module-path and native access flags require a JDK that can read the module jars.
+@rem The probe includes --module-path so it fails on JDKs that cannot read the module descriptors.
+set MAVEN_MODULE_OPTS=
+"%JAVACMD%" --module-path %MODULES_DIR% --add-modules ALL-MODULE-PATH --enable-native-access=org.jline.terminal.ffm -version >NUL 2>&1
+if not ERRORLEVEL 1 (
+ set MAVEN_MODULE_OPTS=--module-path %MODULES_DIR% --add-modules ALL-MODULE-PATH --enable-native-access=org.jline.terminal.ffm,org.jline.nativ
+)
+
if defined MAVEN_DEBUG_SCRIPT (
echo [DEBUG] Launching JVM with command:
- echo [DEBUG] "%JAVACMD%" %INTERNAL_MAVEN_OPTS% %MAVEN_OPTS% %JVM_CONFIG_MAVEN_OPTS% %MAVEN_DEBUG_OPTS% --enable-native-access=ALL-UNNAMED -classpath %LAUNCHER_JAR% "-Dclassworlds.conf=%CLASSWORLDS_CONF%" "-Dmaven.home=%MAVEN_HOME%" "-Dmaven.mainClass=%MAVEN_MAIN_CLASS%" "-Dlibrary.jline.path=%MAVEN_HOME%\lib\jline-native" "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %LAUNCHER_CLASS% %MAVEN_ARGS% %*
+ echo [DEBUG] "%JAVACMD%" %INTERNAL_MAVEN_OPTS% %MAVEN_OPTS% %JVM_CONFIG_MAVEN_OPTS% %MAVEN_DEBUG_OPTS% %MAVEN_MODULE_OPTS% -classpath %LAUNCHER_JAR% "-Dclassworlds.conf=%CLASSWORLDS_CONF%" "-Dmaven.home=%MAVEN_HOME%" "-Dmaven.mainClass=%MAVEN_MAIN_CLASS%" "-Dlibrary.jline.path=%MAVEN_HOME%\lib\jline-native" "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %LAUNCHER_CLASS% %MAVEN_ARGS% %*
)
"%JAVACMD%" ^
@@ -280,7 +289,7 @@ if defined MAVEN_DEBUG_SCRIPT (
%MAVEN_OPTS% ^
%JVM_CONFIG_MAVEN_OPTS% ^
%MAVEN_DEBUG_OPTS% ^
- --enable-native-access=ALL-UNNAMED ^
+ %MAVEN_MODULE_OPTS% ^
-classpath %LAUNCHER_JAR% ^
"-Dclassworlds.conf=%CLASSWORLDS_CONF%" ^
"-Dmaven.home=%MAVEN_HOME%" ^
diff --git a/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm b/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm
index 2f95d5d77126..adee298985d7 100644
--- a/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm
+++ b/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm
@@ -270,9 +270,13 @@ subject to the terms and conditions of the following licenses:
#* *##end ##
#* *###
#* *### Classworlds is in boot directory, not in lib
-#* *##if ( $project.artifact.artifactId == "plexus-classworlds" )
+#* *##if ( $project.artifact.artifactId == "maven-classworlds" )
#* *##set ( $directory = 'boot' ) ##
#* *##end ##
+#* *### JLine modules are in lib/modules directory
+#* *##if ( $groupId == "org.jline" )
+#* *##set ( $directory = 'lib/modules' ) ##
+#* *##end ##
#* *###
#* *### copy license file to lib/$artifactId.license
#* *##set ( $licFile = $directory + '/' + $project.artifact.artifactId + '.license' ) ##
diff --git a/api/maven-api-classworlds/pom.xml b/api/maven-api-classworlds/pom.xml
new file mode 100644
index 000000000000..e19dd9869530
--- /dev/null
+++ b/api/maven-api-classworlds/pom.xml
@@ -0,0 +1,39 @@
+
+
+
+ 4.0.0
+
+ org.apache.maven
+ maven-api
+ 4.1.0-SNAPSHOT
+
+
+ maven-api-classworlds
+ Maven 4 API :: Classworlds
+ Maven 4 API for class loading realms and isolation.
+
+
+
+ org.apache.maven
+ maven-api-annotations
+
+
+
+
diff --git a/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassRealm.java b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassRealm.java
new file mode 100644
index 000000000000..25da736e32bb
--- /dev/null
+++ b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassRealm.java
@@ -0,0 +1,223 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.classworlds;
+
+import java.io.Closeable;
+import java.net.URL;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Nonnull;
+import org.apache.maven.api.annotations.Nullable;
+
+/**
+ * A class loading realm that provides isolated class loading with controlled imports and exports.
+ *
+ * A ClassRealm represents an isolated class loading environment with its own classpath
+ * and controlled access to classes from other realms through imports.
+ *
+ *
+ * @since 4.1.0
+ */
+@Experimental
+public interface ClassRealm extends Closeable {
+
+ /**
+ * Returns the unique identifier for this realm.
+ *
+ * @return the realm identifier
+ */
+ @Nonnull
+ String getId();
+
+ /**
+ * Returns the class world that contains this realm.
+ *
+ * @return the parent class world
+ */
+ @Nonnull
+ ClassWorld getWorld();
+
+ /**
+ * Returns the underlying ClassLoader for this realm.
+ *
+ * This method allows access to the actual ClassLoader implementation
+ * while maintaining API abstraction.
+ *
+ *
+ * @return the underlying ClassLoader
+ */
+ @Nonnull
+ ClassLoader getClassLoader();
+
+ /**
+ * Returns the class loading strategy used by this realm.
+ *
+ * @return the strategy
+ */
+ @Nonnull
+ Strategy getStrategy();
+
+ /**
+ * Adds a URL to this realm's classpath.
+ *
+ * @param url the URL to add
+ */
+ void addURL(@Nonnull URL url);
+
+ /**
+ * Returns the URLs in this realm's classpath.
+ *
+ * @return array of URLs in the classpath
+ */
+ @Nonnull
+ URL[] getURLs();
+
+ /**
+ * Imports classes from the specified realm for the given package.
+ *
+ * @param realmId the identifier of the realm to import from
+ * @param packageName the package name to import (supports wildcards)
+ * @throws NoSuchRealmException if the specified realm doesn't exist
+ */
+ void importFrom(@Nonnull String realmId, @Nonnull String packageName) throws NoSuchRealmException;
+
+ /**
+ * Imports classes from the specified class loader for the given package.
+ *
+ * @param classLoader the class loader to import from
+ * @param packageName the package name to import (supports wildcards)
+ */
+ void importFrom(@Nonnull ClassLoader classLoader, @Nonnull String packageName);
+
+ /**
+ * Returns the class loader that would handle the specified class name through imports.
+ *
+ * @param name the class name
+ * @return the import class loader, or null if no import matches
+ */
+ @Nullable
+ ClassLoader getImportClassLoader(@Nonnull String name);
+
+ // Note: getImportRealms method is not included in the API interface
+ // to avoid conflicts with the existing implementation signature
+
+ /**
+ * Sets the parent class loader for this realm.
+ *
+ * @param parentClassLoader the parent class loader, may be null
+ */
+ void setParentClassLoader(@Nullable ClassLoader parentClassLoader);
+
+ /**
+ * Returns the parent class loader for this realm.
+ *
+ * @return the parent class loader, may be null
+ */
+ @Nullable
+ ClassLoader getParentClassLoader();
+
+ // Note: setParentRealm method is not included in the API interface
+ // to avoid conflicts with the existing implementation signature
+
+ // Note: getParentRealm method is not included in the API interface
+ // to avoid conflicts with the existing implementation signature
+
+ // Note: createChildRealm method is not included in the API interface
+ // to avoid conflicts with the existing implementation signature
+
+ /**
+ * Loads a class from this realm only (not from imports or parent).
+ *
+ * @param name the class name
+ * @return the loaded class, or null if not found
+ */
+ @Nullable
+ Class> loadClassFromSelf(@Nonnull String name);
+
+ /**
+ * Loads a class from imported realms/classloaders.
+ *
+ * @param name the class name
+ * @return the loaded class, or null if not found
+ */
+ @Nullable
+ Class> loadClassFromImport(@Nonnull String name);
+
+ /**
+ * Loads a class from the parent class loader.
+ *
+ * @param name the class name
+ * @return the loaded class, or null if not found
+ */
+ @Nullable
+ Class> loadClassFromParent(@Nonnull String name);
+
+ /**
+ * Loads a resource from this realm only (not from imports or parent).
+ *
+ * @param name the resource name
+ * @return the resource URL, or null if not found
+ */
+ @Nullable
+ URL loadResourceFromSelf(@Nonnull String name);
+
+ /**
+ * Loads a resource from imported realms/classloaders.
+ *
+ * @param name the resource name
+ * @return the resource URL, or null if not found
+ */
+ @Nullable
+ URL loadResourceFromImport(@Nonnull String name);
+
+ /**
+ * Loads a resource from the parent class loader.
+ *
+ * @param name the resource name
+ * @return the resource URL, or null if not found
+ */
+ @Nullable
+ URL loadResourceFromParent(@Nonnull String name);
+
+ /**
+ * Exports a package from a named JPMS module to this realm's unnamed module,
+ * making the package's public types accessible to classes loaded by this realm.
+ *
+ * @param moduleName the source module name
+ * @param packageName the package to export
+ */
+ default void addExports(@Nonnull String moduleName, @Nonnull String packageName) {}
+
+ /**
+ * Opens a package from a named JPMS module to this realm's unnamed module for deep reflection,
+ * making the package's types accessible via reflection (including non-public members).
+ *
+ * @param moduleName the source module name
+ * @param packageName the package to open
+ */
+ default void addOpens(@Nonnull String moduleName, @Nonnull String packageName) {}
+
+ /**
+ * Adds a reads edge from a named JPMS module to this realm's unnamed module,
+ * allowing the named module to access types in this realm.
+ *
+ * @param moduleName the source module that should read this realm's unnamed module
+ */
+ default void addReads(@Nonnull String moduleName) {}
+}
diff --git a/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassWorld.java b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassWorld.java
new file mode 100644
index 000000000000..f0f4f876c7ef
--- /dev/null
+++ b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassWorld.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.classworlds;
+
+import java.io.Closeable;
+import java.util.function.Predicate;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Nonnull;
+import org.apache.maven.api.annotations.Nullable;
+
+/**
+ * A collection of {@link ClassRealm}s, indexed by id.
+ *
+ * A ClassWorld provides a container for managing multiple class loading realms,
+ * each with their own classpath and import/export relationships.
+ *
+ *
+ * @since 4.1.0
+ */
+@Experimental
+public interface ClassWorld extends Closeable {
+
+ /**
+ * Creates a new class realm with the specified id.
+ *
+ * @param id the unique identifier for the realm
+ * @return the newly created class realm
+ * @throws DuplicateRealmException if a realm with the same id already exists
+ */
+ @Nonnull
+ ClassRealm newRealm(@Nonnull String id) throws DuplicateRealmException;
+
+ /**
+ * Creates a new class realm with the specified id and base class loader.
+ *
+ * @param id the unique identifier for the realm
+ * @param classLoader the base class loader for the realm, may be null
+ * @return the newly created class realm
+ * @throws DuplicateRealmException if a realm with the same id already exists
+ */
+ @Nonnull
+ ClassRealm newRealm(@Nonnull String id, @Nullable ClassLoader classLoader) throws DuplicateRealmException;
+
+ /**
+ * Creates a new filtered class realm with the specified id, base class loader, and filter.
+ *
+ * @param id the unique identifier for the realm
+ * @param classLoader the base class loader for the realm, may be null
+ * @param filter the filter to apply to class loading, may be null
+ * @return the newly created class realm
+ * @throws DuplicateRealmException if a realm with the same id already exists
+ */
+ @Nonnull
+ ClassRealm newRealm(@Nonnull String id, @Nullable ClassLoader classLoader, @Nullable Predicate filter)
+ throws DuplicateRealmException;
+
+ /**
+ * Retrieves the class realm with the specified id.
+ *
+ * @param id the realm identifier
+ * @return the class realm
+ * @throws NoSuchRealmException if no realm with the specified id exists
+ */
+ @Nonnull
+ ClassRealm getRealm(@Nonnull String id) throws NoSuchRealmException;
+
+ /**
+ * Retrieves the class realm with the specified id, or null if it doesn't exist.
+ *
+ * @param id the realm identifier
+ * @return the class realm, or null if not found
+ */
+ @Nullable
+ ClassRealm getClassRealm(@Nonnull String id);
+
+ // Note: getRealms method is not included in the API interface
+ // to avoid conflicts with the existing implementation signature
+
+ /**
+ * Disposes of the class realm with the specified id.
+ *
+ * @param id the realm identifier
+ * @throws NoSuchRealmException if no realm with the specified id exists
+ */
+ void disposeRealm(@Nonnull String id) throws NoSuchRealmException;
+
+ /**
+ * Adds a listener to be notified of realm lifecycle events.
+ *
+ * @param listener the listener to add
+ */
+ void addListener(@Nonnull ClassWorldListener listener);
+
+ /**
+ * Removes a listener from realm lifecycle event notifications.
+ *
+ * @param listener the listener to remove
+ */
+ void removeListener(@Nonnull ClassWorldListener listener);
+}
diff --git a/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassWorldException.java b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassWorldException.java
new file mode 100644
index 000000000000..e18d67267d5e
--- /dev/null
+++ b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassWorldException.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.classworlds;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Nonnull;
+import org.apache.maven.api.annotations.Nullable;
+
+/**
+ * Base exception for class world related errors.
+ *
+ * This is the root exception type for all class world operations.
+ * Specific error conditions are represented by subclasses.
+ *
+ *
+ * @since 4.1.0
+ */
+@Experimental
+public class ClassWorldException extends Exception {
+
+ /**
+ * The class world associated with this exception.
+ */
+ private final ClassWorld world;
+
+ /**
+ * Constructs a new ClassWorldException.
+ *
+ * @param world the class world associated with this exception
+ */
+ public ClassWorldException(@Nonnull ClassWorld world) {
+ this.world = world;
+ }
+
+ /**
+ * Constructs a new ClassWorldException with the specified detail message.
+ *
+ * @param world the class world associated with this exception
+ * @param message the detail message
+ */
+ public ClassWorldException(@Nonnull ClassWorld world, @Nullable String message) {
+ super(message);
+ this.world = world;
+ }
+
+ /**
+ * Constructs a new ClassWorldException with the specified detail message and cause.
+ *
+ * @param world the class world associated with this exception
+ * @param message the detail message
+ * @param cause the cause
+ */
+ public ClassWorldException(@Nonnull ClassWorld world, @Nullable String message, @Nullable Throwable cause) {
+ super(message, cause);
+ this.world = world;
+ }
+
+ /**
+ * Constructs a new ClassWorldException with the specified cause.
+ *
+ * @param world the class world associated with this exception
+ * @param cause the cause
+ */
+ public ClassWorldException(@Nonnull ClassWorld world, @Nullable Throwable cause) {
+ super(cause);
+ this.world = world;
+ }
+
+ /**
+ * Returns the class world associated with this exception.
+ *
+ * @return the class world
+ */
+ @Nonnull
+ public ClassWorld getWorld() {
+ return world;
+ }
+}
diff --git a/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassWorldListener.java b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassWorldListener.java
new file mode 100644
index 000000000000..5904b23d87d0
--- /dev/null
+++ b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassWorldListener.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.classworlds;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Nonnull;
+
+/**
+ * Listener interface for class realm lifecycle events.
+ *
+ * Implementations of this interface can be registered with a {@link ClassWorld}
+ * to receive notifications when realms are created or disposed.
+ *
+ *
+ * @since 4.1.0
+ */
+@Experimental
+public interface ClassWorldListener {
+
+ /**
+ * Called when a new class realm is created.
+ *
+ * @param realm the newly created realm
+ */
+ void realmCreated(@Nonnull ClassRealm realm);
+
+ /**
+ * Called when a class realm is disposed.
+ *
+ * @param realm the disposed realm
+ */
+ void realmDisposed(@Nonnull ClassRealm realm);
+}
diff --git a/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/DuplicateRealmException.java b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/DuplicateRealmException.java
new file mode 100644
index 000000000000..7d9ca993b288
--- /dev/null
+++ b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/DuplicateRealmException.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.classworlds;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Nonnull;
+
+/**
+ * Exception thrown when attempting to create a class realm with an identifier
+ * that already exists in the class world.
+ *
+ * @since 4.1.0
+ */
+@Experimental
+public class DuplicateRealmException extends ClassWorldException {
+
+ /**
+ * The duplicate realm identifier.
+ */
+ private final String id;
+
+ /**
+ * Constructs a new DuplicateRealmException.
+ *
+ * @param world the class world
+ * @param id the duplicate realm identifier
+ */
+ public DuplicateRealmException(@Nonnull ClassWorld world, @Nonnull String id) {
+ super(world, "Duplicate realm: " + id);
+ this.id = id;
+ }
+
+ /**
+ * Returns the duplicate realm identifier.
+ *
+ * @return the realm identifier
+ */
+ @Nonnull
+ public String getId() {
+ return id;
+ }
+}
diff --git a/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/NoSuchRealmException.java b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/NoSuchRealmException.java
new file mode 100644
index 000000000000..223fd8df9db9
--- /dev/null
+++ b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/NoSuchRealmException.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.classworlds;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Nonnull;
+
+/**
+ * Exception thrown when attempting to retrieve a class realm with an identifier
+ * that does not exist in the class world.
+ *
+ * @since 4.1.0
+ */
+@Experimental
+public class NoSuchRealmException extends ClassWorldException {
+
+ /**
+ * The missing realm identifier.
+ */
+ private final String id;
+
+ /**
+ * Constructs a new NoSuchRealmException.
+ *
+ * @param world the class world
+ * @param id the missing realm identifier
+ */
+ public NoSuchRealmException(@Nonnull ClassWorld world, @Nonnull String id) {
+ super(world, "No such realm: " + id);
+ this.id = id;
+ }
+
+ /**
+ * Returns the missing realm identifier.
+ *
+ * @return the realm identifier
+ */
+ @Nonnull
+ public String getId() {
+ return id;
+ }
+}
diff --git a/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/Strategy.java b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/Strategy.java
new file mode 100644
index 000000000000..371619f4feda
--- /dev/null
+++ b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/Strategy.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.classworlds;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Nonnull;
+import org.apache.maven.api.annotations.Nullable;
+
+/**
+ * A strategy for defining how classes and resources are located in class realms.
+ *
+ * Different strategies can implement different class loading behaviors, such as
+ * parent-first, child-first, or custom delegation patterns.
+ *
+ *
+ * @since 4.1.0
+ */
+@Experimental
+public interface Strategy {
+
+ /**
+ * Loads a class using this strategy.
+ *
+ * @param name the fully qualified class name
+ * @return the loaded class
+ * @throws ClassNotFoundException if the class cannot be found
+ */
+ @Nonnull
+ Class> loadClass(@Nonnull String name) throws ClassNotFoundException;
+
+ /**
+ * Finds a resource using this strategy.
+ *
+ * @param name the resource name
+ * @return the resource URL, or null if not found
+ */
+ @Nullable
+ URL getResource(@Nonnull String name);
+
+ /**
+ * Finds all resources with the given name using this strategy.
+ *
+ * @param name the resource name
+ * @return an enumeration of resource URLs
+ * @throws IOException if an I/O error occurs
+ */
+ @Nonnull
+ Enumeration getResources(@Nonnull String name) throws IOException;
+
+ /**
+ * Returns the class realm that this strategy operates on.
+ *
+ * @return the associated class realm
+ */
+ @Nonnull
+ ClassRealm getRealm();
+}
diff --git a/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/package-info.java b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/package-info.java
new file mode 100644
index 000000000000..f0b5a0763b88
--- /dev/null
+++ b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/package-info.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Maven 4 API for class loading realms and isolation.
+ *
+ * This package provides the public API for Maven's class loading system, which allows
+ * for isolated class loading environments (realms) with controlled imports and exports
+ * between them.
+ *
+ *
+ * Key concepts:
+ *
+ *
+ * - {@link org.apache.maven.api.classworlds.ClassWorld} - A container for multiple class realms
+ * - {@link org.apache.maven.api.classworlds.ClassRealm} - An isolated class loading environment
+ * - {@link org.apache.maven.api.classworlds.Strategy} - Defines class loading delegation behavior
+ * - {@link org.apache.maven.api.classworlds.ClassWorldListener} - Listens to realm lifecycle events
+ *
+ *
+ * This API follows Maven 4 conventions:
+ *
+ *
+ * - Only public interfaces and enums are exposed
+ * - All interfaces are marked as {@code @Experimental}
+ * - Proper nullability annotations are used
+ * - Implementation details are hidden behind the API
+ *
+ *
+ * @since 4.1.0
+ */
+@org.apache.maven.api.annotations.Experimental
+package org.apache.maven.api.classworlds;
diff --git a/api/pom.xml b/api/pom.xml
index b90c9f605c81..2c917f360293 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -33,6 +33,7 @@
maven-api-annotations
+ maven-api-classworlds
maven-api-di
maven-api-xml
maven-api-model
diff --git a/compat/maven-compat/pom.xml b/compat/maven-compat/pom.xml
index 3d3fcdee232b..0465eb7571ef 100644
--- a/compat/maven-compat/pom.xml
+++ b/compat/maven-compat/pom.xml
@@ -121,8 +121,12 @@ under the License.
- org.codehaus.plexus
- plexus-classworlds
+ org.apache.maven
+ maven-api-classworlds
+
+
+ org.apache.maven
+ maven-classworlds
org.codehaus.plexus
diff --git a/compat/maven-embedder/pom.xml b/compat/maven-embedder/pom.xml
index 2df8588ec116..8060168e8e14 100644
--- a/compat/maven-embedder/pom.xml
+++ b/compat/maven-embedder/pom.xml
@@ -146,8 +146,12 @@ under the License.
- org.codehaus.plexus
- plexus-classworlds
+ org.apache.maven
+ maven-api-classworlds
+
+
+ org.apache.maven
+ maven-classworlds
org.codehaus.plexus
diff --git a/compat/maven-embedder/src/main/java/org/apache/maven/cli/CliRequest.java b/compat/maven-embedder/src/main/java/org/apache/maven/cli/CliRequest.java
index 6d54be4ce054..0b4c45f0056b 100644
--- a/compat/maven-embedder/src/main/java/org/apache/maven/cli/CliRequest.java
+++ b/compat/maven-embedder/src/main/java/org/apache/maven/cli/CliRequest.java
@@ -23,9 +23,9 @@
import java.util.Properties;
import org.apache.commons.cli.CommandLine;
+import org.apache.maven.api.classworlds.ClassWorld;
import org.apache.maven.execution.DefaultMavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionRequest;
-import org.codehaus.plexus.classworlds.ClassWorld;
/**
* CliRequest
diff --git a/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java b/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java
index ebcb335ac074..80df683ae32c 100644
--- a/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java
+++ b/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java
@@ -59,6 +59,9 @@
import org.apache.maven.InternalErrorException;
import org.apache.maven.Maven;
import org.apache.maven.api.Constants;
+import org.apache.maven.api.classworlds.ClassRealm;
+import org.apache.maven.api.classworlds.ClassWorld;
+import org.apache.maven.api.classworlds.NoSuchRealmException;
import org.apache.maven.api.cli.extensions.CoreExtension;
import org.apache.maven.api.cli.extensions.InputSource;
import org.apache.maven.api.services.MessageBuilder;
@@ -117,9 +120,6 @@
import org.codehaus.plexus.DefaultPlexusContainer;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
-import org.codehaus.plexus.classworlds.ClassWorld;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
-import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
import org.codehaus.plexus.logging.LoggerManager;
@@ -259,7 +259,7 @@ public int doMain(String[] args, String workingDirectory, PrintStream stdout, Pr
final Set realms;
if (classWorld != null) {
realms = new HashSet<>();
- for (ClassRealm realm : classWorld.getRealms()) {
+ for (ClassRealm realm : ((org.codehaus.plexus.classworlds.ClassWorld) classWorld).getRealms()) {
realms.add(realm.getId());
}
} else {
@@ -280,7 +280,8 @@ public int doMain(String[] args, String workingDirectory, PrintStream stdout, Pr
return doMain(cliRequest);
} finally {
if (classWorld != null) {
- for (ClassRealm realm : new ArrayList<>(classWorld.getRealms())) {
+ for (ClassRealm realm :
+ new ArrayList<>(((org.codehaus.plexus.classworlds.ClassWorld) classWorld).getRealms())) {
String realmId = realm.getId();
if (!realms.contains(realmId)) {
try {
@@ -708,13 +709,16 @@ void properties(CliRequest cliRequest) throws Exception {
PlexusContainer container(CliRequest cliRequest) throws Exception {
if (cliRequest.classWorld == null) {
- cliRequest.classWorld =
- new ClassWorld("plexus.core", Thread.currentThread().getContextClassLoader());
+ cliRequest.classWorld = new org.codehaus.plexus.classworlds.ClassWorld(
+ "plexus.core", Thread.currentThread().getContextClassLoader());
}
ClassRealm coreRealm = cliRequest.classWorld.getClassRealm("plexus.core");
if (coreRealm == null) {
- coreRealm = cliRequest.classWorld.getRealms().iterator().next();
+ coreRealm = ((org.codehaus.plexus.classworlds.ClassWorld) cliRequest.classWorld)
+ .getRealms()
+ .iterator()
+ .next();
}
List extClassPath = parseExtClasspath(cliRequest);
@@ -726,8 +730,8 @@ PlexusContainer container(CliRequest cliRequest) throws Exception {
ClassRealm containerRealm = setupContainerRealm(cliRequest.classWorld, coreRealm, extClassPath, extensions);
ContainerConfiguration cc = new DefaultContainerConfiguration()
- .setClassWorld(cliRequest.classWorld)
- .setRealm(containerRealm)
+ .setClassWorld((org.codehaus.plexus.classworlds.ClassWorld) cliRequest.classWorld)
+ .setRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) containerRealm)
.setClassPathScanning(PlexusConstants.SCANNING_INDEX)
.setAutoWiring(true)
.setJSR250Lifecycle(true)
@@ -743,7 +747,7 @@ PlexusContainer container(CliRequest cliRequest) throws Exception {
final CoreExports exports = new CoreExports(containerRealm, exportedArtifacts, exportedPackages);
- Thread.currentThread().setContextClassLoader(containerRealm);
+ Thread.currentThread().setContextClassLoader(containerRealm.getClassLoader());
DefaultPlexusContainer container = new DefaultPlexusContainer(cc, new AbstractModule() {
@Override
@@ -769,12 +773,14 @@ protected void configure() {
};
for (CoreExtensionEntry extension : extensions) {
container.discoverComponents(
- extension.getClassRealm(),
+ (org.codehaus.plexus.classworlds.realm.ClassRealm) extension.getClassRealm(),
new AbstractModule() {
@Override
protected void configure() {
try {
- container.lookup(Injector.class).discover(extension.getClassRealm());
+ container
+ .lookup(Injector.class)
+ .discover(extension.getClassRealm().getClassLoader());
} catch (Throwable e) {
// ignore
e.printStackTrace();
@@ -842,8 +848,8 @@ private List loadCoreExtensions(
}
ContainerConfiguration cc = new DefaultContainerConfiguration() //
- .setClassWorld(cliRequest.classWorld) //
- .setRealm(containerRealm) //
+ .setClassWorld((org.codehaus.plexus.classworlds.ClassWorld) cliRequest.classWorld) //
+ .setRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) containerRealm) //
.setClassPathScanning(PlexusConstants.SCANNING_INDEX) //
.setAutoWiring(true) //
.setJSR250Lifecycle(true) //
@@ -909,7 +915,8 @@ private ClassRealm setupContainerRealm(
if (!extClassPath.isEmpty() || !extensions.isEmpty()) {
ClassRealm extRealm = classWorld.newRealm("maven.ext", null);
- extRealm.setParentRealm(coreRealm);
+ ((org.codehaus.plexus.classworlds.realm.ClassRealm) extRealm)
+ .setParentRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) coreRealm);
slf4jLogger.debug("Populating class realm '{}'", extRealm.getId());
@@ -923,11 +930,11 @@ private ClassRealm setupContainerRealm(
Set exportedPackages = entry.getExportedPackages();
ClassRealm realm = entry.getClassRealm();
for (String exportedPackage : exportedPackages) {
- extRealm.importFrom(realm, exportedPackage);
+ extRealm.importFrom(realm.getClassLoader(), exportedPackage);
}
if (exportedPackages.isEmpty()) {
// sisu uses realm imports to establish component visibility
- extRealm.importFrom(realm, realm.getId());
+ extRealm.importFrom(realm.getClassLoader(), realm.getId());
}
}
diff --git a/compat/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java b/compat/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java
index 06990ea303bc..47d110f914cb 100644
--- a/compat/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java
+++ b/compat/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java
@@ -33,6 +33,8 @@
import org.apache.maven.RepositoryUtils;
import org.apache.maven.api.Service;
import org.apache.maven.api.Session;
+import org.apache.maven.api.classworlds.ClassRealm;
+import org.apache.maven.api.classworlds.ClassWorld;
import org.apache.maven.api.cli.extensions.CoreExtension;
import org.apache.maven.api.model.Plugin;
import org.apache.maven.api.services.ArtifactCoordinatesFactory;
@@ -64,8 +66,6 @@
import org.apache.maven.resolver.RepositorySystemSessionFactory;
import org.codehaus.plexus.DefaultPlexusContainer;
import org.codehaus.plexus.PlexusContainer;
-import org.codehaus.plexus.classworlds.ClassWorld;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.RepositorySystemSession.CloseableSession;
@@ -175,12 +175,13 @@ private CoreExtensionEntry createExtension(CoreExtension extension, List providedArtifacts = Collections.emptySet();
String classLoadingStrategy = extension.getClassLoadingStrategy();
if (STRATEGY_PARENT_FIRST.equals(classLoadingStrategy)) {
- realm.importFrom(parentRealm, "");
+ realm.importFrom(parentRealm.getClassLoader(), "");
} else if (STRATEGY_PLUGIN.equals(classLoadingStrategy)) {
coreExports.getExportedPackages().forEach((p, cl) -> realm.importFrom(cl, p));
providedArtifacts = coreExports.getExportedArtifacts();
} else if (STRATEGY_SELF_FIRST.equals(classLoadingStrategy)) {
- realm.setParentRealm(parentRealm);
+ ((org.codehaus.plexus.classworlds.realm.ClassRealm) realm)
+ .setParentRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) parentRealm);
} else {
throw new IllegalArgumentException("Unsupported class-loading strategy '"
+ classLoadingStrategy + "'. Supported values are: " + STRATEGY_PARENT_FIRST
diff --git a/compat/maven-plugin-api/pom.xml b/compat/maven-plugin-api/pom.xml
index 6bd39596deb8..c8fc5e23fc9d 100644
--- a/compat/maven-plugin-api/pom.xml
+++ b/compat/maven-plugin-api/pom.xml
@@ -79,8 +79,8 @@ under the License.
- org.codehaus.plexus
- plexus-classworlds
+ org.apache.maven
+ maven-classworlds
org.apache.maven.resolver
diff --git a/compat/maven-resolver-provider/pom.xml b/compat/maven-resolver-provider/pom.xml
index 79c7507becd2..7130c64d418e 100644
--- a/compat/maven-resolver-provider/pom.xml
+++ b/compat/maven-resolver-provider/pom.xml
@@ -151,6 +151,11 @@ under the License.
plexus-testing
test
+
+ org.apache.maven
+ maven-classworlds
+ test
+
org.mockito
mockito-core
diff --git a/impl/maven-classworlds/pom.xml b/impl/maven-classworlds/pom.xml
new file mode 100644
index 000000000000..ccf4a168668c
--- /dev/null
+++ b/impl/maven-classworlds/pom.xml
@@ -0,0 +1,131 @@
+
+
+
+ 4.0.0
+
+ org.apache.maven
+ maven-impl-modules
+ 4.1.0-SNAPSHOT
+
+
+ maven-classworlds
+ jar
+
+ Maven 4 Classworlds
+ A class loader framework
+
+
+
+ org.apache.maven
+ maven-api-annotations
+
+
+ org.apache.maven
+ maven-api-classworlds
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+
+
+
+
+ org.apache.rat
+ apache-rat-plugin
+
+
+ src/test/test-data/**
+
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+
+ org/codehaus/classworlds/**
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ org.codehaus.plexus.classworlds.launcher.Launcher
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ true
+ -ea:org.codehaus.classworlds:org.codehaus.plexus.classworlds
+ 1
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ org/codehaus/plexus/classworlds/event/*
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+
+ copy
+
+ generate-test-resources
+
+
+
+ org.apache.ant
+ ant
+ 1.10.14
+
+
+ org.apache.logging.log4j
+ log4j-api
+ 2.23.1
+
+
+ jakarta.xml.bind
+ jakarta.xml.bind-api
+ 4.0.2
+
+
+ ${project.build.directory}/test-lib
+
+
+
+
+
+
+
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/BytesURLConnection.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/BytesURLConnection.java
new file mode 100644
index 000000000000..656e77cb09dd
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/BytesURLConnection.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.classworlds;
+
+/*
+ * Copyright 2001-2010 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+
+/**
+ * URLConnection implementation for byte arrays.
+ *
+ * Note: This is a legacy class provided for backward compatibility with Maven 2.
+ * New code should avoid using this internal implementation detail.
+ *
+ * @deprecated This is a legacy internal class.
+ */
+@Deprecated
+public class BytesURLConnection extends URLConnection {
+ protected final byte[] content;
+
+ protected int offset;
+
+ protected int length;
+
+ public BytesURLConnection(URL url, byte[] content) {
+ super(url);
+ this.content = content;
+ }
+
+ public void connect() {}
+
+ public InputStream getInputStream() {
+ return new ByteArrayInputStream(content);
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/BytesURLStreamHandler.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/BytesURLStreamHandler.java
new file mode 100644
index 000000000000..182c57277452
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/BytesURLStreamHandler.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.classworlds;
+
+/*
+ * Copyright 2001-2010 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+
+/**
+ * URLStreamHandler implementation for byte arrays.
+ *
+ * Note: This is a legacy class provided for backward compatibility with Maven 2.
+ * New code should avoid using this internal implementation detail.
+ *
+ * @author Hani Suleiman (hani@formicary.net) Date: Oct 20, 2003 12:45:18 AM
+ * @deprecated This is a legacy internal class.
+ */
+@Deprecated
+public class BytesURLStreamHandler extends URLStreamHandler {
+ final byte[] content;
+
+ int offset;
+
+ int length;
+
+ public BytesURLStreamHandler(byte[] content) {
+ this.content = content;
+ }
+
+ public URLConnection openConnection(URL url) {
+ return new BytesURLConnection(url, content);
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassRealm.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassRealm.java
new file mode 100644
index 000000000000..0469a480d33e
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassRealm.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.classworlds;
+
+/*
+
+Copyright 2002 (C) The Werken Company. All Rights Reserved.
+
+Redistribution and use of this software and associated documentation
+("Software"), with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Redistributions of source code must retain copyright
+ statements and notices. Redistributions must also contain a
+ copy of this document.
+
+2. Redistributions in binary form must reproduce the
+ above copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+3. The name "classworlds" must not be used to endorse or promote
+ products derived from this Software without prior written
+ permission of The Werken Company. For written permission,
+ please contact bob@werken.com.
+
+4. Products derived from this Software may not be called "classworlds"
+ nor may "classworlds" appear in their names without prior written
+ permission of The Werken Company. "classworlds" is a registered
+ trademark of The Werken Company.
+
+5. Due credit should be given to The Werken Company.
+ (http://classworlds.werken.com/).
+
+THIS SOFTWARE IS PROVIDED BY THE WERKEN COMPANY AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+THE WERKEN COMPANY OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+
+/**
+ * Autonomous sub-portion of a ClassWorld.
+ *
+ * This class most closed maps to the ClassLoader
+ * role from Java and in facts can provide a ClassLoader
+ * view of itself using {@link #getClassLoader}.
+ *
+ * This is a legacy interface from Classworlds 1.x, preserved for binary compatibility.
+ * The compiled bytecode of {@code org.eclipse.sisu:org.eclipse.sisu.plexus} references this
+ * interface directly. Removing it breaks all Maven 3+ builds.
+ * New code should use {@link org.codehaus.plexus.classworlds.realm.ClassRealm}.
+ *
+ * @author bob mcwhirter
+ * @author Jason van Zyl
+ * @deprecated Use {@link org.codehaus.plexus.classworlds.realm.ClassRealm} for new code.
+ * This interface must remain on the classpath for Sisu compatibility.
+ */
+@SuppressWarnings("rawtypes")
+@Deprecated
+public interface ClassRealm {
+ String getId();
+
+ ClassWorld getWorld();
+
+ void importFrom(String realmId, String pkgName) throws NoSuchRealmException;
+
+ void addConstituent(URL constituent);
+
+ ClassRealm locateSourceRealm(String className);
+
+ void setParent(ClassRealm classRealm);
+
+ ClassRealm createChildRealm(String id) throws DuplicateRealmException;
+
+ ClassLoader getClassLoader();
+
+ ClassRealm getParent();
+
+ URL[] getConstituents();
+
+ // ----------------------------------------------------------------------
+ // Classloading
+ // ----------------------------------------------------------------------
+
+ Class loadClass(String name) throws ClassNotFoundException;
+
+ // ----------------------------------------------------------------------
+ // Resource handling
+ // ----------------------------------------------------------------------
+
+ URL getResource(String name);
+
+ Enumeration findResources(String name) throws IOException;
+
+ InputStream getResourceAsStream(String name);
+
+ void display();
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassRealmAdapter.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassRealmAdapter.java
new file mode 100644
index 000000000000..736bc4f4332c
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassRealmAdapter.java
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.classworlds;
+
+/*
+ * Copyright 2001-2010 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+
+/**
+ * Adapter that wraps a modern {@link org.codehaus.plexus.classworlds.realm.ClassRealm} and exposes
+ * it as the legacy {@link ClassRealm} interface.
+ *
+ * This class is referenced directly by the compiled bytecode of
+ * {@code org.eclipse.sisu:org.eclipse.sisu.plexus} and must remain on the classpath for
+ * Maven 3+ to function. Do not remove or rename it. New code should not use this adapter;
+ * use {@link org.codehaus.plexus.classworlds.realm.ClassRealm} directly.
+ *
+ * @author Andrew Williams
+ * @deprecated Legacy adapter retained for Sisu binary compatibility.
+ */
+@SuppressWarnings({"UnnecessaryLocalVariable", "DeprecatedIsStillUsed", "rawtypes"})
+@Deprecated
+public class ClassRealmAdapter implements ClassRealm {
+
+ public static ClassRealmAdapter getInstance(org.codehaus.plexus.classworlds.realm.ClassRealm newRealm) {
+ ClassRealmAdapter adapter = new ClassRealmAdapter(newRealm);
+
+ return adapter;
+ }
+
+ private final org.codehaus.plexus.classworlds.realm.ClassRealm realm;
+
+ private ClassRealmAdapter(org.codehaus.plexus.classworlds.realm.ClassRealm newRealm) {
+ this.realm = newRealm;
+ }
+
+ public String getId() {
+ return realm.getId();
+ }
+
+ public ClassWorld getWorld() {
+ return ClassWorldAdapter.getInstance(realm.getWorld());
+ }
+
+ public void importFrom(String realmId, String pkgName) throws NoSuchRealmException {
+ try {
+ realm.importFrom(realmId, pkgName);
+ } catch (org.codehaus.plexus.classworlds.realm.NoSuchRealmException e) {
+ throw new NoSuchRealmException(getWorld(), e.getId());
+ }
+ }
+
+ public void addConstituent(URL constituent) {
+ realm.addURL(constituent);
+ }
+
+ public ClassRealm locateSourceRealm(String className) {
+ ClassLoader importLoader = realm.getImportClassLoader(className);
+
+ if (importLoader instanceof org.codehaus.plexus.classworlds.realm.ClassRealm) {
+ return ClassRealmAdapter.getInstance((org.codehaus.plexus.classworlds.realm.ClassRealm) importLoader);
+ } else {
+ return null;
+ }
+ }
+
+ public void setParent(ClassRealm classRealm) {
+ if (classRealm != null) {
+ realm.setParentRealm(ClassRealmReverseAdapter.getInstance(classRealm));
+ }
+ }
+
+ public ClassRealm createChildRealm(String id) throws DuplicateRealmException {
+ try {
+ return ClassRealmAdapter.getInstance(realm.createChildRealm(id));
+ } catch (org.codehaus.plexus.classworlds.realm.DuplicateRealmException e) {
+ throw new DuplicateRealmException(getWorld(), e.getId());
+ }
+ }
+
+ public ClassLoader getClassLoader() {
+ return realm;
+ }
+
+ public ClassRealm getParent() {
+ return ClassRealmAdapter.getInstance(realm.getParentRealm());
+ }
+
+ public ClassRealm getParentRealm() {
+ return ClassRealmAdapter.getInstance(realm.getParentRealm());
+ }
+
+ public URL[] getConstituents() {
+ return realm.getURLs();
+ }
+
+ public Class loadClass(String name) throws ClassNotFoundException {
+ return realm.loadClass(name);
+ }
+
+ public URL getResource(String name) {
+ return realm.getResource(trimLeadingSlash(name));
+ }
+
+ public Enumeration findResources(String name) throws IOException {
+ return realm.findResources(trimLeadingSlash(name));
+ }
+
+ public InputStream getResourceAsStream(String name) {
+ return realm.getResourceAsStream(trimLeadingSlash(name));
+ }
+
+ public void display() {
+ realm.display();
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof ClassRealm)) return false;
+
+ return getId().equals(((ClassRealm) o).getId());
+ }
+
+ /**
+ * Provides backward-compat with the old classworlds which accepted resource names with leading slashes.
+ */
+ private String trimLeadingSlash(String resource) {
+ if (resource != null && resource.startsWith("/")) {
+ return resource.substring(1);
+ } else {
+ return resource;
+ }
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassRealmReverseAdapter.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassRealmReverseAdapter.java
new file mode 100644
index 000000000000..6019e2dbbe3f
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassRealmReverseAdapter.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.classworlds;
+
+/*
+ * Copyright 2001-2010 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+
+/**
+ * A reverse adapter for ClassRealms.
+ *
+ * Note: This is a legacy internal class provided for backward compatibility with Maven 2.
+ * New code should avoid using this adapter.
+ *
+ * @author Andrew Williams
+ * @deprecated This is a legacy internal class.
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+@Deprecated
+public class ClassRealmReverseAdapter extends org.codehaus.plexus.classworlds.realm.ClassRealm {
+
+ public static ClassRealmReverseAdapter getInstance(ClassRealm oldRealm) {
+ return new ClassRealmReverseAdapter(oldRealm);
+ }
+
+ private final ClassRealm realm;
+
+ private ClassRealmReverseAdapter(ClassRealm oldRealm) {
+ super(ClassWorldReverseAdapter.getInstance(oldRealm.getWorld()), oldRealm.getId(), oldRealm.getClassLoader());
+ this.realm = oldRealm;
+ }
+
+ public String getId() {
+ return realm.getId();
+ }
+
+ public org.codehaus.plexus.classworlds.ClassWorld getWorld() {
+ return ClassWorldReverseAdapter.getInstance(realm.getWorld());
+ }
+
+ public void importFrom(String realmId, String pkgName)
+ throws org.codehaus.plexus.classworlds.realm.NoSuchRealmException {
+ try {
+ realm.importFrom(realmId, pkgName);
+ } catch (NoSuchRealmException e) {
+ throw new org.codehaus.plexus.classworlds.realm.NoSuchRealmException(getWorld(), e.getId());
+ }
+ }
+
+ public void addURL(URL constituent) {
+ realm.addConstituent(constituent);
+ }
+
+ public org.codehaus.plexus.classworlds.realm.ClassRealm locateSourceRealm(String className) {
+ return getInstance(realm.locateSourceRealm(className));
+ }
+
+ public void setParentRealm(org.codehaus.plexus.classworlds.realm.ClassRealm classRealm) {
+ realm.setParent(ClassRealmAdapter.getInstance(classRealm));
+ }
+
+ public org.codehaus.plexus.classworlds.realm.ClassRealm createChildRealm(String id)
+ throws org.codehaus.plexus.classworlds.realm.DuplicateRealmException {
+ try {
+ return getInstance(realm.createChildRealm(id));
+ } catch (DuplicateRealmException e) {
+ throw new org.codehaus.plexus.classworlds.realm.DuplicateRealmException(getWorld(), e.getId());
+ }
+ }
+
+ public ClassLoader getClassLoader() {
+ return realm.getClassLoader();
+ }
+
+ public org.codehaus.plexus.classworlds.realm.ClassRealm getParentRealm() {
+ return getInstance(realm.getParent());
+ }
+
+ public URL[] getURLs() {
+ return realm.getConstituents();
+ }
+
+ public Class loadClass(String name) throws ClassNotFoundException {
+ return realm.loadClass(name);
+ }
+
+ public URL getResource(String name) {
+ return realm.getResource(name);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Enumeration findResources(String name) throws IOException {
+ return realm.findResources(name);
+ }
+
+ public InputStream getResourceAsStream(String name) {
+ return realm.getResourceAsStream(name);
+ }
+
+ public void display() {
+ realm.display();
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof ClassRealm)) return false;
+
+ return getId().equals(((ClassRealm) o).getId());
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorld.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorld.java
new file mode 100644
index 000000000000..cdc01957987b
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorld.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.classworlds;
+
+/*
+ * Copyright 2001-2010 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.util.Collection;
+
+/**
+ * A compatibility wrapper for {@link org.codehaus.plexus.classworlds.ClassWorld}
+ * provided for legacy code.
+ *
+ * Note: This is a legacy class provided for backward compatibility with Maven 2.
+ * New code should use {@link org.codehaus.plexus.classworlds.ClassWorld}.
+ *
+ * @author Andrew Williams
+ * @deprecated Use {@link org.codehaus.plexus.classworlds.ClassWorld}
+ */
+@SuppressWarnings("rawtypes")
+@Deprecated
+public class ClassWorld {
+ private ClassWorldAdapter adapter;
+
+ public ClassWorld(String realmId, ClassLoader classLoader) {
+ adapter = ClassWorldAdapter.getInstance(new org.codehaus.plexus.classworlds.ClassWorld(realmId, classLoader));
+ }
+
+ public ClassWorld() {
+ adapter = ClassWorldAdapter.getInstance(new org.codehaus.plexus.classworlds.ClassWorld());
+ }
+
+ public ClassWorld(boolean ignore) {
+ /* fake */
+ }
+
+ public ClassRealm newRealm(String id) throws DuplicateRealmException {
+ return adapter.newRealm(id);
+ }
+
+ public ClassRealm newRealm(String id, ClassLoader classLoader) throws DuplicateRealmException {
+ return adapter.newRealm(id, classLoader);
+ }
+
+ public void disposeRealm(String id) throws NoSuchRealmException {
+ adapter.disposeRealm(id);
+ }
+
+ public ClassRealm getRealm(String id) throws NoSuchRealmException {
+ return adapter.getRealm(id);
+ }
+
+ public Collection getRealms() {
+ return adapter.getRealms();
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorldAdapter.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorldAdapter.java
new file mode 100644
index 000000000000..61bb50a37aae
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorldAdapter.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.classworlds;
+
+/*
+ * Copyright 2001-2010 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.util.Collection;
+import java.util.Vector;
+
+/**
+ * An adapter for ClassWorlds.
+ *
+ * Note: This is a legacy internal class provided for backward compatibility with Maven 2.
+ * New code should avoid using this adapter.
+ *
+ * @author Andrew Williams
+ * @deprecated This is a legacy internal class.
+ */
+@SuppressWarnings("rawtypes")
+@Deprecated
+public class ClassWorldAdapter extends ClassWorld {
+
+ public static ClassWorldAdapter getInstance(org.codehaus.plexus.classworlds.ClassWorld newWorld) {
+ return new ClassWorldAdapter(newWorld);
+ }
+
+ private final org.codehaus.plexus.classworlds.ClassWorld world;
+
+ private ClassWorldAdapter(org.codehaus.plexus.classworlds.ClassWorld newWorld) {
+ super(false);
+ this.world = newWorld;
+ }
+
+ public ClassRealm newRealm(String id) throws DuplicateRealmException {
+ try {
+ return ClassRealmAdapter.getInstance(world.newRealm(id));
+ } catch (org.codehaus.plexus.classworlds.realm.DuplicateRealmException e) {
+ throw new DuplicateRealmException(this, e.getId());
+ }
+ }
+
+ public ClassRealm newRealm(String id, ClassLoader classLoader) throws DuplicateRealmException {
+ try {
+ return ClassRealmAdapter.getInstance(world.newRealm(id, classLoader));
+ } catch (org.codehaus.plexus.classworlds.realm.DuplicateRealmException e) {
+ throw new DuplicateRealmException(this, e.getId());
+ }
+ }
+
+ public void disposeRealm(String id) throws NoSuchRealmException {
+ try {
+ world.disposeRealm(id);
+ } catch (org.codehaus.plexus.classworlds.realm.NoSuchRealmException e) {
+ throw new NoSuchRealmException(this, e.getId());
+ }
+ }
+
+ public ClassRealm getRealm(String id) throws NoSuchRealmException {
+ try {
+ return ClassRealmAdapter.getInstance(world.getRealm(id));
+ } catch (org.codehaus.plexus.classworlds.realm.NoSuchRealmException e) {
+ throw new NoSuchRealmException(this, e.getId());
+ }
+ }
+
+ public Collection getRealms() {
+ Collection realms = world.getRealms();
+ Vector ret = new Vector<>();
+ for (org.codehaus.plexus.classworlds.realm.ClassRealm classRealm : realms) {
+ ret.add(ClassRealmAdapter.getInstance(classRealm));
+ }
+
+ return ret;
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorldException.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorldException.java
new file mode 100644
index 000000000000..76f51e3e7a4d
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorldException.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.classworlds;
+
+/*
+
+Copyright 2002 (C) The Werken Company. All Rights Reserved.
+
+Redistribution and use of this software and associated documentation
+("Software"), with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Redistributions of source code must retain copyright
+ statements and notices. Redistributions must also contain a
+ copy of this document.
+
+2. Redistributions in binary form must reproduce the
+ above copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+3. The name "classworlds" must not be used to endorse or promote
+ products derived from this Software without prior written
+ permission of The Werken Company. For written permission,
+ please contact bob@werken.com.
+
+4. Products derived from this Software may not be called "classworlds"
+ nor may "classworlds" appear in their names without prior written
+ permission of The Werken Company. "classworlds" is a registered
+ trademark of The Werken Company.
+
+5. Due credit should be given to The Werken Company.
+ (http://classworlds.werken.com/).
+
+THIS SOFTWARE IS PROVIDED BY THE WERKEN COMPANY AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+THE WERKEN COMPANY OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+/**
+ * Base exception for ClassWorld errors.
+ *
+ * Note: This is a legacy exception provided for backward compatibility with Maven 2.
+ * New code should use {@link org.codehaus.plexus.classworlds.ClassWorldException}.
+ *
+ * @author bob mcwhirter
+ * @deprecated Use {@link org.codehaus.plexus.classworlds.ClassWorldException}
+ */
+@Deprecated
+public class ClassWorldException extends Exception {
+ // ------------------------------------------------------------
+ // Instance members
+ // ------------------------------------------------------------
+
+ /**
+ * The world.
+ */
+ private final ClassWorld world;
+
+ // ------------------------------------------------------------
+ // Constructors
+ // ------------------------------------------------------------
+
+ /**
+ * Construct.
+ *
+ * @param world The world.
+ */
+ public ClassWorldException(final ClassWorld world) {
+ this.world = world;
+ }
+
+ /**
+ * Construct.
+ *
+ * @param world The world.
+ * @param msg The detail message.
+ */
+ public ClassWorldException(final ClassWorld world, final String msg) {
+ super(msg);
+ this.world = world;
+ }
+
+ // ------------------------------------------------------------
+ // Instance methods
+ // ------------------------------------------------------------
+
+ /**
+ * Retrieve the world.
+ *
+ * @return The world.
+ */
+ public ClassWorld getWorld() {
+ return this.world;
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorldReverseAdapter.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorldReverseAdapter.java
new file mode 100644
index 000000000000..8a9543a68b9f
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorldReverseAdapter.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.classworlds;
+
+/*
+ * Copyright 2001-2010 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Vector;
+
+/**
+ * A reverse adapter for ClassWorlds.
+ *
+ * Note: This is a legacy internal class provided for backward compatibility with Maven 2.
+ * New code should avoid using this adapter.
+ *
+ * @author Andrew Williams
+ * @deprecated This is a legacy internal class.
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+@Deprecated
+public class ClassWorldReverseAdapter extends org.codehaus.plexus.classworlds.ClassWorld {
+ private static final HashMap instances = new HashMap();
+
+ public static ClassWorldReverseAdapter getInstance(ClassWorld oldWorld) {
+ if (instances.containsKey(oldWorld)) return (ClassWorldReverseAdapter) instances.get(oldWorld);
+
+ ClassWorldReverseAdapter adapter = new ClassWorldReverseAdapter(oldWorld);
+ instances.put(oldWorld, adapter);
+
+ return adapter;
+ }
+
+ private final ClassWorld world;
+
+ private ClassWorldReverseAdapter(ClassWorld newWorld) {
+ super();
+ this.world = newWorld;
+ }
+
+ public org.codehaus.plexus.classworlds.realm.ClassRealm newRealm(String id)
+ throws org.codehaus.plexus.classworlds.realm.DuplicateRealmException {
+ try {
+ return ClassRealmReverseAdapter.getInstance(world.newRealm(id));
+ } catch (DuplicateRealmException e) {
+ throw new org.codehaus.plexus.classworlds.realm.DuplicateRealmException(this, e.getId());
+ }
+ }
+
+ public org.codehaus.plexus.classworlds.realm.ClassRealm newRealm(String id, ClassLoader classLoader)
+ throws org.codehaus.plexus.classworlds.realm.DuplicateRealmException {
+ try {
+ return ClassRealmReverseAdapter.getInstance(world.newRealm(id, classLoader));
+ } catch (DuplicateRealmException e) {
+ throw new org.codehaus.plexus.classworlds.realm.DuplicateRealmException(this, e.getId());
+ }
+ }
+
+ public void disposeRealm(String id) throws org.codehaus.plexus.classworlds.realm.NoSuchRealmException {
+ try {
+ world.disposeRealm(id);
+ } catch (NoSuchRealmException e) {
+ throw new org.codehaus.plexus.classworlds.realm.NoSuchRealmException(this, e.getId());
+ }
+ }
+
+ public org.codehaus.plexus.classworlds.realm.ClassRealm getRealm(String id)
+ throws org.codehaus.plexus.classworlds.realm.NoSuchRealmException {
+ try {
+ return ClassRealmReverseAdapter.getInstance(world.getRealm(id));
+ } catch (NoSuchRealmException e) {
+ throw new org.codehaus.plexus.classworlds.realm.NoSuchRealmException(this, e.getId());
+ }
+ }
+
+ public Collection getRealms() {
+ Collection realms = world.getRealms();
+ Vector ret = new Vector();
+
+ for (Object o : realms) {
+ ClassRealm realm = (ClassRealm) o;
+ ret.add(ClassRealmReverseAdapter.getInstance(realm));
+ }
+
+ return ret;
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ConfigurationException.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ConfigurationException.java
new file mode 100644
index 000000000000..97396ba66831
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ConfigurationException.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.classworlds;
+
+/*
+
+Copyright 2002 (C) The Werken Company. All Rights Reserved.
+
+Redistribution and use of this software and associated documentation
+("Software"), with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Redistributions of source code must retain copyright
+ statements and notices. Redistributions must also contain a
+ copy of this document.
+
+2. Redistributions in binary form must reproduce the
+ above copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+3. The name "classworlds" must not be used to endorse or promote
+ products derived from this Software without prior written
+ permission of The Werken Company. For written permission,
+ please contact bob@werken.com.
+
+4. Products derived from this Software may not be called "classworlds"
+ nor may "classworlds" appear in their names without prior written
+ permission of The Werken Company. "classworlds" is a registered
+ trademark of The Werken Company.
+
+5. Due credit should be given to The Werken Company.
+ (http://classworlds.werken.com/).
+
+THIS SOFTWARE IS PROVIDED BY THE WERKEN COMPANY AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+THE WERKEN COMPANY OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+/**
+ * Indicates an error during Launcher configuration.
+ *
+ * Note: This is a legacy exception provided for backward compatibility with Maven 2.
+ * New code should use {@link org.codehaus.plexus.classworlds.launcher.ConfigurationException}.
+ *
+ * @author bob mcwhirter
+ * @deprecated Use {@link org.codehaus.plexus.classworlds.launcher.ConfigurationException}
+ */
+@Deprecated
+public class ConfigurationException extends Exception {
+ /**
+ * Construct.
+ *
+ * @param msg The message.
+ */
+ public ConfigurationException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Construct.
+ *
+ * @param msg The message.
+ * @param lineNo The number of configuraton line where the problem occured.
+ * @param line The configuration line where the problem occured.
+ */
+ public ConfigurationException(String msg, int lineNo, String line) {
+ super(msg + " (" + lineNo + "): " + line);
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/Configurator.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/Configurator.java
new file mode 100644
index 000000000000..0853c80b5d8c
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/Configurator.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.classworlds;
+
+/*
+ * Copyright 2001-2010 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+
+/**
+ * A compatibility wrapper for {@link org.codehaus.plexus.classworlds.launcher.Configurator}
+ * provided for legacy code.
+ *
+ * Note: This is a legacy class provided for backward compatibility with Maven 2.
+ * New code should use {@link org.codehaus.plexus.classworlds.launcher.Configurator}.
+ *
+ * @author Andrew Williams
+ * @deprecated Use {@link org.codehaus.plexus.classworlds.launcher.Configurator}
+ */
+@Deprecated
+public class Configurator {
+ private final ConfiguratorAdapter config;
+
+ /** Construct.
+ *
+ * @param launcher The launcher to configure.
+ */
+ public Configurator(Launcher launcher) {
+ config = ConfiguratorAdapter.getInstance(
+ new org.codehaus.plexus.classworlds.launcher.Configurator(launcher), launcher);
+ }
+
+ /** Construct.
+ *
+ * @param world The classWorld to configure.
+ */
+ public Configurator(ClassWorld world) {
+ config = ConfiguratorAdapter.getInstance(
+ new org.codehaus.plexus.classworlds.launcher.Configurator(ClassWorldReverseAdapter.getInstance(world)),
+ world);
+ }
+
+ /** set world.
+ * this setter is provided so you can use the same configurator to configure several "worlds"
+ *
+ * @param world The classWorld to configure.
+ */
+ public void setClassWorld(ClassWorld world) {
+ config.setClassWorld(world);
+ }
+
+ /**
+ * Configure from a file.
+ *
+ * @param is The config input stream
+ * @throws IOException If an error occurs reading the config file.
+ * @throws MalformedURLException If the config file contains invalid URLs.
+ * @throws ConfigurationException If the config file is corrupt.
+ * @throws DuplicateRealmException If the config file defines two realms with the same id.
+ * @throws NoSuchRealmException If the config file defines a main entry point in
+ * a non-existent realm.
+ */
+ public void configure(InputStream is)
+ throws IOException, MalformedURLException, ConfigurationException, DuplicateRealmException,
+ NoSuchRealmException {
+ config.configureAdapter(is);
+ }
+
+ /**
+ * Associate parent realms with their children.
+ */
+ protected void associateRealms() {
+ config.associateRealms();
+ }
+
+ /**
+ * Load a glob into the specified classloader.
+ *
+ * @param line The path configuration line.
+ * @param realm The realm to populate
+ * @throws MalformedURLException If the line does not represent
+ * a valid path element.
+ * @throws FileNotFoundException If the line does not represent
+ * a valid path element in the filesystem.
+ */
+ protected void loadGlob(String line, ClassRealm realm) throws MalformedURLException, FileNotFoundException {
+ loadGlob(line, realm, false);
+ }
+
+ /**
+ * Load a glob into the specified classloader.
+ *
+ * @param line The path configuration line.
+ * @param realm The realm to populate
+ * @param optionally Whether the path is optional or required
+ * @throws MalformedURLException If the line does not represent
+ * a valid path element.
+ * @throws FileNotFoundException If the line does not represent
+ * a valid path element in the filesystem.
+ */
+ @SuppressWarnings("RedundantThrows")
+ protected void loadGlob(String line, ClassRealm realm, boolean optionally)
+ throws MalformedURLException, FileNotFoundException {
+ config.loadGlob(line, realm, optionally);
+ }
+
+ /**
+ * Filter a string for system properties.
+ *
+ * @param text The text to filter.
+ * @return The filtered text.
+ * @throws ConfigurationException If the property does not
+ * exist or if there is a syntax error.
+ */
+ @SuppressWarnings("RedundantThrows")
+ protected String filter(String text) throws ConfigurationException {
+ return config.filter(text);
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ConfiguratorAdapter.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ConfiguratorAdapter.java
new file mode 100644
index 000000000000..8cd912c801fe
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ConfiguratorAdapter.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.classworlds;
+
+/*
+ * Copyright 2001-2010 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+
+/**
+ * An adapter for Configurators.
+ *
+ * Note: This is a legacy internal class provided for backward compatibility with Maven 2.
+ * New code should avoid using this adapter.
+ *
+ * @deprecated This is a legacy internal class.
+ */
+@Deprecated
+public class ConfiguratorAdapter extends Configurator {
+
+ public static ConfiguratorAdapter getInstance(
+ org.codehaus.plexus.classworlds.launcher.Configurator newConfig, Launcher launcher) {
+ return new ConfiguratorAdapter(newConfig, launcher);
+ }
+
+ public static ConfiguratorAdapter getInstance(
+ org.codehaus.plexus.classworlds.launcher.Configurator newConfig, ClassWorld world) {
+ return new ConfiguratorAdapter(newConfig, world);
+ }
+
+ private final org.codehaus.plexus.classworlds.launcher.Configurator config;
+
+ private ConfiguratorAdapter(org.codehaus.plexus.classworlds.launcher.Configurator config, Launcher launcher) {
+ super(launcher);
+ this.config = config;
+ }
+
+ private ConfiguratorAdapter(org.codehaus.plexus.classworlds.launcher.Configurator config, ClassWorld world) {
+ super(world);
+ this.config = config;
+ }
+
+ public void associateRealms() {
+ config.associateRealms();
+ }
+
+ @SuppressWarnings("DuplicateThrows")
+ public void configureAdapter(InputStream is)
+ throws IOException, MalformedURLException, ConfigurationException, DuplicateRealmException,
+ NoSuchRealmException {
+ try {
+ config.configure(is);
+ } catch (org.codehaus.plexus.classworlds.launcher.ConfigurationException e) {
+ throw new ConfigurationException(e.getMessage());
+ } catch (org.codehaus.plexus.classworlds.realm.DuplicateRealmException e) {
+ throw new DuplicateRealmException(ClassWorldAdapter.getInstance(e.getWorld()), e.getId());
+ } catch (org.codehaus.plexus.classworlds.realm.NoSuchRealmException e) {
+ throw new NoSuchRealmException(ClassWorldAdapter.getInstance(e.getWorld()), e.getId());
+ }
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/DefaultClassRealm.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/DefaultClassRealm.java
new file mode 100644
index 000000000000..1d13a4dee06f
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/DefaultClassRealm.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.classworlds;
+
+/*
+ * Copyright 2001-2010 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+/**
+ * A compatibility wrapper for {@link org.codehaus.plexus.classworlds.realm.ClassRealm}
+ * provided for legacy code.
+ *
+ * Note: This is a legacy class provided for backward compatibility with Maven 2.
+ * New code should use {@link org.codehaus.plexus.classworlds.realm.ClassRealm}.
+ *
+ * @author Andrew Williams
+ * @deprecated Use {@link org.codehaus.plexus.classworlds.realm.ClassRealm}
+ */
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+
+@SuppressWarnings("rawtypes")
+@Deprecated
+public class DefaultClassRealm implements ClassRealm {
+ private final ClassRealmAdapter adapter;
+
+ public DefaultClassRealm(ClassWorld world, String id) {
+ this(world, id, null);
+ }
+
+ public DefaultClassRealm(ClassWorld world, String id, ClassLoader foreignClassLoader) {
+ this.adapter = ClassRealmAdapter.getInstance(new org.codehaus.plexus.classworlds.realm.ClassRealm(
+ ClassWorldReverseAdapter.getInstance(world), id, foreignClassLoader));
+ }
+
+ public URL[] getConstituents() {
+ return adapter.getConstituents();
+ }
+
+ public ClassRealm getParent() {
+ return adapter.getParentRealm();
+ }
+
+ public void setParent(ClassRealm parent) {
+ adapter.setParent(parent);
+ }
+
+ public String getId() {
+ return adapter.getId();
+ }
+
+ public ClassWorld getWorld() {
+ return adapter.getWorld();
+ }
+
+ public void importFrom(String realmId, String packageName) throws NoSuchRealmException {
+ adapter.importFrom(realmId, packageName);
+ }
+
+ public void addConstituent(URL constituent) {
+ adapter.addConstituent(constituent);
+ }
+
+ /**
+ * Adds a byte[] class definition as a constituent for locating classes.
+ * Currently uses BytesURLStreamHandler to hold a reference of the byte[] in memory.
+ * This ensures we have a unifed URL resource model for all constituents.
+ * The code to cache to disk is commented out - maybe a property to choose which method?
+ *
+ * @param constituent class name
+ * @param b the class definition as a byte[]
+ * @throws ClassNotFoundException when class couldn't be loaded
+ */
+ public void addConstituent(String constituent, byte[] b) throws ClassNotFoundException {
+ try {
+ File path, file;
+ if (constituent.lastIndexOf('.') != -1) {
+ path = new File("byteclass/"
+ + constituent
+ .substring(0, constituent.lastIndexOf('.') + 1)
+ .replace('.', File.separatorChar));
+
+ file = new File(path, constituent.substring(constituent.lastIndexOf('.') + 1) + ".class");
+ } else {
+ path = new File("byteclass/");
+
+ file = new File(path, constituent + ".class");
+ }
+
+ addConstituent(new URL(null, file.toURI().toURL().toExternalForm(), new BytesURLStreamHandler(b)));
+ } catch (java.io.IOException e) {
+ throw new ClassNotFoundException("Couldn't load byte stream.", e);
+ }
+ }
+
+ public ClassRealm locateSourceRealm(String classname) {
+ return adapter.locateSourceRealm(classname);
+ }
+
+ public ClassLoader getClassLoader() {
+ return adapter.getClassLoader();
+ }
+
+ public ClassRealm createChildRealm(String id) throws DuplicateRealmException {
+ return adapter.createChildRealm(id);
+ }
+
+ // ----------------------------------------------------------------------
+ // ClassLoader API
+ // ----------------------------------------------------------------------
+
+ public Class loadClass(String name) throws ClassNotFoundException {
+ return adapter.loadClass(name);
+ }
+
+ public URL getResource(String name) {
+ return adapter.getResource(name);
+ }
+
+ public InputStream getResourceAsStream(String name) {
+ return adapter.getResourceAsStream(name);
+ }
+
+ public Enumeration findResources(String name) throws IOException {
+ return adapter.findResources(name);
+ }
+
+ public void display() {
+ adapter.display();
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/DuplicateRealmException.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/DuplicateRealmException.java
new file mode 100644
index 000000000000..c139554310a3
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/DuplicateRealmException.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.classworlds;
+
+/*
+
+Copyright 2002 (C) The Werken Company. All Rights Reserved.
+
+Redistribution and use of this software and associated documentation
+("Software"), with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Redistributions of source code must retain copyright
+ statements and notices. Redistributions must also contain a
+ copy of this document.
+
+2. Redistributions in binary form must reproduce the
+ above copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+3. The name "classworlds" must not be used to endorse or promote
+ products derived from this Software without prior written
+ permission of The Werken Company. For written permission,
+ please contact bob@werken.com.
+
+4. Products derived from this Software may not be called "classworlds"
+ nor may "classworlds" appear in their names without prior written
+ permission of The Werken Company. "classworlds" is a registered
+ trademark of The Werken Company.
+
+5. Due credit should be given to The Werken Company.
+ (http://classworlds.werken.com/).
+
+THIS SOFTWARE IS PROVIDED BY THE WERKEN COMPANY AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+THE WERKEN COMPANY OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+/**
+ * Indicates an attempt to add a ClassRealm to a
+ * ClassWorld with a duplicate id.
+ *
+ * Note: This is a legacy exception provided for backward compatibility with Maven 2.
+ * New code should use {@link org.codehaus.plexus.classworlds.realm.DuplicateRealmException}.
+ *
+ * @author bob mcwhirter
+ * @deprecated Use {@link org.codehaus.plexus.classworlds.realm.DuplicateRealmException}
+ */
+@Deprecated
+public class DuplicateRealmException extends ClassWorldException {
+ // ------------------------------------------------------------
+ // Instance members
+ // ------------------------------------------------------------
+
+ /**
+ * The realm id.
+ */
+ private final String id;
+
+ // ------------------------------------------------------------
+ // Constructors
+ // ------------------------------------------------------------
+
+ /**
+ * Construct.
+ *
+ * @param world The world.
+ * @param id The realm id.
+ */
+ public DuplicateRealmException(ClassWorld world, String id) {
+ super(world, id);
+ this.id = id;
+ }
+
+ // ------------------------------------------------------------
+ // Instance methods
+ // ------------------------------------------------------------
+
+ /**
+ * Retrieve the duplicate realm id.
+ *
+ * @return The id.
+ */
+ public String getId() {
+ return this.id;
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/Launcher.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/Launcher.java
new file mode 100644
index 000000000000..d181dbbc9096
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/Launcher.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.classworlds;
+
+/*
+ * Copyright 2001-2010 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+/**
+ * A compatibility wrapper for {@link org.codehaus.plexus.classworlds.launcher.Launcher}
+ * provided for legacy code.
+ *
+ * Note: This is a legacy class provided for backward compatibility with Maven 2.
+ * New code should use {@link org.codehaus.plexus.classworlds.launcher.Launcher}.
+ *
+ * @author Andrew Williams
+ * @deprecated Use {@link org.codehaus.plexus.classworlds.launcher.Launcher}
+ */
+@Deprecated
+public class Launcher extends org.codehaus.plexus.classworlds.launcher.Launcher {
+ public Launcher() {}
+
+ // ------------------------------------------------------------
+ // Class methods
+ // ------------------------------------------------------------
+
+ /**
+ * Launch the launcher from the command line.
+ * Will exit using System.exit with an exit code of 0 for success, 100 if there was an unknown exception,
+ * or some other code for an application error.
+ *
+ * @param args The application command-line arguments.
+ */
+ public static void main(String[] args) {
+ org.codehaus.plexus.classworlds.launcher.Launcher.main(args);
+ }
+
+ /**
+ * Launch the launcher.
+ *
+ * @param args The application command-line arguments.
+ * @return an integer exit code
+ * @throws Exception If an error occurs.
+ */
+ public static int mainWithExitCode(String[] args) throws Exception {
+ return org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(args);
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/NoSuchRealmException.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/NoSuchRealmException.java
new file mode 100644
index 000000000000..065cd9cec458
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/NoSuchRealmException.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.classworlds;
+
+/*
+
+Copyright 2002 (C) The Werken Company. All Rights Reserved.
+
+Redistribution and use of this software and associated documentation
+("Software"), with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Redistributions of source code must retain copyright
+ statements and notices. Redistributions must also contain a
+ copy of this document.
+
+2. Redistributions in binary form must reproduce the
+ above copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+3. The name "classworlds" must not be used to endorse or promote
+ products derived from this Software without prior written
+ permission of The Werken Company. For written permission,
+ please contact bob@werken.com.
+
+4. Products derived from this Software may not be called "classworlds"
+ nor may "classworlds" appear in their names without prior written
+ permission of The Werken Company. "classworlds" is a registered
+ trademark of The Werken Company.
+
+5. Due credit should be given to The Werken Company.
+ (http://classworlds.werken.com/).
+
+THIS SOFTWARE IS PROVIDED BY THE WERKEN COMPANY AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+THE WERKEN COMPANY OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+/**
+ * Indicates an attempt to retrieve a ClassRealm from a
+ * ClassWorld with an invalid id.
+ *
+ * Note: This is a legacy exception provided for backward compatibility with Maven 2.
+ * New code should use {@link org.codehaus.plexus.classworlds.realm.NoSuchRealmException}.
+ *
+ * @author bob mcwhirter
+ * @deprecated Use {@link org.codehaus.plexus.classworlds.realm.NoSuchRealmException}
+ */
+@Deprecated
+public class NoSuchRealmException extends ClassWorldException {
+ // ------------------------------------------------------------
+ // Instance members
+ // ------------------------------------------------------------
+
+ /**
+ * The realm id.
+ */
+ private final String id;
+
+ // ------------------------------------------------------------
+ // Constructors
+ // ------------------------------------------------------------
+
+ /**
+ * Construct.
+ *
+ * @param world The world.
+ * @param id The realm id.
+ */
+ public NoSuchRealmException(ClassWorld world, String id) {
+ super(world, id);
+ this.id = id;
+ }
+
+ // ------------------------------------------------------------
+ // Instance methods
+ // ------------------------------------------------------------
+
+ /**
+ * Retrieve the invalid realm id.
+ *
+ * @return The id.
+ */
+ public String getId() {
+ return this.id;
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/package-info.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/package-info.java
new file mode 100644
index 000000000000..e94806c4c752
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/package-info.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/**
+ * Legacy Classworlds 1.x API, preserved for binary compatibility with Eclipse Sisu.
+ *
+ * This package has been deprecated since version 2.5.2 (2014). New code should use
+ * {@code org.codehaus.plexus.classworlds} instead.
+ *
+ * Do not remove this package. The compiled bytecode of
+ * {@code org.eclipse.sisu:org.eclipse.sisu.plexus} references {@link ClassRealm},
+ * {@link ClassRealmAdapter}, and {@link ClassRealmReverseAdapter} from this package.
+ * Removing them causes {@code ClassNotFoundException} at runtime for any application
+ * using Sisu, including all Maven 3+ builds.
+ *
+ * PR #141 removed this package and had to be reverted immediately (commit 223416e).
+ * See {@code COMPATIBILITY.md} for details on what Sisu references and what preconditions
+ * must be satisfied before any future removal.
+ *
+ * @deprecated Use {@code org.codehaus.plexus.classworlds} for new code.
+ */
+package org.codehaus.classworlds;
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorld.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorld.java
new file mode 100644
index 000000000000..27ebb12cb2bf
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorld.java
@@ -0,0 +1,303 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Predicate;
+
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
+import org.codehaus.plexus.classworlds.realm.FilteredClassRealm;
+import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
+
+/**
+ * A collection of ClassRealms, indexed by id.
+ *
+ * @author bob mcwhirter
+ */
+public class ClassWorld implements org.apache.maven.api.classworlds.ClassWorld, Closeable {
+ private Map realms;
+
+ private final List listeners = new ArrayList<>();
+
+ private ModuleLayer moduleLayer;
+
+ private ModuleLayer.Controller moduleLayerController;
+
+ public ClassWorld(String realmId, ClassLoader classLoader) {
+ this();
+
+ try {
+ newRealm(realmId, classLoader);
+ } catch (DuplicateRealmException e) {
+ // Will never happen as we are just creating the world.
+ }
+ }
+
+ public ClassWorld() {
+ this.realms = new LinkedHashMap<>();
+ }
+
+ public ClassRealm newRealm(String id) throws DuplicateRealmException {
+ return newRealm(id, getClass().getClassLoader());
+ }
+
+ public ClassRealm newRealm(String id, ClassLoader classLoader) throws DuplicateRealmException {
+ return newRealm(id, classLoader, null);
+ }
+
+ /**
+ * Adds a class realm with filtering.
+ * Only resources/classes whose name matches a given predicate are exposed.
+ * @param id The identifier for this realm, must not be null.
+ * @param classLoader The base class loader for this realm, may be null to use the bootstrap class
+ * loader.
+ * @param filter a predicate to apply to each resource name to determine if it should be loaded through this class loader
+ * @return the created class realm
+ * @throws DuplicateRealmException in case a realm with the given id does already exist
+ * @since 2.7.0
+ * @see FilteredClassRealm
+ */
+ public synchronized ClassRealm newRealm(String id, ClassLoader classLoader, Predicate filter)
+ throws DuplicateRealmException {
+ if (realms.containsKey(id)) {
+ throw new DuplicateRealmException(this, id);
+ }
+
+ ClassRealm realm;
+
+ if (filter == null) {
+ realm = new ClassRealm(this, id, classLoader);
+ } else {
+ realm = new FilteredClassRealm(filter, this, id, classLoader);
+ }
+ realms.put(id, realm);
+
+ for (ClassWorldListener listener : listeners) {
+ listener.realmCreated(realm);
+ }
+
+ return realm;
+ }
+
+ /**
+ * Closes all contained class realms.
+ * @since 2.7.0
+ */
+ @Override
+ public synchronized void close() throws IOException {
+ realms.values().stream().forEach(this::disposeRealm);
+ realms.clear();
+ }
+
+ public synchronized void disposeRealm(String id) throws NoSuchRealmException {
+ ClassRealm realm = realms.remove(id);
+
+ if (realm != null) {
+ disposeRealm(realm);
+ } else {
+ throw new NoSuchRealmException(this, id);
+ }
+ }
+
+ private void disposeRealm(ClassRealm realm) {
+ try {
+ realm.close();
+ } catch (IOException ignore) {
+ }
+ for (ClassWorldListener listener : listeners) {
+ listener.realmDisposed(realm);
+ }
+ }
+
+ public synchronized ClassRealm getRealm(String id) throws NoSuchRealmException {
+ if (realms.containsKey(id)) {
+ return realms.get(id);
+ }
+
+ throw new NoSuchRealmException(this, id);
+ }
+
+ public synchronized Collection getRealms() {
+ return Collections.unmodifiableList(new ArrayList<>(realms.values()));
+ }
+
+ public synchronized void setModuleLayer(ModuleLayer moduleLayer, ModuleLayer.Controller controller) {
+ this.moduleLayer = moduleLayer;
+ this.moduleLayerController = controller;
+ }
+
+ public ModuleLayer getModuleLayer() {
+ return moduleLayer;
+ }
+
+ public ModuleLayer.Controller getModuleLayerController() {
+ return moduleLayerController;
+ }
+
+ /**
+ * Exports a package from a named module to the given target module.
+ * Looks up the source module in the runtime layer first, then the boot layer.
+ * Only runtime layer modules can be modified via the Controller; boot layer
+ * modules require {@code --add-exports} in the launcher script.
+ *
+ * @param moduleName the source module name
+ * @param packageName the package to export
+ * @param target the target module to export to
+ */
+ public synchronized void addExports(String moduleName, String packageName, Module target) {
+ applyModuleAccess(moduleName, packageName, target, false);
+ }
+
+ /**
+ * Opens a package from a named module to the given target module for deep reflection.
+ * Looks up the source module in the runtime layer first, then the boot layer.
+ * Only runtime layer modules can be modified via the Controller; boot layer
+ * modules require {@code --add-opens} in the launcher script.
+ *
+ * @param moduleName the source module name
+ * @param packageName the package to open
+ * @param target the target module to open to
+ */
+ public synchronized void addOpens(String moduleName, String packageName, Module target) {
+ applyModuleAccess(moduleName, packageName, target, true);
+ }
+
+ /**
+ * Adds a reads edge from the named source module to the given target module.
+ * Only runtime layer modules can be modified via the Controller.
+ *
+ * @param sourceModuleName the source module name
+ * @param target the target module to read
+ */
+ public synchronized void addReads(String sourceModuleName, Module target) {
+ Module source = findModule(sourceModuleName);
+ if (source == null || moduleLayerController == null || isBootLayerModule(source)) {
+ return;
+ }
+ moduleLayerController.addReads(source, target);
+ }
+
+ private void applyModuleAccess(String moduleName, String packageName, Module target, boolean open) {
+ Module source = findModule(moduleName);
+ if (source == null || moduleLayerController == null || isBootLayerModule(source)) {
+ return;
+ }
+ if (open) {
+ moduleLayerController.addOpens(source, packageName, target);
+ } else {
+ moduleLayerController.addExports(source, packageName, target);
+ }
+ }
+
+ private Module findModule(String moduleName) {
+ if (moduleLayer != null) {
+ Module m = moduleLayer.findModule(moduleName).orElse(null);
+ if (m != null) {
+ return m;
+ }
+ }
+ return ModuleLayer.boot().findModule(moduleName).orElse(null);
+ }
+
+ private boolean isBootLayerModule(Module module) {
+ return module.getLayer() == ModuleLayer.boot();
+ }
+
+ // from exports branch
+ public synchronized ClassRealm getClassRealm(String id) {
+ if (realms.containsKey(id)) {
+ return realms.get(id);
+ }
+
+ return null;
+ }
+
+ public synchronized void addListener(ClassWorldListener listener) {
+ // TODO ideally, use object identity, not equals
+ if (!listeners.contains(listener)) {
+ listeners.add(listener);
+ }
+ }
+
+ public synchronized void removeListener(ClassWorldListener listener) {
+ listeners.remove(listener);
+ }
+
+ // API interface methods - newRealm with filter is already implemented above
+
+ @Override
+ public void addListener(org.apache.maven.api.classworlds.ClassWorldListener listener) {
+ if (listener instanceof ClassWorldListener) {
+ addListener((ClassWorldListener) listener);
+ } else {
+ // Wrap the API listener
+ addListener(new ClassWorldListener() {
+ @Override
+ public void realmCreated(ClassRealm realm) {
+ listener.realmCreated(realm);
+ }
+
+ @Override
+ public void realmDisposed(ClassRealm realm) {
+ listener.realmDisposed(realm);
+ }
+
+ @Override
+ public void realmCreated(org.apache.maven.api.classworlds.ClassRealm realm) {
+ listener.realmCreated(realm);
+ }
+
+ @Override
+ public void realmDisposed(org.apache.maven.api.classworlds.ClassRealm realm) {
+ listener.realmDisposed(realm);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void removeListener(org.apache.maven.api.classworlds.ClassWorldListener listener) {
+ // For now, we'll need to track wrapped listeners if this becomes important
+ // This is a limitation of the current design
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorldException.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorldException.java
new file mode 100644
index 000000000000..8a1501652ed1
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorldException.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+/**
+ * Base exception for ClassWorld errors.
+ *
+ * @author bob mcwhirter
+ */
+public class ClassWorldException extends org.apache.maven.api.classworlds.ClassWorldException {
+ // ------------------------------------------------------------
+ // Instance members
+ // ------------------------------------------------------------
+
+ /**
+ * The world.
+ */
+ private ClassWorld world;
+
+ // ------------------------------------------------------------
+ // Constructors
+ // ------------------------------------------------------------
+
+ /**
+ * Construct.
+ *
+ * @param world The world.
+ */
+ public ClassWorldException(final ClassWorld world) {
+ super(world);
+ this.world = world;
+ }
+
+ /**
+ * Construct.
+ *
+ * @param world The world.
+ * @param msg The detail message.
+ */
+ public ClassWorldException(final ClassWorld world, final String msg) {
+ super(world, msg);
+ this.world = world;
+ }
+
+ // ------------------------------------------------------------
+ // Instance methods
+ // ------------------------------------------------------------
+
+ /**
+ * Retrieve the world.
+ *
+ * @return The world.
+ */
+ public ClassWorld getWorld() {
+ return this.world;
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorldListener.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorldListener.java
new file mode 100644
index 000000000000..7e27e279d53b
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorldListener.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+
+public interface ClassWorldListener extends org.apache.maven.api.classworlds.ClassWorldListener {
+ void realmCreated(ClassRealm realm);
+
+ void realmDisposed(ClassRealm realm);
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/UrlUtils.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/UrlUtils.java
new file mode 100644
index 000000000000..00d0e2c2a9e2
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/UrlUtils.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Jason van Zyl
+ */
+public class UrlUtils {
+ public static String normalizeUrlPath(String name) {
+ if (name.startsWith("/")) {
+ name = name.substring(1);
+ }
+
+ // Looking for org/codehaus/werkflow/personality/basic/../common/core-idioms.xml
+ // | i |
+ // +-------+ remove
+ //
+ int i = name.indexOf("/..");
+
+ // Can't be at the beginning because we have no root to refer to so
+ // we start at 1.
+ if (i > 0) {
+ int j = name.lastIndexOf("/", i - 1);
+
+ if (j >= 0) {
+ name = name.substring(0, j) + name.substring(i + 3);
+ }
+ }
+
+ return name;
+ }
+
+ public static Set getURLs(URLClassLoader loader) {
+ return new HashSet<>(Arrays.asList(loader.getURLs()));
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationException.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationException.java
new file mode 100644
index 000000000000..8862146b0d5c
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationException.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.launcher;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+/**
+ * Indicates an error during Launcher configuration.
+ *
+ * @author bob mcwhirter
+ */
+public class ConfigurationException extends Exception {
+ /**
+ * Construct.
+ *
+ * @param msg The message.
+ */
+ public ConfigurationException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Construct.
+ *
+ * @param msg The message.
+ * @param lineNo The number of configuraton line where the problem occured.
+ * @param line The configuration line where the problem occured.
+ */
+ public ConfigurationException(String msg, int lineNo, String line) {
+ super(msg + " (" + lineNo + "): " + line);
+ }
+
+ protected ConfigurationException(Exception cause) {
+ super(cause);
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationHandler.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationHandler.java
new file mode 100644
index 000000000000..628055b52536
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationHandler.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.launcher;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.net.URL;
+
+import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
+import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
+
+/**
+ * Receive notification of the logical content of launcher configuration, independently from parsing.
+ *
+ * @author Igor Fedorenko
+ */
+public interface ConfigurationHandler {
+
+ /**
+ * Define the main class name
+ * @param mainClassName the main class name
+ * @param mainRealmName the main realm from which the main class is loaded
+ */
+ void setAppMain(String mainClassName, String mainRealmName);
+
+ /**
+ * Define a new realm
+ * @param realmName the new realm name
+ * @throws DuplicateRealmException when realm with name already exists
+ */
+ void addRealm(String realmName) throws DuplicateRealmException;
+
+ /**
+ * Add an import specification from a realm
+ * @param realmName the realm name
+ * @param importSpec the import specification
+ * @throws NoSuchRealmException if realm doesn't exist
+ */
+ void addImportFrom(String realmName, String importSpec) throws NoSuchRealmException;
+
+ /**
+ * Add a file to the realm
+ * @param file the file to load content from
+ */
+ void addLoadFile(File file);
+
+ /**
+ * Add an URL to the realm
+ * @param url the url to load content from
+ */
+ void addLoadURL(URL url);
+
+ /**
+ * Add a file to the module path. Jars added via this method will be loaded
+ * as JPMS named modules in a {@link ModuleLayer}, rather than on the classpath.
+ * @param file the jar file to add to the module path
+ */
+ default void addModuleFile(File file) {}
+
+ /**
+ * Export a package from a module to a target module.
+ * @param module the source module name
+ * @param pkg the package to export
+ * @param target the target module name, or {@code ALL-UNNAMED} for the unnamed module
+ */
+ default void addExports(String module, String pkg, String target) {}
+
+ /**
+ * Open a package from a module to a target module for deep reflection.
+ * @param module the source module name
+ * @param pkg the package to open
+ * @param target the target module name, or {@code ALL-UNNAMED} for the unnamed module
+ */
+ default void addOpens(String module, String pkg, String target) {}
+
+ /**
+ * Add a reads edge from a source module to a target module.
+ * @param source the source module name
+ * @param target the target module name
+ */
+ default void addReads(String source, String target) {}
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParser.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParser.java
new file mode 100644
index 000000000000..8a1c7a56fa2c
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParser.java
@@ -0,0 +1,550 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.launcher;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Properties;
+
+import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
+import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
+
+/**
+ * Event based launcher configuration parser, delegating effective configuration handling to ConfigurationHandler.
+ *
+ * @author bob mcwhirter
+ * @author Jason van Zyl
+ * @author Igor Fedorenko
+ * @see ConfigurationHandler
+ */
+public class ConfigurationParser {
+ public static final String MAIN_PREFIX = "main is";
+
+ public static final String SET_PREFIX = "set";
+
+ public static final String IMPORT_PREFIX = "import";
+
+ public static final String LOAD_PREFIX = "load";
+
+ public static final String MODULE_PREFIX = "module";
+
+ public static final String ADD_EXPORTS_PREFIX = "add-exports";
+
+ public static final String ADD_OPENS_PREFIX = "add-opens";
+
+ public static final String ADD_READS_PREFIX = "add-reads";
+
+ /**
+ * Optionally spec prefix.
+ */
+ public static final String OPTIONALLY_PREFIX = "optionally";
+
+ protected static final String FROM_SEPARATOR = " from ";
+
+ protected static final String USING_SEPARATOR = " using ";
+
+ protected static final String DEFAULT_SEPARATOR = " default ";
+
+ private final ConfigurationHandler handler;
+
+ private final Properties systemProperties;
+
+ public ConfigurationParser(ConfigurationHandler handler, Properties systemProperties) {
+ this.handler = handler;
+ this.systemProperties = systemProperties;
+ }
+
+ /**
+ * Parse launcher configuration file and send events to the handler.
+ *
+ * @param is the inputstream
+ * @throws IOException when IOException occurs
+ * @throws ConfigurationException when ConfigurationException occurs
+ * @throws DuplicateRealmException when realm already exists
+ * @throws NoSuchRealmException when realm doesn't exist
+ */
+ public void parse(InputStream is)
+ throws IOException, ConfigurationException, DuplicateRealmException, NoSuchRealmException {
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
+
+ String line;
+ int lineNo = 0;
+ boolean mainSet = false;
+ String curRealm = null;
+
+ while (true) {
+ line = reader.readLine();
+
+ if (line == null) {
+ break;
+ }
+
+ ++lineNo;
+ line = line.trim();
+
+ if (canIgnore(line)) {
+ continue;
+ }
+
+ char lineFirstChar = line.charAt(0);
+ switch (lineFirstChar) {
+ case 'm':
+ if (line.startsWith(MODULE_PREFIX)) {
+ handleModuleConfiguration(line, lineNo);
+ } else {
+ mainSet = handleMainConfiguration(line, lineNo, mainSet);
+ }
+ break;
+ case 's':
+ if (handleSetConfiguration(line, lineNo)) {
+ continue;
+ }
+ break;
+ case '[':
+ curRealm = handleRealmConfiguration(line, lineNo);
+ break;
+ case 'i':
+ handleImportConfiguration(line, lineNo, curRealm);
+ break;
+ case 'l':
+ handleLoadConfiguration(line, lineNo);
+ break;
+ case 'a':
+ handleAddDirective(line, lineNo);
+ break;
+ case 'o':
+ handleOptionallyConfiguration(line, lineNo);
+ break;
+ default:
+ throw new ConfigurationException("Unhandled configuration", lineNo, line);
+ }
+ }
+ }
+ }
+
+ /**
+ * Load a glob into the specified classloader.
+ *
+ * @param line The path configuration line.
+ * @param optionally Whether the path is optional or required
+ * @throws MalformedURLException If the line does not represent
+ * a valid path element.
+ * @throws FileNotFoundException If the line does not represent
+ * a valid path element in the filesystem.
+ * @throws ConfigurationException will never occur (thrown for backwards compatibility)
+ */
+ protected void loadGlob(String line, boolean optionally)
+ throws MalformedURLException, FileNotFoundException, ConfigurationException {
+ File globFile = new File(line);
+
+ File dir = globFile.getParentFile();
+ if (!dir.exists()) {
+ if (optionally) {
+ return;
+ } else {
+ throw new FileNotFoundException(dir.toString());
+ }
+ }
+
+ String localName = globFile.getName();
+
+ int starLoc = localName.indexOf("*");
+
+ final String prefix = localName.substring(0, starLoc);
+
+ final String suffix = localName.substring(starLoc + 1);
+
+ File[] matches = dir.listFiles((dir1, name) -> {
+ if (!name.startsWith(prefix)) {
+ return false;
+ }
+
+ if (!name.endsWith(suffix)) {
+ return false;
+ }
+
+ return true;
+ });
+
+ for (File match : matches) {
+ handler.addLoadFile(match);
+ }
+ }
+
+ /**
+ * Filter a string for system properties.
+ *
+ * @param text The text to filter.
+ * @return The filtered text.
+ * @throws ConfigurationException If the property does not
+ * exist or if there is a syntax error.
+ */
+ protected String filter(String text) throws ConfigurationException {
+ StringBuilder result = new StringBuilder();
+
+ int cur = 0;
+ int textLen = text.length();
+
+ int propStart;
+ int propStop;
+
+ String propName;
+ String propValue;
+
+ while (cur < textLen) {
+ propStart = text.indexOf("${", cur);
+
+ if (propStart < 0) {
+ break;
+ }
+
+ result.append(text, cur, propStart);
+
+ propStop = text.indexOf("}", propStart);
+
+ if (propStop < 0) {
+ throw new ConfigurationException("Unterminated property: " + text.substring(propStart));
+ }
+
+ propName = text.substring(propStart + 2, propStop);
+
+ propValue = systemProperties.getProperty(propName);
+
+ /* do our best if we are not running from surefire */
+ if (propName.equals("basedir") && (propValue == null || propValue.equals(""))) {
+ propValue = (new File("")).getAbsolutePath();
+ }
+
+ if (propValue == null) {
+ throw new ConfigurationException("No such property: " + propName);
+ }
+ result.append(propValue);
+
+ cur = propStop + 1;
+ }
+
+ result.append(text.substring(cur));
+
+ return result.toString();
+ }
+
+ /**
+ * Determine if a line can be ignored because it is
+ * a comment or simply blank.
+ *
+ * @param line The line to test.
+ * @return true if the line is ignorable,
+ * otherwise false.
+ */
+ private boolean canIgnore(String line) {
+ return (line.isEmpty() || line.startsWith("#"));
+ }
+
+ private boolean handleMainConfiguration(String line, int lineNo, boolean mainSet) throws ConfigurationException {
+ if (line.startsWith(MAIN_PREFIX)) {
+ if (mainSet) {
+ throw new ConfigurationException("Duplicate main configuration", lineNo, line);
+ }
+
+ int fromLoc = line.indexOf(FROM_SEPARATOR, MAIN_PREFIX.length());
+
+ if (fromLoc < 0) {
+ throw new ConfigurationException("Missing from clause", lineNo, line);
+ }
+
+ String mainClassName =
+ filter(line.substring(MAIN_PREFIX.length(), fromLoc).trim());
+
+ String mainRealmName =
+ filter(line.substring(fromLoc + FROM_SEPARATOR.length()).trim());
+
+ this.handler.setAppMain(mainClassName, mainRealmName);
+
+ return true;
+ }
+ throw new ConfigurationException("Unhandled configuration", lineNo, line);
+ }
+
+ private boolean handleSetConfiguration(String line, int lineNo) throws ConfigurationException {
+ if (line.startsWith(SET_PREFIX)) {
+ String conf = line.substring(SET_PREFIX.length()).trim();
+
+ int usingLoc = conf.indexOf(USING_SEPARATOR);
+
+ String property = null;
+ String propertiesFileName = null;
+
+ if (usingLoc >= 0) {
+ property = conf.substring(0, usingLoc).trim();
+ propertiesFileName = filter(
+ conf.substring(usingLoc + USING_SEPARATOR.length()).trim());
+ conf = propertiesFileName;
+ }
+
+ String defaultValue = null;
+ int defaultLoc = conf.indexOf(DEFAULT_SEPARATOR);
+
+ if (defaultLoc >= 0) {
+ defaultValue = filter(
+ conf.substring(defaultLoc + DEFAULT_SEPARATOR.length()).trim());
+
+ if (property == null) {
+ property = conf.substring(0, defaultLoc).trim();
+ } else {
+ propertiesFileName = conf.substring(0, defaultLoc).trim();
+ }
+ }
+
+ String value = systemProperties.getProperty(property);
+
+ if (value != null) {
+ return true;
+ }
+
+ if (propertiesFileName != null) {
+ File propertiesFile = new File(propertiesFileName);
+
+ if (propertiesFile.exists()) {
+ Properties properties = new Properties();
+
+ try (InputStream inputStream = Files.newInputStream(Paths.get(propertiesFileName))) {
+ properties.load(inputStream);
+ value = properties.getProperty(property);
+ } catch (Exception e) {
+ // do nothing
+ }
+ }
+ }
+
+ if (value == null && defaultValue != null) {
+ value = defaultValue;
+ }
+
+ if (value != null) {
+ value = filter(value);
+ systemProperties.setProperty(property, value);
+ }
+
+ return false;
+ }
+ throw new ConfigurationException("Unhandled configuration", lineNo, line);
+ }
+
+ private String handleRealmConfiguration(String line, int lineNo)
+ throws ConfigurationException, DuplicateRealmException {
+ int rbrack = line.indexOf("]");
+
+ if (rbrack < 0) {
+ throw new ConfigurationException("Invalid realm specifier", lineNo, line);
+ }
+
+ String realmName = line.substring(1, rbrack);
+ handler.addRealm(realmName);
+ return realmName;
+ }
+
+ private void handleImportConfiguration(String line, int lineNo, String curRealm)
+ throws ConfigurationException, NoSuchRealmException {
+ if (line.startsWith(IMPORT_PREFIX)) {
+ if (curRealm == null) {
+ throw new ConfigurationException("Unhandled import", lineNo, line);
+ }
+ int fromLoc = line.indexOf(FROM_SEPARATOR, IMPORT_PREFIX.length());
+
+ if (fromLoc < 0) {
+ throw new ConfigurationException("Missing from clause", lineNo, line);
+ }
+
+ String importSpec = line.substring(IMPORT_PREFIX.length(), fromLoc).trim();
+ String realmName = line.substring(fromLoc + FROM_SEPARATOR.length()).trim();
+
+ handler.addImportFrom(realmName, importSpec);
+ return;
+ }
+ throw new ConfigurationException("Unhandled configuration", lineNo, line);
+ }
+
+ private void handleLoadConfiguration(String line, int lineNo)
+ throws ConfigurationException, FileNotFoundException, MalformedURLException {
+ if (line.startsWith(LOAD_PREFIX)) {
+ String constituent = line.substring(LOAD_PREFIX.length()).trim();
+ constituent = filter(constituent);
+
+ if (constituent.contains("*")) {
+ loadGlob(constituent, false /*not optionally*/);
+ } else {
+ File file = new File(constituent);
+
+ if (file.exists()) {
+ handler.addLoadFile(file);
+ } else {
+ try {
+ handler.addLoadURL(new URL(constituent));
+ } catch (MalformedURLException e) {
+ throw new FileNotFoundException(constituent);
+ }
+ }
+ }
+ return;
+ }
+ throw new ConfigurationException("Unhandled configuration", lineNo, line);
+ }
+
+ private void handleModuleConfiguration(String line, int lineNo)
+ throws ConfigurationException, FileNotFoundException, MalformedURLException {
+ if (line.startsWith(MODULE_PREFIX)) {
+ String constituent = line.substring(MODULE_PREFIX.length()).trim();
+ constituent = filter(constituent);
+
+ if (constituent.contains("*")) {
+ moduleGlob(constituent);
+ } else {
+ File file = new File(constituent);
+
+ if (file.exists()) {
+ handler.addModuleFile(file);
+ } else {
+ throw new FileNotFoundException(constituent);
+ }
+ }
+ return;
+ }
+ throw new ConfigurationException("Unhandled configuration", lineNo, line);
+ }
+
+ private void handleAddDirective(String line, int lineNo) throws ConfigurationException {
+ if (line.startsWith(ADD_EXPORTS_PREFIX)) {
+ String spec = line.substring(ADD_EXPORTS_PREFIX.length()).trim();
+ parseModulePackageDirective(spec, lineNo, line, handler::addExports);
+ } else if (line.startsWith(ADD_OPENS_PREFIX)) {
+ String spec = line.substring(ADD_OPENS_PREFIX.length()).trim();
+ parseModulePackageDirective(spec, lineNo, line, handler::addOpens);
+ } else if (line.startsWith(ADD_READS_PREFIX)) {
+ String spec = line.substring(ADD_READS_PREFIX.length()).trim();
+ int eqLoc = spec.indexOf('=');
+ if (eqLoc < 0) {
+ throw new ConfigurationException("Missing '=' in add-reads directive", lineNo, line);
+ }
+ String source = spec.substring(0, eqLoc).trim();
+ String target = spec.substring(eqLoc + 1).trim();
+ handler.addReads(source, target);
+ } else {
+ throw new ConfigurationException("Unhandled configuration", lineNo, line);
+ }
+ }
+
+ private void parseModulePackageDirective(String spec, int lineNo, String line, ModulePackageHandler mph)
+ throws ConfigurationException {
+ int slashLoc = spec.indexOf('/');
+ if (slashLoc < 0) {
+ throw new ConfigurationException("Missing '/' in module/package directive", lineNo, line);
+ }
+ String module = spec.substring(0, slashLoc).trim();
+ String rest = spec.substring(slashLoc + 1).trim();
+ String pkg;
+ String target;
+ int eqLoc = rest.indexOf('=');
+ if (eqLoc >= 0) {
+ pkg = rest.substring(0, eqLoc).trim();
+ target = rest.substring(eqLoc + 1).trim();
+ } else {
+ pkg = rest;
+ target = "ALL-UNNAMED";
+ }
+ mph.handle(module, pkg, target);
+ }
+
+ @FunctionalInterface
+ private interface ModulePackageHandler {
+ void handle(String module, String pkg, String target);
+ }
+
+ protected void moduleGlob(String line) throws MalformedURLException, FileNotFoundException {
+ File globFile = new File(line);
+
+ File dir = globFile.getParentFile();
+ if (!dir.exists()) {
+ return;
+ }
+
+ String localName = globFile.getName();
+ int starLoc = localName.indexOf("*");
+ final String prefix = localName.substring(0, starLoc);
+ final String suffix = localName.substring(starLoc + 1);
+
+ File[] matches = dir.listFiles((dir1, name) -> name.startsWith(prefix) && name.endsWith(suffix));
+
+ if (matches != null) {
+ for (File match : matches) {
+ handler.addModuleFile(match);
+ }
+ }
+ }
+
+ private void handleOptionallyConfiguration(String line, int lineNo)
+ throws ConfigurationException, FileNotFoundException, MalformedURLException {
+ if (line.startsWith(OPTIONALLY_PREFIX)) {
+ String constituent = line.substring(OPTIONALLY_PREFIX.length()).trim();
+ constituent = filter(constituent);
+
+ if (constituent.contains("*")) {
+ loadGlob(constituent, true /*optionally*/);
+ } else {
+ File file = new File(constituent);
+
+ if (file.exists()) {
+ handler.addLoadFile(file);
+ } else {
+ try {
+ handler.addLoadURL(new URL(constituent));
+ } catch (MalformedURLException e) {
+ // swallow
+ }
+ }
+ }
+ return;
+ }
+ throw new ConfigurationException("Unhandled configuration", lineNo, line);
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/Configurator.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/Configurator.java
new file mode 100644
index 000000000000..0d8107b59347
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/Configurator.java
@@ -0,0 +1,339 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.launcher;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.module.Configuration;
+import java.lang.module.ModuleFinder;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.codehaus.plexus.classworlds.ClassWorld;
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
+import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
+
+/**
+ * Launcher configurator.
+ *
+ * @author bob mcwhirter
+ * @author Jason van Zyl
+ */
+public class Configurator implements ConfigurationHandler {
+ /**
+ * The launcher to configure.
+ */
+ private Launcher launcher;
+
+ private ClassWorld world;
+
+ /**
+ * Processed Realms.
+ */
+ private Map configuredRealms;
+
+ /**
+ * Current Realm.
+ */
+ private ClassRealm curRealm;
+
+ private ClassLoader foreignClassLoader = null;
+
+ private List modulePaths = new ArrayList<>();
+
+ private List exportDirectives = new ArrayList<>();
+
+ private List openDirectives = new ArrayList<>();
+
+ private List readDirectives = new ArrayList<>();
+
+ /**
+ * Construct.
+ *
+ * @param launcher The launcher to configure.
+ */
+ public Configurator(Launcher launcher) {
+ this.launcher = launcher;
+
+ configuredRealms = new HashMap<>();
+
+ if (launcher != null) {
+ this.foreignClassLoader = launcher.getSystemClassLoader();
+ }
+ }
+
+ /**
+ * Construct.
+ *
+ * @param world The classWorld to configure.
+ */
+ public Configurator(ClassWorld world) {
+ setClassWorld(world);
+ }
+
+ /**
+ * set world.
+ * this setter is provided so you can use the same configurator to configure several "worlds"
+ *
+ * @param world The classWorld to configure.
+ */
+ public void setClassWorld(ClassWorld world) {
+ this.world = world;
+
+ configuredRealms = new HashMap<>();
+ }
+
+ /**
+ * Configure from a file.
+ *
+ * @param is The config input stream
+ * @throws IOException If an error occurs reading the config file.
+ * @throws MalformedURLException If the config file contains invalid URLs.
+ * @throws ConfigurationException If the config file is corrupt.
+ * @throws org.codehaus.plexus.classworlds.realm.DuplicateRealmException If the config file defines two realms with the same id.
+ * @throws org.codehaus.plexus.classworlds.realm.NoSuchRealmException If the config file defines a main entry point in
+ * a non-existent realm.
+ */
+ public void configure(InputStream is)
+ throws IOException, ConfigurationException, DuplicateRealmException, NoSuchRealmException {
+ if (world == null) {
+ world = new ClassWorld();
+ }
+
+ curRealm = null;
+
+ foreignClassLoader = null;
+ modulePaths = new ArrayList<>();
+ exportDirectives = new ArrayList<>();
+ openDirectives = new ArrayList<>();
+ readDirectives = new ArrayList<>();
+
+ if (this.launcher != null) {
+ foreignClassLoader = this.launcher.getSystemClassLoader();
+ }
+
+ ConfigurationParser parser = new ConfigurationParser(this, System.getProperties());
+
+ parser.parse(is);
+
+ createModuleLayer();
+
+ // Associate child realms to their parents.
+ associateRealms();
+
+ if (this.launcher != null) {
+ this.launcher.setWorld(world);
+ }
+ }
+
+ // TODO return this to protected when the legacy wrappers can be removed.
+ /**
+ * Associate parent realms with their children.
+ */
+ public void associateRealms() {
+ List sortRealmNames = new ArrayList<>(configuredRealms.keySet());
+
+ // sort by name
+ sortRealmNames.sort(String::compareTo);
+
+ // So now we have something like the following for defined
+ // realms:
+ //
+ // root
+ // root.maven
+ // root.maven.plugin
+ //
+ // Now if the name of a realm is a superset of an existing realm
+ // the we want to make child/parent associations.
+
+ for (String realmName : sortRealmNames) {
+ int j = realmName.lastIndexOf('.');
+
+ if (j > 0) {
+ String parentRealmName = realmName.substring(0, j);
+
+ ClassRealm parentRealm = configuredRealms.get(parentRealmName);
+
+ if (parentRealm != null) {
+ ClassRealm realm = configuredRealms.get(realmName);
+
+ realm.setParentRealm(parentRealm);
+ }
+ }
+ }
+ }
+
+ public void addImportFrom(String relamName, String importSpec) throws NoSuchRealmException {
+ curRealm.importFrom(relamName, importSpec);
+ }
+
+ public void addLoadFile(File file) {
+ try {
+ curRealm.addURL(file.toURI().toURL());
+ } catch (MalformedURLException e) {
+ // can't really happen... or can it?
+ }
+ }
+
+ public void addLoadURL(URL url) {
+ curRealm.addURL(url);
+ }
+
+ public void addRealm(String realmName) throws DuplicateRealmException {
+ curRealm = world.newRealm(realmName, foreignClassLoader);
+
+ // Stash the configured realm for subsequent association processing.
+ configuredRealms.put(realmName, curRealm);
+ }
+
+ public void addModuleFile(File file) {
+ modulePaths.add(file.toPath());
+ }
+
+ public List getModulePaths() {
+ return modulePaths;
+ }
+
+ public void addExports(String module, String pkg, String target) {
+ exportDirectives.add(new String[] {module, pkg, target});
+ }
+
+ public void addOpens(String module, String pkg, String target) {
+ openDirectives.add(new String[] {module, pkg, target});
+ }
+
+ public void addReads(String source, String target) {
+ readDirectives.add(new String[] {source, target});
+ }
+
+ private void createModuleLayer() {
+ if (modulePaths.isEmpty()
+ && exportDirectives.isEmpty()
+ && openDirectives.isEmpty()
+ && readDirectives.isEmpty()) {
+ return;
+ }
+
+ if (!modulePaths.isEmpty()) {
+ try {
+ Set bootModuleNames = ModuleLayer.boot().modules().stream()
+ .map(Module::getName)
+ .collect(Collectors.toSet());
+
+ ModuleFinder finder = ModuleFinder.of(modulePaths.toArray(new Path[0]));
+ Set newModules = finder.findAll().stream()
+ .map(ref -> ref.descriptor().name())
+ .filter(name -> !bootModuleNames.contains(name))
+ .collect(Collectors.toSet());
+
+ if (!newModules.isEmpty()) {
+ Configuration cf =
+ ModuleLayer.boot().configuration().resolve(finder, ModuleFinder.of(), newModules);
+ ClassLoader parent =
+ foreignClassLoader != null ? foreignClassLoader : ClassLoader.getSystemClassLoader();
+ ModuleLayer.Controller controller =
+ ModuleLayer.defineModulesWithOneLoader(cf, List.of(ModuleLayer.boot()), parent);
+ ModuleLayer layer = controller.layer();
+ foreignClassLoader = layer.findLoader(newModules.iterator().next());
+ world.setModuleLayer(layer, controller);
+ }
+ } catch (Exception e) {
+ addModulePathsToClasspath();
+ }
+ }
+
+ Module unnamedTarget = getClass().getModule();
+
+ for (String[] directive : exportDirectives) {
+ Module target = resolveTargetModule(directive[2], unnamedTarget);
+ if (target != null) {
+ world.addExports(directive[0], directive[1], target);
+ }
+ }
+ for (String[] directive : openDirectives) {
+ Module target = resolveTargetModule(directive[2], unnamedTarget);
+ if (target != null) {
+ world.addOpens(directive[0], directive[1], target);
+ }
+ }
+ for (String[] directive : readDirectives) {
+ Module target = resolveTargetModule(directive[1], unnamedTarget);
+ if (target != null) {
+ world.addReads(directive[0], target);
+ }
+ }
+ }
+
+ private void addModulePathsToClasspath() {
+ if (curRealm != null) {
+ for (Path path : modulePaths) {
+ try {
+ curRealm.addURL(path.toUri().toURL());
+ } catch (MalformedURLException e) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ private Module resolveTargetModule(String targetName, Module unnamedTarget) {
+ if ("ALL-UNNAMED".equals(targetName)) {
+ return unnamedTarget;
+ }
+ ModuleLayer layer = world.getModuleLayer();
+ if (layer != null) {
+ Module m = layer.findModule(targetName).orElse(null);
+ if (m != null) {
+ return m;
+ }
+ }
+ return ModuleLayer.boot().findModule(targetName).orElse(null);
+ }
+
+ public void setAppMain(String mainClassName, String mainRealmName) {
+ if (this.launcher != null) {
+ this.launcher.setAppMain(mainClassName, mainRealmName);
+ }
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/Launcher.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/Launcher.java
new file mode 100644
index 000000000000..7384e43517c5
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/Launcher.java
@@ -0,0 +1,409 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.launcher;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import org.codehaus.plexus.classworlds.ClassWorld;
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
+import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
+
+/**
+ * Command-line invokable application launcher.
+ *
+ * This launcher class assists in the creation of classloaders and ClassRealms
+ * from a configuration file and the launching of the application's main
+ * method from the correct class loaded through the correct classloader.
+ *
+ * The path to the configuration file is specified using the classworlds.conf
+ * system property, typically specified using the -D switch to
+ * java.
+ *
+ * @author bob mcwhirter
+ */
+public class Launcher {
+ protected static final String CLASSWORLDS_CONF = "classworlds.conf";
+
+ protected static final String UBERJAR_CONF_DIR = "WORLDS-INF/conf/";
+
+ protected ClassLoader systemClassLoader;
+
+ protected String mainClassName;
+
+ protected String mainRealmName;
+
+ protected ClassWorld world;
+
+ private int exitCode = 0;
+
+ public Launcher() {
+ this.systemClassLoader = Thread.currentThread().getContextClassLoader();
+ }
+
+ public void setSystemClassLoader(ClassLoader loader) {
+ this.systemClassLoader = loader;
+ }
+
+ public ClassLoader getSystemClassLoader() {
+ return this.systemClassLoader;
+ }
+
+ public int getExitCode() {
+ return exitCode;
+ }
+
+ public void setAppMain(String mainClassName, String mainRealmName) {
+ this.mainClassName = mainClassName;
+
+ this.mainRealmName = mainRealmName;
+ }
+
+ public String getMainRealmName() {
+ return this.mainRealmName;
+ }
+
+ public String getMainClassName() {
+ return this.mainClassName;
+ }
+
+ public void setWorld(ClassWorld world) {
+ this.world = world;
+ }
+
+ public ClassWorld getWorld() {
+ return this.world;
+ }
+
+ /**
+ * Configure from a file.
+ *
+ * @param is The config input stream.
+ * @throws IOException If an error occurs reading the config file.
+ * @throws MalformedURLException If the config file contains invalid URLs.
+ * @throws ConfigurationException If the config file is corrupt.
+ * @throws org.codehaus.plexus.classworlds.realm.DuplicateRealmException If the config file defines two realms
+ * with the same id.
+ * @throws org.codehaus.plexus.classworlds.realm.NoSuchRealmException If the config file defines a main entry
+ * point in a non-existent realm.
+ */
+ public void configure(InputStream is)
+ throws IOException, ConfigurationException, DuplicateRealmException, NoSuchRealmException {
+ Configurator configurator = new Configurator(this);
+
+ configurator.configure(is);
+ }
+
+ /**
+ * Retrieve the main entry class.
+ *
+ * @return The main entry class.
+ * @throws ClassNotFoundException If the class cannot be found.
+ * @throws NoSuchRealmException If the specified main entry realm does not exist.
+ */
+ public Class> getMainClass() throws ClassNotFoundException, NoSuchRealmException {
+ return getMainRealm().loadClass(getMainClassName());
+ }
+
+ /**
+ * Retrieve the main entry realm.
+ *
+ * @return The main entry realm.
+ * @throws NoSuchRealmException If the specified main entry realm does not exist.
+ */
+ public ClassRealm getMainRealm() throws NoSuchRealmException {
+ return getWorld().getRealm(getMainRealmName());
+ }
+
+ /**
+ * Retrieve the enhanced main entry method.
+ *
+ * @return The enhanced main entry method.
+ * @throws ClassNotFoundException If the main entry class cannot be found.
+ * @throws NoSuchMethodException If the main entry method cannot be found.
+ * @throws NoSuchRealmException If the main entry realm cannot be found.
+ */
+ protected Method getEnhancedMainMethod()
+ throws ClassNotFoundException, NoSuchMethodException, NoSuchRealmException {
+ Class> cwClass = getMainRealm().loadClass(ClassWorld.class.getName());
+
+ Method m = getMainClass().getMethod("main", String[].class, cwClass);
+
+ int modifiers = m.getModifiers();
+
+ if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) {
+ if (m.getReturnType() == Integer.TYPE || m.getReturnType() == Void.TYPE) {
+ return m;
+ }
+ }
+
+ throw new NoSuchMethodException("public static void main(String[] args, ClassWorld world)");
+ }
+
+ /**
+ * Retrieve the main entry method.
+ *
+ * @return The main entry method.
+ * @throws ClassNotFoundException If the main entry class cannot be found.
+ * @throws NoSuchMethodException If the main entry method cannot be found.
+ * @throws NoSuchRealmException If the main entry realm cannot be found.
+ */
+ protected Method getMainMethod() throws ClassNotFoundException, NoSuchMethodException, NoSuchRealmException {
+ Method m = getMainClass().getMethod("main", String[].class);
+
+ int modifiers = m.getModifiers();
+
+ if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) {
+ if (m.getReturnType() == Integer.TYPE || m.getReturnType() == Void.TYPE) {
+ return m;
+ }
+ }
+
+ throw new NoSuchMethodException("public static void main(String[] args) in " + getMainClass());
+ }
+
+ /**
+ * Launch the application.
+ *
+ * @param args The application args.
+ * @throws ClassNotFoundException If the main entry class cannot be found.
+ * @throws IllegalAccessException If the method cannot be accessed.
+ * @throws InvocationTargetException If the target of the invokation is invalid.
+ * @throws NoSuchMethodException If the main entry method cannot be found.
+ * @throws NoSuchRealmException If the main entry realm cannot be found.
+ */
+ public void launch(String[] args)
+ throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException,
+ NoSuchRealmException {
+ try {
+ launchEnhanced(args);
+
+ return;
+ } catch (NoSuchMethodException e) {
+ // ignore
+ }
+
+ launchStandard(args);
+ }
+
+ /**
+ * Attempt to launch the application through the enhanced main method.
+ *
+ * This will seek a method with the exact signature of:
+ *
+ * public static void main(String[] args, ClassWorld world)
+ *
+ *
+ * @param args The application args.
+ * @throws ClassNotFoundException If the main entry class cannot be found.
+ * @throws IllegalAccessException If the method cannot be accessed.
+ * @throws InvocationTargetException If the target of the invokation is
+ * invalid.
+ * @throws NoSuchMethodException If the main entry method cannot be found.
+ * @throws NoSuchRealmException If the main entry realm cannot be found.
+ */
+ protected void launchEnhanced(String[] args)
+ throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException,
+ NoSuchRealmException {
+ ClassRealm mainRealm = getMainRealm();
+
+ Class> mainClass = getMainClass();
+
+ Method mainMethod = getEnhancedMainMethod();
+
+ ClassLoader cl = mainRealm;
+
+ // ----------------------------------------------------------------------
+ // This is what the classloader for the main realm looks like when we
+ // boot from the command line:
+ // ----------------------------------------------------------------------
+ // [ AppLauncher$AppClassLoader ] : $CLASSPATH envar
+ // ^
+ // |
+ // |
+ // [ AppLauncher$ExtClassLoader ] : ${java.home}/jre/lib/ext/*.jar
+ // ^
+ // |
+ // |
+ // [ Strategy ]
+ // ----------------------------------------------------------------------
+
+ Thread.currentThread().setContextClassLoader(cl);
+
+ Object ret = mainMethod.invoke(mainClass, args, getWorld());
+
+ if (ret instanceof Integer) {
+ exitCode = (Integer) ret;
+ }
+
+ Thread.currentThread().setContextClassLoader(systemClassLoader);
+ }
+
+ /**
+ * Attempt to launch the application through the standard main method.
+ *
+ * This will seek a method with the exact signature of:
+ *
+ *
+ * public static void main(String[] args)
+ *
+ *
+ * @param args The application args.
+ * @throws ClassNotFoundException If the main entry class cannot be found.
+ * @throws IllegalAccessException If the method cannot be accessed.
+ * @throws InvocationTargetException If the target of the invokation is
+ * invalid.
+ * @throws NoSuchMethodException If the main entry method cannot be found.
+ * @throws NoSuchRealmException If the main entry realm cannot be found.
+ */
+ protected void launchStandard(String[] args)
+ throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException,
+ NoSuchRealmException {
+ ClassRealm mainRealm = getMainRealm();
+
+ Class> mainClass = getMainClass();
+
+ Method mainMethod = getMainMethod();
+
+ Thread.currentThread().setContextClassLoader(mainRealm);
+
+ Object ret = mainMethod.invoke(mainClass, new Object[] {args});
+
+ if (ret instanceof Integer) {
+ exitCode = (Integer) ret;
+ }
+
+ Thread.currentThread().setContextClassLoader(systemClassLoader);
+ }
+
+ // ------------------------------------------------------------
+ // Class methods
+ // ------------------------------------------------------------
+
+ /**
+ * Launch the launcher from the command line.
+ * Will exit using System.exit with an exit code of 0 for success, 100 if there was an unknown exception,
+ * or some other code for an application error.
+ *
+ * @param args The application command-line arguments.
+ */
+ public static void main(String[] args) {
+ try {
+ int exitCode = mainWithExitCode(args);
+
+ System.exit(exitCode);
+ } catch (Exception e) {
+ e.printStackTrace();
+
+ System.exit(100);
+ }
+ }
+
+ /**
+ * Launch the launcher.
+ *
+ * @param args The application command-line arguments.
+ * @return an integer exit code
+ * @throws Exception If an error occurs.
+ */
+ public static int mainWithExitCode(String[] args) throws Exception {
+ String classworldsConf = System.getProperty(CLASSWORLDS_CONF);
+
+ InputStream is;
+
+ Launcher launcher = new Launcher();
+
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+
+ launcher.setSystemClassLoader(cl);
+
+ if (classworldsConf != null) {
+ is = Files.newInputStream(Paths.get(classworldsConf));
+ } else {
+ if ("true".equals(System.getProperty("classworlds.bootstrapped"))) {
+ is = cl.getResourceAsStream(UBERJAR_CONF_DIR + CLASSWORLDS_CONF);
+ } else {
+ is = cl.getResourceAsStream(CLASSWORLDS_CONF);
+ }
+ }
+
+ if (is == null) {
+ throw new Exception("classworlds configuration not specified nor found in the classpath");
+ }
+
+ launcher.configure(is);
+
+ is.close();
+
+ try {
+ launcher.launch(args);
+ } catch (InvocationTargetException e) {
+ ClassRealm realm = launcher.getWorld().getRealm(launcher.getMainRealmName());
+
+ URL[] constituents = realm.getURLs();
+
+ System.out.println("---------------------------------------------------");
+
+ for (int i = 0; i < constituents.length; i++) {
+ System.out.println("constituent[" + i + "]: " + constituents[i]);
+ }
+
+ System.out.println("---------------------------------------------------");
+
+ // Decode ITE (if we can)
+ Throwable t = e.getTargetException();
+
+ if (t instanceof Exception) {
+ throw (Exception) t;
+ }
+ if (t instanceof Error) {
+ throw (Error) t;
+ }
+
+ // Else just toss the ITE
+ throw e;
+ }
+
+ return launcher.getExitCode();
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/ClassRealm.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/ClassRealm.java
new file mode 100644
index 000000000000..dd0695e7863a
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/ClassRealm.java
@@ -0,0 +1,510 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.realm;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.codehaus.plexus.classworlds.ClassWorld;
+import org.codehaus.plexus.classworlds.strategy.Strategy;
+import org.codehaus.plexus.classworlds.strategy.StrategyFactory;
+
+/**
+ * The class loading gateway. Each class realm has access to a base class loader, imports form zero or more other class
+ * loaders, an optional parent class loader and of course its own class path. When queried for a class/resource, a class
+ * realm will always query its base class loader first before it delegates to a pluggable strategy. The strategy in turn
+ * controls the order in which imported class loaders, the parent class loader and the realm itself are searched. The
+ * base class loader is assumed to be capable of loading of the bootstrap classes.
+ *
+ * @author bob mcwhirter
+ * @author Jason van Zyl
+ */
+public class ClassRealm extends URLClassLoader implements org.apache.maven.api.classworlds.ClassRealm {
+
+ private ClassWorld world;
+
+ private String id;
+
+ private SortedSet foreignImports;
+
+ private SortedSet parentImports;
+
+ private Strategy strategy;
+
+ private ClassLoader parentClassLoader;
+
+ private static final boolean IS_PARALLEL_CAPABLE = Closeable.class.isAssignableFrom(URLClassLoader.class);
+
+ private final ConcurrentMap lockMap;
+
+ /**
+ * Creates a new class realm.
+ *
+ * @param world The class world this realm belongs to, must not be null.
+ * @param id The identifier for this realm, must not be null.
+ * @param baseClassLoader The base class loader for this realm, may be null to use the bootstrap class
+ * loader.
+ */
+ public ClassRealm(ClassWorld world, String id, ClassLoader baseClassLoader) {
+ super(new URL[0], baseClassLoader);
+
+ this.world = world;
+
+ this.id = id;
+
+ foreignImports = new TreeSet<>();
+
+ strategy = StrategyFactory.getStrategy(this);
+
+ lockMap = IS_PARALLEL_CAPABLE ? new ConcurrentHashMap<>() : null;
+
+ if (IS_PARALLEL_CAPABLE) {
+ // We must call super.getClassLoadingLock at least once
+ // to avoid NPE in super.loadClass.
+ super.getClassLoadingLock(getClass().getName());
+ }
+ }
+
+ public String getId() {
+ return this.id;
+ }
+
+ public ClassWorld getWorld() {
+ return this.world;
+ }
+
+ /**
+ * Returns the underlying ClassLoader for this realm.
+ *
+ * This method allows access to the actual ClassLoader implementation
+ * while maintaining API abstraction. Since ClassRealm extends URLClassLoader,
+ * this method returns {@code this}.
+ *
+ *
+ * @return the underlying ClassLoader (this instance)
+ */
+ public ClassLoader getClassLoader() {
+ return this;
+ }
+
+ public void importFromParent(String packageName) {
+ if (parentImports == null) {
+ parentImports = new TreeSet<>();
+ }
+
+ parentImports.add(new Entry(null, packageName));
+ }
+
+ boolean isImportedFromParent(String name) {
+ if (parentImports != null && !parentImports.isEmpty()) {
+ for (Entry entry : parentImports) {
+ if (entry.matches(name)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ public void importFrom(String realmId, String packageName) throws NoSuchRealmException {
+ importFrom(getWorld().getRealm(realmId), packageName);
+ }
+
+ public void importFrom(ClassLoader classLoader, String packageName) {
+ foreignImports.add(new Entry(classLoader, packageName));
+ }
+
+ public ClassLoader getImportClassLoader(String name) {
+ for (Entry entry : foreignImports) {
+ if (entry.matches(name)) {
+ return entry.getClassLoader();
+ }
+ }
+
+ return null;
+ }
+
+ public Collection getImportRealms() {
+ Collection importRealms = new HashSet<>();
+
+ for (Entry entry : foreignImports) {
+ if (entry.getClassLoader() instanceof ClassRealm) {
+ importRealms.add((ClassRealm) entry.getClassLoader());
+ }
+ }
+
+ return importRealms;
+ }
+
+ public Strategy getStrategy() {
+ return strategy;
+ }
+
+ public void setParentClassLoader(ClassLoader parentClassLoader) {
+ this.parentClassLoader = parentClassLoader;
+ }
+
+ public ClassLoader getParentClassLoader() {
+ return parentClassLoader;
+ }
+
+ public void setParentRealm(ClassRealm realm) {
+ this.parentClassLoader = realm;
+ }
+
+ public ClassRealm getParentRealm() {
+ return (parentClassLoader instanceof ClassRealm) ? (ClassRealm) parentClassLoader : null;
+ }
+
+ // Implementation of the original method signature for backward compatibility
+ public ClassRealm createChildRealm(String id) throws DuplicateRealmException {
+ ClassRealm childRealm = getWorld().newRealm(id, (ClassLoader) null);
+ childRealm.setParentRealm(this);
+ return childRealm;
+ }
+
+ public void addURL(URL url) {
+ String urlStr = url.toExternalForm();
+
+ if (urlStr.startsWith("jar:") && urlStr.endsWith("!/")) {
+ urlStr = urlStr.substring(4, urlStr.length() - 2);
+
+ try {
+ url = new URL(urlStr);
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+ }
+
+ super.addURL(url);
+ }
+
+ public void addExports(String moduleName, String packageName) {
+ world.addExports(moduleName, packageName, getUnnamedModule());
+ }
+
+ public void addOpens(String moduleName, String packageName) {
+ world.addOpens(moduleName, packageName, getUnnamedModule());
+ }
+
+ public void addReads(String moduleName) {
+ world.addReads(moduleName, getUnnamedModule());
+ }
+
+ // ----------------------------------------------------------------------
+ // We delegate to the Strategy here so that we can change the behavior
+ // of any existing ClassRealm.
+ // ----------------------------------------------------------------------
+
+ public Class> loadClass(String name) throws ClassNotFoundException {
+ return loadClass(name, false);
+ }
+
+ protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if (IS_PARALLEL_CAPABLE) {
+ return unsynchronizedLoadClass(name, resolve);
+
+ } else {
+ synchronized (this) {
+ return unsynchronizedLoadClass(name, resolve);
+ }
+ }
+ }
+
+ private Class> unsynchronizedLoadClass(String name, boolean resolve) throws ClassNotFoundException {
+ try {
+ // first, try loading bootstrap classes
+ return super.loadClass(name, resolve);
+ } catch (ClassNotFoundException e) {
+ // next, try loading via imports, self and parent as controlled by strategy
+ return strategy.loadClass(name);
+ }
+ }
+
+ // overwrites
+ // https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/ClassLoader.html#findClass(java.lang.String,java.lang.String)
+ // introduced in Java9
+ protected Class> findClass(String moduleName, String name) {
+ if (moduleName != null) {
+ return null;
+ }
+ try {
+ return findClassInternal(name);
+ } catch (ClassNotFoundException e) {
+ try {
+ return strategy.getRealm().findClass(name);
+ } catch (ClassNotFoundException nestedException) {
+ return null;
+ }
+ }
+ }
+
+ protected Class> findClass(String name) throws ClassNotFoundException {
+ /*
+ * NOTE: This gets only called from ClassLoader.loadClass(Class, boolean) while we try to check for bootstrap
+ * stuff. Don't scan our class path yet, loadClassFromSelf() will do this later when called by the strategy.
+ */
+ throw new ClassNotFoundException(name);
+ }
+
+ protected Class> findClassInternal(String name) throws ClassNotFoundException {
+ return super.findClass(name);
+ }
+
+ public URL getResource(String name) {
+ URL resource = super.getResource(name);
+ return resource != null ? resource : strategy.getResource(name);
+ }
+
+ public URL findResource(String name) {
+ return super.findResource(name);
+ }
+
+ public Enumeration getResources(String name) throws IOException {
+ Collection resources = new LinkedHashSet<>(Collections.list(super.getResources(name)));
+ resources.addAll(Collections.list(strategy.getResources(name)));
+ return Collections.enumeration(resources);
+ }
+
+ public Enumeration findResources(String name) throws IOException {
+ return super.findResources(name);
+ }
+
+ // ----------------------------------------------------------------------------
+ // Display methods
+ // ----------------------------------------------------------------------------
+
+ public void display() {
+ display(System.out);
+ }
+
+ public void display(PrintStream out) {
+ out.println("-----------------------------------------------------");
+
+ for (ClassRealm cr = this; cr != null; cr = (ClassRealm) cr.getParentRealm()) {
+ out.println("realm = " + cr.getId());
+ out.println("strategy = " + cr.getStrategy().getClass().getName());
+
+ showUrls(cr, out);
+
+ out.println();
+ }
+
+ out.println("-----------------------------------------------------");
+ }
+
+ private static void showUrls(ClassRealm classRealm, PrintStream out) {
+ URL[] urls = classRealm.getURLs();
+
+ for (int i = 0; i < urls.length; i++) {
+ out.println("urls[" + i + "] = " + urls[i]);
+ }
+
+ out.println("Number of foreign imports: " + classRealm.foreignImports.size());
+
+ for (Entry entry : classRealm.foreignImports) {
+ out.println("import: " + entry);
+ }
+
+ if (classRealm.parentImports != null) {
+ out.println("Number of parent imports: " + classRealm.parentImports.size());
+
+ for (Entry entry : classRealm.parentImports) {
+ out.println("import: " + entry);
+ }
+ }
+ }
+
+ public String toString() {
+ return "ClassRealm[" + getId() + ", parent: " + getParentClassLoader() + "]";
+ }
+
+ // ---------------------------------------------------------------------------------------------
+ // Search methods that can be ordered by strategies to load a class
+ // ---------------------------------------------------------------------------------------------
+
+ public Class> loadClassFromImport(String name) {
+ ClassLoader importClassLoader = getImportClassLoader(name);
+
+ if (importClassLoader != null) {
+ try {
+ return importClassLoader.loadClass(name);
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ }
+
+ return null;
+ }
+
+ public Class> loadClassFromSelf(String name) {
+ synchronized (getClassRealmLoadingLock(name)) {
+ try {
+ Class> clazz = findLoadedClass(name);
+
+ if (clazz == null) {
+ clazz = findClassInternal(name);
+ }
+
+ return clazz;
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ }
+ }
+
+ private Object getClassRealmLoadingLock(String name) {
+ if (IS_PARALLEL_CAPABLE) {
+ return getClassLoadingLock(name);
+ } else {
+ return this;
+ }
+ }
+
+ @Override
+ protected Object getClassLoadingLock(String name) {
+ if (IS_PARALLEL_CAPABLE) {
+ Object newLock = new Object();
+ Object lock = lockMap.putIfAbsent(name, newLock);
+ return (lock == null) ? newLock : lock;
+ }
+ return this;
+ }
+
+ public Class> loadClassFromParent(String name) {
+ ClassLoader parent = getParentClassLoader();
+
+ if (parent != null && isImportedFromParent(name)) {
+ try {
+ return parent.loadClass(name);
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ }
+
+ return null;
+ }
+
+ // ---------------------------------------------------------------------------------------------
+ // Search methods that can be ordered by strategies to get a resource
+ // ---------------------------------------------------------------------------------------------
+
+ public URL loadResourceFromImport(String name) {
+ ClassLoader importClassLoader = getImportClassLoader(name);
+
+ if (importClassLoader != null) {
+ return importClassLoader.getResource(name);
+ }
+
+ return null;
+ }
+
+ public URL loadResourceFromSelf(String name) {
+ return findResource(name);
+ }
+
+ public URL loadResourceFromParent(String name) {
+ ClassLoader parent = getParentClassLoader();
+
+ if (parent != null && isImportedFromParent(name)) {
+ return parent.getResource(name);
+ } else {
+ return null;
+ }
+ }
+
+ // ---------------------------------------------------------------------------------------------
+ // Search methods that can be ordered by strategies to get resources
+ // ---------------------------------------------------------------------------------------------
+
+ public Enumeration loadResourcesFromImport(String name) {
+ ClassLoader importClassLoader = getImportClassLoader(name);
+
+ if (importClassLoader != null) {
+ try {
+ return importClassLoader.getResources(name);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ return null;
+ }
+
+ public Enumeration loadResourcesFromSelf(String name) {
+ try {
+ return findResources(name);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ public Enumeration loadResourcesFromParent(String name) {
+ ClassLoader parent = getParentClassLoader();
+
+ if (parent != null && isImportedFromParent(name)) {
+ try {
+ return parent.getResources(name);
+ } catch (IOException e) {
+ // eat it
+ }
+ }
+
+ return null;
+ }
+
+ static {
+ if (IS_PARALLEL_CAPABLE) // Avoid running this method on older jdks
+ {
+ registerAsParallelCapable();
+ }
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/DuplicateRealmException.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/DuplicateRealmException.java
new file mode 100644
index 000000000000..59d0299ead9d
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/DuplicateRealmException.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.realm;
+
+import org.codehaus.plexus.classworlds.ClassWorld;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+/**
+ * Indicates an attempt to add a ClassRealm to a
+ * ClassWorld with a duplicate id.
+ *
+ * @author bob mcwhirter
+ */
+public class DuplicateRealmException extends org.apache.maven.api.classworlds.DuplicateRealmException {
+ // ------------------------------------------------------------
+ // Instance members
+ // ------------------------------------------------------------
+
+ /**
+ * The realm id.
+ */
+ private String id;
+
+ // ------------------------------------------------------------
+ // Constructors
+ // ------------------------------------------------------------
+
+ /**
+ * Construct.
+ *
+ * @param world The world.
+ * @param id The realm id.
+ */
+ public DuplicateRealmException(ClassWorld world, String id) {
+ super(world, id);
+ this.id = id;
+ }
+
+ // ------------------------------------------------------------
+ // Instance methods
+ // ------------------------------------------------------------
+
+ /**
+ * Retrieve the duplicate realm id.
+ *
+ * @return The id.
+ */
+ public String getId() {
+ return this.id;
+ }
+
+ /**
+ * Retrieve the world.
+ *
+ * @return The world.
+ */
+ public ClassWorld getWorld() {
+ return (ClassWorld) super.getWorld();
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/Entry.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/Entry.java
new file mode 100644
index 000000000000..316c3c7ff4cf
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/Entry.java
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.realm;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+/**
+ * Import description entry.
+ *
+ * @author bob mcwhirter
+ */
+class Entry implements Comparable {
+
+ final ClassLoader classLoader;
+
+ final String pkgName;
+
+ Entry(ClassLoader realm, String pkgName) {
+ this.classLoader = realm;
+
+ this.pkgName = pkgName;
+ }
+
+ // ------------------------------------------------------------
+ // Instance methods
+ // ------------------------------------------------------------
+
+ /**
+ * Retrieve the class loader.
+ *
+ * @return The class loader.
+ */
+ ClassLoader getClassLoader() {
+ return this.classLoader;
+ }
+
+ /**
+ * Retrieve the package name.
+ *
+ * @return The package name.
+ */
+ String getPackageName() {
+ return this.pkgName;
+ }
+
+ /**
+ * Determine if the class/resource name matches the package
+ * described by this entry.
+ *
+ * @param name The class or resource name to test, must not be null.
+ * @return true if this entry matches the
+ * classname, otherwise false.
+ */
+ boolean matches(String name) {
+ String pkg = getPackageName();
+
+ if (pkg.endsWith(".*")) {
+ String pkgName;
+
+ if (name.indexOf('/') < 0) {
+ // a binary class name, e.g. java.lang.Object
+
+ int index = name.lastIndexOf('.');
+ pkgName = (index < 0) ? "" : name.substring(0, index);
+ } else {
+ // a resource name, e.g. java/lang/Object.class
+
+ int index = name.lastIndexOf('/');
+ pkgName = (index < 0) ? "" : name.substring(0, index).replace('/', '.');
+ }
+
+ return pkgName.length() == pkg.length() - 2 && pkg.regionMatches(0, pkgName, 0, pkgName.length());
+ } else if (pkg.length() > 0) {
+ if (name.indexOf('/') < 0) {
+ // a binary class name, e.g. java.lang.Object
+
+ if (name.startsWith(pkg)) {
+ if (name.length() == pkg.length()) {
+ // exact match of class name
+ return true;
+ } else if (name.charAt(pkg.length()) == '.') {
+ // prefix match of package name
+ return true;
+ } else if (name.charAt(pkg.length()) == '$') {
+ // prefix match of enclosing type
+ return true;
+ }
+ }
+ } else {
+ // a resource name, e.g. java/lang/Object.class
+
+ if (name.equals(pkg)) {
+ // exact match of resource name
+ return true;
+ }
+
+ pkg = pkg.replace('.', '/');
+
+ if (name.startsWith(pkg) && name.length() > pkg.length()) {
+ if (name.charAt(pkg.length()) == '/') {
+ // prefix match of package directory
+ return true;
+ } else if (name.charAt(pkg.length()) == '$') {
+ // prefix match of nested class file
+ return true;
+ } else if (name.length() == pkg.length() + 6 && name.endsWith(".class")) {
+ // exact match of class file
+ return true;
+ }
+ }
+ }
+
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ // java.lang.Comparable
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ /**
+ * Compare this entry to another for relative ordering.
+ *
+ *
+ * The natural ordering of Entry objects is reverse-alphabetical
+ * based upon package name.
+ *
+ *
+ * @param that The object to compare.
+ * @return -1 if this object sorts before that object, 0
+ * if they are equal, or 1 if this object sorts
+ * after that object.
+ */
+ public int compareTo(Entry that) {
+ // We are reverse sorting this list, so that
+ // we get longer matches first:
+ //
+ // com.werken.foo.bar
+ // com.werken.foo
+ // com.werken
+
+ return -(getPackageName().compareTo(that.getPackageName()));
+ }
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ // java.lang.Object
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ /**
+ * Test this entry for equality to another.
+ *
+ *
+ * Consistent with {@link #compareTo}, this method tests
+ * for equality purely on the package name.
+ *
+ *
+ * @param thatObj The object to compare
+ * @return true if the two objects are
+ * semantically equivalent, otherwise false.
+ */
+ public boolean equals(Object thatObj) {
+ Entry that = (Entry) thatObj;
+
+ return getPackageName().equals(that.getPackageName());
+ }
+
+ /**
+ *
+ * Consistent with {@link #equals}, this method creates a hashCode
+ * based on the packagename.
+ *
+ */
+ public int hashCode() {
+ return getPackageName().hashCode();
+ }
+
+ public String toString() {
+ return "Entry[import " + getPackageName() + " from realm " + getClassLoader() + "]";
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/FilteredClassRealm.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/FilteredClassRealm.java
new file mode 100644
index 000000000000..dfb2eefa9166
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/FilteredClassRealm.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.realm;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.function.Predicate;
+
+import org.codehaus.plexus.classworlds.ClassWorld;
+
+/**
+ * Similar to {@link ClassRealm} but only exposing some resources of the underlying URL.
+ * Only supposed to be called from {@link ClassWorld}.
+ */
+public class FilteredClassRealm extends ClassRealm {
+ private final Predicate filter;
+
+ /**
+ * Creates a new class realm.
+ *
+ * @param filter a predicate to apply to each resource name to determine if it should be loaded through this class loader
+ * @param world The class world this realm belongs to, must not be null.
+ * @param id The identifier for this realm, must not be null.
+ * @param baseClassLoader The base class loader for this realm, may be null to use the bootstrap class
+ * loader.
+ */
+ public FilteredClassRealm(Predicate filter, ClassWorld world, String id, ClassLoader baseClassLoader) {
+ super(world, id, baseClassLoader);
+ this.filter = filter;
+ }
+
+ @Override
+ protected Class> findClassInternal(String name) throws ClassNotFoundException {
+ String resourceName = name.replace('.', '/').concat(".class");
+ if (!filter.test(resourceName)) {
+ throw new ClassNotFoundException(name);
+ }
+ return super.findClassInternal(name);
+ }
+
+ @Override
+ public URL findResource(String name) {
+ if (!filter.test(name)) {
+ return null;
+ }
+ return super.findResource(name);
+ }
+
+ @Override
+ public Enumeration findResources(String name) throws IOException {
+ if (!filter.test(name)) {
+ return Collections.emptyEnumeration();
+ }
+ return super.findResources(name);
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/NoSuchRealmException.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/NoSuchRealmException.java
new file mode 100644
index 000000000000..d98252ccbcc5
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/NoSuchRealmException.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.realm;
+
+import org.codehaus.plexus.classworlds.ClassWorld;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+/**
+ * Indicates an attempt to retrieve a ClassRealm from a
+ * ClassWorld with an invalid id.
+ *
+ * @author bob mcwhirter
+ */
+public class NoSuchRealmException extends org.apache.maven.api.classworlds.NoSuchRealmException {
+ // ------------------------------------------------------------
+ // Instance members
+ // ------------------------------------------------------------
+
+ /**
+ * The realm id.
+ */
+ private String id;
+
+ // ------------------------------------------------------------
+ // Constructors
+ // ------------------------------------------------------------
+
+ /**
+ * Construct.
+ *
+ * @param world The world.
+ * @param id The realm id.
+ */
+ public NoSuchRealmException(ClassWorld world, String id) {
+ super(world, id);
+ this.id = id;
+ }
+
+ // ------------------------------------------------------------
+ // Instance methods
+ // ------------------------------------------------------------
+
+ /**
+ * Retrieve the invalid realm id.
+ *
+ * @return The id.
+ */
+ public String getId() {
+ return this.id;
+ }
+
+ /**
+ * Retrieve the world.
+ *
+ * @return The world.
+ */
+ public ClassWorld getWorld() {
+ return (ClassWorld) super.getWorld();
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/AbstractStrategy.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/AbstractStrategy.java
new file mode 100644
index 000000000000..91af5b3cfdd9
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/AbstractStrategy.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.strategy;
+
+import java.net.URL;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.LinkedHashSet;
+
+import org.codehaus.plexus.classworlds.UrlUtils;
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+/**
+ * @author Jason van Zyl
+ */
+public abstract class AbstractStrategy implements Strategy {
+
+ protected ClassRealm realm;
+
+ public AbstractStrategy(ClassRealm realm) {
+ this.realm = realm;
+ }
+
+ protected String getNormalizedResource(String name) {
+ return UrlUtils.normalizeUrlPath(name);
+ }
+
+ protected Enumeration combineResources(Enumeration en1, Enumeration en2, Enumeration en3) {
+ Collection urls = new LinkedHashSet<>();
+
+ addAll(urls, en1);
+ addAll(urls, en2);
+ addAll(urls, en3);
+
+ return Collections.enumeration(urls);
+ }
+
+ private void addAll(Collection target, Enumeration en) {
+ if (en != null) {
+ while (en.hasMoreElements()) {
+ target.add(en.nextElement());
+ }
+ }
+ }
+
+ public ClassRealm getRealm() {
+ return realm;
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/OsgiBundleStrategy.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/OsgiBundleStrategy.java
new file mode 100644
index 000000000000..24b690d7aaac
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/OsgiBundleStrategy.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.strategy;
+
+/*
+ * Copyright 2001-2010 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+
+public class OsgiBundleStrategy extends AbstractStrategy {
+
+ // java.* from parent
+ // imported packages [Import-Package header with explicit constraints on the exporter]
+ // requires bundle [Required-Bundle]
+ // self [Bundle-Classpath header]
+ // attached fragments
+ //
+ // We need to trya and be OSGi r4 compliant in the loading of all the bundles so that we can try to
+ // load eclipse without requiring equinox. Or any other OSGi container for that matter.
+ public OsgiBundleStrategy(ClassRealm realm) {
+ super(realm);
+ }
+
+ public Class> loadClass(String name) throws ClassNotFoundException {
+ Class> clazz = realm.loadClassFromImport(name);
+
+ if (clazz == null) {
+ clazz = realm.loadClassFromSelf(name);
+
+ if (clazz == null) {
+ clazz = realm.loadClassFromParent(name);
+
+ if (clazz == null) {
+ throw new ClassNotFoundException(name);
+ }
+ }
+ }
+
+ return clazz;
+ }
+
+ public URL getResource(String name) {
+ URL resource = realm.loadResourceFromImport(name);
+
+ if (resource == null) {
+ resource = realm.loadResourceFromSelf(name);
+
+ if (resource == null) {
+ resource = realm.loadResourceFromParent(name);
+ }
+ }
+
+ return resource;
+ }
+
+ public Enumeration getResources(String name) throws IOException {
+ Enumeration imports = realm.loadResourcesFromImport(name);
+ Enumeration self = realm.loadResourcesFromSelf(name);
+ Enumeration parent = realm.loadResourcesFromParent(name);
+
+ return combineResources(imports, self, parent);
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/ParentFirstStrategy.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/ParentFirstStrategy.java
new file mode 100644
index 000000000000..fc571f3ed88a
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/ParentFirstStrategy.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.strategy;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+
+/**
+ * @author Jason van Zyl
+ */
+public class ParentFirstStrategy extends AbstractStrategy {
+
+ public ParentFirstStrategy(ClassRealm realm) {
+ super(realm);
+ }
+
+ public Class> loadClass(String name) throws ClassNotFoundException {
+ Class> clazz = realm.loadClassFromImport(name);
+
+ if (clazz == null) {
+ clazz = realm.loadClassFromParent(name);
+
+ if (clazz == null) {
+ clazz = realm.loadClassFromSelf(name);
+
+ if (clazz == null) {
+ throw new ClassNotFoundException(name);
+ }
+ }
+ }
+
+ return clazz;
+ }
+
+ public URL getResource(String name) {
+ URL resource = realm.loadResourceFromImport(name);
+
+ if (resource == null) {
+ resource = realm.loadResourceFromParent(name);
+
+ if (resource == null) {
+ resource = realm.loadResourceFromSelf(name);
+ }
+ }
+
+ return resource;
+ }
+
+ public Enumeration getResources(String name) throws IOException {
+ Enumeration imports = realm.loadResourcesFromImport(name);
+ Enumeration parent = realm.loadResourcesFromParent(name);
+ Enumeration self = realm.loadResourcesFromSelf(name);
+
+ return combineResources(imports, parent, self);
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/SelfFirstStrategy.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/SelfFirstStrategy.java
new file mode 100644
index 000000000000..52351e837aca
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/SelfFirstStrategy.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.strategy;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+
+/**
+ * @author Jason van Zyl
+ */
+public class SelfFirstStrategy extends AbstractStrategy {
+
+ public SelfFirstStrategy(ClassRealm realm) {
+ super(realm);
+ }
+
+ public Class> loadClass(String name) throws ClassNotFoundException {
+ Class> clazz = realm.loadClassFromImport(name);
+
+ if (clazz == null) {
+ clazz = realm.loadClassFromSelf(name);
+
+ if (clazz == null) {
+ clazz = realm.loadClassFromParent(name);
+
+ if (clazz == null) {
+ throw new ClassNotFoundException(name);
+ }
+ }
+ }
+
+ return clazz;
+ }
+
+ public URL getResource(String name) {
+ URL resource = realm.loadResourceFromImport(name);
+
+ if (resource == null) {
+ resource = realm.loadResourceFromSelf(name);
+
+ if (resource == null) {
+ resource = realm.loadResourceFromParent(name);
+ }
+ }
+
+ return resource;
+ }
+
+ public Enumeration getResources(String name) throws IOException {
+ Enumeration imports = realm.loadResourcesFromImport(name);
+ Enumeration self = realm.loadResourcesFromSelf(name);
+ Enumeration parent = realm.loadResourcesFromParent(name);
+
+ return combineResources(imports, self, parent);
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/Strategy.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/Strategy.java
new file mode 100644
index 000000000000..63c17092778a
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/Strategy.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.strategy;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+
+/**
+ * A strategy is a class for defining how classes and resources are located
+ * in classworlds.
+ */
+public interface Strategy extends org.apache.maven.api.classworlds.Strategy {
+
+ Class> loadClass(String name) throws ClassNotFoundException;
+
+ URL getResource(String name);
+
+ Enumeration getResources(String name) throws IOException;
+
+ ClassRealm getRealm();
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/StrategyFactory.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/StrategyFactory.java
new file mode 100644
index 000000000000..26c8f9237c17
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/StrategyFactory.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.strategy;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+
+/**
+ * StrategyFactory loads a strategy, either default or from a given hint.
+ */
+public class StrategyFactory {
+
+ public static Strategy getStrategy(ClassRealm realm) {
+ return getStrategy(realm, "default");
+ }
+
+ public static Strategy getStrategy(ClassRealm realm, String hint) {
+ // TODO: Here we shall check hint to load non-default strategies
+
+ return new SelfFirstStrategy(realm);
+ }
+}
diff --git a/impl/maven-classworlds/src/test/java/org/apache/maven/api/classworlds/ApiTest.java b/impl/maven-classworlds/src/test/java/org/apache/maven/api/classworlds/ApiTest.java
new file mode 100644
index 000000000000..65d8c1766e9d
--- /dev/null
+++ b/impl/maven-classworlds/src/test/java/org/apache/maven/api/classworlds/ApiTest.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.classworlds;
+
+import java.net.URL;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * Test to demonstrate the Maven 4 ClassWorlds API.
+ */
+class ApiTest {
+
+ @Test
+ void testApiUsage() throws Exception {
+ // Create a ClassWorld using the implementation
+ ClassWorld world = new org.codehaus.plexus.classworlds.ClassWorld();
+
+ // Test basic API methods
+ assertNotNull(world);
+
+ // Create a realm
+ ClassRealm realm = world.newRealm("test-realm");
+ assertNotNull(realm);
+ assertEquals("test-realm", realm.getId());
+ assertSame(world, realm.getWorld());
+
+ // Test getClassLoader() method
+ ClassLoader classLoader = realm.getClassLoader();
+ assertNotNull(classLoader);
+ assertSame(realm, classLoader); // Should return the realm itself
+
+ // Test URL operations
+ URL url = new URL("file:///tmp/test.jar");
+ realm.addURL(url);
+ URL[] urls = realm.getURLs();
+ assertTrue(urls.length > 0);
+
+ // Test import operations
+ ClassRealm importRealm = world.newRealm("import-realm");
+ realm.importFrom(importRealm.getId(), "org.example");
+
+ // Test parent operations
+ realm.setParentClassLoader(ClassLoader.getSystemClassLoader());
+ assertNotNull(realm.getParentClassLoader());
+
+ // Test strategy
+ Strategy strategy = realm.getStrategy();
+ assertNotNull(strategy);
+ assertSame(realm, strategy.getRealm());
+
+ // Test listener
+ TestListener listener = new TestListener();
+ world.addListener(listener);
+
+ ClassRealm newRealm = world.newRealm("listener-test");
+ assertTrue(listener.realmCreated);
+ assertEquals(newRealm, listener.createdRealm);
+
+ world.disposeRealm("listener-test");
+ assertTrue(listener.realmDisposed);
+ assertEquals(newRealm, listener.disposedRealm);
+
+ // Test exception handling
+ try {
+ world.newRealm("test-realm"); // Duplicate
+ fail("Should have thrown DuplicateRealmException");
+ } catch (DuplicateRealmException e) {
+ assertEquals("test-realm", e.getId());
+ assertSame(world, e.getWorld());
+ }
+
+ try {
+ world.getRealm("non-existent");
+ fail("Should have thrown NoSuchRealmException");
+ } catch (NoSuchRealmException e) {
+ assertEquals("non-existent", e.getId());
+ assertSame(world, e.getWorld());
+ }
+
+ // Test null-safe operations
+ assertNull(world.getClassRealm("non-existent"));
+
+ world.close();
+ }
+
+ private static class TestListener implements ClassWorldListener {
+ boolean realmCreated = false;
+ boolean realmDisposed = false;
+ ClassRealm createdRealm;
+ ClassRealm disposedRealm;
+
+ @Override
+ public void realmCreated(ClassRealm realm) {
+ this.realmCreated = true;
+ this.createdRealm = realm;
+ }
+
+ @Override
+ public void realmDisposed(ClassRealm realm) {
+ this.realmDisposed = true;
+ this.disposedRealm = realm;
+ }
+ }
+}
diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/AbstractClassWorldsTestCase.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/AbstractClassWorldsTestCase.java
new file mode 100644
index 000000000000..8f4c3699a44c
--- /dev/null
+++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/AbstractClassWorldsTestCase.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.net.URL;
+
+/**
+ * @author Jason van Zyl
+ */
+public abstract class AbstractClassWorldsTestCase {
+ protected URL getJarUrl(String jarName) {
+ return TestUtil.getTestResourceUrl(jarName);
+ }
+}
diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/ClassView.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/ClassView.java
new file mode 100644
index 000000000000..cffd6a1a1c1f
--- /dev/null
+++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/ClassView.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+public class ClassView {
+ /**
+ * * Formats Class information for debug output purposes.
+ * *
+ * * @param clz the Class to print information for
+ * *
+ * * @return a String describing the Class in detail
+ */
+ public static String toString(Class> clz) {
+ if (clz.isPrimitive()) {
+ return clz.toString();
+ } else if (clz.isArray()) {
+ return "Array of " + toString(clz.getComponentType());
+ } else if (clz.isInterface()) {
+ return toInterfaceString(clz, "");
+ } else {
+ return toClassString(clz, "");
+ }
+ }
+
+ /**
+ * * Formats Class information for debug output purposes.
+ * *
+ * * @param clz the Class to print information for
+ * * @param sIndent the indentation to precede each line of output
+ * *
+ * * @return a String describing the Class in detail
+ */
+ private static String toClassString(Class> clz, String sIndent) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(sIndent)
+ .append("Class ")
+ .append(clz.getName())
+ .append(" (")
+ .append(toString(clz.getClassLoader()))
+ .append(')');
+
+ sIndent += " ";
+
+ Class>[] aclz = clz.getInterfaces();
+ for (Class> aClass : aclz) {
+ sb.append('\n').append(toInterfaceString(aClass, sIndent));
+ }
+
+ clz = clz.getSuperclass();
+ if (clz != null) {
+ sb.append('\n').append(toClassString(clz, sIndent));
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * * Formats interface information for debug output purposes.
+ * *
+ * * @param clz the interface Class to print information for
+ * * @param sIndent the indentation to precede each line of output
+ * *
+ * * @return a String describing the interface Class in detail
+ */
+ private static String toInterfaceString(Class> clz, String sIndent) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(sIndent)
+ .append("Interface ")
+ .append(clz.getName())
+ .append(" (")
+ .append(toString(clz.getClassLoader()))
+ .append(')');
+
+ Class>[] aclz = clz.getInterfaces();
+ for (Class> aClass : aclz) {
+ clz = aClass;
+
+ sb.append('\n').append(toInterfaceString(clz, sIndent + " "));
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * * Format a description for the specified ClassLoader object.
+ * *
+ * * @param loader the ClassLoader instance (or null)
+ * *
+ * * @return a String description of the ClassLoader
+ */
+ private static String toString(ClassLoader loader) {
+ if (loader == null) {
+ return "System ClassLoader";
+ }
+
+ return "ClassLoader class=" + loader.getClass().getName() + ", hashCode=" + loader.hashCode();
+ }
+}
diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/ClassWorldTest.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/ClassWorldTest.java
new file mode 100644
index 000000000000..75f6ebd39965
--- /dev/null
+++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/ClassWorldTest.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Enumeration;
+
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
+import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+class ClassWorldTest extends AbstractClassWorldsTestCase {
+ private ClassWorld world;
+
+ @BeforeEach
+ public void setUp() {
+ this.world = new ClassWorld();
+ }
+
+ @AfterEach
+ public void tearDown() {
+ this.world = null;
+ }
+
+ @Test
+ void testEmpty() {
+ assertTrue(this.world.getRealms().isEmpty());
+ }
+
+ @Test
+ void testNewRealm() throws Exception {
+ ClassRealm realm = this.world.newRealm("foo");
+
+ assertNotNull(realm);
+ }
+
+ @Test
+ void testGetRealm() throws Exception {
+ ClassRealm realm = this.world.newRealm("foo");
+
+ assertSame(realm, this.world.getRealm("foo"));
+ }
+
+ @Test
+ void testNewRealmDuplicate() {
+ try {
+ this.world.newRealm("foo");
+ this.world.newRealm("foo");
+
+ fail("throw DuplicateRealmException");
+ } catch (DuplicateRealmException e) {
+ // expected and correct
+
+ assertSame(this.world, e.getWorld());
+
+ assertEquals("foo", e.getId());
+ }
+ }
+
+ @Test
+ void testGetRealmNoSuch() {
+ try {
+ this.world.getRealm("foo");
+ fail("throw NoSuchRealmException");
+ } catch (NoSuchRealmException e) {
+ // expected and correct
+
+ assertSame(this.world, e.getWorld());
+
+ assertEquals("foo", e.getId());
+ }
+ }
+
+ @Test
+ void testGetRealms() throws Exception {
+ assertTrue(this.world.getRealms().isEmpty());
+
+ ClassRealm foo = this.world.newRealm("foo");
+
+ assertEquals(1, this.world.getRealms().size());
+
+ assertTrue(this.world.getRealms().contains(foo));
+
+ ClassRealm bar = this.world.newRealm("bar");
+
+ assertEquals(2, this.world.getRealms().size());
+
+ assertTrue(this.world.getRealms().contains(bar));
+ }
+
+ @Test
+ void testPLX334() throws Exception {
+ ClassLoader loader = new URLClassLoader(new URL[] {getJarUrl("component1-1.0.jar")});
+ world.newRealm("netbeans", loader);
+ ClassRealm plexus = world.newRealm("plexus");
+ plexus.importFrom("netbeans", "META-INF/plexus");
+ plexus.importFrom("netbeans", "org.codehaus.plexus");
+ Enumeration e = plexus.getResources("META-INF/plexus/components.xml");
+ assertNotNull(e);
+ int resourceCount = 0;
+ for (Enumeration resources = e; resources.hasMoreElements(); ) {
+ URL obj = resources.nextElement();
+ assertTrue(obj.getPath().contains("/component1-1.0.jar!/META-INF/plexus/components.xml"));
+ resourceCount++;
+ }
+ // assertEquals( 2, resourceCount );
+ // for some reason surefire-plugin 2.3 returned 2 items there:
+ // for example:
+ // jar:file:/home/mkleint/.m2/repository/org/codehaus/plexus/plexus-archiver/1.0-alpha-7/plexus-archiver-1.0-alpha-7.jar!/META-INF/plexus/components.xml
+ // jar:file:/home/mkleint/src/plexus-trunk/plexus-classworlds/src/test-jars/component1-1.0.jar!/META-INF/plexus/components.xml
+ // However only 1 is correct, which is actually returned by the 2.4 surefire-plugin
+
+ assertEquals(1, resourceCount);
+ Class> c = plexus.loadClass("org.codehaus.plexus.Component1");
+ assertNotNull(c);
+ }
+}
diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/TestUtil.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/TestUtil.java
new file mode 100644
index 000000000000..e1dca7d81ff2
--- /dev/null
+++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/TestUtil.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * @author Ben Walding
+ */
+public class TestUtil {
+
+ public static URL getTestResourceUrl(String resourceName) {
+ File baseDir = new File(getBasedir());
+
+ File testDir = new File(baseDir, "src/test/test-data");
+
+ File resourceFile = new File(testDir, resourceName);
+
+ try {
+ return resourceFile.toURI().toURL();
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static String getBasedir() {
+ String basedir = System.getProperty("basedir");
+
+ /* do our best if we are not running from surefire */
+ if (basedir == null || basedir.length() <= 0) {
+ basedir = (new File("")).getAbsolutePath();
+ }
+
+ return basedir;
+ }
+}
diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/UrlUtilsTest.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/UrlUtilsTest.java
new file mode 100644
index 000000000000..cdecf0a44bce
--- /dev/null
+++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/UrlUtilsTest.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class UrlUtilsTest {
+
+ @Test
+ public void testNormalizeUrlPath() {
+ assertEquals("org/codehaus/Test.class", UrlUtils.normalizeUrlPath("org/codehaus/Test.class"));
+ assertEquals("org/Test.class", UrlUtils.normalizeUrlPath("org/codehaus/../Test.class"));
+ assertEquals("../../some.jar/org/Test.class", UrlUtils.normalizeUrlPath("../../some.jar/org/Test.class"));
+ }
+}
diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParserTest.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParserTest.java
new file mode 100644
index 000000000000..69c83e09d2bf
--- /dev/null
+++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParserTest.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.launcher;
+
+import org.codehaus.plexus.classworlds.AbstractClassWorldsTestCase;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+class ConfigurationParserTest extends AbstractClassWorldsTestCase {
+
+ ConfigurationParser configurator = new ConfigurationParser(null, System.getProperties());
+
+ @Test
+ void testFilterUnterminated() {
+ try {
+ this.configurator.filter("${cheese");
+ fail("throw ConfigurationException");
+ } catch (ConfigurationException e) {
+ // expected and correct
+ assertTrue(e.getMessage().startsWith("Unterminated"));
+ }
+ }
+
+ @Test
+ void testFilterSolitary() throws Exception {
+ System.setProperty("classworlds.test.prop", "test prop value");
+
+ String result = this.configurator.filter("${classworlds.test.prop}");
+
+ assertEquals("test prop value", result);
+ }
+
+ @Test
+ void testFilterAtStart() throws Exception {
+ System.setProperty("classworlds.test.prop", "test prop value");
+
+ String result = this.configurator.filter("${classworlds.test.prop}cheese");
+
+ assertEquals("test prop valuecheese", result);
+ }
+
+ @Test
+ void testFilterAtEnd() throws Exception {
+ System.setProperty("classworlds.test.prop", "test prop value");
+
+ String result = this.configurator.filter("cheese${classworlds.test.prop}");
+
+ assertEquals("cheesetest prop value", result);
+ }
+
+ @Test
+ void testFilterMultiple() throws Exception {
+ System.setProperty("classworlds.test.prop.one", "test prop value one");
+
+ System.setProperty("classworlds.test.prop.two", "test prop value two");
+
+ String result =
+ this.configurator.filter("I like ${classworlds.test.prop.one} and ${classworlds.test.prop.two} a lot");
+
+ assertEquals("I like test prop value one and test prop value two a lot", result);
+ }
+
+ @Test
+ void testFilterNonExistent() {
+ try {
+ this.configurator.filter("${gollygeewillikers}");
+ fail("throw ConfigurationException");
+ } catch (ConfigurationException e) {
+ // expected and correct
+ assertTrue(e.getMessage().startsWith("No such property"));
+ }
+ }
+
+ @Test
+ void testFilterInMiddle() throws Exception {
+ System.setProperty("classworlds.test.prop", "test prop value");
+
+ String result = this.configurator.filter("cheese${classworlds.test.prop}toast");
+
+ assertEquals("cheesetest prop valuetoast", result);
+ }
+}
diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfiguratorTest.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfiguratorTest.java
new file mode 100644
index 000000000000..2cfcd0bb8e95
--- /dev/null
+++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfiguratorTest.java
@@ -0,0 +1,365 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.launcher;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.net.URL;
+import java.util.Collection;
+
+import org.codehaus.plexus.classworlds.AbstractClassWorldsTestCase;
+import org.codehaus.plexus.classworlds.ClassWorld;
+import org.codehaus.plexus.classworlds.TestUtil;
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+class ConfiguratorTest extends AbstractClassWorldsTestCase {
+ private Launcher launcher;
+ private Configurator configurator;
+
+ @BeforeEach
+ public void setUp() {
+ this.launcher = new Launcher();
+ this.configurator = new Configurator(this.launcher);
+ }
+
+ @AfterEach
+ public void tearDown() {
+ this.launcher = null;
+ this.configurator = null;
+ System.getProperties().remove("set.using.existent");
+ System.getProperties().remove("set.using.default");
+ System.getProperties().remove("set.using.nonexistent");
+ System.getProperties().remove("set.using.nonexistent.default");
+ System.getProperties().remove("set.using.missing");
+ System.getProperties().remove("set.using.filtered.default");
+ }
+
+ @Test
+ void testConfigureNonexistent() throws Exception {
+ try {
+ this.configurator.configure(getConfigPath("notfound.conf"));
+ fail("throw FileNotFoundException");
+ } catch (FileNotFoundException e) {
+ // expected and correct
+ }
+ }
+
+ @Test
+ void testConfigureDuplicateMain() throws Exception {
+ try {
+ this.configurator.configure(getConfigPath("dupe-main.conf"));
+ fail("throw ConfigurationException");
+ } catch (ConfigurationException e) {
+ // expected and correct
+ assertTrue(e.getMessage().startsWith("Duplicate main"));
+ }
+ }
+
+ @Test
+ void testConfigureDuplicateRealm() throws Exception {
+ try {
+ this.configurator.configure(getConfigPath("dupe-realm.conf"));
+ fail("throw DuplicateRealmException");
+ } catch (DuplicateRealmException e) {
+ // expected and correct
+ assertEquals("dupe.realm", e.getId());
+ }
+ }
+
+ @Test
+ void testConfigureEarlyImport() throws Exception {
+ try {
+ this.configurator.configure(getConfigPath("early-import.conf"));
+ fail("throw ConfigurationException");
+ } catch (ConfigurationException e) {
+ // expected and correct
+ assertTrue(e.getMessage().startsWith("Unhandled import"));
+ }
+ }
+
+ @Test
+ void testConfigureRealmSyntax() throws Exception {
+ try {
+ this.configurator.configure(getConfigPath("realm-syntax.conf"));
+ fail("throw ConfigurationException");
+ } catch (ConfigurationException e) {
+ // expected and correct
+ assertTrue(e.getMessage().startsWith("Invalid realm"));
+ }
+ }
+
+ @Test
+ void testConfigureValid() throws Exception {
+ this.configurator.configure(getConfigPath("valid.conf"));
+
+ assertEquals("org.apache.maven.app.App", this.launcher.getMainClassName());
+
+ assertEquals("maven", this.launcher.getMainRealmName());
+
+ ClassWorld world = this.launcher.getWorld();
+
+ Collection realms = world.getRealms();
+
+ assertEquals(4, realms.size());
+
+ assertNotNull(world.getRealm("ant"));
+ assertNotNull(world.getRealm("maven"));
+ assertNotNull(world.getRealm("xml"));
+
+ ClassRealm antRealm = world.getRealm("ant");
+ ClassRealm mavenRealm = world.getRealm("maven");
+ ClassRealm xmlRealm = world.getRealm("xml");
+ ClassRealm globRealm = world.getRealm("glob");
+
+ assertSame(null, antRealm.getImportClassLoader("org.apache.tools.Ant"));
+
+ // Ant has dependency to xerces:xercesImpl (test)
+ assertSame(null, antRealm.getImportClassLoader("org.xml.sax.SAXException"));
+
+ assertSame(xmlRealm, antRealm.getImportClassLoader("jakarta.xml.bind.JAXBException"));
+
+ assertSame(null, mavenRealm.getImportClassLoader("org.apache.maven.app.App"));
+
+ assertSame(xmlRealm, mavenRealm.getImportClassLoader("jakarta.xml.bind.JAXBException"));
+
+ URL[] urls = globRealm.getURLs();
+
+ String basedir = TestUtil.getBasedir();
+ assertArrayContains(
+ urls, new File(basedir, "src/test/test-data/nested.jar").toURI().toURL());
+ assertArrayContains(
+ urls, new File(basedir, "src/test/test-data/a.jar").toURI().toURL());
+ assertArrayContains(
+ urls, new File(basedir, "src/test/test-data/b.jar").toURI().toURL());
+ assertArrayContains(
+ urls, new File(basedir, "src/test/test-data/c.jar").toURI().toURL());
+ }
+
+ @Test
+ void testConfigureOptionallyNonExistent() throws Exception {
+ this.configurator.configure(getConfigPath("optionally-nonexistent.conf"));
+
+ assertEquals("org.apache.maven.app.App", this.launcher.getMainClassName());
+
+ assertEquals("opt", this.launcher.getMainRealmName());
+
+ ClassWorld world = this.launcher.getWorld();
+
+ Collection realms = world.getRealms();
+
+ assertEquals(1, realms.size());
+
+ assertNotNull(world.getRealm("opt"));
+
+ ClassRealm optRealm = world.getRealm("opt");
+
+ URL[] urls = optRealm.getURLs();
+
+ assertEquals(0, urls.length, "no urls");
+ }
+
+ @Test
+ void testConfigureOptionallyExistent() throws Exception {
+ this.configurator.configure(getConfigPath("optionally-existent.conf"));
+
+ assertEquals("org.apache.maven.app.App", this.launcher.getMainClassName());
+
+ assertEquals("opt", this.launcher.getMainRealmName());
+
+ ClassWorld world = this.launcher.getWorld();
+
+ Collection realms = world.getRealms();
+
+ assertEquals(1, realms.size());
+
+ assertNotNull(world.getRealm("opt"));
+
+ ClassRealm optRealm = world.getRealm("opt");
+
+ URL[] urls = optRealm.getURLs();
+
+ assertEquals(1, urls.length, "one url");
+
+ assertSame(null, optRealm.getImportClassLoader("jakarta.xml.bind.JAXBException"));
+ }
+
+ @Test
+ void testConfigureUnhandled() throws Exception {
+ try {
+ this.configurator.configure(getConfigPath("unhandled.conf"));
+ fail("throw ConfigurationException");
+ } catch (ConfigurationException e) {
+ // expected and correct
+ assertTrue(e.getMessage().startsWith("Unhandled configuration"));
+ }
+ }
+
+ @Test
+ void testSetUsingExistent() throws Exception {
+ assertNull(System.getProperty("set.using.existent"));
+
+ this.configurator.configure(getConfigPath("set-using-existent.conf"));
+
+ assertEquals("testSetUsingExistent", System.getProperty("set.using.existent"));
+ }
+
+ @Test
+ void testSetUsingNonExistent() throws Exception {
+ assertNull(System.getProperty("set.using.nonexistent"));
+
+ this.configurator.configure(getConfigPath("set-using-nonexistent.conf"));
+
+ assertNull(System.getProperty("set.using.nonexistent"));
+ }
+
+ @Test
+ void testSetUsingNonExistentDefault() throws Exception {
+ assertNull(System.getProperty("set.using.nonexistent.default"));
+
+ this.configurator.configure(getConfigPath("set-using-nonexistent.conf"));
+
+ assertEquals("testSetUsingNonExistentDefault", System.getProperty("set.using.nonexistent.default"));
+ }
+
+ @Test
+ void testSetUsingNonExistentOverride() throws Exception {
+ assertNull(System.getProperty("set.using.default"));
+ System.setProperty("set.using.default", "testSetUsingNonExistentOverride");
+
+ this.configurator.configure(getConfigPath("set-using-nonexistent.conf"));
+
+ assertEquals("testSetUsingNonExistentOverride", System.getProperty("set.using.default"));
+ }
+
+ @Test
+ void testSetUsingExistentOverride() throws Exception {
+ assertNull(System.getProperty("set.using.existent"));
+ System.setProperty("set.using.existent", "testSetUsingExistentOverride");
+
+ this.configurator.configure(getConfigPath("set-using-existent.conf"));
+
+ assertEquals("testSetUsingExistentOverride", System.getProperty("set.using.existent"));
+ }
+
+ @Test
+ void testSetUsingExistentDefault() throws Exception {
+ assertNull(System.getProperty("set.using.default"));
+
+ this.configurator.configure(getConfigPath("set-using-existent.conf"));
+
+ assertEquals("testSetUsingExistentDefault", System.getProperty("set.using.default"));
+ }
+
+ @Test
+ void testSetUsingMissingDefault() throws Exception {
+ assertNull(System.getProperty("set.using.missing"));
+
+ this.configurator.configure(getConfigPath("set-using-missing.conf"));
+
+ assertEquals("testSetUsingMissingDefault", System.getProperty("set.using.missing"));
+ }
+
+ @Test
+ void testSetUsingMissingOverride() throws Exception {
+ assertNull(System.getProperty("set.using.missing"));
+ System.setProperty("set.using.missing", "testSetUsingMissingOverride");
+
+ this.configurator.configure(getConfigPath("set-using-missing.conf"));
+
+ assertEquals("testSetUsingMissingOverride", System.getProperty("set.using.missing"));
+ }
+
+ @Test
+ void testSetUsingFilteredDefault() throws Exception {
+ assertNull(System.getProperty("set.using.filtered.default"));
+
+ this.configurator.configure(getConfigPath("set-using-missing.conf"));
+
+ assertEquals(System.getProperty("user.home") + "/m2", System.getProperty("set.using.filtered.default"));
+ }
+
+ @Test
+ void testFromFromFrom() throws Exception {
+ this.configurator.configure(getConfigPath("valid-from-from-from.conf"));
+
+ assertEquals("com.from.from.from.Main", this.launcher.getMainClassName());
+
+ assertEquals("from", this.launcher.getMainRealmName());
+
+ ClassWorld world = this.launcher.getWorld();
+
+ ClassRealm antRealm = world.getRealm("ant");
+ Collection antImportRealms = antRealm.getImportRealms();
+ assertEquals(1, antImportRealms.size());
+ assertEquals("from", antImportRealms.stream().findFirst().get().getId());
+
+ ClassRealm mavenRealm = world.getRealm("maven");
+ Collection mavenImportRealms = mavenRealm.getImportRealms();
+ assertEquals(1, mavenImportRealms.size());
+ assertEquals("from", mavenImportRealms.stream().findFirst().get().getId());
+
+ ClassRealm globRealm = world.getRealm("glob");
+ Collection globImportRealms = globRealm.getImportRealms();
+ assertEquals(0, globImportRealms.size());
+
+ ClassRealm fromRealm = world.getRealm("from");
+ Collection fromImportRealms = fromRealm.getImportRealms();
+ assertEquals(0, fromImportRealms.size());
+ }
+
+ private FileInputStream getConfigPath(String name) throws Exception {
+ return new FileInputStream(new File(new File(TestUtil.getBasedir(), "src/test/test-data"), name));
+ }
+
+ private void assertArrayContains(URL[] array, URL url) {
+ for (URL value : array) {
+ if (url.equals(value)) {
+ return;
+ }
+ }
+ fail("URL (" + url + ") not found in array of URLs");
+ }
+}
diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/launcher/LauncherTest.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/launcher/LauncherTest.java
new file mode 100644
index 000000000000..39ca08cb7303
--- /dev/null
+++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/launcher/LauncherTest.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.launcher;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+import java.io.File;
+import java.io.FileInputStream;
+
+import org.codehaus.plexus.classworlds.AbstractClassWorldsTestCase;
+import org.codehaus.plexus.classworlds.TestUtil;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.fail;
+
+class LauncherTest extends AbstractClassWorldsTestCase {
+ private Launcher launcher;
+
+ @BeforeEach
+ public void setUp() {
+ this.launcher = new Launcher();
+
+ this.launcher.setSystemClassLoader(Thread.currentThread().getContextClassLoader());
+ }
+
+ @AfterEach
+ public void tearDown() {
+ this.launcher = null;
+ }
+
+ @Test
+ void testConfigureValid() throws Exception {
+ launcher.configure(getConfigPath("valid-launch.conf"));
+
+ Class> mainClass = launcher.getMainClass();
+
+ assertNotNull(mainClass);
+
+ assertEquals("a.A", mainClass.getName());
+
+ assertEquals("app", launcher.getMainRealm().getId());
+ }
+
+ @Test
+ void testLaunchValidStandard() throws Exception {
+ launcher.configure(getConfigPath("valid-launch.conf"));
+
+ launcher.launch(new String[] {});
+ }
+
+ @Test
+ void testLaunchValidStandardExitCode() throws Exception {
+ launcher.configure(getConfigPath("valid-launch-exitCode.conf"));
+
+ launcher.launch(new String[] {});
+
+ assertEquals(15, launcher.getExitCode(), "check exit code");
+ }
+
+ @Test
+ void testLaunchValidEnhanced() throws Exception {
+ launcher.configure(getConfigPath("valid-enh-launch.conf"));
+
+ launcher.launch(new String[] {});
+ }
+
+ @Test
+ void testLaunchValidEnhancedExitCode() throws Exception {
+ launcher.configure(getConfigPath("valid-enh-launch-exitCode.conf"));
+
+ launcher.launch(new String[] {});
+
+ assertEquals(45, launcher.getExitCode(), "check exit code");
+ }
+
+ @Test
+ void testLaunchNoSuchMethod() throws Exception {
+ launcher.configure(getConfigPath("launch-nomethod.conf"));
+
+ try {
+ launcher.launch(new String[] {});
+ fail("should have thrown NoSuchMethodException");
+ } catch (NoSuchMethodException e) {
+ // expected and correct
+ }
+ }
+
+ @Test
+ void testLaunchClassNotFound() throws Exception {
+ launcher.configure(getConfigPath("launch-noclass.conf"));
+
+ try {
+ launcher.launch(new String[] {});
+ fail("throw ClassNotFoundException");
+ } catch (ClassNotFoundException e) {
+ // expected and correct
+ }
+ }
+
+ private FileInputStream getConfigPath(String name) throws Exception {
+ String basedir = TestUtil.getBasedir();
+
+ return new FileInputStream(new File(new File(basedir, "src/test/test-data"), name));
+ }
+}
diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/ClassRealmImplTest.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/ClassRealmImplTest.java
new file mode 100644
index 000000000000..6d5d2f8f9afe
--- /dev/null
+++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/ClassRealmImplTest.java
@@ -0,0 +1,482 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.realm;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.codehaus.plexus.classworlds.AbstractClassWorldsTestCase;
+import org.codehaus.plexus.classworlds.ClassWorld;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.fail;
+
+class ClassRealmImplTest extends AbstractClassWorldsTestCase {
+ private ClassWorld world;
+
+ @BeforeEach
+ public void setUp() {
+ this.world = new ClassWorld();
+ }
+
+ @AfterEach
+ public void tearDown() {
+ this.world = null;
+ }
+
+ @Test
+ void testNewRealm() throws Exception {
+ ClassRealm realm = this.world.newRealm("foo");
+
+ assertNotNull(realm);
+
+ assertSame(this.world, realm.getWorld());
+
+ assertEquals("foo", realm.getId());
+ }
+
+ @Test
+ void testLocateSourceRealmNoImports() {
+ ClassRealm realm = new ClassRealm(this.world, "foo", null);
+
+ assertSame(null, realm.getImportClassLoader("com.werken.Stuff"));
+ }
+
+ @Test
+ void testLocateSourceRealmSimpleImport() throws Exception {
+ ClassRealm mainRealm = this.world.newRealm("main");
+
+ ClassRealm werkflowRealm = this.world.newRealm("werkflow");
+
+ mainRealm.importFrom("werkflow", "com.werken.werkflow");
+
+ assertSame(werkflowRealm, mainRealm.getImportClassLoader("com.werken.werkflow.WerkflowEngine"));
+
+ assertSame(werkflowRealm, mainRealm.getImportClassLoader("com/werken/werkflow/some.properties"));
+
+ assertSame(werkflowRealm, mainRealm.getImportClassLoader("com.werken.werkflow.process.ProcessManager"));
+
+ assertSame(null, mainRealm.getImportClassLoader("com.werken.blissed.Process"));
+
+ assertSame(null, mainRealm.getImportClassLoader("java.lang.Object"));
+
+ assertSame(null, mainRealm.getImportClassLoader("NoviceProgrammerClass"));
+ }
+
+ @Test
+ void testLocateSourceRealmMultipleImport() throws Exception {
+ ClassRealm mainRealm = this.world.newRealm("main");
+
+ ClassRealm werkflowRealm = this.world.newRealm("werkflow");
+
+ ClassRealm blissedRealm = this.world.newRealm("blissed");
+
+ mainRealm.importFrom("werkflow", "com.werken.werkflow");
+
+ mainRealm.importFrom("blissed", "com.werken.blissed");
+
+ assertSame(werkflowRealm, mainRealm.getImportClassLoader("com.werken.werkflow.WerkflowEngine"));
+
+ assertSame(werkflowRealm, mainRealm.getImportClassLoader("com.werken.werkflow.process.ProcessManager"));
+
+ assertSame(blissedRealm, mainRealm.getImportClassLoader("com.werken.blissed.Process"));
+
+ assertSame(blissedRealm, mainRealm.getImportClassLoader("com.werken.blissed.guard.BooleanGuard"));
+
+ assertSame(null, mainRealm.getImportClassLoader("java.lang.Object"));
+
+ assertSame(null, mainRealm.getImportClassLoader("NoviceProgrammerClass"));
+ }
+
+ @Test
+ void testLocateSourceRealmHierachy() throws Exception {
+ ClassRealm mainRealm = this.world.newRealm("main");
+
+ ClassRealm fooRealm = this.world.newRealm("foo");
+
+ ClassRealm fooBarRealm = this.world.newRealm("fooBar");
+
+ ClassRealm fooBarBazRealm = this.world.newRealm("fooBarBaz");
+
+ mainRealm.importFrom("foo", "foo");
+
+ mainRealm.importFrom("fooBar", "foo.bar");
+
+ mainRealm.importFrom("fooBarBaz", "foo.bar.baz");
+
+ assertSame(fooRealm, mainRealm.getImportClassLoader("foo.Goober"));
+
+ assertSame(fooRealm, mainRealm.getImportClassLoader("foo.cheese.Goober"));
+
+ assertSame(fooBarRealm, mainRealm.getImportClassLoader("foo.bar.Goober"));
+
+ assertSame(fooBarRealm, mainRealm.getImportClassLoader("foo.bar.cheese.Goober"));
+
+ assertSame(fooBarBazRealm, mainRealm.getImportClassLoader("foo.bar.baz.Goober"));
+
+ assertSame(fooBarBazRealm, mainRealm.getImportClassLoader("foo.bar.baz.cheese.Goober"));
+
+ assertSame(null, mainRealm.getImportClassLoader("java.lang.Object"));
+
+ assertSame(null, mainRealm.getImportClassLoader("NoviceProgrammerClass"));
+ }
+
+ @Test
+ void testLocateSourceRealmHierachyReverse() throws Exception {
+ ClassRealm fooBarBazRealm = this.world.newRealm("fooBarBaz");
+
+ ClassRealm fooBarRealm = this.world.newRealm("fooBar");
+
+ ClassRealm fooRealm = this.world.newRealm("foo");
+
+ ClassRealm mainRealm = this.world.newRealm("main");
+
+ mainRealm.importFrom("fooBarBaz", "foo.bar.baz");
+
+ mainRealm.importFrom("fooBar", "foo.bar");
+
+ mainRealm.importFrom("foo", "foo");
+
+ assertSame(fooRealm, mainRealm.getImportClassLoader("foo.Goober"));
+
+ assertSame(fooRealm, mainRealm.getImportClassLoader("foo.cheese.Goober"));
+
+ assertSame(fooBarRealm, mainRealm.getImportClassLoader("foo.bar.Goober"));
+
+ assertSame(fooBarRealm, mainRealm.getImportClassLoader("foo.bar.cheese.Goober"));
+
+ assertSame(fooBarBazRealm, mainRealm.getImportClassLoader("foo.bar.baz.Goober"));
+
+ assertSame(fooBarBazRealm, mainRealm.getImportClassLoader("foo.bar.baz.cheese.Goober"));
+
+ assertSame(null, mainRealm.getImportClassLoader("java.lang.Object"));
+
+ assertSame(null, mainRealm.getImportClassLoader("NoviceProgrammerClass"));
+ }
+
+ @Test
+ void testLoadClassSystemClass() throws Exception {
+ ClassRealm mainRealm = this.world.newRealm("main");
+
+ Class> cls = mainRealm.loadClass("java.lang.Object");
+
+ assertNotNull(cls);
+ }
+
+ @Test
+ void testLoadClassNonSystemClass() throws Exception {
+ ClassRealm mainRealm = this.world.newRealm("main");
+
+ try {
+ Class> c = mainRealm.loadClass("com.werken.projectz.UberThing");
+
+ System.out.println("c = " + c);
+
+ fail("A ClassNotFoundException should be thrown!");
+ } catch (ClassNotFoundException e) {
+ // expected and correct
+ }
+ }
+
+ @Test
+ void testLoadClassClassWorldsClass() throws Exception {
+ ClassRealm mainRealm = this.world.newRealm("main");
+
+ Class> cls = mainRealm.loadClass("org.codehaus.plexus.classworlds.ClassWorld");
+
+ assertNotNull(cls);
+
+ assertSame(ClassWorld.class, cls);
+ }
+
+ @Test
+ void testLoadClassLocal() throws Exception {
+ ClassRealm mainRealm = this.world.newRealm("main");
+
+ try {
+ mainRealm.loadClass("a.A");
+ } catch (ClassNotFoundException e) {
+ // expected and correct
+ }
+
+ mainRealm.addURL(getJarUrl("a.jar"));
+
+ Class> classA = mainRealm.loadClass("a.A");
+
+ assertNotNull(classA);
+
+ ClassRealm otherRealm = this.world.newRealm("other");
+
+ try {
+ otherRealm.loadClass("a.A");
+ } catch (ClassNotFoundException e) {
+ // expected and correct
+ }
+ }
+
+ @Test
+ void testLoadClassImported() throws Exception {
+ ClassRealm mainRealm = this.world.newRealm("main");
+
+ ClassRealm realmA = this.world.newRealm("realmA");
+
+ try {
+ realmA.loadClass("a.A");
+
+ fail("realmA.loadClass(a.A) should have thrown a ClassNotFoundException");
+ } catch (ClassNotFoundException e) {
+ // expected and correct
+ }
+
+ realmA.addURL(getJarUrl("a.jar"));
+
+ try {
+ mainRealm.loadClass("a.A");
+
+ fail("mainRealm.loadClass(a.A) should have thrown a ClassNotFoundException");
+ } catch (ClassNotFoundException e) {
+ // expected and correct
+ }
+
+ mainRealm.importFrom("realmA", "a");
+
+ Class> classA = realmA.loadClass("a.A");
+
+ assertNotNull(classA);
+
+ assertEquals(realmA, classA.getClassLoader());
+
+ Class> classMain = mainRealm.loadClass("a.A");
+
+ assertNotNull(classMain);
+
+ assertEquals(realmA, classMain.getClassLoader());
+
+ assertSame(classA, classMain);
+ }
+
+ @Test
+ void testLoadClassPackage() throws Exception {
+ ClassRealm realmA = this.world.newRealm("realmA");
+ realmA.addURL(getJarUrl("a.jar"));
+
+ Class> clazz = realmA.loadClass("a.A");
+ assertNotNull(clazz);
+ assertEquals("a.A", clazz.getName());
+
+ Package p = clazz.getPackage();
+ assertNotNull(p);
+ assertEquals("a", p.getName(), "p.getName()");
+ }
+
+ @Test
+ void testLoadClassComplex() throws Exception {
+ ClassRealm realmA = this.world.newRealm("realmA");
+ ClassRealm realmB = this.world.newRealm("realmB");
+ ClassRealm realmC = this.world.newRealm("realmC");
+
+ realmA.addURL(getJarUrl("a.jar"));
+ realmB.addURL(getJarUrl("b.jar"));
+ realmC.addURL(getJarUrl("c.jar"));
+
+ realmC.importFrom("realmA", "a");
+
+ realmC.importFrom("realmB", "b");
+
+ realmA.importFrom("realmC", "c");
+
+ Class> classAA = realmA.loadClass("a.A");
+ Class> classBB = realmB.loadClass("b.B");
+ Class> classCC = realmC.loadClass("c.C");
+
+ assertNotNull(classAA);
+ assertNotNull(classBB);
+ assertNotNull(classCC);
+
+ assertEquals(realmA, classAA.getClassLoader());
+
+ assertEquals(realmB, classBB.getClassLoader());
+
+ assertEquals(realmC, classCC.getClassLoader());
+
+ // load from C
+
+ Class> classAC = realmC.loadClass("a.A");
+
+ assertNotNull(classAC);
+
+ assertSame(classAA, classAC);
+
+ assertEquals(realmA, classAC.getClassLoader());
+
+ Class> classBC = realmC.loadClass("b.B");
+
+ assertNotNull(classBC);
+
+ assertSame(classBB, classBC);
+
+ assertEquals(realmB, classBC.getClassLoader());
+
+ // load from A
+
+ Class> classCA = realmA.loadClass("c.C");
+
+ assertNotNull(classCA);
+
+ assertSame(classCC, classCA);
+
+ assertEquals(realmC, classCA.getClassLoader());
+
+ try {
+ realmA.loadClass("b.B");
+ fail("throw ClassNotFoundException");
+ } catch (ClassNotFoundException e) {
+ // expected and correct
+ }
+
+ // load from B
+
+ try {
+ realmB.loadClass("a.A");
+ fail("throw ClassNotFoundException");
+ } catch (ClassNotFoundException e) {
+ // expected and correct
+ }
+
+ try {
+ realmB.loadClass("c.C");
+ fail("throw ClassNotFoundException");
+ } catch (ClassNotFoundException e) {
+ // expected and correct
+ }
+ }
+
+ @Test
+ void testLoadClassClassWorldsClassRepeatedly() throws Exception {
+ ClassRealm mainRealm = this.world.newRealm("main");
+
+ for (int i = 0; i < 100; i++) {
+ Class> cls = mainRealm.loadClass("org.codehaus.plexus.classworlds.ClassWorld");
+
+ assertNotNull(cls);
+
+ assertSame(ClassWorld.class, cls);
+ }
+ }
+
+ @Test
+ void testLoadClassWithModuleNameJava9() {
+ final ExtendedClassRealm mainRealm = new ExtendedClassRealm(world);
+ mainRealm.addURL(getJarUrl("a.jar"));
+ assertNotNull(mainRealm.simulateLoadClassFromModule("a.A"));
+ }
+
+ @Test
+ void testGetResourcesBaseBeforeSelf() throws Exception {
+ String resource = "common.properties";
+
+ ClassRealm base = this.world.newRealm("realmA");
+ base.addURL(getJarUrl("a.jar"));
+
+ URL baseUrl = base.getResource(resource);
+ assertNotNull(baseUrl);
+
+ ClassRealm sub = this.world.newRealm("realmB", base);
+ sub.addURL(getJarUrl("b.jar"));
+
+ URL subUrl = sub.getResource(resource);
+ assertNotNull(subUrl);
+
+ assertEquals(baseUrl, subUrl);
+
+ List urls = new ArrayList<>();
+ for (URL url : Collections.list(sub.getResources(resource))) {
+ String path = url.toString();
+ path = path.substring(path.lastIndexOf('/', path.lastIndexOf(".jar!")));
+ urls.add(path);
+ }
+ assertEquals(Arrays.asList("/a.jar!/common.properties", "/b.jar!/common.properties"), urls);
+ }
+
+ @Test
+ void testGetResourcesSelfBeforeParent() throws Exception {
+ String resource = "common.properties";
+
+ ClassRealm parent = this.world.newRealm("realmA");
+ parent.addURL(getJarUrl("a.jar"));
+
+ URL parentUrl = parent.getResource(resource);
+ assertNotNull(parentUrl);
+
+ ClassRealm child = parent.createChildRealm("realmB");
+ child.addURL(getJarUrl("b.jar"));
+
+ URL childUrl = child.getResource(resource);
+ assertNotNull(childUrl);
+
+ List urls = Collections.list(child.getResources(resource));
+ assertNotNull(urls);
+ assertEquals(Arrays.asList(childUrl, parentUrl), urls);
+ }
+
+ /**
+ * Simulates new {@code java.lang.ClassLoader#findClass(String,String)} introduced with Java 9.
+ * It is reversed in terms of inheritance but enables to simulate the same behavior in these tests.
+ * @see ClassLoader#findClass(String,String)
+ */
+ private static class ExtendedClassRealm extends ClassRealm {
+
+ ExtendedClassRealm(final ClassWorld world) {
+ super(world, "java9", Thread.currentThread().getContextClassLoader());
+ }
+
+ public Class> simulateLoadClassFromModule(final String name) {
+ synchronized (getClassLoadingLock(name)) {
+ Class> c = findLoadedClass(name);
+ if (c == null) {
+ c = findClass(null, name);
+ }
+ return c;
+ }
+ }
+ }
+}
diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/DefaultClassRealmTest.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/DefaultClassRealmTest.java
new file mode 100644
index 000000000000..c79468c97367
--- /dev/null
+++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/DefaultClassRealmTest.java
@@ -0,0 +1,346 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.realm;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
+
+import org.codehaus.plexus.classworlds.AbstractClassWorldsTestCase;
+import org.codehaus.plexus.classworlds.ClassWorld;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+class DefaultClassRealmTest extends AbstractClassWorldsTestCase {
+ // ----------------------------------------------------------------------
+ // Class testing
+ // ----------------------------------------------------------------------
+
+ @Test
+ void testLoadClassFromRealm() throws Exception {
+ ClassRealm mainRealm = new ClassRealm(new ClassWorld(), "main", null);
+
+ mainRealm.addURL(getJarUrl("component0-1.0.jar"));
+
+ loadClass(mainRealm, "org.codehaus.plexus.Component0");
+ }
+
+ @Test
+ void testLoadClassFromChildRealmWhereClassIsLocatedInParentRealm() throws Exception {
+ ClassRealm mainRealm = new ClassRealm(new ClassWorld(), "main", null);
+
+ mainRealm.addURL(getJarUrl("component0-1.0.jar"));
+
+ ClassRealm childRealm = mainRealm.createChildRealm("child");
+
+ loadClass(childRealm, "org.codehaus.plexus.Component0");
+ }
+
+ @Test
+ void testLoadClassFromChildRealmWhereClassIsLocatedInGrantParentRealm() throws Exception {
+ ClassRealm mainRealm = new ClassRealm(new ClassWorld(), "main", null);
+
+ mainRealm.addURL(getJarUrl("component0-1.0.jar"));
+
+ ClassRealm childRealm = mainRealm.createChildRealm("child");
+
+ ClassRealm grandchildRealm = childRealm.createChildRealm("grandchild");
+
+ loadClass(grandchildRealm, "org.codehaus.plexus.Component0");
+ }
+
+ @Test
+ void testLoadClassFromChildRealmWhereClassIsLocatedInBothChildRealmAndParentRealm() throws Exception {
+ ClassRealm mainRealm = new ClassRealm(new ClassWorld(), "parent", null);
+
+ mainRealm.addURL(getJarUrl("component5-1.0.jar"));
+
+ ClassRealm childRealm = mainRealm.createChildRealm("child");
+
+ childRealm.addURL(getJarUrl("component5-2.0.jar"));
+
+ Class> cls = loadClass(childRealm, "test.Component5");
+
+ assertSame(childRealm, cls.getClassLoader());
+ assertEquals(1, cls.getMethods().length);
+ assertEquals("printNew", cls.getMethods()[0].getName());
+ }
+
+ @Test
+ void testLoadNonExistentClass() {
+ ClassRealm mainRealm = new ClassRealm(new ClassWorld(), "main", null);
+
+ mainRealm.addURL(getJarUrl("component0-1.0.jar"));
+
+ try {
+ mainRealm.loadClass("org.foo.bar.NonExistentClass");
+
+ fail("A ClassNotFoundException should have been thrown!");
+ } catch (ClassNotFoundException e) {
+ // expected
+ }
+ }
+
+ @Test
+ void testImport() throws Exception {
+ ClassWorld world = new ClassWorld();
+
+ ClassRealm r0 = world.newRealm("r0");
+
+ ClassRealm r1 = world.newRealm("r1");
+
+ r0.addURL(getJarUrl("component0-1.0.jar"));
+
+ r1.importFrom("r0", "org.codehaus.plexus");
+
+ loadClass(r1, "org.codehaus.plexus.Component0");
+ }
+
+ @Test
+ void testParentImport() throws Exception {
+ ClassWorld world = new ClassWorld();
+
+ ClassRealm parent = world.newRealm("parent");
+
+ ClassRealm child = world.newRealm("child");
+
+ parent.addURL(getJarUrl("component0-1.0.jar"));
+
+ child.setParentRealm(parent);
+
+ Class> type = loadClass(child, "org.codehaus.plexus.Component0");
+
+ child.importFromParent("non-existing");
+
+ assertSame(null, loadClassOrNull(child, "org.codehaus.plexus.Component0"));
+
+ child.importFromParent("org.codehaus.plexus");
+
+ assertSame(type, loadClass(child, "org.codehaus.plexus.Component0"));
+ }
+
+ @Test
+ void testLoadClassFromBaseClassLoaderBeforeSelf() throws Exception {
+ ClassWorld world = new ClassWorld();
+
+ ClassRealm base = world.newRealm("base");
+
+ base.addURL(getJarUrl("a.jar"));
+
+ ClassRealm child = world.newRealm("child", base);
+
+ child.addURL(getJarUrl("a.jar"));
+
+ Class> baseClass = loadClass(base, "a.A");
+ Class> childClass = loadClass(child, "a.A");
+
+ assertSame(base, baseClass.getClassLoader());
+ assertSame(base, childClass.getClassLoader());
+ assertSame(baseClass, childClass);
+ }
+
+ @Test
+ void testLoadClassFromRealmWithCircularClassReferences() throws Exception {
+ ClassRealm mainRealm = new ClassRealm(new ClassWorld(), "main", null);
+
+ mainRealm.addURL(getJarUrl("circular-0.1.jar"));
+
+ /*
+ * This was reported to fail with a ClassCircularityError in IBM JDK 1.5.0-SR2, 1.5.0-SR7 and 1.6.0-SR2. It
+ * works in IBM JDK 1.5.0-SR10 and 1.6.0-SR6.
+ */
+ loadClass(mainRealm, "A$C");
+ }
+
+ // ----------------------------------------------------------------------
+ // Resource testing
+ // ----------------------------------------------------------------------
+
+ @Test
+ void testResource() throws Exception {
+ ClassRealm mainRealm = new ClassRealm(new ClassWorld(), "main", null);
+
+ mainRealm.addURL(getJarUrl("component0-1.0.jar"));
+
+ getResource(mainRealm, "META-INF/plexus/components.xml");
+ }
+
+ @Test
+ void testMalformedResource() throws Exception {
+ URL jarUrl = getJarUrl("component0-1.0.jar");
+
+ ClassRealm mainRealm = new ClassRealm(new ClassWorld(), "main", null);
+
+ mainRealm.addURL(jarUrl);
+
+ ClassLoader officialClassLoader = new URLClassLoader(new URL[] {jarUrl});
+
+ String resource = "META-INF/plexus/components.xml";
+
+ assertNotNull(mainRealm.getResource(resource));
+ assertNotNull(officialClassLoader.getResource(resource));
+
+ /*
+ * NOTE: Resource names with a leading slash are invalid when passed to a class loader and must not be found!
+ * One can use a leading slash in Class.getResource() but not in ClassLoader.getResource().
+ */
+
+ assertSame(null, mainRealm.getResource("/" + resource));
+ assertSame(null, officialClassLoader.getResource("/" + resource));
+ }
+
+ @Test
+ void testFindResourceOnlyScansSelf() throws Exception {
+ ClassRealm mainRealm = new ClassRealm(new ClassWorld(), "main", null);
+
+ mainRealm.addURL(getJarUrl("a.jar"));
+
+ ClassRealm childRealm = mainRealm.createChildRealm("child");
+
+ childRealm.addURL(getJarUrl("b.jar"));
+
+ assertNotNull(childRealm.getResource("a.properties"));
+ assertNotNull(childRealm.getResource("b.properties"));
+
+ assertNull(childRealm.findResource("a.properties"));
+
+ assertNotNull(childRealm.findResource("b.properties"));
+ }
+
+ @Test
+ void testFindResourcesOnlyScansSelf() throws Exception {
+ ClassRealm mainRealm = new ClassRealm(new ClassWorld(), "main", null);
+
+ mainRealm.addURL(getJarUrl("a.jar"));
+
+ ClassRealm childRealm = mainRealm.createChildRealm("child");
+
+ childRealm.addURL(getJarUrl("b.jar"));
+
+ assertTrue(childRealm.getResources("a.properties").hasMoreElements());
+ assertTrue(childRealm.getResources("b.properties").hasMoreElements());
+
+ assertFalse(childRealm.findResources("a.properties").hasMoreElements());
+
+ assertTrue(childRealm.findResources("b.properties").hasMoreElements());
+ }
+
+ /** Should never deadlock. Ever */
+ @Test
+ void testParallelDeadlockClassRealm() throws InterruptedException {
+ for (int i = 0; i < 100; i++) {
+ doOneDeadlockAttempt();
+ }
+ }
+
+ private void doOneDeadlockAttempt() throws InterruptedException {
+ // Deadlock sample graciously ripped from http://docs.oracle.com/javase/7/docs/technotes/guides/lang/cl-mt.html
+ final ClassRealm cl1 = new ClassRealm(new ClassWorld(), "cl1", null);
+ final ClassRealm cl2 = new ClassRealm(new ClassWorld(), "cl2", cl1);
+ cl1.setParentRealm(cl2);
+ cl1.addURL(getJarUrl("deadlock.jar"));
+ cl2.addURL(getJarUrl("deadlock.jar"));
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ Runnable r1 = () -> {
+ try {
+ latch.await();
+ cl1.loadClass("deadlock.A");
+ } catch (ClassNotFoundException | InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ };
+
+ Runnable r2 = () -> {
+ try {
+ latch.await();
+ cl1.loadClass("deadlock.C");
+ } catch (ClassNotFoundException | InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ };
+
+ Thread thread = new Thread(r1);
+ thread.start();
+ Thread thread1 = new Thread(r2);
+ thread1.start();
+ latch.countDown();
+ thread.join();
+ thread1.join();
+ }
+
+ // ----------------------------------------------------------------------
+ //
+ // ----------------------------------------------------------------------
+
+ private Class> loadClassOrNull(ClassRealm realm, String name) {
+ try {
+ return loadClass(realm, name);
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ }
+
+ private Class> loadClass(ClassRealm realm, String name) throws ClassNotFoundException {
+ Class> cls = realm.loadClass(name);
+
+ /*
+ * NOTE: Load the class both directly from the realm and indirectly from an (ordinary) child class loader which
+ * uses the specified class realm for parent delegation. The child class loader itself has no additional class
+ * path entries but relies entirely on the provided class realm. Hence, the created child class loader should in
+ * theory be able to load exactly the same classes/resources as the underlying class realm. In practice, it will
+ * test that class realms properly integrate into the standard Java class loader hierarchy.
+ */
+ ClassLoader childLoader = new URLClassLoader(new URL[0], realm);
+ assertEquals(cls, childLoader.loadClass(name));
+
+ return cls;
+ }
+
+ private void getResource(ClassRealm realm, String name) throws Exception {
+ ClassLoader childLoader = new URLClassLoader(new URL[0], realm);
+ assertNotNull(realm.getResource(name));
+ assertEquals(realm.getResource(name), childLoader.getResource(name));
+ assertEquals(Collections.list(realm.getResources(name)), Collections.list(childLoader.getResources(name)));
+ }
+}
diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/EntryTest.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/EntryTest.java
new file mode 100644
index 000000000000..f0115df603d0
--- /dev/null
+++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/EntryTest.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.realm;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+import org.codehaus.plexus.classworlds.AbstractClassWorldsTestCase;
+import org.codehaus.plexus.classworlds.ClassWorld;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * @author Ben Walding
+ */
+class EntryTest extends AbstractClassWorldsTestCase {
+
+ @Test
+ void testCompareTo() throws Exception {
+ ClassWorld cw = new ClassWorld();
+ ClassRealm r = cw.newRealm("test1");
+
+ Entry entry1 = new Entry(r, "org.test");
+ Entry entry2 = new Entry(r, "org.test.impl");
+
+ assertTrue(entry1.compareTo(entry2) > 0, "org.test > org.test.impl");
+ }
+
+ /**
+ * Tests the equality is realm independant
+ */
+ @Test
+ void testEquals() throws DuplicateRealmException {
+ ClassWorld cw = new ClassWorld();
+ ClassRealm r1 = cw.newRealm("test1");
+ ClassRealm r2 = cw.newRealm("test2");
+
+ Entry entry1 = new Entry(r1, "org.test");
+ Entry entry2 = new Entry(r2, "org.test");
+
+ assertEquals(entry1, entry2, "entry1 == entry2");
+ assertEquals(entry1.hashCode(), entry2.hashCode(), "entry1.hashCode() == entry2.hashCode()");
+ }
+
+ @Test
+ void testMatchesClassByPackageImport() throws Exception {
+ ClassWorld cw = new ClassWorld();
+ ClassRealm r = cw.newRealm("test1");
+
+ Entry entry = new Entry(r, "org.test");
+
+ assertTrue(entry.matches("org.test.MyClass"));
+ assertTrue(entry.matches("org.test.MyClass$NestedClass"));
+ assertTrue(entry.matches("org.test.MyClassUtils"));
+ assertTrue(entry.matches("org.test.impl.MyClass"));
+ assertFalse(entry.matches("org.tests.AnotherClass"));
+ }
+
+ @Test
+ void testMatchesClassByClassImport() throws Exception {
+ ClassWorld cw = new ClassWorld();
+ ClassRealm r = cw.newRealm("test1");
+
+ Entry entry = new Entry(r, "org.test.MyClass");
+
+ assertTrue(entry.matches("org.test.MyClass"));
+ assertTrue(entry.matches("org.test.MyClass$NestedClass"));
+ assertFalse(entry.matches("org.test.MyClassUtils"));
+ assertFalse(entry.matches("org.test.AnotherClass"));
+ }
+
+ @Test
+ void testMatchesResourceByPackageImport() throws Exception {
+ ClassWorld cw = new ClassWorld();
+ ClassRealm r = cw.newRealm("test1");
+
+ Entry entry = new Entry(r, "org.test");
+
+ assertTrue(entry.matches("org/test/MyClass.class"));
+ assertTrue(entry.matches("org/test/MyClass$NestedClass.class"));
+ assertTrue(entry.matches("org/test/MyClasses.properties"));
+ assertTrue(entry.matches("org/test/impl/MyClass.class"));
+ assertFalse(entry.matches("org/tests/AnotherClass.class"));
+ }
+
+ @Test
+ void testMatchesResourceByClassImport() throws Exception {
+ ClassWorld cw = new ClassWorld();
+ ClassRealm r = cw.newRealm("test1");
+
+ Entry entry = new Entry(r, "org.test.MyClass");
+
+ assertTrue(entry.matches("org/test/MyClass.class"));
+ assertTrue(entry.matches("org/test/MyClass$NestedClass.class"));
+ assertFalse(entry.matches("org/test/MyClass.properties"));
+ assertFalse(entry.matches("org/test/AnotherClass"));
+ }
+
+ @Test
+ void testMatchesAllImport() throws Exception {
+ ClassWorld cw = new ClassWorld();
+ ClassRealm r = cw.newRealm("test1");
+
+ Entry entry = new Entry(r, "");
+
+ assertTrue(entry.matches("org.test.MyClass"));
+ assertTrue(entry.matches("org.test.MyClass$NestedClass"));
+ assertTrue(entry.matches("org/test/MyClass.class"));
+ assertTrue(entry.matches("org/test/MyClass.properties"));
+ }
+
+ @Test
+ void testMatchesResourceByResourceImport() throws Exception {
+ ClassWorld cw = new ClassWorld();
+ ClassRealm r = cw.newRealm("test1");
+
+ Entry entry1 = new Entry(r, "some.properties");
+
+ assertTrue(entry1.matches("some.properties"));
+ assertFalse(entry1.matches("other.properties"));
+
+ Entry entry2 = new Entry(r, "org/test/some.properties");
+
+ assertTrue(entry2.matches("org/test/some.properties"));
+ assertFalse(entry2.matches("org/test/other.properties"));
+ }
+
+ @Test
+ void testMatchesClassByExactPackageImport() throws Exception {
+ ClassWorld cw = new ClassWorld();
+ ClassRealm r = cw.newRealm("test1");
+
+ Entry entry = new Entry(r, "org.test.*");
+
+ assertTrue(entry.matches("org.test.MyClass"));
+ assertTrue(entry.matches("org.test.MyClass$NestedClass"));
+ assertTrue(entry.matches("org.test.MyClassUtils"));
+ assertFalse(entry.matches("org.test.impl.MyClass"));
+ assertFalse(entry.matches("org.tests.AnotherClass"));
+ }
+
+ @Test
+ void testMatchesResourceByExactPackageImport() throws Exception {
+ ClassWorld cw = new ClassWorld();
+ ClassRealm r = cw.newRealm("test1");
+
+ Entry entry = new Entry(r, "org.test.*");
+
+ assertTrue(entry.matches("org/test/MyClass.class"));
+ assertTrue(entry.matches("org/test/MyClass$NestedClass.class"));
+ assertTrue(entry.matches("org/test/MyClasses.properties"));
+ assertFalse(entry.matches("org/test/impl/MyClass.class"));
+ assertFalse(entry.matches("org/tests/AnotherClass.class"));
+ }
+}
diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/FilteredClassRealmTest.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/FilteredClassRealmTest.java
new file mode 100644
index 000000000000..a85076da341a
--- /dev/null
+++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/FilteredClassRealmTest.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.realm;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Predicate;
+
+import org.codehaus.plexus.classworlds.AbstractClassWorldsTestCase;
+import org.codehaus.plexus.classworlds.ClassWorld;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class FilteredClassRealmTest extends AbstractClassWorldsTestCase {
+ private ClassWorld world;
+ private ClassRealm realmA;
+
+ @BeforeEach
+ public void setUp() throws DuplicateRealmException {
+ this.world = new ClassWorld();
+ // only allow loading resources whose names start with "a."
+ Set allowedResourcePrefixes = new HashSet<>();
+ allowedResourcePrefixes.add("a.");
+ allowedResourcePrefixes.add("a/Aa");
+ realmA = this.world.newRealm(
+ "realmA",
+ getClass().getClassLoader(),
+ s -> allowedResourcePrefixes.stream().anyMatch(s::startsWith));
+ }
+
+ @Test
+ void testLoadResources() throws Exception {
+ realmA.addURL(getJarUrl("a.jar"));
+ assertNull(realmA.getResource("common.properties"));
+ assertFalse(realmA.getResources("common.properties").hasMoreElements());
+
+ assertNotNull(realmA.getResource("a.properties"));
+ assertTrue(realmA.getResources("a.properties").hasMoreElements());
+ }
+
+ @Test
+ void testLoadClass() throws ClassNotFoundException {
+ assertThrows(ClassNotFoundException.class, () -> realmA.loadClass("a.Aa"));
+ realmA.addURL(getJarUrl("a.jar"));
+
+ assertNotNull(realmA.loadClass("a.Aa"));
+ assertThrows(ClassNotFoundException.class, () -> realmA.loadClass("a.A"));
+
+ assertNotNull(realmA.loadClass("a.Aa"));
+ assertThrows(ClassNotFoundException.class, () -> realmA.loadClass("a.A"));
+ }
+
+ @Test
+ void testLoadClassWithModule() throws IOException {
+ try (ExtendedFilteredClassRealm realmA = new ExtendedFilteredClassRealm(world, s -> s.startsWith("a/Aa"))) {
+ realmA.addURL(getJarUrl("a.jar"));
+ assertNotNull(realmA.simulateLoadClassFromModule("a.Aa"));
+ assertNull(realmA.simulateLoadClassFromModule("a.A"));
+ }
+ }
+
+ /**
+ * Simulates new {@code java.lang.ClassLoader#findClass(String,String)} introduced with Java 9.
+ * It is reversed in terms of inheritance but enables to simulate the same behavior in these tests.
+ * @see ClassLoader#findClass(String,String)
+ * @see ClassRealmImplTest.ExtendedClassRealm
+ */
+ static class ExtendedFilteredClassRealm extends FilteredClassRealm {
+
+ ExtendedFilteredClassRealm(final ClassWorld world, Predicate filter) {
+ super(filter, world, "java9", Thread.currentThread().getContextClassLoader());
+ }
+
+ public Class> simulateLoadClassFromModule(final String name) {
+ synchronized (getClassLoadingLock(name)) {
+ Class> c = findLoadedClass(name);
+ if (c == null) {
+ c = findClass(null, name);
+ }
+ return c;
+ }
+ }
+ }
+}
diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/strategy/StrategyTest.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/strategy/StrategyTest.java
new file mode 100644
index 000000000000..d8ed59bb7c68
--- /dev/null
+++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/strategy/StrategyTest.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.plexus.classworlds.strategy;
+
+/*
+ * Copyright 2001-2006 Codehaus Foundation.
+ *
+ * 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.
+ */
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+
+import org.codehaus.plexus.classworlds.AbstractClassWorldsTestCase;
+import org.codehaus.plexus.classworlds.ClassWorld;
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
+// jars within jars
+// hierarchy vs graph
+
+class StrategyTest extends AbstractClassWorldsTestCase {
+ private ClassRealm realm;
+
+ private Strategy strategy;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ this.realm = new ClassWorld().newRealm("realm");
+ this.strategy = this.realm.getStrategy();
+ realm.addURL(getJarUrl("component0-1.0.jar"));
+ }
+
+ @Test
+ void testLoadingOfApplicationClass() throws Exception {
+ assertNotNull(strategy.loadClass("org.codehaus.plexus.Component0"));
+ }
+
+ @Test
+ void testLoadingOfApplicationClassThenDoingItAgain() throws Exception {
+ Class> c = strategy.loadClass("org.codehaus.plexus.Component0");
+
+ assertNotNull(c);
+
+ c = strategy.loadClass("org.codehaus.plexus.Component0");
+
+ assertNotNull(c);
+ }
+
+ @Test
+ void testLoadingOfSystemClass() throws Exception {
+ assertNotNull(strategy.getRealm().loadClass("java.lang.Object"));
+ }
+
+ @Test
+ void testLoadingOfNonExistentClass() {
+ try {
+ strategy.loadClass("org.codehaus.plexus.NonExistentComponent");
+
+ fail("Should have thrown a ClassNotFoundException!");
+ } catch (ClassNotFoundException e) {
+ // do nothing
+ }
+ }
+
+ @Test
+ void testGetApplicationResource() throws Exception {
+ URL resource = strategy.getResource("META-INF/plexus/components.xml");
+
+ assertNotNull(resource);
+
+ String content = getContent(resource.openStream());
+
+ assertTrue(content.startsWith(""));
+ }
+
+ @Test
+ void testGetSystemResource() {
+ assumeTrue(
+ getJavaVersion() < 9.0,
+ "Due to strong encapsulation you cannot get the java/lang/Object.class as resource since Java 9");
+
+ URL resource = strategy.getRealm().getResource("java/lang/Object.class");
+
+ assertNotNull(resource);
+ }
+
+ @Test
+ void testFindResources() throws Exception {
+ realm.addURL(getJarUrl("component1-1.0.jar"));
+
+ Enumeration e = strategy.getResources("META-INF/plexus/components.xml");
+ assertNotNull(e);
+
+ int resourceCount = 0;
+ while (e.hasMoreElements()) {
+ e.nextElement();
+ resourceCount++;
+ }
+ assertEquals(2, resourceCount);
+ }
+
+ protected String getContent(InputStream in) throws Exception {
+ byte[] buffer = new byte[1024];
+
+ int read;
+
+ StringBuilder content = new StringBuilder();
+
+ while ((read = in.read(buffer, 0, 1024)) >= 0) {
+ content.append(new String(buffer, 0, read));
+ }
+
+ return content.toString();
+ }
+
+ private double getJavaVersion() {
+ return Double.parseDouble(System.getProperty("java.specification.version"));
+ }
+}
diff --git a/impl/maven-classworlds/src/test/test-data/a.jar b/impl/maven-classworlds/src/test/test-data/a.jar
new file mode 100644
index 000000000000..9297ed84fab1
Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/a.jar differ
diff --git a/impl/maven-classworlds/src/test/test-data/a.properties b/impl/maven-classworlds/src/test/test-data/a.properties
new file mode 100644
index 000000000000..d0e2fad85157
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/a.properties
@@ -0,0 +1,18 @@
+################################################################################
+#
+# Copyright 2001-2006 The Codehaus Foundation.
+#
+# 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.
+#
+
+a properties
diff --git a/impl/maven-classworlds/src/test/test-data/b.jar b/impl/maven-classworlds/src/test/test-data/b.jar
new file mode 100644
index 000000000000..0776ca62b2e5
Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/b.jar differ
diff --git a/impl/maven-classworlds/src/test/test-data/b_old.jar b/impl/maven-classworlds/src/test/test-data/b_old.jar
new file mode 100644
index 000000000000..6ea6947e3323
Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/b_old.jar differ
diff --git a/impl/maven-classworlds/src/test/test-data/c.jar b/impl/maven-classworlds/src/test/test-data/c.jar
new file mode 100644
index 000000000000..8e48a862c7c8
Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/c.jar differ
diff --git a/impl/maven-classworlds/src/test/test-data/circular-0.1.jar b/impl/maven-classworlds/src/test/test-data/circular-0.1.jar
new file mode 100644
index 000000000000..0bfb3a472867
Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/circular-0.1.jar differ
diff --git a/impl/maven-classworlds/src/test/test-data/component0-1.0.jar b/impl/maven-classworlds/src/test/test-data/component0-1.0.jar
new file mode 100644
index 000000000000..8a4f4020eb15
Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/component0-1.0.jar differ
diff --git a/impl/maven-classworlds/src/test/test-data/component1-1.0.jar b/impl/maven-classworlds/src/test/test-data/component1-1.0.jar
new file mode 100644
index 000000000000..2e1e4566f4ec
Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/component1-1.0.jar differ
diff --git a/impl/maven-classworlds/src/test/test-data/component2-1.0.jar b/impl/maven-classworlds/src/test/test-data/component2-1.0.jar
new file mode 100644
index 000000000000..e1629a4053b6
Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/component2-1.0.jar differ
diff --git a/impl/maven-classworlds/src/test/test-data/component3-1.0.jar b/impl/maven-classworlds/src/test/test-data/component3-1.0.jar
new file mode 100644
index 000000000000..211a32968e5c
Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/component3-1.0.jar differ
diff --git a/impl/maven-classworlds/src/test/test-data/component4-1.0.jar b/impl/maven-classworlds/src/test/test-data/component4-1.0.jar
new file mode 100644
index 000000000000..e16fc3cea405
Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/component4-1.0.jar differ
diff --git a/impl/maven-classworlds/src/test/test-data/component5-1.0.jar b/impl/maven-classworlds/src/test/test-data/component5-1.0.jar
new file mode 100644
index 000000000000..39dc325d8ba0
Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/component5-1.0.jar differ
diff --git a/impl/maven-classworlds/src/test/test-data/component5-2.0.jar b/impl/maven-classworlds/src/test/test-data/component5-2.0.jar
new file mode 100644
index 000000000000..9e734a10a905
Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/component5-2.0.jar differ
diff --git a/impl/maven-classworlds/src/test/test-data/d.jar b/impl/maven-classworlds/src/test/test-data/d.jar
new file mode 100644
index 000000000000..f523220c776e
Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/d.jar differ
diff --git a/impl/maven-classworlds/src/test/test-data/deadlock.jar b/impl/maven-classworlds/src/test/test-data/deadlock.jar
new file mode 100644
index 000000000000..95147bca36a8
Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/deadlock.jar differ
diff --git a/impl/maven-classworlds/src/test/test-data/dupe-main.conf b/impl/maven-classworlds/src/test/test-data/dupe-main.conf
new file mode 100644
index 000000000000..ddd111f9a7f5
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/dupe-main.conf
@@ -0,0 +1,3 @@
+
+main is com.werken.Foo from foo
+main is com.werken.Bar from bar
diff --git a/impl/maven-classworlds/src/test/test-data/dupe-realm.conf b/impl/maven-classworlds/src/test/test-data/dupe-realm.conf
new file mode 100644
index 000000000000..345f51f0e1bd
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/dupe-realm.conf
@@ -0,0 +1,9 @@
+
+[cheese]
+
+[dupe.realm]
+
+[potatoes]
+
+[dupe.realm]
+
diff --git a/impl/maven-classworlds/src/test/test-data/early-import.conf b/impl/maven-classworlds/src/test/test-data/early-import.conf
new file mode 100644
index 000000000000..23378ecf009c
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/early-import.conf
@@ -0,0 +1,2 @@
+
+import org.xml.sax from root
diff --git a/impl/maven-classworlds/src/test/test-data/from-from-0.0.1-from-load-import.jar b/impl/maven-classworlds/src/test/test-data/from-from-0.0.1-from-load-import.jar
new file mode 100644
index 000000000000..1688b673dc33
Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/from-from-0.0.1-from-load-import.jar differ
diff --git a/impl/maven-classworlds/src/test/test-data/inheritance.conf b/impl/maven-classworlds/src/test/test-data/inheritance.conf
new file mode 100644
index 000000000000..977cc663e640
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/inheritance.conf
@@ -0,0 +1,18 @@
+# ------------------------------------------------------------
+# Define the main entry-point
+# ------------------------------------------------------------
+
+main is org.apache.maven.app.App from root.maven
+
+# ------------------------------------------------------------
+# Start defining realms
+# ------------------------------------------------------------
+
+[root]
+load ${basedir}/src/test/test-data/a.jar
+
+[root.maven]
+load ${basedir}/src/test/test-data/b.jar
+
+[root.maven.plugin]
+load ${basedir}/src/test/test-data/c.jar
diff --git a/impl/maven-classworlds/src/test/test-data/launch-noclass.conf b/impl/maven-classworlds/src/test/test-data/launch-noclass.conf
new file mode 100644
index 000000000000..b6b844079612
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/launch-noclass.conf
@@ -0,0 +1,6 @@
+
+main is b.Goober from app
+
+[app]
+ load ${basedir}/src/test/test-data/a.jar
+ load ${basedir}/src/test/test-data/b.jar
diff --git a/impl/maven-classworlds/src/test/test-data/launch-nomethod.conf b/impl/maven-classworlds/src/test/test-data/launch-nomethod.conf
new file mode 100644
index 000000000000..35954a3bf258
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/launch-nomethod.conf
@@ -0,0 +1,7 @@
+
+main is c.C from app
+
+[app]
+ load ${basedir}/src/test/test-data/a.jar
+ load ${basedir}/src/test/test-data/b.jar
+ load ${basedir}/src/test/test-data/c.jar
diff --git a/impl/maven-classworlds/src/test/test-data/nested.jar b/impl/maven-classworlds/src/test/test-data/nested.jar
new file mode 100644
index 000000000000..821c14cbc36a
Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/nested.jar differ
diff --git a/impl/maven-classworlds/src/test/test-data/nested.properties b/impl/maven-classworlds/src/test/test-data/nested.properties
new file mode 100644
index 000000000000..0462a1553247
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/nested.properties
@@ -0,0 +1,18 @@
+################################################################################
+#
+# Copyright 2001-2006 The Codehaus Foundation.
+#
+# 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.
+#
+
+nested.properties
diff --git a/impl/maven-classworlds/src/test/test-data/optionally-existent.conf b/impl/maven-classworlds/src/test/test-data/optionally-existent.conf
new file mode 100644
index 000000000000..fe102574c95d
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/optionally-existent.conf
@@ -0,0 +1,14 @@
+
+# ------------------------------------------------------------
+# Define the main entry-point
+# ------------------------------------------------------------
+
+main is org.apache.maven.app.App from opt
+
+# ------------------------------------------------------------
+# Start defining realms
+# ------------------------------------------------------------
+
+[opt]
+ optionally ${basedir}/target/test-lib/jakarta.xml.bind-api-4.0.2.jar
+
diff --git a/impl/maven-classworlds/src/test/test-data/optionally-nonexistent.conf b/impl/maven-classworlds/src/test/test-data/optionally-nonexistent.conf
new file mode 100644
index 000000000000..4048e077e6e4
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/optionally-nonexistent.conf
@@ -0,0 +1,13 @@
+
+# ------------------------------------------------------------
+# Define the main entry-point
+# ------------------------------------------------------------
+
+main is org.apache.maven.app.App from opt
+
+# ------------------------------------------------------------
+# Start defining realms
+# ------------------------------------------------------------
+
+[opt]
+ optionally /non/existant/path/*.jar
diff --git a/impl/maven-classworlds/src/test/test-data/realm-syntax.conf b/impl/maven-classworlds/src/test/test-data/realm-syntax.conf
new file mode 100644
index 000000000000..27b9e73f7d0c
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/realm-syntax.conf
@@ -0,0 +1,3 @@
+
+[foo
+]
diff --git a/impl/maven-classworlds/src/test/test-data/resources/classworlds.conf b/impl/maven-classworlds/src/test/test-data/resources/classworlds.conf
new file mode 100644
index 000000000000..b91e608776a8
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/resources/classworlds.conf
@@ -0,0 +1,12 @@
+# ------------------------------------------------------------
+# Define the main entry-point
+# ------------------------------------------------------------
+
+main is foo from root
+
+# ------------------------------------------------------------
+# Start defining realms
+# ------------------------------------------------------------
+
+[root]
+load ${basedir}/src/test/test-data/resources/werkflow.jar
diff --git a/impl/maven-classworlds/src/test/test-data/resources/werkflow.jar b/impl/maven-classworlds/src/test/test-data/resources/werkflow.jar
new file mode 100644
index 000000000000..315a149b7a72
Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/resources/werkflow.jar differ
diff --git a/impl/maven-classworlds/src/test/test-data/set-using-existent.conf b/impl/maven-classworlds/src/test/test-data/set-using-existent.conf
new file mode 100644
index 000000000000..11415b8ebf96
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/set-using-existent.conf
@@ -0,0 +1,19 @@
+
+# ------------------------------------------------------------
+# Define the main entry-point
+# ------------------------------------------------------------
+
+main is org.apache.maven.app.App from opt
+
+# ------------------------------------------------------------
+# Set properties
+# ------------------------------------------------------------
+set set.using.existent using ${basedir}/src/test/test-data/set-using-existent.properties
+set set.using.default using ${basedir}/src/test/test-data/set-using-existent.properties default testSetUsingExistentDefault
+
+# ------------------------------------------------------------
+# Start defining realms
+# ------------------------------------------------------------
+
+[opt]
+
diff --git a/impl/maven-classworlds/src/test/test-data/set-using-existent.properties b/impl/maven-classworlds/src/test/test-data/set-using-existent.properties
new file mode 100644
index 000000000000..6434aef94a33
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/set-using-existent.properties
@@ -0,0 +1,19 @@
+################################################################################
+#
+# Copyright 2001-2006 The Codehaus Foundation.
+#
+# 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.
+#
+
+set.using.existent=testSetUsingExistent
+
diff --git a/impl/maven-classworlds/src/test/test-data/set-using-missing.conf b/impl/maven-classworlds/src/test/test-data/set-using-missing.conf
new file mode 100644
index 000000000000..6f8ccc53d251
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/set-using-missing.conf
@@ -0,0 +1,19 @@
+
+# ------------------------------------------------------------
+# Define the main entry-point
+# ------------------------------------------------------------
+
+main is org.apache.maven.app.App from opt
+
+# ------------------------------------------------------------
+# Set properties
+# ------------------------------------------------------------
+set set.using.missing default testSetUsingMissingDefault
+set set.using.filtered.default default ${user.home}/m2
+
+# ------------------------------------------------------------
+# Start defining realms
+# ------------------------------------------------------------
+
+[opt]
+
diff --git a/impl/maven-classworlds/src/test/test-data/set-using-nonexistent.conf b/impl/maven-classworlds/src/test/test-data/set-using-nonexistent.conf
new file mode 100644
index 000000000000..4640cf52c2e1
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/set-using-nonexistent.conf
@@ -0,0 +1,19 @@
+
+# ------------------------------------------------------------
+# Define the main entry-point
+# ------------------------------------------------------------
+
+main is org.apache.maven.app.App from opt
+
+# ------------------------------------------------------------
+# Set properties
+# ------------------------------------------------------------
+set set.using.nonexistent using ${basedir}/src/test/test-data/set-using-nonexistent.properties
+set set.using.nonexistent.default using ${basedir}/src/test/test-data/set-using-nonexistent.properties default testSetUsingNonExistentDefault
+
+# ------------------------------------------------------------
+# Start defining realms
+# ------------------------------------------------------------
+
+[opt]
+
diff --git a/impl/maven-classworlds/src/test/test-data/unhandled.conf b/impl/maven-classworlds/src/test/test-data/unhandled.conf
new file mode 100644
index 000000000000..3f808fe665dc
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/unhandled.conf
@@ -0,0 +1,2 @@
+
+foo
diff --git a/impl/maven-classworlds/src/test/test-data/valid-enh-launch-exitCode.conf b/impl/maven-classworlds/src/test/test-data/valid-enh-launch-exitCode.conf
new file mode 100644
index 000000000000..af06c25bbe39
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/valid-enh-launch-exitCode.conf
@@ -0,0 +1,6 @@
+
+main is b.Bb from app
+
+[app]
+ load ${basedir}/src/test/test-data/a.jar
+ load ${basedir}/src/test/test-data/b.jar
diff --git a/impl/maven-classworlds/src/test/test-data/valid-enh-launch.conf b/impl/maven-classworlds/src/test/test-data/valid-enh-launch.conf
new file mode 100644
index 000000000000..55f8f22c6e73
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/valid-enh-launch.conf
@@ -0,0 +1,6 @@
+
+main is b.B from app
+
+[app]
+ load ${basedir}/src/test/test-data/a.jar
+ load ${basedir}/src/test/test-data/b.jar
diff --git a/impl/maven-classworlds/src/test/test-data/valid-from-from-from.conf b/impl/maven-classworlds/src/test/test-data/valid-from-from-from.conf
new file mode 100644
index 000000000000..e4bcf8a6de7b
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/valid-from-from-from.conf
@@ -0,0 +1,24 @@
+
+# ------------------------------------------------------------
+# Define the main entry-point
+# ------------------------------------------------------------
+
+main is com.from.from.from.Main from from
+
+# ------------------------------------------------------------
+# Start defining realms
+# ------------------------------------------------------------
+
+[from]
+ load ${basedir}/src/test/test-data/from-from-0.0.1-from-load-import.jar
+
+[ant]
+ import com.from.from.from from from
+ load ${basedir}/target/test-lib/ant-1.10.14.jar
+
+[maven]
+ import com.from.from.from from from
+ load ${basedir}/target/test-lib/log4j-api-2.23.1.jar
+
+[glob]
+ load ${basedir}/src/test/test-data/*.jar
diff --git a/impl/maven-classworlds/src/test/test-data/valid-launch-exitCode.conf b/impl/maven-classworlds/src/test/test-data/valid-launch-exitCode.conf
new file mode 100644
index 000000000000..9cbc8235b962
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/valid-launch-exitCode.conf
@@ -0,0 +1,5 @@
+
+main is a.Aa from app
+
+[app]
+ load ${basedir}/src/test/test-data/a.jar
diff --git a/impl/maven-classworlds/src/test/test-data/valid-launch.conf b/impl/maven-classworlds/src/test/test-data/valid-launch.conf
new file mode 100644
index 000000000000..e5114ad1337b
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/valid-launch.conf
@@ -0,0 +1,5 @@
+
+main is a.A from app
+
+[app]
+ load ${basedir}/src/test/test-data/a.jar
diff --git a/impl/maven-classworlds/src/test/test-data/valid.conf b/impl/maven-classworlds/src/test/test-data/valid.conf
new file mode 100644
index 000000000000..2e7054f7bfdf
--- /dev/null
+++ b/impl/maven-classworlds/src/test/test-data/valid.conf
@@ -0,0 +1,24 @@
+
+# ------------------------------------------------------------
+# Define the main entry-point
+# ------------------------------------------------------------
+
+main is org.apache.maven.app.App from maven
+
+# ------------------------------------------------------------
+# Start defining realms
+# ------------------------------------------------------------
+
+[xml]
+ load ${basedir}/target/test-lib/jakarta.xml.bind-api-4.0.2.jar
+
+[ant]
+ import jakarta.xml.bind from xml
+ load ${basedir}/target/test-lib/ant-1.10.14.jar
+
+[maven]
+ import jakarta.xml.bind from xml
+ load ${basedir}/target/test-lib/log4j-api-2.23.1.jar
+
+[glob]
+ load ${basedir}/src/test/test-data/*.jar
diff --git a/impl/maven-cli/pom.xml b/impl/maven-cli/pom.xml
index 39b6fcaa5e16..6d84e70d1c13 100644
--- a/impl/maven-cli/pom.xml
+++ b/impl/maven-cli/pom.xml
@@ -146,8 +146,12 @@ under the License.
- org.codehaus.plexus
- plexus-classworlds
+ org.apache.maven
+ maven-api-classworlds
+
+
+ org.apache.maven
+ maven-classworlds
org.codehaus.plexus
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java
index fdb957d5bb8a..7a632ecddcf7 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java
@@ -23,6 +23,7 @@
import java.io.OutputStream;
import org.apache.maven.api.annotations.Nullable;
+import org.apache.maven.api.classworlds.ClassWorld;
import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.InvokerException;
import org.apache.maven.api.cli.Parser;
@@ -30,7 +31,6 @@
import org.apache.maven.api.services.MessageBuilderFactory;
import org.apache.maven.cling.invoker.logging.SystemLogger;
import org.apache.maven.jline.JLineMessageBuilderFactory;
-import org.codehaus.plexus.classworlds.ClassWorld;
import static java.util.Objects.requireNonNull;
@@ -47,7 +47,10 @@ public abstract class ClingSupport {
* Ctor that creates "managed" ClassWorld. This constructor is not used in "normal" circumstances.
*/
public ClingSupport() {
- this(new ClassWorld(CORE_CLASS_REALM_ID, Thread.currentThread().getContextClassLoader()), true);
+ this(
+ new org.codehaus.plexus.classworlds.ClassWorld(
+ CORE_CLASS_REALM_ID, Thread.currentThread().getContextClassLoader()),
+ true);
}
/**
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenCling.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenCling.java
index 65fa91e78e84..99d1a0a75c8a 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenCling.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenCling.java
@@ -23,13 +23,13 @@
import java.io.OutputStream;
import org.apache.maven.api.annotations.Nullable;
+import org.apache.maven.api.classworlds.ClassWorld;
import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.Parser;
import org.apache.maven.api.cli.ParserRequest;
import org.apache.maven.cling.invoker.ProtoLookup;
import org.apache.maven.cling.invoker.mvn.MavenInvoker;
import org.apache.maven.cling.invoker.mvn.MavenParser;
-import org.codehaus.plexus.classworlds.ClassWorld;
/**
* Maven CLI "new-gen".
@@ -47,7 +47,7 @@ public static void main(String[] args) throws IOException {
/**
* ClassWorld Launcher "enhanced" entry point: returning exitCode and accepts Class World.
*/
- public static int main(String[] args, ClassWorld world) throws IOException {
+ public static int main(String[] args, org.codehaus.plexus.classworlds.ClassWorld world) throws IOException {
return new MavenCling(world).run(args, null, null, null, false);
}
@@ -56,7 +56,7 @@ public static int main(String[] args, ClassWorld world) throws IOException {
*/
public static int main(
String[] args,
- ClassWorld world,
+ org.codehaus.plexus.classworlds.ClassWorld world,
@Nullable InputStream stdIn,
@Nullable OutputStream stdOut,
@Nullable OutputStream stdErr)
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenEncCling.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenEncCling.java
index 3fb3a765ab11..1b5b6a2c72fa 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenEncCling.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenEncCling.java
@@ -23,13 +23,13 @@
import java.io.OutputStream;
import org.apache.maven.api.annotations.Nullable;
+import org.apache.maven.api.classworlds.ClassWorld;
import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.Parser;
import org.apache.maven.api.cli.ParserRequest;
import org.apache.maven.cling.invoker.ProtoLookup;
import org.apache.maven.cling.invoker.mvnenc.EncryptInvoker;
import org.apache.maven.cling.invoker.mvnenc.EncryptParser;
-import org.codehaus.plexus.classworlds.ClassWorld;
/**
* Maven encrypt CLI "new-gen".
@@ -47,7 +47,7 @@ public static void main(String[] args) throws IOException {
/**
* ClassWorld Launcher "enhanced" entry point: returning exitCode and accepts Class World.
*/
- public static int main(String[] args, ClassWorld world) throws IOException {
+ public static int main(String[] args, org.codehaus.plexus.classworlds.ClassWorld world) throws IOException {
return new MavenEncCling(world).run(args, null, null, null, false);
}
@@ -56,7 +56,7 @@ public static int main(String[] args, ClassWorld world) throws IOException {
*/
public static int main(
String[] args,
- ClassWorld world,
+ org.codehaus.plexus.classworlds.ClassWorld world,
@Nullable InputStream stdIn,
@Nullable OutputStream stdOut,
@Nullable OutputStream stdErr)
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenShellCling.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenShellCling.java
index ce28bb636143..399553cd078c 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenShellCling.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenShellCling.java
@@ -23,13 +23,13 @@
import java.io.OutputStream;
import org.apache.maven.api.annotations.Nullable;
+import org.apache.maven.api.classworlds.ClassWorld;
import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.Parser;
import org.apache.maven.api.cli.ParserRequest;
import org.apache.maven.cling.invoker.ProtoLookup;
import org.apache.maven.cling.invoker.mvnsh.ShellInvoker;
import org.apache.maven.cling.invoker.mvnsh.ShellParser;
-import org.codehaus.plexus.classworlds.ClassWorld;
/**
* Maven shell.
@@ -47,7 +47,7 @@ public static void main(String[] args) throws IOException {
/**
* ClassWorld Launcher "enhanced" entry point: returning exitCode and accepts Class World.
*/
- public static int main(String[] args, ClassWorld world) throws IOException {
+ public static int main(String[] args, org.codehaus.plexus.classworlds.ClassWorld world) throws IOException {
return new MavenShellCling(world).run(args, null, null, null, false);
}
@@ -56,7 +56,7 @@ public static int main(String[] args, ClassWorld world) throws IOException {
*/
public static int main(
String[] args,
- ClassWorld world,
+ org.codehaus.plexus.classworlds.ClassWorld world,
@Nullable InputStream stdIn,
@Nullable OutputStream stdOut,
@Nullable OutputStream stdErr)
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenUpCling.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenUpCling.java
index f5650f0525ec..6d82b98ba8de 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenUpCling.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenUpCling.java
@@ -23,13 +23,13 @@
import java.io.OutputStream;
import org.apache.maven.api.annotations.Nullable;
+import org.apache.maven.api.classworlds.ClassWorld;
import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.Parser;
import org.apache.maven.api.cli.ParserRequest;
import org.apache.maven.cling.invoker.ProtoLookup;
import org.apache.maven.cling.invoker.mvnup.UpgradeInvoker;
import org.apache.maven.cling.invoker.mvnup.UpgradeParser;
-import org.codehaus.plexus.classworlds.ClassWorld;
/**
* Maven upgrade CLI "new-gen".
@@ -47,7 +47,7 @@ public static void main(String[] args) throws IOException {
/**
* ClassWorld Launcher "enhanced" entry point: returning exitCode and accepts Class World.
*/
- public static int main(String[] args, ClassWorld world) throws IOException {
+ public static int main(String[] args, org.codehaus.plexus.classworlds.ClassWorld world) throws IOException {
return new MavenUpCling(world).run(args, null, null, null, false);
}
@@ -56,7 +56,7 @@ public static int main(String[] args, ClassWorld world) throws IOException {
*/
public static int main(
String[] args,
- ClassWorld world,
+ org.codehaus.plexus.classworlds.ClassWorld world,
@Nullable InputStream stdIn,
@Nullable OutputStream stdOut,
@Nullable OutputStream stdErr)
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/extensions/BootstrapCoreExtensionManager.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/extensions/BootstrapCoreExtensionManager.java
index d0735af889a6..4ac6051730f5 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/extensions/BootstrapCoreExtensionManager.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/extensions/BootstrapCoreExtensionManager.java
@@ -32,6 +32,8 @@
import org.apache.maven.api.Session;
import org.apache.maven.api.annotations.Nullable;
import org.apache.maven.api.cache.RequestCacheFactory;
+import org.apache.maven.api.classworlds.ClassRealm;
+import org.apache.maven.api.classworlds.ClassWorld;
import org.apache.maven.api.cli.extensions.CoreExtension;
import org.apache.maven.api.di.Inject;
import org.apache.maven.api.di.Named;
@@ -67,8 +69,6 @@
import org.apache.maven.resolver.RepositorySystemSessionFactory;
import org.codehaus.plexus.DefaultPlexusContainer;
import org.codehaus.plexus.PlexusContainer;
-import org.codehaus.plexus.classworlds.ClassWorld;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.RepositorySystemSession.CloseableSession;
@@ -180,12 +180,13 @@ private CoreExtensionEntry createExtension(CoreExtension extension, List providedArtifacts = Collections.emptySet();
String classLoadingStrategy = extension.getClassLoadingStrategy();
if (STRATEGY_PARENT_FIRST.equals(classLoadingStrategy)) {
- realm.importFrom(parentRealm, "");
+ realm.importFrom(parentRealm.getClassLoader(), "");
} else if (STRATEGY_PLUGIN.equals(classLoadingStrategy)) {
coreExports.getExportedPackages().forEach((p, cl) -> realm.importFrom(cl, p));
providedArtifacts = coreExports.getExportedArtifacts();
} else if (STRATEGY_SELF_FIRST.equals(classLoadingStrategy)) {
- realm.setParentRealm(parentRealm);
+ ((org.codehaus.plexus.classworlds.realm.ClassRealm) realm)
+ .setParentRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) parentRealm);
} else {
throw new IllegalArgumentException("Unsupported class-loading strategy '"
+ classLoadingStrategy + "'. Supported values are: " + STRATEGY_PARENT_FIRST
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/PlexusContainerCapsuleFactory.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/PlexusContainerCapsuleFactory.java
index f1290b02e574..557e6ac6448a 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/PlexusContainerCapsuleFactory.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/PlexusContainerCapsuleFactory.java
@@ -31,6 +31,8 @@
import com.google.inject.Module;
import org.apache.maven.api.Constants;
import org.apache.maven.api.ProtoSession;
+import org.apache.maven.api.classworlds.ClassRealm;
+import org.apache.maven.api.classworlds.ClassWorld;
import org.apache.maven.api.cli.Logger;
import org.apache.maven.api.cli.extensions.CoreExtension;
import org.apache.maven.api.services.MessageBuilderFactory;
@@ -55,8 +57,6 @@
import org.codehaus.plexus.DefaultPlexusContainer;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
-import org.codehaus.plexus.classworlds.ClassWorld;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.logging.LoggerManager;
import org.slf4j.ILoggerFactory;
@@ -98,8 +98,8 @@ protected DefaultPlexusContainer container(
ClassRealm containerRealm =
setupContainerRealm(context.logger, classWorld, coreRealm, extClassPath, loadedExtensionsEntries);
ContainerConfiguration cc = new DefaultContainerConfiguration()
- .setClassWorld(classWorld)
- .setRealm(containerRealm)
+ .setClassWorld((org.codehaus.plexus.classworlds.ClassWorld) classWorld)
+ .setRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) containerRealm)
.setClassPathScanning(PlexusConstants.SCANNING_INDEX)
.setAutoWiring(true)
.setJSR250Lifecycle(true)
@@ -111,7 +111,7 @@ protected DefaultPlexusContainer container(
containerRealm,
collectExportedArtifacts(coreEntry, loadedExtensionsEntries),
collectExportedPackages(coreEntry, loadedExtensionsEntries));
- Thread.currentThread().setContextClassLoader(containerRealm);
+ Thread.currentThread().setContextClassLoader(containerRealm.getClassLoader());
DefaultPlexusContainer container = new DefaultPlexusContainer(cc, getCustomModule(context, exports));
// NOTE: To avoid inconsistencies, we'll use the TCCL exclusively for lookups
@@ -124,14 +124,18 @@ protected DefaultPlexusContainer container(
List failures = new ArrayList<>();
for (LoadedCoreExtension extension : loadedExtensions) {
container.discoverComponents(
- extension.entry().getClassRealm(),
+ (org.codehaus.plexus.classworlds.realm.ClassRealm)
+ extension.entry().getClassRealm(),
new AbstractModule() {
@Override
protected void configure() {
try {
container
.lookup(Injector.class)
- .discover(extension.entry().getClassRealm());
+ .discover(extension
+ .entry()
+ .getClassRealm()
+ .getClassLoader());
} catch (Throwable e) {
failures.add(new IllegalStateException(
"Injection failure in "
@@ -231,7 +235,8 @@ protected ClassRealm setupContainerRealm(
if (!extClassPath.isEmpty() || !extensions.isEmpty()) {
ClassRealm extRealm = classWorld.newRealm("maven.ext", null);
- extRealm.setParentRealm(coreRealm);
+ ((org.codehaus.plexus.classworlds.realm.ClassRealm) extRealm)
+ .setParentRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) coreRealm);
logger.debug("Populating class realm '" + extRealm.getId() + "'");
@@ -246,11 +251,11 @@ protected ClassRealm setupContainerRealm(
Set exportedPackages = entry.getExportedPackages();
ClassRealm realm = entry.getClassRealm();
for (String exportedPackage : exportedPackages) {
- extRealm.importFrom(realm, exportedPackage);
+ extRealm.importFrom(realm.getClassLoader(), exportedPackage);
}
if (exportedPackages.isEmpty()) {
// sisu uses realm imports to establish component visibility
- extRealm.importFrom(realm, realm.getId());
+ extRealm.importFrom(realm.getClassLoader(), realm.getId());
}
}
@@ -271,8 +276,8 @@ protected List loadCoreExtensions(
return List.of();
}
ContainerConfiguration cc = new DefaultContainerConfiguration()
- .setClassWorld(containerRealm.getWorld())
- .setRealm(containerRealm)
+ .setClassWorld((org.codehaus.plexus.classworlds.ClassWorld) containerRealm.getWorld())
+ .setRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) containerRealm)
.setClassPathScanning(PlexusConstants.SCANNING_INDEX)
.setAutoWiring(true)
.setJSR250Lifecycle(true)
diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTest.java
index b1115a4dce4f..0be124387f22 100644
--- a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTest.java
+++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTest.java
@@ -26,11 +26,11 @@
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
+import org.apache.maven.api.classworlds.ClassWorld;
import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.InvokerException;
import org.apache.maven.api.cli.Parser;
import org.apache.maven.cling.invoker.ProtoLookup;
-import org.codehaus.plexus.classworlds.ClassWorld;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java
index 91a69729b021..e02e0369cf7c 100644
--- a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java
+++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java
@@ -29,12 +29,12 @@
import java.util.Map;
import eu.maveniverse.maven.mimir.testing.MimirInfuser;
+import org.apache.maven.api.classworlds.ClassWorld;
import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.InvokerException;
import org.apache.maven.api.cli.Parser;
import org.apache.maven.api.cli.ParserRequest;
import org.apache.maven.jline.JLineMessageBuilderFactory;
-import org.codehaus.plexus.classworlds.ClassWorld;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -156,7 +156,7 @@ protected Map invoke(Path cwd, Path userHome, Collection
}
protected ClassWorld createClassWorld() {
- return new ClassWorld("plexus.core", ClassLoader.getSystemClassLoader());
+ return new org.codehaus.plexus.classworlds.ClassWorld("plexus.core", ClassLoader.getSystemClassLoader());
}
protected abstract Invoker createInvoker(ClassWorld classWorld);
diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/resident/ResidentMavenInvokerTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/resident/ResidentMavenInvokerTest.java
index c59ad3249195..9f57715e464a 100644
--- a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/resident/ResidentMavenInvokerTest.java
+++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/resident/ResidentMavenInvokerTest.java
@@ -24,12 +24,12 @@
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
+import org.apache.maven.api.classworlds.ClassWorld;
import org.apache.maven.api.cli.Invoker;
import org.apache.maven.api.cli.Parser;
import org.apache.maven.cling.invoker.ProtoLookup;
import org.apache.maven.cling.invoker.mvn.MavenInvokerTestSupport;
import org.apache.maven.cling.invoker.mvn.MavenParser;
-import org.codehaus.plexus.classworlds.ClassWorld;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
diff --git a/impl/maven-core/pom.xml b/impl/maven-core/pom.xml
index 07ffa6662b12..0d0a834e3adc 100644
--- a/impl/maven-core/pom.xml
+++ b/impl/maven-core/pom.xml
@@ -152,8 +152,12 @@ under the License.
- org.codehaus.plexus
- plexus-classworlds
+ org.apache.maven
+ maven-api-classworlds
+
+
+ org.apache.maven
+ maven-classworlds
javax.inject
@@ -396,6 +400,40 @@ under the License.
org.apache.maven.toolchain.ToolchainManagerPrivate
org.apache.maven.toolchain.ToolchainPrivate
org.apache.maven.toolchain.ToolchainsBuilder
+
+ org.apache.maven.classrealm.ClassRealmManager#createExtensionRealm(org.apache.maven.model.Plugin,java.util.List):METHOD_RETURN_TYPE_CHANGED
+ org.apache.maven.classrealm.ClassRealmManager#createPluginRealm(org.apache.maven.model.Plugin,java.lang.ClassLoader,java.util.List,java.util.Map,java.util.List):METHOD_RETURN_TYPE_CHANGED
+ org.apache.maven.classrealm.ClassRealmManager#createProjectRealm(org.apache.maven.model.Model,java.util.List):METHOD_RETURN_TYPE_CHANGED
+ org.apache.maven.classrealm.ClassRealmManager#getCoreRealm():METHOD_RETURN_TYPE_CHANGED
+ org.apache.maven.classrealm.ClassRealmManager#getMavenApiRealm():METHOD_RETURN_TYPE_CHANGED
+ org.apache.maven.classrealm.ClassRealmManagerDelegate#setupRealm(org.codehaus.plexus.classworlds.realm.ClassRealm,org.apache.maven.classrealm.ClassRealmRequest):METHOD_REMOVED
+ org.apache.maven.classrealm.DefaultClassRealmManager#createExtensionRealm(org.apache.maven.model.Plugin,java.util.List):METHOD_RETURN_TYPE_CHANGED
+ org.apache.maven.classrealm.DefaultClassRealmManager#createPluginRealm(org.apache.maven.model.Plugin,java.lang.ClassLoader,java.util.List,java.util.Map,java.util.List):METHOD_RETURN_TYPE_CHANGED
+ org.apache.maven.classrealm.DefaultClassRealmManager#createProjectRealm(org.apache.maven.model.Model,java.util.List):METHOD_RETURN_TYPE_CHANGED
+ org.apache.maven.classrealm.DefaultClassRealmManager#getCoreRealm():METHOD_RETURN_TYPE_CHANGED
+ org.apache.maven.classrealm.DefaultClassRealmManager#getMavenApiRealm():METHOD_RETURN_TYPE_CHANGED
+ org.apache.maven.plugin.BuildPluginManager#getPluginRealm(org.apache.maven.execution.MavenSession,org.apache.maven.plugin.descriptor.PluginDescriptor):METHOD_RETURN_TYPE_CHANGED
+ org.apache.maven.plugin.DefaultBuildPluginManager#getPluginRealm(org.apache.maven.execution.MavenSession,org.apache.maven.plugin.descriptor.PluginDescriptor):METHOD_RETURN_TYPE_CHANGED
+ org.apache.maven.plugin.ExtensionRealmCache#put(org.apache.maven.plugin.ExtensionRealmCache$Key,org.codehaus.plexus.classworlds.realm.ClassRealm,org.apache.maven.project.ExtensionDescriptor,java.util.List):METHOD_REMOVED
+ org.apache.maven.plugin.ExtensionRealmCache$CacheRecord#getRealm():METHOD_RETURN_TYPE_CHANGED
+ org.apache.maven.plugin.PluginContainerException#getPluginRealm():METHOD_RETURN_TYPE_CHANGED
+ org.apache.maven.plugin.PluginContainerException#PluginContainerException(org.apache.maven.model.Plugin,org.codehaus.plexus.classworlds.realm.ClassRealm,java.lang.String,org.codehaus.plexus.configuration.PlexusConfigurationException):CONSTRUCTOR_REMOVED
+ org.apache.maven.plugin.PluginContainerException#PluginContainerException(org.apache.maven.plugin.descriptor.MojoDescriptor,org.codehaus.plexus.classworlds.realm.ClassRealm,java.lang.String,java.lang.Throwable):CONSTRUCTOR_REMOVED
+ org.apache.maven.plugin.PluginContainerException#PluginContainerException(org.apache.maven.model.Plugin,org.codehaus.plexus.classworlds.realm.ClassRealm,java.lang.String,org.codehaus.plexus.component.repository.exception.ComponentRepositoryException):CONSTRUCTOR_REMOVED
+ org.apache.maven.plugin.PluginContainerException#PluginContainerException(org.apache.maven.model.Plugin,org.codehaus.plexus.classworlds.realm.ClassRealm,java.lang.String,java.lang.Throwable):CONSTRUCTOR_REMOVED
+ org.apache.maven.plugin.PluginContainerException#PluginContainerException(org.apache.maven.plugin.descriptor.MojoDescriptor,org.codehaus.plexus.classworlds.realm.ClassRealm,java.lang.String,org.codehaus.plexus.component.repository.exception.ComponentLookupException):CONSTRUCTOR_REMOVED
+ org.apache.maven.plugin.PluginManagerException#PluginManagerException(org.apache.maven.plugin.descriptor.MojoDescriptor,org.apache.maven.project.MavenProject,java.lang.String,org.codehaus.plexus.classworlds.realm.NoSuchRealmException):CONSTRUCTOR_REMOVED
+ org.apache.maven.plugin.PluginRealmCache#put(org.apache.maven.plugin.PluginRealmCache$Key,org.codehaus.plexus.classworlds.realm.ClassRealm,java.util.List):METHOD_REMOVED
+ org.apache.maven.plugin.PluginRealmCache$CacheRecord#getRealm():METHOD_RETURN_TYPE_CHANGED
+ org.apache.maven.plugin.PluginRealmCache$CacheRecord#PluginRealmCache$CacheRecord(org.codehaus.plexus.classworlds.realm.ClassRealm,java.util.List):CONSTRUCTOR_REMOVED
+ org.apache.maven.project.MavenProject#getClassRealm():METHOD_RETURN_TYPE_CHANGED
+ org.apache.maven.project.MavenProject#setClassRealm(org.codehaus.plexus.classworlds.realm.ClassRealm):METHOD_REMOVED
+ org.apache.maven.project.ProjectRealmCache#put(org.apache.maven.project.ProjectRealmCache$Key,org.codehaus.plexus.classworlds.realm.ClassRealm,org.eclipse.aether.graph.DependencyFilter):METHOD_REMOVED
+ org.apache.maven.project.ProjectRealmCache$CacheRecord#getRealm():METHOD_RETURN_TYPE_CHANGED
+
+ org.apache.maven.plugin.DefaultExtensionRealmCache#put(org.apache.maven.plugin.ExtensionRealmCache$Key,org.codehaus.plexus.classworlds.realm.ClassRealm,org.apache.maven.project.ExtensionDescriptor,java.util.List):METHOD_REMOVED
+ org.apache.maven.plugin.DefaultPluginRealmCache#put(org.apache.maven.plugin.PluginRealmCache$Key,org.codehaus.plexus.classworlds.realm.ClassRealm,java.util.List):METHOD_REMOVED
+ org.apache.maven.project.DefaultProjectRealmCache#put(org.apache.maven.project.ProjectRealmCache$Key,org.codehaus.plexus.classworlds.realm.ClassRealm,org.eclipse.aether.graph.DependencyFilter):METHOD_REMOVED
diff --git a/impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java b/impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java
index 23e85a14b01e..0e6db84ba2f0 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java
@@ -465,7 +465,9 @@ protected Collection getProjectScopedExtensionComponents(Collection apiV4Imports = new HashMap<>();
- apiV4Imports.put("org.apache.maven.api", containerRealm);
- apiV4Imports.put("org.slf4j", containerRealm);
+ apiV4Imports.put("org.apache.maven.api", containerRealm.getClassLoader());
+ apiV4Imports.put("org.slf4j", containerRealm.getClassLoader());
this.maven4ApiRealm = createRealm(API_V4_REALMID, RealmType.Core, null, null, apiV4Imports, null);
this.providedArtifacts = exports.getExportedArtifacts();
@@ -207,6 +213,8 @@ private ClassRealm createRealm(
populateRealm(classRealm, constituents);
+ applyModuleAccessDescriptors(classRealm);
+
return classRealm;
}
@@ -219,7 +227,7 @@ public ClassRealm getCoreRealm() {
public ClassRealm createProjectRealm(Model model, List artifacts) {
Objects.requireNonNull(model, "model cannot be null");
- ClassLoader parent = getMavenApiRealm();
+ ClassLoader parent = getMavenApiRealm().getClassLoader();
return createRealm(getKey(model), RealmType.Project, parent, null, null, artifacts);
}
@@ -232,7 +240,8 @@ private static String getKey(Model model) {
public ClassRealm createExtensionRealm(Plugin plugin, List artifacts) {
Objects.requireNonNull(plugin, "plugin cannot be null");
- Map foreignImports = Collections.singletonMap("", getMavenApiRealm());
+ Map foreignImports =
+ Collections.singletonMap("", getMavenApiRealm().getClassLoader());
return createRealm(
getKey(plugin, true), RealmType.Extension, PARENT_CLASSLOADER, null, foreignImports, artifacts);
@@ -302,7 +311,7 @@ private void callDelegates(
for (ClassRealmManagerDelegate delegate : delegates) {
try {
- delegate.setupRealm(classRealm, request);
+ delegate.setupRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) classRealm, request);
} catch (Exception e) {
logger.error(
delegate.getClass().getName() + " failed to setup class realm " + classRealm + ": "
@@ -353,7 +362,7 @@ private void wireRealm(ClassRealm classRealm, List parentImports, Map resources = implRealm.getResources(MODULE_ACCESS_DESCRIPTOR);
+ while (resources.hasMoreElements()) {
+ URL resource = resources.nextElement();
+ try (BufferedReader reader =
+ new BufferedReader(new InputStreamReader(resource.openStream(), StandardCharsets.UTF_8))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ line = line.trim();
+ if (line.isEmpty() || line.startsWith("#")) {
+ continue;
+ }
+ applyModuleAccessDirective(implRealm, line);
+ }
+ }
+ }
+ } catch (IOException e) {
+ logger.debug("Failed to read module-access descriptors for realm {}", classRealm.getId(), e);
+ }
+ }
+
+ private void applyModuleAccessDirective(org.codehaus.plexus.classworlds.realm.ClassRealm realm, String line) {
+ if (line.startsWith("add-exports ")) {
+ applyExportOrOpen(realm, line.substring("add-exports ".length()).trim(), false);
+ } else if (line.startsWith("add-opens ")) {
+ applyExportOrOpen(realm, line.substring("add-opens ".length()).trim(), true);
+ } else if (line.startsWith("add-reads ")) {
+ String module = line.substring("add-reads ".length()).trim();
+ realm.addReads(module);
+ } else {
+ logger.debug("Unknown module-access directive: {}", line);
+ }
+ }
+
+ private void applyExportOrOpen(org.codehaus.plexus.classworlds.realm.ClassRealm realm, String spec, boolean open) {
+ int slash = spec.indexOf('/');
+ if (slash <= 0) {
+ logger.debug("Invalid module-access directive (missing '/'): {}", spec);
+ return;
+ }
+ String module = spec.substring(0, slash);
+ String pkg = spec.substring(slash + 1);
+ int eq = pkg.indexOf('=');
+ if (eq > 0) {
+ String target = pkg.substring(eq + 1).trim();
+ pkg = pkg.substring(0, eq).trim();
+ if (!"ALL-UNNAMED".equals(target)) {
+ logger.warn("module-access directive target '{}' ignored, only ALL-UNNAMED is supported", target);
+ }
+ }
+ if (open) {
+ realm.addOpens(module, pkg);
+ } else {
+ realm.addExports(module, pkg);
+ }
+ }
}
diff --git a/impl/maven-core/src/main/java/org/apache/maven/configuration/internal/EnhancedComponentConfigurator.java b/impl/maven-core/src/main/java/org/apache/maven/configuration/internal/EnhancedComponentConfigurator.java
index 334eafe3af9c..8b779726f617 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/configuration/internal/EnhancedComponentConfigurator.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/configuration/internal/EnhancedComponentConfigurator.java
@@ -52,7 +52,8 @@ public void configureComponent(
try {
ClassRealmConverter.pushContextRealm(realm);
- this.configureComponent(component, configuration, evaluator, (ClassLoader) realm, listener);
+ this.configureComponent(
+ component, configuration, evaluator, realm != null ? realm.getClassLoader() : null, listener);
} finally {
ClassRealmConverter.popContextRealm();
}
diff --git a/impl/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExports.java b/impl/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExports.java
index aec37e704177..617b64cc9859 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExports.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExports.java
@@ -23,7 +23,7 @@
import java.util.Map;
import java.util.Set;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
+import org.apache.maven.api.classworlds.ClassRealm;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.collectingAndThen;
@@ -47,7 +47,8 @@ public CoreExports(CoreExtensionEntry entry) {
public CoreExports(ClassRealm realm, Set exportedArtifacts, Set exportedPackages) {
this.artifacts = Collections.unmodifiableSet(new HashSet<>(exportedArtifacts));
this.packages = exportedPackages.stream()
- .collect(collectingAndThen(toMap(identity(), v -> realm), Collections::unmodifiableMap));
+ .collect(collectingAndThen(
+ toMap(identity(), v -> realm.getClassLoader()), Collections::unmodifiableMap));
}
/**
diff --git a/impl/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExtensionEntry.java b/impl/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExtensionEntry.java
index f19a5bf6ef57..03f7dbdcc6e9 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExtensionEntry.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExtensionEntry.java
@@ -29,10 +29,10 @@
import java.util.LinkedHashSet;
import java.util.Set;
+import org.apache.maven.api.classworlds.ClassRealm;
import org.apache.maven.api.xml.XmlNode;
import org.apache.maven.project.ExtensionDescriptor;
import org.apache.maven.project.ExtensionDescriptorBuilder;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
/**
* Provides information about artifacts (identified by groupId:artifactId string key) and classpath elements exported by
@@ -107,7 +107,7 @@ public static CoreExtensionEntry discoverFrom(ClassRealm loader) {
Set packages = new LinkedHashSet<>();
try {
- Enumeration urls = loader.getResources(BUILDER.getExtensionDescriptorLocation());
+ Enumeration urls = loader.getClassLoader().getResources(BUILDER.getExtensionDescriptorLocation());
while (urls.hasMoreElements()) {
try (InputStream is = urls.nextElement().openStream()) {
diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/CoreRealm.java b/impl/maven-core/src/main/java/org/apache/maven/internal/CoreRealm.java
index b27cec352ffe..150d8101cfb5 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/internal/CoreRealm.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/internal/CoreRealm.java
@@ -20,8 +20,8 @@
import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Nonnull;
-import org.codehaus.plexus.classworlds.ClassWorld;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
+import org.apache.maven.api.classworlds.ClassRealm;
+import org.apache.maven.api.classworlds.ClassWorld;
/**
* Access to core {@link ClassRealm}.
diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProject.java b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProject.java
index 5c53a246a5c5..5283ae430bd6 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProject.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProject.java
@@ -58,7 +58,11 @@ public DefaultProject(InternalMavenSession session, MavenProject project) {
this.project = project;
ClassLoader ttcl = Thread.currentThread().getContextClassLoader();
try {
- Thread.currentThread().setContextClassLoader(project.getClassRealm());
+ Thread.currentThread()
+ .setContextClassLoader(
+ project.getClassRealm() != null
+ ? project.getClassRealm().getClassLoader()
+ : null);
this.packaging = session.requirePackaging(project.getPackaging());
} finally {
Thread.currentThread().setContextClassLoader(ttcl);
diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/internal/DefaultCoreRealm.java b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/internal/DefaultCoreRealm.java
index 5d85bab5b668..2d0f7bf48bd3 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/internal/DefaultCoreRealm.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/internal/DefaultCoreRealm.java
@@ -22,9 +22,9 @@
import javax.inject.Named;
import javax.inject.Singleton;
+import org.apache.maven.api.classworlds.ClassRealm;
import org.apache.maven.internal.CoreRealm;
import org.codehaus.plexus.PlexusContainer;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
@Named
@Singleton
diff --git a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolver.java b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolver.java
index f41b00f9056a..e516fc575362 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolver.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolver.java
@@ -33,6 +33,7 @@
import java.util.stream.Collectors;
import org.apache.maven.RepositoryUtils;
+import org.apache.maven.api.classworlds.ClassRealm;
import org.apache.maven.api.services.MessageBuilderFactory;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.ArtifactUtils;
@@ -112,9 +113,12 @@ public void resolveProjectDependencies(
throws LifecycleExecutionException {
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
try {
- ClassLoader projectRealm = project.getClassRealm();
- if (projectRealm != null && projectRealm != tccl) {
- Thread.currentThread().setContextClassLoader(projectRealm);
+ ClassRealm projectRealm = project.getClassRealm();
+ if (projectRealm != null) {
+ ClassLoader projectClassLoader = projectRealm.getClassLoader();
+ if (projectClassLoader != tccl) {
+ Thread.currentThread().setContextClassLoader(projectClassLoader);
+ }
}
if (project.getDependencyArtifacts() == null) {
diff --git a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/BuilderCommon.java b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/BuilderCommon.java
index f94271ed1f22..30bb7ddccfd6 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/BuilderCommon.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/BuilderCommon.java
@@ -28,6 +28,7 @@
import java.util.Set;
import org.apache.maven.api.MonotonicClock;
+import org.apache.maven.api.classworlds.ClassRealm;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.execution.BuildFailure;
import org.apache.maven.execution.ExecutionEvent;
@@ -55,7 +56,6 @@
import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException;
import org.apache.maven.plugin.version.PluginVersionResolutionException;
import org.apache.maven.project.MavenProject;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -201,7 +201,7 @@ public void handleBuildError(
public static void attachToThread(MavenProject currentProject) {
ClassRealm projectRealm = currentProject.getClassRealm();
if (projectRealm != null) {
- Thread.currentThread().setContextClassLoader(projectRealm);
+ Thread.currentThread().setContextClassLoader(projectRealm.getClassLoader());
}
}
diff --git a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java
index 460d15bb32e3..6069d848025a 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java
@@ -43,6 +43,7 @@
import org.apache.maven.api.Lifecycle;
import org.apache.maven.api.MonotonicClock;
+import org.apache.maven.api.classworlds.ClassRealm;
import org.apache.maven.api.services.LifecycleRegistry;
import org.apache.maven.api.services.MavenException;
import org.apache.maven.api.xml.XmlNode;
@@ -84,7 +85,6 @@
import org.apache.maven.plugin.descriptor.Parameter;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.project.MavenProject;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.eclipse.aether.repository.RemoteRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -1109,7 +1109,7 @@ private MojoExecutionConfigurator mojoExecutionConfigurator(MojoExecution mojoEx
public static void attachToThread(MavenProject currentProject) {
ClassRealm projectRealm = currentProject.getClassRealm();
if (projectRealm != null) {
- Thread.currentThread().setContextClassLoader(projectRealm);
+ Thread.currentThread().setContextClassLoader(projectRealm.getClassLoader());
}
}
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/BuildPluginManager.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/BuildPluginManager.java
index b3741041e08b..729f656372ec 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/BuildPluginManager.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/BuildPluginManager.java
@@ -20,11 +20,11 @@
import java.util.List;
+import org.apache.maven.api.classworlds.ClassRealm;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.repository.RemoteRepository;
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java
index e395d1ed000b..cc96dda4ba1d 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java
@@ -30,6 +30,7 @@
import org.apache.maven.api.Project;
import org.apache.maven.api.Service;
+import org.apache.maven.api.classworlds.ClassRealm;
import org.apache.maven.api.services.MavenException;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.MojoExecutionEvent;
@@ -43,7 +44,6 @@
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.repository.RemoteRepository;
import org.slf4j.LoggerFactory;
@@ -113,7 +113,7 @@ public void executeMojo(MavenSession session, MojoExecution mojoExecution)
}
ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
- Thread.currentThread().setContextClassLoader(pluginRealm);
+ Thread.currentThread().setContextClassLoader(pluginRealm.getClassLoader());
MavenSession oldSession = legacySupport.getSession();
@@ -171,7 +171,7 @@ public void executeMojo(MavenSession session, MojoExecution mojoExecution)
PrintStream ps = new PrintStream(os);
ps.println(
"A required class was missing while executing " + mojoDescriptor.getId() + ": " + e.getMessage());
- pluginRealm.display(ps);
+ ((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm).display(ps);
Exception wrapper = new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), e);
throw new PluginExecutionException(mojoExecution, project, wrapper);
} catch (LinkageError e) {
@@ -181,7 +181,7 @@ public void executeMojo(MavenSession session, MojoExecution mojoExecution)
PrintStream ps = new PrintStream(os);
ps.println("An API incompatibility was encountered while executing " + mojoDescriptor.getId() + ": "
+ e.getClass().getName() + ": " + e.getMessage());
- pluginRealm.display(ps);
+ ((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm).display(ps);
Exception wrapper = new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), e);
throw new PluginExecutionException(mojoExecution, project, wrapper);
} catch (ClassCastException e) {
@@ -191,7 +191,7 @@ public void executeMojo(MavenSession session, MojoExecution mojoExecution)
PrintStream ps = new PrintStream(os);
ps.println("A type incompatibility occurred while executing " + mojoDescriptor.getId() + ": "
+ e.getMessage());
- pluginRealm.display(ps);
+ ((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm).display(ps);
throw new PluginExecutionException(mojoExecution, project, os.toString(), e);
} catch (RuntimeException e) {
mojoExecutionListener.afterExecutionFailure(
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java
index ce3cb5135f3e..bd6f5ea744b6 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java
@@ -28,11 +28,11 @@
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
+import org.apache.maven.api.classworlds.ClassRealm;
+import org.apache.maven.api.classworlds.NoSuchRealmException;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.project.ExtensionDescriptor;
import org.apache.maven.project.MavenProject;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
-import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable;
/**
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginRealmCache.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginRealmCache.java
index 1e822a2ccb0c..badce69522b9 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginRealmCache.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginRealmCache.java
@@ -29,11 +29,11 @@
import java.util.concurrent.ConcurrentHashMap;
import org.apache.maven.RepositoryUtils;
+import org.apache.maven.api.classworlds.ClassRealm;
+import org.apache.maven.api.classworlds.NoSuchRealmException;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.Plugin;
import org.apache.maven.project.MavenProject;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
-import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.graph.DependencyFilter;
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/ExtensionRealmCache.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/ExtensionRealmCache.java
index 22bb57ed2d1b..6ba35ae771ac 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/ExtensionRealmCache.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/ExtensionRealmCache.java
@@ -20,10 +20,10 @@
import java.util.List;
+import org.apache.maven.api.classworlds.ClassRealm;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.project.ExtensionDescriptor;
import org.apache.maven.project.MavenProject;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
/**
* Caches extension class realms. Warning: This is an internal utility interface that is only public
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginContainerException.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginContainerException.java
index 55ac337cddb2..63eed33efadc 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginContainerException.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginContainerException.java
@@ -18,9 +18,9 @@
*/
package org.apache.maven.plugin;
+import org.apache.maven.api.classworlds.ClassRealm;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.component.repository.exception.ComponentRepositoryException;
import org.codehaus.plexus.configuration.PlexusConfigurationException;
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginManagerException.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginManagerException.java
index f3fd390fc0de..4b47a6fd5c66 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginManagerException.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginManagerException.java
@@ -18,13 +18,13 @@
*/
package org.apache.maven.plugin;
+import org.apache.maven.api.classworlds.NoSuchRealmException;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.PlexusContainerException;
-import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
import org.codehaus.plexus.component.repository.exception.ComponentRepositoryException;
import org.codehaus.plexus.configuration.PlexusConfigurationException;
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginRealmCache.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginRealmCache.java
index 7fcbd87949a6..6fb5535e4a1a 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginRealmCache.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginRealmCache.java
@@ -21,10 +21,10 @@
import java.util.List;
import java.util.Map;
+import org.apache.maven.api.classworlds.ClassRealm;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.Plugin;
import org.apache.maven.project.MavenProject;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.repository.RemoteRepository;
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
index 15f3d8df7b64..575bb51fdfe7 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
@@ -50,6 +50,7 @@
import org.apache.maven.api.Project;
import org.apache.maven.api.Service;
import org.apache.maven.api.Session;
+import org.apache.maven.api.classworlds.ClassRealm;
import org.apache.maven.api.plugin.descriptor.Resolution;
import org.apache.maven.api.services.DependencyResolver;
import org.apache.maven.api.services.DependencyResolverResult;
@@ -105,7 +106,6 @@
import org.apache.maven.session.scope.internal.SessionScopeModule;
import org.codehaus.plexus.DefaultPlexusContainer;
import org.codehaus.plexus.PlexusContainer;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.component.composition.CycleDetectedInComponentGraphException;
import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
import org.codehaus.plexus.component.configurator.ComponentConfigurator;
@@ -357,10 +357,10 @@ public void setupPluginRealm(
List pluginArtifacts = extensionRecord.getArtifacts();
for (ComponentDescriptor> componentDescriptor : pluginDescriptor.getComponents()) {
- componentDescriptor.setRealm(pluginRealm);
+ componentDescriptor.setRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm);
}
- pluginDescriptor.setClassRealm(pluginRealm);
+ pluginDescriptor.setClassRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm);
pluginDescriptor.setArtifacts(pluginArtifacts);
} else {
boolean v4api = pluginDescriptor.getMojos().stream().anyMatch(MojoDescriptor::isV4Api);
@@ -381,10 +381,10 @@ public void setupPluginRealm(
pluginDescriptor.getClassRealm(), pluginDescriptor.getArtifacts());
});
- pluginDescriptor.setClassRealm(cacheRecord.getRealm());
+ pluginDescriptor.setClassRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) cacheRecord.getRealm());
pluginDescriptor.setArtifacts(new ArrayList<>(cacheRecord.getArtifacts()));
for (ComponentDescriptor> componentDescriptor : pluginDescriptor.getComponents()) {
- componentDescriptor.setRealm(cacheRecord.getRealm());
+ componentDescriptor.setRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) cacheRecord.getRealm());
}
pluginRealmCache.register(project, cacheKey, cacheRecord);
@@ -427,7 +427,7 @@ private void createPluginRealm(
discoverPluginComponents(pluginRealm, plugin, pluginDescriptor);
pluginDescriptor.setDependencyNode(result.getRoot());
- pluginDescriptor.setClassRealm(pluginRealm);
+ pluginDescriptor.setClassRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm);
pluginDescriptor.setArtifacts(pluginArtifacts);
}
@@ -439,16 +439,16 @@ private void discoverPluginComponents(
if (pluginDescriptor != null) {
for (MojoDescriptor mojo : pluginDescriptor.getMojos()) {
if (!mojo.isV4Api()) {
- mojo.setRealm(pluginRealm);
+ mojo.setRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm);
container.addComponentDescriptor(mojo);
}
}
}
- Thread.currentThread().setContextClassLoader(pluginRealm);
+ Thread.currentThread().setContextClassLoader(pluginRealm.getClassLoader());
((DefaultPlexusContainer) container)
.discoverComponents(
- pluginRealm,
+ (org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm,
new SessionScopeModule(container.lookup(SessionScope.class)),
new MojoExecutionScopeModule(container.lookup(MojoExecutionScope.class)),
new PluginConfigurationModule(plugin.getDelegate()),
@@ -479,12 +479,14 @@ private Map calcImports(
MavenProject project, ClassLoader parent, List imports, boolean v4api) {
Map foreignImports = new HashMap<>();
- ClassLoader projectRealm = project.getClassRealm();
+ ClassRealm projectRealm = project.getClassRealm();
if (projectRealm != null) {
- foreignImports.put("", projectRealm);
+ foreignImports.put("", projectRealm.getClassLoader());
} else {
foreignImports.put(
- "", v4api ? classRealmManager.getMaven4ApiRealm() : classRealmManager.getMavenApiRealm());
+ "",
+ (v4api ? classRealmManager.getMaven4ApiRealm() : classRealmManager.getMavenApiRealm())
+ .getClassLoader());
}
if (parent != null && imports != null) {
@@ -523,10 +525,11 @@ public T getConfiguredMojo(Class mojoInterface, MavenSession session, Moj
// We are forcing the use of the plugin realm for all lookups that might occur during
// the lifecycle that is part of the lookup. Here we are specifically trying to keep
// lookups that occur in contextualize calls in line with the right realm.
- ClassRealm oldLookupRealm = container.setLookupRealm(pluginRealm);
+ ClassRealm oldLookupRealm =
+ container.setLookupRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm);
ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
- Thread.currentThread().setContextClassLoader(pluginRealm);
+ Thread.currentThread().setContextClassLoader(pluginRealm.getClassLoader());
try {
if (mojoDescriptor.isV4Api()) {
@@ -536,7 +539,7 @@ public T getConfiguredMojo(Class mojoInterface, MavenSession session, Moj
}
} finally {
Thread.currentThread().setContextClassLoader(oldClassLoader);
- container.setLookupRealm(oldLookupRealm);
+ container.setLookupRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) oldLookupRealm);
}
}
@@ -558,7 +561,7 @@ private T loadV4Mojo(
LoggerFactory.getLogger(mojoExecution.getMojoDescriptor().getFullGoalName()));
try {
Injector injector = Injector.create();
- injector.discover(pluginRealm);
+ injector.discover(pluginRealm.getClassLoader());
// Add known classes
// TODO: get those from the existing plexus scopes ?
injector.bindInstance(Session.class, sessionV4);
@@ -703,7 +706,7 @@ private T loadV3Mojo(
ps.println("Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '"
+ pluginDescriptor.getId() + "'. A required class is missing: "
+ cause.getMessage());
- pluginRealm.display(ps);
+ ((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm).display(ps);
throw new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), cause);
} else if (cause instanceof LinkageError) {
@@ -712,7 +715,7 @@ private T loadV3Mojo(
ps.println("Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '"
+ pluginDescriptor.getId() + "' due to an API incompatibility: "
+ e.getClass().getName() + ": " + cause.getMessage());
- pluginRealm.display(ps);
+ ((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm).display(ps);
throw new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), cause);
}
@@ -814,7 +817,12 @@ private void populateMojoExecutionFields(
+ configuratorId + " configurator -->");
}
- configurator.configureComponent(mojo, configuration, expressionEvaluator, pluginRealm, validator);
+ configurator.configureComponent(
+ mojo,
+ configuration,
+ expressionEvaluator,
+ (org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm,
+ validator);
logger.debug("-- end configuration --");
@@ -850,7 +858,7 @@ private void populateMojoExecutionFields(
PrintStream ps = new PrintStream(os);
ps.println("A required class was missing during configuration of mojo " + mojoDescriptor.getId() + ": "
+ e.getMessage());
- pluginRealm.display(ps);
+ ((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm).display(ps);
throw new PluginConfigurationException(mojoDescriptor.getPluginDescriptor(), os.toString(), e);
} catch (LinkageError e) {
@@ -858,7 +866,7 @@ private void populateMojoExecutionFields(
PrintStream ps = new PrintStream(os);
ps.println("An API incompatibility was encountered during configuration of mojo " + mojoDescriptor.getId()
+ ": " + e.getClass().getName() + ": " + e.getMessage());
- pluginRealm.display(ps);
+ ((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm).display(ps);
throw new PluginConfigurationException(mojoDescriptor.getPluginDescriptor(), os.toString(), e);
} finally {
diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java
index 6bde396a97e7..b0b64fbf83d2 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java
@@ -33,6 +33,7 @@
import java.util.Set;
import org.apache.maven.RepositoryUtils;
+import org.apache.maven.api.classworlds.ClassRealm;
import org.apache.maven.api.xml.XmlNode;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.InvalidRepositoryException;
@@ -49,7 +50,6 @@
import org.apache.maven.plugin.PluginManagerException;
import org.apache.maven.plugin.PluginResolutionException;
import org.apache.maven.plugin.version.PluginVersionResolutionException;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.util.filter.ExclusionsDependencyFilter;
@@ -246,7 +246,7 @@ public synchronized ProjectRealmCache.CacheRecord createProjectRealm(
}
for (String export : exports) {
- projectRealm.importFrom(extensionRealm, export);
+ projectRealm.importFrom(extensionRealm.getClassLoader(), export);
}
}
@@ -265,13 +265,16 @@ record = projectRealmCache.put(projectRealmKey, projectRealm, extensionArtifactF
@Override
public void selectProjectRealm(MavenProject project) {
- ClassLoader projectRealm = project.getClassRealm();
+ ClassRealm projectRealm = project.getClassRealm();
+ ClassLoader classLoader;
if (projectRealm == null) {
- projectRealm = classRealmManager.getCoreRealm();
+ classLoader = classRealmManager.getCoreRealm().getClassLoader();
+ } else {
+ classLoader = projectRealm.getClassLoader();
}
- Thread.currentThread().setContextClassLoader(projectRealm);
+ Thread.currentThread().setContextClassLoader(classLoader);
}
private static boolean containsExpression(String value) {
diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java
index 9111177c3489..54099459cdf5 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java
@@ -27,8 +27,8 @@
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
-import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
+import org.apache.maven.api.classworlds.ClassRealm;
+import org.apache.maven.api.classworlds.NoSuchRealmException;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable;
import org.eclipse.aether.graph.DependencyFilter;
diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/MavenProject.java b/impl/maven-core/src/main/java/org/apache/maven/project/MavenProject.java
index e6c2c05acbf0..ecdbf2b0eec1 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/project/MavenProject.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/project/MavenProject.java
@@ -43,6 +43,7 @@
import org.apache.maven.api.ProjectScope;
import org.apache.maven.api.SourceRoot;
import org.apache.maven.api.annotations.Nonnull;
+import org.apache.maven.api.classworlds.ClassRealm;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
@@ -79,7 +80,6 @@
import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
import org.apache.maven.model.root.RootLocator;
import org.apache.maven.project.artifact.InvalidDependencyVersionException;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.repository.RemoteRepository;
diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectRealmCache.java b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectRealmCache.java
index dd07b49842d1..05c204e1475a 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectRealmCache.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectRealmCache.java
@@ -20,7 +20,7 @@
import java.util.List;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
+import org.apache.maven.api.classworlds.ClassRealm;
import org.eclipse.aether.graph.DependencyFilter;
/**
diff --git a/impl/maven-core/src/main/resources/META-INF/maven/extension.xml b/impl/maven-core/src/main/resources/META-INF/maven/extension.xml
index 56667ed69e35..570fece8f4c8 100644
--- a/impl/maven-core/src/main/resources/META-INF/maven/extension.xml
+++ b/impl/maven-core/src/main/resources/META-INF/maven/extension.xml
@@ -77,12 +77,9 @@ under the License.
org.eclipse.aether.version
org.eclipse.aether.util
-
+
org.codehaus.plexus.classworlds
-
- org.codehaus.classworlds
-
org.codehaus.plexus.util.xml.Xpp3Dom
org.codehaus.plexus.util.xml.Xpp3DomBuilder
@@ -154,7 +151,7 @@ under the License.
org.apache.maven:maven-api-xml
classworlds:classworlds
- org.codehaus.plexus:plexus-classworlds
+ org.apache.maven:maven-classworlds
org.codehaus.plexus:plexus-component-api
org.codehaus.plexus:plexus-container-default
plexus:plexus-container-default
diff --git a/impl/maven-core/src/test/java/org/apache/maven/classrealm/DefaultClassRealmManagerTest.java b/impl/maven-core/src/test/java/org/apache/maven/classrealm/DefaultClassRealmManagerTest.java
index 411a483b224a..08862fad9635 100644
--- a/impl/maven-core/src/test/java/org/apache/maven/classrealm/DefaultClassRealmManagerTest.java
+++ b/impl/maven-core/src/test/java/org/apache/maven/classrealm/DefaultClassRealmManagerTest.java
@@ -23,13 +23,13 @@
import java.util.HashSet;
import java.util.List;
+import org.apache.maven.api.classworlds.ClassRealm;
import org.apache.maven.extension.internal.CoreExports;
import org.apache.maven.internal.impl.internal.DefaultCoreRealm;
import org.apache.maven.model.Model;
import org.codehaus.plexus.DefaultPlexusContainer;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.PlexusContainerException;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.eclipse.aether.artifact.Artifact;
import org.junit.jupiter.api.Test;
import org.mockito.InOrder;
@@ -58,7 +58,11 @@ private DefaultClassRealmManager newDefaultClassRealmManager(PlexusContainer con
return new DefaultClassRealmManager(
new DefaultCoreRealm(container),
new ArrayList(),
- new CoreExports(new ClassRealm(null, "test", null), new HashSet(), exportedPackages));
+ new CoreExports(
+ new org.codehaus.plexus.classworlds.realm.ClassRealm(
+ new org.codehaus.plexus.classworlds.ClassWorld(), "test", null),
+ new HashSet(),
+ exportedPackages));
}
private List newTestArtifactList() {
diff --git a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/BuildPluginManagerStub.java b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/BuildPluginManagerStub.java
index 9566f569d1c7..6fb7c3617757 100644
--- a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/BuildPluginManagerStub.java
+++ b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/BuildPluginManagerStub.java
@@ -20,13 +20,13 @@
import java.util.List;
+import org.apache.maven.api.classworlds.ClassRealm;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.BuildPluginManager;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.repository.RemoteRepository;
diff --git a/impl/maven-core/src/test/java/org/apache/maven/plugin/PluginManagerTest.java b/impl/maven-core/src/test/java/org/apache/maven/plugin/PluginManagerTest.java
index f1621347257f..4290108a4dd8 100644
--- a/impl/maven-core/src/test/java/org/apache/maven/plugin/PluginManagerTest.java
+++ b/impl/maven-core/src/test/java/org/apache/maven/plugin/PluginManagerTest.java
@@ -23,6 +23,7 @@
import java.util.List;
import org.apache.maven.AbstractCoreMavenComponentTestCase;
+import org.apache.maven.api.classworlds.ClassRealm;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.DefaultRepositoryRequest;
import org.apache.maven.artifact.repository.RepositoryRequest;
@@ -31,7 +32,6 @@
import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.project.MavenProject;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.component.repository.ComponentDescriptor;
import org.junit.jupiter.api.Test;
diff --git a/impl/pom.xml b/impl/pom.xml
index b41717c555ea..d05a9ce2879f 100644
--- a/impl/pom.xml
+++ b/impl/pom.xml
@@ -31,6 +31,7 @@ under the License.
Maven 4 Implementation Modules
+ maven-classworlds
maven-support
maven-impl
maven-di
diff --git a/its/core-it-suite/src/test/resources/mng-5530-mojo-execution-scope/extension/src/main/java/org/apache/maven/its/mng5530/mojoexecutionscope/extension/TestClassRealmManagerDelegate.java b/its/core-it-suite/src/test/resources/mng-5530-mojo-execution-scope/extension/src/main/java/org/apache/maven/its/mng5530/mojoexecutionscope/extension/TestClassRealmManagerDelegate.java
index 6c064d273132..805e9a8ba6e6 100644
--- a/its/core-it-suite/src/test/resources/mng-5530-mojo-execution-scope/extension/src/main/java/org/apache/maven/its/mng5530/mojoexecutionscope/extension/TestClassRealmManagerDelegate.java
+++ b/its/core-it-suite/src/test/resources/mng-5530-mojo-execution-scope/extension/src/main/java/org/apache/maven/its/mng5530/mojoexecutionscope/extension/TestClassRealmManagerDelegate.java
@@ -35,10 +35,10 @@
import javax.inject.Named;
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.apache.maven.classrealm.ClassRealmManagerDelegate;
import org.apache.maven.classrealm.ClassRealmRequest;
import org.apache.maven.classrealm.ClassRealmRequest.RealmType;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
@Named
public class TestClassRealmManagerDelegate implements ClassRealmManagerDelegate {
diff --git a/its/core-it-suite/src/test/resources/mng-5771-core-extensions/repo-src/maven-it-core-extensions-no-descriptor/src/main/java/org/apache/maven/its/core_extensions/TestClassRealmManagerDelegate.java b/its/core-it-suite/src/test/resources/mng-5771-core-extensions/repo-src/maven-it-core-extensions-no-descriptor/src/main/java/org/apache/maven/its/core_extensions/TestClassRealmManagerDelegate.java
index edb262ce2dc6..287c936c7ee3 100644
--- a/its/core-it-suite/src/test/resources/mng-5771-core-extensions/repo-src/maven-it-core-extensions-no-descriptor/src/main/java/org/apache/maven/its/core_extensions/TestClassRealmManagerDelegate.java
+++ b/its/core-it-suite/src/test/resources/mng-5771-core-extensions/repo-src/maven-it-core-extensions-no-descriptor/src/main/java/org/apache/maven/its/core_extensions/TestClassRealmManagerDelegate.java
@@ -18,9 +18,9 @@
*/
package org.apache.maven.its.core_extensions;
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.apache.maven.classrealm.ClassRealmManagerDelegate;
import org.apache.maven.classrealm.ClassRealmRequest;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.component.annotations.Component;
@Component(role = ClassRealmManagerDelegate.class, hint = "TestClassRealmManagerDelegate")
diff --git a/its/core-it-suite/src/test/resources/mng-5771-core-extensions/repo-src/maven-it-core-extensions/src/main/java/org/apache/maven/its/core_extensions/TestClassRealmManagerDelegate.java b/its/core-it-suite/src/test/resources/mng-5771-core-extensions/repo-src/maven-it-core-extensions/src/main/java/org/apache/maven/its/core_extensions/TestClassRealmManagerDelegate.java
index edb262ce2dc6..287c936c7ee3 100644
--- a/its/core-it-suite/src/test/resources/mng-5771-core-extensions/repo-src/maven-it-core-extensions/src/main/java/org/apache/maven/its/core_extensions/TestClassRealmManagerDelegate.java
+++ b/its/core-it-suite/src/test/resources/mng-5771-core-extensions/repo-src/maven-it-core-extensions/src/main/java/org/apache/maven/its/core_extensions/TestClassRealmManagerDelegate.java
@@ -18,9 +18,9 @@
*/
package org.apache.maven.its.core_extensions;
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.apache.maven.classrealm.ClassRealmManagerDelegate;
import org.apache.maven.classrealm.ClassRealmRequest;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.component.annotations.Component;
@Component(role = ClassRealmManagerDelegate.class, hint = "TestClassRealmManagerDelegate")
diff --git a/pom.xml b/pom.xml
index d83ddb44cb59..208341ae5390 100644
--- a/pom.xml
+++ b/pom.xml
@@ -144,7 +144,6 @@ under the License.
9.10.1
1.18.10
- 2.12.0
1.11.0
1.5.2
5.1.0
@@ -238,6 +237,11 @@ under the License.
maven-api-annotations
${project.version}
+
+ org.apache.maven
+ maven-api-classworlds
+ ${project.version}
+
org.apache.maven
maven-api-model
@@ -398,6 +402,10 @@ under the License.
org.apache.maven
maven-xml-impl
+
+ org.codehaus.plexus
+ plexus-classworlds
+
@@ -421,9 +429,9 @@ under the License.
1
- org.codehaus.plexus
- plexus-classworlds
- ${classWorldsVersion}
+ org.apache.maven
+ maven-classworlds
+ ${project.version}
org.codehaus.plexus
@@ -641,6 +649,10 @@ under the License.
com.google.inject
guice
+
+ org.codehaus.plexus
+ plexus-classworlds
+
diff --git a/src/graph/ReactorGraph.java b/src/graph/ReactorGraph.java
index 57daef3f643b..788eeac9dedf 100755
--- a/src/graph/ReactorGraph.java
+++ b/src/graph/ReactorGraph.java
@@ -42,7 +42,7 @@ public class ReactorGraph {
CLUSTER_PATTERNS.put("JLine", Pattern.compile("^org\\.jline:.*"));
CLUSTER_PATTERNS.put("Maven API", Pattern.compile("^org\\.apache\\.maven:maven-api-(?!impl).*"));
CLUSTER_PATTERNS.put("Maven Resolver", Pattern.compile("^org\\.apache\\.maven\\.resolver:.*"));
- CLUSTER_PATTERNS.put("Maven Implementation", Pattern.compile("^org\\.apache\\.maven:maven-(support|impl|di|core|cli|xml|jline|logging|executor|testing):.*"));
+ CLUSTER_PATTERNS.put("Maven Implementation", Pattern.compile("^org\\.apache\\.maven:maven-(support|impl|di|core|cli|xml|jline|logging|executor|testing|classworlds):.*"));
CLUSTER_PATTERNS.put("Maven Compatibility", Pattern.compile("^org\\.apache\\.maven:maven-(artifact|builder-support|compat|embedder|model|model-builder|plugin-api|repository-metadata|resolver-provider|settings|settings-builder|toolchain-builder|toolchain-model):.*"));
CLUSTER_PATTERNS.put("Sisu", Pattern.compile("(^org\\.eclipse\\.sisu:.*)|(.*:guice:.*)|(.*:javax.inject:.*)|(.*:javax.annotation-api:.*)|(.*:aopalliance:.*)"));
CLUSTER_PATTERNS.put("Plexus", Pattern.compile("^org\\.codehaus\\.plexus:.*"));