From 877664a7703924c1fd87cb52600412c54ae347cd Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Sat, 9 Aug 2025 13:39:00 +0000 Subject: [PATCH 01/13] 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 | 124 +++++ .../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, 6912 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 5f55a310c8bd..3077fd28edbf 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 04b149010b0e..8882b060a543 100755 --- a/apache-maven/src/assembly/maven/bin/mvn +++ b/apache-maven/src/assembly/maven/bin/mvn @@ -226,7 +226,7 @@ fi if [ -n "$MAVEN_DEBUG_SCRIPT" ]; then echo "[DEBUG] Final MAVEN_OPTS: $MAVEN_OPTS" >&2 fi -LAUNCHER_JAR=`echo "$MAVEN_HOME"/boot/plexus-classworlds-*.jar` +LAUNCHER_JAR=`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 f25f85858f7a..4b72e3f63529 100644 --- a/apache-maven/src/assembly/maven/bin/mvn.cmd +++ b/apache-maven/src/assembly/maven/bin/mvn.cmd @@ -266,7 +266,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 2f95d5d77126..b1060a81330e 100644 --- a/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm +++ b/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm @@ -270,7 +270,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 3d3fcdee232b..0465eb7571ef 100644 --- a/compat/maven-compat/pom.xml +++ b/compat/maven-compat/pom.xml @@ -121,8 +121,12 @@ under the License. - org.codehaus.plexus - plexus-classworlds + org.apache.maven + maven-api-classworlds + + + org.apache.maven + maven-classworlds org.codehaus.plexus diff --git a/compat/maven-embedder/pom.xml b/compat/maven-embedder/pom.xml index 2df8588ec116..8060168e8e14 100644 --- a/compat/maven-embedder/pom.xml +++ b/compat/maven-embedder/pom.xml @@ -146,8 +146,12 @@ under the License. - org.codehaus.plexus - plexus-classworlds + org.apache.maven + maven-api-classworlds + + + org.apache.maven + maven-classworlds org.codehaus.plexus diff --git a/compat/maven-embedder/src/main/java/org/apache/maven/cli/CliRequest.java b/compat/maven-embedder/src/main/java/org/apache/maven/cli/CliRequest.java index 6d54be4ce054..0b4c45f0056b 100644 --- a/compat/maven-embedder/src/main/java/org/apache/maven/cli/CliRequest.java +++ b/compat/maven-embedder/src/main/java/org/apache/maven/cli/CliRequest.java @@ -23,9 +23,9 @@ import java.util.Properties; import org.apache.commons.cli.CommandLine; +import org.apache.maven.api.classworlds.ClassWorld; import org.apache.maven.execution.DefaultMavenExecutionRequest; import org.apache.maven.execution.MavenExecutionRequest; -import org.codehaus.plexus.classworlds.ClassWorld; /** * CliRequest diff --git a/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java b/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java index ebcb335ac074..80df683ae32c 100644 --- a/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java +++ b/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java @@ -59,6 +59,9 @@ import org.apache.maven.InternalErrorException; import org.apache.maven.Maven; import org.apache.maven.api.Constants; +import org.apache.maven.api.classworlds.ClassRealm; +import org.apache.maven.api.classworlds.ClassWorld; +import org.apache.maven.api.classworlds.NoSuchRealmException; import org.apache.maven.api.cli.extensions.CoreExtension; import org.apache.maven.api.cli.extensions.InputSource; import org.apache.maven.api.services.MessageBuilder; @@ -117,9 +120,6 @@ import org.codehaus.plexus.DefaultPlexusContainer; import org.codehaus.plexus.PlexusConstants; import org.codehaus.plexus.PlexusContainer; -import org.codehaus.plexus.classworlds.ClassWorld; -import org.codehaus.plexus.classworlds.realm.ClassRealm; -import org.codehaus.plexus.classworlds.realm.NoSuchRealmException; import org.codehaus.plexus.component.repository.exception.ComponentLookupException; import org.codehaus.plexus.components.secdispatcher.SecDispatcher; import org.codehaus.plexus.logging.LoggerManager; @@ -259,7 +259,7 @@ public int doMain(String[] args, String workingDirectory, PrintStream stdout, Pr final Set realms; if (classWorld != null) { realms = new HashSet<>(); - for (ClassRealm realm : classWorld.getRealms()) { + for (ClassRealm realm : ((org.codehaus.plexus.classworlds.ClassWorld) classWorld).getRealms()) { realms.add(realm.getId()); } } else { @@ -280,7 +280,8 @@ public int doMain(String[] args, String workingDirectory, PrintStream stdout, Pr return doMain(cliRequest); } finally { if (classWorld != null) { - for (ClassRealm realm : new ArrayList<>(classWorld.getRealms())) { + for (ClassRealm realm : + new ArrayList<>(((org.codehaus.plexus.classworlds.ClassWorld) classWorld).getRealms())) { String realmId = realm.getId(); if (!realms.contains(realmId)) { try { @@ -708,13 +709,16 @@ void properties(CliRequest cliRequest) throws Exception { PlexusContainer container(CliRequest cliRequest) throws Exception { if (cliRequest.classWorld == null) { - cliRequest.classWorld = - new ClassWorld("plexus.core", Thread.currentThread().getContextClassLoader()); + cliRequest.classWorld = new org.codehaus.plexus.classworlds.ClassWorld( + "plexus.core", Thread.currentThread().getContextClassLoader()); } ClassRealm coreRealm = cliRequest.classWorld.getClassRealm("plexus.core"); if (coreRealm == null) { - coreRealm = cliRequest.classWorld.getRealms().iterator().next(); + coreRealm = ((org.codehaus.plexus.classworlds.ClassWorld) cliRequest.classWorld) + .getRealms() + .iterator() + .next(); } List extClassPath = parseExtClasspath(cliRequest); @@ -726,8 +730,8 @@ PlexusContainer container(CliRequest cliRequest) throws Exception { ClassRealm containerRealm = setupContainerRealm(cliRequest.classWorld, coreRealm, extClassPath, extensions); ContainerConfiguration cc = new DefaultContainerConfiguration() - .setClassWorld(cliRequest.classWorld) - .setRealm(containerRealm) + .setClassWorld((org.codehaus.plexus.classworlds.ClassWorld) cliRequest.classWorld) + .setRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) containerRealm) .setClassPathScanning(PlexusConstants.SCANNING_INDEX) .setAutoWiring(true) .setJSR250Lifecycle(true) @@ -743,7 +747,7 @@ PlexusContainer container(CliRequest cliRequest) throws Exception { final CoreExports exports = new CoreExports(containerRealm, exportedArtifacts, exportedPackages); - Thread.currentThread().setContextClassLoader(containerRealm); + Thread.currentThread().setContextClassLoader(containerRealm.getClassLoader()); DefaultPlexusContainer container = new DefaultPlexusContainer(cc, new AbstractModule() { @Override @@ -769,12 +773,14 @@ protected void configure() { }; for (CoreExtensionEntry extension : extensions) { container.discoverComponents( - extension.getClassRealm(), + (org.codehaus.plexus.classworlds.realm.ClassRealm) extension.getClassRealm(), new AbstractModule() { @Override protected void configure() { try { - container.lookup(Injector.class).discover(extension.getClassRealm()); + container + .lookup(Injector.class) + .discover(extension.getClassRealm().getClassLoader()); } catch (Throwable e) { // ignore e.printStackTrace(); @@ -842,8 +848,8 @@ private List loadCoreExtensions( } ContainerConfiguration cc = new DefaultContainerConfiguration() // - .setClassWorld(cliRequest.classWorld) // - .setRealm(containerRealm) // + .setClassWorld((org.codehaus.plexus.classworlds.ClassWorld) cliRequest.classWorld) // + .setRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) containerRealm) // .setClassPathScanning(PlexusConstants.SCANNING_INDEX) // .setAutoWiring(true) // .setJSR250Lifecycle(true) // @@ -909,7 +915,8 @@ private ClassRealm setupContainerRealm( if (!extClassPath.isEmpty() || !extensions.isEmpty()) { ClassRealm extRealm = classWorld.newRealm("maven.ext", null); - extRealm.setParentRealm(coreRealm); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) extRealm) + .setParentRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) coreRealm); slf4jLogger.debug("Populating class realm '{}'", extRealm.getId()); @@ -923,11 +930,11 @@ private ClassRealm setupContainerRealm( Set exportedPackages = entry.getExportedPackages(); ClassRealm realm = entry.getClassRealm(); for (String exportedPackage : exportedPackages) { - extRealm.importFrom(realm, exportedPackage); + extRealm.importFrom(realm.getClassLoader(), exportedPackage); } if (exportedPackages.isEmpty()) { // sisu uses realm imports to establish component visibility - extRealm.importFrom(realm, realm.getId()); + extRealm.importFrom(realm.getClassLoader(), realm.getId()); } } diff --git a/compat/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java b/compat/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java index 06990ea303bc..47d110f914cb 100644 --- a/compat/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java +++ b/compat/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java @@ -33,6 +33,8 @@ import org.apache.maven.RepositoryUtils; import org.apache.maven.api.Service; import org.apache.maven.api.Session; +import org.apache.maven.api.classworlds.ClassRealm; +import org.apache.maven.api.classworlds.ClassWorld; import org.apache.maven.api.cli.extensions.CoreExtension; import org.apache.maven.api.model.Plugin; import org.apache.maven.api.services.ArtifactCoordinatesFactory; @@ -64,8 +66,6 @@ import org.apache.maven.resolver.RepositorySystemSessionFactory; import org.codehaus.plexus.DefaultPlexusContainer; import org.codehaus.plexus.PlexusContainer; -import org.codehaus.plexus.classworlds.ClassWorld; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.RepositorySystemSession.CloseableSession; @@ -175,12 +175,13 @@ private CoreExtensionEntry createExtension(CoreExtension extension, List providedArtifacts = Collections.emptySet(); String classLoadingStrategy = extension.getClassLoadingStrategy(); if (STRATEGY_PARENT_FIRST.equals(classLoadingStrategy)) { - realm.importFrom(parentRealm, ""); + realm.importFrom(parentRealm.getClassLoader(), ""); } else if (STRATEGY_PLUGIN.equals(classLoadingStrategy)) { coreExports.getExportedPackages().forEach((p, cl) -> realm.importFrom(cl, p)); providedArtifacts = coreExports.getExportedArtifacts(); } else if (STRATEGY_SELF_FIRST.equals(classLoadingStrategy)) { - realm.setParentRealm(parentRealm); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) realm) + .setParentRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) parentRealm); } else { throw new IllegalArgumentException("Unsupported class-loading strategy '" + classLoadingStrategy + "'. Supported values are: " + STRATEGY_PARENT_FIRST diff --git a/compat/maven-plugin-api/pom.xml b/compat/maven-plugin-api/pom.xml index 6bd39596deb8..c8fc5e23fc9d 100644 --- a/compat/maven-plugin-api/pom.xml +++ b/compat/maven-plugin-api/pom.xml @@ -79,8 +79,8 @@ under the License. - org.codehaus.plexus - plexus-classworlds + org.apache.maven + maven-classworlds org.apache.maven.resolver diff --git a/impl/maven-classworlds/pom.xml b/impl/maven-classworlds/pom.xml new file mode 100644 index 000000000000..5d141de70bb2 --- /dev/null +++ b/impl/maven-classworlds/pom.xml @@ -0,0 +1,124 @@ + + + + 4.0.0 + + org.apache.maven + maven-impl-modules + 4.1.0-SNAPSHOT + + + maven-classworlds + jar + + Maven 4 Classworlds + A class loader framework + + + + org.apache.maven + maven-api-annotations + + + org.apache.maven + maven-api-classworlds + + + org.junit.jupiter + junit-jupiter-api + test + + + + + + + org.apache.rat + apache-rat-plugin + + + src/test/test-data/** + + + + + org.apache.maven.plugins + maven-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_@UH&#G@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+=-fImhH63&#KM@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==`fJQ4gpjVSqnD1sW0_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 39b6fcaa5e16..6d84e70d1c13 100644 --- a/impl/maven-cli/pom.xml +++ b/impl/maven-cli/pom.xml @@ -146,8 +146,12 @@ under the License.
- org.codehaus.plexus - plexus-classworlds + org.apache.maven + maven-api-classworlds + + + org.apache.maven + maven-classworlds org.codehaus.plexus diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java index fdb957d5bb8a..7a632ecddcf7 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java @@ -23,6 +23,7 @@ import java.io.OutputStream; import org.apache.maven.api.annotations.Nullable; +import org.apache.maven.api.classworlds.ClassWorld; import org.apache.maven.api.cli.Invoker; import org.apache.maven.api.cli.InvokerException; import org.apache.maven.api.cli.Parser; @@ -30,7 +31,6 @@ import org.apache.maven.api.services.MessageBuilderFactory; import org.apache.maven.cling.invoker.logging.SystemLogger; import org.apache.maven.jline.JLineMessageBuilderFactory; -import org.codehaus.plexus.classworlds.ClassWorld; import static java.util.Objects.requireNonNull; @@ -47,7 +47,10 @@ public abstract class ClingSupport { * Ctor that creates "managed" ClassWorld. This constructor is not used in "normal" circumstances. */ public ClingSupport() { - this(new ClassWorld(CORE_CLASS_REALM_ID, Thread.currentThread().getContextClassLoader()), true); + this( + new org.codehaus.plexus.classworlds.ClassWorld( + CORE_CLASS_REALM_ID, Thread.currentThread().getContextClassLoader()), + true); } /** diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenCling.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenCling.java index 65fa91e78e84..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 d0735af889a6..4ac6051730f5 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/extensions/BootstrapCoreExtensionManager.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/extensions/BootstrapCoreExtensionManager.java @@ -32,6 +32,8 @@ import org.apache.maven.api.Session; import org.apache.maven.api.annotations.Nullable; import org.apache.maven.api.cache.RequestCacheFactory; +import org.apache.maven.api.classworlds.ClassRealm; +import org.apache.maven.api.classworlds.ClassWorld; import org.apache.maven.api.cli.extensions.CoreExtension; import org.apache.maven.api.di.Inject; import org.apache.maven.api.di.Named; @@ -67,8 +69,6 @@ import org.apache.maven.resolver.RepositorySystemSessionFactory; import org.codehaus.plexus.DefaultPlexusContainer; import org.codehaus.plexus.PlexusContainer; -import org.codehaus.plexus.classworlds.ClassWorld; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.RepositorySystemSession.CloseableSession; @@ -180,12 +180,13 @@ private CoreExtensionEntry createExtension(CoreExtension extension, List providedArtifacts = Collections.emptySet(); String classLoadingStrategy = extension.getClassLoadingStrategy(); if (STRATEGY_PARENT_FIRST.equals(classLoadingStrategy)) { - realm.importFrom(parentRealm, ""); + realm.importFrom(parentRealm.getClassLoader(), ""); } else if (STRATEGY_PLUGIN.equals(classLoadingStrategy)) { coreExports.getExportedPackages().forEach((p, cl) -> realm.importFrom(cl, p)); providedArtifacts = coreExports.getExportedArtifacts(); } else if (STRATEGY_SELF_FIRST.equals(classLoadingStrategy)) { - realm.setParentRealm(parentRealm); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) realm) + .setParentRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) parentRealm); } else { throw new IllegalArgumentException("Unsupported class-loading strategy '" + classLoadingStrategy + "'. Supported values are: " + STRATEGY_PARENT_FIRST diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/PlexusContainerCapsuleFactory.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/PlexusContainerCapsuleFactory.java index f1290b02e574..557e6ac6448a 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/PlexusContainerCapsuleFactory.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/PlexusContainerCapsuleFactory.java @@ -31,6 +31,8 @@ import com.google.inject.Module; import org.apache.maven.api.Constants; import org.apache.maven.api.ProtoSession; +import org.apache.maven.api.classworlds.ClassRealm; +import org.apache.maven.api.classworlds.ClassWorld; import org.apache.maven.api.cli.Logger; import org.apache.maven.api.cli.extensions.CoreExtension; import org.apache.maven.api.services.MessageBuilderFactory; @@ -55,8 +57,6 @@ import org.codehaus.plexus.DefaultPlexusContainer; import org.codehaus.plexus.PlexusConstants; import org.codehaus.plexus.PlexusContainer; -import org.codehaus.plexus.classworlds.ClassWorld; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.codehaus.plexus.logging.LoggerManager; import org.slf4j.ILoggerFactory; @@ -98,8 +98,8 @@ protected DefaultPlexusContainer container( ClassRealm containerRealm = setupContainerRealm(context.logger, classWorld, coreRealm, extClassPath, loadedExtensionsEntries); ContainerConfiguration cc = new DefaultContainerConfiguration() - .setClassWorld(classWorld) - .setRealm(containerRealm) + .setClassWorld((org.codehaus.plexus.classworlds.ClassWorld) classWorld) + .setRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) containerRealm) .setClassPathScanning(PlexusConstants.SCANNING_INDEX) .setAutoWiring(true) .setJSR250Lifecycle(true) @@ -111,7 +111,7 @@ protected DefaultPlexusContainer container( containerRealm, collectExportedArtifacts(coreEntry, loadedExtensionsEntries), collectExportedPackages(coreEntry, loadedExtensionsEntries)); - Thread.currentThread().setContextClassLoader(containerRealm); + Thread.currentThread().setContextClassLoader(containerRealm.getClassLoader()); DefaultPlexusContainer container = new DefaultPlexusContainer(cc, getCustomModule(context, exports)); // NOTE: To avoid inconsistencies, we'll use the TCCL exclusively for lookups @@ -124,14 +124,18 @@ protected DefaultPlexusContainer container( List failures = new ArrayList<>(); for (LoadedCoreExtension extension : loadedExtensions) { container.discoverComponents( - extension.entry().getClassRealm(), + (org.codehaus.plexus.classworlds.realm.ClassRealm) + extension.entry().getClassRealm(), new AbstractModule() { @Override protected void configure() { try { container .lookup(Injector.class) - .discover(extension.entry().getClassRealm()); + .discover(extension + .entry() + .getClassRealm() + .getClassLoader()); } catch (Throwable e) { failures.add(new IllegalStateException( "Injection failure in " @@ -231,7 +235,8 @@ protected ClassRealm setupContainerRealm( if (!extClassPath.isEmpty() || !extensions.isEmpty()) { ClassRealm extRealm = classWorld.newRealm("maven.ext", null); - extRealm.setParentRealm(coreRealm); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) extRealm) + .setParentRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) coreRealm); logger.debug("Populating class realm '" + extRealm.getId() + "'"); @@ -246,11 +251,11 @@ protected ClassRealm setupContainerRealm( Set exportedPackages = entry.getExportedPackages(); ClassRealm realm = entry.getClassRealm(); for (String exportedPackage : exportedPackages) { - extRealm.importFrom(realm, exportedPackage); + extRealm.importFrom(realm.getClassLoader(), exportedPackage); } if (exportedPackages.isEmpty()) { // sisu uses realm imports to establish component visibility - extRealm.importFrom(realm, realm.getId()); + extRealm.importFrom(realm.getClassLoader(), realm.getId()); } } @@ -271,8 +276,8 @@ protected List loadCoreExtensions( return List.of(); } ContainerConfiguration cc = new DefaultContainerConfiguration() - .setClassWorld(containerRealm.getWorld()) - .setRealm(containerRealm) + .setClassWorld((org.codehaus.plexus.classworlds.ClassWorld) containerRealm.getWorld()) + .setRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) containerRealm) .setClassPathScanning(PlexusConstants.SCANNING_INDEX) .setAutoWiring(true) .setJSR250Lifecycle(true) diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTest.java index b1115a4dce4f..0be124387f22 100644 --- a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTest.java +++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTest.java @@ -26,11 +26,11 @@ import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; +import org.apache.maven.api.classworlds.ClassWorld; import org.apache.maven.api.cli.Invoker; import org.apache.maven.api.cli.InvokerException; import org.apache.maven.api.cli.Parser; import org.apache.maven.cling.invoker.ProtoLookup; -import org.codehaus.plexus.classworlds.ClassWorld; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java index 91a69729b021..e02e0369cf7c 100644 --- a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java +++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java @@ -29,12 +29,12 @@ import java.util.Map; import eu.maveniverse.maven.mimir.testing.MimirInfuser; +import org.apache.maven.api.classworlds.ClassWorld; import org.apache.maven.api.cli.Invoker; import org.apache.maven.api.cli.InvokerException; import org.apache.maven.api.cli.Parser; import org.apache.maven.api.cli.ParserRequest; import org.apache.maven.jline.JLineMessageBuilderFactory; -import org.codehaus.plexus.classworlds.ClassWorld; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -156,7 +156,7 @@ protected Map invoke(Path cwd, Path userHome, Collection } protected ClassWorld createClassWorld() { - return new ClassWorld("plexus.core", ClassLoader.getSystemClassLoader()); + return new org.codehaus.plexus.classworlds.ClassWorld("plexus.core", ClassLoader.getSystemClassLoader()); } protected abstract Invoker createInvoker(ClassWorld classWorld); diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/resident/ResidentMavenInvokerTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/resident/ResidentMavenInvokerTest.java index c59ad3249195..9f57715e464a 100644 --- a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/resident/ResidentMavenInvokerTest.java +++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/resident/ResidentMavenInvokerTest.java @@ -24,12 +24,12 @@ import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; +import org.apache.maven.api.classworlds.ClassWorld; import org.apache.maven.api.cli.Invoker; import org.apache.maven.api.cli.Parser; import org.apache.maven.cling.invoker.ProtoLookup; import org.apache.maven.cling.invoker.mvn.MavenInvokerTestSupport; import org.apache.maven.cling.invoker.mvn.MavenParser; -import org.codehaus.plexus.classworlds.ClassWorld; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/impl/maven-core/pom.xml b/impl/maven-core/pom.xml index 07ffa6662b12..0d0a834e3adc 100644 --- a/impl/maven-core/pom.xml +++ b/impl/maven-core/pom.xml @@ -152,8 +152,12 @@ under the License. - org.codehaus.plexus - plexus-classworlds + org.apache.maven + maven-api-classworlds + + + org.apache.maven + maven-classworlds javax.inject @@ -396,6 +400,40 @@ under the License. org.apache.maven.toolchain.ToolchainManagerPrivate org.apache.maven.toolchain.ToolchainPrivate org.apache.maven.toolchain.ToolchainsBuilder + + org.apache.maven.classrealm.ClassRealmManager#createExtensionRealm(org.apache.maven.model.Plugin,java.util.List):METHOD_RETURN_TYPE_CHANGED + org.apache.maven.classrealm.ClassRealmManager#createPluginRealm(org.apache.maven.model.Plugin,java.lang.ClassLoader,java.util.List,java.util.Map,java.util.List):METHOD_RETURN_TYPE_CHANGED + org.apache.maven.classrealm.ClassRealmManager#createProjectRealm(org.apache.maven.model.Model,java.util.List):METHOD_RETURN_TYPE_CHANGED + org.apache.maven.classrealm.ClassRealmManager#getCoreRealm():METHOD_RETURN_TYPE_CHANGED + org.apache.maven.classrealm.ClassRealmManager#getMavenApiRealm():METHOD_RETURN_TYPE_CHANGED + org.apache.maven.classrealm.ClassRealmManagerDelegate#setupRealm(org.codehaus.plexus.classworlds.realm.ClassRealm,org.apache.maven.classrealm.ClassRealmRequest):METHOD_REMOVED + org.apache.maven.classrealm.DefaultClassRealmManager#createExtensionRealm(org.apache.maven.model.Plugin,java.util.List):METHOD_RETURN_TYPE_CHANGED + org.apache.maven.classrealm.DefaultClassRealmManager#createPluginRealm(org.apache.maven.model.Plugin,java.lang.ClassLoader,java.util.List,java.util.Map,java.util.List):METHOD_RETURN_TYPE_CHANGED + org.apache.maven.classrealm.DefaultClassRealmManager#createProjectRealm(org.apache.maven.model.Model,java.util.List):METHOD_RETURN_TYPE_CHANGED + org.apache.maven.classrealm.DefaultClassRealmManager#getCoreRealm():METHOD_RETURN_TYPE_CHANGED + org.apache.maven.classrealm.DefaultClassRealmManager#getMavenApiRealm():METHOD_RETURN_TYPE_CHANGED + org.apache.maven.plugin.BuildPluginManager#getPluginRealm(org.apache.maven.execution.MavenSession,org.apache.maven.plugin.descriptor.PluginDescriptor):METHOD_RETURN_TYPE_CHANGED + org.apache.maven.plugin.DefaultBuildPluginManager#getPluginRealm(org.apache.maven.execution.MavenSession,org.apache.maven.plugin.descriptor.PluginDescriptor):METHOD_RETURN_TYPE_CHANGED + org.apache.maven.plugin.ExtensionRealmCache#put(org.apache.maven.plugin.ExtensionRealmCache$Key,org.codehaus.plexus.classworlds.realm.ClassRealm,org.apache.maven.project.ExtensionDescriptor,java.util.List):METHOD_REMOVED + org.apache.maven.plugin.ExtensionRealmCache$CacheRecord#getRealm():METHOD_RETURN_TYPE_CHANGED + org.apache.maven.plugin.PluginContainerException#getPluginRealm():METHOD_RETURN_TYPE_CHANGED + org.apache.maven.plugin.PluginContainerException#PluginContainerException(org.apache.maven.model.Plugin,org.codehaus.plexus.classworlds.realm.ClassRealm,java.lang.String,org.codehaus.plexus.configuration.PlexusConfigurationException):CONSTRUCTOR_REMOVED + org.apache.maven.plugin.PluginContainerException#PluginContainerException(org.apache.maven.plugin.descriptor.MojoDescriptor,org.codehaus.plexus.classworlds.realm.ClassRealm,java.lang.String,java.lang.Throwable):CONSTRUCTOR_REMOVED + org.apache.maven.plugin.PluginContainerException#PluginContainerException(org.apache.maven.model.Plugin,org.codehaus.plexus.classworlds.realm.ClassRealm,java.lang.String,org.codehaus.plexus.component.repository.exception.ComponentRepositoryException):CONSTRUCTOR_REMOVED + org.apache.maven.plugin.PluginContainerException#PluginContainerException(org.apache.maven.model.Plugin,org.codehaus.plexus.classworlds.realm.ClassRealm,java.lang.String,java.lang.Throwable):CONSTRUCTOR_REMOVED + org.apache.maven.plugin.PluginContainerException#PluginContainerException(org.apache.maven.plugin.descriptor.MojoDescriptor,org.codehaus.plexus.classworlds.realm.ClassRealm,java.lang.String,org.codehaus.plexus.component.repository.exception.ComponentLookupException):CONSTRUCTOR_REMOVED + org.apache.maven.plugin.PluginManagerException#PluginManagerException(org.apache.maven.plugin.descriptor.MojoDescriptor,org.apache.maven.project.MavenProject,java.lang.String,org.codehaus.plexus.classworlds.realm.NoSuchRealmException):CONSTRUCTOR_REMOVED + org.apache.maven.plugin.PluginRealmCache#put(org.apache.maven.plugin.PluginRealmCache$Key,org.codehaus.plexus.classworlds.realm.ClassRealm,java.util.List):METHOD_REMOVED + org.apache.maven.plugin.PluginRealmCache$CacheRecord#getRealm():METHOD_RETURN_TYPE_CHANGED + org.apache.maven.plugin.PluginRealmCache$CacheRecord#PluginRealmCache$CacheRecord(org.codehaus.plexus.classworlds.realm.ClassRealm,java.util.List):CONSTRUCTOR_REMOVED + org.apache.maven.project.MavenProject#getClassRealm():METHOD_RETURN_TYPE_CHANGED + org.apache.maven.project.MavenProject#setClassRealm(org.codehaus.plexus.classworlds.realm.ClassRealm):METHOD_REMOVED + org.apache.maven.project.ProjectRealmCache#put(org.apache.maven.project.ProjectRealmCache$Key,org.codehaus.plexus.classworlds.realm.ClassRealm,org.eclipse.aether.graph.DependencyFilter):METHOD_REMOVED + org.apache.maven.project.ProjectRealmCache$CacheRecord#getRealm():METHOD_RETURN_TYPE_CHANGED + + org.apache.maven.plugin.DefaultExtensionRealmCache#put(org.apache.maven.plugin.ExtensionRealmCache$Key,org.codehaus.plexus.classworlds.realm.ClassRealm,org.apache.maven.project.ExtensionDescriptor,java.util.List):METHOD_REMOVED + org.apache.maven.plugin.DefaultPluginRealmCache#put(org.apache.maven.plugin.PluginRealmCache$Key,org.codehaus.plexus.classworlds.realm.ClassRealm,java.util.List):METHOD_REMOVED + org.apache.maven.project.DefaultProjectRealmCache#put(org.apache.maven.project.ProjectRealmCache$Key,org.codehaus.plexus.classworlds.realm.ClassRealm,org.eclipse.aether.graph.DependencyFilter):METHOD_REMOVED diff --git a/impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java b/impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java index 23e85a14b01e..0e6db84ba2f0 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java +++ b/impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java @@ -465,7 +465,9 @@ protected Collection getProjectScopedExtensionComponents(Collection apiV4Imports = new HashMap<>(); - apiV4Imports.put("org.apache.maven.api", containerRealm); - apiV4Imports.put("org.slf4j", containerRealm); + apiV4Imports.put("org.apache.maven.api", containerRealm.getClassLoader()); + apiV4Imports.put("org.slf4j", containerRealm.getClassLoader()); this.maven4ApiRealm = createRealm(API_V4_REALMID, RealmType.Core, null, null, apiV4Imports, null); this.providedArtifacts = exports.getExportedArtifacts(); @@ -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 460d15bb32e3..6069d848025a 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java +++ b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java @@ -43,6 +43,7 @@ import org.apache.maven.api.Lifecycle; import org.apache.maven.api.MonotonicClock; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.api.services.LifecycleRegistry; import org.apache.maven.api.services.MavenException; import org.apache.maven.api.xml.XmlNode; @@ -84,7 +85,6 @@ import org.apache.maven.plugin.descriptor.Parameter; import org.apache.maven.plugin.descriptor.PluginDescriptor; import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.eclipse.aether.repository.RemoteRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -1109,7 +1109,7 @@ private MojoExecutionConfigurator mojoExecutionConfigurator(MojoExecution mojoEx public static void attachToThread(MavenProject currentProject) { ClassRealm projectRealm = currentProject.getClassRealm(); if (projectRealm != null) { - Thread.currentThread().setContextClassLoader(projectRealm); + Thread.currentThread().setContextClassLoader(projectRealm.getClassLoader()); } } diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/BuildPluginManager.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/BuildPluginManager.java index b3741041e08b..729f656372ec 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/BuildPluginManager.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/BuildPluginManager.java @@ -20,11 +20,11 @@ import java.util.List; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Plugin; import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.plugin.descriptor.PluginDescriptor; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.repository.RemoteRepository; diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java index e395d1ed000b..cc96dda4ba1d 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java @@ -30,6 +30,7 @@ import org.apache.maven.api.Project; import org.apache.maven.api.Service; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.api.services.MavenException; import org.apache.maven.execution.MavenSession; import org.apache.maven.execution.MojoExecutionEvent; @@ -43,7 +44,6 @@ import org.apache.maven.plugin.descriptor.PluginDescriptor; import org.apache.maven.plugin.logging.Log; import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.repository.RemoteRepository; import org.slf4j.LoggerFactory; @@ -113,7 +113,7 @@ public void executeMojo(MavenSession session, MojoExecution mojoExecution) } ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(pluginRealm); + Thread.currentThread().setContextClassLoader(pluginRealm.getClassLoader()); MavenSession oldSession = legacySupport.getSession(); @@ -171,7 +171,7 @@ public void executeMojo(MavenSession session, MojoExecution mojoExecution) PrintStream ps = new PrintStream(os); ps.println( "A required class was missing while executing " + mojoDescriptor.getId() + ": " + e.getMessage()); - pluginRealm.display(ps); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm).display(ps); Exception wrapper = new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), e); throw new PluginExecutionException(mojoExecution, project, wrapper); } catch (LinkageError e) { @@ -181,7 +181,7 @@ public void executeMojo(MavenSession session, MojoExecution mojoExecution) PrintStream ps = new PrintStream(os); ps.println("An API incompatibility was encountered while executing " + mojoDescriptor.getId() + ": " + e.getClass().getName() + ": " + e.getMessage()); - pluginRealm.display(ps); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm).display(ps); Exception wrapper = new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), e); throw new PluginExecutionException(mojoExecution, project, wrapper); } catch (ClassCastException e) { @@ -191,7 +191,7 @@ public void executeMojo(MavenSession session, MojoExecution mojoExecution) PrintStream ps = new PrintStream(os); ps.println("A type incompatibility occurred while executing " + mojoDescriptor.getId() + ": " + e.getMessage()); - pluginRealm.display(ps); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm).display(ps); throw new PluginExecutionException(mojoExecution, project, os.toString(), e); } catch (RuntimeException e) { mojoExecutionListener.afterExecutionFailure( diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java index ce3cb5135f3e..bd6f5ea744b6 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java @@ -28,11 +28,11 @@ import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; +import org.apache.maven.api.classworlds.ClassRealm; +import org.apache.maven.api.classworlds.NoSuchRealmException; import org.apache.maven.artifact.Artifact; import org.apache.maven.project.ExtensionDescriptor; import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.classworlds.realm.ClassRealm; -import org.codehaus.plexus.classworlds.realm.NoSuchRealmException; import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable; /** diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginRealmCache.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginRealmCache.java index 1e822a2ccb0c..badce69522b9 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginRealmCache.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginRealmCache.java @@ -29,11 +29,11 @@ import java.util.concurrent.ConcurrentHashMap; import org.apache.maven.RepositoryUtils; +import org.apache.maven.api.classworlds.ClassRealm; +import org.apache.maven.api.classworlds.NoSuchRealmException; import org.apache.maven.artifact.Artifact; import org.apache.maven.model.Plugin; import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.classworlds.realm.ClassRealm; -import org.codehaus.plexus.classworlds.realm.NoSuchRealmException; import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.graph.DependencyFilter; diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/ExtensionRealmCache.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/ExtensionRealmCache.java index 22bb57ed2d1b..6ba35ae771ac 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/ExtensionRealmCache.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/ExtensionRealmCache.java @@ -20,10 +20,10 @@ import java.util.List; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.artifact.Artifact; import org.apache.maven.project.ExtensionDescriptor; import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.classworlds.realm.ClassRealm; /** * Caches extension class realms. Warning: This is an internal utility interface that is only public diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginContainerException.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginContainerException.java index 55ac337cddb2..63eed33efadc 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginContainerException.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginContainerException.java @@ -18,9 +18,9 @@ */ package org.apache.maven.plugin; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.model.Plugin; import org.apache.maven.plugin.descriptor.MojoDescriptor; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.codehaus.plexus.component.repository.exception.ComponentLookupException; import org.codehaus.plexus.component.repository.exception.ComponentRepositoryException; import org.codehaus.plexus.configuration.PlexusConfigurationException; diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginManagerException.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginManagerException.java index f3fd390fc0de..4b47a6fd5c66 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginManagerException.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginManagerException.java @@ -18,13 +18,13 @@ */ package org.apache.maven.plugin; +import org.apache.maven.api.classworlds.NoSuchRealmException; import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; import org.apache.maven.model.Plugin; import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.plugin.descriptor.PluginDescriptor; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.PlexusContainerException; -import org.codehaus.plexus.classworlds.realm.NoSuchRealmException; import org.codehaus.plexus.component.repository.exception.ComponentRepositoryException; import org.codehaus.plexus.configuration.PlexusConfigurationException; diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginRealmCache.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginRealmCache.java index 7fcbd87949a6..6fb5535e4a1a 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginRealmCache.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginRealmCache.java @@ -21,10 +21,10 @@ import java.util.List; import java.util.Map; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.artifact.Artifact; import org.apache.maven.model.Plugin; import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.graph.DependencyFilter; import org.eclipse.aether.repository.RemoteRepository; diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java index 15f3d8df7b64..575bb51fdfe7 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java @@ -50,6 +50,7 @@ import org.apache.maven.api.Project; import org.apache.maven.api.Service; import org.apache.maven.api.Session; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.api.plugin.descriptor.Resolution; import org.apache.maven.api.services.DependencyResolver; import org.apache.maven.api.services.DependencyResolverResult; @@ -105,7 +106,6 @@ import org.apache.maven.session.scope.internal.SessionScopeModule; import org.codehaus.plexus.DefaultPlexusContainer; import org.codehaus.plexus.PlexusContainer; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.codehaus.plexus.component.composition.CycleDetectedInComponentGraphException; import org.codehaus.plexus.component.configurator.ComponentConfigurationException; import org.codehaus.plexus.component.configurator.ComponentConfigurator; @@ -357,10 +357,10 @@ public void setupPluginRealm( List pluginArtifacts = extensionRecord.getArtifacts(); for (ComponentDescriptor componentDescriptor : pluginDescriptor.getComponents()) { - componentDescriptor.setRealm(pluginRealm); + componentDescriptor.setRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm); } - pluginDescriptor.setClassRealm(pluginRealm); + pluginDescriptor.setClassRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm); pluginDescriptor.setArtifacts(pluginArtifacts); } else { boolean v4api = pluginDescriptor.getMojos().stream().anyMatch(MojoDescriptor::isV4Api); @@ -381,10 +381,10 @@ public void setupPluginRealm( pluginDescriptor.getClassRealm(), pluginDescriptor.getArtifacts()); }); - pluginDescriptor.setClassRealm(cacheRecord.getRealm()); + pluginDescriptor.setClassRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) cacheRecord.getRealm()); pluginDescriptor.setArtifacts(new ArrayList<>(cacheRecord.getArtifacts())); for (ComponentDescriptor componentDescriptor : pluginDescriptor.getComponents()) { - componentDescriptor.setRealm(cacheRecord.getRealm()); + componentDescriptor.setRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) cacheRecord.getRealm()); } pluginRealmCache.register(project, cacheKey, cacheRecord); @@ -427,7 +427,7 @@ private void createPluginRealm( discoverPluginComponents(pluginRealm, plugin, pluginDescriptor); pluginDescriptor.setDependencyNode(result.getRoot()); - pluginDescriptor.setClassRealm(pluginRealm); + pluginDescriptor.setClassRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm); pluginDescriptor.setArtifacts(pluginArtifacts); } @@ -439,16 +439,16 @@ private void discoverPluginComponents( if (pluginDescriptor != null) { for (MojoDescriptor mojo : pluginDescriptor.getMojos()) { if (!mojo.isV4Api()) { - mojo.setRealm(pluginRealm); + mojo.setRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm); container.addComponentDescriptor(mojo); } } } - Thread.currentThread().setContextClassLoader(pluginRealm); + Thread.currentThread().setContextClassLoader(pluginRealm.getClassLoader()); ((DefaultPlexusContainer) container) .discoverComponents( - pluginRealm, + (org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm, new SessionScopeModule(container.lookup(SessionScope.class)), new MojoExecutionScopeModule(container.lookup(MojoExecutionScope.class)), new PluginConfigurationModule(plugin.getDelegate()), @@ -479,12 +479,14 @@ private Map calcImports( MavenProject project, ClassLoader parent, List imports, boolean v4api) { Map foreignImports = new HashMap<>(); - ClassLoader projectRealm = project.getClassRealm(); + ClassRealm projectRealm = project.getClassRealm(); if (projectRealm != null) { - foreignImports.put("", projectRealm); + foreignImports.put("", projectRealm.getClassLoader()); } else { foreignImports.put( - "", v4api ? classRealmManager.getMaven4ApiRealm() : classRealmManager.getMavenApiRealm()); + "", + (v4api ? classRealmManager.getMaven4ApiRealm() : classRealmManager.getMavenApiRealm()) + .getClassLoader()); } if (parent != null && imports != null) { @@ -523,10 +525,11 @@ public T getConfiguredMojo(Class mojoInterface, MavenSession session, Moj // We are forcing the use of the plugin realm for all lookups that might occur during // the lifecycle that is part of the lookup. Here we are specifically trying to keep // lookups that occur in contextualize calls in line with the right realm. - ClassRealm oldLookupRealm = container.setLookupRealm(pluginRealm); + ClassRealm oldLookupRealm = + container.setLookupRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm); ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(pluginRealm); + Thread.currentThread().setContextClassLoader(pluginRealm.getClassLoader()); try { if (mojoDescriptor.isV4Api()) { @@ -536,7 +539,7 @@ public T getConfiguredMojo(Class mojoInterface, MavenSession session, Moj } } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); - container.setLookupRealm(oldLookupRealm); + container.setLookupRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) oldLookupRealm); } } @@ -558,7 +561,7 @@ private T loadV4Mojo( LoggerFactory.getLogger(mojoExecution.getMojoDescriptor().getFullGoalName())); try { Injector injector = Injector.create(); - injector.discover(pluginRealm); + injector.discover(pluginRealm.getClassLoader()); // Add known classes // TODO: get those from the existing plexus scopes ? injector.bindInstance(Session.class, sessionV4); @@ -703,7 +706,7 @@ private T loadV3Mojo( ps.println("Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '" + pluginDescriptor.getId() + "'. A required class is missing: " + cause.getMessage()); - pluginRealm.display(ps); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm).display(ps); throw new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), cause); } else if (cause instanceof LinkageError) { @@ -712,7 +715,7 @@ private T loadV3Mojo( ps.println("Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '" + pluginDescriptor.getId() + "' due to an API incompatibility: " + e.getClass().getName() + ": " + cause.getMessage()); - pluginRealm.display(ps); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm).display(ps); throw new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), cause); } @@ -814,7 +817,12 @@ private void populateMojoExecutionFields( + configuratorId + " configurator -->"); } - configurator.configureComponent(mojo, configuration, expressionEvaluator, pluginRealm, validator); + configurator.configureComponent( + mojo, + configuration, + expressionEvaluator, + (org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm, + validator); logger.debug("-- end configuration --"); @@ -850,7 +858,7 @@ private void populateMojoExecutionFields( PrintStream ps = new PrintStream(os); ps.println("A required class was missing during configuration of mojo " + mojoDescriptor.getId() + ": " + e.getMessage()); - pluginRealm.display(ps); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm).display(ps); throw new PluginConfigurationException(mojoDescriptor.getPluginDescriptor(), os.toString(), e); } catch (LinkageError e) { @@ -858,7 +866,7 @@ private void populateMojoExecutionFields( PrintStream ps = new PrintStream(os); ps.println("An API incompatibility was encountered during configuration of mojo " + mojoDescriptor.getId() + ": " + e.getClass().getName() + ": " + e.getMessage()); - pluginRealm.display(ps); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm).display(ps); throw new PluginConfigurationException(mojoDescriptor.getPluginDescriptor(), os.toString(), e); } finally { diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java index 6bde396a97e7..b0b64fbf83d2 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java +++ b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java @@ -33,6 +33,7 @@ import java.util.Set; import org.apache.maven.RepositoryUtils; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.api.xml.XmlNode; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.InvalidRepositoryException; @@ -49,7 +50,6 @@ import org.apache.maven.plugin.PluginManagerException; import org.apache.maven.plugin.PluginResolutionException; import org.apache.maven.plugin.version.PluginVersionResolutionException; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.codehaus.plexus.util.xml.Xpp3Dom; import org.eclipse.aether.graph.DependencyFilter; import org.eclipse.aether.util.filter.ExclusionsDependencyFilter; @@ -246,7 +246,7 @@ public synchronized ProjectRealmCache.CacheRecord createProjectRealm( } for (String export : exports) { - projectRealm.importFrom(extensionRealm, export); + projectRealm.importFrom(extensionRealm.getClassLoader(), export); } } @@ -265,13 +265,16 @@ record = projectRealmCache.put(projectRealmKey, projectRealm, extensionArtifactF @Override public void selectProjectRealm(MavenProject project) { - ClassLoader projectRealm = project.getClassRealm(); + ClassRealm projectRealm = project.getClassRealm(); + ClassLoader classLoader; if (projectRealm == null) { - projectRealm = classRealmManager.getCoreRealm(); + classLoader = classRealmManager.getCoreRealm().getClassLoader(); + } else { + classLoader = projectRealm.getClassLoader(); } - Thread.currentThread().setContextClassLoader(projectRealm); + Thread.currentThread().setContextClassLoader(classLoader); } private static boolean containsExpression(String value) { diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java index 9111177c3489..54099459cdf5 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java +++ b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java @@ -27,8 +27,8 @@ import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; -import org.codehaus.plexus.classworlds.realm.ClassRealm; -import org.codehaus.plexus.classworlds.realm.NoSuchRealmException; +import org.apache.maven.api.classworlds.ClassRealm; +import org.apache.maven.api.classworlds.NoSuchRealmException; import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable; import org.eclipse.aether.graph.DependencyFilter; diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/MavenProject.java b/impl/maven-core/src/main/java/org/apache/maven/project/MavenProject.java index e6c2c05acbf0..ecdbf2b0eec1 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/project/MavenProject.java +++ b/impl/maven-core/src/main/java/org/apache/maven/project/MavenProject.java @@ -43,6 +43,7 @@ import org.apache.maven.api.ProjectScope; import org.apache.maven.api.SourceRoot; import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.ArtifactUtils; import org.apache.maven.artifact.DependencyResolutionRequiredException; @@ -79,7 +80,6 @@ import org.apache.maven.model.io.xpp3.MavenXpp3Writer; import org.apache.maven.model.root.RootLocator; import org.apache.maven.project.artifact.InvalidDependencyVersionException; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.codehaus.plexus.util.xml.Xpp3Dom; import org.eclipse.aether.graph.DependencyFilter; import org.eclipse.aether.repository.RemoteRepository; diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectRealmCache.java b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectRealmCache.java index dd07b49842d1..05c204e1475a 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectRealmCache.java +++ b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectRealmCache.java @@ -20,7 +20,7 @@ import java.util.List; -import org.codehaus.plexus.classworlds.realm.ClassRealm; +import org.apache.maven.api.classworlds.ClassRealm; import org.eclipse.aether.graph.DependencyFilter; /** diff --git a/impl/maven-core/src/main/resources/META-INF/maven/extension.xml b/impl/maven-core/src/main/resources/META-INF/maven/extension.xml index 56667ed69e35..570fece8f4c8 100644 --- a/impl/maven-core/src/main/resources/META-INF/maven/extension.xml +++ b/impl/maven-core/src/main/resources/META-INF/maven/extension.xml @@ -77,12 +77,9 @@ under the License. org.eclipse.aether.version org.eclipse.aether.util - + org.codehaus.plexus.classworlds - - org.codehaus.classworlds - org.codehaus.plexus.util.xml.Xpp3Dom org.codehaus.plexus.util.xml.Xpp3DomBuilder @@ -154,7 +151,7 @@ under the License. org.apache.maven:maven-api-xml classworlds:classworlds - org.codehaus.plexus:plexus-classworlds + org.apache.maven:maven-classworlds org.codehaus.plexus:plexus-component-api org.codehaus.plexus:plexus-container-default plexus:plexus-container-default diff --git a/impl/maven-core/src/test/java/org/apache/maven/classrealm/DefaultClassRealmManagerTest.java b/impl/maven-core/src/test/java/org/apache/maven/classrealm/DefaultClassRealmManagerTest.java index 411a483b224a..08862fad9635 100644 --- a/impl/maven-core/src/test/java/org/apache/maven/classrealm/DefaultClassRealmManagerTest.java +++ b/impl/maven-core/src/test/java/org/apache/maven/classrealm/DefaultClassRealmManagerTest.java @@ -23,13 +23,13 @@ import java.util.HashSet; import java.util.List; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.extension.internal.CoreExports; import org.apache.maven.internal.impl.internal.DefaultCoreRealm; import org.apache.maven.model.Model; import org.codehaus.plexus.DefaultPlexusContainer; import org.codehaus.plexus.PlexusContainer; import org.codehaus.plexus.PlexusContainerException; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.eclipse.aether.artifact.Artifact; import org.junit.jupiter.api.Test; import org.mockito.InOrder; @@ -58,7 +58,11 @@ private DefaultClassRealmManager newDefaultClassRealmManager(PlexusContainer con return new DefaultClassRealmManager( new DefaultCoreRealm(container), new ArrayList(), - new CoreExports(new ClassRealm(null, "test", null), new HashSet(), exportedPackages)); + new CoreExports( + new org.codehaus.plexus.classworlds.realm.ClassRealm( + new org.codehaus.plexus.classworlds.ClassWorld(), "test", null), + new HashSet(), + exportedPackages)); } private List newTestArtifactList() { diff --git a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/BuildPluginManagerStub.java b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/BuildPluginManagerStub.java index 9566f569d1c7..6fb7c3617757 100644 --- a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/BuildPluginManagerStub.java +++ b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/BuildPluginManagerStub.java @@ -20,13 +20,13 @@ import java.util.List; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Plugin; import org.apache.maven.plugin.BuildPluginManager; import org.apache.maven.plugin.MojoExecution; import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.plugin.descriptor.PluginDescriptor; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.repository.RemoteRepository; diff --git a/impl/maven-core/src/test/java/org/apache/maven/plugin/PluginManagerTest.java b/impl/maven-core/src/test/java/org/apache/maven/plugin/PluginManagerTest.java index f1621347257f..4290108a4dd8 100644 --- a/impl/maven-core/src/test/java/org/apache/maven/plugin/PluginManagerTest.java +++ b/impl/maven-core/src/test/java/org/apache/maven/plugin/PluginManagerTest.java @@ -23,6 +23,7 @@ import java.util.List; import org.apache.maven.AbstractCoreMavenComponentTestCase; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.repository.DefaultRepositoryRequest; import org.apache.maven.artifact.repository.RepositoryRequest; @@ -31,7 +32,6 @@ import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.plugin.descriptor.PluginDescriptor; import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.codehaus.plexus.component.repository.ComponentDescriptor; import org.junit.jupiter.api.Test; diff --git a/impl/pom.xml b/impl/pom.xml index b41717c555ea..d05a9ce2879f 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -31,6 +31,7 @@ under the License. Maven 4 Implementation Modules + maven-classworlds maven-support maven-impl maven-di diff --git a/its/core-it-suite/src/test/resources/mng-5530-mojo-execution-scope/extension/src/main/java/org/apache/maven/its/mng5530/mojoexecutionscope/extension/TestClassRealmManagerDelegate.java b/its/core-it-suite/src/test/resources/mng-5530-mojo-execution-scope/extension/src/main/java/org/apache/maven/its/mng5530/mojoexecutionscope/extension/TestClassRealmManagerDelegate.java index 6c064d273132..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 d83ddb44cb59..66394cea20e7 100644 --- a/pom.xml +++ b/pom.xml @@ -144,7 +144,6 @@ under the License. 9.10.1 1.18.10 - 2.12.0 1.11.0 1.5.2 5.1.0 @@ -238,6 +237,11 @@ under the License. maven-api-annotations ${project.version} + + org.apache.maven + maven-api-classworlds + ${project.version} + org.apache.maven maven-api-model @@ -421,9 +425,9 @@ under the License. 1 - org.codehaus.plexus - plexus-classworlds - ${classWorldsVersion} + org.apache.maven + maven-classworlds + ${project.version} org.codehaus.plexus From c5ab33fc427284cdc187bac7c7a248033cace60c Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 28 Aug 2025 11:45:16 +0200 Subject: [PATCH 02/13] Some fixes --- apache-maven/src/assembly/component.xml | 2 ++ .../configuration/internal/EnhancedComponentConfigurator.java | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apache-maven/src/assembly/component.xml b/apache-maven/src/assembly/component.xml index 3077fd28edbf..f964b5d59bc8 100644 --- a/apache-maven/src/assembly/component.xml +++ b/apache-maven/src/assembly/component.xml @@ -23,6 +23,7 @@ under the License. false boot + org.apache.maven:maven-api-classworlds org.apache.maven:maven-classworlds @@ -31,6 +32,7 @@ under the License. false lib + org.apache.maven:maven-api-classworlds org.apache.maven:maven-classworlds diff --git a/impl/maven-core/src/main/java/org/apache/maven/configuration/internal/EnhancedComponentConfigurator.java b/impl/maven-core/src/main/java/org/apache/maven/configuration/internal/EnhancedComponentConfigurator.java index 9ce045d64048..8b779726f617 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/configuration/internal/EnhancedComponentConfigurator.java +++ b/impl/maven-core/src/main/java/org/apache/maven/configuration/internal/EnhancedComponentConfigurator.java @@ -52,7 +52,8 @@ public void configureComponent( try { ClassRealmConverter.pushContextRealm(realm); - this.configureComponent(component, configuration, evaluator, realm, listener); + this.configureComponent( + component, configuration, evaluator, realm != null ? realm.getClassLoader() : null, listener); } finally { ClassRealmConverter.popContextRealm(); } From 03a7ac4e72d533c0889eb411087c392f93ccc571 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Wed, 24 Jun 2026 11:00:28 +0200 Subject: [PATCH 03/13] Fully eliminate plexus-classworlds from the dependency tree - Exclude plexus-classworlds transitive dependency from Sisu and plexus-testing in root pom dependencyManagement - Add maven-classworlds test dependency to maven-resolver-provider (needed by plexus-testing at runtime) - Copy legacy org.codehaus.classworlds adapter classes into maven-classworlds (required by Sisu's ClassRealmConverter and AbstractComponentConfigurator) - Add checkstyle exclusion for legacy package and RAT exclusion for test data Co-Authored-By: Claude Opus 4.6 --- compat/maven-resolver-provider/pom.xml | 5 + impl/maven-classworlds/pom.xml | 7 + .../classworlds/BytesURLConnection.java | 68 +++++++ .../classworlds/BytesURLStreamHandler.java | 65 +++++++ .../org/codehaus/classworlds/ClassRealm.java | 128 ++++++++++++++ .../classworlds/ClassRealmAdapter.java | 166 ++++++++++++++++++ .../classworlds/ClassRealmReverseAdapter.java | 142 +++++++++++++++ .../org/codehaus/classworlds/ClassWorld.java | 85 +++++++++ .../classworlds/ClassWorldAdapter.java | 105 +++++++++++ .../classworlds/ClassWorldException.java | 122 +++++++++++++ .../classworlds/ClassWorldReverseAdapter.java | 117 ++++++++++++ .../classworlds/ConfigurationException.java | 96 ++++++++++ .../codehaus/classworlds/Configurator.java | 151 ++++++++++++++++ .../classworlds/ConfiguratorAdapter.java | 92 ++++++++++ .../classworlds/DefaultClassRealm.java | 162 +++++++++++++++++ .../classworlds/DuplicateRealmException.java | 114 ++++++++++++ .../org/codehaus/classworlds/Launcher.java | 76 ++++++++ .../classworlds/NoSuchRealmException.java | 114 ++++++++++++ .../codehaus/classworlds/package-info.java | 37 ++++ .../realm/FilteredClassRealmTest.java | 6 +- pom.xml | 8 + 21 files changed, 1864 insertions(+), 2 deletions(-) create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/classworlds/BytesURLConnection.java create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/classworlds/BytesURLStreamHandler.java create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassRealm.java create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassRealmAdapter.java create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassRealmReverseAdapter.java create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorld.java create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorldAdapter.java create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorldException.java create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorldReverseAdapter.java create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ConfigurationException.java create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/classworlds/Configurator.java create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ConfiguratorAdapter.java create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/classworlds/DefaultClassRealm.java create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/classworlds/DuplicateRealmException.java create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/classworlds/Launcher.java create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/classworlds/NoSuchRealmException.java create mode 100644 impl/maven-classworlds/src/main/java/org/codehaus/classworlds/package-info.java diff --git a/compat/maven-resolver-provider/pom.xml b/compat/maven-resolver-provider/pom.xml index 79c7507becd2..7130c64d418e 100644 --- a/compat/maven-resolver-provider/pom.xml +++ b/compat/maven-resolver-provider/pom.xml @@ -151,6 +151,11 @@ under the License. plexus-testing test + + org.apache.maven + maven-classworlds + test + org.mockito mockito-core diff --git a/impl/maven-classworlds/pom.xml b/impl/maven-classworlds/pom.xml index 5d141de70bb2..ccf4a168668c 100644 --- a/impl/maven-classworlds/pom.xml +++ b/impl/maven-classworlds/pom.xml @@ -58,6 +58,13 @@ under the License. + + org.apache.maven.plugins + maven-checkstyle-plugin + + org/codehaus/classworlds/** + + org.apache.maven.plugins maven-jar-plugin diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/BytesURLConnection.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/BytesURLConnection.java new file mode 100644 index 000000000000..656e77cb09dd --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/BytesURLConnection.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.classworlds; + +/* + * Copyright 2001-2010 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; + +/** + * URLConnection implementation for byte arrays. + * + *

Note: This is a legacy class provided for backward compatibility with Maven 2. + * New code should avoid using this internal implementation detail.

+ * + * @deprecated This is a legacy internal class. + */ +@Deprecated +public class BytesURLConnection extends URLConnection { + protected final byte[] content; + + protected int offset; + + protected int length; + + public BytesURLConnection(URL url, byte[] content) { + super(url); + this.content = content; + } + + public void connect() {} + + public InputStream getInputStream() { + return new ByteArrayInputStream(content); + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/BytesURLStreamHandler.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/BytesURLStreamHandler.java new file mode 100644 index 000000000000..182c57277452 --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/BytesURLStreamHandler.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.classworlds; + +/* + * Copyright 2001-2010 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; + +/** + * URLStreamHandler implementation for byte arrays. + * + *

Note: This is a legacy class provided for backward compatibility with Maven 2. + * New code should avoid using this internal implementation detail.

+ * + * @author Hani Suleiman (hani@formicary.net) Date: Oct 20, 2003 12:45:18 AM + * @deprecated This is a legacy internal class. + */ +@Deprecated +public class BytesURLStreamHandler extends URLStreamHandler { + final byte[] content; + + int offset; + + int length; + + public BytesURLStreamHandler(byte[] content) { + this.content = content; + } + + public URLConnection openConnection(URL url) { + return new BytesURLConnection(url, content); + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassRealm.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassRealm.java new file mode 100644 index 000000000000..0469a480d33e --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassRealm.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.classworlds; + +/* + +Copyright 2002 (C) The Werken Company. All Rights Reserved. + +Redistribution and use of this software and associated documentation +("Software"), with or without modification, are permitted provided +that the following conditions are met: + +1. Redistributions of source code must retain copyright + statements and notices. Redistributions must also contain a + copy of this document. + +2. Redistributions in binary form must reproduce the + above copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +3. The name "classworlds" must not be used to endorse or promote + products derived from this Software without prior written + permission of The Werken Company. For written permission, + please contact bob@werken.com. + +4. Products derived from this Software may not be called "classworlds" + nor may "classworlds" appear in their names without prior written + permission of The Werken Company. "classworlds" is a registered + trademark of The Werken Company. + +5. Due credit should be given to The Werken Company. + (http://classworlds.werken.com/). + +THIS SOFTWARE IS PROVIDED BY THE WERKEN COMPANY AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +THE WERKEN COMPANY OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; + +/** + *

Autonomous sub-portion of a ClassWorld.

+ * + *

This class most closed maps to the ClassLoader + * role from Java and in facts can provide a ClassLoader + * view of itself using {@link #getClassLoader}.

+ * + *

This is a legacy interface from Classworlds 1.x, preserved for binary compatibility. + * The compiled bytecode of {@code org.eclipse.sisu:org.eclipse.sisu.plexus} references this + * interface directly. Removing it breaks all Maven 3+ builds. + * New code should use {@link org.codehaus.plexus.classworlds.realm.ClassRealm}.

+ * + * @author bob mcwhirter + * @author Jason van Zyl + * @deprecated Use {@link org.codehaus.plexus.classworlds.realm.ClassRealm} for new code. + * This interface must remain on the classpath for Sisu compatibility. + */ +@SuppressWarnings("rawtypes") +@Deprecated +public interface ClassRealm { + String getId(); + + ClassWorld getWorld(); + + void importFrom(String realmId, String pkgName) throws NoSuchRealmException; + + void addConstituent(URL constituent); + + ClassRealm locateSourceRealm(String className); + + void setParent(ClassRealm classRealm); + + ClassRealm createChildRealm(String id) throws DuplicateRealmException; + + ClassLoader getClassLoader(); + + ClassRealm getParent(); + + URL[] getConstituents(); + + // ---------------------------------------------------------------------- + // Classloading + // ---------------------------------------------------------------------- + + Class loadClass(String name) throws ClassNotFoundException; + + // ---------------------------------------------------------------------- + // Resource handling + // ---------------------------------------------------------------------- + + URL getResource(String name); + + Enumeration findResources(String name) throws IOException; + + InputStream getResourceAsStream(String name); + + void display(); +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassRealmAdapter.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassRealmAdapter.java new file mode 100644 index 000000000000..736bc4f4332c --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassRealmAdapter.java @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.classworlds; + +/* + * Copyright 2001-2010 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; + +/** + * Adapter that wraps a modern {@link org.codehaus.plexus.classworlds.realm.ClassRealm} and exposes + * it as the legacy {@link ClassRealm} interface. + * + *

This class is referenced directly by the compiled bytecode of + * {@code org.eclipse.sisu:org.eclipse.sisu.plexus} and must remain on the classpath for + * Maven 3+ to function. Do not remove or rename it. New code should not use this adapter; + * use {@link org.codehaus.plexus.classworlds.realm.ClassRealm} directly.

+ * + * @author Andrew Williams + * @deprecated Legacy adapter retained for Sisu binary compatibility. + */ +@SuppressWarnings({"UnnecessaryLocalVariable", "DeprecatedIsStillUsed", "rawtypes"}) +@Deprecated +public class ClassRealmAdapter implements ClassRealm { + + public static ClassRealmAdapter getInstance(org.codehaus.plexus.classworlds.realm.ClassRealm newRealm) { + ClassRealmAdapter adapter = new ClassRealmAdapter(newRealm); + + return adapter; + } + + private final org.codehaus.plexus.classworlds.realm.ClassRealm realm; + + private ClassRealmAdapter(org.codehaus.plexus.classworlds.realm.ClassRealm newRealm) { + this.realm = newRealm; + } + + public String getId() { + return realm.getId(); + } + + public ClassWorld getWorld() { + return ClassWorldAdapter.getInstance(realm.getWorld()); + } + + public void importFrom(String realmId, String pkgName) throws NoSuchRealmException { + try { + realm.importFrom(realmId, pkgName); + } catch (org.codehaus.plexus.classworlds.realm.NoSuchRealmException e) { + throw new NoSuchRealmException(getWorld(), e.getId()); + } + } + + public void addConstituent(URL constituent) { + realm.addURL(constituent); + } + + public ClassRealm locateSourceRealm(String className) { + ClassLoader importLoader = realm.getImportClassLoader(className); + + if (importLoader instanceof org.codehaus.plexus.classworlds.realm.ClassRealm) { + return ClassRealmAdapter.getInstance((org.codehaus.plexus.classworlds.realm.ClassRealm) importLoader); + } else { + return null; + } + } + + public void setParent(ClassRealm classRealm) { + if (classRealm != null) { + realm.setParentRealm(ClassRealmReverseAdapter.getInstance(classRealm)); + } + } + + public ClassRealm createChildRealm(String id) throws DuplicateRealmException { + try { + return ClassRealmAdapter.getInstance(realm.createChildRealm(id)); + } catch (org.codehaus.plexus.classworlds.realm.DuplicateRealmException e) { + throw new DuplicateRealmException(getWorld(), e.getId()); + } + } + + public ClassLoader getClassLoader() { + return realm; + } + + public ClassRealm getParent() { + return ClassRealmAdapter.getInstance(realm.getParentRealm()); + } + + public ClassRealm getParentRealm() { + return ClassRealmAdapter.getInstance(realm.getParentRealm()); + } + + public URL[] getConstituents() { + return realm.getURLs(); + } + + public Class loadClass(String name) throws ClassNotFoundException { + return realm.loadClass(name); + } + + public URL getResource(String name) { + return realm.getResource(trimLeadingSlash(name)); + } + + public Enumeration findResources(String name) throws IOException { + return realm.findResources(trimLeadingSlash(name)); + } + + public InputStream getResourceAsStream(String name) { + return realm.getResourceAsStream(trimLeadingSlash(name)); + } + + public void display() { + realm.display(); + } + + public boolean equals(Object o) { + if (!(o instanceof ClassRealm)) return false; + + return getId().equals(((ClassRealm) o).getId()); + } + + /** + * Provides backward-compat with the old classworlds which accepted resource names with leading slashes. + */ + private String trimLeadingSlash(String resource) { + if (resource != null && resource.startsWith("/")) { + return resource.substring(1); + } else { + return resource; + } + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassRealmReverseAdapter.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassRealmReverseAdapter.java new file mode 100644 index 000000000000..6019e2dbbe3f --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassRealmReverseAdapter.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.classworlds; + +/* + * Copyright 2001-2010 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; + +/** + * A reverse adapter for ClassRealms. + * + *

Note: This is a legacy internal class provided for backward compatibility with Maven 2. + * New code should avoid using this adapter.

+ * + * @author Andrew Williams + * @deprecated This is a legacy internal class. + */ +@SuppressWarnings({"unchecked", "rawtypes"}) +@Deprecated +public class ClassRealmReverseAdapter extends org.codehaus.plexus.classworlds.realm.ClassRealm { + + public static ClassRealmReverseAdapter getInstance(ClassRealm oldRealm) { + return new ClassRealmReverseAdapter(oldRealm); + } + + private final ClassRealm realm; + + private ClassRealmReverseAdapter(ClassRealm oldRealm) { + super(ClassWorldReverseAdapter.getInstance(oldRealm.getWorld()), oldRealm.getId(), oldRealm.getClassLoader()); + this.realm = oldRealm; + } + + public String getId() { + return realm.getId(); + } + + public org.codehaus.plexus.classworlds.ClassWorld getWorld() { + return ClassWorldReverseAdapter.getInstance(realm.getWorld()); + } + + public void importFrom(String realmId, String pkgName) + throws org.codehaus.plexus.classworlds.realm.NoSuchRealmException { + try { + realm.importFrom(realmId, pkgName); + } catch (NoSuchRealmException e) { + throw new org.codehaus.plexus.classworlds.realm.NoSuchRealmException(getWorld(), e.getId()); + } + } + + public void addURL(URL constituent) { + realm.addConstituent(constituent); + } + + public org.codehaus.plexus.classworlds.realm.ClassRealm locateSourceRealm(String className) { + return getInstance(realm.locateSourceRealm(className)); + } + + public void setParentRealm(org.codehaus.plexus.classworlds.realm.ClassRealm classRealm) { + realm.setParent(ClassRealmAdapter.getInstance(classRealm)); + } + + public org.codehaus.plexus.classworlds.realm.ClassRealm createChildRealm(String id) + throws org.codehaus.plexus.classworlds.realm.DuplicateRealmException { + try { + return getInstance(realm.createChildRealm(id)); + } catch (DuplicateRealmException e) { + throw new org.codehaus.plexus.classworlds.realm.DuplicateRealmException(getWorld(), e.getId()); + } + } + + public ClassLoader getClassLoader() { + return realm.getClassLoader(); + } + + public org.codehaus.plexus.classworlds.realm.ClassRealm getParentRealm() { + return getInstance(realm.getParent()); + } + + public URL[] getURLs() { + return realm.getConstituents(); + } + + public Class loadClass(String name) throws ClassNotFoundException { + return realm.loadClass(name); + } + + public URL getResource(String name) { + return realm.getResource(name); + } + + @SuppressWarnings("unchecked") + public Enumeration findResources(String name) throws IOException { + return realm.findResources(name); + } + + public InputStream getResourceAsStream(String name) { + return realm.getResourceAsStream(name); + } + + public void display() { + realm.display(); + } + + public boolean equals(Object o) { + if (!(o instanceof ClassRealm)) return false; + + return getId().equals(((ClassRealm) o).getId()); + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorld.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorld.java new file mode 100644 index 000000000000..cdc01957987b --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorld.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.classworlds; + +/* + * Copyright 2001-2010 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.Collection; + +/** + * A compatibility wrapper for {@link org.codehaus.plexus.classworlds.ClassWorld} + * provided for legacy code. + * + *

Note: This is a legacy class provided for backward compatibility with Maven 2. + * New code should use {@link org.codehaus.plexus.classworlds.ClassWorld}.

+ * + * @author Andrew Williams + * @deprecated Use {@link org.codehaus.plexus.classworlds.ClassWorld} + */ +@SuppressWarnings("rawtypes") +@Deprecated +public class ClassWorld { + private ClassWorldAdapter adapter; + + public ClassWorld(String realmId, ClassLoader classLoader) { + adapter = ClassWorldAdapter.getInstance(new org.codehaus.plexus.classworlds.ClassWorld(realmId, classLoader)); + } + + public ClassWorld() { + adapter = ClassWorldAdapter.getInstance(new org.codehaus.plexus.classworlds.ClassWorld()); + } + + public ClassWorld(boolean ignore) { + /* fake */ + } + + public ClassRealm newRealm(String id) throws DuplicateRealmException { + return adapter.newRealm(id); + } + + public ClassRealm newRealm(String id, ClassLoader classLoader) throws DuplicateRealmException { + return adapter.newRealm(id, classLoader); + } + + public void disposeRealm(String id) throws NoSuchRealmException { + adapter.disposeRealm(id); + } + + public ClassRealm getRealm(String id) throws NoSuchRealmException { + return adapter.getRealm(id); + } + + public Collection getRealms() { + return adapter.getRealms(); + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorldAdapter.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorldAdapter.java new file mode 100644 index 000000000000..61bb50a37aae --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorldAdapter.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.classworlds; + +/* + * Copyright 2001-2010 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.Collection; +import java.util.Vector; + +/** + * An adapter for ClassWorlds. + * + *

Note: This is a legacy internal class provided for backward compatibility with Maven 2. + * New code should avoid using this adapter.

+ * + * @author Andrew Williams + * @deprecated This is a legacy internal class. + */ +@SuppressWarnings("rawtypes") +@Deprecated +public class ClassWorldAdapter extends ClassWorld { + + public static ClassWorldAdapter getInstance(org.codehaus.plexus.classworlds.ClassWorld newWorld) { + return new ClassWorldAdapter(newWorld); + } + + private final org.codehaus.plexus.classworlds.ClassWorld world; + + private ClassWorldAdapter(org.codehaus.plexus.classworlds.ClassWorld newWorld) { + super(false); + this.world = newWorld; + } + + public ClassRealm newRealm(String id) throws DuplicateRealmException { + try { + return ClassRealmAdapter.getInstance(world.newRealm(id)); + } catch (org.codehaus.plexus.classworlds.realm.DuplicateRealmException e) { + throw new DuplicateRealmException(this, e.getId()); + } + } + + public ClassRealm newRealm(String id, ClassLoader classLoader) throws DuplicateRealmException { + try { + return ClassRealmAdapter.getInstance(world.newRealm(id, classLoader)); + } catch (org.codehaus.plexus.classworlds.realm.DuplicateRealmException e) { + throw new DuplicateRealmException(this, e.getId()); + } + } + + public void disposeRealm(String id) throws NoSuchRealmException { + try { + world.disposeRealm(id); + } catch (org.codehaus.plexus.classworlds.realm.NoSuchRealmException e) { + throw new NoSuchRealmException(this, e.getId()); + } + } + + public ClassRealm getRealm(String id) throws NoSuchRealmException { + try { + return ClassRealmAdapter.getInstance(world.getRealm(id)); + } catch (org.codehaus.plexus.classworlds.realm.NoSuchRealmException e) { + throw new NoSuchRealmException(this, e.getId()); + } + } + + public Collection getRealms() { + Collection realms = world.getRealms(); + Vector ret = new Vector<>(); + for (org.codehaus.plexus.classworlds.realm.ClassRealm classRealm : realms) { + ret.add(ClassRealmAdapter.getInstance(classRealm)); + } + + return ret; + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorldException.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorldException.java new file mode 100644 index 000000000000..76f51e3e7a4d --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorldException.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.classworlds; + +/* + +Copyright 2002 (C) The Werken Company. All Rights Reserved. + +Redistribution and use of this software and associated documentation +("Software"), with or without modification, are permitted provided +that the following conditions are met: + +1. Redistributions of source code must retain copyright + statements and notices. Redistributions must also contain a + copy of this document. + +2. Redistributions in binary form must reproduce the + above copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +3. The name "classworlds" must not be used to endorse or promote + products derived from this Software without prior written + permission of The Werken Company. For written permission, + please contact bob@werken.com. + +4. Products derived from this Software may not be called "classworlds" + nor may "classworlds" appear in their names without prior written + permission of The Werken Company. "classworlds" is a registered + trademark of The Werken Company. + +5. Due credit should be given to The Werken Company. + (http://classworlds.werken.com/). + +THIS SOFTWARE IS PROVIDED BY THE WERKEN COMPANY AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +THE WERKEN COMPANY OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +/** + * Base exception for ClassWorld errors. + * + *

Note: This is a legacy exception provided for backward compatibility with Maven 2. + * New code should use {@link org.codehaus.plexus.classworlds.ClassWorldException}.

+ * + * @author bob mcwhirter + * @deprecated Use {@link org.codehaus.plexus.classworlds.ClassWorldException} + */ +@Deprecated +public class ClassWorldException extends Exception { + // ------------------------------------------------------------ + // Instance members + // ------------------------------------------------------------ + + /** + * The world. + */ + private final ClassWorld world; + + // ------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------ + + /** + * Construct. + * + * @param world The world. + */ + public ClassWorldException(final ClassWorld world) { + this.world = world; + } + + /** + * Construct. + * + * @param world The world. + * @param msg The detail message. + */ + public ClassWorldException(final ClassWorld world, final String msg) { + super(msg); + this.world = world; + } + + // ------------------------------------------------------------ + // Instance methods + // ------------------------------------------------------------ + + /** + * Retrieve the world. + * + * @return The world. + */ + public ClassWorld getWorld() { + return this.world; + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorldReverseAdapter.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorldReverseAdapter.java new file mode 100644 index 000000000000..8a9543a68b9f --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ClassWorldReverseAdapter.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.classworlds; + +/* + * Copyright 2001-2010 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.Collection; +import java.util.HashMap; +import java.util.Vector; + +/** + * A reverse adapter for ClassWorlds. + * + *

Note: This is a legacy internal class provided for backward compatibility with Maven 2. + * New code should avoid using this adapter.

+ * + * @author Andrew Williams + * @deprecated This is a legacy internal class. + */ +@SuppressWarnings({"unchecked", "rawtypes"}) +@Deprecated +public class ClassWorldReverseAdapter extends org.codehaus.plexus.classworlds.ClassWorld { + private static final HashMap instances = new HashMap(); + + public static ClassWorldReverseAdapter getInstance(ClassWorld oldWorld) { + if (instances.containsKey(oldWorld)) return (ClassWorldReverseAdapter) instances.get(oldWorld); + + ClassWorldReverseAdapter adapter = new ClassWorldReverseAdapter(oldWorld); + instances.put(oldWorld, adapter); + + return adapter; + } + + private final ClassWorld world; + + private ClassWorldReverseAdapter(ClassWorld newWorld) { + super(); + this.world = newWorld; + } + + public org.codehaus.plexus.classworlds.realm.ClassRealm newRealm(String id) + throws org.codehaus.plexus.classworlds.realm.DuplicateRealmException { + try { + return ClassRealmReverseAdapter.getInstance(world.newRealm(id)); + } catch (DuplicateRealmException e) { + throw new org.codehaus.plexus.classworlds.realm.DuplicateRealmException(this, e.getId()); + } + } + + public org.codehaus.plexus.classworlds.realm.ClassRealm newRealm(String id, ClassLoader classLoader) + throws org.codehaus.plexus.classworlds.realm.DuplicateRealmException { + try { + return ClassRealmReverseAdapter.getInstance(world.newRealm(id, classLoader)); + } catch (DuplicateRealmException e) { + throw new org.codehaus.plexus.classworlds.realm.DuplicateRealmException(this, e.getId()); + } + } + + public void disposeRealm(String id) throws org.codehaus.plexus.classworlds.realm.NoSuchRealmException { + try { + world.disposeRealm(id); + } catch (NoSuchRealmException e) { + throw new org.codehaus.plexus.classworlds.realm.NoSuchRealmException(this, e.getId()); + } + } + + public org.codehaus.plexus.classworlds.realm.ClassRealm getRealm(String id) + throws org.codehaus.plexus.classworlds.realm.NoSuchRealmException { + try { + return ClassRealmReverseAdapter.getInstance(world.getRealm(id)); + } catch (NoSuchRealmException e) { + throw new org.codehaus.plexus.classworlds.realm.NoSuchRealmException(this, e.getId()); + } + } + + public Collection getRealms() { + Collection realms = world.getRealms(); + Vector ret = new Vector(); + + for (Object o : realms) { + ClassRealm realm = (ClassRealm) o; + ret.add(ClassRealmReverseAdapter.getInstance(realm)); + } + + return ret; + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ConfigurationException.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ConfigurationException.java new file mode 100644 index 000000000000..97396ba66831 --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ConfigurationException.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.classworlds; + +/* + +Copyright 2002 (C) The Werken Company. All Rights Reserved. + +Redistribution and use of this software and associated documentation +("Software"), with or without modification, are permitted provided +that the following conditions are met: + +1. Redistributions of source code must retain copyright + statements and notices. Redistributions must also contain a + copy of this document. + +2. Redistributions in binary form must reproduce the + above copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +3. The name "classworlds" must not be used to endorse or promote + products derived from this Software without prior written + permission of The Werken Company. For written permission, + please contact bob@werken.com. + +4. Products derived from this Software may not be called "classworlds" + nor may "classworlds" appear in their names without prior written + permission of The Werken Company. "classworlds" is a registered + trademark of The Werken Company. + +5. Due credit should be given to The Werken Company. + (http://classworlds.werken.com/). + +THIS SOFTWARE IS PROVIDED BY THE WERKEN COMPANY AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +THE WERKEN COMPANY OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +/** + * Indicates an error during Launcher configuration. + * + *

Note: This is a legacy exception provided for backward compatibility with Maven 2. + * New code should use {@link org.codehaus.plexus.classworlds.launcher.ConfigurationException}.

+ * + * @author bob mcwhirter + * @deprecated Use {@link org.codehaus.plexus.classworlds.launcher.ConfigurationException} + */ +@Deprecated +public class ConfigurationException extends Exception { + /** + * Construct. + * + * @param msg The message. + */ + public ConfigurationException(String msg) { + super(msg); + } + + /** + * Construct. + * + * @param msg The message. + * @param lineNo The number of configuraton line where the problem occured. + * @param line The configuration line where the problem occured. + */ + public ConfigurationException(String msg, int lineNo, String line) { + super(msg + " (" + lineNo + "): " + line); + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/Configurator.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/Configurator.java new file mode 100644 index 000000000000..0853c80b5d8c --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/Configurator.java @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.classworlds; + +/* + * Copyright 2001-2010 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; + +/** + * A compatibility wrapper for {@link org.codehaus.plexus.classworlds.launcher.Configurator} + * provided for legacy code. + * + *

Note: This is a legacy class provided for backward compatibility with Maven 2. + * New code should use {@link org.codehaus.plexus.classworlds.launcher.Configurator}.

+ * + * @author Andrew Williams + * @deprecated Use {@link org.codehaus.plexus.classworlds.launcher.Configurator} + */ +@Deprecated +public class Configurator { + private final ConfiguratorAdapter config; + + /** Construct. + * + * @param launcher The launcher to configure. + */ + public Configurator(Launcher launcher) { + config = ConfiguratorAdapter.getInstance( + new org.codehaus.plexus.classworlds.launcher.Configurator(launcher), launcher); + } + + /** Construct. + * + * @param world The classWorld to configure. + */ + public Configurator(ClassWorld world) { + config = ConfiguratorAdapter.getInstance( + new org.codehaus.plexus.classworlds.launcher.Configurator(ClassWorldReverseAdapter.getInstance(world)), + world); + } + + /** set world. + * this setter is provided so you can use the same configurator to configure several "worlds" + * + * @param world The classWorld to configure. + */ + public void setClassWorld(ClassWorld world) { + config.setClassWorld(world); + } + + /** + * Configure from a file. + * + * @param is The config input stream + * @throws IOException If an error occurs reading the config file. + * @throws MalformedURLException If the config file contains invalid URLs. + * @throws ConfigurationException If the config file is corrupt. + * @throws DuplicateRealmException If the config file defines two realms with the same id. + * @throws NoSuchRealmException If the config file defines a main entry point in + * a non-existent realm. + */ + public void configure(InputStream is) + throws IOException, MalformedURLException, ConfigurationException, DuplicateRealmException, + NoSuchRealmException { + config.configureAdapter(is); + } + + /** + * Associate parent realms with their children. + */ + protected void associateRealms() { + config.associateRealms(); + } + + /** + * Load a glob into the specified classloader. + * + * @param line The path configuration line. + * @param realm The realm to populate + * @throws MalformedURLException If the line does not represent + * a valid path element. + * @throws FileNotFoundException If the line does not represent + * a valid path element in the filesystem. + */ + protected void loadGlob(String line, ClassRealm realm) throws MalformedURLException, FileNotFoundException { + loadGlob(line, realm, false); + } + + /** + * Load a glob into the specified classloader. + * + * @param line The path configuration line. + * @param realm The realm to populate + * @param optionally Whether the path is optional or required + * @throws MalformedURLException If the line does not represent + * a valid path element. + * @throws FileNotFoundException If the line does not represent + * a valid path element in the filesystem. + */ + @SuppressWarnings("RedundantThrows") + protected void loadGlob(String line, ClassRealm realm, boolean optionally) + throws MalformedURLException, FileNotFoundException { + config.loadGlob(line, realm, optionally); + } + + /** + * Filter a string for system properties. + * + * @param text The text to filter. + * @return The filtered text. + * @throws ConfigurationException If the property does not + * exist or if there is a syntax error. + */ + @SuppressWarnings("RedundantThrows") + protected String filter(String text) throws ConfigurationException { + return config.filter(text); + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ConfiguratorAdapter.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ConfiguratorAdapter.java new file mode 100644 index 000000000000..8cd912c801fe --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/ConfiguratorAdapter.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.classworlds; + +/* + * Copyright 2001-2010 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; + +/** + * An adapter for Configurators. + * + *

Note: This is a legacy internal class provided for backward compatibility with Maven 2. + * New code should avoid using this adapter.

+ * + * @deprecated This is a legacy internal class. + */ +@Deprecated +public class ConfiguratorAdapter extends Configurator { + + public static ConfiguratorAdapter getInstance( + org.codehaus.plexus.classworlds.launcher.Configurator newConfig, Launcher launcher) { + return new ConfiguratorAdapter(newConfig, launcher); + } + + public static ConfiguratorAdapter getInstance( + org.codehaus.plexus.classworlds.launcher.Configurator newConfig, ClassWorld world) { + return new ConfiguratorAdapter(newConfig, world); + } + + private final org.codehaus.plexus.classworlds.launcher.Configurator config; + + private ConfiguratorAdapter(org.codehaus.plexus.classworlds.launcher.Configurator config, Launcher launcher) { + super(launcher); + this.config = config; + } + + private ConfiguratorAdapter(org.codehaus.plexus.classworlds.launcher.Configurator config, ClassWorld world) { + super(world); + this.config = config; + } + + public void associateRealms() { + config.associateRealms(); + } + + @SuppressWarnings("DuplicateThrows") + public void configureAdapter(InputStream is) + throws IOException, MalformedURLException, ConfigurationException, DuplicateRealmException, + NoSuchRealmException { + try { + config.configure(is); + } catch (org.codehaus.plexus.classworlds.launcher.ConfigurationException e) { + throw new ConfigurationException(e.getMessage()); + } catch (org.codehaus.plexus.classworlds.realm.DuplicateRealmException e) { + throw new DuplicateRealmException(ClassWorldAdapter.getInstance(e.getWorld()), e.getId()); + } catch (org.codehaus.plexus.classworlds.realm.NoSuchRealmException e) { + throw new NoSuchRealmException(ClassWorldAdapter.getInstance(e.getWorld()), e.getId()); + } + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/DefaultClassRealm.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/DefaultClassRealm.java new file mode 100644 index 000000000000..1d13a4dee06f --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/DefaultClassRealm.java @@ -0,0 +1,162 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.classworlds; + +/* + * Copyright 2001-2010 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * A compatibility wrapper for {@link org.codehaus.plexus.classworlds.realm.ClassRealm} + * provided for legacy code. + * + *

Note: This is a legacy class provided for backward compatibility with Maven 2. + * New code should use {@link org.codehaus.plexus.classworlds.realm.ClassRealm}.

+ * + * @author Andrew Williams + * @deprecated Use {@link org.codehaus.plexus.classworlds.realm.ClassRealm} + */ +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; + +@SuppressWarnings("rawtypes") +@Deprecated +public class DefaultClassRealm implements ClassRealm { + private final ClassRealmAdapter adapter; + + public DefaultClassRealm(ClassWorld world, String id) { + this(world, id, null); + } + + public DefaultClassRealm(ClassWorld world, String id, ClassLoader foreignClassLoader) { + this.adapter = ClassRealmAdapter.getInstance(new org.codehaus.plexus.classworlds.realm.ClassRealm( + ClassWorldReverseAdapter.getInstance(world), id, foreignClassLoader)); + } + + public URL[] getConstituents() { + return adapter.getConstituents(); + } + + public ClassRealm getParent() { + return adapter.getParentRealm(); + } + + public void setParent(ClassRealm parent) { + adapter.setParent(parent); + } + + public String getId() { + return adapter.getId(); + } + + public ClassWorld getWorld() { + return adapter.getWorld(); + } + + public void importFrom(String realmId, String packageName) throws NoSuchRealmException { + adapter.importFrom(realmId, packageName); + } + + public void addConstituent(URL constituent) { + adapter.addConstituent(constituent); + } + + /** + * Adds a byte[] class definition as a constituent for locating classes. + * Currently uses BytesURLStreamHandler to hold a reference of the byte[] in memory. + * This ensures we have a unifed URL resource model for all constituents. + * The code to cache to disk is commented out - maybe a property to choose which method? + * + * @param constituent class name + * @param b the class definition as a byte[] + * @throws ClassNotFoundException when class couldn't be loaded + */ + public void addConstituent(String constituent, byte[] b) throws ClassNotFoundException { + try { + File path, file; + if (constituent.lastIndexOf('.') != -1) { + path = new File("byteclass/" + + constituent + .substring(0, constituent.lastIndexOf('.') + 1) + .replace('.', File.separatorChar)); + + file = new File(path, constituent.substring(constituent.lastIndexOf('.') + 1) + ".class"); + } else { + path = new File("byteclass/"); + + file = new File(path, constituent + ".class"); + } + + addConstituent(new URL(null, file.toURI().toURL().toExternalForm(), new BytesURLStreamHandler(b))); + } catch (java.io.IOException e) { + throw new ClassNotFoundException("Couldn't load byte stream.", e); + } + } + + public ClassRealm locateSourceRealm(String classname) { + return adapter.locateSourceRealm(classname); + } + + public ClassLoader getClassLoader() { + return adapter.getClassLoader(); + } + + public ClassRealm createChildRealm(String id) throws DuplicateRealmException { + return adapter.createChildRealm(id); + } + + // ---------------------------------------------------------------------- + // ClassLoader API + // ---------------------------------------------------------------------- + + public Class loadClass(String name) throws ClassNotFoundException { + return adapter.loadClass(name); + } + + public URL getResource(String name) { + return adapter.getResource(name); + } + + public InputStream getResourceAsStream(String name) { + return adapter.getResourceAsStream(name); + } + + public Enumeration findResources(String name) throws IOException { + return adapter.findResources(name); + } + + public void display() { + adapter.display(); + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/DuplicateRealmException.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/DuplicateRealmException.java new file mode 100644 index 000000000000..c139554310a3 --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/DuplicateRealmException.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.classworlds; + +/* + +Copyright 2002 (C) The Werken Company. All Rights Reserved. + +Redistribution and use of this software and associated documentation +("Software"), with or without modification, are permitted provided +that the following conditions are met: + +1. Redistributions of source code must retain copyright + statements and notices. Redistributions must also contain a + copy of this document. + +2. Redistributions in binary form must reproduce the + above copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +3. The name "classworlds" must not be used to endorse or promote + products derived from this Software without prior written + permission of The Werken Company. For written permission, + please contact bob@werken.com. + +4. Products derived from this Software may not be called "classworlds" + nor may "classworlds" appear in their names without prior written + permission of The Werken Company. "classworlds" is a registered + trademark of The Werken Company. + +5. Due credit should be given to The Werken Company. + (http://classworlds.werken.com/). + +THIS SOFTWARE IS PROVIDED BY THE WERKEN COMPANY AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +THE WERKEN COMPANY OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +/** + * Indicates an attempt to add a ClassRealm to a + * ClassWorld with a duplicate id. + * + *

Note: This is a legacy exception provided for backward compatibility with Maven 2. + * New code should use {@link org.codehaus.plexus.classworlds.realm.DuplicateRealmException}.

+ * + * @author bob mcwhirter + * @deprecated Use {@link org.codehaus.plexus.classworlds.realm.DuplicateRealmException} + */ +@Deprecated +public class DuplicateRealmException extends ClassWorldException { + // ------------------------------------------------------------ + // Instance members + // ------------------------------------------------------------ + + /** + * The realm id. + */ + private final String id; + + // ------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------ + + /** + * Construct. + * + * @param world The world. + * @param id The realm id. + */ + public DuplicateRealmException(ClassWorld world, String id) { + super(world, id); + this.id = id; + } + + // ------------------------------------------------------------ + // Instance methods + // ------------------------------------------------------------ + + /** + * Retrieve the duplicate realm id. + * + * @return The id. + */ + public String getId() { + return this.id; + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/Launcher.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/Launcher.java new file mode 100644 index 000000000000..d181dbbc9096 --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/Launcher.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.classworlds; + +/* + * Copyright 2001-2010 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * A compatibility wrapper for {@link org.codehaus.plexus.classworlds.launcher.Launcher} + * provided for legacy code. + * + *

Note: This is a legacy class provided for backward compatibility with Maven 2. + * New code should use {@link org.codehaus.plexus.classworlds.launcher.Launcher}.

+ * + * @author Andrew Williams + * @deprecated Use {@link org.codehaus.plexus.classworlds.launcher.Launcher} + */ +@Deprecated +public class Launcher extends org.codehaus.plexus.classworlds.launcher.Launcher { + public Launcher() {} + + // ------------------------------------------------------------ + // Class methods + // ------------------------------------------------------------ + + /** + * Launch the launcher from the command line. + * Will exit using System.exit with an exit code of 0 for success, 100 if there was an unknown exception, + * or some other code for an application error. + * + * @param args The application command-line arguments. + */ + public static void main(String[] args) { + org.codehaus.plexus.classworlds.launcher.Launcher.main(args); + } + + /** + * Launch the launcher. + * + * @param args The application command-line arguments. + * @return an integer exit code + * @throws Exception If an error occurs. + */ + public static int mainWithExitCode(String[] args) throws Exception { + return org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(args); + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/NoSuchRealmException.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/NoSuchRealmException.java new file mode 100644 index 000000000000..065cd9cec458 --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/NoSuchRealmException.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.classworlds; + +/* + +Copyright 2002 (C) The Werken Company. All Rights Reserved. + +Redistribution and use of this software and associated documentation +("Software"), with or without modification, are permitted provided +that the following conditions are met: + +1. Redistributions of source code must retain copyright + statements and notices. Redistributions must also contain a + copy of this document. + +2. Redistributions in binary form must reproduce the + above copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +3. The name "classworlds" must not be used to endorse or promote + products derived from this Software without prior written + permission of The Werken Company. For written permission, + please contact bob@werken.com. + +4. Products derived from this Software may not be called "classworlds" + nor may "classworlds" appear in their names without prior written + permission of The Werken Company. "classworlds" is a registered + trademark of The Werken Company. + +5. Due credit should be given to The Werken Company. + (http://classworlds.werken.com/). + +THIS SOFTWARE IS PROVIDED BY THE WERKEN COMPANY AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +THE WERKEN COMPANY OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +/** + * Indicates an attempt to retrieve a ClassRealm from a + * ClassWorld with an invalid id. + * + *

Note: This is a legacy exception provided for backward compatibility with Maven 2. + * New code should use {@link org.codehaus.plexus.classworlds.realm.NoSuchRealmException}.

+ * + * @author bob mcwhirter + * @deprecated Use {@link org.codehaus.plexus.classworlds.realm.NoSuchRealmException} + */ +@Deprecated +public class NoSuchRealmException extends ClassWorldException { + // ------------------------------------------------------------ + // Instance members + // ------------------------------------------------------------ + + /** + * The realm id. + */ + private final String id; + + // ------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------ + + /** + * Construct. + * + * @param world The world. + * @param id The realm id. + */ + public NoSuchRealmException(ClassWorld world, String id) { + super(world, id); + this.id = id; + } + + // ------------------------------------------------------------ + // Instance methods + // ------------------------------------------------------------ + + /** + * Retrieve the invalid realm id. + * + * @return The id. + */ + public String getId() { + return this.id; + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/package-info.java b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/package-info.java new file mode 100644 index 000000000000..e94806c4c752 --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/classworlds/package-info.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +/** + * Legacy Classworlds 1.x API, preserved for binary compatibility with Eclipse Sisu. + * + *

This package has been deprecated since version 2.5.2 (2014). New code should use + * {@code org.codehaus.plexus.classworlds} instead.

+ * + *

Do not remove this package. The compiled bytecode of + * {@code org.eclipse.sisu:org.eclipse.sisu.plexus} references {@link ClassRealm}, + * {@link ClassRealmAdapter}, and {@link ClassRealmReverseAdapter} from this package. + * Removing them causes {@code ClassNotFoundException} at runtime for any application + * using Sisu, including all Maven 3+ builds.

+ * + *

PR #141 removed this package and had to be reverted immediately (commit 223416e). + * See {@code COMPATIBILITY.md} for details on what Sisu references and what preconditions + * must be satisfied before any future removal.

+ * + * @deprecated Use {@code org.codehaus.plexus.classworlds} for new code. + */ +package org.codehaus.classworlds; diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/FilteredClassRealmTest.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/FilteredClassRealmTest.java index c0b8d6ec4b71..a85076da341a 100644 --- 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 @@ -45,8 +45,10 @@ public void setUp() throws DuplicateRealmException { Set allowedResourcePrefixes = new HashSet<>(); allowedResourcePrefixes.add("a."); allowedResourcePrefixes.add("a/Aa"); - realmA = this.world.newRealm("realmA", getClass().getClassLoader(), s -> allowedResourcePrefixes.stream() - .anyMatch(s::startsWith)); + realmA = this.world.newRealm( + "realmA", + getClass().getClassLoader(), + s -> allowedResourcePrefixes.stream().anyMatch(s::startsWith)); } @Test diff --git a/pom.xml b/pom.xml index 66394cea20e7..208341ae5390 100644 --- a/pom.xml +++ b/pom.xml @@ -402,6 +402,10 @@ under the License. org.apache.maven maven-xml-impl + + org.codehaus.plexus + plexus-classworlds +
@@ -645,6 +649,10 @@ under the License. com.google.inject guice + + org.codehaus.plexus + plexus-classworlds + From bb0414441e29aae6a68a05f117aaa1297dd4b2ae Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Wed, 24 Jun 2026 12:41:12 +0200 Subject: [PATCH 04/13] [ISSUE-11028] Enable targeted --enable-native-access for JLine modules Move JLine jars from lib/ to lib/modules/ and load them via --module-path + --add-modules ALL-MODULE-PATH so that --enable-native-access can target org.jline.terminal.ffm and org.jline.nativ by name instead of using the broad ALL-UNNAMED. Changes: - Add `module` directive to classworlds ConfigurationParser with glob support, mirroring the existing `load` directive - Separate JLine jars into lib/modules/ in the assembly descriptor - Update m2.conf with `module ${maven.home}/lib/modules/*.jar` - Update mvn/mvn.cmd launcher scripts to use --module-path, --add-modules ALL-MODULE-PATH, and targeted --enable-native-access - Update LICENSE.vm to classify JLine under lib/modules/ Co-Authored-By: Claude Opus 4.6 --- apache-maven/src/assembly/component.xml | 9 ++++ apache-maven/src/assembly/maven/bin/m2.conf | 2 + apache-maven/src/assembly/maven/bin/mvn | 7 ++- apache-maven/src/assembly/maven/bin/mvn.cmd | 9 ++-- .../appended-resources/META-INF/LICENSE.vm | 4 ++ .../launcher/ConfigurationHandler.java | 7 +++ .../launcher/ConfigurationParser.java | 52 ++++++++++++++++++- .../classworlds/launcher/Configurator.java | 12 +++++ 8 files changed, 96 insertions(+), 6 deletions(-) diff --git a/apache-maven/src/assembly/component.xml b/apache-maven/src/assembly/component.xml index f964b5d59bc8..9a17e1338f5a 100644 --- a/apache-maven/src/assembly/component.xml +++ b/apache-maven/src/assembly/component.xml @@ -28,12 +28,21 @@ under the License. + + false + lib/modules + + org.jline:* + + + false lib org.apache.maven:maven-api-classworlds org.apache.maven:maven-classworlds + org.jline:* diff --git a/apache-maven/src/assembly/maven/bin/m2.conf b/apache-maven/src/assembly/maven/bin/m2.conf index b91431dea5f9..58aeb83a02a7 100644 --- a/apache-maven/src/assembly/maven/bin/m2.conf +++ b/apache-maven/src/assembly/maven/bin/m2.conf @@ -23,6 +23,8 @@ main is ${maven.mainClass} from plexus.core set maven.conf default ${maven.home}/conf set maven.installation.conf default ${maven.conf} +module ${maven.home}/lib/modules/*.jar + [plexus.core] load ${maven.conf}/logging optionally ${maven.home}/lib/ext/redisson/*.jar diff --git a/apache-maven/src/assembly/maven/bin/mvn b/apache-maven/src/assembly/maven/bin/mvn index 8882b060a543..9089e13c9762 100755 --- a/apache-maven/src/assembly/maven/bin/mvn +++ b/apache-maven/src/assembly/maven/bin/mvn @@ -226,8 +226,9 @@ fi if [ -n "$MAVEN_DEBUG_SCRIPT" ]; then echo "[DEBUG] Final MAVEN_OPTS: $MAVEN_OPTS" >&2 fi -LAUNCHER_JAR=`echo "$MAVEN_HOME"/boot/maven-classworlds-*.jar` +LAUNCHER_JAR="$MAVEN_HOME/boot/*" LAUNCHER_CLASS=org.codehaus.plexus.classworlds.launcher.Launcher +MODULES_DIR="$MAVEN_HOME/lib/modules" # For Cygwin and MinGW, switch paths to Windows format before running java(1) command if $cygwin || $mingw ; then @@ -279,7 +280,9 @@ MAVEN_MAIN_CLASS=${MAVEN_MAIN_CLASS:=org.apache.maven.cling.MavenCling} cmd="\"$JAVACMD\" \ $MAVEN_OPTS \ $MAVEN_DEBUG_OPTS \ - --enable-native-access=ALL-UNNAMED \ + --module-path \"$MODULES_DIR\" \ + --add-modules ALL-MODULE-PATH \ + --enable-native-access=org.jline.terminal.ffm,org.jline.nativ \ -classpath \"$LAUNCHER_JAR\" \ \"-Dclassworlds.conf=$CLASSWORLDS_CONF\" \ \"-Dmaven.home=$MAVEN_HOME\" \ diff --git a/apache-maven/src/assembly/maven/bin/mvn.cmd b/apache-maven/src/assembly/maven/bin/mvn.cmd index 4b72e3f63529..eeae7d0b9513 100644 --- a/apache-maven/src/assembly/maven/bin/mvn.cmd +++ b/apache-maven/src/assembly/maven/bin/mvn.cmd @@ -266,13 +266,14 @@ goto processArgs :endHandleArgs call :processArgs %* -for %%i in ("%MAVEN_HOME%"\boot\maven-classworlds-*) do set LAUNCHER_JAR="%%i" +set LAUNCHER_JAR="%MAVEN_HOME%\boot\*" set LAUNCHER_CLASS=org.codehaus.plexus.classworlds.launcher.Launcher +set MODULES_DIR="%MAVEN_HOME%\lib\modules" if "%MAVEN_MAIN_CLASS%"=="" @set MAVEN_MAIN_CLASS=org.apache.maven.cling.MavenCling if defined MAVEN_DEBUG_SCRIPT ( echo [DEBUG] Launching JVM with command: - echo [DEBUG] "%JAVACMD%" %INTERNAL_MAVEN_OPTS% %MAVEN_OPTS% %JVM_CONFIG_MAVEN_OPTS% %MAVEN_DEBUG_OPTS% --enable-native-access=ALL-UNNAMED -classpath %LAUNCHER_JAR% "-Dclassworlds.conf=%CLASSWORLDS_CONF%" "-Dmaven.home=%MAVEN_HOME%" "-Dmaven.mainClass=%MAVEN_MAIN_CLASS%" "-Dlibrary.jline.path=%MAVEN_HOME%\lib\jline-native" "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %LAUNCHER_CLASS% %MAVEN_ARGS% %* + echo [DEBUG] "%JAVACMD%" %INTERNAL_MAVEN_OPTS% %MAVEN_OPTS% %JVM_CONFIG_MAVEN_OPTS% %MAVEN_DEBUG_OPTS% --module-path %MODULES_DIR% --add-modules ALL-MODULE-PATH --enable-native-access=org.jline.terminal.ffm,org.jline.nativ -classpath %LAUNCHER_JAR% "-Dclassworlds.conf=%CLASSWORLDS_CONF%" "-Dmaven.home=%MAVEN_HOME%" "-Dmaven.mainClass=%MAVEN_MAIN_CLASS%" "-Dlibrary.jline.path=%MAVEN_HOME%\lib\jline-native" "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %LAUNCHER_CLASS% %MAVEN_ARGS% %* ) "%JAVACMD%" ^ @@ -280,7 +281,9 @@ if defined MAVEN_DEBUG_SCRIPT ( %MAVEN_OPTS% ^ %JVM_CONFIG_MAVEN_OPTS% ^ %MAVEN_DEBUG_OPTS% ^ - --enable-native-access=ALL-UNNAMED ^ + --module-path %MODULES_DIR% ^ + --add-modules ALL-MODULE-PATH ^ + --enable-native-access=org.jline.terminal.ffm,org.jline.nativ ^ -classpath %LAUNCHER_JAR% ^ "-Dclassworlds.conf=%CLASSWORLDS_CONF%" ^ "-Dmaven.home=%MAVEN_HOME%" ^ diff --git a/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm b/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm index b1060a81330e..adee298985d7 100644 --- a/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm +++ b/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm @@ -273,6 +273,10 @@ subject to the terms and conditions of the following licenses: #* *##if ( $project.artifact.artifactId == "maven-classworlds" ) #* *##set ( $directory = 'boot' ) ## #* *##end ## +#* *### JLine modules are in lib/modules directory +#* *##if ( $groupId == "org.jline" ) +#* *##set ( $directory = 'lib/modules' ) ## +#* *##end ## #* *### #* *### copy license file to lib/$artifactId.license #* *##set ( $licFile = $directory + '/' + $project.artifact.artifactId + '.license' ) ## diff --git a/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 index 2bd42c1e8907..e90142ff12c4 100644 --- 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 @@ -80,4 +80,11 @@ public interface ConfigurationHandler { * @param url the url to load content from */ void addLoadURL(URL url); + + /** + * Add a file to the module path. Jars added via this method will be loaded + * as JPMS named modules in a {@link ModuleLayer}, rather than on the classpath. + * @param file the jar file to add to the module path + */ + default void addModuleFile(File file) {} } 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 index 6f47b4aeb440..92eb27ba716d 100644 --- 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 @@ -67,6 +67,8 @@ public class ConfigurationParser { public static final String LOAD_PREFIX = "load"; + public static final String MODULE_PREFIX = "module"; + /** * Optionally spec prefix. */ @@ -122,7 +124,11 @@ public void parse(InputStream is) char lineFirstChar = line.charAt(0); switch (lineFirstChar) { case 'm': - mainSet = handleMainConfiguration(line, lineNo, mainSet); + if (line.startsWith(MODULE_PREFIX)) { + handleModuleConfiguration(line, lineNo); + } else { + mainSet = handleMainConfiguration(line, lineNo, mainSet); + } break; case 's': if (handleSetConfiguration(line, lineNo)) { @@ -416,6 +422,50 @@ private void handleLoadConfiguration(String line, int lineNo) throw new ConfigurationException("Unhandled configuration", lineNo, line); } + private void handleModuleConfiguration(String line, int lineNo) + throws ConfigurationException, FileNotFoundException, MalformedURLException { + if (line.startsWith(MODULE_PREFIX)) { + String constituent = line.substring(MODULE_PREFIX.length()).trim(); + constituent = filter(constituent); + + if (constituent.contains("*")) { + moduleGlob(constituent); + } else { + File file = new File(constituent); + + if (file.exists()) { + handler.addModuleFile(file); + } else { + throw new FileNotFoundException(constituent); + } + } + return; + } + throw new ConfigurationException("Unhandled configuration", lineNo, line); + } + + protected void moduleGlob(String line) throws MalformedURLException, FileNotFoundException { + File globFile = new File(line); + + File dir = globFile.getParentFile(); + if (!dir.exists()) { + return; + } + + String localName = globFile.getName(); + int starLoc = localName.indexOf("*"); + final String prefix = localName.substring(0, starLoc); + final String suffix = localName.substring(starLoc + 1); + + File[] matches = dir.listFiles((dir1, name) -> name.startsWith(prefix) && name.endsWith(suffix)); + + if (matches != null) { + for (File match : matches) { + handler.addModuleFile(match); + } + } + } + private void handleOptionallyConfiguration(String line, int lineNo) throws ConfigurationException, FileNotFoundException, MalformedURLException { if (line.startsWith(OPTIONALLY_PREFIX)) { 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 index 85aa9f78b617..6f124c74dbe6 100644 --- 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 @@ -39,6 +39,7 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; +import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -75,6 +76,8 @@ public class Configurator implements ConfigurationHandler { private ClassLoader foreignClassLoader = null; + private List modulePaths = new ArrayList<>(); + /** * Construct. * @@ -131,6 +134,7 @@ public void configure(InputStream is) curRealm = null; foreignClassLoader = null; + modulePaths = new ArrayList<>(); if (this.launcher != null) { foreignClassLoader = this.launcher.getSystemClassLoader(); @@ -208,6 +212,14 @@ public void addRealm(String realmName) throws DuplicateRealmException { configuredRealms.put(realmName, curRealm); } + public void addModuleFile(File file) { + modulePaths.add(file.toPath()); + } + + public List getModulePaths() { + return modulePaths; + } + public void setAppMain(String mainClassName, String mainRealmName) { if (this.launcher != null) { this.launcher.setAppMain(mainClassName, mainRealmName); From cb708489d9abc7c042f197c5ae4b45e9030e7e1f Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Wed, 24 Jun 2026 14:52:22 +0200 Subject: [PATCH 05/13] [ISSUE-11028] Add runtime ModuleLayer support with module access control Add JPMS ModuleLayer creation in classworlds for runtime module loading with add-exports/add-opens/add-reads directives in m2.conf. Plugins and extensions can declare module access requirements via META-INF/maven/module-access descriptors, processed for all realm types. Co-Authored-By: Claude Opus 4.6 --- .../plexus/classworlds/ClassWorld.java | 17 +++ .../launcher/ConfigurationHandler.java | 23 ++++ .../launcher/ConfigurationParser.java | 56 ++++++++++ .../classworlds/launcher/Configurator.java | 100 ++++++++++++++++++ .../plexus/classworlds/realm/ClassRealm.java | 53 ++++++++++ .../classrealm/DefaultClassRealmManager.java | 63 +++++++++++ 6 files changed, 312 insertions(+) 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 index 794c19c89a5b..d4d1b5da0453 100644 --- 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 @@ -59,6 +59,10 @@ public class ClassWorld implements org.apache.maven.api.classworlds.ClassWorld, private final List listeners = new ArrayList<>(); + private ModuleLayer moduleLayer; + + private ModuleLayer.Controller moduleLayerController; + public ClassWorld(String realmId, ClassLoader classLoader) { this(); @@ -157,6 +161,19 @@ public synchronized Collection getRealms() { return Collections.unmodifiableList(new ArrayList<>(realms.values())); } + public void setModuleLayer(ModuleLayer moduleLayer, ModuleLayer.Controller controller) { + this.moduleLayer = moduleLayer; + this.moduleLayerController = controller; + } + + public ModuleLayer getModuleLayer() { + return moduleLayer; + } + + public ModuleLayer.Controller getModuleLayerController() { + return moduleLayerController; + } + // from exports branch public synchronized ClassRealm getClassRealm(String id) { if (realms.containsKey(id)) { 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 index e90142ff12c4..628055b52536 100644 --- 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 @@ -87,4 +87,27 @@ public interface ConfigurationHandler { * @param file the jar file to add to the module path */ default void addModuleFile(File file) {} + + /** + * Export a package from a module to a target module. + * @param module the source module name + * @param pkg the package to export + * @param target the target module name, or {@code ALL-UNNAMED} for the unnamed module + */ + default void addExports(String module, String pkg, String target) {} + + /** + * Open a package from a module to a target module for deep reflection. + * @param module the source module name + * @param pkg the package to open + * @param target the target module name, or {@code ALL-UNNAMED} for the unnamed module + */ + default void addOpens(String module, String pkg, String target) {} + + /** + * Add a reads edge from a source module to a target module. + * @param source the source module name + * @param target the target module name + */ + default void addReads(String source, String target) {} } diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParser.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParser.java index 92eb27ba716d..8a1c7a56fa2c 100644 --- 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 @@ -69,6 +69,12 @@ public class ConfigurationParser { public static final String MODULE_PREFIX = "module"; + public static final String ADD_EXPORTS_PREFIX = "add-exports"; + + public static final String ADD_OPENS_PREFIX = "add-opens"; + + public static final String ADD_READS_PREFIX = "add-reads"; + /** * Optionally spec prefix. */ @@ -144,6 +150,9 @@ public void parse(InputStream is) case 'l': handleLoadConfiguration(line, lineNo); break; + case 'a': + handleAddDirective(line, lineNo); + break; case 'o': handleOptionallyConfiguration(line, lineNo); break; @@ -444,6 +453,53 @@ private void handleModuleConfiguration(String line, int lineNo) throw new ConfigurationException("Unhandled configuration", lineNo, line); } + private void handleAddDirective(String line, int lineNo) throws ConfigurationException { + if (line.startsWith(ADD_EXPORTS_PREFIX)) { + String spec = line.substring(ADD_EXPORTS_PREFIX.length()).trim(); + parseModulePackageDirective(spec, lineNo, line, handler::addExports); + } else if (line.startsWith(ADD_OPENS_PREFIX)) { + String spec = line.substring(ADD_OPENS_PREFIX.length()).trim(); + parseModulePackageDirective(spec, lineNo, line, handler::addOpens); + } else if (line.startsWith(ADD_READS_PREFIX)) { + String spec = line.substring(ADD_READS_PREFIX.length()).trim(); + int eqLoc = spec.indexOf('='); + if (eqLoc < 0) { + throw new ConfigurationException("Missing '=' in add-reads directive", lineNo, line); + } + String source = spec.substring(0, eqLoc).trim(); + String target = spec.substring(eqLoc + 1).trim(); + handler.addReads(source, target); + } else { + throw new ConfigurationException("Unhandled configuration", lineNo, line); + } + } + + private void parseModulePackageDirective(String spec, int lineNo, String line, ModulePackageHandler mph) + throws ConfigurationException { + int slashLoc = spec.indexOf('/'); + if (slashLoc < 0) { + throw new ConfigurationException("Missing '/' in module/package directive", lineNo, line); + } + String module = spec.substring(0, slashLoc).trim(); + String rest = spec.substring(slashLoc + 1).trim(); + String pkg; + String target; + int eqLoc = rest.indexOf('='); + if (eqLoc >= 0) { + pkg = rest.substring(0, eqLoc).trim(); + target = rest.substring(eqLoc + 1).trim(); + } else { + pkg = rest; + target = "ALL-UNNAMED"; + } + mph.handle(module, pkg, target); + } + + @FunctionalInterface + private interface ModulePackageHandler { + void handle(String module, String pkg, String target); + } + protected void moduleGlob(String line) throws MalformedURLException, FileNotFoundException { File globFile = new File(line); 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 index 6f124c74dbe6..70b2aaf6231a 100644 --- 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 @@ -37,6 +37,8 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.lang.module.Configuration; +import java.lang.module.ModuleFinder; import java.net.MalformedURLException; import java.net.URL; import java.nio.file.Path; @@ -44,6 +46,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import org.codehaus.plexus.classworlds.ClassWorld; import org.codehaus.plexus.classworlds.realm.ClassRealm; @@ -78,6 +82,12 @@ public class Configurator implements ConfigurationHandler { private List modulePaths = new ArrayList<>(); + private List exportDirectives = new ArrayList<>(); + + private List openDirectives = new ArrayList<>(); + + private List readDirectives = new ArrayList<>(); + /** * Construct. * @@ -135,6 +145,9 @@ public void configure(InputStream is) foreignClassLoader = null; modulePaths = new ArrayList<>(); + exportDirectives = new ArrayList<>(); + openDirectives = new ArrayList<>(); + readDirectives = new ArrayList<>(); if (this.launcher != null) { foreignClassLoader = this.launcher.getSystemClassLoader(); @@ -144,6 +157,8 @@ public void configure(InputStream is) parser.parse(is); + createModuleLayer(); + // Associate child realms to their parents. associateRealms(); @@ -220,6 +235,91 @@ public List getModulePaths() { return modulePaths; } + public void addExports(String module, String pkg, String target) { + exportDirectives.add(new String[] {module, pkg, target}); + } + + public void addOpens(String module, String pkg, String target) { + openDirectives.add(new String[] {module, pkg, target}); + } + + public void addReads(String source, String target) { + readDirectives.add(new String[] {source, target}); + } + + private void createModuleLayer() { + if (modulePaths.isEmpty() + && exportDirectives.isEmpty() + && openDirectives.isEmpty() + && readDirectives.isEmpty()) { + return; + } + + Set bootModuleNames = + ModuleLayer.boot().modules().stream().map(Module::getName).collect(Collectors.toSet()); + + ModuleLayer.Controller controller = null; + ModuleLayer layer = ModuleLayer.boot(); + + if (!modulePaths.isEmpty()) { + ModuleFinder finder = ModuleFinder.of(modulePaths.toArray(new Path[0])); + Set newModules = finder.findAll().stream() + .map(ref -> ref.descriptor().name()) + .filter(name -> !bootModuleNames.contains(name)) + .collect(Collectors.toSet()); + + if (!newModules.isEmpty()) { + Configuration cf = ModuleLayer.boot().configuration().resolve(finder, ModuleFinder.of(), newModules); + ClassLoader parent = + foreignClassLoader != null ? foreignClassLoader : ClassLoader.getSystemClassLoader(); + controller = ModuleLayer.defineModulesWithOneLoader(cf, List.of(ModuleLayer.boot()), parent); + layer = controller.layer(); + foreignClassLoader = layer.findLoader(newModules.iterator().next()); + } + } + + Module unnamedTarget = getClass().getModule(); + + applyExportOpenDirectives(layer, controller, bootModuleNames, unnamedTarget); + + for (String[] directive : readDirectives) { + Module source = layer.findModule(directive[0]).orElse(null); + Module target = "ALL-UNNAMED".equals(directive[1]) + ? unnamedTarget + : layer.findModule(directive[1]).orElse(null); + if (source != null && target != null && controller != null && !bootModuleNames.contains(source.getName())) { + controller.addReads(source, target); + } + } + + if (controller != null) { + world.setModuleLayer(layer, controller); + } + } + + private void applyExportOpenDirectives( + ModuleLayer layer, ModuleLayer.Controller controller, Set bootModuleNames, Module unnamedTarget) { + for (boolean open : new boolean[] {false, true}) { + for (String[] directive : open ? openDirectives : exportDirectives) { + Module source = layer.findModule(directive[0]).orElse(null); + if (source == null || bootModuleNames.contains(source.getName())) { + continue; + } + Module target = "ALL-UNNAMED".equals(directive[2]) + ? unnamedTarget + : layer.findModule(directive[2]).orElse(null); + if (target == null || controller == null) { + continue; + } + if (open) { + controller.addOpens(source, directive[1], target); + } else { + controller.addExports(source, directive[1], target); + } + } + } + } + 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/realm/ClassRealm.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/ClassRealm.java index 290e440d5549..42a9a89bbe06 100644 --- 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 @@ -227,6 +227,59 @@ public void addURL(URL url) { super.addURL(url); } + /** + * Export a package from a named module to this realm's unnamed module. + * Uses the {@link ModuleLayer.Controller} from the {@link ClassWorld} for runtime layer modules. + * + * @param moduleName the source module name + * @param packageName the package to export + */ + public void addExports(String moduleName, String packageName) { + applyModuleAccess(moduleName, packageName, false); + } + + /** + * Open a package from a named module to this realm's unnamed module for deep reflection. + * Uses the {@link ModuleLayer.Controller} from the {@link ClassWorld} for runtime layer modules. + * + * @param moduleName the source module name + * @param packageName the package to open + */ + public void addOpens(String moduleName, String packageName) { + applyModuleAccess(moduleName, packageName, true); + } + + private void applyModuleAccess(String moduleName, String packageName, boolean open) { + Module target = getUnnamedModule(); + + ModuleLayer runtimeLayer = world.getModuleLayer(); + ModuleLayer.Controller controller = world.getModuleLayerController(); + + Module source = null; + boolean isRuntimeModule = false; + + if (runtimeLayer != null) { + source = runtimeLayer.findModule(moduleName).orElse(null); + if (source != null) { + isRuntimeModule = true; + } + } + if (source == null) { + source = ModuleLayer.boot().findModule(moduleName).orElse(null); + } + if (source == null) { + return; + } + + if (isRuntimeModule && controller != null) { + if (open) { + controller.addOpens(source, packageName, target); + } else { + controller.addExports(source, packageName, target); + } + } + } + // ---------------------------------------------------------------------- // We delegate to the Strategy here so that we can change the behavior // of any existing ClassRealm. diff --git a/impl/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java b/impl/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java index 73fcf7d52eba..01f0b47cb814 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java +++ b/impl/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java @@ -22,10 +22,16 @@ import javax.inject.Named; import javax.inject.Singleton; +import java.io.BufferedReader; import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; +import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -207,6 +213,8 @@ private ClassRealm createRealm( populateRealm(classRealm, constituents); + applyModuleAccessDescriptors(classRealm); + return classRealm; } @@ -365,4 +373,59 @@ private static Object getId(ClassLoader classLoader) { } return classLoader; } + + private static final String MODULE_ACCESS_DESCRIPTOR = "META-INF/maven/module-access"; + + private void applyModuleAccessDescriptors(ClassRealm classRealm) { + var implRealm = (org.codehaus.plexus.classworlds.realm.ClassRealm) classRealm; + try { + Enumeration resources = implRealm.getResources(MODULE_ACCESS_DESCRIPTOR); + while (resources.hasMoreElements()) { + URL resource = resources.nextElement(); + try (BufferedReader reader = + new BufferedReader(new InputStreamReader(resource.openStream(), StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (line.isEmpty() || line.startsWith("#")) { + continue; + } + applyModuleAccessDirective(implRealm, line); + } + } + } + } catch (IOException e) { + logger.debug("Failed to read module-access descriptors for realm {}", classRealm.getId(), e); + } + } + + private void applyModuleAccessDirective(org.codehaus.plexus.classworlds.realm.ClassRealm realm, String line) { + if (line.startsWith("add-exports ")) { + String spec = line.substring("add-exports ".length()).trim(); + int slash = spec.indexOf('/'); + if (slash > 0) { + String module = spec.substring(0, slash); + String pkg = spec.substring(slash + 1); + int eq = pkg.indexOf('='); + if (eq > 0) { + pkg = pkg.substring(0, eq); + } + realm.addExports(module, pkg); + } + } else if (line.startsWith("add-opens ")) { + String spec = line.substring("add-opens ".length()).trim(); + int slash = spec.indexOf('/'); + if (slash > 0) { + String module = spec.substring(0, slash); + String pkg = spec.substring(slash + 1); + int eq = pkg.indexOf('='); + if (eq > 0) { + pkg = pkg.substring(0, eq); + } + realm.addOpens(module, pkg); + } + } else { + logger.debug("Unknown module-access directive: {}", line); + } + } } From 553e2fc478317fba263f24cbbaa8c2a088f7fec6 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Wed, 24 Jun 2026 15:56:10 +0200 Subject: [PATCH 06/13] [ISSUE-11028] Refine module access API design Move module access logic from ClassRealm into ClassWorld to encapsulate the Controller. Add addExports/addOpens/addReads to the API interface as default methods returning boolean for success feedback. Synchronize setModuleLayer. Support add-reads in module-access descriptors and log on failure. Co-Authored-By: Claude Opus 4.6 --- .../maven/api/classworlds/ClassRealm.java | 41 ++++++++++ .../plexus/classworlds/ClassWorld.java | 82 ++++++++++++++++++- .../classworlds/launcher/Configurator.java | 71 ++++++++-------- .../plexus/classworlds/realm/ClassRealm.java | 53 ++---------- .../classrealm/DefaultClassRealmManager.java | 54 +++++++----- 5 files changed, 193 insertions(+), 108 deletions(-) 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 index 182b8a4326e1..07260abfba44 100644 --- 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 @@ -194,4 +194,45 @@ public interface ClassRealm extends Closeable { */ @Nullable URL loadResourceFromParent(@Nonnull String name); + + /** + * Exports a package from a named JPMS module to this realm's unnamed module, + * making the package's public types accessible to classes loaded by this realm. + * + * @param moduleName the source module name + * @param packageName the package to export + * @return {@code true} if the export was applied, {@code false} if the module was not found + * or is in the boot layer (which requires {@code --add-exports} in the launcher) + * @since 4.1.0 + */ + default boolean addExports(@Nonnull String moduleName, @Nonnull String packageName) { + return false; + } + + /** + * Opens a package from a named JPMS module to this realm's unnamed module for deep reflection, + * making the package's types accessible via reflection (including non-public members). + * + * @param moduleName the source module name + * @param packageName the package to open + * @return {@code true} if the open was applied, {@code false} if the module was not found + * or is in the boot layer (which requires {@code --add-opens} in the launcher) + * @since 4.1.0 + */ + default boolean addOpens(@Nonnull String moduleName, @Nonnull String packageName) { + return false; + } + + /** + * Adds a reads edge from a named JPMS module to this realm's unnamed module, + * allowing the named module to access types in this realm. + * + * @param moduleName the source module that should read this realm's unnamed module + * @return {@code true} if the reads edge was added, {@code false} if the module was not found + * or is in the boot layer + * @since 4.1.0 + */ + default boolean addReads(@Nonnull String moduleName) { + return false; + } } 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 index d4d1b5da0453..69305e6a1335 100644 --- 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 @@ -161,7 +161,7 @@ public synchronized Collection getRealms() { return Collections.unmodifiableList(new ArrayList<>(realms.values())); } - public void setModuleLayer(ModuleLayer moduleLayer, ModuleLayer.Controller controller) { + public synchronized void setModuleLayer(ModuleLayer moduleLayer, ModuleLayer.Controller controller) { this.moduleLayer = moduleLayer; this.moduleLayerController = controller; } @@ -174,6 +174,86 @@ public ModuleLayer.Controller getModuleLayerController() { return moduleLayerController; } + /** + * Exports a package from a named module to the given target module. + * Looks up the source module in the runtime layer first, then the boot layer. + * Only runtime layer modules can be modified via the Controller; boot layer + * modules require {@code --add-exports} in the launcher script. + * + * @param moduleName the source module name + * @param packageName the package to export + * @param target the target module to export to + * @return {@code true} if the export was applied successfully + */ + public synchronized boolean addExports(String moduleName, String packageName, Module target) { + return applyModuleAccess(moduleName, packageName, target, false); + } + + /** + * Opens a package from a named module to the given target module for deep reflection. + * Looks up the source module in the runtime layer first, then the boot layer. + * Only runtime layer modules can be modified via the Controller; boot layer + * modules require {@code --add-opens} in the launcher script. + * + * @param moduleName the source module name + * @param packageName the package to open + * @param target the target module to open to + * @return {@code true} if the open was applied successfully + */ + public synchronized boolean addOpens(String moduleName, String packageName, Module target) { + return applyModuleAccess(moduleName, packageName, target, true); + } + + /** + * Adds a reads edge from the named source module to the given target module. + * Only runtime layer modules can be modified via the Controller. + * + * @param sourceModuleName the source module name + * @param target the target module to read + * @return {@code true} if the reads edge was added successfully + */ + public synchronized boolean addReads(String sourceModuleName, Module target) { + Module source = findModule(sourceModuleName); + if (source == null || moduleLayerController == null) { + return false; + } + if (isBootLayerModule(source)) { + return false; + } + moduleLayerController.addReads(source, target); + return true; + } + + private boolean applyModuleAccess(String moduleName, String packageName, Module target, boolean open) { + Module source = findModule(moduleName); + if (source == null || moduleLayerController == null) { + return false; + } + if (isBootLayerModule(source)) { + return false; + } + if (open) { + moduleLayerController.addOpens(source, packageName, target); + } else { + moduleLayerController.addExports(source, packageName, target); + } + return true; + } + + private Module findModule(String moduleName) { + if (moduleLayer != null) { + Module m = moduleLayer.findModule(moduleName).orElse(null); + if (m != null) { + return m; + } + } + return ModuleLayer.boot().findModule(moduleName).orElse(null); + } + + private boolean isBootLayerModule(Module module) { + return module.getLayer() == ModuleLayer.boot(); + } + // from exports branch public synchronized ClassRealm getClassRealm(String id) { if (realms.containsKey(id)) { 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 index 70b2aaf6231a..b1199632846c 100644 --- 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 @@ -255,13 +255,10 @@ private void createModuleLayer() { return; } - Set bootModuleNames = - ModuleLayer.boot().modules().stream().map(Module::getName).collect(Collectors.toSet()); - - ModuleLayer.Controller controller = null; - ModuleLayer layer = ModuleLayer.boot(); - if (!modulePaths.isEmpty()) { + Set bootModuleNames = + ModuleLayer.boot().modules().stream().map(Module::getName).collect(Collectors.toSet()); + ModuleFinder finder = ModuleFinder.of(modulePaths.toArray(new Path[0])); Set newModules = finder.findAll().stream() .map(ref -> ref.descriptor().name()) @@ -272,52 +269,48 @@ private void createModuleLayer() { Configuration cf = ModuleLayer.boot().configuration().resolve(finder, ModuleFinder.of(), newModules); ClassLoader parent = foreignClassLoader != null ? foreignClassLoader : ClassLoader.getSystemClassLoader(); - controller = ModuleLayer.defineModulesWithOneLoader(cf, List.of(ModuleLayer.boot()), parent); - layer = controller.layer(); + ModuleLayer.Controller controller = + ModuleLayer.defineModulesWithOneLoader(cf, List.of(ModuleLayer.boot()), parent); + ModuleLayer layer = controller.layer(); foreignClassLoader = layer.findLoader(newModules.iterator().next()); + world.setModuleLayer(layer, controller); } } Module unnamedTarget = getClass().getModule(); - applyExportOpenDirectives(layer, controller, bootModuleNames, unnamedTarget); - - for (String[] directive : readDirectives) { - Module source = layer.findModule(directive[0]).orElse(null); - Module target = "ALL-UNNAMED".equals(directive[1]) - ? unnamedTarget - : layer.findModule(directive[1]).orElse(null); - if (source != null && target != null && controller != null && !bootModuleNames.contains(source.getName())) { - controller.addReads(source, target); + for (String[] directive : exportDirectives) { + Module target = resolveTargetModule(directive[2], unnamedTarget); + if (target != null) { + world.addExports(directive[0], directive[1], target); } } - - if (controller != null) { - world.setModuleLayer(layer, controller); + for (String[] directive : openDirectives) { + Module target = resolveTargetModule(directive[2], unnamedTarget); + if (target != null) { + world.addOpens(directive[0], directive[1], target); + } + } + for (String[] directive : readDirectives) { + Module target = resolveTargetModule(directive[1], unnamedTarget); + if (target != null) { + world.addReads(directive[0], target); + } } } - private void applyExportOpenDirectives( - ModuleLayer layer, ModuleLayer.Controller controller, Set bootModuleNames, Module unnamedTarget) { - for (boolean open : new boolean[] {false, true}) { - for (String[] directive : open ? openDirectives : exportDirectives) { - Module source = layer.findModule(directive[0]).orElse(null); - if (source == null || bootModuleNames.contains(source.getName())) { - continue; - } - Module target = "ALL-UNNAMED".equals(directive[2]) - ? unnamedTarget - : layer.findModule(directive[2]).orElse(null); - if (target == null || controller == null) { - continue; - } - if (open) { - controller.addOpens(source, directive[1], target); - } else { - controller.addExports(source, directive[1], target); - } + private Module resolveTargetModule(String targetName, Module unnamedTarget) { + if ("ALL-UNNAMED".equals(targetName)) { + return unnamedTarget; + } + ModuleLayer layer = world.getModuleLayer(); + if (layer != null) { + Module m = layer.findModule(targetName).orElse(null); + if (m != null) { + return m; } } + return ModuleLayer.boot().findModule(targetName).orElse(null); } public void setAppMain(String mainClassName, String mainRealmName) { 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 index 42a9a89bbe06..73925feac863 100644 --- 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 @@ -227,57 +227,16 @@ public void addURL(URL url) { super.addURL(url); } - /** - * Export a package from a named module to this realm's unnamed module. - * Uses the {@link ModuleLayer.Controller} from the {@link ClassWorld} for runtime layer modules. - * - * @param moduleName the source module name - * @param packageName the package to export - */ - public void addExports(String moduleName, String packageName) { - applyModuleAccess(moduleName, packageName, false); + public boolean addExports(String moduleName, String packageName) { + return world.addExports(moduleName, packageName, getUnnamedModule()); } - /** - * Open a package from a named module to this realm's unnamed module for deep reflection. - * Uses the {@link ModuleLayer.Controller} from the {@link ClassWorld} for runtime layer modules. - * - * @param moduleName the source module name - * @param packageName the package to open - */ - public void addOpens(String moduleName, String packageName) { - applyModuleAccess(moduleName, packageName, true); + public boolean addOpens(String moduleName, String packageName) { + return world.addOpens(moduleName, packageName, getUnnamedModule()); } - private void applyModuleAccess(String moduleName, String packageName, boolean open) { - Module target = getUnnamedModule(); - - ModuleLayer runtimeLayer = world.getModuleLayer(); - ModuleLayer.Controller controller = world.getModuleLayerController(); - - Module source = null; - boolean isRuntimeModule = false; - - if (runtimeLayer != null) { - source = runtimeLayer.findModule(moduleName).orElse(null); - if (source != null) { - isRuntimeModule = true; - } - } - if (source == null) { - source = ModuleLayer.boot().findModule(moduleName).orElse(null); - } - if (source == null) { - return; - } - - if (isRuntimeModule && controller != null) { - if (open) { - controller.addOpens(source, packageName, target); - } else { - controller.addExports(source, packageName, target); - } - } + public boolean addReads(String moduleName) { + return world.addReads(moduleName, getUnnamedModule()); } // ---------------------------------------------------------------------- diff --git a/impl/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java b/impl/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java index 01f0b47cb814..7dd9064ff103 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java +++ b/impl/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java @@ -401,31 +401,43 @@ private void applyModuleAccessDescriptors(ClassRealm classRealm) { private void applyModuleAccessDirective(org.codehaus.plexus.classworlds.realm.ClassRealm realm, String line) { if (line.startsWith("add-exports ")) { - String spec = line.substring("add-exports ".length()).trim(); - int slash = spec.indexOf('/'); - if (slash > 0) { - String module = spec.substring(0, slash); - String pkg = spec.substring(slash + 1); - int eq = pkg.indexOf('='); - if (eq > 0) { - pkg = pkg.substring(0, eq); - } - realm.addExports(module, pkg); - } + applyExportOrOpen(realm, line.substring("add-exports ".length()).trim(), false); } else if (line.startsWith("add-opens ")) { - String spec = line.substring("add-opens ".length()).trim(); - int slash = spec.indexOf('/'); - if (slash > 0) { - String module = spec.substring(0, slash); - String pkg = spec.substring(slash + 1); - int eq = pkg.indexOf('='); - if (eq > 0) { - pkg = pkg.substring(0, eq); - } - realm.addOpens(module, pkg); + applyExportOrOpen(realm, line.substring("add-opens ".length()).trim(), true); + } else if (line.startsWith("add-reads ")) { + String module = line.substring("add-reads ".length()).trim(); + if (!realm.addReads(module)) { + logger.debug(" Failed to add-reads {} for realm {}", module, realm.getId()); } } else { logger.debug("Unknown module-access directive: {}", line); } } + + private void applyExportOrOpen(org.codehaus.plexus.classworlds.realm.ClassRealm realm, String spec, boolean open) { + int slash = spec.indexOf('/'); + if (slash <= 0) { + logger.debug("Invalid module-access directive (missing '/'): {}", spec); + return; + } + String module = spec.substring(0, slash); + String pkg = spec.substring(slash + 1); + int eq = pkg.indexOf('='); + if (eq > 0) { + String target = pkg.substring(eq + 1).trim(); + pkg = pkg.substring(0, eq).trim(); + if (!"ALL-UNNAMED".equals(target)) { + logger.warn("module-access directive target '{}' ignored, only ALL-UNNAMED is supported", target); + } + } + boolean applied = open ? realm.addOpens(module, pkg) : realm.addExports(module, pkg); + if (!applied) { + logger.debug( + " Failed to {} {}/{} for realm {}", + open ? "add-opens" : "add-exports", + module, + pkg, + realm.getId()); + } + } } From 00bf58c672b222df0132e3529f522d2b856b1505 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Wed, 24 Jun 2026 16:07:42 +0200 Subject: [PATCH 07/13] [ISSUE-11028] Fix binary compat and Java 17 support - Use void return type for addExports/addOpens/addReads to preserve binary compatibility on the ClassRealm class - Remove redundant @since tags (interface already has @since 4.1.0) - Conditionally enable --module-path and --enable-native-access only on Java 21+ (FFM API not available on Java 17) - Add classpath fallback in Configurator when ModuleLayer creation fails (e.g. module jars reference unavailable APIs) Co-Authored-By: Claude Opus 4.6 --- apache-maven/src/assembly/maven/bin/mvn | 10 ++-- apache-maven/src/assembly/maven/bin/mvn.cmd | 13 +++-- .../maven/api/classworlds/ClassRealm.java | 21 ++------ .../plexus/classworlds/ClassWorld.java | 31 ++++------- .../classworlds/launcher/Configurator.java | 54 ++++++++++++------- .../plexus/classworlds/realm/ClassRealm.java | 12 ++--- .../classrealm/DefaultClassRealmManager.java | 16 ++---- 7 files changed, 76 insertions(+), 81 deletions(-) diff --git a/apache-maven/src/assembly/maven/bin/mvn b/apache-maven/src/assembly/maven/bin/mvn index 9089e13c9762..d24bfba0ff22 100755 --- a/apache-maven/src/assembly/maven/bin/mvn +++ b/apache-maven/src/assembly/maven/bin/mvn @@ -230,6 +230,12 @@ LAUNCHER_JAR="$MAVEN_HOME/boot/*" LAUNCHER_CLASS=org.codehaus.plexus.classworlds.launcher.Launcher MODULES_DIR="$MAVEN_HOME/lib/modules" +# Module-path and native access flags require Java 21+ (FFM API) +MAVEN_MODULE_OPTS="" +if "$JAVACMD" --enable-native-access=org.jline.terminal.ffm -version >/dev/null 2>&1; then + MAVEN_MODULE_OPTS="--module-path \"$MODULES_DIR\" --add-modules ALL-MODULE-PATH --enable-native-access=org.jline.terminal.ffm,org.jline.nativ" +fi + # For Cygwin and MinGW, switch paths to Windows format before running java(1) command if $cygwin || $mingw ; then [ -n "$JAVA_HOME" ] && @@ -280,9 +286,7 @@ MAVEN_MAIN_CLASS=${MAVEN_MAIN_CLASS:=org.apache.maven.cling.MavenCling} cmd="\"$JAVACMD\" \ $MAVEN_OPTS \ $MAVEN_DEBUG_OPTS \ - --module-path \"$MODULES_DIR\" \ - --add-modules ALL-MODULE-PATH \ - --enable-native-access=org.jline.terminal.ffm,org.jline.nativ \ + $MAVEN_MODULE_OPTS \ -classpath \"$LAUNCHER_JAR\" \ \"-Dclassworlds.conf=$CLASSWORLDS_CONF\" \ \"-Dmaven.home=$MAVEN_HOME\" \ diff --git a/apache-maven/src/assembly/maven/bin/mvn.cmd b/apache-maven/src/assembly/maven/bin/mvn.cmd index eeae7d0b9513..8d4d5b8896c8 100644 --- a/apache-maven/src/assembly/maven/bin/mvn.cmd +++ b/apache-maven/src/assembly/maven/bin/mvn.cmd @@ -271,9 +271,16 @@ set LAUNCHER_CLASS=org.codehaus.plexus.classworlds.launcher.Launcher set MODULES_DIR="%MAVEN_HOME%\lib\modules" if "%MAVEN_MAIN_CLASS%"=="" @set MAVEN_MAIN_CLASS=org.apache.maven.cling.MavenCling +@rem Module-path and native access flags require Java 21+ (FFM API) +set MAVEN_MODULE_OPTS= +"%JAVACMD%" --enable-native-access=org.jline.terminal.ffm -version >NUL 2>&1 +if not ERRORLEVEL 1 ( + set MAVEN_MODULE_OPTS=--module-path %MODULES_DIR% --add-modules ALL-MODULE-PATH --enable-native-access=org.jline.terminal.ffm,org.jline.nativ +) + if defined MAVEN_DEBUG_SCRIPT ( echo [DEBUG] Launching JVM with command: - echo [DEBUG] "%JAVACMD%" %INTERNAL_MAVEN_OPTS% %MAVEN_OPTS% %JVM_CONFIG_MAVEN_OPTS% %MAVEN_DEBUG_OPTS% --module-path %MODULES_DIR% --add-modules ALL-MODULE-PATH --enable-native-access=org.jline.terminal.ffm,org.jline.nativ -classpath %LAUNCHER_JAR% "-Dclassworlds.conf=%CLASSWORLDS_CONF%" "-Dmaven.home=%MAVEN_HOME%" "-Dmaven.mainClass=%MAVEN_MAIN_CLASS%" "-Dlibrary.jline.path=%MAVEN_HOME%\lib\jline-native" "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %LAUNCHER_CLASS% %MAVEN_ARGS% %* + echo [DEBUG] "%JAVACMD%" %INTERNAL_MAVEN_OPTS% %MAVEN_OPTS% %JVM_CONFIG_MAVEN_OPTS% %MAVEN_DEBUG_OPTS% %MAVEN_MODULE_OPTS% -classpath %LAUNCHER_JAR% "-Dclassworlds.conf=%CLASSWORLDS_CONF%" "-Dmaven.home=%MAVEN_HOME%" "-Dmaven.mainClass=%MAVEN_MAIN_CLASS%" "-Dlibrary.jline.path=%MAVEN_HOME%\lib\jline-native" "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %LAUNCHER_CLASS% %MAVEN_ARGS% %* ) "%JAVACMD%" ^ @@ -281,9 +288,7 @@ if defined MAVEN_DEBUG_SCRIPT ( %MAVEN_OPTS% ^ %JVM_CONFIG_MAVEN_OPTS% ^ %MAVEN_DEBUG_OPTS% ^ - --module-path %MODULES_DIR% ^ - --add-modules ALL-MODULE-PATH ^ - --enable-native-access=org.jline.terminal.ffm,org.jline.nativ ^ + %MAVEN_MODULE_OPTS% ^ -classpath %LAUNCHER_JAR% ^ "-Dclassworlds.conf=%CLASSWORLDS_CONF%" ^ "-Dmaven.home=%MAVEN_HOME%" ^ 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 index 07260abfba44..25da736e32bb 100644 --- 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 @@ -201,13 +201,8 @@ public interface ClassRealm extends Closeable { * * @param moduleName the source module name * @param packageName the package to export - * @return {@code true} if the export was applied, {@code false} if the module was not found - * or is in the boot layer (which requires {@code --add-exports} in the launcher) - * @since 4.1.0 */ - default boolean addExports(@Nonnull String moduleName, @Nonnull String packageName) { - return false; - } + default void addExports(@Nonnull String moduleName, @Nonnull String packageName) {} /** * Opens a package from a named JPMS module to this realm's unnamed module for deep reflection, @@ -215,24 +210,14 @@ default boolean addExports(@Nonnull String moduleName, @Nonnull String packageNa * * @param moduleName the source module name * @param packageName the package to open - * @return {@code true} if the open was applied, {@code false} if the module was not found - * or is in the boot layer (which requires {@code --add-opens} in the launcher) - * @since 4.1.0 */ - default boolean addOpens(@Nonnull String moduleName, @Nonnull String packageName) { - return false; - } + default void addOpens(@Nonnull String moduleName, @Nonnull String packageName) {} /** * Adds a reads edge from a named JPMS module to this realm's unnamed module, * allowing the named module to access types in this realm. * * @param moduleName the source module that should read this realm's unnamed module - * @return {@code true} if the reads edge was added, {@code false} if the module was not found - * or is in the boot layer - * @since 4.1.0 */ - default boolean addReads(@Nonnull String moduleName) { - return false; - } + default void addReads(@Nonnull String moduleName) {} } 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 index 69305e6a1335..27ebb12cb2bf 100644 --- 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 @@ -183,10 +183,9 @@ public ModuleLayer.Controller getModuleLayerController() { * @param moduleName the source module name * @param packageName the package to export * @param target the target module to export to - * @return {@code true} if the export was applied successfully */ - public synchronized boolean addExports(String moduleName, String packageName, Module target) { - return applyModuleAccess(moduleName, packageName, target, false); + public synchronized void addExports(String moduleName, String packageName, Module target) { + applyModuleAccess(moduleName, packageName, target, false); } /** @@ -198,10 +197,9 @@ public synchronized boolean addExports(String moduleName, String packageName, Mo * @param moduleName the source module name * @param packageName the package to open * @param target the target module to open to - * @return {@code true} if the open was applied successfully */ - public synchronized boolean addOpens(String moduleName, String packageName, Module target) { - return applyModuleAccess(moduleName, packageName, target, true); + public synchronized void addOpens(String moduleName, String packageName, Module target) { + applyModuleAccess(moduleName, packageName, target, true); } /** @@ -210,34 +208,25 @@ public synchronized boolean addOpens(String moduleName, String packageName, Modu * * @param sourceModuleName the source module name * @param target the target module to read - * @return {@code true} if the reads edge was added successfully */ - public synchronized boolean addReads(String sourceModuleName, Module target) { + public synchronized void addReads(String sourceModuleName, Module target) { Module source = findModule(sourceModuleName); - if (source == null || moduleLayerController == null) { - return false; - } - if (isBootLayerModule(source)) { - return false; + if (source == null || moduleLayerController == null || isBootLayerModule(source)) { + return; } moduleLayerController.addReads(source, target); - return true; } - private boolean applyModuleAccess(String moduleName, String packageName, Module target, boolean open) { + private void applyModuleAccess(String moduleName, String packageName, Module target, boolean open) { Module source = findModule(moduleName); - if (source == null || moduleLayerController == null) { - return false; - } - if (isBootLayerModule(source)) { - return false; + if (source == null || moduleLayerController == null || isBootLayerModule(source)) { + return; } if (open) { moduleLayerController.addOpens(source, packageName, target); } else { moduleLayerController.addExports(source, packageName, target); } - return true; } private Module findModule(String moduleName) { 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 index b1199632846c..0d8107b59347 100644 --- 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 @@ -256,24 +256,30 @@ private void createModuleLayer() { } if (!modulePaths.isEmpty()) { - Set bootModuleNames = - ModuleLayer.boot().modules().stream().map(Module::getName).collect(Collectors.toSet()); - - ModuleFinder finder = ModuleFinder.of(modulePaths.toArray(new Path[0])); - Set newModules = finder.findAll().stream() - .map(ref -> ref.descriptor().name()) - .filter(name -> !bootModuleNames.contains(name)) - .collect(Collectors.toSet()); - - if (!newModules.isEmpty()) { - Configuration cf = ModuleLayer.boot().configuration().resolve(finder, ModuleFinder.of(), newModules); - ClassLoader parent = - foreignClassLoader != null ? foreignClassLoader : ClassLoader.getSystemClassLoader(); - ModuleLayer.Controller controller = - ModuleLayer.defineModulesWithOneLoader(cf, List.of(ModuleLayer.boot()), parent); - ModuleLayer layer = controller.layer(); - foreignClassLoader = layer.findLoader(newModules.iterator().next()); - world.setModuleLayer(layer, controller); + try { + Set bootModuleNames = ModuleLayer.boot().modules().stream() + .map(Module::getName) + .collect(Collectors.toSet()); + + ModuleFinder finder = ModuleFinder.of(modulePaths.toArray(new Path[0])); + Set newModules = finder.findAll().stream() + .map(ref -> ref.descriptor().name()) + .filter(name -> !bootModuleNames.contains(name)) + .collect(Collectors.toSet()); + + if (!newModules.isEmpty()) { + Configuration cf = + ModuleLayer.boot().configuration().resolve(finder, ModuleFinder.of(), newModules); + ClassLoader parent = + foreignClassLoader != null ? foreignClassLoader : ClassLoader.getSystemClassLoader(); + ModuleLayer.Controller controller = + ModuleLayer.defineModulesWithOneLoader(cf, List.of(ModuleLayer.boot()), parent); + ModuleLayer layer = controller.layer(); + foreignClassLoader = layer.findLoader(newModules.iterator().next()); + world.setModuleLayer(layer, controller); + } + } catch (Exception e) { + addModulePathsToClasspath(); } } @@ -299,6 +305,18 @@ private void createModuleLayer() { } } + private void addModulePathsToClasspath() { + if (curRealm != null) { + for (Path path : modulePaths) { + try { + curRealm.addURL(path.toUri().toURL()); + } catch (MalformedURLException e) { + // ignore + } + } + } + } + private Module resolveTargetModule(String targetName, Module unnamedTarget) { if ("ALL-UNNAMED".equals(targetName)) { return unnamedTarget; 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 index 73925feac863..dd0695e7863a 100644 --- 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 @@ -227,16 +227,16 @@ public void addURL(URL url) { super.addURL(url); } - public boolean addExports(String moduleName, String packageName) { - return world.addExports(moduleName, packageName, getUnnamedModule()); + public void addExports(String moduleName, String packageName) { + world.addExports(moduleName, packageName, getUnnamedModule()); } - public boolean addOpens(String moduleName, String packageName) { - return world.addOpens(moduleName, packageName, getUnnamedModule()); + public void addOpens(String moduleName, String packageName) { + world.addOpens(moduleName, packageName, getUnnamedModule()); } - public boolean addReads(String moduleName) { - return world.addReads(moduleName, getUnnamedModule()); + public void addReads(String moduleName) { + world.addReads(moduleName, getUnnamedModule()); } // ---------------------------------------------------------------------- diff --git a/impl/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java b/impl/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java index 7dd9064ff103..3de57d6094cb 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java +++ b/impl/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java @@ -406,9 +406,7 @@ private void applyModuleAccessDirective(org.codehaus.plexus.classworlds.realm.Cl applyExportOrOpen(realm, line.substring("add-opens ".length()).trim(), true); } else if (line.startsWith("add-reads ")) { String module = line.substring("add-reads ".length()).trim(); - if (!realm.addReads(module)) { - logger.debug(" Failed to add-reads {} for realm {}", module, realm.getId()); - } + realm.addReads(module); } else { logger.debug("Unknown module-access directive: {}", line); } @@ -430,14 +428,10 @@ private void applyExportOrOpen(org.codehaus.plexus.classworlds.realm.ClassRealm logger.warn("module-access directive target '{}' ignored, only ALL-UNNAMED is supported", target); } } - boolean applied = open ? realm.addOpens(module, pkg) : realm.addExports(module, pkg); - if (!applied) { - logger.debug( - " Failed to {} {}/{} for realm {}", - open ? "add-opens" : "add-exports", - module, - pkg, - realm.getId()); + if (open) { + realm.addOpens(module, pkg); + } else { + realm.addExports(module, pkg); } } } From ebe6c52bfc22c2396cd95a0ec3b61e93c5d7449a Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Wed, 24 Jun 2026 17:17:07 +0200 Subject: [PATCH 08/13] [ISSUE-11028] Fix CI failures: module probe, IT import, reactor graph - Include --module-path in launcher probe so it fails correctly on JDKs that cannot read the module descriptors (class version 66.0) - Revert CustomComponentConfigurator import to impl ClassRealm (needed for @Override compatibility with AbstractComponentConfigurator) - Add maven-classworlds to ReactorGraph cluster patterns Co-Authored-By: Claude Opus 4.6 --- apache-maven/src/assembly/maven/bin/mvn | 5 +++-- apache-maven/src/assembly/maven/bin/mvn.cmd | 5 +++-- .../maven/plugin/coreit/CustomComponentConfigurator.java | 2 +- src/graph/ReactorGraph.java | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apache-maven/src/assembly/maven/bin/mvn b/apache-maven/src/assembly/maven/bin/mvn index d24bfba0ff22..7340b1e6144b 100755 --- a/apache-maven/src/assembly/maven/bin/mvn +++ b/apache-maven/src/assembly/maven/bin/mvn @@ -230,9 +230,10 @@ LAUNCHER_JAR="$MAVEN_HOME/boot/*" LAUNCHER_CLASS=org.codehaus.plexus.classworlds.launcher.Launcher MODULES_DIR="$MAVEN_HOME/lib/modules" -# Module-path and native access flags require Java 21+ (FFM API) +# Module-path and native access flags require a JDK that can read the module jars. +# The probe includes --module-path so it fails on JDKs that cannot read the module descriptors. MAVEN_MODULE_OPTS="" -if "$JAVACMD" --enable-native-access=org.jline.terminal.ffm -version >/dev/null 2>&1; then +if "$JAVACMD" --module-path "$MODULES_DIR" --add-modules ALL-MODULE-PATH --enable-native-access=org.jline.terminal.ffm -version >/dev/null 2>&1; then MAVEN_MODULE_OPTS="--module-path \"$MODULES_DIR\" --add-modules ALL-MODULE-PATH --enable-native-access=org.jline.terminal.ffm,org.jline.nativ" fi diff --git a/apache-maven/src/assembly/maven/bin/mvn.cmd b/apache-maven/src/assembly/maven/bin/mvn.cmd index 8d4d5b8896c8..795db16d5947 100644 --- a/apache-maven/src/assembly/maven/bin/mvn.cmd +++ b/apache-maven/src/assembly/maven/bin/mvn.cmd @@ -271,9 +271,10 @@ set LAUNCHER_CLASS=org.codehaus.plexus.classworlds.launcher.Launcher set MODULES_DIR="%MAVEN_HOME%\lib\modules" if "%MAVEN_MAIN_CLASS%"=="" @set MAVEN_MAIN_CLASS=org.apache.maven.cling.MavenCling -@rem Module-path and native access flags require Java 21+ (FFM API) +@rem Module-path and native access flags require a JDK that can read the module jars. +@rem The probe includes --module-path so it fails on JDKs that cannot read the module descriptors. set MAVEN_MODULE_OPTS= -"%JAVACMD%" --enable-native-access=org.jline.terminal.ffm -version >NUL 2>&1 +"%JAVACMD%" --module-path %MODULES_DIR% --add-modules ALL-MODULE-PATH --enable-native-access=org.jline.terminal.ffm -version >NUL 2>&1 if not ERRORLEVEL 1 ( set MAVEN_MODULE_OPTS=--module-path %MODULES_DIR% --add-modules ALL-MODULE-PATH --enable-native-access=org.jline.terminal.ffm,org.jline.nativ ) 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 4f3406951ad0..299168546461 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.apache.maven.api.classworlds.ClassRealm; +import org.codehaus.plexus.classworlds.realm.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/src/graph/ReactorGraph.java b/src/graph/ReactorGraph.java index 57daef3f643b..788eeac9dedf 100755 --- a/src/graph/ReactorGraph.java +++ b/src/graph/ReactorGraph.java @@ -42,7 +42,7 @@ public class ReactorGraph { CLUSTER_PATTERNS.put("JLine", Pattern.compile("^org\\.jline:.*")); CLUSTER_PATTERNS.put("Maven API", Pattern.compile("^org\\.apache\\.maven:maven-api-(?!impl).*")); CLUSTER_PATTERNS.put("Maven Resolver", Pattern.compile("^org\\.apache\\.maven\\.resolver:.*")); - CLUSTER_PATTERNS.put("Maven Implementation", Pattern.compile("^org\\.apache\\.maven:maven-(support|impl|di|core|cli|xml|jline|logging|executor|testing):.*")); + CLUSTER_PATTERNS.put("Maven Implementation", Pattern.compile("^org\\.apache\\.maven:maven-(support|impl|di|core|cli|xml|jline|logging|executor|testing|classworlds):.*")); CLUSTER_PATTERNS.put("Maven Compatibility", Pattern.compile("^org\\.apache\\.maven:maven-(artifact|builder-support|compat|embedder|model|model-builder|plugin-api|repository-metadata|resolver-provider|settings|settings-builder|toolchain-builder|toolchain-model):.*")); CLUSTER_PATTERNS.put("Sisu", Pattern.compile("(^org\\.eclipse\\.sisu:.*)|(.*:guice:.*)|(.*:javax.inject:.*)|(.*:javax.annotation-api:.*)|(.*:aopalliance:.*)")); CLUSTER_PATTERNS.put("Plexus", Pattern.compile("^org\\.codehaus\\.plexus:.*")); From 02f41b36fcc762340ffc07c4e7d8a1d14e7239e1 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Wed, 24 Jun 2026 20:21:53 +0200 Subject: [PATCH 09/13] [ISSUE-11028] Use impl ClassWorld type in Cling main() methods for reflection compat Launcher and maven-executor use reflection to find main(String[], ClassWorld) using the impl ClassWorld class. The API ClassWorld type doesn't match, causing NoSuchMethodException at runtime. Use FQ org.codehaus.plexus.classworlds.ClassWorld in all main() method parameters while keeping the API import for ProtoLookup mapping. Co-Authored-By: Claude Opus 4.6 --- .../src/main/java/org/apache/maven/cling/MavenCling.java | 4 ++-- .../src/main/java/org/apache/maven/cling/MavenEncCling.java | 4 ++-- .../src/main/java/org/apache/maven/cling/MavenShellCling.java | 4 ++-- .../src/main/java/org/apache/maven/cling/MavenUpCling.java | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) 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 37c68b7721aa..99d1a0a75c8a 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenCling.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenCling.java @@ -47,7 +47,7 @@ public static void main(String[] args) throws IOException { /** * ClassWorld Launcher "enhanced" entry point: returning exitCode and accepts Class World. */ - public static int main(String[] args, ClassWorld world) throws IOException { + public static int main(String[] args, org.codehaus.plexus.classworlds.ClassWorld world) throws IOException { return new MavenCling(world).run(args, null, null, null, false); } @@ -56,7 +56,7 @@ public static int main(String[] args, ClassWorld world) throws IOException { */ public static int main( String[] args, - ClassWorld world, + org.codehaus.plexus.classworlds.ClassWorld world, @Nullable InputStream stdIn, @Nullable OutputStream stdOut, @Nullable OutputStream stdErr) diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenEncCling.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenEncCling.java index 2c8a0cba0b0e..1b5b6a2c72fa 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenEncCling.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenEncCling.java @@ -47,7 +47,7 @@ public static void main(String[] args) throws IOException { /** * ClassWorld Launcher "enhanced" entry point: returning exitCode and accepts Class World. */ - public static int main(String[] args, ClassWorld world) throws IOException { + public static int main(String[] args, org.codehaus.plexus.classworlds.ClassWorld world) throws IOException { return new MavenEncCling(world).run(args, null, null, null, false); } @@ -56,7 +56,7 @@ public static int main(String[] args, ClassWorld world) throws IOException { */ public static int main( String[] args, - ClassWorld world, + org.codehaus.plexus.classworlds.ClassWorld world, @Nullable InputStream stdIn, @Nullable OutputStream stdOut, @Nullable OutputStream stdErr) diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenShellCling.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenShellCling.java index df1d6b363297..399553cd078c 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenShellCling.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenShellCling.java @@ -47,7 +47,7 @@ public static void main(String[] args) throws IOException { /** * ClassWorld Launcher "enhanced" entry point: returning exitCode and accepts Class World. */ - public static int main(String[] args, ClassWorld world) throws IOException { + public static int main(String[] args, org.codehaus.plexus.classworlds.ClassWorld world) throws IOException { return new MavenShellCling(world).run(args, null, null, null, false); } @@ -56,7 +56,7 @@ public static int main(String[] args, ClassWorld world) throws IOException { */ public static int main( String[] args, - ClassWorld world, + org.codehaus.plexus.classworlds.ClassWorld world, @Nullable InputStream stdIn, @Nullable OutputStream stdOut, @Nullable OutputStream stdErr) diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenUpCling.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenUpCling.java index 43cb3f90fba0..6d82b98ba8de 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenUpCling.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenUpCling.java @@ -47,7 +47,7 @@ public static void main(String[] args) throws IOException { /** * ClassWorld Launcher "enhanced" entry point: returning exitCode and accepts Class World. */ - public static int main(String[] args, ClassWorld world) throws IOException { + public static int main(String[] args, org.codehaus.plexus.classworlds.ClassWorld world) throws IOException { return new MavenUpCling(world).run(args, null, null, null, false); } @@ -56,7 +56,7 @@ public static int main(String[] args, ClassWorld world) throws IOException { */ public static int main( String[] args, - ClassWorld world, + org.codehaus.plexus.classworlds.ClassWorld world, @Nullable InputStream stdIn, @Nullable OutputStream stdOut, @Nullable OutputStream stdErr) From fa6434d60cd76aa87ce2d392a94005c44baa7d92 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Wed, 24 Jun 2026 20:29:24 +0200 Subject: [PATCH 10/13] [ISSUE-11028] Fix boot classpath on Windows/MinGW Build the classpath from individual jar files instead of using a wildcard, since cygpath --windows with a wildcard in the path can cause issues on MinGW/Git Bash. Also convert MODULES_DIR to Windows format and construct MAVEN_MODULE_OPTS after path conversion. Co-Authored-By: Claude Opus 4.6 --- apache-maven/src/assembly/maven/bin/mvn | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/apache-maven/src/assembly/maven/bin/mvn b/apache-maven/src/assembly/maven/bin/mvn index 7340b1e6144b..d1da8cedf99f 100755 --- a/apache-maven/src/assembly/maven/bin/mvn +++ b/apache-maven/src/assembly/maven/bin/mvn @@ -226,25 +226,34 @@ fi if [ -n "$MAVEN_DEBUG_SCRIPT" ]; then echo "[DEBUG] Final MAVEN_OPTS: $MAVEN_OPTS" >&2 fi -LAUNCHER_JAR="$MAVEN_HOME/boot/*" +LAUNCHER_JAR="" +for _jar in "$MAVEN_HOME"/boot/*.jar; do + [ -f "$_jar" ] && LAUNCHER_JAR="${LAUNCHER_JAR:+$LAUNCHER_JAR:}$_jar" +done LAUNCHER_CLASS=org.codehaus.plexus.classworlds.launcher.Launcher MODULES_DIR="$MAVEN_HOME/lib/modules" # Module-path and native access flags require a JDK that can read the module jars. # The probe includes --module-path so it fails on JDKs that cannot read the module descriptors. -MAVEN_MODULE_OPTS="" +USE_MODULE_PATH=false if "$JAVACMD" --module-path "$MODULES_DIR" --add-modules ALL-MODULE-PATH --enable-native-access=org.jline.terminal.ffm -version >/dev/null 2>&1; then - MAVEN_MODULE_OPTS="--module-path \"$MODULES_DIR\" --add-modules ALL-MODULE-PATH --enable-native-access=org.jline.terminal.ffm,org.jline.nativ" + USE_MODULE_PATH=true fi # For Cygwin and MinGW, switch paths to Windows format before running java(1) command if $cygwin || $mingw ; then [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --windows "$JAVA_HOME"` - LAUNCHER_JAR=`cygpath --windows "$LAUNCHER_JAR"` + LAUNCHER_JAR=`cygpath --windows --path "$LAUNCHER_JAR"` CLASSWORLDS_CONF=`cygpath --windows "$CLASSWORLDS_CONF"` MAVEN_HOME=`cygpath --windows "$MAVEN_HOME"` MAVEN_PROJECTBASEDIR=`cygpath --windows "$MAVEN_PROJECTBASEDIR"` + MODULES_DIR=`cygpath --windows "$MODULES_DIR"` +fi + +MAVEN_MODULE_OPTS="" +if $USE_MODULE_PATH; then + MAVEN_MODULE_OPTS="--module-path \"$MODULES_DIR\" --add-modules ALL-MODULE-PATH --enable-native-access=org.jline.terminal.ffm,org.jline.nativ" fi handle_args() { From 779a1a453485f1493c85f79fc2e2a81b2a29d942 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Wed, 24 Jun 2026 20:32:27 +0200 Subject: [PATCH 11/13] [ISSUE-11028] Temporary: enable debug logging for Windows CI diagnosis Co-Authored-By: Claude Opus 4.6 --- .github/workflows/maven.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 13ca49d2f73a..966f83b3fd18 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -214,7 +214,7 @@ jobs: - name: Build with downloaded Maven shell: bash - run: mvn verify -Papache-release -Dgpg.skip=true -e -B -V + run: MAVEN_DEBUG_SCRIPT=1 mvn verify -Papache-release -Dgpg.skip=true -e -B -V - name: Build site with downloaded Maven shell: bash From f88911449e78110e844acb494e43a025127e447b Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Wed, 24 Jun 2026 21:00:51 +0200 Subject: [PATCH 12/13] [ISSUE-11028] Preserve binary compat for ClassRealmManagerDelegate ClassRealmManagerDelegate.setupRealm() is an SPI that extensions implement. Changing its ClassRealm parameter type from impl to API breaks pre-compiled extensions. Revert the interface to use the impl ClassRealm type and cast in DefaultClassRealmManager. Also revert IT test sources and remove temporary CI debug logging. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/maven.yml | 2 +- .../org/apache/maven/classrealm/ClassRealmManagerDelegate.java | 2 +- .../org/apache/maven/classrealm/DefaultClassRealmManager.java | 2 +- .../extension/TestClassRealmManagerDelegate.java | 2 +- .../its/core_extensions/TestClassRealmManagerDelegate.java | 2 +- .../its/core_extensions/TestClassRealmManagerDelegate.java | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 966f83b3fd18..13ca49d2f73a 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -214,7 +214,7 @@ jobs: - name: Build with downloaded Maven shell: bash - run: MAVEN_DEBUG_SCRIPT=1 mvn verify -Papache-release -Dgpg.skip=true -e -B -V + run: mvn verify -Papache-release -Dgpg.skip=true -e -B -V - name: Build site with downloaded Maven shell: bash diff --git a/impl/maven-core/src/main/java/org/apache/maven/classrealm/ClassRealmManagerDelegate.java b/impl/maven-core/src/main/java/org/apache/maven/classrealm/ClassRealmManagerDelegate.java index 4dc2e9109058..d56eac5864f6 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/classrealm/ClassRealmManagerDelegate.java +++ b/impl/maven-core/src/main/java/org/apache/maven/classrealm/ClassRealmManagerDelegate.java @@ -18,7 +18,7 @@ */ package org.apache.maven.classrealm; -import org.apache.maven.api.classworlds.ClassRealm; +import org.codehaus.plexus.classworlds.realm.ClassRealm; /** * ClassRealmManagerDelegate is used to perform addition configuration of diff --git a/impl/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java b/impl/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java index 3de57d6094cb..e69018aa8a87 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java +++ b/impl/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java @@ -311,7 +311,7 @@ private void callDelegates( for (ClassRealmManagerDelegate delegate : delegates) { try { - delegate.setupRealm(classRealm, request); + delegate.setupRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) classRealm, request); } catch (Exception e) { logger.error( delegate.getClass().getName() + " failed to setup class realm " + classRealm + ": " 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 af8955c21a8b..805e9a8ba6e6 100644 --- a/its/core-it-suite/src/test/resources/mng-5530-mojo-execution-scope/extension/src/main/java/org/apache/maven/its/mng5530/mojoexecutionscope/extension/TestClassRealmManagerDelegate.java +++ b/its/core-it-suite/src/test/resources/mng-5530-mojo-execution-scope/extension/src/main/java/org/apache/maven/its/mng5530/mojoexecutionscope/extension/TestClassRealmManagerDelegate.java @@ -35,7 +35,7 @@ import javax.inject.Named; -import org.apache.maven.api.classworlds.ClassRealm; +import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.apache.maven.classrealm.ClassRealmManagerDelegate; import org.apache.maven.classrealm.ClassRealmRequest; import org.apache.maven.classrealm.ClassRealmRequest.RealmType; 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 44dd46fdd3aa..287c936c7ee3 100644 --- a/its/core-it-suite/src/test/resources/mng-5771-core-extensions/repo-src/maven-it-core-extensions-no-descriptor/src/main/java/org/apache/maven/its/core_extensions/TestClassRealmManagerDelegate.java +++ b/its/core-it-suite/src/test/resources/mng-5771-core-extensions/repo-src/maven-it-core-extensions-no-descriptor/src/main/java/org/apache/maven/its/core_extensions/TestClassRealmManagerDelegate.java @@ -18,7 +18,7 @@ */ package org.apache.maven.its.core_extensions; -import org.apache.maven.api.classworlds.ClassRealm; +import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.apache.maven.classrealm.ClassRealmManagerDelegate; import org.apache.maven.classrealm.ClassRealmRequest; import org.codehaus.plexus.component.annotations.Component; 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 44dd46fdd3aa..287c936c7ee3 100644 --- a/its/core-it-suite/src/test/resources/mng-5771-core-extensions/repo-src/maven-it-core-extensions/src/main/java/org/apache/maven/its/core_extensions/TestClassRealmManagerDelegate.java +++ b/its/core-it-suite/src/test/resources/mng-5771-core-extensions/repo-src/maven-it-core-extensions/src/main/java/org/apache/maven/its/core_extensions/TestClassRealmManagerDelegate.java @@ -18,7 +18,7 @@ */ package org.apache.maven.its.core_extensions; -import org.apache.maven.api.classworlds.ClassRealm; +import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.apache.maven.classrealm.ClassRealmManagerDelegate; import org.apache.maven.classrealm.ClassRealmRequest; import org.codehaus.plexus.component.annotations.Component; From 29413dc59d416b92275b81c63f0710f9df8368b3 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Wed, 24 Jun 2026 21:04:56 +0200 Subject: [PATCH 13/13] [ISSUE-11028] Include JLine jars in lib/ for embedded executor compat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JLine jars were exclusively placed in lib/modules/ for JPMS module path usage, but the EmbeddedMavenExecutor constructs its classpath from lib/ only. Include JLine in both locations — the JVM's module system prefers module path over classpath, so there's no conflict when both paths are active. Co-Authored-By: Claude Opus 4.6 --- apache-maven/src/assembly/component.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/apache-maven/src/assembly/component.xml b/apache-maven/src/assembly/component.xml index 9a17e1338f5a..ae86c5639f6b 100644 --- a/apache-maven/src/assembly/component.xml +++ b/apache-maven/src/assembly/component.xml @@ -42,7 +42,6 @@ under the License. org.apache.maven:maven-api-classworlds org.apache.maven:maven-classworlds - org.jline:*