From fe2ac45330433bb426e8b27badae33752177284c Mon Sep 17 00:00:00 2001
From: Guillaume Nodet
Date: Sat, 9 Aug 2025 13:39:00 +0000
Subject: [PATCH] Remove jline-native and JPMS support, extract
org.apache.maven.api.classworld to separate API module
- Remove jline-native references from Maven launcher scripts (mvn and mvn.cmd)
- Remove jline-native dependencies and configurations from POMs
- Remove module-info.java file from maven-classworlds
- Remove jline-native assembly configurations and README
- Create new maven-api-classworlds module in api/ directory
- Move org.apache.maven.api.classworlds package to new API module
- Update dependency management and module references
- Add maven-api-classworlds dependency to modules that use the API
This change separates the public API from the implementation, following Maven 4
architecture patterns, and removes the JPMS and jline-native dependencies that
were causing complexity in the build and runtime environment.
---
apache-maven/src/assembly/component.xml | 5 +-
apache-maven/src/assembly/maven/bin/mvn | 2 +-
apache-maven/src/assembly/maven/bin/mvn.cmd | 2 +-
.../appended-resources/META-INF/LICENSE.vm | 2 +-
api/maven-api-classworlds/pom.xml | 39 ++
.../maven/api/classworlds/ClassRealm.java | 197 +++++++
.../maven/api/classworlds/ClassWorld.java | 117 ++++
.../api/classworlds/ClassWorldException.java | 94 ++++
.../api/classworlds/ClassWorldListener.java | 49 ++
.../classworlds/DuplicateRealmException.java | 58 ++
.../api/classworlds/NoSuchRealmException.java | 58 ++
.../maven/api/classworlds/Strategy.java | 77 +++
.../maven/api/classworlds/package-info.java | 49 ++
api/pom.xml | 1 +
compat/maven-compat/pom.xml | 8 +-
compat/maven-embedder/pom.xml | 8 +-
.../java/org/apache/maven/cli/CliRequest.java | 2 +-
.../java/org/apache/maven/cli/MavenCli.java | 43 +-
.../BootstrapCoreExtensionManager.java | 9 +-
compat/maven-plugin-api/pom.xml | 4 +-
impl/maven-classworlds/pom.xml | 115 ++++
.../plexus/classworlds/ClassWorld.java | 217 ++++++++
.../classworlds/ClassWorldException.java | 89 ++++
.../classworlds/ClassWorldListener.java | 43 ++
.../codehaus/plexus/classworlds/UrlUtils.java | 74 +++
.../launcher/ConfigurationException.java | 66 +++
.../launcher/ConfigurationHandler.java | 83 +++
.../launcher/ConfigurationParser.java | 444 ++++++++++++++++
.../classworlds/launcher/Configurator.java | 216 ++++++++
.../plexus/classworlds/launcher/Launcher.java | 409 ++++++++++++++
.../plexus/classworlds/realm/ClassRealm.java | 498 ++++++++++++++++++
.../realm/DuplicateRealmException.java | 91 ++++
.../plexus/classworlds/realm/Entry.java | 212 ++++++++
.../classworlds/realm/FilteredClassRealm.java | 74 +++
.../realm/NoSuchRealmException.java | 91 ++++
.../strategy/AbstractStrategy.java | 82 +++
.../strategy/OsgiBundleStrategy.java | 96 ++++
.../strategy/ParentFirstStrategy.java | 89 ++++
.../strategy/SelfFirstStrategy.java | 89 ++++
.../plexus/classworlds/strategy/Strategy.java | 56 ++
.../classworlds/strategy/StrategyFactory.java | 53 ++
.../apache/maven/api/classworlds/ApiTest.java | 128 +++++
.../AbstractClassWorldsTestCase.java | 46 ++
.../plexus/classworlds/ClassView.java | 130 +++++
.../plexus/classworlds/ClassWorldTest.java | 158 ++++++
.../codehaus/plexus/classworlds/TestUtil.java | 70 +++
.../plexus/classworlds/UrlUtilsTest.java | 49 ++
.../launcher/ConfigurationParserTest.java | 101 ++++
.../launcher/ConfiguratorTest.java | 365 +++++++++++++
.../classworlds/launcher/LauncherTest.java | 138 +++++
.../classworlds/realm/ClassRealmImplTest.java | 482 +++++++++++++++++
.../realm/DefaultClassRealmTest.java | 346 ++++++++++++
.../plexus/classworlds/realm/EntryTest.java | 186 +++++++
.../realm/FilteredClassRealmTest.java | 105 ++++
.../classworlds/strategy/StrategyTest.java | 153 ++++++
.../src/test/test-data/a.jar | Bin 0 -> 1584 bytes
.../src/test/test-data/a.properties | 18 +
.../src/test/test-data/b.jar | Bin 0 -> 1404 bytes
.../src/test/test-data/b_old.jar | Bin 0 -> 1225 bytes
.../src/test/test-data/c.jar | Bin 0 -> 722 bytes
.../src/test/test-data/circular-0.1.jar | Bin 0 -> 4820 bytes
.../src/test/test-data/component0-1.0.jar | Bin 0 -> 1651 bytes
.../src/test/test-data/component1-1.0.jar | Bin 0 -> 1650 bytes
.../src/test/test-data/component2-1.0.jar | Bin 0 -> 1651 bytes
.../src/test/test-data/component3-1.0.jar | Bin 0 -> 1651 bytes
.../src/test/test-data/component4-1.0.jar | Bin 0 -> 1651 bytes
.../src/test/test-data/component5-1.0.jar | Bin 0 -> 1887 bytes
.../src/test/test-data/component5-2.0.jar | Bin 0 -> 1886 bytes
.../src/test/test-data/d.jar | Bin 0 -> 718 bytes
.../src/test/test-data/deadlock.jar | Bin 0 -> 1576 bytes
.../src/test/test-data/dupe-main.conf | 3 +
.../src/test/test-data/dupe-realm.conf | 9 +
.../src/test/test-data/early-import.conf | 2 +
.../from-from-0.0.1-from-load-import.jar | Bin 0 -> 2252 bytes
.../src/test/test-data/inheritance.conf | 18 +
.../src/test/test-data/launch-noclass.conf | 6 +
.../src/test/test-data/launch-nomethod.conf | 7 +
.../src/test/test-data/nested.jar | Bin 0 -> 1743 bytes
.../src/test/test-data/nested.properties | 18 +
.../test/test-data/optionally-existent.conf | 14 +
.../test-data/optionally-nonexistent.conf | 13 +
.../src/test/test-data/realm-syntax.conf | 3 +
.../test/test-data/resources/classworlds.conf | 12 +
.../src/test/test-data/resources/werkflow.jar | Bin 0 -> 5390 bytes
.../test/test-data/set-using-existent.conf | 19 +
.../test-data/set-using-existent.properties | 19 +
.../src/test/test-data/set-using-missing.conf | 19 +
.../test/test-data/set-using-nonexistent.conf | 19 +
.../src/test/test-data/unhandled.conf | 2 +
.../test-data/valid-enh-launch-exitCode.conf | 6 +
.../src/test/test-data/valid-enh-launch.conf | 6 +
.../test/test-data/valid-from-from-from.conf | 24 +
.../test/test-data/valid-launch-exitCode.conf | 5 +
.../src/test/test-data/valid-launch.conf | 5 +
.../src/test/test-data/valid.conf | 24 +
impl/maven-cli/pom.xml | 8 +-
.../org/apache/maven/cling/ClingSupport.java | 7 +-
.../org/apache/maven/cling/MavenCling.java | 2 +-
.../org/apache/maven/cling/MavenEncCling.java | 2 +-
.../apache/maven/cling/MavenShellCling.java | 2 +-
.../org/apache/maven/cling/MavenUpCling.java | 2 +-
.../BootstrapCoreExtensionManager.java | 9 +-
.../PlexusContainerCapsuleFactory.java | 29 +-
.../cling/invoker/mvn/MavenInvokerTest.java | 2 +-
.../invoker/mvn/MavenInvokerTestSupport.java | 4 +-
.../resident/ResidentMavenInvokerTest.java | 2 +-
impl/maven-core/pom.xml | 42 +-
.../java/org/apache/maven/DefaultMaven.java | 4 +-
.../maven/classrealm/ClassRealmManager.java | 2 +-
.../classrealm/ClassRealmManagerDelegate.java | 2 +-
.../classrealm/DefaultClassRealmManager.java | 17 +-
.../EnhancedComponentConfigurator.java | 2 +-
.../maven/extension/internal/CoreExports.java | 5 +-
.../internal/CoreExtensionEntry.java | 4 +-
.../org/apache/maven/internal/CoreRealm.java | 4 +-
.../maven/internal/impl/DefaultProject.java | 6 +-
.../impl/internal/DefaultCoreRealm.java | 2 +-
.../internal/LifecycleDependencyResolver.java | 10 +-
.../internal/builder/BuilderCommon.java | 4 +-
.../concurrent/BuildPlanExecutor.java | 4 +-
.../maven/plugin/BuildPluginManager.java | 2 +-
.../plugin/DefaultBuildPluginManager.java | 10 +-
.../plugin/DefaultExtensionRealmCache.java | 4 +-
.../maven/plugin/DefaultPluginRealmCache.java | 4 +-
.../maven/plugin/ExtensionRealmCache.java | 2 +-
.../plugin/PluginContainerException.java | 2 +-
.../maven/plugin/PluginManagerException.java | 2 +-
.../apache/maven/plugin/PluginRealmCache.java | 2 +-
.../internal/DefaultMavenPluginManager.java | 50 +-
.../project/DefaultProjectBuildingHelper.java | 13 +-
.../project/DefaultProjectRealmCache.java | 4 +-
.../apache/maven/project/MavenProject.java | 2 +-
.../maven/project/ProjectRealmCache.java | 2 +-
.../resources/META-INF/maven/extension.xml | 7 +-
.../DefaultClassRealmManagerTest.java | 8 +-
.../internal/stub/BuildPluginManagerStub.java | 2 +-
.../maven/plugin/PluginManagerTest.java | 2 +-
impl/pom.xml | 1 +
.../TestClassRealmManagerDelegate.java | 2 +-
.../TestClassRealmManagerDelegate.java | 2 +-
.../TestClassRealmManagerDelegate.java | 2 +-
.../coreit/CustomComponentConfigurator.java | 2 +-
pom.xml | 12 +-
143 files changed, 6903 insertions(+), 152 deletions(-)
create mode 100644 api/maven-api-classworlds/pom.xml
create mode 100644 api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassRealm.java
create mode 100644 api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassWorld.java
create mode 100644 api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassWorldException.java
create mode 100644 api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassWorldListener.java
create mode 100644 api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/DuplicateRealmException.java
create mode 100644 api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/NoSuchRealmException.java
create mode 100644 api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/Strategy.java
create mode 100644 api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/package-info.java
create mode 100644 impl/maven-classworlds/pom.xml
create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorld.java
create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorldException.java
create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorldListener.java
create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/UrlUtils.java
create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationException.java
create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationHandler.java
create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParser.java
create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/Configurator.java
create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/Launcher.java
create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/ClassRealm.java
create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/DuplicateRealmException.java
create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/Entry.java
create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/FilteredClassRealm.java
create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/NoSuchRealmException.java
create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/AbstractStrategy.java
create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/OsgiBundleStrategy.java
create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/ParentFirstStrategy.java
create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/SelfFirstStrategy.java
create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/Strategy.java
create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/StrategyFactory.java
create mode 100644 impl/maven-classworlds/src/test/java/org/apache/maven/api/classworlds/ApiTest.java
create mode 100644 impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/AbstractClassWorldsTestCase.java
create mode 100644 impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/ClassView.java
create mode 100644 impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/ClassWorldTest.java
create mode 100644 impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/TestUtil.java
create mode 100644 impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/UrlUtilsTest.java
create mode 100644 impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParserTest.java
create mode 100644 impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfiguratorTest.java
create mode 100644 impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/launcher/LauncherTest.java
create mode 100644 impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/ClassRealmImplTest.java
create mode 100644 impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/DefaultClassRealmTest.java
create mode 100644 impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/EntryTest.java
create mode 100644 impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/FilteredClassRealmTest.java
create mode 100644 impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/strategy/StrategyTest.java
create mode 100644 impl/maven-classworlds/src/test/test-data/a.jar
create mode 100644 impl/maven-classworlds/src/test/test-data/a.properties
create mode 100644 impl/maven-classworlds/src/test/test-data/b.jar
create mode 100644 impl/maven-classworlds/src/test/test-data/b_old.jar
create mode 100644 impl/maven-classworlds/src/test/test-data/c.jar
create mode 100644 impl/maven-classworlds/src/test/test-data/circular-0.1.jar
create mode 100644 impl/maven-classworlds/src/test/test-data/component0-1.0.jar
create mode 100644 impl/maven-classworlds/src/test/test-data/component1-1.0.jar
create mode 100644 impl/maven-classworlds/src/test/test-data/component2-1.0.jar
create mode 100644 impl/maven-classworlds/src/test/test-data/component3-1.0.jar
create mode 100644 impl/maven-classworlds/src/test/test-data/component4-1.0.jar
create mode 100644 impl/maven-classworlds/src/test/test-data/component5-1.0.jar
create mode 100644 impl/maven-classworlds/src/test/test-data/component5-2.0.jar
create mode 100644 impl/maven-classworlds/src/test/test-data/d.jar
create mode 100644 impl/maven-classworlds/src/test/test-data/deadlock.jar
create mode 100644 impl/maven-classworlds/src/test/test-data/dupe-main.conf
create mode 100644 impl/maven-classworlds/src/test/test-data/dupe-realm.conf
create mode 100644 impl/maven-classworlds/src/test/test-data/early-import.conf
create mode 100644 impl/maven-classworlds/src/test/test-data/from-from-0.0.1-from-load-import.jar
create mode 100644 impl/maven-classworlds/src/test/test-data/inheritance.conf
create mode 100644 impl/maven-classworlds/src/test/test-data/launch-noclass.conf
create mode 100644 impl/maven-classworlds/src/test/test-data/launch-nomethod.conf
create mode 100644 impl/maven-classworlds/src/test/test-data/nested.jar
create mode 100644 impl/maven-classworlds/src/test/test-data/nested.properties
create mode 100644 impl/maven-classworlds/src/test/test-data/optionally-existent.conf
create mode 100644 impl/maven-classworlds/src/test/test-data/optionally-nonexistent.conf
create mode 100644 impl/maven-classworlds/src/test/test-data/realm-syntax.conf
create mode 100644 impl/maven-classworlds/src/test/test-data/resources/classworlds.conf
create mode 100644 impl/maven-classworlds/src/test/test-data/resources/werkflow.jar
create mode 100644 impl/maven-classworlds/src/test/test-data/set-using-existent.conf
create mode 100644 impl/maven-classworlds/src/test/test-data/set-using-existent.properties
create mode 100644 impl/maven-classworlds/src/test/test-data/set-using-missing.conf
create mode 100644 impl/maven-classworlds/src/test/test-data/set-using-nonexistent.conf
create mode 100644 impl/maven-classworlds/src/test/test-data/unhandled.conf
create mode 100644 impl/maven-classworlds/src/test/test-data/valid-enh-launch-exitCode.conf
create mode 100644 impl/maven-classworlds/src/test/test-data/valid-enh-launch.conf
create mode 100644 impl/maven-classworlds/src/test/test-data/valid-from-from-from.conf
create mode 100644 impl/maven-classworlds/src/test/test-data/valid-launch-exitCode.conf
create mode 100644 impl/maven-classworlds/src/test/test-data/valid-launch.conf
create mode 100644 impl/maven-classworlds/src/test/test-data/valid.conf
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 ClassRealms, indexed by id.
+ *
+ * @author bob mcwhirter
+ */
+public class ClassWorld implements org.apache.maven.api.classworlds.ClassWorld, Closeable {
+ private Map realms;
+
+ private final List listeners = new ArrayList<>();
+
+ 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 ClassRealms
+ * from a configuration file and the launching of the application's main
+ * method from the correct class loaded through the correct classloader.
+ *
+ * The path to the configuration file is specified using the classworlds.conf
+ * system property, typically specified using the -D switch to
+ * java.
+ *
+ * @author bob mcwhirter
+ */
+public class Launcher {
+ protected static final String CLASSWORLDS_CONF = "classworlds.conf";
+
+ protected static final String UBERJAR_CONF_DIR = "WORLDS-INF/conf/";
+
+ protected ClassLoader systemClassLoader;
+
+ protected String mainClassName;
+
+ protected String mainRealmName;
+
+ protected ClassWorld world;
+
+ private int exitCode = 0;
+
+ public Launcher() {
+ this.systemClassLoader = Thread.currentThread().getContextClassLoader();
+ }
+
+ public void setSystemClassLoader(ClassLoader loader) {
+ this.systemClassLoader = loader;
+ }
+
+ public ClassLoader getSystemClassLoader() {
+ return this.systemClassLoader;
+ }
+
+ public int getExitCode() {
+ return exitCode;
+ }
+
+ public void setAppMain(String mainClassName, String mainRealmName) {
+ this.mainClassName = mainClassName;
+
+ this.mainRealmName = mainRealmName;
+ }
+
+ public String getMainRealmName() {
+ return this.mainRealmName;
+ }
+
+ public String getMainClassName() {
+ return this.mainClassName;
+ }
+
+ public void setWorld(ClassWorld world) {
+ this.world = world;
+ }
+
+ public ClassWorld getWorld() {
+ return this.world;
+ }
+
+ /**
+ * Configure from a file.
+ *
+ * @param is The config input stream.
+ * @throws IOException If an error occurs reading the config file.
+ * @throws MalformedURLException If the config file contains invalid URLs.
+ * @throws ConfigurationException If the config file is corrupt.
+ * @throws org.codehaus.plexus.classworlds.realm.DuplicateRealmException If the config file defines two realms
+ * with the same id.
+ * @throws org.codehaus.plexus.classworlds.realm.NoSuchRealmException If the config file defines a main entry
+ * point in a non-existent realm.
+ */
+ public void configure(InputStream is)
+ throws IOException, ConfigurationException, DuplicateRealmException, NoSuchRealmException {
+ Configurator configurator = new Configurator(this);
+
+ configurator.configure(is);
+ }
+
+ /**
+ * Retrieve the main entry class.
+ *
+ * @return The main entry class.
+ * @throws ClassNotFoundException If the class cannot be found.
+ * @throws NoSuchRealmException If the specified main entry realm does not exist.
+ */
+ public Class> getMainClass() throws ClassNotFoundException, NoSuchRealmException {
+ return getMainRealm().loadClass(getMainClassName());
+ }
+
+ /**
+ * Retrieve the main entry realm.
+ *
+ * @return The main entry realm.
+ * @throws NoSuchRealmException If the specified main entry realm does not exist.
+ */
+ public ClassRealm getMainRealm() throws NoSuchRealmException {
+ return getWorld().getRealm(getMainRealmName());
+ }
+
+ /**
+ * Retrieve the enhanced main entry method.
+ *
+ * @return The enhanced main entry method.
+ * @throws ClassNotFoundException If the main entry class cannot be found.
+ * @throws NoSuchMethodException If the main entry method cannot be found.
+ * @throws NoSuchRealmException If the main entry realm cannot be found.
+ */
+ protected Method getEnhancedMainMethod()
+ throws ClassNotFoundException, NoSuchMethodException, NoSuchRealmException {
+ Class> cwClass = getMainRealm().loadClass(ClassWorld.class.getName());
+
+ Method m = getMainClass().getMethod("main", String[].class, cwClass);
+
+ int modifiers = m.getModifiers();
+
+ if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) {
+ if (m.getReturnType() == Integer.TYPE || m.getReturnType() == Void.TYPE) {
+ return m;
+ }
+ }
+
+ throw new NoSuchMethodException("public static void main(String[] args, ClassWorld world)");
+ }
+
+ /**
+ * Retrieve the main entry method.
+ *
+ * @return The main entry method.
+ * @throws ClassNotFoundException If the main entry class cannot be found.
+ * @throws NoSuchMethodException If the main entry method cannot be found.
+ * @throws NoSuchRealmException If the main entry realm cannot be found.
+ */
+ protected Method getMainMethod() throws ClassNotFoundException, NoSuchMethodException, NoSuchRealmException {
+ Method m = getMainClass().getMethod("main", String[].class);
+
+ int modifiers = m.getModifiers();
+
+ if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) {
+ if (m.getReturnType() == Integer.TYPE || m.getReturnType() == Void.TYPE) {
+ return m;
+ }
+ }
+
+ throw new NoSuchMethodException("public static void main(String[] args) in " + getMainClass());
+ }
+
+ /**
+ * Launch the application.
+ *
+ * @param args The application args.
+ * @throws ClassNotFoundException If the main entry class cannot be found.
+ * @throws IllegalAccessException If the method cannot be accessed.
+ * @throws InvocationTargetException If the target of the invokation is invalid.
+ * @throws NoSuchMethodException If the main entry method cannot be found.
+ * @throws NoSuchRealmException If the main entry realm cannot be found.
+ */
+ public void launch(String[] args)
+ throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException,
+ NoSuchRealmException {
+ try {
+ launchEnhanced(args);
+
+ return;
+ } catch (NoSuchMethodException e) {
+ // ignore
+ }
+
+ launchStandard(args);
+ }
+
+ /**
+ * Attempt to launch the application through the enhanced main method.
+ *
+ * This will seek a method with the exact signature of:
+ *
+ * public static void main(String[] args, ClassWorld world)
+ *
+ *
+ * @param args The application args.
+ * @throws ClassNotFoundException If the main entry class cannot be found.
+ * @throws IllegalAccessException If the method cannot be accessed.
+ * @throws InvocationTargetException If the target of the invokation is
+ * invalid.
+ * @throws NoSuchMethodException If the main entry method cannot be found.
+ * @throws NoSuchRealmException If the main entry realm cannot be found.
+ */
+ protected void launchEnhanced(String[] args)
+ throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException,
+ NoSuchRealmException {
+ ClassRealm mainRealm = getMainRealm();
+
+ Class> mainClass = getMainClass();
+
+ Method mainMethod = getEnhancedMainMethod();
+
+ ClassLoader cl = mainRealm;
+
+ // ----------------------------------------------------------------------
+ // This is what the classloader for the main realm looks like when we
+ // boot from the command line:
+ // ----------------------------------------------------------------------
+ // [ AppLauncher$AppClassLoader ] : $CLASSPATH envar
+ // ^
+ // |
+ // |
+ // [ AppLauncher$ExtClassLoader ] : ${java.home}/jre/lib/ext/*.jar
+ // ^
+ // |
+ // |
+ // [ Strategy ]
+ // ----------------------------------------------------------------------
+
+ Thread.currentThread().setContextClassLoader(cl);
+
+ Object ret = mainMethod.invoke(mainClass, args, getWorld());
+
+ if (ret instanceof Integer) {
+ exitCode = (Integer) ret;
+ }
+
+ Thread.currentThread().setContextClassLoader(systemClassLoader);
+ }
+
+ /**
+ * Attempt to launch the application through the standard main method.
+ *
+ * This will seek a method with the exact signature of:
+ *
+ *
+ * public static void main(String[] args)
+ *
+ *
+ * @param args The application args.
+ * @throws ClassNotFoundException If the main entry class cannot be found.
+ * @throws IllegalAccessException If the method cannot be accessed.
+ * @throws InvocationTargetException If the target of the invokation is
+ * invalid.
+ * @throws NoSuchMethodException If the main entry method cannot be found.
+ * @throws NoSuchRealmException If the main entry realm cannot be found.
+ */
+ protected void launchStandard(String[] args)
+ throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException,
+ NoSuchRealmException {
+ ClassRealm mainRealm = getMainRealm();
+
+ Class> mainClass = getMainClass();
+
+ Method mainMethod = getMainMethod();
+
+ Thread.currentThread().setContextClassLoader(mainRealm);
+
+ Object ret = mainMethod.invoke(mainClass, new Object[] {args});
+
+ if (ret instanceof Integer) {
+ exitCode = (Integer) ret;
+ }
+
+ Thread.currentThread().setContextClassLoader(systemClassLoader);
+ }
+
+ // ------------------------------------------------------------
+ // Class methods
+ // ------------------------------------------------------------
+
+ /**
+ * Launch the launcher from the command line.
+ * Will exit using System.exit with an exit code of 0 for success, 100 if there was an unknown exception,
+ * or some other code for an application error.
+ *
+ * @param args The application command-line arguments.
+ */
+ public static void main(String[] args) {
+ try {
+ int exitCode = mainWithExitCode(args);
+
+ System.exit(exitCode);
+ } catch (Exception e) {
+ e.printStackTrace();
+
+ System.exit(100);
+ }
+ }
+
+ /**
+ * Launch the launcher.
+ *
+ * @param args The application command-line arguments.
+ * @return an integer exit code
+ * @throws Exception If an error occurs.
+ */
+ public static int mainWithExitCode(String[] args) throws Exception {
+ String classworldsConf = System.getProperty(CLASSWORLDS_CONF);
+
+ InputStream is;
+
+ Launcher launcher = new Launcher();
+
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+
+ launcher.setSystemClassLoader(cl);
+
+ if (classworldsConf != null) {
+ is = Files.newInputStream(Paths.get(classworldsConf));
+ } else {
+ if ("true".equals(System.getProperty("classworlds.bootstrapped"))) {
+ is = cl.getResourceAsStream(UBERJAR_CONF_DIR + CLASSWORLDS_CONF);
+ } else {
+ is = cl.getResourceAsStream(CLASSWORLDS_CONF);
+ }
+ }
+
+ if (is == null) {
+ throw new Exception("classworlds configuration not specified nor found in the classpath");
+ }
+
+ launcher.configure(is);
+
+ is.close();
+
+ try {
+ launcher.launch(args);
+ } catch (InvocationTargetException e) {
+ ClassRealm realm = launcher.getWorld().getRealm(launcher.getMainRealmName());
+
+ URL[] constituents = realm.getURLs();
+
+ System.out.println("---------------------------------------------------");
+
+ for (int i = 0; i < constituents.length; i++) {
+ System.out.println("constituent[" + i + "]: " + constituents[i]);
+ }
+
+ System.out.println("---------------------------------------------------");
+
+ // Decode ITE (if we can)
+ Throwable t = e.getTargetException();
+
+ if (t instanceof Exception) {
+ throw (Exception) t;
+ }
+ if (t instanceof Error) {
+ throw (Error) t;
+ }
+
+ // Else just toss the ITE
+ throw e;
+ }
+
+ return launcher.getExitCode();
+ }
+}
diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/ClassRealm.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/ClassRealm.java
new file mode 100644
index 000000000000..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 0000000000000000000000000000000000000000..9297ed84fab125f8ac1c72a3e5f65b9e43c2c9e0
GIT binary patch
literal 1584
zcmWIWW@Zs#-~hrqovhXjNPv@pg~8V~#8KDN&rSc|DFy~+h5&DN4v-2asImZ@nni#r
z;F^6M{XE@VgG2Ou-9G!CIql=Et9OytTUYDcne&^246YbIcv__A<*VcAd$DvC3+IfN
zl1HRxXlJlYf2R2(O-=l%c(~Z~CC|jPE1s#o&iqvLv4|1u5aB7&H{5~NfH24*Twq7-
zMDqp6sfqdshbnhjn1B^R2~M!Qqh4}OVsUY9;At;rLlN7T$;*uUwr$(CPrlT&)UcVm
zG*GbXK~})ROTDYNw9PW&_gvmIeOCLs&3mmAMDan(z9m
zWhty%VrFJFZ<>WcpwY(13F6f%FDvd`mdrbSWM4D0NoL-uv=wJ$^M5HDdi)Q(JH1d#
z=@QpciC32^X8zbdW$q=GCU+CZT*k8rb}#joRA0U8dfiFbwJLURQ9@P1DzQ>KMP(hi
z+J?Ppx6MM8mHRikT%F0qF`ZL)%>oPM#2c?oHr=*^_F)}dx1!CNR4~nuxeaA$k(03GAb25AK-K$5A9NBI-&rEJpMZkm(
z0T#!Ikd7yhGKJbL66K9ExqFw65{2k-m{|LiTjPF?F8INDSh)T0?J4XrZ9JyWk4b-IfA`BqX4l<_lLa^O<^|7Pt@t(k*%Zk$
zc_x9(g=$+jU3{o}H~Fx`y#vd7b#sg(_PDaz9?g_mar=g&^XW?!ZI{-+NUc|4`)I%F
zw?&_;Lp3l8Rt2*g6|K*x@lj*pKjD(f=bQJg^x5UC(DZX@wk8vnieFP_U8WnX6YxyP
z=+K*c(l?5O%ru_~70HXaR^M{i;Sg~^scZ9T_fCm~y$Ydz`;~&V?EZSpXgR>>YxPGt
zd0~sH)35bQHhJlRBt0L4apKCNOzJQa8Fb%MB
z$s^J;v@=+yKhyk?rY8PWJX~!0l4oMt70*;(XMQUBSi}f+i13u?8}2}BKp5l@F0iBY
z(0l=MYLY&}p(eX3O~DGG1SeSDNiR7kvAEdRZ=>H~1Cc$~mvQp8mg#z5U2AYfKH$g%
zFCRhKu7np)mV^~NwL0P1a6ic@=>_|Tz>enzTc*{Nf7@OD{_meUd4@BFJr|EE9CB^(
zcB)vtcHMi9yEnfID@II#$|bXq|Gi-zrV!bl2}r#b#Aw7
z)GntdcYY|%_Sz%$_23lg$k6vojcrI6_cU`x$@Ofre(7<4OPf3_F
zu|#I$sWPqghOX_~zb$9}6n1Pvd~jOQySp`wc98-~$7bXnn9&>l;&IRW{k3=49$b;y
z_E-4W4&8bG89}kw^hZ7a8PMyRjF7;CBmwlm0R?H2zEct;bX@%$fuSQXC-)MI)55(>
z>{p_@UHG@vUqL(QI-Sl(D$EQZ!~KXOYl@z`X@dj}9F+kN}L1*h3_vWd^OFW%y9k?d3UlieN^N-rj->%9Ypfi6lYd7>pT9#AOh6%^$c
zq!yKArWT*}KI0p@A=KlNw#F%a4_&R3KDun+6x(&NE=ClnSQv;ADbg3sI6xd89cxnRV#8117&Cg
zumv*VT9L9h$OJA1+&K|p!Z9EdlrIrBf)XEcvION!1OO#gU{ZvnM09P)@dU9PrjZ%w
zbwmnA)(473kiiw>2TzO6I13f(c=(+22xaJERQ3JZBU-U@PIr*7
zcTjN2TrEc_pD#U+cy&rA^Z1CGG@Z-9FwB^VF_78y_+2SjCcq(?BFFv<-oBjTK
z_6$51xG%U|KfbYFxXC=IbyYsc*?&KK9{8N9^s?v)nRst)c;PDflsUyqg{O9&Ne|MV
z{9j~l)k3W%>7z+9MY|@gQfT?RJVSH-(i5o@J2TgYPHYWaX*_Ym52tn76Q68a73Veg
zytbj?$Hkq_yBn`aW|#fHpuiHX^zcpmET37^vga<@ernB|UUs+btn7O6v&7Z6Y@ht@
zaq6rq7Ou_mC+D!8aJX4DCw7ziw|7USUtHZ*yN$8>L09AT8xL1(WlWrIov=Gy+bto(
z@`vv?or;vsy}rV~EjC>KseWObalg*K&%B^emrpTSDbL8ja14lXCks$q07Eqi8ms}f
z-iI9|>ehJ*_g*~~w%eLxZ9@Kw1rr`!Xi{L2)haF6wM236B((z1FaA3%IQjlD)KB?w
zFZ9i$2fXi}fBslBKmNTuL)(kW4HrrapFEmzQ1Wz4SU68uy+Qhh8kO
zMGM994!TXg^W`PaziY4cqEri8g-u?}|B~I1spHzdOEA@KXXQ7xZ&}y$T>Fg*91nQE
z3AlQzVS~4Hz#U`Z;|VhkM*aJFTK}%$hm8xrnPtCueBeiP#$oey?QHjdvVj8E%6Foo
zDllMAg9De5NrV}9<^+Z?5C|~5bp+8!Srw!Wky(**2`IC|z>-FFAQP?)DeDG!BP_<2
zn;<5DEIt8bg7OxsjmRkkl)GSHN#hG36Q0-5wITZ*;%kV;C5?8#Jd8+80p6@^AQ@I5
MlmLd)Y-SJ-07pZXF#rGn
literal 0
HcmV?d00001
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 0000000000000000000000000000000000000000..8e48a862c7c8b15b050581e80c6180d1b57903d3
GIT binary patch
literal 722
zcmWIWW@h1H00EUr7A9Z@l;8x?zOEsTx}JV+`T;;?A`BcrsEnpe1X-D{qo1dnYjB93
zuiIzeGpBvLb@eXtdh2SPJ9B<>kiiw>2TzO6I13f(c=(+22xaJERQ3JZBU-U@PIr*7
zcTjN2TrEc_pD#U+cy&rA^Z1CGG*PTUD&ny|yE`tSWF`M~OPu(y#YTPA%Rga7KgkkmSLe
zPeLc2SbE7?`-}f0tr!V|pH{*x>&lu!QrZ(_=cfcM>X)it+-xEFG;5<&y=g+LW
zkN*H~J(LkbRvV)!r@v~(1nL9K8oR7%t$@EhfWG~EGd%=GTN7ny1k=-;)@!KOMa#mT
zv!Z3Co;5V(VBxLueQyXdI?rlk(6UG-#-SwR>pP@Np`?P?04x_8Yv>#&mAPGjg)>4P
z%YBin9y<;Eqa#n$DILIMVX`2e{#hnEiZzb%s#i&1q6N$X23iYiYxHzLSVV&5df;0v
zsQFPX>~J>rzcFL`-t0Wi755uk&MmeMgtP1ROy8!Maw5vN0EIyD3Y^f1iQ7Dpt?
zVj9`X)FDegOZv(A`_TfSeKdYaHlktsU`d~4V~(dO+DutsI2qad5-7Hv6-v{K4Gkq~
zO+4w*ciuZr+2-0gGqoX;N3)?uzIXjj({&;^i}aHTMAhoOz1Q~Rl6P8rglUMH+*(Er
z5{&4{v{wo-hBq0nNif%j^UYs9jd6U`snA=0|UQDY#r0QwTxa+%zf3uXQr~YxdwN1mXKRn2((~EKj%)`r>uWTwE*{b*s
zR>E?$G}DWPQ=0@5uKUuMAfs8)*bsZu!BakNrv1b1cE`ybVLnv^ur$BIR0e<6mneRr
z$V<0O@D@rCsnmGnMpkD|*aLONv6CLua33k$TqsSta
zC7;CcL|x;1*v`T()_sqy^X0Xi^X$bYvDcllU3SDhWVn8j?34{QBJg*B_7cZsG
zCF>n|u1!8dIP(Kra0JOqTnGtPxh{85vqbCNdwtrwmulJ>io?fZ(5b|3I9i&AfdwXn
z3pQ{RL&iANs#QfWHz#0oxlB?8&C2KYSd$(d>0X)*N*Rqh7mt=iyU(lDil;5frMbza
zRXzzhOR@+%q%S5oK4++FQ6`q@#BhoI!t4V%+lWV}(*g#$igUQg1*0r-RXZ(8)gHgi
zSH8(I$M+`*=PSb@&3z!rmvM4={dleH+(08OtFOR1S^{fIT~Fl7dYWSpmuTmthfKxe-?2aCgqF}Y$cw(i<6fkK!eteixR+y6`Ke#xzxZqRD
z7i>A>2pNF;oc$_6;UGg1Lpdx+_{%(dIX8i$!*t7aDpE+b_Pi%2inpg1M4>7h;7ZB&
zhqIr%PG~*7)b>D8au7OoW}656BrbiaVi@{G_c7a^@|1ZWotE$AGl;!rt;y
zf{plll)I_SvCaXyL$_Hcg=mnPLSl+eL2f#)7)Ii<5W@DwjS7xXk^Wn@bRA_`gY&%3
z!;%@Q6VG{yd=)5P)h-V$+zm>ZHqEW}?QrnLJY8m7D?cmqgZUH&l>&o6{tPSgqk^>YLto_dWXKTa(;ZP`oUeL7C6(@8EpaI
zvb@t~{1$&5JN;O|n$_w{L`Kkf>M-0SQzjL)m!w$#wuwfqfI-}iSAsRKoHr%uCH~b(
zx^%E`GnQyUw9%stB~H}x)E}FjU{*{lA45hT-m&9@*DJgiyjgOQTTx3R0GXeiODn>W
zTI>gJE=uYuD9diN2q654aN%TiKwZr6kd%e$p-0*IjKpRF`R*j+5PHkQXlI$l_*=cj4QrY7}M(VW%o3+A4c%{daR%>6qNWBM%?-0oNVV_R
z=5wFC5kfIJgr$E=7QO{{7x!n=OkDnV|g0CqA
zdGBk>q_^gC!l{rlqj_f4{L3DQsz7?5&a2F+XuGEPrP#Z?ILTc!+U_>WhyBRqdi-H-
zYr0)F{(QkhNxqT=j+e%^5;JvQ@Zuw{C5(W9P7kh0cL^02
zeoAVp`n|DHrFNu2rgcy3C|fhf)a!i{{u!bB=ldT&*!39mQC*yL4tz@L+WK5@-Li@Q=qy*HtCuR~D@GZVx20upz`
zRjSk!%~?b{@sGVKJuWB)eQ(|NkgcFf(NFuHj4DHH-v~KAVDLi7l@qF?EiHqis!fR%
ztcgLxyOL>>CG+*v-OOI|rtfKW@aeL;fB6QS>?|5w=7H|w9)@KP#YrD0kSi{8j3+VA
z=nla?~OADzq|YVaJO4AcD5oquk-v?_kvs9e?wT>5G-A6afEd!69|0HQu+Im
z3H>MNzxDLrdirlY{lDw!;4|Fp8Q_djyY&BZ)*J~Aj#dO`8!IAfO&L&Pw}F~H{#b@|
zm}5N%e@FO@^AQxDh>`OiSylsv>LgRUUbh&t3r8T+3*ke{TAkRhej)<;Xd2q>;9pBoAY!0FK;sDN
zd%c8hzKkK!U*huBwR5pzoQ1DmK8%Gx^|(=(^wIQy*aCq#fS&@enXY|1
GNB;$~V-Y0)
literal 0
HcmV?d00001
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 0000000000000000000000000000000000000000..8a4f4020eb154fad67d3b6aeaa34090acb054090
GIT binary patch
literal 1651
zcmWIWW@Zs#-~htO%?k|}kN_tG3xls~h@-BjpPT-_Qw$8u3<2Kk93T};P-Ou)HH!dM
zz%~0i`gyv!28ZbRx_$ONbK1vSSMMUPx31Q?Gv_x48C)@b@U%$J%U8$K_hRWP7S0(j
zBsDWwra#mCk)|g8R6Ja4`jTg2+7-`KUuS+Q`dGvWc7@NyUN0A*9Uu&H1sB*w=w1*&
zc11x>YDHh0Hv)wi@)HC5iYC1SmT`c@_n%fnNoZoFs~Jy??Rj4AX9ld6!n
z(dW+_UX^=9z#yXMFy7dN(@e-@Seee!3@
zm7XJ)lKwW$xx%H;8gTT40z=U9`IBc%U-y3gl&P~~m`o;5Z0uxYl8~Iys3OeF4GuY*
zHP_E(0v%Qf#JFQy8;8?fQqvMkb4t*CklTNj?_hvH>v;{`ue&!XZ=8^@P|hKzW9kNl
zZ`(@)S5_~L`WO`^#LfR;;gK!U+(svT&iU+{X}tdXW6lNrt9Y4RxmCGSxkZ~Szf6_y
z<#1cRTP@{yl``kmnd_g}*zGck;WU5HxA#ini<2wocl>h6bCq#PuC>Wj4K;Zof5dgl
zgl5l-*9Ffj_N41S+FZ=_Wa?>t#}%qQ!W>(R#ojqq2`>D}|JNu(@$kQ!YWB`DHT|29
zu9}#oaD8`k-b;(~H+^Pv20fV7_?HoseYd0;+8+UWor@6^IE+jp45+yumbF2-9~Ho}
zGpH;;*NU7ULCFXKY=KO;R-}>uWC9lh_Jjz^w+L_;$b_2!Pm-ukN6syve2f4eflPGW
zsJR-SOHc!0DKO9=xed45LHP{g)sM^5gb{D=T5z{tdwpV8Ac
jdSoJ}5l}WkfcrR1#F1$Nyjj^mnmK_m5vcVKGl&NOW97L1
literal 0
HcmV?d00001
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 0000000000000000000000000000000000000000..2e1e4566f4ec5510748ebbf005b9e233fadbdcbf
GIT binary patch
literal 1650
zcmWIWW@Zs#-~hrY%?k|}kN_tG3xls~h@-BjpPT-_Qw$8u3<2Kk93T};P-Ou)HH!dM
zz%~0i`gyv!28ZbRx_$ONbK1vSSMMUPx31Q?Gv_x48C)@b@U%$J%U8$K_hRWP7S0(j
zBsDWwra#mCk)|g8R6Ja4`jTg2+7-`KUuS+Q`dGvWc7@NyUN0A*9Uu&H1sB*w=w1*&
zc11x>YDHh0Hv)wi@)HC5iYC1SmT`c@_n%fnNoZoOz0Jy??Rj4AX9ld6!n
z(dW+_UX^0n=B4+CDPisby1)Qm)E0AN=NiZqa6aHRx;Oa+GyXIQWq>LuqS78hH8&A)EP#>QsL
zR#wIqR>o%L#MaE^cf)|12u``{d7*
zD?LXpCH-xhbA?NxHQ?w81%{yI^C!=kzV7|}DN|>~FqurA*x1R)Bq2GYQAL=U8ys@S
zH=HcU1Ujq|h;hfZHV&t|q^2d7=9HlOAh+M1|6qc^vH8JKuimF98+sUIyh&12OH}x_
z{p+Hj_u5yx)~8OAs9})zirFo3Lbbf|-KX8v@4nXAFn$T#nz$fiVamdsg-!#_3jkww7Oo1LvPu+tHk{reA&Sk}kHt%{w}T
ziVQe&W#7s4e}CgzQ|8yWr|735$JUlYriEqa4%vF>E1CRX7|*7ga>M@ilk|?}_l4Uc
zOoMozRFz-Qm3?kGJJZ;aE5>+(Jt+6iXIilNFwo~*jG(Y#WD;RO&HS*O4a)qe0G^vc
zMFF~2Ub#
h$Y}(WOAz2b4ijzE%JL
literal 0
HcmV?d00001
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 0000000000000000000000000000000000000000..e1629a4053b653dc875047ab7f6ef37286a712c5
GIT binary patch
literal 1651
zcmWIWW@Zs#-~hs@%?k|}kN_tG3xls~h@-BjpPT-_Qw$8u3<2Kk93T};P-Ou)HH!dM
zz%~0i`gyv!28ZbRx_$ONbK1vSSMMUPx31Q?Gv_x48C)@b@U%$J%U8$K_hRWP7S0(j
zBsDWwra#mCk)|g8R6Ja4`jTg2+7-`KUuS+Q`dGvWc7@NyUN0A*9Uu&H1sB*wQ_#F1
zfb5EboYacaVts_0(N)V~QJtKhTacfZnpaY+SCN~u+4J0a-4o}%&uU-P_0!$>I4EF4
zW4B22juT7*b^%K??A5R8?bnFax3pI^Ro=KIV!eX;RwfV2!&9VgzGxObSd#LLDf9`G
zs*tzQ=g%8nm3sPm>zvheG-L7=KVtbyt-pFcUjO|u=YsxKyv(lLs@$pEqD_`x
zrpotnxGmqUmU6sGne*z*^-pZ?{MP0UibzPmZ^rN#N1KC?N49?WX|%LvN8i&rH5Jp%MP7b7Te7@0&EP;);lYlCt>
zDu8EaP+5Sk6*)hGk`V&f0-12FNF@Qt1TF^b2@#ZU5#TV82{!?rBoR&rE%za`s-mGjO&744(2-NzA8N>qsr98bc
literal 0
HcmV?d00001
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 0000000000000000000000000000000000000000..211a32968e5cb5f576a56a32661a628919005064
GIT binary patch
literal 1651
zcmWIWW@Zs#-~hsD%?k|}kN_tG3xls~h@-BjpPT-_Qw$8u3<2Kk93T};P-Ou)HH!dM
zz%~0i`gyv!28ZbRx_$ONbK1vSSMMUPx31Q?Gv_x48C)@b@U%$J%U8$K_hRWP7S0(j
zBsDWwra#mCk)|g8R6Ja4`jTg2+7-`KUuS+Q`dGvWc7@NyUN0A*9Uu&H1sB*w=w1*&
zc11x>YDHh0Hv)wi@)HC5iYC1SmT`c@_n%fnNoZog<2Jy??Rj4AX9ld6!n
z(dW+_UX^$o4aHLfqKup8vVi^k>hNJwnNQ*Bsg8;>Nb~
z&!UpQPyS4~(sSfe(%+^zSGW{f1CE|hU)y|wGIdrAlgZ?Xjh&245|T3-
zRfL(j!68@B-*Pb%=&(v4M#MI{)3tFp-6b_Gu{5Uy-3Ph-_WTDE1dh!Qj(YVzMcL59
z;6;Kcw;?C<#{Fx>r&h(U46D(deB=T1kAO$FjymY=JY78f{@vpB-yd@>=wHRl?8>dm
zoyslRWcg*Pd@qOF^4)4F$E%b%ug+Zm#KvxyQ4FW~gTB323SXRDIltqVOP;HYOLDDE
zrfR6k3;83iQzkTfX1p$VUa==#|Iy}Rt|wDZ^E<9k?GfhKS}gX?u}W~^PyW9~8H$Jh
z-Bh!8mZ|CAe00^sEQRa4oAX{;oWJQan=|Oatj52LpzPam=5yf@px3z=L4m`_B*K82
z`(ardl>1QuJUfHR0(7m&`4N>V5
zVi8wM%fHx}}NHZr8CIYqoVFvL4V&=dG
literal 0
HcmV?d00001
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 0000000000000000000000000000000000000000..e16fc3cea40571aeb2c59679148864e1b313c14f
GIT binary patch
literal 1651
zcmWIWW@Zs#-~htu%?k|}kN_tG3xls~h@-BjpPT-_Qw$8u3<2Kk93T};P-Ou)HH!dM
zz%~0i`gyv!28ZbRx_$ONbK1vSSMMUPx31Q?Gv_x48C)@b@U%$J%U8$K_hRWP7S0(j
zBsDWwra#mCk)|g8R6Ja4`jTg2+7-`KUuS+Q`dGvWc7@NyUN0A*9Uu&H1sB*w)6l#i
zfb5EboYacaVts_0(N)V~QJtKhTacfZnpaY+SCN~u+4J0a-4o}%&uU-P_0!$>I4EF4
zW4B22juT7*b^%K??A5R8?bnFax3pI^Ro=KIV!eX;RwfV2!&7G6c+o6+uq5RfQ|J>W
zRUvPq&!0EED)sdB);X)`XvX9#e#G*Z#yiJHOn1AFH2+`+`_R<=IClonMGt@&w+~r>
zviU{nU|(Sm174_#j6`SvU{?x?G>~C%r38aa1&0o2Sg@JsCFdj-7h8YLzi!9I
z#%9Y_R>l@q#%AWmw(h~7ON*X_Y(Fz6#Eq@)`JX#YfA(D2BP6_c&5=zmZfraMEGqf?
z_U$esI*Q_bJMT9tJOxM7Rw(
znK$lVD?YUh+Z|CXa>G$szumAp-b3y+qUS?NrRqj-7(I(3;
zQ{{U(+?MZFOF3Sp%z1U@`X@GayNqHu%^&pby;At%H3d07jr$CdYa#Hg=&v5$JS!8caBwp3xD$eHOf#t{O_iky|YYB
z|K_8sCT1yI-`$+|(&GG0pV^#24`wy~Wdvp4+kc)X907WrixCt!j7%a7sJS1OwL!Tb
z6~MDIs4PI&iku%o$p`^#flRnoq>=z+0v7}Jgb2#F2yht4gqr|Qk_e}R@-cF50p()^
z_y}a8>qgDh_*{Y-2up#12FY!>-44oc2yhD>VuS%w;LpU}19@H%pG2jxcuPyt3J
rw)~8qwvi(f6qv|q1e8q>;64r$ab%hRZ&o&tW=wq*jy`>m!_vu38R@>g4>~g8aPHypm$Qirk#dp6AZ%o;dG)R{Nr^pYF!T
zPlEzBG;)hJ|2&}-z_=hFu+DE)tY=nX=R9MdOIO_Fb-2PO&y1chc^3B+Po0xz&Nr_p
z^Yr!BIjiY-tz{Wc)^fX5OFe2PY@V||t-h?J0=AsOMI!xpA+^2h-;aJ=;hF0-rwI&KQo
zhmh14jpjp80s{LG$@y7{Wr^AzKIi;={6jT7b@etrd-~|9mWJ=ClRjZ5&S-n;dU^Z!
zg%-df$nfdYRctE*7?@V9dN2v-!ID-cg~&(qCofvaX|`zEbZ)R~Y<_c9`2g(-M{x~$
zV9LN;B1}x-Q`c!rXF4GVf?P8a^aS_sosZ5*V{*S`%g@9T0d#$(}S-%
z1O6@Et@xvK-*x8Ai8r6PdFQh8U!AH}!}4d^%AzD~`;D4>Sw_*C6)%AS;=l+=qL2uzK#M?W+(8D4M^_(1zQYOvwI)6x2Uo<$E?*6C=!eY|>m`SiAypr8iDOLjiC|F%u`vImu14ST1U
zNqCG*A`GYnAFNaZ6?~`wUXp=|!2oYmt;ppNDAgi>EszPp@zOvoom7L4f@*
zE$F!gIRruZ9yt%9hF~2qPOy~%$R>asft(~kc@+V8n4u=%$hpWSfs!zCA^{~~1h5CD
c3b0!+O#&t80B=?{kYY|C3<5g)J2(*m04MV&`~Uy|
literal 0
HcmV?d00001
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 0000000000000000000000000000000000000000..9e734a10a9057e9a1dff790225d51dcacf89eb36
GIT binary patch
literal 1886
zcmWIWW@Zs#-~ht!g-fj%kN_tG3xls~h@-BjpPT-_Qw$8u3<2Kk93T};P-Ou)HH!dM
zz%~0i`gyv!28ZbRx_$ONbK1vSSMMUPx31Q?Gv_x48C)@b@U%$J%U8$K_hRWP7S0(j
zC67qY(9U3){!H^nnwt1i@o=%}OP+~oS3FaFo%yNgV-X|RA;MFlZ@2@k0b!6sxWJCO
zh~^6cWQP>wq*jy`>m!_vu38R@>g4>~g8aPHypm$Qirk#dp6AZ%o;dG)R{Nr^pYF!T
zPlEzBG;)hJ|2&}-z_=hFu+DE)tY=nX=R9MdOIO_Fb-2PO&y1chc^3B+Po0xz&Nr_p
z^Yr!BIjiY-tz{Wc)^fX5OFe2PY@V||t-h?J0=AsOMI!xpA+^2h-;aJ*%`%so;AbleoE
z4%BU>*ej^
z7g_*|Aj79mSFx=OU|?FY>cJ$S2TNL+6e1r@k6O2o(`?bS>D*w~Jldtm>jSha9K|*0
zfhhxXjZ11;VrfnZlB2+3n%ZyZ&*Ui3dcUBcd>#i!TU*49##VP{)r|)u1T_@CR9j3^
zJuYYY?L&TMYSKgg2kw&3^;A?Y?J>9h{PyUT=2A8)<2+8``V^xPVru>O{W
zX(=x!#h3JLpa0W0b)&56?A)XqHx4@2E#(rQwvW-PA|_@C!3enyd#lT-S)v2|0zBv0+N7WwxgH{4soBL8RBn^Zd{`4$!B9S7u`
zH*($!S-skC;RE}4RjXT#+^3t;?YDf(3K9$~QhC8~J}>KItUs46$lVej*q!r%u7adH
zMkWyk)N&72sDa8oQ~)o?K&4=SH>y_TVhEII5x^G6glk1A13@NmG2kw)5GEW$HUVCE
zA?ybgU&sXpsQ5yFFF+>TeyDEL!VK9Z=t&+_#2~;NU}QrJ9E9~CEy%eUROBGQewY^Y
z%z_+(plpwv1yMt=4j3oc3ISvjK#o98lAx@L06ff46L4f)WRpNi7&(!Ek}v|;0aFFo
ZEtn>O5_Et!D;r2LClCe!o&61*hyXw|D=Gj0
literal 0
HcmV?d00001
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 0000000000000000000000000000000000000000..f523220c776e19233d77bf8e6adc8b70b5568076
GIT binary patch
literal 718
zcmWIWW@h1H00EUr7A9Z@l;8x?zOEsTx}JV+`T;;?A`BcrsEnpe1X-D{qo1dnYjB93
zuiIzeGpBvLb@eXtdh2SPJ9B<>kiiw>2TzO6I13f(c=(+22xaJERQ3JZBU-U@PIr*7
zcTjN2TrEc_pD#U+cy&rA^Z1CGGsAK@R?dS#BH3
zk8bsO5_Q$M`04UTYBDbjew3?m#LZ=yd7}TInO|y9B1iA~mEAieQ%!HO_Okb!WG;E$
z&enZ3=aA;DHRt#p*vmHG%hh}hK462RDo&ZG+3@m8`c?%v<
T0p6@^ASo6gd;p|xFoJjhqouzM
literal 0
HcmV?d00001
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 0000000000000000000000000000000000000000..95147bca36a88e49d9a83980b11541febb6e0824
GIT binary patch
literal 1576
zcmWIWW@Zs#;Nak3uqfnkW*(j{<{BKL=j-;__snS@Z(Y5MyxzK6=gyqp9At3C_`%a6JuhD!Pv48Bt5`T^
zyp}v7Evc39LtItu`4TOQ=i;JI#8sy;OwImO^s$H$>jK0GM<}EDKmaJ4
zlA4&3lb@We@2HoYlUQ8r+jo$wDL}yWy?5=jNVob6>!MWFb{}X8yR@v}{}uMVMy!|r
ztq#4gi2udnMHic9HJ-j>ot`jn{cq+2BDUO8R%*M{WZ3M&S89pz+$s<;c(Gb%8c*N)
zpq-lj3(u4*`DncS9jP+O;mN8`_XG65y*iWdd!tC_y+xEUckEAF8}(Jzigmz
zFK{k$-wO252jac#1o5(~-)X*s0Rpy*OTDiu|2x3P-D|p0VPjT=!0+4N+M=(jh5kG3
zmGqFgX3C6dM|_Hmo=-jV`fml3#Ny<`4ws!yJIz*DW_9srPr?^=>!zI^&aN5NR)MQe
zpA5el_RCN^R-ZRz&*YhUKm1KTH`@uz=!Y2fTz>H4oH|SOzo_$`I|(9Z?o497`cSsy@nWX#z$uFydN2ND3uuqM
zCY^ffCpflO+Zr9(3-tCk;=S$+i|wU%kMU|AMpYVpdb;cFXjAjg|Y}@iQ2I$$jx6>P2LM;KNQx(n$|k6XRk
z3Z-Q39&Jg;zvL~pVyT`|l3~}AP%DGL)YtMVC0vJlQ#X}guUpb2V;k~gds4@A>7J&U
z(M1ca_B7|!gd`Yt1&ORMw7SpmYQyQO#XgquAfK~zyp`Sqk8DOJ5eC%a30C%iiYHV6
zFK-BPRz`-5Y@60k<2P>d|#02PvxV#mq>$p$RC!o0SdZH#Q(F1-fl6IGh0yLHKh3
literal 0
HcmV?d00001
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 0000000000000000000000000000000000000000..1688b673dc33b22c15fb860c492cbf3349c58fa2
GIT binary patch
literal 2252
zcmWIWW@h1HVBlb2$k<^K$$$hnfoxyb5Jz24KR5jVpfVAlG7hjZJArSNK|pB`28tq-
z`8xV}y1532==r*R_C0gj$6HtLBCofu*10q1HwPJ9F@Es0NbBqw&x>EZPF~~H^YCTk
zjQUVyDZW%~+LC22tU{wSf2d7c`b@LJl1-E4$&_X3&om`o&wL^Ispw-7BLm0{5T~Z2
zIh6(IhUENQuq&~v0C@(aBCQCoGCpKw1k?&+s72F&9-#a{4e$WXO)N{zLvk>>VhK#e
zK=ObiUN*!|?2n3<=SoRe5wTpD=V?~sFt
z?cvp1!W;zz=0D)_-5@P2E@7^A%(YfU!faie)uRd9oV^S`ZZ28a$!&k2-{_vEN(;;G
z9qQljSU;ch^3R{TT4sxbjjb|fJ|O{T7pEl(moi7QZIdiYH=6R%(s@_ujpP$CE8GtT
z6r^h8sX4~GeBbk4IJju)@xOBYOWviYsMSqNZrk;G<$=mBu~Vkg6Wha>oZ7NpAC&zh
z{_cm3jKy7xNe>rHmiUz6AoNr6m6p7(t^
zIwj~sL(sJ87kG|&l-pH?M4evOr?%;f)-UhIm(qczD~mZK6TiI>exoZH9a79W^B7lX
z^)81UUKvTetv4TDcInwszQpS#f66l^v4wV1)K~88=v2DK`LcEL*-!F_3}yt3lPMY>
z*))K0G8q^rnncBE0Wfh?|7a+Rfv?gZG`fX5n~ig}htr
ztE-o;-MnWR>!@+!fQrJb3%1OswtV=S$}R8kFnH3JAFSS{k^62OD`a_GYj8)@m2>~L
z+2->VJkBjGbqOlV+P-H~<6RA}n2gW&KfN?PnlhW?nIroj>%$8rgjOZqO~1p!r_+=k
zAym3HaBp3nK`7Ik9w837i6?{~|L6;N-pD-H{VbgRJG7j_$UeDa&9_S{MPv4G6E
z)xYEy%sihUroY><(B^)}Gg;#UtL_D~d2ev~U{rebRY&H`-5GON+P|*hez5y_tg*WN
zIrHYjClxA!h1HvuE30<1{%1l2Hz?yBWN?qQ2L`hT5E~F3+yzDX1*t_PnW@FzI$9@v
zynXaO`fT*oK6U;otKgoPm##mTvN|k$@+Zc&!y_^)urRG|-rB}_jq?;Ersnz8C3WSc
z`a_Wl%3pPe35oX+_FwjLnAi(g}5k$j_5QsM1r7}n{2rOw-2a<4YNM$s##n{SJ
zuy(M;tAR{N35#$lx(-ko3jrXfo&_?IN?hF9KxHljfV6!9GU3{wr4??ypfVT&KzbE`
zIR;fPh5_isET~LI7!U|F03J@T5*lGUx@J%rjnG_&sTt@PSc#3>6i}IsFl7rKQ;jppal`KvDmU0!r1S?+8ZsvCT}AWf*EOsUcbn>NaJavUVa-
zLemII_9k*|b;;h0Y}vC6_x^^I+g-lzd4KP7e$Vng@AJpU0l~jb7{rs}Ed!l(ELaISAGarzNvEPD5GcoxyQ=0gc5`uqV(RB;qIP?9q7bu^
z7qn^A8LAo;feNPXF`B(KuM@@$Wr7?K+XWk^u{CJmdS77wXwX-GKoj`!UE+WhfC&!4
z_&@`Rgg^{21dj=R19WDCY*jndqydMhHrheheCs6z{JMAYUbGw7ypkKE`T5P;0Iwh3
z>wl)g8y+vh6MEK@C^Ch`4tRmUcWk7TMd1{QYZ0d{_MJ0c?V01JG+FL7pg2e)Qv{zL
zMq8U6*YE1zWsGLDRO0McLdQl=56^dY7?Rjrw%ld4x}Eo=?gmMzwogyNIO0w3YI8?d
zO07%0IP^(5&o3GMx0r4TC=+z}{^`-%_UvhKUHr_NI?CIKx#Gw;&mcJjF>>E>Y|uzm
zlFPV{;1rf)u%tBsH+r}1C%1#L!v8VS&WwJoo}ca!W?eB%uNfTdscLg)U6FsLK}(YU
z>fim$Km9@;KF?E|gwOEky8MU|lN9bsz^7qqzA(%2Yp4L1>9SwKI>;tHnbKZ;BHY#H
ziMbwE0wX=M+6&=TeM04fd!{cFCX{r;Zj6N??+a#~Ayi-g==+I&K*m{k%IKWILayzI
z?^t2|B^6dfYL#QqP*GRDp?(o!G7&H1N9rY<`0ygz8Z~;l;8pf4X;IC;a@-yEdFTpL
zn~HQ+4c?im+c)q02K6hWhQ&JDt0QvREIa;&L`}Z4yGsvKJ%zN~P>IFTSmlm*wdMx)
zpD4Y##RH+F@XW;Dl?w0gehhVGaf>3FdJyW!N#z7)98O}~eH4;c8QwKtMtkDxJl>lX
z{V>|u>;C!JCPQMW7`ZJVEk;%CDNoKVH8mey_p|&HIuJRz~9#iYECKnTJxnrS<6SeK4JZj{K@FQ4?@zP|6f^~wQ
zJ{_mb$dmUtdKEic==>CDOFc-DrBZr!L2K_6W#|mI7l|^Pc}Txo=Iy|VL$gLxGfI)n8h7$tE7yP
zdquWiI$5(=VOSSDxg=qtJ6fM)q~3FJaY1q+S#N(Er#{E@>B|+2Lh)EYO=Fa7*lZN1
z(1k0r<`+AhVnj1zA1y6NgPOEgq+NJF&>M-VD1+6BlTV%Szk#(acpmY(b!qN?>@&m|
zI;S)LNkj>HzB{X>BU5ItKz`JR_}ETd`(kd-gNYtdwRt9hGISOvgPn)f7%nxtV>io0|zH>P%6x%pYeQdMQ_$
ztEgT2{)nZ;(Cx~rg`GABKH}{9Sqr!?U!}QbnDTyBU4vqE+>7@V5cmosK{Pi;5H2f!
zP>zEZS5!#Uc{p^)=_aHuu|-@eox%=xlKo9WNhLO3<0b+e;@=S25R(PDBLrKOALAF$
zA|_8~jF4Z)$@GJJANk2i>&c1o{o&KZHwa#b4Ab#W!J82Md&^s=z{BXKaw3htWvoNh0U_`U&m{n?gMcF$7#axl7vQP4MgRZ+
literal 0
HcmV?d00001
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 0000000000000000000000000000000000000000..315a149b7a72a9589306a43a5a723a6e601b112c
GIT binary patch
literal 5390
zcmbVQ1yod97anqGiBV~gk{m#W7Re!GXc$63kglPXR6sfe1O^F3LK;Dk5CN$H1!V|H
z31v`1NkaN|T{4+q9Wf&uR%%rXtm%p%W6tQJ54tsrzHPN}B<>o>T8_4Fq1U
zvh!RAXsZK>FxL1t<_{B5hyyr$|C!Oy-%^Et=;7*N_br*opq>CtGLNYyT0$_Qx1E8xK!cgr&2Cm+ybXbN^NR6-!SCtN%dZ{W}yl8)wM1
z|H=T*U*TA}y12L^zD?)*0{%Vc5`P8r{iDA93V8Y=yexgdJ}%BE!wJM2R#4N5o)KJd
zC}w0!hsGhPCZ>@9Rd?o53tlFqcz0QvcF_Xw?!1)1>6Yf5ogTqe?s8amY8hRnd9T^^
zGt>IwdzFC-x2N~O83*QL;V+ilQ(4poOl$9uQfCV*Fa_J5Q83#o^|0RrRpZm96WttI
z6q=h4hbPybo0=l$j~|j6yK2f^@o@A#ROiZMO=b#(A`bPsN6Uj=&kjH0%B+
zJgqWsl&{KXkC@efUo`YM$a3ZZ
zO$NOs3MHyHOcD9S2P{zgUqi&xN8|e=J-n{
zcG0R2aFS~Dh6R~g&u+k&Cm9*jwW(9?CIaq+vql&X;87cz7ZHFbt}J!&=fZ7dry9H}
zWlbD-O;E(N1|XgaN7hVRk=H8)9V`RvNH*IEQJ1W;_;bn{m~aW^kn)RBKs2i?bvhUn
zrz!ynlH-4zT=BFlk&7*yzvwwv8PCa%y@e}!CbX2fFSg3tB8*`?buDimGEaopv^hr8
zLY36%blYh}m-zQiy6Z>U-K}-h66HAgp)|)T3YOIV*4UERVkV3?U={EVZ}wc7w0{5}
zDQq{9R*E|^=T1P*WyT_606|OWJ+=F2#hZj23>v-iC1M7MQ|xnqa8`2MX@X%FcJw`N
z1`?Hcb&&)6g_os4_08_}LRNI7PS0eXFJImy8f2W`5KrMU#qj$tUHT;MNnF35pIIHW-shqo1<<{
z`+)h<^{@bQN0>`)L_d!i{CxbGo-gQD!8s#npvbuT(yYq(Y>}!MYsgp_iOQs#EYuB{
z?9jM3)$krdx%;|A?$LP7g|{yWT2F^4g4DBrTw{y
zr0N;Gtv|ba3#U3>QJP@S5AkYS8yue=GWo3WWQ667Ee0%ShGm*ANZk`HF|Q9Plvg}xs+7f
z8Js$K`*rwp{M5&P!r13mKb|>za!?2M!`bMgwZ)-97;jJLjkfNBk&_-0j5z~aqA?u
z9LU%%rH+0cH}|lvBNQkHyEsSl-Z-P=zII}=NNgj6bB=l1t|Z|n6iVbx$N@#DNNn4t
zm3!2-cTUzR-i_zy9SstjC5Bqxh9y4R9rWMs-%O(b&GlMlY%OnG-nU^)=*Wt&oKEF-
zoq!ya36H>REs{LT92XS@!12>kg2wb>id_4+G)!&$G`Q`1wVgAU2q`F-xG892zO)3y
z2=5@nfP6Z_vV%UEFSmRYvXm-SCS2bvp1}THg55n+(8J^H(%~s~R1N%pWcq`Xtz129
z1RSg#TwOe|WV&Ri<2oipd41N<+86B;4C19YtXrrtTc$v#)ivPWR(i!Ge!XUPooWsk>y4
zTjr8xPa@09clqiXFMs7no-c7O^7&*%sjNOPw7dovR=HhKye8+0=E^r9cQ+=-fImhH63KM@ocE>)<>
zwp`FXe}#&l%rq~OT_NakxU7AMZp@iEy)){%D7uvEWZY5;Q?`YLTxr>fI*HW$f>!!^
zV>ioN9#Zx0=UmXXjVBD^6tu(LLxGrvM#eIkEI$rfWv~L_A(;~achR^v8Qbm%yK4%V
zob6JOafK&TBiA!MvFl--OsIaz~G3-_w}yD{zDR=8e#D-WJV?v~#QsYJ5Ub)1To
zbb^7p8qDmQuM^Z1G&X)QIj^;{=_TzVbwSlzWnsS{whSXxjM+J@NRHX0qkeX;gLA4i
zqTS^|Z@6PzWJUHWec+&D>0Vu$AWYBZw3UbE)K|*)qEO$`J2diC(8z;Z?W5T}op0Y=
zaUD9mH0%2d(k=RCzPoMNOZI$pr#PI6{1w`t!JnEFR$1&%wBtoku3i@U
z3U$4@TI%u?1)ppUo4JE9jVS4AW$Z#qL&$J?IOPy%vS|A)GZRl`oj0@*XFJZJ0Z>R4CUfc}4QZ}jFoTh@JCooUciNsAz
zi!65WUy}|rOT`5Mtf`MLero*#@F6|@vVWAqFf~P}rXCdR<>PhNz)TAx0SZKy*D0`*
z$5smTj+#XUjzZjv9wc~K0mEyFjbW^`H;CtG
z%FDUm3wPr)jAn@i3i}^yJ;h6fj|=6=*`b4NW9ZW
zzIn(6_9t*3(-QCMk18M+G5xxbdnAK=>v;=QyCeqUjiRsGdc^8;i%T4a`TF@PKjs&h
zmoIwSBtI+MH5bh2Do%H79Iq~}O8H1Kx1qF1s@0?5hgeL1{3*-Nu4vG^$`RQl#t~6t
zknd88H%9;U_3L7aW<%o!9+UZ3%Ch1U>;m3~;txx<%*rYa69R;HQx_&*EN%AipEPj<
zE&Hh}j~36bTCl+6d^WX9Ggl|27aqGHN#+COHYf-4hc!ci#pi==`fJQ4gpjVS=#g
zAZ9k^7B3zH4ctsu?t|bmS2piwz0P>qnD1sW0_g2yI*T(AFtaI&wuYz`;+0oMz>?LPz>*TQFTrhZXrWQCJ&b3u7xB#Q1h-9
zLj=dRxl}WSQsgK4k?WQ+m$^wAop{g@DqeycL~;Dhi%gf>z0BC
zFG;C-w^kD6x@Xb*;I8GUz)K()k=HE2rwnM9JH7{%&214XYV`DDn9o%vYHi#ukEmz2
z#Z7ZGKV;kOji~IzB8;fepG4EwlDqv?=z3$j$pRpFW=b)I3FpBBh4-Px{2ndO(=OIU
z4n$5>;5&7QxKTf~N#hWX?}zxT^v@%w@9LzVvyaW^Veok?@{S;e6PaZ9CD${2L9Lb+}QCF8lZ*UfV9x(;6xl4U(inl)4h%z>_|3i`0a{~b3y|H(8@vyQf1|DaN-}`HtL*Dq+U#n>kSl*^k0z9;Ked3q!EJXfa0X5r|J6)J84X18IU){rA|
zpNNd}1UpE%yry2G$%Rxh+LB?ue33O7jc0;SH&ZFHy4z+DDalm^tdkp4JV0#a%ulIK
z*UpZ`PwVcbDVb5jdK+-CY)n@D$cN>y6af-o^NWKE!uzv=@tvG00Za1Or(gO09(JT?
z9B<&UH2%}_g3lg4{t7#$ZyX=^_bLe6=zlltz`BQ1{oeK;P4)K(Y~z0v;q@cpm<;ge
zP^=Je6q@yW=rLK~&lp%?;0R;jj~M@y3I2?S6$*~z{$`|quk??Pf-Rzs6Xrj6-#OA_yYE;@bsPap
ntj8$5Y?A!kU`6M`P
literal 0
HcmV?d00001
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