diff --git a/apache-maven/src/assembly/component.xml b/apache-maven/src/assembly/component.xml
index 4d75c9a38ca8..0849b7caf3b6 100644
--- a/apache-maven/src/assembly/component.xml
+++ b/apache-maven/src/assembly/component.xml
@@ -23,14 +23,15 @@ under the License.
false
boot
- org.codehaus.plexus:plexus-classworlds
+ org.apache.maven:maven-classworlds
+
false
lib
- org.codehaus.plexus:plexus-classworlds
+ org.apache.maven:maven-classworlds
diff --git a/apache-maven/src/assembly/maven/bin/mvn b/apache-maven/src/assembly/maven/bin/mvn
index 8559d47af557..3caad69ab773 100755
--- a/apache-maven/src/assembly/maven/bin/mvn
+++ b/apache-maven/src/assembly/maven/bin/mvn
@@ -190,7 +190,7 @@ concat_lines() {
MAVEN_PROJECTBASEDIR="`find_maven_basedir "$@"`"
MAVEN_OPTS="$MAVEN_OPTS `concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config"`"
-LAUNCHER_JAR=`echo "$MAVEN_HOME"/boot/plexus-classworlds-*.jar`
+LAUNCHER_JAR=`echo "$MAVEN_HOME"/boot/maven-classworlds-*.jar`
LAUNCHER_CLASS=org.codehaus.plexus.classworlds.launcher.Launcher
# For Cygwin and MinGW, switch paths to Windows format before running java(1) command
diff --git a/apache-maven/src/assembly/maven/bin/mvn.cmd b/apache-maven/src/assembly/maven/bin/mvn.cmd
index a3e8600df3d1..daa33e7cf8cb 100644
--- a/apache-maven/src/assembly/maven/bin/mvn.cmd
+++ b/apache-maven/src/assembly/maven/bin/mvn.cmd
@@ -247,7 +247,7 @@ goto processArgs
:endHandleArgs
call :processArgs %*
-for %%i in ("%MAVEN_HOME%"\boot\plexus-classworlds-*) do set LAUNCHER_JAR="%%i"
+for %%i in ("%MAVEN_HOME%"\boot\maven-classworlds-*) do set LAUNCHER_JAR="%%i"
set LAUNCHER_CLASS=org.codehaus.plexus.classworlds.launcher.Launcher
if "%MAVEN_MAIN_CLASS%"=="" @set MAVEN_MAIN_CLASS=org.apache.maven.cling.MavenCling
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 02ddf974cf45..ce75af2f3176 100644
--- a/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm
+++ b/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm
@@ -67,7 +67,7 @@ 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
#* *###
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..182b8a4326e1
--- /dev/null
+++ b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassRealm.java
@@ -0,0 +1,197 @@
+/*
+ * 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);
+}
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 f6919bb5306e..918118d29647 100644
--- a/compat/maven-compat/pom.xml
+++ b/compat/maven-compat/pom.xml
@@ -117,8 +117,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 b1136b19c845..916e325a6068 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 7f9e5f4a13ba..cd000c514165 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;
@@ -225,7 +225,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 {
@@ -246,7 +246,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 {
@@ -673,13 +674,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);
@@ -691,8 +695,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)
@@ -708,7 +712,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
@@ -734,12 +738,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();
@@ -807,8 +813,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) //
@@ -874,7 +880,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());
@@ -888,11 +895,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 321bde249d82..b4b48b600436 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;
@@ -174,12 +174,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/impl/maven-classworlds/pom.xml b/impl/maven-classworlds/pom.xml
new file mode 100644
index 000000000000..8e4880e1f812
--- /dev/null
+++ b/impl/maven-classworlds/pom.xml
@@ -0,0 +1,115 @@
+
+
+
+ 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.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/plexus/classworlds/ClassWorld.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorld.java
new file mode 100644
index 000000000000..794c19c89a5b
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorld.java
@@ -0,0 +1,217 @@
+/*
+ * 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 ClassRealm
s, 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<>();
+
+ 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()));
+ }
+
+ // 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..2bd42c1e8907
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationHandler.java
@@ -0,0 +1,83 @@
+/*
+ * 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);
+}
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..6f47b4aeb440
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParser.java
@@ -0,0 +1,444 @@
+/*
+ * 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";
+
+ /**
+ * 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':
+ 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 '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 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..85aa9f78b617
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/Configurator.java
@@ -0,0 +1,216 @@
+/*
+ * 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.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+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;
+
+ /**
+ * 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;
+
+ if (this.launcher != null) {
+ foreignClassLoader = this.launcher.getSystemClassLoader();
+ }
+
+ ConfigurationParser parser = new ConfigurationParser(this, System.getProperties());
+
+ parser.parse(is);
+
+ // 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 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 ClassRealm
s
+ * 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..290e440d5549
--- /dev/null
+++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/ClassRealm.java
@@ -0,0 +1,498 @@
+/*
+ * 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);
+ }
+
+ // ----------------------------------------------------------------------
+ // 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..c0b8d6ec4b71
--- /dev/null
+++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/FilteredClassRealmTest.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.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 591120a5d6c9..a88d9100c453 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..37c68b7721aa 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".
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..2c8a0cba0b0e 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".
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..df1d6b363297 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.
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..43cb3f90fba0 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".
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 89dd0e0c0e3f..7c4d4c254678 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;
@@ -179,12 +179,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 eae08feb2d05..7314c728ffd8 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 a50bc24b6104..6a75f117b218 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
@@ -28,11 +28,11 @@
import java.util.List;
import java.util.Map;
+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.jline.JLineMessageBuilderFactory;
-import org.codehaus.plexus.classworlds.ClassWorld;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -149,7 +149,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 56b9a105583c..6169e8215707 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 8c9291ac7ded..fa10ffb64f0e 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
@@ -404,6 +408,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 4a64c009a2a6..ec2ed28d3e3c 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
@@ -464,7 +464,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();
@@ -219,7 +219,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 +232,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);
@@ -353,7 +354,7 @@ private void wireRealm(ClassRealm classRealm, List parentImports, Map 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 7e8428a6b168..fa4a871a19a4 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;
@@ -1100,7 +1100,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 6d9c76100d8e..1b9af3c86bad 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
@@ -27,6 +27,7 @@
import java.util.List;
import org.apache.maven.api.Project;
+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;
@@ -40,7 +41,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;
@@ -110,7 +110,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();
@@ -164,7 +164,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) {
@@ -174,7 +174,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) {
@@ -184,7 +184,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 b20211525854..0319c101861f 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 681955f21db3..ca25a22638b0 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 7d55730f5d96..d0915cee2046 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
@@ -48,6 +48,7 @@
import org.apache.maven.api.PathType;
import org.apache.maven.api.Project;
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;
@@ -103,7 +104,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);
@@ -699,7 +702,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) {
@@ -708,7 +711,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);
}
@@ -810,7 +813,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 --");
@@ -846,7 +854,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) {
@@ -854,7 +862,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 f8f77c7ceab3..e58a3f4e569b 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;
@@ -239,7 +239,7 @@ public synchronized ProjectRealmCache.CacheRecord createProjectRealm(
}
for (String export : exports) {
- projectRealm.importFrom(extensionRealm, export);
+ projectRealm.importFrom(extensionRealm.getClassLoader(), export);
}
}
@@ -258,13 +258,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 List toAetherArtifacts(final List pluginArtifacts) {
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 82a1a814c1b0..b9dc37f764c2 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 3b934c922e9b..5483d62286b8 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 9b05f7a4ada4..2a68baef5e35 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
@@ -76,12 +76,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
@@ -153,7 +150,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 c90e75bece85..3a1cb57c791b 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;
@@ -59,7 +59,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 93b8fbc7ba1d..ed5e6c6e7fe0 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..af8955c21a8b 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.apache.maven.api.classworlds.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..44dd46fdd3aa 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.apache.maven.api.classworlds.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..44dd46fdd3aa 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.apache.maven.api.classworlds.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-support/core-it-plugins/maven-it-plugin-configuration/src/main/java/org/apache/maven/plugin/coreit/CustomComponentConfigurator.java b/its/core-it-support/core-it-plugins/maven-it-plugin-configuration/src/main/java/org/apache/maven/plugin/coreit/CustomComponentConfigurator.java
index 299168546461..4f3406951ad0 100644
--- a/its/core-it-support/core-it-plugins/maven-it-plugin-configuration/src/main/java/org/apache/maven/plugin/coreit/CustomComponentConfigurator.java
+++ b/its/core-it-support/core-it-plugins/maven-it-plugin-configuration/src/main/java/org/apache/maven/plugin/coreit/CustomComponentConfigurator.java
@@ -18,7 +18,7 @@
*/
package org.apache.maven.plugin.coreit;
-import org.codehaus.plexus.classworlds.realm.ClassRealm;
+import org.apache.maven.api.classworlds.ClassRealm;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.configurator.AbstractComponentConfigurator;
import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
diff --git a/pom.xml b/pom.xml
index 0fe79e32ae12..b2103cb693b4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -145,7 +145,6 @@ under the License.
3.27.3
9.8
1.17.6
- 2.9.0
1.9.0
5.1.0
33.4.8-jre
@@ -235,6 +234,11 @@ under the License.
maven-api-annotations
${project.version}
+
+ org.apache.maven
+ maven-api-classworlds
+ ${project.version}
+
org.apache.maven
maven-api-model
@@ -418,9 +422,9 @@ under the License.
1
- org.codehaus.plexus
- plexus-classworlds
- ${classWorldsVersion}
+ org.apache.maven
+ maven-classworlds
+ ${project.version}
org.codehaus.plexus