diff --git a/apache-maven/src/assembly/component.xml b/apache-maven/src/assembly/component.xml index 4d75c9a38ca8..0849b7caf3b6 100644 --- a/apache-maven/src/assembly/component.xml +++ b/apache-maven/src/assembly/component.xml @@ -23,14 +23,15 @@ under the License. false boot - org.codehaus.plexus:plexus-classworlds + org.apache.maven:maven-classworlds + false lib - org.codehaus.plexus:plexus-classworlds + org.apache.maven:maven-classworlds diff --git a/apache-maven/src/assembly/maven/bin/mvn b/apache-maven/src/assembly/maven/bin/mvn index 8559d47af557..3caad69ab773 100755 --- a/apache-maven/src/assembly/maven/bin/mvn +++ b/apache-maven/src/assembly/maven/bin/mvn @@ -190,7 +190,7 @@ concat_lines() { MAVEN_PROJECTBASEDIR="`find_maven_basedir "$@"`" MAVEN_OPTS="$MAVEN_OPTS `concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config"`" -LAUNCHER_JAR=`echo "$MAVEN_HOME"/boot/plexus-classworlds-*.jar` +LAUNCHER_JAR=`echo "$MAVEN_HOME"/boot/maven-classworlds-*.jar` LAUNCHER_CLASS=org.codehaus.plexus.classworlds.launcher.Launcher # For Cygwin and MinGW, switch paths to Windows format before running java(1) command diff --git a/apache-maven/src/assembly/maven/bin/mvn.cmd b/apache-maven/src/assembly/maven/bin/mvn.cmd index a3e8600df3d1..daa33e7cf8cb 100644 --- a/apache-maven/src/assembly/maven/bin/mvn.cmd +++ b/apache-maven/src/assembly/maven/bin/mvn.cmd @@ -247,7 +247,7 @@ goto processArgs :endHandleArgs call :processArgs %* -for %%i in ("%MAVEN_HOME%"\boot\plexus-classworlds-*) do set LAUNCHER_JAR="%%i" +for %%i in ("%MAVEN_HOME%"\boot\maven-classworlds-*) do set LAUNCHER_JAR="%%i" set LAUNCHER_CLASS=org.codehaus.plexus.classworlds.launcher.Launcher if "%MAVEN_MAIN_CLASS%"=="" @set MAVEN_MAIN_CLASS=org.apache.maven.cling.MavenCling diff --git a/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm b/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm index 02ddf974cf45..ce75af2f3176 100644 --- a/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm +++ b/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm @@ -67,7 +67,7 @@ subject to the terms and conditions of the following licenses: #* *##end #* *### #* *### Classworlds is in boot directory, not in lib -#* *##if ( $project.artifact.artifactId == "plexus-classworlds" ) +#* *##if ( $project.artifact.artifactId == "maven-classworlds" ) #* *##set ( $directory = 'boot' ) #* *##end #* *### diff --git a/api/maven-api-classworlds/pom.xml b/api/maven-api-classworlds/pom.xml new file mode 100644 index 000000000000..e19dd9869530 --- /dev/null +++ b/api/maven-api-classworlds/pom.xml @@ -0,0 +1,39 @@ + + + + 4.0.0 + + org.apache.maven + maven-api + 4.1.0-SNAPSHOT + + + maven-api-classworlds + Maven 4 API :: Classworlds + Maven 4 API for class loading realms and isolation. + + + + org.apache.maven + maven-api-annotations + + + + diff --git a/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassRealm.java b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassRealm.java new file mode 100644 index 000000000000..182b8a4326e1 --- /dev/null +++ b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassRealm.java @@ -0,0 +1,197 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.api.classworlds; + +import java.io.Closeable; +import java.net.URL; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * A class loading realm that provides isolated class loading with controlled imports and exports. + *

+ * A ClassRealm represents an isolated class loading environment with its own classpath + * and controlled access to classes from other realms through imports. + *

+ * + * @since 4.1.0 + */ +@Experimental +public interface ClassRealm extends Closeable { + + /** + * Returns the unique identifier for this realm. + * + * @return the realm identifier + */ + @Nonnull + String getId(); + + /** + * Returns the class world that contains this realm. + * + * @return the parent class world + */ + @Nonnull + ClassWorld getWorld(); + + /** + * Returns the underlying ClassLoader for this realm. + *

+ * This method allows access to the actual ClassLoader implementation + * while maintaining API abstraction. + *

+ * + * @return the underlying ClassLoader + */ + @Nonnull + ClassLoader getClassLoader(); + + /** + * Returns the class loading strategy used by this realm. + * + * @return the strategy + */ + @Nonnull + Strategy getStrategy(); + + /** + * Adds a URL to this realm's classpath. + * + * @param url the URL to add + */ + void addURL(@Nonnull URL url); + + /** + * Returns the URLs in this realm's classpath. + * + * @return array of URLs in the classpath + */ + @Nonnull + URL[] getURLs(); + + /** + * Imports classes from the specified realm for the given package. + * + * @param realmId the identifier of the realm to import from + * @param packageName the package name to import (supports wildcards) + * @throws NoSuchRealmException if the specified realm doesn't exist + */ + void importFrom(@Nonnull String realmId, @Nonnull String packageName) throws NoSuchRealmException; + + /** + * Imports classes from the specified class loader for the given package. + * + * @param classLoader the class loader to import from + * @param packageName the package name to import (supports wildcards) + */ + void importFrom(@Nonnull ClassLoader classLoader, @Nonnull String packageName); + + /** + * Returns the class loader that would handle the specified class name through imports. + * + * @param name the class name + * @return the import class loader, or null if no import matches + */ + @Nullable + ClassLoader getImportClassLoader(@Nonnull String name); + + // Note: getImportRealms method is not included in the API interface + // to avoid conflicts with the existing implementation signature + + /** + * Sets the parent class loader for this realm. + * + * @param parentClassLoader the parent class loader, may be null + */ + void setParentClassLoader(@Nullable ClassLoader parentClassLoader); + + /** + * Returns the parent class loader for this realm. + * + * @return the parent class loader, may be null + */ + @Nullable + ClassLoader getParentClassLoader(); + + // Note: setParentRealm method is not included in the API interface + // to avoid conflicts with the existing implementation signature + + // Note: getParentRealm method is not included in the API interface + // to avoid conflicts with the existing implementation signature + + // Note: createChildRealm method is not included in the API interface + // to avoid conflicts with the existing implementation signature + + /** + * Loads a class from this realm only (not from imports or parent). + * + * @param name the class name + * @return the loaded class, or null if not found + */ + @Nullable + Class loadClassFromSelf(@Nonnull String name); + + /** + * Loads a class from imported realms/classloaders. + * + * @param name the class name + * @return the loaded class, or null if not found + */ + @Nullable + Class loadClassFromImport(@Nonnull String name); + + /** + * Loads a class from the parent class loader. + * + * @param name the class name + * @return the loaded class, or null if not found + */ + @Nullable + Class loadClassFromParent(@Nonnull String name); + + /** + * Loads a resource from this realm only (not from imports or parent). + * + * @param name the resource name + * @return the resource URL, or null if not found + */ + @Nullable + URL loadResourceFromSelf(@Nonnull String name); + + /** + * Loads a resource from imported realms/classloaders. + * + * @param name the resource name + * @return the resource URL, or null if not found + */ + @Nullable + URL loadResourceFromImport(@Nonnull String name); + + /** + * Loads a resource from the parent class loader. + * + * @param name the resource name + * @return the resource URL, or null if not found + */ + @Nullable + URL loadResourceFromParent(@Nonnull String name); +} diff --git a/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassWorld.java b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassWorld.java new file mode 100644 index 000000000000..f0f4f876c7ef --- /dev/null +++ b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassWorld.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.api.classworlds; + +import java.io.Closeable; +import java.util.function.Predicate; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * A collection of {@link ClassRealm}s, indexed by id. + *

+ * A ClassWorld provides a container for managing multiple class loading realms, + * each with their own classpath and import/export relationships. + *

+ * + * @since 4.1.0 + */ +@Experimental +public interface ClassWorld extends Closeable { + + /** + * Creates a new class realm with the specified id. + * + * @param id the unique identifier for the realm + * @return the newly created class realm + * @throws DuplicateRealmException if a realm with the same id already exists + */ + @Nonnull + ClassRealm newRealm(@Nonnull String id) throws DuplicateRealmException; + + /** + * Creates a new class realm with the specified id and base class loader. + * + * @param id the unique identifier for the realm + * @param classLoader the base class loader for the realm, may be null + * @return the newly created class realm + * @throws DuplicateRealmException if a realm with the same id already exists + */ + @Nonnull + ClassRealm newRealm(@Nonnull String id, @Nullable ClassLoader classLoader) throws DuplicateRealmException; + + /** + * Creates a new filtered class realm with the specified id, base class loader, and filter. + * + * @param id the unique identifier for the realm + * @param classLoader the base class loader for the realm, may be null + * @param filter the filter to apply to class loading, may be null + * @return the newly created class realm + * @throws DuplicateRealmException if a realm with the same id already exists + */ + @Nonnull + ClassRealm newRealm(@Nonnull String id, @Nullable ClassLoader classLoader, @Nullable Predicate filter) + throws DuplicateRealmException; + + /** + * Retrieves the class realm with the specified id. + * + * @param id the realm identifier + * @return the class realm + * @throws NoSuchRealmException if no realm with the specified id exists + */ + @Nonnull + ClassRealm getRealm(@Nonnull String id) throws NoSuchRealmException; + + /** + * Retrieves the class realm with the specified id, or null if it doesn't exist. + * + * @param id the realm identifier + * @return the class realm, or null if not found + */ + @Nullable + ClassRealm getClassRealm(@Nonnull String id); + + // Note: getRealms method is not included in the API interface + // to avoid conflicts with the existing implementation signature + + /** + * Disposes of the class realm with the specified id. + * + * @param id the realm identifier + * @throws NoSuchRealmException if no realm with the specified id exists + */ + void disposeRealm(@Nonnull String id) throws NoSuchRealmException; + + /** + * Adds a listener to be notified of realm lifecycle events. + * + * @param listener the listener to add + */ + void addListener(@Nonnull ClassWorldListener listener); + + /** + * Removes a listener from realm lifecycle event notifications. + * + * @param listener the listener to remove + */ + void removeListener(@Nonnull ClassWorldListener listener); +} diff --git a/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassWorldException.java b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassWorldException.java new file mode 100644 index 000000000000..e18d67267d5e --- /dev/null +++ b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassWorldException.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.api.classworlds; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * Base exception for class world related errors. + *

+ * This is the root exception type for all class world operations. + * Specific error conditions are represented by subclasses. + *

+ * + * @since 4.1.0 + */ +@Experimental +public class ClassWorldException extends Exception { + + /** + * The class world associated with this exception. + */ + private final ClassWorld world; + + /** + * Constructs a new ClassWorldException. + * + * @param world the class world associated with this exception + */ + public ClassWorldException(@Nonnull ClassWorld world) { + this.world = world; + } + + /** + * Constructs a new ClassWorldException with the specified detail message. + * + * @param world the class world associated with this exception + * @param message the detail message + */ + public ClassWorldException(@Nonnull ClassWorld world, @Nullable String message) { + super(message); + this.world = world; + } + + /** + * Constructs a new ClassWorldException with the specified detail message and cause. + * + * @param world the class world associated with this exception + * @param message the detail message + * @param cause the cause + */ + public ClassWorldException(@Nonnull ClassWorld world, @Nullable String message, @Nullable Throwable cause) { + super(message, cause); + this.world = world; + } + + /** + * Constructs a new ClassWorldException with the specified cause. + * + * @param world the class world associated with this exception + * @param cause the cause + */ + public ClassWorldException(@Nonnull ClassWorld world, @Nullable Throwable cause) { + super(cause); + this.world = world; + } + + /** + * Returns the class world associated with this exception. + * + * @return the class world + */ + @Nonnull + public ClassWorld getWorld() { + return world; + } +} diff --git a/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassWorldListener.java b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassWorldListener.java new file mode 100644 index 000000000000..5904b23d87d0 --- /dev/null +++ b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/ClassWorldListener.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.api.classworlds; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Listener interface for class realm lifecycle events. + *

+ * Implementations of this interface can be registered with a {@link ClassWorld} + * to receive notifications when realms are created or disposed. + *

+ * + * @since 4.1.0 + */ +@Experimental +public interface ClassWorldListener { + + /** + * Called when a new class realm is created. + * + * @param realm the newly created realm + */ + void realmCreated(@Nonnull ClassRealm realm); + + /** + * Called when a class realm is disposed. + * + * @param realm the disposed realm + */ + void realmDisposed(@Nonnull ClassRealm realm); +} diff --git a/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/DuplicateRealmException.java b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/DuplicateRealmException.java new file mode 100644 index 000000000000..7d9ca993b288 --- /dev/null +++ b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/DuplicateRealmException.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.api.classworlds; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Exception thrown when attempting to create a class realm with an identifier + * that already exists in the class world. + * + * @since 4.1.0 + */ +@Experimental +public class DuplicateRealmException extends ClassWorldException { + + /** + * The duplicate realm identifier. + */ + private final String id; + + /** + * Constructs a new DuplicateRealmException. + * + * @param world the class world + * @param id the duplicate realm identifier + */ + public DuplicateRealmException(@Nonnull ClassWorld world, @Nonnull String id) { + super(world, "Duplicate realm: " + id); + this.id = id; + } + + /** + * Returns the duplicate realm identifier. + * + * @return the realm identifier + */ + @Nonnull + public String getId() { + return id; + } +} diff --git a/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/NoSuchRealmException.java b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/NoSuchRealmException.java new file mode 100644 index 000000000000..223fd8df9db9 --- /dev/null +++ b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/NoSuchRealmException.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.api.classworlds; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Exception thrown when attempting to retrieve a class realm with an identifier + * that does not exist in the class world. + * + * @since 4.1.0 + */ +@Experimental +public class NoSuchRealmException extends ClassWorldException { + + /** + * The missing realm identifier. + */ + private final String id; + + /** + * Constructs a new NoSuchRealmException. + * + * @param world the class world + * @param id the missing realm identifier + */ + public NoSuchRealmException(@Nonnull ClassWorld world, @Nonnull String id) { + super(world, "No such realm: " + id); + this.id = id; + } + + /** + * Returns the missing realm identifier. + * + * @return the realm identifier + */ + @Nonnull + public String getId() { + return id; + } +} diff --git a/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/Strategy.java b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/Strategy.java new file mode 100644 index 000000000000..371619f4feda --- /dev/null +++ b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/Strategy.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.api.classworlds; + +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * A strategy for defining how classes and resources are located in class realms. + *

+ * Different strategies can implement different class loading behaviors, such as + * parent-first, child-first, or custom delegation patterns. + *

+ * + * @since 4.1.0 + */ +@Experimental +public interface Strategy { + + /** + * Loads a class using this strategy. + * + * @param name the fully qualified class name + * @return the loaded class + * @throws ClassNotFoundException if the class cannot be found + */ + @Nonnull + Class loadClass(@Nonnull String name) throws ClassNotFoundException; + + /** + * Finds a resource using this strategy. + * + * @param name the resource name + * @return the resource URL, or null if not found + */ + @Nullable + URL getResource(@Nonnull String name); + + /** + * Finds all resources with the given name using this strategy. + * + * @param name the resource name + * @return an enumeration of resource URLs + * @throws IOException if an I/O error occurs + */ + @Nonnull + Enumeration getResources(@Nonnull String name) throws IOException; + + /** + * Returns the class realm that this strategy operates on. + * + * @return the associated class realm + */ + @Nonnull + ClassRealm getRealm(); +} diff --git a/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/package-info.java b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/package-info.java new file mode 100644 index 000000000000..f0b5a0763b88 --- /dev/null +++ b/api/maven-api-classworlds/src/main/java/org/apache/maven/api/classworlds/package-info.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Maven 4 API for class loading realms and isolation. + *

+ * This package provides the public API for Maven's class loading system, which allows + * for isolated class loading environments (realms) with controlled imports and exports + * between them. + *

+ *

+ * Key concepts: + *

+ * + *

+ * This API follows Maven 4 conventions: + *

+ * + * + * @since 4.1.0 + */ +@org.apache.maven.api.annotations.Experimental +package org.apache.maven.api.classworlds; diff --git a/api/pom.xml b/api/pom.xml index b90c9f605c81..2c917f360293 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -33,6 +33,7 @@ maven-api-annotations + maven-api-classworlds maven-api-di maven-api-xml maven-api-model diff --git a/compat/maven-compat/pom.xml b/compat/maven-compat/pom.xml index f6919bb5306e..918118d29647 100644 --- a/compat/maven-compat/pom.xml +++ b/compat/maven-compat/pom.xml @@ -117,8 +117,12 @@ under the License. - org.codehaus.plexus - plexus-classworlds + org.apache.maven + maven-api-classworlds + + + org.apache.maven + maven-classworlds org.codehaus.plexus diff --git a/compat/maven-embedder/pom.xml b/compat/maven-embedder/pom.xml index b1136b19c845..916e325a6068 100644 --- a/compat/maven-embedder/pom.xml +++ b/compat/maven-embedder/pom.xml @@ -146,8 +146,12 @@ under the License. - org.codehaus.plexus - plexus-classworlds + org.apache.maven + maven-api-classworlds + + + org.apache.maven + maven-classworlds org.codehaus.plexus diff --git a/compat/maven-embedder/src/main/java/org/apache/maven/cli/CliRequest.java b/compat/maven-embedder/src/main/java/org/apache/maven/cli/CliRequest.java index 6d54be4ce054..0b4c45f0056b 100644 --- a/compat/maven-embedder/src/main/java/org/apache/maven/cli/CliRequest.java +++ b/compat/maven-embedder/src/main/java/org/apache/maven/cli/CliRequest.java @@ -23,9 +23,9 @@ import java.util.Properties; import org.apache.commons.cli.CommandLine; +import org.apache.maven.api.classworlds.ClassWorld; import org.apache.maven.execution.DefaultMavenExecutionRequest; import org.apache.maven.execution.MavenExecutionRequest; -import org.codehaus.plexus.classworlds.ClassWorld; /** * CliRequest diff --git a/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java b/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java index 7f9e5f4a13ba..cd000c514165 100644 --- a/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java +++ b/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java @@ -59,6 +59,9 @@ import org.apache.maven.InternalErrorException; import org.apache.maven.Maven; import org.apache.maven.api.Constants; +import org.apache.maven.api.classworlds.ClassRealm; +import org.apache.maven.api.classworlds.ClassWorld; +import org.apache.maven.api.classworlds.NoSuchRealmException; import org.apache.maven.api.cli.extensions.CoreExtension; import org.apache.maven.api.cli.extensions.InputSource; import org.apache.maven.api.services.MessageBuilder; @@ -117,9 +120,6 @@ import org.codehaus.plexus.DefaultPlexusContainer; import org.codehaus.plexus.PlexusConstants; import org.codehaus.plexus.PlexusContainer; -import org.codehaus.plexus.classworlds.ClassWorld; -import org.codehaus.plexus.classworlds.realm.ClassRealm; -import org.codehaus.plexus.classworlds.realm.NoSuchRealmException; import org.codehaus.plexus.component.repository.exception.ComponentLookupException; import org.codehaus.plexus.components.secdispatcher.SecDispatcher; import org.codehaus.plexus.logging.LoggerManager; @@ -225,7 +225,7 @@ public int doMain(String[] args, String workingDirectory, PrintStream stdout, Pr final Set realms; if (classWorld != null) { realms = new HashSet<>(); - for (ClassRealm realm : classWorld.getRealms()) { + for (ClassRealm realm : ((org.codehaus.plexus.classworlds.ClassWorld) classWorld).getRealms()) { realms.add(realm.getId()); } } else { @@ -246,7 +246,8 @@ public int doMain(String[] args, String workingDirectory, PrintStream stdout, Pr return doMain(cliRequest); } finally { if (classWorld != null) { - for (ClassRealm realm : new ArrayList<>(classWorld.getRealms())) { + for (ClassRealm realm : + new ArrayList<>(((org.codehaus.plexus.classworlds.ClassWorld) classWorld).getRealms())) { String realmId = realm.getId(); if (!realms.contains(realmId)) { try { @@ -673,13 +674,16 @@ void properties(CliRequest cliRequest) throws Exception { PlexusContainer container(CliRequest cliRequest) throws Exception { if (cliRequest.classWorld == null) { - cliRequest.classWorld = - new ClassWorld("plexus.core", Thread.currentThread().getContextClassLoader()); + cliRequest.classWorld = new org.codehaus.plexus.classworlds.ClassWorld( + "plexus.core", Thread.currentThread().getContextClassLoader()); } ClassRealm coreRealm = cliRequest.classWorld.getClassRealm("plexus.core"); if (coreRealm == null) { - coreRealm = cliRequest.classWorld.getRealms().iterator().next(); + coreRealm = ((org.codehaus.plexus.classworlds.ClassWorld) cliRequest.classWorld) + .getRealms() + .iterator() + .next(); } List extClassPath = parseExtClasspath(cliRequest); @@ -691,8 +695,8 @@ PlexusContainer container(CliRequest cliRequest) throws Exception { ClassRealm containerRealm = setupContainerRealm(cliRequest.classWorld, coreRealm, extClassPath, extensions); ContainerConfiguration cc = new DefaultContainerConfiguration() - .setClassWorld(cliRequest.classWorld) - .setRealm(containerRealm) + .setClassWorld((org.codehaus.plexus.classworlds.ClassWorld) cliRequest.classWorld) + .setRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) containerRealm) .setClassPathScanning(PlexusConstants.SCANNING_INDEX) .setAutoWiring(true) .setJSR250Lifecycle(true) @@ -708,7 +712,7 @@ PlexusContainer container(CliRequest cliRequest) throws Exception { final CoreExports exports = new CoreExports(containerRealm, exportedArtifacts, exportedPackages); - Thread.currentThread().setContextClassLoader(containerRealm); + Thread.currentThread().setContextClassLoader(containerRealm.getClassLoader()); DefaultPlexusContainer container = new DefaultPlexusContainer(cc, new AbstractModule() { @Override @@ -734,12 +738,14 @@ protected void configure() { }; for (CoreExtensionEntry extension : extensions) { container.discoverComponents( - extension.getClassRealm(), + (org.codehaus.plexus.classworlds.realm.ClassRealm) extension.getClassRealm(), new AbstractModule() { @Override protected void configure() { try { - container.lookup(Injector.class).discover(extension.getClassRealm()); + container + .lookup(Injector.class) + .discover(extension.getClassRealm().getClassLoader()); } catch (Throwable e) { // ignore e.printStackTrace(); @@ -807,8 +813,8 @@ private List loadCoreExtensions( } ContainerConfiguration cc = new DefaultContainerConfiguration() // - .setClassWorld(cliRequest.classWorld) // - .setRealm(containerRealm) // + .setClassWorld((org.codehaus.plexus.classworlds.ClassWorld) cliRequest.classWorld) // + .setRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) containerRealm) // .setClassPathScanning(PlexusConstants.SCANNING_INDEX) // .setAutoWiring(true) // .setJSR250Lifecycle(true) // @@ -874,7 +880,8 @@ private ClassRealm setupContainerRealm( if (!extClassPath.isEmpty() || !extensions.isEmpty()) { ClassRealm extRealm = classWorld.newRealm("maven.ext", null); - extRealm.setParentRealm(coreRealm); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) extRealm) + .setParentRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) coreRealm); slf4jLogger.debug("Populating class realm '{}'", extRealm.getId()); @@ -888,11 +895,11 @@ private ClassRealm setupContainerRealm( Set exportedPackages = entry.getExportedPackages(); ClassRealm realm = entry.getClassRealm(); for (String exportedPackage : exportedPackages) { - extRealm.importFrom(realm, exportedPackage); + extRealm.importFrom(realm.getClassLoader(), exportedPackage); } if (exportedPackages.isEmpty()) { // sisu uses realm imports to establish component visibility - extRealm.importFrom(realm, realm.getId()); + extRealm.importFrom(realm.getClassLoader(), realm.getId()); } } diff --git a/compat/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java b/compat/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java index 321bde249d82..b4b48b600436 100644 --- a/compat/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java +++ b/compat/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java @@ -33,6 +33,8 @@ import org.apache.maven.RepositoryUtils; import org.apache.maven.api.Service; import org.apache.maven.api.Session; +import org.apache.maven.api.classworlds.ClassRealm; +import org.apache.maven.api.classworlds.ClassWorld; import org.apache.maven.api.cli.extensions.CoreExtension; import org.apache.maven.api.model.Plugin; import org.apache.maven.api.services.ArtifactCoordinatesFactory; @@ -64,8 +66,6 @@ import org.apache.maven.resolver.RepositorySystemSessionFactory; import org.codehaus.plexus.DefaultPlexusContainer; import org.codehaus.plexus.PlexusContainer; -import org.codehaus.plexus.classworlds.ClassWorld; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.RepositorySystemSession.CloseableSession; @@ -174,12 +174,13 @@ private CoreExtensionEntry createExtension(CoreExtension extension, List providedArtifacts = Collections.emptySet(); String classLoadingStrategy = extension.getClassLoadingStrategy(); if (STRATEGY_PARENT_FIRST.equals(classLoadingStrategy)) { - realm.importFrom(parentRealm, ""); + realm.importFrom(parentRealm.getClassLoader(), ""); } else if (STRATEGY_PLUGIN.equals(classLoadingStrategy)) { coreExports.getExportedPackages().forEach((p, cl) -> realm.importFrom(cl, p)); providedArtifacts = coreExports.getExportedArtifacts(); } else if (STRATEGY_SELF_FIRST.equals(classLoadingStrategy)) { - realm.setParentRealm(parentRealm); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) realm) + .setParentRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) parentRealm); } else { throw new IllegalArgumentException("Unsupported class-loading strategy '" + classLoadingStrategy + "'. Supported values are: " + STRATEGY_PARENT_FIRST diff --git a/compat/maven-plugin-api/pom.xml b/compat/maven-plugin-api/pom.xml index 6bd39596deb8..c8fc5e23fc9d 100644 --- a/compat/maven-plugin-api/pom.xml +++ b/compat/maven-plugin-api/pom.xml @@ -79,8 +79,8 @@ under the License. - org.codehaus.plexus - plexus-classworlds + org.apache.maven + maven-classworlds org.apache.maven.resolver diff --git a/impl/maven-classworlds/pom.xml b/impl/maven-classworlds/pom.xml new file mode 100644 index 000000000000..8e4880e1f812 --- /dev/null +++ b/impl/maven-classworlds/pom.xml @@ -0,0 +1,115 @@ + + + + 4.0.0 + + org.apache.maven + maven-impl-modules + 4.1.0-SNAPSHOT + + + maven-classworlds + jar + + Maven 4 Classworlds + A class loader framework + + + + org.apache.maven + maven-api-annotations + + + org.apache.maven + maven-api-classworlds + + + org.junit.jupiter + junit-jupiter-api + test + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + org.codehaus.plexus.classworlds.launcher.Launcher + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + -ea:org.codehaus.classworlds:org.codehaus.plexus.classworlds + 1 + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org/codehaus/plexus/classworlds/event/* + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + + copy + + generate-test-resources + + + + org.apache.ant + ant + 1.10.14 + + + org.apache.logging.log4j + log4j-api + 2.23.1 + + + jakarta.xml.bind + jakarta.xml.bind-api + 4.0.2 + + + ${project.build.directory}/test-lib + + + + + + + diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorld.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorld.java new file mode 100644 index 000000000000..794c19c89a5b --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorld.java @@ -0,0 +1,217 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.Closeable; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; + +import org.codehaus.plexus.classworlds.realm.ClassRealm; +import org.codehaus.plexus.classworlds.realm.DuplicateRealmException; +import org.codehaus.plexus.classworlds.realm.FilteredClassRealm; +import org.codehaus.plexus.classworlds.realm.NoSuchRealmException; + +/** + * A collection of ClassRealms, indexed by id. + * + * @author bob mcwhirter + */ +public class ClassWorld implements org.apache.maven.api.classworlds.ClassWorld, Closeable { + private Map realms; + + private final List listeners = new ArrayList<>(); + + public ClassWorld(String realmId, ClassLoader classLoader) { + this(); + + try { + newRealm(realmId, classLoader); + } catch (DuplicateRealmException e) { + // Will never happen as we are just creating the world. + } + } + + public ClassWorld() { + this.realms = new LinkedHashMap<>(); + } + + public ClassRealm newRealm(String id) throws DuplicateRealmException { + return newRealm(id, getClass().getClassLoader()); + } + + public ClassRealm newRealm(String id, ClassLoader classLoader) throws DuplicateRealmException { + return newRealm(id, classLoader, null); + } + + /** + * Adds a class realm with filtering. + * Only resources/classes whose name matches a given predicate are exposed. + * @param id The identifier for this realm, must not be null. + * @param classLoader The base class loader for this realm, may be null to use the bootstrap class + * loader. + * @param filter a predicate to apply to each resource name to determine if it should be loaded through this class loader + * @return the created class realm + * @throws DuplicateRealmException in case a realm with the given id does already exist + * @since 2.7.0 + * @see FilteredClassRealm + */ + public synchronized ClassRealm newRealm(String id, ClassLoader classLoader, Predicate filter) + throws DuplicateRealmException { + if (realms.containsKey(id)) { + throw new DuplicateRealmException(this, id); + } + + ClassRealm realm; + + if (filter == null) { + realm = new ClassRealm(this, id, classLoader); + } else { + realm = new FilteredClassRealm(filter, this, id, classLoader); + } + realms.put(id, realm); + + for (ClassWorldListener listener : listeners) { + listener.realmCreated(realm); + } + + return realm; + } + + /** + * Closes all contained class realms. + * @since 2.7.0 + */ + @Override + public synchronized void close() throws IOException { + realms.values().stream().forEach(this::disposeRealm); + realms.clear(); + } + + public synchronized void disposeRealm(String id) throws NoSuchRealmException { + ClassRealm realm = realms.remove(id); + + if (realm != null) { + disposeRealm(realm); + } else { + throw new NoSuchRealmException(this, id); + } + } + + private void disposeRealm(ClassRealm realm) { + try { + realm.close(); + } catch (IOException ignore) { + } + for (ClassWorldListener listener : listeners) { + listener.realmDisposed(realm); + } + } + + public synchronized ClassRealm getRealm(String id) throws NoSuchRealmException { + if (realms.containsKey(id)) { + return realms.get(id); + } + + throw new NoSuchRealmException(this, id); + } + + public synchronized Collection getRealms() { + return Collections.unmodifiableList(new ArrayList<>(realms.values())); + } + + // from exports branch + public synchronized ClassRealm getClassRealm(String id) { + if (realms.containsKey(id)) { + return realms.get(id); + } + + return null; + } + + public synchronized void addListener(ClassWorldListener listener) { + // TODO ideally, use object identity, not equals + if (!listeners.contains(listener)) { + listeners.add(listener); + } + } + + public synchronized void removeListener(ClassWorldListener listener) { + listeners.remove(listener); + } + + // API interface methods - newRealm with filter is already implemented above + + @Override + public void addListener(org.apache.maven.api.classworlds.ClassWorldListener listener) { + if (listener instanceof ClassWorldListener) { + addListener((ClassWorldListener) listener); + } else { + // Wrap the API listener + addListener(new ClassWorldListener() { + @Override + public void realmCreated(ClassRealm realm) { + listener.realmCreated(realm); + } + + @Override + public void realmDisposed(ClassRealm realm) { + listener.realmDisposed(realm); + } + + @Override + public void realmCreated(org.apache.maven.api.classworlds.ClassRealm realm) { + listener.realmCreated(realm); + } + + @Override + public void realmDisposed(org.apache.maven.api.classworlds.ClassRealm realm) { + listener.realmDisposed(realm); + } + }); + } + } + + @Override + public void removeListener(org.apache.maven.api.classworlds.ClassWorldListener listener) { + // For now, we'll need to track wrapped listeners if this becomes important + // This is a limitation of the current design + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorldException.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorldException.java new file mode 100644 index 000000000000..8a1501652ed1 --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorldException.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Base exception for ClassWorld errors. + * + * @author bob mcwhirter + */ +public class ClassWorldException extends org.apache.maven.api.classworlds.ClassWorldException { + // ------------------------------------------------------------ + // Instance members + // ------------------------------------------------------------ + + /** + * The world. + */ + private ClassWorld world; + + // ------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------ + + /** + * Construct. + * + * @param world The world. + */ + public ClassWorldException(final ClassWorld world) { + super(world); + this.world = world; + } + + /** + * Construct. + * + * @param world The world. + * @param msg The detail message. + */ + public ClassWorldException(final ClassWorld world, final String msg) { + super(world, msg); + this.world = world; + } + + // ------------------------------------------------------------ + // Instance methods + // ------------------------------------------------------------ + + /** + * Retrieve the world. + * + * @return The world. + */ + public ClassWorld getWorld() { + return this.world; + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorldListener.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorldListener.java new file mode 100644 index 000000000000..7e27e279d53b --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/ClassWorldListener.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.codehaus.plexus.classworlds.realm.ClassRealm; + +public interface ClassWorldListener extends org.apache.maven.api.classworlds.ClassWorldListener { + void realmCreated(ClassRealm realm); + + void realmDisposed(ClassRealm realm); +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/UrlUtils.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/UrlUtils.java new file mode 100644 index 000000000000..00d0e2c2a9e2 --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/UrlUtils.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Jason van Zyl + */ +public class UrlUtils { + public static String normalizeUrlPath(String name) { + if (name.startsWith("/")) { + name = name.substring(1); + } + + // Looking for org/codehaus/werkflow/personality/basic/../common/core-idioms.xml + // | i | + // +-------+ remove + // + int i = name.indexOf("/.."); + + // Can't be at the beginning because we have no root to refer to so + // we start at 1. + if (i > 0) { + int j = name.lastIndexOf("/", i - 1); + + if (j >= 0) { + name = name.substring(0, j) + name.substring(i + 3); + } + } + + return name; + } + + public static Set getURLs(URLClassLoader loader) { + return new HashSet<>(Arrays.asList(loader.getURLs())); + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationException.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationException.java new file mode 100644 index 000000000000..8862146b0d5c --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationException.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.launcher; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Indicates an error during Launcher configuration. + * + * @author bob mcwhirter + */ +public class ConfigurationException extends Exception { + /** + * Construct. + * + * @param msg The message. + */ + public ConfigurationException(String msg) { + super(msg); + } + + /** + * Construct. + * + * @param msg The message. + * @param lineNo The number of configuraton line where the problem occured. + * @param line The configuration line where the problem occured. + */ + public ConfigurationException(String msg, int lineNo, String line) { + super(msg + " (" + lineNo + "): " + line); + } + + protected ConfigurationException(Exception cause) { + super(cause); + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationHandler.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationHandler.java new file mode 100644 index 000000000000..2bd42c1e8907 --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationHandler.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.launcher; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.File; +import java.net.URL; + +import org.codehaus.plexus.classworlds.realm.DuplicateRealmException; +import org.codehaus.plexus.classworlds.realm.NoSuchRealmException; + +/** + * Receive notification of the logical content of launcher configuration, independently from parsing. + * + * @author Igor Fedorenko + */ +public interface ConfigurationHandler { + + /** + * Define the main class name + * @param mainClassName the main class name + * @param mainRealmName the main realm from which the main class is loaded + */ + void setAppMain(String mainClassName, String mainRealmName); + + /** + * Define a new realm + * @param realmName the new realm name + * @throws DuplicateRealmException when realm with name already exists + */ + void addRealm(String realmName) throws DuplicateRealmException; + + /** + * Add an import specification from a realm + * @param realmName the realm name + * @param importSpec the import specification + * @throws NoSuchRealmException if realm doesn't exist + */ + void addImportFrom(String realmName, String importSpec) throws NoSuchRealmException; + + /** + * Add a file to the realm + * @param file the file to load content from + */ + void addLoadFile(File file); + + /** + * Add an URL to the realm + * @param url the url to load content from + */ + void addLoadURL(URL url); +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParser.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParser.java new file mode 100644 index 000000000000..6f47b4aeb440 --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParser.java @@ -0,0 +1,444 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.launcher; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Properties; + +import org.codehaus.plexus.classworlds.realm.DuplicateRealmException; +import org.codehaus.plexus.classworlds.realm.NoSuchRealmException; + +/** + * Event based launcher configuration parser, delegating effective configuration handling to ConfigurationHandler. + * + * @author bob mcwhirter + * @author Jason van Zyl + * @author Igor Fedorenko + * @see ConfigurationHandler + */ +public class ConfigurationParser { + public static final String MAIN_PREFIX = "main is"; + + public static final String SET_PREFIX = "set"; + + public static final String IMPORT_PREFIX = "import"; + + public static final String LOAD_PREFIX = "load"; + + /** + * Optionally spec prefix. + */ + public static final String OPTIONALLY_PREFIX = "optionally"; + + protected static final String FROM_SEPARATOR = " from "; + + protected static final String USING_SEPARATOR = " using "; + + protected static final String DEFAULT_SEPARATOR = " default "; + + private final ConfigurationHandler handler; + + private final Properties systemProperties; + + public ConfigurationParser(ConfigurationHandler handler, Properties systemProperties) { + this.handler = handler; + this.systemProperties = systemProperties; + } + + /** + * Parse launcher configuration file and send events to the handler. + * + * @param is the inputstream + * @throws IOException when IOException occurs + * @throws ConfigurationException when ConfigurationException occurs + * @throws DuplicateRealmException when realm already exists + * @throws NoSuchRealmException when realm doesn't exist + */ + public void parse(InputStream is) + throws IOException, ConfigurationException, DuplicateRealmException, NoSuchRealmException { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { + + String line; + int lineNo = 0; + boolean mainSet = false; + String curRealm = null; + + while (true) { + line = reader.readLine(); + + if (line == null) { + break; + } + + ++lineNo; + line = line.trim(); + + if (canIgnore(line)) { + continue; + } + + char lineFirstChar = line.charAt(0); + switch (lineFirstChar) { + case 'm': + mainSet = handleMainConfiguration(line, lineNo, mainSet); + break; + case 's': + if (handleSetConfiguration(line, lineNo)) { + continue; + } + break; + case '[': + curRealm = handleRealmConfiguration(line, lineNo); + break; + case 'i': + handleImportConfiguration(line, lineNo, curRealm); + break; + case 'l': + handleLoadConfiguration(line, lineNo); + break; + case 'o': + handleOptionallyConfiguration(line, lineNo); + break; + default: + throw new ConfigurationException("Unhandled configuration", lineNo, line); + } + } + } + } + + /** + * Load a glob into the specified classloader. + * + * @param line The path configuration line. + * @param optionally Whether the path is optional or required + * @throws MalformedURLException If the line does not represent + * a valid path element. + * @throws FileNotFoundException If the line does not represent + * a valid path element in the filesystem. + * @throws ConfigurationException will never occur (thrown for backwards compatibility) + */ + protected void loadGlob(String line, boolean optionally) + throws MalformedURLException, FileNotFoundException, ConfigurationException { + File globFile = new File(line); + + File dir = globFile.getParentFile(); + if (!dir.exists()) { + if (optionally) { + return; + } else { + throw new FileNotFoundException(dir.toString()); + } + } + + String localName = globFile.getName(); + + int starLoc = localName.indexOf("*"); + + final String prefix = localName.substring(0, starLoc); + + final String suffix = localName.substring(starLoc + 1); + + File[] matches = dir.listFiles((dir1, name) -> { + if (!name.startsWith(prefix)) { + return false; + } + + if (!name.endsWith(suffix)) { + return false; + } + + return true; + }); + + for (File match : matches) { + handler.addLoadFile(match); + } + } + + /** + * Filter a string for system properties. + * + * @param text The text to filter. + * @return The filtered text. + * @throws ConfigurationException If the property does not + * exist or if there is a syntax error. + */ + protected String filter(String text) throws ConfigurationException { + StringBuilder result = new StringBuilder(); + + int cur = 0; + int textLen = text.length(); + + int propStart; + int propStop; + + String propName; + String propValue; + + while (cur < textLen) { + propStart = text.indexOf("${", cur); + + if (propStart < 0) { + break; + } + + result.append(text, cur, propStart); + + propStop = text.indexOf("}", propStart); + + if (propStop < 0) { + throw new ConfigurationException("Unterminated property: " + text.substring(propStart)); + } + + propName = text.substring(propStart + 2, propStop); + + propValue = systemProperties.getProperty(propName); + + /* do our best if we are not running from surefire */ + if (propName.equals("basedir") && (propValue == null || propValue.equals(""))) { + propValue = (new File("")).getAbsolutePath(); + } + + if (propValue == null) { + throw new ConfigurationException("No such property: " + propName); + } + result.append(propValue); + + cur = propStop + 1; + } + + result.append(text.substring(cur)); + + return result.toString(); + } + + /** + * Determine if a line can be ignored because it is + * a comment or simply blank. + * + * @param line The line to test. + * @return true if the line is ignorable, + * otherwise false. + */ + private boolean canIgnore(String line) { + return (line.isEmpty() || line.startsWith("#")); + } + + private boolean handleMainConfiguration(String line, int lineNo, boolean mainSet) throws ConfigurationException { + if (line.startsWith(MAIN_PREFIX)) { + if (mainSet) { + throw new ConfigurationException("Duplicate main configuration", lineNo, line); + } + + int fromLoc = line.indexOf(FROM_SEPARATOR, MAIN_PREFIX.length()); + + if (fromLoc < 0) { + throw new ConfigurationException("Missing from clause", lineNo, line); + } + + String mainClassName = + filter(line.substring(MAIN_PREFIX.length(), fromLoc).trim()); + + String mainRealmName = + filter(line.substring(fromLoc + FROM_SEPARATOR.length()).trim()); + + this.handler.setAppMain(mainClassName, mainRealmName); + + return true; + } + throw new ConfigurationException("Unhandled configuration", lineNo, line); + } + + private boolean handleSetConfiguration(String line, int lineNo) throws ConfigurationException { + if (line.startsWith(SET_PREFIX)) { + String conf = line.substring(SET_PREFIX.length()).trim(); + + int usingLoc = conf.indexOf(USING_SEPARATOR); + + String property = null; + String propertiesFileName = null; + + if (usingLoc >= 0) { + property = conf.substring(0, usingLoc).trim(); + propertiesFileName = filter( + conf.substring(usingLoc + USING_SEPARATOR.length()).trim()); + conf = propertiesFileName; + } + + String defaultValue = null; + int defaultLoc = conf.indexOf(DEFAULT_SEPARATOR); + + if (defaultLoc >= 0) { + defaultValue = filter( + conf.substring(defaultLoc + DEFAULT_SEPARATOR.length()).trim()); + + if (property == null) { + property = conf.substring(0, defaultLoc).trim(); + } else { + propertiesFileName = conf.substring(0, defaultLoc).trim(); + } + } + + String value = systemProperties.getProperty(property); + + if (value != null) { + return true; + } + + if (propertiesFileName != null) { + File propertiesFile = new File(propertiesFileName); + + if (propertiesFile.exists()) { + Properties properties = new Properties(); + + try (InputStream inputStream = Files.newInputStream(Paths.get(propertiesFileName))) { + properties.load(inputStream); + value = properties.getProperty(property); + } catch (Exception e) { + // do nothing + } + } + } + + if (value == null && defaultValue != null) { + value = defaultValue; + } + + if (value != null) { + value = filter(value); + systemProperties.setProperty(property, value); + } + + return false; + } + throw new ConfigurationException("Unhandled configuration", lineNo, line); + } + + private String handleRealmConfiguration(String line, int lineNo) + throws ConfigurationException, DuplicateRealmException { + int rbrack = line.indexOf("]"); + + if (rbrack < 0) { + throw new ConfigurationException("Invalid realm specifier", lineNo, line); + } + + String realmName = line.substring(1, rbrack); + handler.addRealm(realmName); + return realmName; + } + + private void handleImportConfiguration(String line, int lineNo, String curRealm) + throws ConfigurationException, NoSuchRealmException { + if (line.startsWith(IMPORT_PREFIX)) { + if (curRealm == null) { + throw new ConfigurationException("Unhandled import", lineNo, line); + } + int fromLoc = line.indexOf(FROM_SEPARATOR, IMPORT_PREFIX.length()); + + if (fromLoc < 0) { + throw new ConfigurationException("Missing from clause", lineNo, line); + } + + String importSpec = line.substring(IMPORT_PREFIX.length(), fromLoc).trim(); + String realmName = line.substring(fromLoc + FROM_SEPARATOR.length()).trim(); + + handler.addImportFrom(realmName, importSpec); + return; + } + throw new ConfigurationException("Unhandled configuration", lineNo, line); + } + + private void handleLoadConfiguration(String line, int lineNo) + throws ConfigurationException, FileNotFoundException, MalformedURLException { + if (line.startsWith(LOAD_PREFIX)) { + String constituent = line.substring(LOAD_PREFIX.length()).trim(); + constituent = filter(constituent); + + if (constituent.contains("*")) { + loadGlob(constituent, false /*not optionally*/); + } else { + File file = new File(constituent); + + if (file.exists()) { + handler.addLoadFile(file); + } else { + try { + handler.addLoadURL(new URL(constituent)); + } catch (MalformedURLException e) { + throw new FileNotFoundException(constituent); + } + } + } + return; + } + throw new ConfigurationException("Unhandled configuration", lineNo, line); + } + + private void handleOptionallyConfiguration(String line, int lineNo) + throws ConfigurationException, FileNotFoundException, MalformedURLException { + if (line.startsWith(OPTIONALLY_PREFIX)) { + String constituent = line.substring(OPTIONALLY_PREFIX.length()).trim(); + constituent = filter(constituent); + + if (constituent.contains("*")) { + loadGlob(constituent, true /*optionally*/); + } else { + File file = new File(constituent); + + if (file.exists()) { + handler.addLoadFile(file); + } else { + try { + handler.addLoadURL(new URL(constituent)); + } catch (MalformedURLException e) { + // swallow + } + } + } + return; + } + throw new ConfigurationException("Unhandled configuration", lineNo, line); + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/Configurator.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/Configurator.java new file mode 100644 index 000000000000..85aa9f78b617 --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/Configurator.java @@ -0,0 +1,216 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.launcher; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.codehaus.plexus.classworlds.ClassWorld; +import org.codehaus.plexus.classworlds.realm.ClassRealm; +import org.codehaus.plexus.classworlds.realm.DuplicateRealmException; +import org.codehaus.plexus.classworlds.realm.NoSuchRealmException; + +/** + * Launcher configurator. + * + * @author bob mcwhirter + * @author Jason van Zyl + */ +public class Configurator implements ConfigurationHandler { + /** + * The launcher to configure. + */ + private Launcher launcher; + + private ClassWorld world; + + /** + * Processed Realms. + */ + private Map configuredRealms; + + /** + * Current Realm. + */ + private ClassRealm curRealm; + + private ClassLoader foreignClassLoader = null; + + /** + * Construct. + * + * @param launcher The launcher to configure. + */ + public Configurator(Launcher launcher) { + this.launcher = launcher; + + configuredRealms = new HashMap<>(); + + if (launcher != null) { + this.foreignClassLoader = launcher.getSystemClassLoader(); + } + } + + /** + * Construct. + * + * @param world The classWorld to configure. + */ + public Configurator(ClassWorld world) { + setClassWorld(world); + } + + /** + * set world. + * this setter is provided so you can use the same configurator to configure several "worlds" + * + * @param world The classWorld to configure. + */ + public void setClassWorld(ClassWorld world) { + this.world = world; + + configuredRealms = new HashMap<>(); + } + + /** + * Configure from a file. + * + * @param is The config input stream + * @throws IOException If an error occurs reading the config file. + * @throws MalformedURLException If the config file contains invalid URLs. + * @throws ConfigurationException If the config file is corrupt. + * @throws org.codehaus.plexus.classworlds.realm.DuplicateRealmException If the config file defines two realms with the same id. + * @throws org.codehaus.plexus.classworlds.realm.NoSuchRealmException If the config file defines a main entry point in + * a non-existent realm. + */ + public void configure(InputStream is) + throws IOException, ConfigurationException, DuplicateRealmException, NoSuchRealmException { + if (world == null) { + world = new ClassWorld(); + } + + curRealm = null; + + foreignClassLoader = null; + + if (this.launcher != null) { + foreignClassLoader = this.launcher.getSystemClassLoader(); + } + + ConfigurationParser parser = new ConfigurationParser(this, System.getProperties()); + + parser.parse(is); + + // Associate child realms to their parents. + associateRealms(); + + if (this.launcher != null) { + this.launcher.setWorld(world); + } + } + + // TODO return this to protected when the legacy wrappers can be removed. + /** + * Associate parent realms with their children. + */ + public void associateRealms() { + List sortRealmNames = new ArrayList<>(configuredRealms.keySet()); + + // sort by name + sortRealmNames.sort(String::compareTo); + + // So now we have something like the following for defined + // realms: + // + // root + // root.maven + // root.maven.plugin + // + // Now if the name of a realm is a superset of an existing realm + // the we want to make child/parent associations. + + for (String realmName : sortRealmNames) { + int j = realmName.lastIndexOf('.'); + + if (j > 0) { + String parentRealmName = realmName.substring(0, j); + + ClassRealm parentRealm = configuredRealms.get(parentRealmName); + + if (parentRealm != null) { + ClassRealm realm = configuredRealms.get(realmName); + + realm.setParentRealm(parentRealm); + } + } + } + } + + public void addImportFrom(String relamName, String importSpec) throws NoSuchRealmException { + curRealm.importFrom(relamName, importSpec); + } + + public void addLoadFile(File file) { + try { + curRealm.addURL(file.toURI().toURL()); + } catch (MalformedURLException e) { + // can't really happen... or can it? + } + } + + public void addLoadURL(URL url) { + curRealm.addURL(url); + } + + public void addRealm(String realmName) throws DuplicateRealmException { + curRealm = world.newRealm(realmName, foreignClassLoader); + + // Stash the configured realm for subsequent association processing. + configuredRealms.put(realmName, curRealm); + } + + public void setAppMain(String mainClassName, String mainRealmName) { + if (this.launcher != null) { + this.launcher.setAppMain(mainClassName, mainRealmName); + } + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/Launcher.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/Launcher.java new file mode 100644 index 000000000000..7384e43517c5 --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/launcher/Launcher.java @@ -0,0 +1,409 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.launcher; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; + +import org.codehaus.plexus.classworlds.ClassWorld; +import org.codehaus.plexus.classworlds.realm.ClassRealm; +import org.codehaus.plexus.classworlds.realm.DuplicateRealmException; +import org.codehaus.plexus.classworlds.realm.NoSuchRealmException; + +/** + *

Command-line invokable application launcher.

+ * + *

This launcher class assists in the creation of classloaders and ClassRealms + * from a configuration file and the launching of the application's main + * method from the correct class loaded through the correct classloader.

+ * + *

The path to the configuration file is specified using the classworlds.conf + * system property, typically specified using the -D switch to + * java.

+ * + * @author bob mcwhirter + */ +public class Launcher { + protected static final String CLASSWORLDS_CONF = "classworlds.conf"; + + protected static final String UBERJAR_CONF_DIR = "WORLDS-INF/conf/"; + + protected ClassLoader systemClassLoader; + + protected String mainClassName; + + protected String mainRealmName; + + protected ClassWorld world; + + private int exitCode = 0; + + public Launcher() { + this.systemClassLoader = Thread.currentThread().getContextClassLoader(); + } + + public void setSystemClassLoader(ClassLoader loader) { + this.systemClassLoader = loader; + } + + public ClassLoader getSystemClassLoader() { + return this.systemClassLoader; + } + + public int getExitCode() { + return exitCode; + } + + public void setAppMain(String mainClassName, String mainRealmName) { + this.mainClassName = mainClassName; + + this.mainRealmName = mainRealmName; + } + + public String getMainRealmName() { + return this.mainRealmName; + } + + public String getMainClassName() { + return this.mainClassName; + } + + public void setWorld(ClassWorld world) { + this.world = world; + } + + public ClassWorld getWorld() { + return this.world; + } + + /** + * Configure from a file. + * + * @param is The config input stream. + * @throws IOException If an error occurs reading the config file. + * @throws MalformedURLException If the config file contains invalid URLs. + * @throws ConfigurationException If the config file is corrupt. + * @throws org.codehaus.plexus.classworlds.realm.DuplicateRealmException If the config file defines two realms + * with the same id. + * @throws org.codehaus.plexus.classworlds.realm.NoSuchRealmException If the config file defines a main entry + * point in a non-existent realm. + */ + public void configure(InputStream is) + throws IOException, ConfigurationException, DuplicateRealmException, NoSuchRealmException { + Configurator configurator = new Configurator(this); + + configurator.configure(is); + } + + /** + * Retrieve the main entry class. + * + * @return The main entry class. + * @throws ClassNotFoundException If the class cannot be found. + * @throws NoSuchRealmException If the specified main entry realm does not exist. + */ + public Class getMainClass() throws ClassNotFoundException, NoSuchRealmException { + return getMainRealm().loadClass(getMainClassName()); + } + + /** + * Retrieve the main entry realm. + * + * @return The main entry realm. + * @throws NoSuchRealmException If the specified main entry realm does not exist. + */ + public ClassRealm getMainRealm() throws NoSuchRealmException { + return getWorld().getRealm(getMainRealmName()); + } + + /** + * Retrieve the enhanced main entry method. + * + * @return The enhanced main entry method. + * @throws ClassNotFoundException If the main entry class cannot be found. + * @throws NoSuchMethodException If the main entry method cannot be found. + * @throws NoSuchRealmException If the main entry realm cannot be found. + */ + protected Method getEnhancedMainMethod() + throws ClassNotFoundException, NoSuchMethodException, NoSuchRealmException { + Class cwClass = getMainRealm().loadClass(ClassWorld.class.getName()); + + Method m = getMainClass().getMethod("main", String[].class, cwClass); + + int modifiers = m.getModifiers(); + + if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) { + if (m.getReturnType() == Integer.TYPE || m.getReturnType() == Void.TYPE) { + return m; + } + } + + throw new NoSuchMethodException("public static void main(String[] args, ClassWorld world)"); + } + + /** + * Retrieve the main entry method. + * + * @return The main entry method. + * @throws ClassNotFoundException If the main entry class cannot be found. + * @throws NoSuchMethodException If the main entry method cannot be found. + * @throws NoSuchRealmException If the main entry realm cannot be found. + */ + protected Method getMainMethod() throws ClassNotFoundException, NoSuchMethodException, NoSuchRealmException { + Method m = getMainClass().getMethod("main", String[].class); + + int modifiers = m.getModifiers(); + + if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) { + if (m.getReturnType() == Integer.TYPE || m.getReturnType() == Void.TYPE) { + return m; + } + } + + throw new NoSuchMethodException("public static void main(String[] args) in " + getMainClass()); + } + + /** + * Launch the application. + * + * @param args The application args. + * @throws ClassNotFoundException If the main entry class cannot be found. + * @throws IllegalAccessException If the method cannot be accessed. + * @throws InvocationTargetException If the target of the invokation is invalid. + * @throws NoSuchMethodException If the main entry method cannot be found. + * @throws NoSuchRealmException If the main entry realm cannot be found. + */ + public void launch(String[] args) + throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, + NoSuchRealmException { + try { + launchEnhanced(args); + + return; + } catch (NoSuchMethodException e) { + // ignore + } + + launchStandard(args); + } + + /** + *

Attempt to launch the application through the enhanced main method.

+ * + *

This will seek a method with the exact signature of:

+ *
+     *  public static void main(String[] args, ClassWorld world)
+     *  
+ * + * @param args The application args. + * @throws ClassNotFoundException If the main entry class cannot be found. + * @throws IllegalAccessException If the method cannot be accessed. + * @throws InvocationTargetException If the target of the invokation is + * invalid. + * @throws NoSuchMethodException If the main entry method cannot be found. + * @throws NoSuchRealmException If the main entry realm cannot be found. + */ + protected void launchEnhanced(String[] args) + throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, + NoSuchRealmException { + ClassRealm mainRealm = getMainRealm(); + + Class mainClass = getMainClass(); + + Method mainMethod = getEnhancedMainMethod(); + + ClassLoader cl = mainRealm; + + // ---------------------------------------------------------------------- + // This is what the classloader for the main realm looks like when we + // boot from the command line: + // ---------------------------------------------------------------------- + // [ AppLauncher$AppClassLoader ] : $CLASSPATH envar + // ^ + // | + // | + // [ AppLauncher$ExtClassLoader ] : ${java.home}/jre/lib/ext/*.jar + // ^ + // | + // | + // [ Strategy ] + // ---------------------------------------------------------------------- + + Thread.currentThread().setContextClassLoader(cl); + + Object ret = mainMethod.invoke(mainClass, args, getWorld()); + + if (ret instanceof Integer) { + exitCode = (Integer) ret; + } + + Thread.currentThread().setContextClassLoader(systemClassLoader); + } + + /** + *

Attempt to launch the application through the standard main method.

+ * + *

This will seek a method with the exact signature of:

+ * + *
+     *  public static void main(String[] args)
+     *  
+ * + * @param args The application args. + * @throws ClassNotFoundException If the main entry class cannot be found. + * @throws IllegalAccessException If the method cannot be accessed. + * @throws InvocationTargetException If the target of the invokation is + * invalid. + * @throws NoSuchMethodException If the main entry method cannot be found. + * @throws NoSuchRealmException If the main entry realm cannot be found. + */ + protected void launchStandard(String[] args) + throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, + NoSuchRealmException { + ClassRealm mainRealm = getMainRealm(); + + Class mainClass = getMainClass(); + + Method mainMethod = getMainMethod(); + + Thread.currentThread().setContextClassLoader(mainRealm); + + Object ret = mainMethod.invoke(mainClass, new Object[] {args}); + + if (ret instanceof Integer) { + exitCode = (Integer) ret; + } + + Thread.currentThread().setContextClassLoader(systemClassLoader); + } + + // ------------------------------------------------------------ + // Class methods + // ------------------------------------------------------------ + + /** + * Launch the launcher from the command line. + * Will exit using System.exit with an exit code of 0 for success, 100 if there was an unknown exception, + * or some other code for an application error. + * + * @param args The application command-line arguments. + */ + public static void main(String[] args) { + try { + int exitCode = mainWithExitCode(args); + + System.exit(exitCode); + } catch (Exception e) { + e.printStackTrace(); + + System.exit(100); + } + } + + /** + * Launch the launcher. + * + * @param args The application command-line arguments. + * @return an integer exit code + * @throws Exception If an error occurs. + */ + public static int mainWithExitCode(String[] args) throws Exception { + String classworldsConf = System.getProperty(CLASSWORLDS_CONF); + + InputStream is; + + Launcher launcher = new Launcher(); + + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + + launcher.setSystemClassLoader(cl); + + if (classworldsConf != null) { + is = Files.newInputStream(Paths.get(classworldsConf)); + } else { + if ("true".equals(System.getProperty("classworlds.bootstrapped"))) { + is = cl.getResourceAsStream(UBERJAR_CONF_DIR + CLASSWORLDS_CONF); + } else { + is = cl.getResourceAsStream(CLASSWORLDS_CONF); + } + } + + if (is == null) { + throw new Exception("classworlds configuration not specified nor found in the classpath"); + } + + launcher.configure(is); + + is.close(); + + try { + launcher.launch(args); + } catch (InvocationTargetException e) { + ClassRealm realm = launcher.getWorld().getRealm(launcher.getMainRealmName()); + + URL[] constituents = realm.getURLs(); + + System.out.println("---------------------------------------------------"); + + for (int i = 0; i < constituents.length; i++) { + System.out.println("constituent[" + i + "]: " + constituents[i]); + } + + System.out.println("---------------------------------------------------"); + + // Decode ITE (if we can) + Throwable t = e.getTargetException(); + + if (t instanceof Exception) { + throw (Exception) t; + } + if (t instanceof Error) { + throw (Error) t; + } + + // Else just toss the ITE + throw e; + } + + return launcher.getExitCode(); + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/ClassRealm.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/ClassRealm.java new file mode 100644 index 000000000000..290e440d5549 --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/ClassRealm.java @@ -0,0 +1,498 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.realm; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.Closeable; +import java.io.IOException; +import java.io.PrintStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.codehaus.plexus.classworlds.ClassWorld; +import org.codehaus.plexus.classworlds.strategy.Strategy; +import org.codehaus.plexus.classworlds.strategy.StrategyFactory; + +/** + * The class loading gateway. Each class realm has access to a base class loader, imports form zero or more other class + * loaders, an optional parent class loader and of course its own class path. When queried for a class/resource, a class + * realm will always query its base class loader first before it delegates to a pluggable strategy. The strategy in turn + * controls the order in which imported class loaders, the parent class loader and the realm itself are searched. The + * base class loader is assumed to be capable of loading of the bootstrap classes. + * + * @author bob mcwhirter + * @author Jason van Zyl + */ +public class ClassRealm extends URLClassLoader implements org.apache.maven.api.classworlds.ClassRealm { + + private ClassWorld world; + + private String id; + + private SortedSet foreignImports; + + private SortedSet parentImports; + + private Strategy strategy; + + private ClassLoader parentClassLoader; + + private static final boolean IS_PARALLEL_CAPABLE = Closeable.class.isAssignableFrom(URLClassLoader.class); + + private final ConcurrentMap lockMap; + + /** + * Creates a new class realm. + * + * @param world The class world this realm belongs to, must not be null. + * @param id The identifier for this realm, must not be null. + * @param baseClassLoader The base class loader for this realm, may be null to use the bootstrap class + * loader. + */ + public ClassRealm(ClassWorld world, String id, ClassLoader baseClassLoader) { + super(new URL[0], baseClassLoader); + + this.world = world; + + this.id = id; + + foreignImports = new TreeSet<>(); + + strategy = StrategyFactory.getStrategy(this); + + lockMap = IS_PARALLEL_CAPABLE ? new ConcurrentHashMap<>() : null; + + if (IS_PARALLEL_CAPABLE) { + // We must call super.getClassLoadingLock at least once + // to avoid NPE in super.loadClass. + super.getClassLoadingLock(getClass().getName()); + } + } + + public String getId() { + return this.id; + } + + public ClassWorld getWorld() { + return this.world; + } + + /** + * Returns the underlying ClassLoader for this realm. + *

+ * This method allows access to the actual ClassLoader implementation + * while maintaining API abstraction. Since ClassRealm extends URLClassLoader, + * this method returns {@code this}. + *

+ * + * @return the underlying ClassLoader (this instance) + */ + public ClassLoader getClassLoader() { + return this; + } + + public void importFromParent(String packageName) { + if (parentImports == null) { + parentImports = new TreeSet<>(); + } + + parentImports.add(new Entry(null, packageName)); + } + + boolean isImportedFromParent(String name) { + if (parentImports != null && !parentImports.isEmpty()) { + for (Entry entry : parentImports) { + if (entry.matches(name)) { + return true; + } + } + + return false; + } + + return true; + } + + public void importFrom(String realmId, String packageName) throws NoSuchRealmException { + importFrom(getWorld().getRealm(realmId), packageName); + } + + public void importFrom(ClassLoader classLoader, String packageName) { + foreignImports.add(new Entry(classLoader, packageName)); + } + + public ClassLoader getImportClassLoader(String name) { + for (Entry entry : foreignImports) { + if (entry.matches(name)) { + return entry.getClassLoader(); + } + } + + return null; + } + + public Collection getImportRealms() { + Collection importRealms = new HashSet<>(); + + for (Entry entry : foreignImports) { + if (entry.getClassLoader() instanceof ClassRealm) { + importRealms.add((ClassRealm) entry.getClassLoader()); + } + } + + return importRealms; + } + + public Strategy getStrategy() { + return strategy; + } + + public void setParentClassLoader(ClassLoader parentClassLoader) { + this.parentClassLoader = parentClassLoader; + } + + public ClassLoader getParentClassLoader() { + return parentClassLoader; + } + + public void setParentRealm(ClassRealm realm) { + this.parentClassLoader = realm; + } + + public ClassRealm getParentRealm() { + return (parentClassLoader instanceof ClassRealm) ? (ClassRealm) parentClassLoader : null; + } + + // Implementation of the original method signature for backward compatibility + public ClassRealm createChildRealm(String id) throws DuplicateRealmException { + ClassRealm childRealm = getWorld().newRealm(id, (ClassLoader) null); + childRealm.setParentRealm(this); + return childRealm; + } + + public void addURL(URL url) { + String urlStr = url.toExternalForm(); + + if (urlStr.startsWith("jar:") && urlStr.endsWith("!/")) { + urlStr = urlStr.substring(4, urlStr.length() - 2); + + try { + url = new URL(urlStr); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + } + + super.addURL(url); + } + + // ---------------------------------------------------------------------- + // We delegate to the Strategy here so that we can change the behavior + // of any existing ClassRealm. + // ---------------------------------------------------------------------- + + public Class loadClass(String name) throws ClassNotFoundException { + return loadClass(name, false); + } + + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (IS_PARALLEL_CAPABLE) { + return unsynchronizedLoadClass(name, resolve); + + } else { + synchronized (this) { + return unsynchronizedLoadClass(name, resolve); + } + } + } + + private Class unsynchronizedLoadClass(String name, boolean resolve) throws ClassNotFoundException { + try { + // first, try loading bootstrap classes + return super.loadClass(name, resolve); + } catch (ClassNotFoundException e) { + // next, try loading via imports, self and parent as controlled by strategy + return strategy.loadClass(name); + } + } + + // overwrites + // https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/ClassLoader.html#findClass(java.lang.String,java.lang.String) + // introduced in Java9 + protected Class findClass(String moduleName, String name) { + if (moduleName != null) { + return null; + } + try { + return findClassInternal(name); + } catch (ClassNotFoundException e) { + try { + return strategy.getRealm().findClass(name); + } catch (ClassNotFoundException nestedException) { + return null; + } + } + } + + protected Class findClass(String name) throws ClassNotFoundException { + /* + * NOTE: This gets only called from ClassLoader.loadClass(Class, boolean) while we try to check for bootstrap + * stuff. Don't scan our class path yet, loadClassFromSelf() will do this later when called by the strategy. + */ + throw new ClassNotFoundException(name); + } + + protected Class findClassInternal(String name) throws ClassNotFoundException { + return super.findClass(name); + } + + public URL getResource(String name) { + URL resource = super.getResource(name); + return resource != null ? resource : strategy.getResource(name); + } + + public URL findResource(String name) { + return super.findResource(name); + } + + public Enumeration getResources(String name) throws IOException { + Collection resources = new LinkedHashSet<>(Collections.list(super.getResources(name))); + resources.addAll(Collections.list(strategy.getResources(name))); + return Collections.enumeration(resources); + } + + public Enumeration findResources(String name) throws IOException { + return super.findResources(name); + } + + // ---------------------------------------------------------------------------- + // Display methods + // ---------------------------------------------------------------------------- + + public void display() { + display(System.out); + } + + public void display(PrintStream out) { + out.println("-----------------------------------------------------"); + + for (ClassRealm cr = this; cr != null; cr = (ClassRealm) cr.getParentRealm()) { + out.println("realm = " + cr.getId()); + out.println("strategy = " + cr.getStrategy().getClass().getName()); + + showUrls(cr, out); + + out.println(); + } + + out.println("-----------------------------------------------------"); + } + + private static void showUrls(ClassRealm classRealm, PrintStream out) { + URL[] urls = classRealm.getURLs(); + + for (int i = 0; i < urls.length; i++) { + out.println("urls[" + i + "] = " + urls[i]); + } + + out.println("Number of foreign imports: " + classRealm.foreignImports.size()); + + for (Entry entry : classRealm.foreignImports) { + out.println("import: " + entry); + } + + if (classRealm.parentImports != null) { + out.println("Number of parent imports: " + classRealm.parentImports.size()); + + for (Entry entry : classRealm.parentImports) { + out.println("import: " + entry); + } + } + } + + public String toString() { + return "ClassRealm[" + getId() + ", parent: " + getParentClassLoader() + "]"; + } + + // --------------------------------------------------------------------------------------------- + // Search methods that can be ordered by strategies to load a class + // --------------------------------------------------------------------------------------------- + + public Class loadClassFromImport(String name) { + ClassLoader importClassLoader = getImportClassLoader(name); + + if (importClassLoader != null) { + try { + return importClassLoader.loadClass(name); + } catch (ClassNotFoundException e) { + return null; + } + } + + return null; + } + + public Class loadClassFromSelf(String name) { + synchronized (getClassRealmLoadingLock(name)) { + try { + Class clazz = findLoadedClass(name); + + if (clazz == null) { + clazz = findClassInternal(name); + } + + return clazz; + } catch (ClassNotFoundException e) { + return null; + } + } + } + + private Object getClassRealmLoadingLock(String name) { + if (IS_PARALLEL_CAPABLE) { + return getClassLoadingLock(name); + } else { + return this; + } + } + + @Override + protected Object getClassLoadingLock(String name) { + if (IS_PARALLEL_CAPABLE) { + Object newLock = new Object(); + Object lock = lockMap.putIfAbsent(name, newLock); + return (lock == null) ? newLock : lock; + } + return this; + } + + public Class loadClassFromParent(String name) { + ClassLoader parent = getParentClassLoader(); + + if (parent != null && isImportedFromParent(name)) { + try { + return parent.loadClass(name); + } catch (ClassNotFoundException e) { + return null; + } + } + + return null; + } + + // --------------------------------------------------------------------------------------------- + // Search methods that can be ordered by strategies to get a resource + // --------------------------------------------------------------------------------------------- + + public URL loadResourceFromImport(String name) { + ClassLoader importClassLoader = getImportClassLoader(name); + + if (importClassLoader != null) { + return importClassLoader.getResource(name); + } + + return null; + } + + public URL loadResourceFromSelf(String name) { + return findResource(name); + } + + public URL loadResourceFromParent(String name) { + ClassLoader parent = getParentClassLoader(); + + if (parent != null && isImportedFromParent(name)) { + return parent.getResource(name); + } else { + return null; + } + } + + // --------------------------------------------------------------------------------------------- + // Search methods that can be ordered by strategies to get resources + // --------------------------------------------------------------------------------------------- + + public Enumeration loadResourcesFromImport(String name) { + ClassLoader importClassLoader = getImportClassLoader(name); + + if (importClassLoader != null) { + try { + return importClassLoader.getResources(name); + } catch (IOException e) { + return null; + } + } + + return null; + } + + public Enumeration loadResourcesFromSelf(String name) { + try { + return findResources(name); + } catch (IOException e) { + return null; + } + } + + public Enumeration loadResourcesFromParent(String name) { + ClassLoader parent = getParentClassLoader(); + + if (parent != null && isImportedFromParent(name)) { + try { + return parent.getResources(name); + } catch (IOException e) { + // eat it + } + } + + return null; + } + + static { + if (IS_PARALLEL_CAPABLE) // Avoid running this method on older jdks + { + registerAsParallelCapable(); + } + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/DuplicateRealmException.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/DuplicateRealmException.java new file mode 100644 index 000000000000..59d0299ead9d --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/DuplicateRealmException.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.realm; + +import org.codehaus.plexus.classworlds.ClassWorld; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Indicates an attempt to add a ClassRealm to a + * ClassWorld with a duplicate id. + * + * @author bob mcwhirter + */ +public class DuplicateRealmException extends org.apache.maven.api.classworlds.DuplicateRealmException { + // ------------------------------------------------------------ + // Instance members + // ------------------------------------------------------------ + + /** + * The realm id. + */ + private String id; + + // ------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------ + + /** + * Construct. + * + * @param world The world. + * @param id The realm id. + */ + public DuplicateRealmException(ClassWorld world, String id) { + super(world, id); + this.id = id; + } + + // ------------------------------------------------------------ + // Instance methods + // ------------------------------------------------------------ + + /** + * Retrieve the duplicate realm id. + * + * @return The id. + */ + public String getId() { + return this.id; + } + + /** + * Retrieve the world. + * + * @return The world. + */ + public ClassWorld getWorld() { + return (ClassWorld) super.getWorld(); + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/Entry.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/Entry.java new file mode 100644 index 000000000000..316c3c7ff4cf --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/Entry.java @@ -0,0 +1,212 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.realm; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Import description entry. + * + * @author bob mcwhirter + */ +class Entry implements Comparable { + + final ClassLoader classLoader; + + final String pkgName; + + Entry(ClassLoader realm, String pkgName) { + this.classLoader = realm; + + this.pkgName = pkgName; + } + + // ------------------------------------------------------------ + // Instance methods + // ------------------------------------------------------------ + + /** + * Retrieve the class loader. + * + * @return The class loader. + */ + ClassLoader getClassLoader() { + return this.classLoader; + } + + /** + * Retrieve the package name. + * + * @return The package name. + */ + String getPackageName() { + return this.pkgName; + } + + /** + * Determine if the class/resource name matches the package + * described by this entry. + * + * @param name The class or resource name to test, must not be null. + * @return true if this entry matches the + * classname, otherwise false. + */ + boolean matches(String name) { + String pkg = getPackageName(); + + if (pkg.endsWith(".*")) { + String pkgName; + + if (name.indexOf('/') < 0) { + // a binary class name, e.g. java.lang.Object + + int index = name.lastIndexOf('.'); + pkgName = (index < 0) ? "" : name.substring(0, index); + } else { + // a resource name, e.g. java/lang/Object.class + + int index = name.lastIndexOf('/'); + pkgName = (index < 0) ? "" : name.substring(0, index).replace('/', '.'); + } + + return pkgName.length() == pkg.length() - 2 && pkg.regionMatches(0, pkgName, 0, pkgName.length()); + } else if (pkg.length() > 0) { + if (name.indexOf('/') < 0) { + // a binary class name, e.g. java.lang.Object + + if (name.startsWith(pkg)) { + if (name.length() == pkg.length()) { + // exact match of class name + return true; + } else if (name.charAt(pkg.length()) == '.') { + // prefix match of package name + return true; + } else if (name.charAt(pkg.length()) == '$') { + // prefix match of enclosing type + return true; + } + } + } else { + // a resource name, e.g. java/lang/Object.class + + if (name.equals(pkg)) { + // exact match of resource name + return true; + } + + pkg = pkg.replace('.', '/'); + + if (name.startsWith(pkg) && name.length() > pkg.length()) { + if (name.charAt(pkg.length()) == '/') { + // prefix match of package directory + return true; + } else if (name.charAt(pkg.length()) == '$') { + // prefix match of nested class file + return true; + } else if (name.length() == pkg.length() + 6 && name.endsWith(".class")) { + // exact match of class file + return true; + } + } + } + + return false; + } else { + return true; + } + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // java.lang.Comparable + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + /** + * Compare this entry to another for relative ordering. + *

+ *

+ * The natural ordering of Entry objects is reverse-alphabetical + * based upon package name. + *

+ * + * @param that The object to compare. + * @return -1 if this object sorts before that object, 0 + * if they are equal, or 1 if this object sorts + * after that object. + */ + public int compareTo(Entry that) { + // We are reverse sorting this list, so that + // we get longer matches first: + // + // com.werken.foo.bar + // com.werken.foo + // com.werken + + return -(getPackageName().compareTo(that.getPackageName())); + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // java.lang.Object + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + /** + * Test this entry for equality to another. + *

+ *

+ * Consistent with {@link #compareTo}, this method tests + * for equality purely on the package name. + *

+ * + * @param thatObj The object to compare + * @return true if the two objects are + * semantically equivalent, otherwise false. + */ + public boolean equals(Object thatObj) { + Entry that = (Entry) thatObj; + + return getPackageName().equals(that.getPackageName()); + } + + /** + *

+ * Consistent with {@link #equals}, this method creates a hashCode + * based on the packagename. + *

+ */ + public int hashCode() { + return getPackageName().hashCode(); + } + + public String toString() { + return "Entry[import " + getPackageName() + " from realm " + getClassLoader() + "]"; + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/FilteredClassRealm.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/FilteredClassRealm.java new file mode 100644 index 000000000000..dfb2eefa9166 --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/FilteredClassRealm.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.realm; + +import java.io.IOException; +import java.net.URL; +import java.util.Collections; +import java.util.Enumeration; +import java.util.function.Predicate; + +import org.codehaus.plexus.classworlds.ClassWorld; + +/** + * Similar to {@link ClassRealm} but only exposing some resources of the underlying URL. + * Only supposed to be called from {@link ClassWorld}. + */ +public class FilteredClassRealm extends ClassRealm { + private final Predicate filter; + + /** + * Creates a new class realm. + * + * @param filter a predicate to apply to each resource name to determine if it should be loaded through this class loader + * @param world The class world this realm belongs to, must not be null. + * @param id The identifier for this realm, must not be null. + * @param baseClassLoader The base class loader for this realm, may be null to use the bootstrap class + * loader. + */ + public FilteredClassRealm(Predicate filter, ClassWorld world, String id, ClassLoader baseClassLoader) { + super(world, id, baseClassLoader); + this.filter = filter; + } + + @Override + protected Class findClassInternal(String name) throws ClassNotFoundException { + String resourceName = name.replace('.', '/').concat(".class"); + if (!filter.test(resourceName)) { + throw new ClassNotFoundException(name); + } + return super.findClassInternal(name); + } + + @Override + public URL findResource(String name) { + if (!filter.test(name)) { + return null; + } + return super.findResource(name); + } + + @Override + public Enumeration findResources(String name) throws IOException { + if (!filter.test(name)) { + return Collections.emptyEnumeration(); + } + return super.findResources(name); + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/NoSuchRealmException.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/NoSuchRealmException.java new file mode 100644 index 000000000000..d98252ccbcc5 --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/realm/NoSuchRealmException.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.realm; + +import org.codehaus.plexus.classworlds.ClassWorld; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Indicates an attempt to retrieve a ClassRealm from a + * ClassWorld with an invalid id. + * + * @author bob mcwhirter + */ +public class NoSuchRealmException extends org.apache.maven.api.classworlds.NoSuchRealmException { + // ------------------------------------------------------------ + // Instance members + // ------------------------------------------------------------ + + /** + * The realm id. + */ + private String id; + + // ------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------ + + /** + * Construct. + * + * @param world The world. + * @param id The realm id. + */ + public NoSuchRealmException(ClassWorld world, String id) { + super(world, id); + this.id = id; + } + + // ------------------------------------------------------------ + // Instance methods + // ------------------------------------------------------------ + + /** + * Retrieve the invalid realm id. + * + * @return The id. + */ + public String getId() { + return this.id; + } + + /** + * Retrieve the world. + * + * @return The world. + */ + public ClassWorld getWorld() { + return (ClassWorld) super.getWorld(); + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/AbstractStrategy.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/AbstractStrategy.java new file mode 100644 index 000000000000..91af5b3cfdd9 --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/AbstractStrategy.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.strategy; + +import java.net.URL; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.LinkedHashSet; + +import org.codehaus.plexus.classworlds.UrlUtils; +import org.codehaus.plexus.classworlds.realm.ClassRealm; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @author Jason van Zyl + */ +public abstract class AbstractStrategy implements Strategy { + + protected ClassRealm realm; + + public AbstractStrategy(ClassRealm realm) { + this.realm = realm; + } + + protected String getNormalizedResource(String name) { + return UrlUtils.normalizeUrlPath(name); + } + + protected Enumeration combineResources(Enumeration en1, Enumeration en2, Enumeration en3) { + Collection urls = new LinkedHashSet<>(); + + addAll(urls, en1); + addAll(urls, en2); + addAll(urls, en3); + + return Collections.enumeration(urls); + } + + private void addAll(Collection target, Enumeration en) { + if (en != null) { + while (en.hasMoreElements()) { + target.add(en.nextElement()); + } + } + } + + public ClassRealm getRealm() { + return realm; + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/OsgiBundleStrategy.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/OsgiBundleStrategy.java new file mode 100644 index 000000000000..24b690d7aaac --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/OsgiBundleStrategy.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.strategy; + +/* + * Copyright 2001-2010 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; + +import org.codehaus.plexus.classworlds.realm.ClassRealm; + +public class OsgiBundleStrategy extends AbstractStrategy { + + // java.* from parent + // imported packages [Import-Package header with explicit constraints on the exporter] + // requires bundle [Required-Bundle] + // self [Bundle-Classpath header] + // attached fragments + // + // We need to trya and be OSGi r4 compliant in the loading of all the bundles so that we can try to + // load eclipse without requiring equinox. Or any other OSGi container for that matter. + public OsgiBundleStrategy(ClassRealm realm) { + super(realm); + } + + public Class loadClass(String name) throws ClassNotFoundException { + Class clazz = realm.loadClassFromImport(name); + + if (clazz == null) { + clazz = realm.loadClassFromSelf(name); + + if (clazz == null) { + clazz = realm.loadClassFromParent(name); + + if (clazz == null) { + throw new ClassNotFoundException(name); + } + } + } + + return clazz; + } + + public URL getResource(String name) { + URL resource = realm.loadResourceFromImport(name); + + if (resource == null) { + resource = realm.loadResourceFromSelf(name); + + if (resource == null) { + resource = realm.loadResourceFromParent(name); + } + } + + return resource; + } + + public Enumeration getResources(String name) throws IOException { + Enumeration imports = realm.loadResourcesFromImport(name); + Enumeration self = realm.loadResourcesFromSelf(name); + Enumeration parent = realm.loadResourcesFromParent(name); + + return combineResources(imports, self, parent); + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/ParentFirstStrategy.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/ParentFirstStrategy.java new file mode 100644 index 000000000000..fc571f3ed88a --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/ParentFirstStrategy.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.strategy; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; + +import org.codehaus.plexus.classworlds.realm.ClassRealm; + +/** + * @author Jason van Zyl + */ +public class ParentFirstStrategy extends AbstractStrategy { + + public ParentFirstStrategy(ClassRealm realm) { + super(realm); + } + + public Class loadClass(String name) throws ClassNotFoundException { + Class clazz = realm.loadClassFromImport(name); + + if (clazz == null) { + clazz = realm.loadClassFromParent(name); + + if (clazz == null) { + clazz = realm.loadClassFromSelf(name); + + if (clazz == null) { + throw new ClassNotFoundException(name); + } + } + } + + return clazz; + } + + public URL getResource(String name) { + URL resource = realm.loadResourceFromImport(name); + + if (resource == null) { + resource = realm.loadResourceFromParent(name); + + if (resource == null) { + resource = realm.loadResourceFromSelf(name); + } + } + + return resource; + } + + public Enumeration getResources(String name) throws IOException { + Enumeration imports = realm.loadResourcesFromImport(name); + Enumeration parent = realm.loadResourcesFromParent(name); + Enumeration self = realm.loadResourcesFromSelf(name); + + return combineResources(imports, parent, self); + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/SelfFirstStrategy.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/SelfFirstStrategy.java new file mode 100644 index 000000000000..52351e837aca --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/SelfFirstStrategy.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.strategy; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; + +import org.codehaus.plexus.classworlds.realm.ClassRealm; + +/** + * @author Jason van Zyl + */ +public class SelfFirstStrategy extends AbstractStrategy { + + public SelfFirstStrategy(ClassRealm realm) { + super(realm); + } + + public Class loadClass(String name) throws ClassNotFoundException { + Class clazz = realm.loadClassFromImport(name); + + if (clazz == null) { + clazz = realm.loadClassFromSelf(name); + + if (clazz == null) { + clazz = realm.loadClassFromParent(name); + + if (clazz == null) { + throw new ClassNotFoundException(name); + } + } + } + + return clazz; + } + + public URL getResource(String name) { + URL resource = realm.loadResourceFromImport(name); + + if (resource == null) { + resource = realm.loadResourceFromSelf(name); + + if (resource == null) { + resource = realm.loadResourceFromParent(name); + } + } + + return resource; + } + + public Enumeration getResources(String name) throws IOException { + Enumeration imports = realm.loadResourcesFromImport(name); + Enumeration self = realm.loadResourcesFromSelf(name); + Enumeration parent = realm.loadResourcesFromParent(name); + + return combineResources(imports, self, parent); + } +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/Strategy.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/Strategy.java new file mode 100644 index 000000000000..63c17092778a --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/Strategy.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.strategy; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; + +import org.codehaus.plexus.classworlds.realm.ClassRealm; + +/** + * A strategy is a class for defining how classes and resources are located + * in classworlds. + */ +public interface Strategy extends org.apache.maven.api.classworlds.Strategy { + + Class loadClass(String name) throws ClassNotFoundException; + + URL getResource(String name); + + Enumeration getResources(String name) throws IOException; + + ClassRealm getRealm(); +} diff --git a/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/StrategyFactory.java b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/StrategyFactory.java new file mode 100644 index 000000000000..26c8f9237c17 --- /dev/null +++ b/impl/maven-classworlds/src/main/java/org/codehaus/plexus/classworlds/strategy/StrategyFactory.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.strategy; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.codehaus.plexus.classworlds.realm.ClassRealm; + +/** + * StrategyFactory loads a strategy, either default or from a given hint. + */ +public class StrategyFactory { + + public static Strategy getStrategy(ClassRealm realm) { + return getStrategy(realm, "default"); + } + + public static Strategy getStrategy(ClassRealm realm, String hint) { + // TODO: Here we shall check hint to load non-default strategies + + return new SelfFirstStrategy(realm); + } +} diff --git a/impl/maven-classworlds/src/test/java/org/apache/maven/api/classworlds/ApiTest.java b/impl/maven-classworlds/src/test/java/org/apache/maven/api/classworlds/ApiTest.java new file mode 100644 index 000000000000..65d8c1766e9d --- /dev/null +++ b/impl/maven-classworlds/src/test/java/org/apache/maven/api/classworlds/ApiTest.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.api.classworlds; + +import java.net.URL; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * Test to demonstrate the Maven 4 ClassWorlds API. + */ +class ApiTest { + + @Test + void testApiUsage() throws Exception { + // Create a ClassWorld using the implementation + ClassWorld world = new org.codehaus.plexus.classworlds.ClassWorld(); + + // Test basic API methods + assertNotNull(world); + + // Create a realm + ClassRealm realm = world.newRealm("test-realm"); + assertNotNull(realm); + assertEquals("test-realm", realm.getId()); + assertSame(world, realm.getWorld()); + + // Test getClassLoader() method + ClassLoader classLoader = realm.getClassLoader(); + assertNotNull(classLoader); + assertSame(realm, classLoader); // Should return the realm itself + + // Test URL operations + URL url = new URL("file:///tmp/test.jar"); + realm.addURL(url); + URL[] urls = realm.getURLs(); + assertTrue(urls.length > 0); + + // Test import operations + ClassRealm importRealm = world.newRealm("import-realm"); + realm.importFrom(importRealm.getId(), "org.example"); + + // Test parent operations + realm.setParentClassLoader(ClassLoader.getSystemClassLoader()); + assertNotNull(realm.getParentClassLoader()); + + // Test strategy + Strategy strategy = realm.getStrategy(); + assertNotNull(strategy); + assertSame(realm, strategy.getRealm()); + + // Test listener + TestListener listener = new TestListener(); + world.addListener(listener); + + ClassRealm newRealm = world.newRealm("listener-test"); + assertTrue(listener.realmCreated); + assertEquals(newRealm, listener.createdRealm); + + world.disposeRealm("listener-test"); + assertTrue(listener.realmDisposed); + assertEquals(newRealm, listener.disposedRealm); + + // Test exception handling + try { + world.newRealm("test-realm"); // Duplicate + fail("Should have thrown DuplicateRealmException"); + } catch (DuplicateRealmException e) { + assertEquals("test-realm", e.getId()); + assertSame(world, e.getWorld()); + } + + try { + world.getRealm("non-existent"); + fail("Should have thrown NoSuchRealmException"); + } catch (NoSuchRealmException e) { + assertEquals("non-existent", e.getId()); + assertSame(world, e.getWorld()); + } + + // Test null-safe operations + assertNull(world.getClassRealm("non-existent")); + + world.close(); + } + + private static class TestListener implements ClassWorldListener { + boolean realmCreated = false; + boolean realmDisposed = false; + ClassRealm createdRealm; + ClassRealm disposedRealm; + + @Override + public void realmCreated(ClassRealm realm) { + this.realmCreated = true; + this.createdRealm = realm; + } + + @Override + public void realmDisposed(ClassRealm realm) { + this.realmDisposed = true; + this.disposedRealm = realm; + } + } +} diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/AbstractClassWorldsTestCase.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/AbstractClassWorldsTestCase.java new file mode 100644 index 000000000000..8f4c3699a44c --- /dev/null +++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/AbstractClassWorldsTestCase.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.net.URL; + +/** + * @author Jason van Zyl + */ +public abstract class AbstractClassWorldsTestCase { + protected URL getJarUrl(String jarName) { + return TestUtil.getTestResourceUrl(jarName); + } +} diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/ClassView.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/ClassView.java new file mode 100644 index 000000000000..cffd6a1a1c1f --- /dev/null +++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/ClassView.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class ClassView { + /** + * * Formats Class information for debug output purposes. + * * + * * @param clz the Class to print information for + * * + * * @return a String describing the Class in detail + */ + public static String toString(Class clz) { + if (clz.isPrimitive()) { + return clz.toString(); + } else if (clz.isArray()) { + return "Array of " + toString(clz.getComponentType()); + } else if (clz.isInterface()) { + return toInterfaceString(clz, ""); + } else { + return toClassString(clz, ""); + } + } + + /** + * * Formats Class information for debug output purposes. + * * + * * @param clz the Class to print information for + * * @param sIndent the indentation to precede each line of output + * * + * * @return a String describing the Class in detail + */ + private static String toClassString(Class clz, String sIndent) { + StringBuilder sb = new StringBuilder(); + sb.append(sIndent) + .append("Class ") + .append(clz.getName()) + .append(" (") + .append(toString(clz.getClassLoader())) + .append(')'); + + sIndent += " "; + + Class[] aclz = clz.getInterfaces(); + for (Class aClass : aclz) { + sb.append('\n').append(toInterfaceString(aClass, sIndent)); + } + + clz = clz.getSuperclass(); + if (clz != null) { + sb.append('\n').append(toClassString(clz, sIndent)); + } + + return sb.toString(); + } + + /** + * * Formats interface information for debug output purposes. + * * + * * @param clz the interface Class to print information for + * * @param sIndent the indentation to precede each line of output + * * + * * @return a String describing the interface Class in detail + */ + private static String toInterfaceString(Class clz, String sIndent) { + StringBuilder sb = new StringBuilder(); + sb.append(sIndent) + .append("Interface ") + .append(clz.getName()) + .append(" (") + .append(toString(clz.getClassLoader())) + .append(')'); + + Class[] aclz = clz.getInterfaces(); + for (Class aClass : aclz) { + clz = aClass; + + sb.append('\n').append(toInterfaceString(clz, sIndent + " ")); + } + + return sb.toString(); + } + + /** + * * Format a description for the specified ClassLoader object. + * * + * * @param loader the ClassLoader instance (or null) + * * + * * @return a String description of the ClassLoader + */ + private static String toString(ClassLoader loader) { + if (loader == null) { + return "System ClassLoader"; + } + + return "ClassLoader class=" + loader.getClass().getName() + ", hashCode=" + loader.hashCode(); + } +} diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/ClassWorldTest.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/ClassWorldTest.java new file mode 100644 index 000000000000..75f6ebd39965 --- /dev/null +++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/ClassWorldTest.java @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Enumeration; + +import org.codehaus.plexus.classworlds.realm.ClassRealm; +import org.codehaus.plexus.classworlds.realm.DuplicateRealmException; +import org.codehaus.plexus.classworlds.realm.NoSuchRealmException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +class ClassWorldTest extends AbstractClassWorldsTestCase { + private ClassWorld world; + + @BeforeEach + public void setUp() { + this.world = new ClassWorld(); + } + + @AfterEach + public void tearDown() { + this.world = null; + } + + @Test + void testEmpty() { + assertTrue(this.world.getRealms().isEmpty()); + } + + @Test + void testNewRealm() throws Exception { + ClassRealm realm = this.world.newRealm("foo"); + + assertNotNull(realm); + } + + @Test + void testGetRealm() throws Exception { + ClassRealm realm = this.world.newRealm("foo"); + + assertSame(realm, this.world.getRealm("foo")); + } + + @Test + void testNewRealmDuplicate() { + try { + this.world.newRealm("foo"); + this.world.newRealm("foo"); + + fail("throw DuplicateRealmException"); + } catch (DuplicateRealmException e) { + // expected and correct + + assertSame(this.world, e.getWorld()); + + assertEquals("foo", e.getId()); + } + } + + @Test + void testGetRealmNoSuch() { + try { + this.world.getRealm("foo"); + fail("throw NoSuchRealmException"); + } catch (NoSuchRealmException e) { + // expected and correct + + assertSame(this.world, e.getWorld()); + + assertEquals("foo", e.getId()); + } + } + + @Test + void testGetRealms() throws Exception { + assertTrue(this.world.getRealms().isEmpty()); + + ClassRealm foo = this.world.newRealm("foo"); + + assertEquals(1, this.world.getRealms().size()); + + assertTrue(this.world.getRealms().contains(foo)); + + ClassRealm bar = this.world.newRealm("bar"); + + assertEquals(2, this.world.getRealms().size()); + + assertTrue(this.world.getRealms().contains(bar)); + } + + @Test + void testPLX334() throws Exception { + ClassLoader loader = new URLClassLoader(new URL[] {getJarUrl("component1-1.0.jar")}); + world.newRealm("netbeans", loader); + ClassRealm plexus = world.newRealm("plexus"); + plexus.importFrom("netbeans", "META-INF/plexus"); + plexus.importFrom("netbeans", "org.codehaus.plexus"); + Enumeration e = plexus.getResources("META-INF/plexus/components.xml"); + assertNotNull(e); + int resourceCount = 0; + for (Enumeration resources = e; resources.hasMoreElements(); ) { + URL obj = resources.nextElement(); + assertTrue(obj.getPath().contains("/component1-1.0.jar!/META-INF/plexus/components.xml")); + resourceCount++; + } + // assertEquals( 2, resourceCount ); + // for some reason surefire-plugin 2.3 returned 2 items there: + // for example: + // jar:file:/home/mkleint/.m2/repository/org/codehaus/plexus/plexus-archiver/1.0-alpha-7/plexus-archiver-1.0-alpha-7.jar!/META-INF/plexus/components.xml + // jar:file:/home/mkleint/src/plexus-trunk/plexus-classworlds/src/test-jars/component1-1.0.jar!/META-INF/plexus/components.xml + // However only 1 is correct, which is actually returned by the 2.4 surefire-plugin + + assertEquals(1, resourceCount); + Class c = plexus.loadClass("org.codehaus.plexus.Component1"); + assertNotNull(c); + } +} diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/TestUtil.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/TestUtil.java new file mode 100644 index 000000000000..e1dca7d81ff2 --- /dev/null +++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/TestUtil.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +/** + * @author Ben Walding + */ +public class TestUtil { + + public static URL getTestResourceUrl(String resourceName) { + File baseDir = new File(getBasedir()); + + File testDir = new File(baseDir, "src/test/test-data"); + + File resourceFile = new File(testDir, resourceName); + + try { + return resourceFile.toURI().toURL(); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + + public static String getBasedir() { + String basedir = System.getProperty("basedir"); + + /* do our best if we are not running from surefire */ + if (basedir == null || basedir.length() <= 0) { + basedir = (new File("")).getAbsolutePath(); + } + + return basedir; + } +} diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/UrlUtilsTest.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/UrlUtilsTest.java new file mode 100644 index 000000000000..cdecf0a44bce --- /dev/null +++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/UrlUtilsTest.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class UrlUtilsTest { + + @Test + public void testNormalizeUrlPath() { + assertEquals("org/codehaus/Test.class", UrlUtils.normalizeUrlPath("org/codehaus/Test.class")); + assertEquals("org/Test.class", UrlUtils.normalizeUrlPath("org/codehaus/../Test.class")); + assertEquals("../../some.jar/org/Test.class", UrlUtils.normalizeUrlPath("../../some.jar/org/Test.class")); + } +} diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParserTest.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParserTest.java new file mode 100644 index 000000000000..69c83e09d2bf --- /dev/null +++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfigurationParserTest.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.launcher; + +import org.codehaus.plexus.classworlds.AbstractClassWorldsTestCase; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +class ConfigurationParserTest extends AbstractClassWorldsTestCase { + + ConfigurationParser configurator = new ConfigurationParser(null, System.getProperties()); + + @Test + void testFilterUnterminated() { + try { + this.configurator.filter("${cheese"); + fail("throw ConfigurationException"); + } catch (ConfigurationException e) { + // expected and correct + assertTrue(e.getMessage().startsWith("Unterminated")); + } + } + + @Test + void testFilterSolitary() throws Exception { + System.setProperty("classworlds.test.prop", "test prop value"); + + String result = this.configurator.filter("${classworlds.test.prop}"); + + assertEquals("test prop value", result); + } + + @Test + void testFilterAtStart() throws Exception { + System.setProperty("classworlds.test.prop", "test prop value"); + + String result = this.configurator.filter("${classworlds.test.prop}cheese"); + + assertEquals("test prop valuecheese", result); + } + + @Test + void testFilterAtEnd() throws Exception { + System.setProperty("classworlds.test.prop", "test prop value"); + + String result = this.configurator.filter("cheese${classworlds.test.prop}"); + + assertEquals("cheesetest prop value", result); + } + + @Test + void testFilterMultiple() throws Exception { + System.setProperty("classworlds.test.prop.one", "test prop value one"); + + System.setProperty("classworlds.test.prop.two", "test prop value two"); + + String result = + this.configurator.filter("I like ${classworlds.test.prop.one} and ${classworlds.test.prop.two} a lot"); + + assertEquals("I like test prop value one and test prop value two a lot", result); + } + + @Test + void testFilterNonExistent() { + try { + this.configurator.filter("${gollygeewillikers}"); + fail("throw ConfigurationException"); + } catch (ConfigurationException e) { + // expected and correct + assertTrue(e.getMessage().startsWith("No such property")); + } + } + + @Test + void testFilterInMiddle() throws Exception { + System.setProperty("classworlds.test.prop", "test prop value"); + + String result = this.configurator.filter("cheese${classworlds.test.prop}toast"); + + assertEquals("cheesetest prop valuetoast", result); + } +} diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfiguratorTest.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfiguratorTest.java new file mode 100644 index 000000000000..2cfcd0bb8e95 --- /dev/null +++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/launcher/ConfiguratorTest.java @@ -0,0 +1,365 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.launcher; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.net.URL; +import java.util.Collection; + +import org.codehaus.plexus.classworlds.AbstractClassWorldsTestCase; +import org.codehaus.plexus.classworlds.ClassWorld; +import org.codehaus.plexus.classworlds.TestUtil; +import org.codehaus.plexus.classworlds.realm.ClassRealm; +import org.codehaus.plexus.classworlds.realm.DuplicateRealmException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +class ConfiguratorTest extends AbstractClassWorldsTestCase { + private Launcher launcher; + private Configurator configurator; + + @BeforeEach + public void setUp() { + this.launcher = new Launcher(); + this.configurator = new Configurator(this.launcher); + } + + @AfterEach + public void tearDown() { + this.launcher = null; + this.configurator = null; + System.getProperties().remove("set.using.existent"); + System.getProperties().remove("set.using.default"); + System.getProperties().remove("set.using.nonexistent"); + System.getProperties().remove("set.using.nonexistent.default"); + System.getProperties().remove("set.using.missing"); + System.getProperties().remove("set.using.filtered.default"); + } + + @Test + void testConfigureNonexistent() throws Exception { + try { + this.configurator.configure(getConfigPath("notfound.conf")); + fail("throw FileNotFoundException"); + } catch (FileNotFoundException e) { + // expected and correct + } + } + + @Test + void testConfigureDuplicateMain() throws Exception { + try { + this.configurator.configure(getConfigPath("dupe-main.conf")); + fail("throw ConfigurationException"); + } catch (ConfigurationException e) { + // expected and correct + assertTrue(e.getMessage().startsWith("Duplicate main")); + } + } + + @Test + void testConfigureDuplicateRealm() throws Exception { + try { + this.configurator.configure(getConfigPath("dupe-realm.conf")); + fail("throw DuplicateRealmException"); + } catch (DuplicateRealmException e) { + // expected and correct + assertEquals("dupe.realm", e.getId()); + } + } + + @Test + void testConfigureEarlyImport() throws Exception { + try { + this.configurator.configure(getConfigPath("early-import.conf")); + fail("throw ConfigurationException"); + } catch (ConfigurationException e) { + // expected and correct + assertTrue(e.getMessage().startsWith("Unhandled import")); + } + } + + @Test + void testConfigureRealmSyntax() throws Exception { + try { + this.configurator.configure(getConfigPath("realm-syntax.conf")); + fail("throw ConfigurationException"); + } catch (ConfigurationException e) { + // expected and correct + assertTrue(e.getMessage().startsWith("Invalid realm")); + } + } + + @Test + void testConfigureValid() throws Exception { + this.configurator.configure(getConfigPath("valid.conf")); + + assertEquals("org.apache.maven.app.App", this.launcher.getMainClassName()); + + assertEquals("maven", this.launcher.getMainRealmName()); + + ClassWorld world = this.launcher.getWorld(); + + Collection realms = world.getRealms(); + + assertEquals(4, realms.size()); + + assertNotNull(world.getRealm("ant")); + assertNotNull(world.getRealm("maven")); + assertNotNull(world.getRealm("xml")); + + ClassRealm antRealm = world.getRealm("ant"); + ClassRealm mavenRealm = world.getRealm("maven"); + ClassRealm xmlRealm = world.getRealm("xml"); + ClassRealm globRealm = world.getRealm("glob"); + + assertSame(null, antRealm.getImportClassLoader("org.apache.tools.Ant")); + + // Ant has dependency to xerces:xercesImpl (test) + assertSame(null, antRealm.getImportClassLoader("org.xml.sax.SAXException")); + + assertSame(xmlRealm, antRealm.getImportClassLoader("jakarta.xml.bind.JAXBException")); + + assertSame(null, mavenRealm.getImportClassLoader("org.apache.maven.app.App")); + + assertSame(xmlRealm, mavenRealm.getImportClassLoader("jakarta.xml.bind.JAXBException")); + + URL[] urls = globRealm.getURLs(); + + String basedir = TestUtil.getBasedir(); + assertArrayContains( + urls, new File(basedir, "src/test/test-data/nested.jar").toURI().toURL()); + assertArrayContains( + urls, new File(basedir, "src/test/test-data/a.jar").toURI().toURL()); + assertArrayContains( + urls, new File(basedir, "src/test/test-data/b.jar").toURI().toURL()); + assertArrayContains( + urls, new File(basedir, "src/test/test-data/c.jar").toURI().toURL()); + } + + @Test + void testConfigureOptionallyNonExistent() throws Exception { + this.configurator.configure(getConfigPath("optionally-nonexistent.conf")); + + assertEquals("org.apache.maven.app.App", this.launcher.getMainClassName()); + + assertEquals("opt", this.launcher.getMainRealmName()); + + ClassWorld world = this.launcher.getWorld(); + + Collection realms = world.getRealms(); + + assertEquals(1, realms.size()); + + assertNotNull(world.getRealm("opt")); + + ClassRealm optRealm = world.getRealm("opt"); + + URL[] urls = optRealm.getURLs(); + + assertEquals(0, urls.length, "no urls"); + } + + @Test + void testConfigureOptionallyExistent() throws Exception { + this.configurator.configure(getConfigPath("optionally-existent.conf")); + + assertEquals("org.apache.maven.app.App", this.launcher.getMainClassName()); + + assertEquals("opt", this.launcher.getMainRealmName()); + + ClassWorld world = this.launcher.getWorld(); + + Collection realms = world.getRealms(); + + assertEquals(1, realms.size()); + + assertNotNull(world.getRealm("opt")); + + ClassRealm optRealm = world.getRealm("opt"); + + URL[] urls = optRealm.getURLs(); + + assertEquals(1, urls.length, "one url"); + + assertSame(null, optRealm.getImportClassLoader("jakarta.xml.bind.JAXBException")); + } + + @Test + void testConfigureUnhandled() throws Exception { + try { + this.configurator.configure(getConfigPath("unhandled.conf")); + fail("throw ConfigurationException"); + } catch (ConfigurationException e) { + // expected and correct + assertTrue(e.getMessage().startsWith("Unhandled configuration")); + } + } + + @Test + void testSetUsingExistent() throws Exception { + assertNull(System.getProperty("set.using.existent")); + + this.configurator.configure(getConfigPath("set-using-existent.conf")); + + assertEquals("testSetUsingExistent", System.getProperty("set.using.existent")); + } + + @Test + void testSetUsingNonExistent() throws Exception { + assertNull(System.getProperty("set.using.nonexistent")); + + this.configurator.configure(getConfigPath("set-using-nonexistent.conf")); + + assertNull(System.getProperty("set.using.nonexistent")); + } + + @Test + void testSetUsingNonExistentDefault() throws Exception { + assertNull(System.getProperty("set.using.nonexistent.default")); + + this.configurator.configure(getConfigPath("set-using-nonexistent.conf")); + + assertEquals("testSetUsingNonExistentDefault", System.getProperty("set.using.nonexistent.default")); + } + + @Test + void testSetUsingNonExistentOverride() throws Exception { + assertNull(System.getProperty("set.using.default")); + System.setProperty("set.using.default", "testSetUsingNonExistentOverride"); + + this.configurator.configure(getConfigPath("set-using-nonexistent.conf")); + + assertEquals("testSetUsingNonExistentOverride", System.getProperty("set.using.default")); + } + + @Test + void testSetUsingExistentOverride() throws Exception { + assertNull(System.getProperty("set.using.existent")); + System.setProperty("set.using.existent", "testSetUsingExistentOverride"); + + this.configurator.configure(getConfigPath("set-using-existent.conf")); + + assertEquals("testSetUsingExistentOverride", System.getProperty("set.using.existent")); + } + + @Test + void testSetUsingExistentDefault() throws Exception { + assertNull(System.getProperty("set.using.default")); + + this.configurator.configure(getConfigPath("set-using-existent.conf")); + + assertEquals("testSetUsingExistentDefault", System.getProperty("set.using.default")); + } + + @Test + void testSetUsingMissingDefault() throws Exception { + assertNull(System.getProperty("set.using.missing")); + + this.configurator.configure(getConfigPath("set-using-missing.conf")); + + assertEquals("testSetUsingMissingDefault", System.getProperty("set.using.missing")); + } + + @Test + void testSetUsingMissingOverride() throws Exception { + assertNull(System.getProperty("set.using.missing")); + System.setProperty("set.using.missing", "testSetUsingMissingOverride"); + + this.configurator.configure(getConfigPath("set-using-missing.conf")); + + assertEquals("testSetUsingMissingOverride", System.getProperty("set.using.missing")); + } + + @Test + void testSetUsingFilteredDefault() throws Exception { + assertNull(System.getProperty("set.using.filtered.default")); + + this.configurator.configure(getConfigPath("set-using-missing.conf")); + + assertEquals(System.getProperty("user.home") + "/m2", System.getProperty("set.using.filtered.default")); + } + + @Test + void testFromFromFrom() throws Exception { + this.configurator.configure(getConfigPath("valid-from-from-from.conf")); + + assertEquals("com.from.from.from.Main", this.launcher.getMainClassName()); + + assertEquals("from", this.launcher.getMainRealmName()); + + ClassWorld world = this.launcher.getWorld(); + + ClassRealm antRealm = world.getRealm("ant"); + Collection antImportRealms = antRealm.getImportRealms(); + assertEquals(1, antImportRealms.size()); + assertEquals("from", antImportRealms.stream().findFirst().get().getId()); + + ClassRealm mavenRealm = world.getRealm("maven"); + Collection mavenImportRealms = mavenRealm.getImportRealms(); + assertEquals(1, mavenImportRealms.size()); + assertEquals("from", mavenImportRealms.stream().findFirst().get().getId()); + + ClassRealm globRealm = world.getRealm("glob"); + Collection globImportRealms = globRealm.getImportRealms(); + assertEquals(0, globImportRealms.size()); + + ClassRealm fromRealm = world.getRealm("from"); + Collection fromImportRealms = fromRealm.getImportRealms(); + assertEquals(0, fromImportRealms.size()); + } + + private FileInputStream getConfigPath(String name) throws Exception { + return new FileInputStream(new File(new File(TestUtil.getBasedir(), "src/test/test-data"), name)); + } + + private void assertArrayContains(URL[] array, URL url) { + for (URL value : array) { + if (url.equals(value)) { + return; + } + } + fail("URL (" + url + ") not found in array of URLs"); + } +} diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/launcher/LauncherTest.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/launcher/LauncherTest.java new file mode 100644 index 000000000000..39ca08cb7303 --- /dev/null +++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/launcher/LauncherTest.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.launcher; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.io.File; +import java.io.FileInputStream; + +import org.codehaus.plexus.classworlds.AbstractClassWorldsTestCase; +import org.codehaus.plexus.classworlds.TestUtil; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; + +class LauncherTest extends AbstractClassWorldsTestCase { + private Launcher launcher; + + @BeforeEach + public void setUp() { + this.launcher = new Launcher(); + + this.launcher.setSystemClassLoader(Thread.currentThread().getContextClassLoader()); + } + + @AfterEach + public void tearDown() { + this.launcher = null; + } + + @Test + void testConfigureValid() throws Exception { + launcher.configure(getConfigPath("valid-launch.conf")); + + Class mainClass = launcher.getMainClass(); + + assertNotNull(mainClass); + + assertEquals("a.A", mainClass.getName()); + + assertEquals("app", launcher.getMainRealm().getId()); + } + + @Test + void testLaunchValidStandard() throws Exception { + launcher.configure(getConfigPath("valid-launch.conf")); + + launcher.launch(new String[] {}); + } + + @Test + void testLaunchValidStandardExitCode() throws Exception { + launcher.configure(getConfigPath("valid-launch-exitCode.conf")); + + launcher.launch(new String[] {}); + + assertEquals(15, launcher.getExitCode(), "check exit code"); + } + + @Test + void testLaunchValidEnhanced() throws Exception { + launcher.configure(getConfigPath("valid-enh-launch.conf")); + + launcher.launch(new String[] {}); + } + + @Test + void testLaunchValidEnhancedExitCode() throws Exception { + launcher.configure(getConfigPath("valid-enh-launch-exitCode.conf")); + + launcher.launch(new String[] {}); + + assertEquals(45, launcher.getExitCode(), "check exit code"); + } + + @Test + void testLaunchNoSuchMethod() throws Exception { + launcher.configure(getConfigPath("launch-nomethod.conf")); + + try { + launcher.launch(new String[] {}); + fail("should have thrown NoSuchMethodException"); + } catch (NoSuchMethodException e) { + // expected and correct + } + } + + @Test + void testLaunchClassNotFound() throws Exception { + launcher.configure(getConfigPath("launch-noclass.conf")); + + try { + launcher.launch(new String[] {}); + fail("throw ClassNotFoundException"); + } catch (ClassNotFoundException e) { + // expected and correct + } + } + + private FileInputStream getConfigPath(String name) throws Exception { + String basedir = TestUtil.getBasedir(); + + return new FileInputStream(new File(new File(basedir, "src/test/test-data"), name)); + } +} diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/ClassRealmImplTest.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/ClassRealmImplTest.java new file mode 100644 index 000000000000..6d5d2f8f9afe --- /dev/null +++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/ClassRealmImplTest.java @@ -0,0 +1,482 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.realm; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.codehaus.plexus.classworlds.AbstractClassWorldsTestCase; +import org.codehaus.plexus.classworlds.ClassWorld; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.fail; + +class ClassRealmImplTest extends AbstractClassWorldsTestCase { + private ClassWorld world; + + @BeforeEach + public void setUp() { + this.world = new ClassWorld(); + } + + @AfterEach + public void tearDown() { + this.world = null; + } + + @Test + void testNewRealm() throws Exception { + ClassRealm realm = this.world.newRealm("foo"); + + assertNotNull(realm); + + assertSame(this.world, realm.getWorld()); + + assertEquals("foo", realm.getId()); + } + + @Test + void testLocateSourceRealmNoImports() { + ClassRealm realm = new ClassRealm(this.world, "foo", null); + + assertSame(null, realm.getImportClassLoader("com.werken.Stuff")); + } + + @Test + void testLocateSourceRealmSimpleImport() throws Exception { + ClassRealm mainRealm = this.world.newRealm("main"); + + ClassRealm werkflowRealm = this.world.newRealm("werkflow"); + + mainRealm.importFrom("werkflow", "com.werken.werkflow"); + + assertSame(werkflowRealm, mainRealm.getImportClassLoader("com.werken.werkflow.WerkflowEngine")); + + assertSame(werkflowRealm, mainRealm.getImportClassLoader("com/werken/werkflow/some.properties")); + + assertSame(werkflowRealm, mainRealm.getImportClassLoader("com.werken.werkflow.process.ProcessManager")); + + assertSame(null, mainRealm.getImportClassLoader("com.werken.blissed.Process")); + + assertSame(null, mainRealm.getImportClassLoader("java.lang.Object")); + + assertSame(null, mainRealm.getImportClassLoader("NoviceProgrammerClass")); + } + + @Test + void testLocateSourceRealmMultipleImport() throws Exception { + ClassRealm mainRealm = this.world.newRealm("main"); + + ClassRealm werkflowRealm = this.world.newRealm("werkflow"); + + ClassRealm blissedRealm = this.world.newRealm("blissed"); + + mainRealm.importFrom("werkflow", "com.werken.werkflow"); + + mainRealm.importFrom("blissed", "com.werken.blissed"); + + assertSame(werkflowRealm, mainRealm.getImportClassLoader("com.werken.werkflow.WerkflowEngine")); + + assertSame(werkflowRealm, mainRealm.getImportClassLoader("com.werken.werkflow.process.ProcessManager")); + + assertSame(blissedRealm, mainRealm.getImportClassLoader("com.werken.blissed.Process")); + + assertSame(blissedRealm, mainRealm.getImportClassLoader("com.werken.blissed.guard.BooleanGuard")); + + assertSame(null, mainRealm.getImportClassLoader("java.lang.Object")); + + assertSame(null, mainRealm.getImportClassLoader("NoviceProgrammerClass")); + } + + @Test + void testLocateSourceRealmHierachy() throws Exception { + ClassRealm mainRealm = this.world.newRealm("main"); + + ClassRealm fooRealm = this.world.newRealm("foo"); + + ClassRealm fooBarRealm = this.world.newRealm("fooBar"); + + ClassRealm fooBarBazRealm = this.world.newRealm("fooBarBaz"); + + mainRealm.importFrom("foo", "foo"); + + mainRealm.importFrom("fooBar", "foo.bar"); + + mainRealm.importFrom("fooBarBaz", "foo.bar.baz"); + + assertSame(fooRealm, mainRealm.getImportClassLoader("foo.Goober")); + + assertSame(fooRealm, mainRealm.getImportClassLoader("foo.cheese.Goober")); + + assertSame(fooBarRealm, mainRealm.getImportClassLoader("foo.bar.Goober")); + + assertSame(fooBarRealm, mainRealm.getImportClassLoader("foo.bar.cheese.Goober")); + + assertSame(fooBarBazRealm, mainRealm.getImportClassLoader("foo.bar.baz.Goober")); + + assertSame(fooBarBazRealm, mainRealm.getImportClassLoader("foo.bar.baz.cheese.Goober")); + + assertSame(null, mainRealm.getImportClassLoader("java.lang.Object")); + + assertSame(null, mainRealm.getImportClassLoader("NoviceProgrammerClass")); + } + + @Test + void testLocateSourceRealmHierachyReverse() throws Exception { + ClassRealm fooBarBazRealm = this.world.newRealm("fooBarBaz"); + + ClassRealm fooBarRealm = this.world.newRealm("fooBar"); + + ClassRealm fooRealm = this.world.newRealm("foo"); + + ClassRealm mainRealm = this.world.newRealm("main"); + + mainRealm.importFrom("fooBarBaz", "foo.bar.baz"); + + mainRealm.importFrom("fooBar", "foo.bar"); + + mainRealm.importFrom("foo", "foo"); + + assertSame(fooRealm, mainRealm.getImportClassLoader("foo.Goober")); + + assertSame(fooRealm, mainRealm.getImportClassLoader("foo.cheese.Goober")); + + assertSame(fooBarRealm, mainRealm.getImportClassLoader("foo.bar.Goober")); + + assertSame(fooBarRealm, mainRealm.getImportClassLoader("foo.bar.cheese.Goober")); + + assertSame(fooBarBazRealm, mainRealm.getImportClassLoader("foo.bar.baz.Goober")); + + assertSame(fooBarBazRealm, mainRealm.getImportClassLoader("foo.bar.baz.cheese.Goober")); + + assertSame(null, mainRealm.getImportClassLoader("java.lang.Object")); + + assertSame(null, mainRealm.getImportClassLoader("NoviceProgrammerClass")); + } + + @Test + void testLoadClassSystemClass() throws Exception { + ClassRealm mainRealm = this.world.newRealm("main"); + + Class cls = mainRealm.loadClass("java.lang.Object"); + + assertNotNull(cls); + } + + @Test + void testLoadClassNonSystemClass() throws Exception { + ClassRealm mainRealm = this.world.newRealm("main"); + + try { + Class c = mainRealm.loadClass("com.werken.projectz.UberThing"); + + System.out.println("c = " + c); + + fail("A ClassNotFoundException should be thrown!"); + } catch (ClassNotFoundException e) { + // expected and correct + } + } + + @Test + void testLoadClassClassWorldsClass() throws Exception { + ClassRealm mainRealm = this.world.newRealm("main"); + + Class cls = mainRealm.loadClass("org.codehaus.plexus.classworlds.ClassWorld"); + + assertNotNull(cls); + + assertSame(ClassWorld.class, cls); + } + + @Test + void testLoadClassLocal() throws Exception { + ClassRealm mainRealm = this.world.newRealm("main"); + + try { + mainRealm.loadClass("a.A"); + } catch (ClassNotFoundException e) { + // expected and correct + } + + mainRealm.addURL(getJarUrl("a.jar")); + + Class classA = mainRealm.loadClass("a.A"); + + assertNotNull(classA); + + ClassRealm otherRealm = this.world.newRealm("other"); + + try { + otherRealm.loadClass("a.A"); + } catch (ClassNotFoundException e) { + // expected and correct + } + } + + @Test + void testLoadClassImported() throws Exception { + ClassRealm mainRealm = this.world.newRealm("main"); + + ClassRealm realmA = this.world.newRealm("realmA"); + + try { + realmA.loadClass("a.A"); + + fail("realmA.loadClass(a.A) should have thrown a ClassNotFoundException"); + } catch (ClassNotFoundException e) { + // expected and correct + } + + realmA.addURL(getJarUrl("a.jar")); + + try { + mainRealm.loadClass("a.A"); + + fail("mainRealm.loadClass(a.A) should have thrown a ClassNotFoundException"); + } catch (ClassNotFoundException e) { + // expected and correct + } + + mainRealm.importFrom("realmA", "a"); + + Class classA = realmA.loadClass("a.A"); + + assertNotNull(classA); + + assertEquals(realmA, classA.getClassLoader()); + + Class classMain = mainRealm.loadClass("a.A"); + + assertNotNull(classMain); + + assertEquals(realmA, classMain.getClassLoader()); + + assertSame(classA, classMain); + } + + @Test + void testLoadClassPackage() throws Exception { + ClassRealm realmA = this.world.newRealm("realmA"); + realmA.addURL(getJarUrl("a.jar")); + + Class clazz = realmA.loadClass("a.A"); + assertNotNull(clazz); + assertEquals("a.A", clazz.getName()); + + Package p = clazz.getPackage(); + assertNotNull(p); + assertEquals("a", p.getName(), "p.getName()"); + } + + @Test + void testLoadClassComplex() throws Exception { + ClassRealm realmA = this.world.newRealm("realmA"); + ClassRealm realmB = this.world.newRealm("realmB"); + ClassRealm realmC = this.world.newRealm("realmC"); + + realmA.addURL(getJarUrl("a.jar")); + realmB.addURL(getJarUrl("b.jar")); + realmC.addURL(getJarUrl("c.jar")); + + realmC.importFrom("realmA", "a"); + + realmC.importFrom("realmB", "b"); + + realmA.importFrom("realmC", "c"); + + Class classAA = realmA.loadClass("a.A"); + Class classBB = realmB.loadClass("b.B"); + Class classCC = realmC.loadClass("c.C"); + + assertNotNull(classAA); + assertNotNull(classBB); + assertNotNull(classCC); + + assertEquals(realmA, classAA.getClassLoader()); + + assertEquals(realmB, classBB.getClassLoader()); + + assertEquals(realmC, classCC.getClassLoader()); + + // load from C + + Class classAC = realmC.loadClass("a.A"); + + assertNotNull(classAC); + + assertSame(classAA, classAC); + + assertEquals(realmA, classAC.getClassLoader()); + + Class classBC = realmC.loadClass("b.B"); + + assertNotNull(classBC); + + assertSame(classBB, classBC); + + assertEquals(realmB, classBC.getClassLoader()); + + // load from A + + Class classCA = realmA.loadClass("c.C"); + + assertNotNull(classCA); + + assertSame(classCC, classCA); + + assertEquals(realmC, classCA.getClassLoader()); + + try { + realmA.loadClass("b.B"); + fail("throw ClassNotFoundException"); + } catch (ClassNotFoundException e) { + // expected and correct + } + + // load from B + + try { + realmB.loadClass("a.A"); + fail("throw ClassNotFoundException"); + } catch (ClassNotFoundException e) { + // expected and correct + } + + try { + realmB.loadClass("c.C"); + fail("throw ClassNotFoundException"); + } catch (ClassNotFoundException e) { + // expected and correct + } + } + + @Test + void testLoadClassClassWorldsClassRepeatedly() throws Exception { + ClassRealm mainRealm = this.world.newRealm("main"); + + for (int i = 0; i < 100; i++) { + Class cls = mainRealm.loadClass("org.codehaus.plexus.classworlds.ClassWorld"); + + assertNotNull(cls); + + assertSame(ClassWorld.class, cls); + } + } + + @Test + void testLoadClassWithModuleNameJava9() { + final ExtendedClassRealm mainRealm = new ExtendedClassRealm(world); + mainRealm.addURL(getJarUrl("a.jar")); + assertNotNull(mainRealm.simulateLoadClassFromModule("a.A")); + } + + @Test + void testGetResourcesBaseBeforeSelf() throws Exception { + String resource = "common.properties"; + + ClassRealm base = this.world.newRealm("realmA"); + base.addURL(getJarUrl("a.jar")); + + URL baseUrl = base.getResource(resource); + assertNotNull(baseUrl); + + ClassRealm sub = this.world.newRealm("realmB", base); + sub.addURL(getJarUrl("b.jar")); + + URL subUrl = sub.getResource(resource); + assertNotNull(subUrl); + + assertEquals(baseUrl, subUrl); + + List urls = new ArrayList<>(); + for (URL url : Collections.list(sub.getResources(resource))) { + String path = url.toString(); + path = path.substring(path.lastIndexOf('/', path.lastIndexOf(".jar!"))); + urls.add(path); + } + assertEquals(Arrays.asList("/a.jar!/common.properties", "/b.jar!/common.properties"), urls); + } + + @Test + void testGetResourcesSelfBeforeParent() throws Exception { + String resource = "common.properties"; + + ClassRealm parent = this.world.newRealm("realmA"); + parent.addURL(getJarUrl("a.jar")); + + URL parentUrl = parent.getResource(resource); + assertNotNull(parentUrl); + + ClassRealm child = parent.createChildRealm("realmB"); + child.addURL(getJarUrl("b.jar")); + + URL childUrl = child.getResource(resource); + assertNotNull(childUrl); + + List urls = Collections.list(child.getResources(resource)); + assertNotNull(urls); + assertEquals(Arrays.asList(childUrl, parentUrl), urls); + } + + /** + * Simulates new {@code java.lang.ClassLoader#findClass(String,String)} introduced with Java 9. + * It is reversed in terms of inheritance but enables to simulate the same behavior in these tests. + * @see ClassLoader#findClass(String,String) + */ + private static class ExtendedClassRealm extends ClassRealm { + + ExtendedClassRealm(final ClassWorld world) { + super(world, "java9", Thread.currentThread().getContextClassLoader()); + } + + public Class simulateLoadClassFromModule(final String name) { + synchronized (getClassLoadingLock(name)) { + Class c = findLoadedClass(name); + if (c == null) { + c = findClass(null, name); + } + return c; + } + } + } +} diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/DefaultClassRealmTest.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/DefaultClassRealmTest.java new file mode 100644 index 000000000000..c79468c97367 --- /dev/null +++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/DefaultClassRealmTest.java @@ -0,0 +1,346 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.realm; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Collections; +import java.util.concurrent.CountDownLatch; + +import org.codehaus.plexus.classworlds.AbstractClassWorldsTestCase; +import org.codehaus.plexus.classworlds.ClassWorld; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +class DefaultClassRealmTest extends AbstractClassWorldsTestCase { + // ---------------------------------------------------------------------- + // Class testing + // ---------------------------------------------------------------------- + + @Test + void testLoadClassFromRealm() throws Exception { + ClassRealm mainRealm = new ClassRealm(new ClassWorld(), "main", null); + + mainRealm.addURL(getJarUrl("component0-1.0.jar")); + + loadClass(mainRealm, "org.codehaus.plexus.Component0"); + } + + @Test + void testLoadClassFromChildRealmWhereClassIsLocatedInParentRealm() throws Exception { + ClassRealm mainRealm = new ClassRealm(new ClassWorld(), "main", null); + + mainRealm.addURL(getJarUrl("component0-1.0.jar")); + + ClassRealm childRealm = mainRealm.createChildRealm("child"); + + loadClass(childRealm, "org.codehaus.plexus.Component0"); + } + + @Test + void testLoadClassFromChildRealmWhereClassIsLocatedInGrantParentRealm() throws Exception { + ClassRealm mainRealm = new ClassRealm(new ClassWorld(), "main", null); + + mainRealm.addURL(getJarUrl("component0-1.0.jar")); + + ClassRealm childRealm = mainRealm.createChildRealm("child"); + + ClassRealm grandchildRealm = childRealm.createChildRealm("grandchild"); + + loadClass(grandchildRealm, "org.codehaus.plexus.Component0"); + } + + @Test + void testLoadClassFromChildRealmWhereClassIsLocatedInBothChildRealmAndParentRealm() throws Exception { + ClassRealm mainRealm = new ClassRealm(new ClassWorld(), "parent", null); + + mainRealm.addURL(getJarUrl("component5-1.0.jar")); + + ClassRealm childRealm = mainRealm.createChildRealm("child"); + + childRealm.addURL(getJarUrl("component5-2.0.jar")); + + Class cls = loadClass(childRealm, "test.Component5"); + + assertSame(childRealm, cls.getClassLoader()); + assertEquals(1, cls.getMethods().length); + assertEquals("printNew", cls.getMethods()[0].getName()); + } + + @Test + void testLoadNonExistentClass() { + ClassRealm mainRealm = new ClassRealm(new ClassWorld(), "main", null); + + mainRealm.addURL(getJarUrl("component0-1.0.jar")); + + try { + mainRealm.loadClass("org.foo.bar.NonExistentClass"); + + fail("A ClassNotFoundException should have been thrown!"); + } catch (ClassNotFoundException e) { + // expected + } + } + + @Test + void testImport() throws Exception { + ClassWorld world = new ClassWorld(); + + ClassRealm r0 = world.newRealm("r0"); + + ClassRealm r1 = world.newRealm("r1"); + + r0.addURL(getJarUrl("component0-1.0.jar")); + + r1.importFrom("r0", "org.codehaus.plexus"); + + loadClass(r1, "org.codehaus.plexus.Component0"); + } + + @Test + void testParentImport() throws Exception { + ClassWorld world = new ClassWorld(); + + ClassRealm parent = world.newRealm("parent"); + + ClassRealm child = world.newRealm("child"); + + parent.addURL(getJarUrl("component0-1.0.jar")); + + child.setParentRealm(parent); + + Class type = loadClass(child, "org.codehaus.plexus.Component0"); + + child.importFromParent("non-existing"); + + assertSame(null, loadClassOrNull(child, "org.codehaus.plexus.Component0")); + + child.importFromParent("org.codehaus.plexus"); + + assertSame(type, loadClass(child, "org.codehaus.plexus.Component0")); + } + + @Test + void testLoadClassFromBaseClassLoaderBeforeSelf() throws Exception { + ClassWorld world = new ClassWorld(); + + ClassRealm base = world.newRealm("base"); + + base.addURL(getJarUrl("a.jar")); + + ClassRealm child = world.newRealm("child", base); + + child.addURL(getJarUrl("a.jar")); + + Class baseClass = loadClass(base, "a.A"); + Class childClass = loadClass(child, "a.A"); + + assertSame(base, baseClass.getClassLoader()); + assertSame(base, childClass.getClassLoader()); + assertSame(baseClass, childClass); + } + + @Test + void testLoadClassFromRealmWithCircularClassReferences() throws Exception { + ClassRealm mainRealm = new ClassRealm(new ClassWorld(), "main", null); + + mainRealm.addURL(getJarUrl("circular-0.1.jar")); + + /* + * This was reported to fail with a ClassCircularityError in IBM JDK 1.5.0-SR2, 1.5.0-SR7 and 1.6.0-SR2. It + * works in IBM JDK 1.5.0-SR10 and 1.6.0-SR6. + */ + loadClass(mainRealm, "A$C"); + } + + // ---------------------------------------------------------------------- + // Resource testing + // ---------------------------------------------------------------------- + + @Test + void testResource() throws Exception { + ClassRealm mainRealm = new ClassRealm(new ClassWorld(), "main", null); + + mainRealm.addURL(getJarUrl("component0-1.0.jar")); + + getResource(mainRealm, "META-INF/plexus/components.xml"); + } + + @Test + void testMalformedResource() throws Exception { + URL jarUrl = getJarUrl("component0-1.0.jar"); + + ClassRealm mainRealm = new ClassRealm(new ClassWorld(), "main", null); + + mainRealm.addURL(jarUrl); + + ClassLoader officialClassLoader = new URLClassLoader(new URL[] {jarUrl}); + + String resource = "META-INF/plexus/components.xml"; + + assertNotNull(mainRealm.getResource(resource)); + assertNotNull(officialClassLoader.getResource(resource)); + + /* + * NOTE: Resource names with a leading slash are invalid when passed to a class loader and must not be found! + * One can use a leading slash in Class.getResource() but not in ClassLoader.getResource(). + */ + + assertSame(null, mainRealm.getResource("/" + resource)); + assertSame(null, officialClassLoader.getResource("/" + resource)); + } + + @Test + void testFindResourceOnlyScansSelf() throws Exception { + ClassRealm mainRealm = new ClassRealm(new ClassWorld(), "main", null); + + mainRealm.addURL(getJarUrl("a.jar")); + + ClassRealm childRealm = mainRealm.createChildRealm("child"); + + childRealm.addURL(getJarUrl("b.jar")); + + assertNotNull(childRealm.getResource("a.properties")); + assertNotNull(childRealm.getResource("b.properties")); + + assertNull(childRealm.findResource("a.properties")); + + assertNotNull(childRealm.findResource("b.properties")); + } + + @Test + void testFindResourcesOnlyScansSelf() throws Exception { + ClassRealm mainRealm = new ClassRealm(new ClassWorld(), "main", null); + + mainRealm.addURL(getJarUrl("a.jar")); + + ClassRealm childRealm = mainRealm.createChildRealm("child"); + + childRealm.addURL(getJarUrl("b.jar")); + + assertTrue(childRealm.getResources("a.properties").hasMoreElements()); + assertTrue(childRealm.getResources("b.properties").hasMoreElements()); + + assertFalse(childRealm.findResources("a.properties").hasMoreElements()); + + assertTrue(childRealm.findResources("b.properties").hasMoreElements()); + } + + /** Should never deadlock. Ever */ + @Test + void testParallelDeadlockClassRealm() throws InterruptedException { + for (int i = 0; i < 100; i++) { + doOneDeadlockAttempt(); + } + } + + private void doOneDeadlockAttempt() throws InterruptedException { + // Deadlock sample graciously ripped from http://docs.oracle.com/javase/7/docs/technotes/guides/lang/cl-mt.html + final ClassRealm cl1 = new ClassRealm(new ClassWorld(), "cl1", null); + final ClassRealm cl2 = new ClassRealm(new ClassWorld(), "cl2", cl1); + cl1.setParentRealm(cl2); + cl1.addURL(getJarUrl("deadlock.jar")); + cl2.addURL(getJarUrl("deadlock.jar")); + final CountDownLatch latch = new CountDownLatch(1); + + Runnable r1 = () -> { + try { + latch.await(); + cl1.loadClass("deadlock.A"); + } catch (ClassNotFoundException | InterruptedException e) { + throw new RuntimeException(e); + } + }; + + Runnable r2 = () -> { + try { + latch.await(); + cl1.loadClass("deadlock.C"); + } catch (ClassNotFoundException | InterruptedException e) { + throw new RuntimeException(e); + } + }; + + Thread thread = new Thread(r1); + thread.start(); + Thread thread1 = new Thread(r2); + thread1.start(); + latch.countDown(); + thread.join(); + thread1.join(); + } + + // ---------------------------------------------------------------------- + // + // ---------------------------------------------------------------------- + + private Class loadClassOrNull(ClassRealm realm, String name) { + try { + return loadClass(realm, name); + } catch (ClassNotFoundException e) { + return null; + } + } + + private Class loadClass(ClassRealm realm, String name) throws ClassNotFoundException { + Class cls = realm.loadClass(name); + + /* + * NOTE: Load the class both directly from the realm and indirectly from an (ordinary) child class loader which + * uses the specified class realm for parent delegation. The child class loader itself has no additional class + * path entries but relies entirely on the provided class realm. Hence, the created child class loader should in + * theory be able to load exactly the same classes/resources as the underlying class realm. In practice, it will + * test that class realms properly integrate into the standard Java class loader hierarchy. + */ + ClassLoader childLoader = new URLClassLoader(new URL[0], realm); + assertEquals(cls, childLoader.loadClass(name)); + + return cls; + } + + private void getResource(ClassRealm realm, String name) throws Exception { + ClassLoader childLoader = new URLClassLoader(new URL[0], realm); + assertNotNull(realm.getResource(name)); + assertEquals(realm.getResource(name), childLoader.getResource(name)); + assertEquals(Collections.list(realm.getResources(name)), Collections.list(childLoader.getResources(name))); + } +} diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/EntryTest.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/EntryTest.java new file mode 100644 index 000000000000..f0115df603d0 --- /dev/null +++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/EntryTest.java @@ -0,0 +1,186 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.realm; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import org.codehaus.plexus.classworlds.AbstractClassWorldsTestCase; +import org.codehaus.plexus.classworlds.ClassWorld; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author Ben Walding + */ +class EntryTest extends AbstractClassWorldsTestCase { + + @Test + void testCompareTo() throws Exception { + ClassWorld cw = new ClassWorld(); + ClassRealm r = cw.newRealm("test1"); + + Entry entry1 = new Entry(r, "org.test"); + Entry entry2 = new Entry(r, "org.test.impl"); + + assertTrue(entry1.compareTo(entry2) > 0, "org.test > org.test.impl"); + } + + /** + * Tests the equality is realm independant + */ + @Test + void testEquals() throws DuplicateRealmException { + ClassWorld cw = new ClassWorld(); + ClassRealm r1 = cw.newRealm("test1"); + ClassRealm r2 = cw.newRealm("test2"); + + Entry entry1 = new Entry(r1, "org.test"); + Entry entry2 = new Entry(r2, "org.test"); + + assertEquals(entry1, entry2, "entry1 == entry2"); + assertEquals(entry1.hashCode(), entry2.hashCode(), "entry1.hashCode() == entry2.hashCode()"); + } + + @Test + void testMatchesClassByPackageImport() throws Exception { + ClassWorld cw = new ClassWorld(); + ClassRealm r = cw.newRealm("test1"); + + Entry entry = new Entry(r, "org.test"); + + assertTrue(entry.matches("org.test.MyClass")); + assertTrue(entry.matches("org.test.MyClass$NestedClass")); + assertTrue(entry.matches("org.test.MyClassUtils")); + assertTrue(entry.matches("org.test.impl.MyClass")); + assertFalse(entry.matches("org.tests.AnotherClass")); + } + + @Test + void testMatchesClassByClassImport() throws Exception { + ClassWorld cw = new ClassWorld(); + ClassRealm r = cw.newRealm("test1"); + + Entry entry = new Entry(r, "org.test.MyClass"); + + assertTrue(entry.matches("org.test.MyClass")); + assertTrue(entry.matches("org.test.MyClass$NestedClass")); + assertFalse(entry.matches("org.test.MyClassUtils")); + assertFalse(entry.matches("org.test.AnotherClass")); + } + + @Test + void testMatchesResourceByPackageImport() throws Exception { + ClassWorld cw = new ClassWorld(); + ClassRealm r = cw.newRealm("test1"); + + Entry entry = new Entry(r, "org.test"); + + assertTrue(entry.matches("org/test/MyClass.class")); + assertTrue(entry.matches("org/test/MyClass$NestedClass.class")); + assertTrue(entry.matches("org/test/MyClasses.properties")); + assertTrue(entry.matches("org/test/impl/MyClass.class")); + assertFalse(entry.matches("org/tests/AnotherClass.class")); + } + + @Test + void testMatchesResourceByClassImport() throws Exception { + ClassWorld cw = new ClassWorld(); + ClassRealm r = cw.newRealm("test1"); + + Entry entry = new Entry(r, "org.test.MyClass"); + + assertTrue(entry.matches("org/test/MyClass.class")); + assertTrue(entry.matches("org/test/MyClass$NestedClass.class")); + assertFalse(entry.matches("org/test/MyClass.properties")); + assertFalse(entry.matches("org/test/AnotherClass")); + } + + @Test + void testMatchesAllImport() throws Exception { + ClassWorld cw = new ClassWorld(); + ClassRealm r = cw.newRealm("test1"); + + Entry entry = new Entry(r, ""); + + assertTrue(entry.matches("org.test.MyClass")); + assertTrue(entry.matches("org.test.MyClass$NestedClass")); + assertTrue(entry.matches("org/test/MyClass.class")); + assertTrue(entry.matches("org/test/MyClass.properties")); + } + + @Test + void testMatchesResourceByResourceImport() throws Exception { + ClassWorld cw = new ClassWorld(); + ClassRealm r = cw.newRealm("test1"); + + Entry entry1 = new Entry(r, "some.properties"); + + assertTrue(entry1.matches("some.properties")); + assertFalse(entry1.matches("other.properties")); + + Entry entry2 = new Entry(r, "org/test/some.properties"); + + assertTrue(entry2.matches("org/test/some.properties")); + assertFalse(entry2.matches("org/test/other.properties")); + } + + @Test + void testMatchesClassByExactPackageImport() throws Exception { + ClassWorld cw = new ClassWorld(); + ClassRealm r = cw.newRealm("test1"); + + Entry entry = new Entry(r, "org.test.*"); + + assertTrue(entry.matches("org.test.MyClass")); + assertTrue(entry.matches("org.test.MyClass$NestedClass")); + assertTrue(entry.matches("org.test.MyClassUtils")); + assertFalse(entry.matches("org.test.impl.MyClass")); + assertFalse(entry.matches("org.tests.AnotherClass")); + } + + @Test + void testMatchesResourceByExactPackageImport() throws Exception { + ClassWorld cw = new ClassWorld(); + ClassRealm r = cw.newRealm("test1"); + + Entry entry = new Entry(r, "org.test.*"); + + assertTrue(entry.matches("org/test/MyClass.class")); + assertTrue(entry.matches("org/test/MyClass$NestedClass.class")); + assertTrue(entry.matches("org/test/MyClasses.properties")); + assertFalse(entry.matches("org/test/impl/MyClass.class")); + assertFalse(entry.matches("org/tests/AnotherClass.class")); + } +} diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/FilteredClassRealmTest.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/FilteredClassRealmTest.java new file mode 100644 index 000000000000..c0b8d6ec4b71 --- /dev/null +++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/realm/FilteredClassRealmTest.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.realm; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Predicate; + +import org.codehaus.plexus.classworlds.AbstractClassWorldsTestCase; +import org.codehaus.plexus.classworlds.ClassWorld; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class FilteredClassRealmTest extends AbstractClassWorldsTestCase { + private ClassWorld world; + private ClassRealm realmA; + + @BeforeEach + public void setUp() throws DuplicateRealmException { + this.world = new ClassWorld(); + // only allow loading resources whose names start with "a." + Set allowedResourcePrefixes = new HashSet<>(); + allowedResourcePrefixes.add("a."); + allowedResourcePrefixes.add("a/Aa"); + realmA = this.world.newRealm("realmA", getClass().getClassLoader(), s -> allowedResourcePrefixes.stream() + .anyMatch(s::startsWith)); + } + + @Test + void testLoadResources() throws Exception { + realmA.addURL(getJarUrl("a.jar")); + assertNull(realmA.getResource("common.properties")); + assertFalse(realmA.getResources("common.properties").hasMoreElements()); + + assertNotNull(realmA.getResource("a.properties")); + assertTrue(realmA.getResources("a.properties").hasMoreElements()); + } + + @Test + void testLoadClass() throws ClassNotFoundException { + assertThrows(ClassNotFoundException.class, () -> realmA.loadClass("a.Aa")); + realmA.addURL(getJarUrl("a.jar")); + + assertNotNull(realmA.loadClass("a.Aa")); + assertThrows(ClassNotFoundException.class, () -> realmA.loadClass("a.A")); + + assertNotNull(realmA.loadClass("a.Aa")); + assertThrows(ClassNotFoundException.class, () -> realmA.loadClass("a.A")); + } + + @Test + void testLoadClassWithModule() throws IOException { + try (ExtendedFilteredClassRealm realmA = new ExtendedFilteredClassRealm(world, s -> s.startsWith("a/Aa"))) { + realmA.addURL(getJarUrl("a.jar")); + assertNotNull(realmA.simulateLoadClassFromModule("a.Aa")); + assertNull(realmA.simulateLoadClassFromModule("a.A")); + } + } + + /** + * Simulates new {@code java.lang.ClassLoader#findClass(String,String)} introduced with Java 9. + * It is reversed in terms of inheritance but enables to simulate the same behavior in these tests. + * @see ClassLoader#findClass(String,String) + * @see ClassRealmImplTest.ExtendedClassRealm + */ + static class ExtendedFilteredClassRealm extends FilteredClassRealm { + + ExtendedFilteredClassRealm(final ClassWorld world, Predicate filter) { + super(filter, world, "java9", Thread.currentThread().getContextClassLoader()); + } + + public Class simulateLoadClassFromModule(final String name) { + synchronized (getClassLoadingLock(name)) { + Class c = findLoadedClass(name); + if (c == null) { + c = findClass(null, name); + } + return c; + } + } + } +} diff --git a/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/strategy/StrategyTest.java b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/strategy/StrategyTest.java new file mode 100644 index 000000000000..d8ed59bb7c68 --- /dev/null +++ b/impl/maven-classworlds/src/test/java/org/codehaus/plexus/classworlds/strategy/StrategyTest.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.plexus.classworlds.strategy; + +/* + * Copyright 2001-2006 Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; + +import org.codehaus.plexus.classworlds.AbstractClassWorldsTestCase; +import org.codehaus.plexus.classworlds.ClassWorld; +import org.codehaus.plexus.classworlds.realm.ClassRealm; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +// jars within jars +// hierarchy vs graph + +class StrategyTest extends AbstractClassWorldsTestCase { + private ClassRealm realm; + + private Strategy strategy; + + @BeforeEach + public void setUp() throws Exception { + this.realm = new ClassWorld().newRealm("realm"); + this.strategy = this.realm.getStrategy(); + realm.addURL(getJarUrl("component0-1.0.jar")); + } + + @Test + void testLoadingOfApplicationClass() throws Exception { + assertNotNull(strategy.loadClass("org.codehaus.plexus.Component0")); + } + + @Test + void testLoadingOfApplicationClassThenDoingItAgain() throws Exception { + Class c = strategy.loadClass("org.codehaus.plexus.Component0"); + + assertNotNull(c); + + c = strategy.loadClass("org.codehaus.plexus.Component0"); + + assertNotNull(c); + } + + @Test + void testLoadingOfSystemClass() throws Exception { + assertNotNull(strategy.getRealm().loadClass("java.lang.Object")); + } + + @Test + void testLoadingOfNonExistentClass() { + try { + strategy.loadClass("org.codehaus.plexus.NonExistentComponent"); + + fail("Should have thrown a ClassNotFoundException!"); + } catch (ClassNotFoundException e) { + // do nothing + } + } + + @Test + void testGetApplicationResource() throws Exception { + URL resource = strategy.getResource("META-INF/plexus/components.xml"); + + assertNotNull(resource); + + String content = getContent(resource.openStream()); + + assertTrue(content.startsWith("")); + } + + @Test + void testGetSystemResource() { + assumeTrue( + getJavaVersion() < 9.0, + "Due to strong encapsulation you cannot get the java/lang/Object.class as resource since Java 9"); + + URL resource = strategy.getRealm().getResource("java/lang/Object.class"); + + assertNotNull(resource); + } + + @Test + void testFindResources() throws Exception { + realm.addURL(getJarUrl("component1-1.0.jar")); + + Enumeration e = strategy.getResources("META-INF/plexus/components.xml"); + assertNotNull(e); + + int resourceCount = 0; + while (e.hasMoreElements()) { + e.nextElement(); + resourceCount++; + } + assertEquals(2, resourceCount); + } + + protected String getContent(InputStream in) throws Exception { + byte[] buffer = new byte[1024]; + + int read; + + StringBuilder content = new StringBuilder(); + + while ((read = in.read(buffer, 0, 1024)) >= 0) { + content.append(new String(buffer, 0, read)); + } + + return content.toString(); + } + + private double getJavaVersion() { + return Double.parseDouble(System.getProperty("java.specification.version")); + } +} diff --git a/impl/maven-classworlds/src/test/test-data/a.jar b/impl/maven-classworlds/src/test/test-data/a.jar new file mode 100644 index 000000000000..9297ed84fab1 Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/a.jar differ diff --git a/impl/maven-classworlds/src/test/test-data/a.properties b/impl/maven-classworlds/src/test/test-data/a.properties new file mode 100644 index 000000000000..d0e2fad85157 --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/a.properties @@ -0,0 +1,18 @@ +################################################################################ +# +# Copyright 2001-2006 The Codehaus Foundation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +a properties diff --git a/impl/maven-classworlds/src/test/test-data/b.jar b/impl/maven-classworlds/src/test/test-data/b.jar new file mode 100644 index 000000000000..0776ca62b2e5 Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/b.jar differ diff --git a/impl/maven-classworlds/src/test/test-data/b_old.jar b/impl/maven-classworlds/src/test/test-data/b_old.jar new file mode 100644 index 000000000000..6ea6947e3323 Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/b_old.jar differ diff --git a/impl/maven-classworlds/src/test/test-data/c.jar b/impl/maven-classworlds/src/test/test-data/c.jar new file mode 100644 index 000000000000..8e48a862c7c8 Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/c.jar differ diff --git a/impl/maven-classworlds/src/test/test-data/circular-0.1.jar b/impl/maven-classworlds/src/test/test-data/circular-0.1.jar new file mode 100644 index 000000000000..0bfb3a472867 Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/circular-0.1.jar differ diff --git a/impl/maven-classworlds/src/test/test-data/component0-1.0.jar b/impl/maven-classworlds/src/test/test-data/component0-1.0.jar new file mode 100644 index 000000000000..8a4f4020eb15 Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/component0-1.0.jar differ diff --git a/impl/maven-classworlds/src/test/test-data/component1-1.0.jar b/impl/maven-classworlds/src/test/test-data/component1-1.0.jar new file mode 100644 index 000000000000..2e1e4566f4ec Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/component1-1.0.jar differ diff --git a/impl/maven-classworlds/src/test/test-data/component2-1.0.jar b/impl/maven-classworlds/src/test/test-data/component2-1.0.jar new file mode 100644 index 000000000000..e1629a4053b6 Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/component2-1.0.jar differ diff --git a/impl/maven-classworlds/src/test/test-data/component3-1.0.jar b/impl/maven-classworlds/src/test/test-data/component3-1.0.jar new file mode 100644 index 000000000000..211a32968e5c Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/component3-1.0.jar differ diff --git a/impl/maven-classworlds/src/test/test-data/component4-1.0.jar b/impl/maven-classworlds/src/test/test-data/component4-1.0.jar new file mode 100644 index 000000000000..e16fc3cea405 Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/component4-1.0.jar differ diff --git a/impl/maven-classworlds/src/test/test-data/component5-1.0.jar b/impl/maven-classworlds/src/test/test-data/component5-1.0.jar new file mode 100644 index 000000000000..39dc325d8ba0 Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/component5-1.0.jar differ diff --git a/impl/maven-classworlds/src/test/test-data/component5-2.0.jar b/impl/maven-classworlds/src/test/test-data/component5-2.0.jar new file mode 100644 index 000000000000..9e734a10a905 Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/component5-2.0.jar differ diff --git a/impl/maven-classworlds/src/test/test-data/d.jar b/impl/maven-classworlds/src/test/test-data/d.jar new file mode 100644 index 000000000000..f523220c776e Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/d.jar differ diff --git a/impl/maven-classworlds/src/test/test-data/deadlock.jar b/impl/maven-classworlds/src/test/test-data/deadlock.jar new file mode 100644 index 000000000000..95147bca36a8 Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/deadlock.jar differ diff --git a/impl/maven-classworlds/src/test/test-data/dupe-main.conf b/impl/maven-classworlds/src/test/test-data/dupe-main.conf new file mode 100644 index 000000000000..ddd111f9a7f5 --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/dupe-main.conf @@ -0,0 +1,3 @@ + +main is com.werken.Foo from foo +main is com.werken.Bar from bar diff --git a/impl/maven-classworlds/src/test/test-data/dupe-realm.conf b/impl/maven-classworlds/src/test/test-data/dupe-realm.conf new file mode 100644 index 000000000000..345f51f0e1bd --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/dupe-realm.conf @@ -0,0 +1,9 @@ + +[cheese] + +[dupe.realm] + +[potatoes] + +[dupe.realm] + diff --git a/impl/maven-classworlds/src/test/test-data/early-import.conf b/impl/maven-classworlds/src/test/test-data/early-import.conf new file mode 100644 index 000000000000..23378ecf009c --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/early-import.conf @@ -0,0 +1,2 @@ + +import org.xml.sax from root diff --git a/impl/maven-classworlds/src/test/test-data/from-from-0.0.1-from-load-import.jar b/impl/maven-classworlds/src/test/test-data/from-from-0.0.1-from-load-import.jar new file mode 100644 index 000000000000..1688b673dc33 Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/from-from-0.0.1-from-load-import.jar differ diff --git a/impl/maven-classworlds/src/test/test-data/inheritance.conf b/impl/maven-classworlds/src/test/test-data/inheritance.conf new file mode 100644 index 000000000000..977cc663e640 --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/inheritance.conf @@ -0,0 +1,18 @@ +# ------------------------------------------------------------ +# Define the main entry-point +# ------------------------------------------------------------ + +main is org.apache.maven.app.App from root.maven + +# ------------------------------------------------------------ +# Start defining realms +# ------------------------------------------------------------ + +[root] +load ${basedir}/src/test/test-data/a.jar + +[root.maven] +load ${basedir}/src/test/test-data/b.jar + +[root.maven.plugin] +load ${basedir}/src/test/test-data/c.jar diff --git a/impl/maven-classworlds/src/test/test-data/launch-noclass.conf b/impl/maven-classworlds/src/test/test-data/launch-noclass.conf new file mode 100644 index 000000000000..b6b844079612 --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/launch-noclass.conf @@ -0,0 +1,6 @@ + +main is b.Goober from app + +[app] + load ${basedir}/src/test/test-data/a.jar + load ${basedir}/src/test/test-data/b.jar diff --git a/impl/maven-classworlds/src/test/test-data/launch-nomethod.conf b/impl/maven-classworlds/src/test/test-data/launch-nomethod.conf new file mode 100644 index 000000000000..35954a3bf258 --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/launch-nomethod.conf @@ -0,0 +1,7 @@ + +main is c.C from app + +[app] + load ${basedir}/src/test/test-data/a.jar + load ${basedir}/src/test/test-data/b.jar + load ${basedir}/src/test/test-data/c.jar diff --git a/impl/maven-classworlds/src/test/test-data/nested.jar b/impl/maven-classworlds/src/test/test-data/nested.jar new file mode 100644 index 000000000000..821c14cbc36a Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/nested.jar differ diff --git a/impl/maven-classworlds/src/test/test-data/nested.properties b/impl/maven-classworlds/src/test/test-data/nested.properties new file mode 100644 index 000000000000..0462a1553247 --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/nested.properties @@ -0,0 +1,18 @@ +################################################################################ +# +# Copyright 2001-2006 The Codehaus Foundation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +nested.properties diff --git a/impl/maven-classworlds/src/test/test-data/optionally-existent.conf b/impl/maven-classworlds/src/test/test-data/optionally-existent.conf new file mode 100644 index 000000000000..fe102574c95d --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/optionally-existent.conf @@ -0,0 +1,14 @@ + +# ------------------------------------------------------------ +# Define the main entry-point +# ------------------------------------------------------------ + +main is org.apache.maven.app.App from opt + +# ------------------------------------------------------------ +# Start defining realms +# ------------------------------------------------------------ + +[opt] + optionally ${basedir}/target/test-lib/jakarta.xml.bind-api-4.0.2.jar + diff --git a/impl/maven-classworlds/src/test/test-data/optionally-nonexistent.conf b/impl/maven-classworlds/src/test/test-data/optionally-nonexistent.conf new file mode 100644 index 000000000000..4048e077e6e4 --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/optionally-nonexistent.conf @@ -0,0 +1,13 @@ + +# ------------------------------------------------------------ +# Define the main entry-point +# ------------------------------------------------------------ + +main is org.apache.maven.app.App from opt + +# ------------------------------------------------------------ +# Start defining realms +# ------------------------------------------------------------ + +[opt] + optionally /non/existant/path/*.jar diff --git a/impl/maven-classworlds/src/test/test-data/realm-syntax.conf b/impl/maven-classworlds/src/test/test-data/realm-syntax.conf new file mode 100644 index 000000000000..27b9e73f7d0c --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/realm-syntax.conf @@ -0,0 +1,3 @@ + +[foo +] diff --git a/impl/maven-classworlds/src/test/test-data/resources/classworlds.conf b/impl/maven-classworlds/src/test/test-data/resources/classworlds.conf new file mode 100644 index 000000000000..b91e608776a8 --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/resources/classworlds.conf @@ -0,0 +1,12 @@ +# ------------------------------------------------------------ +# Define the main entry-point +# ------------------------------------------------------------ + +main is foo from root + +# ------------------------------------------------------------ +# Start defining realms +# ------------------------------------------------------------ + +[root] +load ${basedir}/src/test/test-data/resources/werkflow.jar diff --git a/impl/maven-classworlds/src/test/test-data/resources/werkflow.jar b/impl/maven-classworlds/src/test/test-data/resources/werkflow.jar new file mode 100644 index 000000000000..315a149b7a72 Binary files /dev/null and b/impl/maven-classworlds/src/test/test-data/resources/werkflow.jar differ diff --git a/impl/maven-classworlds/src/test/test-data/set-using-existent.conf b/impl/maven-classworlds/src/test/test-data/set-using-existent.conf new file mode 100644 index 000000000000..11415b8ebf96 --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/set-using-existent.conf @@ -0,0 +1,19 @@ + +# ------------------------------------------------------------ +# Define the main entry-point +# ------------------------------------------------------------ + +main is org.apache.maven.app.App from opt + +# ------------------------------------------------------------ +# Set properties +# ------------------------------------------------------------ +set set.using.existent using ${basedir}/src/test/test-data/set-using-existent.properties +set set.using.default using ${basedir}/src/test/test-data/set-using-existent.properties default testSetUsingExistentDefault + +# ------------------------------------------------------------ +# Start defining realms +# ------------------------------------------------------------ + +[opt] + diff --git a/impl/maven-classworlds/src/test/test-data/set-using-existent.properties b/impl/maven-classworlds/src/test/test-data/set-using-existent.properties new file mode 100644 index 000000000000..6434aef94a33 --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/set-using-existent.properties @@ -0,0 +1,19 @@ +################################################################################ +# +# Copyright 2001-2006 The Codehaus Foundation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set.using.existent=testSetUsingExistent + diff --git a/impl/maven-classworlds/src/test/test-data/set-using-missing.conf b/impl/maven-classworlds/src/test/test-data/set-using-missing.conf new file mode 100644 index 000000000000..6f8ccc53d251 --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/set-using-missing.conf @@ -0,0 +1,19 @@ + +# ------------------------------------------------------------ +# Define the main entry-point +# ------------------------------------------------------------ + +main is org.apache.maven.app.App from opt + +# ------------------------------------------------------------ +# Set properties +# ------------------------------------------------------------ +set set.using.missing default testSetUsingMissingDefault +set set.using.filtered.default default ${user.home}/m2 + +# ------------------------------------------------------------ +# Start defining realms +# ------------------------------------------------------------ + +[opt] + diff --git a/impl/maven-classworlds/src/test/test-data/set-using-nonexistent.conf b/impl/maven-classworlds/src/test/test-data/set-using-nonexistent.conf new file mode 100644 index 000000000000..4640cf52c2e1 --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/set-using-nonexistent.conf @@ -0,0 +1,19 @@ + +# ------------------------------------------------------------ +# Define the main entry-point +# ------------------------------------------------------------ + +main is org.apache.maven.app.App from opt + +# ------------------------------------------------------------ +# Set properties +# ------------------------------------------------------------ +set set.using.nonexistent using ${basedir}/src/test/test-data/set-using-nonexistent.properties +set set.using.nonexistent.default using ${basedir}/src/test/test-data/set-using-nonexistent.properties default testSetUsingNonExistentDefault + +# ------------------------------------------------------------ +# Start defining realms +# ------------------------------------------------------------ + +[opt] + diff --git a/impl/maven-classworlds/src/test/test-data/unhandled.conf b/impl/maven-classworlds/src/test/test-data/unhandled.conf new file mode 100644 index 000000000000..3f808fe665dc --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/unhandled.conf @@ -0,0 +1,2 @@ + +foo diff --git a/impl/maven-classworlds/src/test/test-data/valid-enh-launch-exitCode.conf b/impl/maven-classworlds/src/test/test-data/valid-enh-launch-exitCode.conf new file mode 100644 index 000000000000..af06c25bbe39 --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/valid-enh-launch-exitCode.conf @@ -0,0 +1,6 @@ + +main is b.Bb from app + +[app] + load ${basedir}/src/test/test-data/a.jar + load ${basedir}/src/test/test-data/b.jar diff --git a/impl/maven-classworlds/src/test/test-data/valid-enh-launch.conf b/impl/maven-classworlds/src/test/test-data/valid-enh-launch.conf new file mode 100644 index 000000000000..55f8f22c6e73 --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/valid-enh-launch.conf @@ -0,0 +1,6 @@ + +main is b.B from app + +[app] + load ${basedir}/src/test/test-data/a.jar + load ${basedir}/src/test/test-data/b.jar diff --git a/impl/maven-classworlds/src/test/test-data/valid-from-from-from.conf b/impl/maven-classworlds/src/test/test-data/valid-from-from-from.conf new file mode 100644 index 000000000000..e4bcf8a6de7b --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/valid-from-from-from.conf @@ -0,0 +1,24 @@ + +# ------------------------------------------------------------ +# Define the main entry-point +# ------------------------------------------------------------ + +main is com.from.from.from.Main from from + +# ------------------------------------------------------------ +# Start defining realms +# ------------------------------------------------------------ + +[from] + load ${basedir}/src/test/test-data/from-from-0.0.1-from-load-import.jar + +[ant] + import com.from.from.from from from + load ${basedir}/target/test-lib/ant-1.10.14.jar + +[maven] + import com.from.from.from from from + load ${basedir}/target/test-lib/log4j-api-2.23.1.jar + +[glob] + load ${basedir}/src/test/test-data/*.jar diff --git a/impl/maven-classworlds/src/test/test-data/valid-launch-exitCode.conf b/impl/maven-classworlds/src/test/test-data/valid-launch-exitCode.conf new file mode 100644 index 000000000000..9cbc8235b962 --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/valid-launch-exitCode.conf @@ -0,0 +1,5 @@ + +main is a.Aa from app + +[app] + load ${basedir}/src/test/test-data/a.jar diff --git a/impl/maven-classworlds/src/test/test-data/valid-launch.conf b/impl/maven-classworlds/src/test/test-data/valid-launch.conf new file mode 100644 index 000000000000..e5114ad1337b --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/valid-launch.conf @@ -0,0 +1,5 @@ + +main is a.A from app + +[app] + load ${basedir}/src/test/test-data/a.jar diff --git a/impl/maven-classworlds/src/test/test-data/valid.conf b/impl/maven-classworlds/src/test/test-data/valid.conf new file mode 100644 index 000000000000..2e7054f7bfdf --- /dev/null +++ b/impl/maven-classworlds/src/test/test-data/valid.conf @@ -0,0 +1,24 @@ + +# ------------------------------------------------------------ +# Define the main entry-point +# ------------------------------------------------------------ + +main is org.apache.maven.app.App from maven + +# ------------------------------------------------------------ +# Start defining realms +# ------------------------------------------------------------ + +[xml] + load ${basedir}/target/test-lib/jakarta.xml.bind-api-4.0.2.jar + +[ant] + import jakarta.xml.bind from xml + load ${basedir}/target/test-lib/ant-1.10.14.jar + +[maven] + import jakarta.xml.bind from xml + load ${basedir}/target/test-lib/log4j-api-2.23.1.jar + +[glob] + load ${basedir}/src/test/test-data/*.jar diff --git a/impl/maven-cli/pom.xml b/impl/maven-cli/pom.xml index 591120a5d6c9..a88d9100c453 100644 --- a/impl/maven-cli/pom.xml +++ b/impl/maven-cli/pom.xml @@ -146,8 +146,12 @@ under the License.
- org.codehaus.plexus - plexus-classworlds + org.apache.maven + maven-api-classworlds + + + org.apache.maven + maven-classworlds org.codehaus.plexus diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java index fdb957d5bb8a..7a632ecddcf7 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java @@ -23,6 +23,7 @@ import java.io.OutputStream; import org.apache.maven.api.annotations.Nullable; +import org.apache.maven.api.classworlds.ClassWorld; import org.apache.maven.api.cli.Invoker; import org.apache.maven.api.cli.InvokerException; import org.apache.maven.api.cli.Parser; @@ -30,7 +31,6 @@ import org.apache.maven.api.services.MessageBuilderFactory; import org.apache.maven.cling.invoker.logging.SystemLogger; import org.apache.maven.jline.JLineMessageBuilderFactory; -import org.codehaus.plexus.classworlds.ClassWorld; import static java.util.Objects.requireNonNull; @@ -47,7 +47,10 @@ public abstract class ClingSupport { * Ctor that creates "managed" ClassWorld. This constructor is not used in "normal" circumstances. */ public ClingSupport() { - this(new ClassWorld(CORE_CLASS_REALM_ID, Thread.currentThread().getContextClassLoader()), true); + this( + new org.codehaus.plexus.classworlds.ClassWorld( + CORE_CLASS_REALM_ID, Thread.currentThread().getContextClassLoader()), + true); } /** diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenCling.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenCling.java index 65fa91e78e84..37c68b7721aa 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenCling.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenCling.java @@ -23,13 +23,13 @@ import java.io.OutputStream; import org.apache.maven.api.annotations.Nullable; +import org.apache.maven.api.classworlds.ClassWorld; import org.apache.maven.api.cli.Invoker; import org.apache.maven.api.cli.Parser; import org.apache.maven.api.cli.ParserRequest; import org.apache.maven.cling.invoker.ProtoLookup; import org.apache.maven.cling.invoker.mvn.MavenInvoker; import org.apache.maven.cling.invoker.mvn.MavenParser; -import org.codehaus.plexus.classworlds.ClassWorld; /** * Maven CLI "new-gen". diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenEncCling.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenEncCling.java index 3fb3a765ab11..2c8a0cba0b0e 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenEncCling.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenEncCling.java @@ -23,13 +23,13 @@ import java.io.OutputStream; import org.apache.maven.api.annotations.Nullable; +import org.apache.maven.api.classworlds.ClassWorld; import org.apache.maven.api.cli.Invoker; import org.apache.maven.api.cli.Parser; import org.apache.maven.api.cli.ParserRequest; import org.apache.maven.cling.invoker.ProtoLookup; import org.apache.maven.cling.invoker.mvnenc.EncryptInvoker; import org.apache.maven.cling.invoker.mvnenc.EncryptParser; -import org.codehaus.plexus.classworlds.ClassWorld; /** * Maven encrypt CLI "new-gen". diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenShellCling.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenShellCling.java index ce28bb636143..df1d6b363297 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenShellCling.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenShellCling.java @@ -23,13 +23,13 @@ import java.io.OutputStream; import org.apache.maven.api.annotations.Nullable; +import org.apache.maven.api.classworlds.ClassWorld; import org.apache.maven.api.cli.Invoker; import org.apache.maven.api.cli.Parser; import org.apache.maven.api.cli.ParserRequest; import org.apache.maven.cling.invoker.ProtoLookup; import org.apache.maven.cling.invoker.mvnsh.ShellInvoker; import org.apache.maven.cling.invoker.mvnsh.ShellParser; -import org.codehaus.plexus.classworlds.ClassWorld; /** * Maven shell. diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenUpCling.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenUpCling.java index f5650f0525ec..43cb3f90fba0 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenUpCling.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/MavenUpCling.java @@ -23,13 +23,13 @@ import java.io.OutputStream; import org.apache.maven.api.annotations.Nullable; +import org.apache.maven.api.classworlds.ClassWorld; import org.apache.maven.api.cli.Invoker; import org.apache.maven.api.cli.Parser; import org.apache.maven.api.cli.ParserRequest; import org.apache.maven.cling.invoker.ProtoLookup; import org.apache.maven.cling.invoker.mvnup.UpgradeInvoker; import org.apache.maven.cling.invoker.mvnup.UpgradeParser; -import org.codehaus.plexus.classworlds.ClassWorld; /** * Maven upgrade CLI "new-gen". diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/extensions/BootstrapCoreExtensionManager.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/extensions/BootstrapCoreExtensionManager.java index 89dd0e0c0e3f..7c4d4c254678 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/extensions/BootstrapCoreExtensionManager.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/extensions/BootstrapCoreExtensionManager.java @@ -32,6 +32,8 @@ import org.apache.maven.api.Session; import org.apache.maven.api.annotations.Nullable; import org.apache.maven.api.cache.RequestCacheFactory; +import org.apache.maven.api.classworlds.ClassRealm; +import org.apache.maven.api.classworlds.ClassWorld; import org.apache.maven.api.cli.extensions.CoreExtension; import org.apache.maven.api.di.Inject; import org.apache.maven.api.di.Named; @@ -67,8 +69,6 @@ import org.apache.maven.resolver.RepositorySystemSessionFactory; import org.codehaus.plexus.DefaultPlexusContainer; import org.codehaus.plexus.PlexusContainer; -import org.codehaus.plexus.classworlds.ClassWorld; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.RepositorySystemSession.CloseableSession; @@ -179,12 +179,13 @@ private CoreExtensionEntry createExtension(CoreExtension extension, List providedArtifacts = Collections.emptySet(); String classLoadingStrategy = extension.getClassLoadingStrategy(); if (STRATEGY_PARENT_FIRST.equals(classLoadingStrategy)) { - realm.importFrom(parentRealm, ""); + realm.importFrom(parentRealm.getClassLoader(), ""); } else if (STRATEGY_PLUGIN.equals(classLoadingStrategy)) { coreExports.getExportedPackages().forEach((p, cl) -> realm.importFrom(cl, p)); providedArtifacts = coreExports.getExportedArtifacts(); } else if (STRATEGY_SELF_FIRST.equals(classLoadingStrategy)) { - realm.setParentRealm(parentRealm); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) realm) + .setParentRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) parentRealm); } else { throw new IllegalArgumentException("Unsupported class-loading strategy '" + classLoadingStrategy + "'. Supported values are: " + STRATEGY_PARENT_FIRST diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/PlexusContainerCapsuleFactory.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/PlexusContainerCapsuleFactory.java index f1290b02e574..557e6ac6448a 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/PlexusContainerCapsuleFactory.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/PlexusContainerCapsuleFactory.java @@ -31,6 +31,8 @@ import com.google.inject.Module; import org.apache.maven.api.Constants; import org.apache.maven.api.ProtoSession; +import org.apache.maven.api.classworlds.ClassRealm; +import org.apache.maven.api.classworlds.ClassWorld; import org.apache.maven.api.cli.Logger; import org.apache.maven.api.cli.extensions.CoreExtension; import org.apache.maven.api.services.MessageBuilderFactory; @@ -55,8 +57,6 @@ import org.codehaus.plexus.DefaultPlexusContainer; import org.codehaus.plexus.PlexusConstants; import org.codehaus.plexus.PlexusContainer; -import org.codehaus.plexus.classworlds.ClassWorld; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.codehaus.plexus.logging.LoggerManager; import org.slf4j.ILoggerFactory; @@ -98,8 +98,8 @@ protected DefaultPlexusContainer container( ClassRealm containerRealm = setupContainerRealm(context.logger, classWorld, coreRealm, extClassPath, loadedExtensionsEntries); ContainerConfiguration cc = new DefaultContainerConfiguration() - .setClassWorld(classWorld) - .setRealm(containerRealm) + .setClassWorld((org.codehaus.plexus.classworlds.ClassWorld) classWorld) + .setRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) containerRealm) .setClassPathScanning(PlexusConstants.SCANNING_INDEX) .setAutoWiring(true) .setJSR250Lifecycle(true) @@ -111,7 +111,7 @@ protected DefaultPlexusContainer container( containerRealm, collectExportedArtifacts(coreEntry, loadedExtensionsEntries), collectExportedPackages(coreEntry, loadedExtensionsEntries)); - Thread.currentThread().setContextClassLoader(containerRealm); + Thread.currentThread().setContextClassLoader(containerRealm.getClassLoader()); DefaultPlexusContainer container = new DefaultPlexusContainer(cc, getCustomModule(context, exports)); // NOTE: To avoid inconsistencies, we'll use the TCCL exclusively for lookups @@ -124,14 +124,18 @@ protected DefaultPlexusContainer container( List failures = new ArrayList<>(); for (LoadedCoreExtension extension : loadedExtensions) { container.discoverComponents( - extension.entry().getClassRealm(), + (org.codehaus.plexus.classworlds.realm.ClassRealm) + extension.entry().getClassRealm(), new AbstractModule() { @Override protected void configure() { try { container .lookup(Injector.class) - .discover(extension.entry().getClassRealm()); + .discover(extension + .entry() + .getClassRealm() + .getClassLoader()); } catch (Throwable e) { failures.add(new IllegalStateException( "Injection failure in " @@ -231,7 +235,8 @@ protected ClassRealm setupContainerRealm( if (!extClassPath.isEmpty() || !extensions.isEmpty()) { ClassRealm extRealm = classWorld.newRealm("maven.ext", null); - extRealm.setParentRealm(coreRealm); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) extRealm) + .setParentRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) coreRealm); logger.debug("Populating class realm '" + extRealm.getId() + "'"); @@ -246,11 +251,11 @@ protected ClassRealm setupContainerRealm( Set exportedPackages = entry.getExportedPackages(); ClassRealm realm = entry.getClassRealm(); for (String exportedPackage : exportedPackages) { - extRealm.importFrom(realm, exportedPackage); + extRealm.importFrom(realm.getClassLoader(), exportedPackage); } if (exportedPackages.isEmpty()) { // sisu uses realm imports to establish component visibility - extRealm.importFrom(realm, realm.getId()); + extRealm.importFrom(realm.getClassLoader(), realm.getId()); } } @@ -271,8 +276,8 @@ protected List loadCoreExtensions( return List.of(); } ContainerConfiguration cc = new DefaultContainerConfiguration() - .setClassWorld(containerRealm.getWorld()) - .setRealm(containerRealm) + .setClassWorld((org.codehaus.plexus.classworlds.ClassWorld) containerRealm.getWorld()) + .setRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) containerRealm) .setClassPathScanning(PlexusConstants.SCANNING_INDEX) .setAutoWiring(true) .setJSR250Lifecycle(true) diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTest.java index eae08feb2d05..7314c728ffd8 100644 --- a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTest.java +++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTest.java @@ -26,11 +26,11 @@ import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; +import org.apache.maven.api.classworlds.ClassWorld; import org.apache.maven.api.cli.Invoker; import org.apache.maven.api.cli.InvokerException; import org.apache.maven.api.cli.Parser; import org.apache.maven.cling.invoker.ProtoLookup; -import org.codehaus.plexus.classworlds.ClassWorld; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java index a50bc24b6104..6a75f117b218 100644 --- a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java +++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java @@ -28,11 +28,11 @@ import java.util.List; import java.util.Map; +import org.apache.maven.api.classworlds.ClassWorld; import org.apache.maven.api.cli.Invoker; import org.apache.maven.api.cli.Parser; import org.apache.maven.api.cli.ParserRequest; import org.apache.maven.jline.JLineMessageBuilderFactory; -import org.codehaus.plexus.classworlds.ClassWorld; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -149,7 +149,7 @@ protected Map invoke(Path cwd, Path userHome, Collection } protected ClassWorld createClassWorld() { - return new ClassWorld("plexus.core", ClassLoader.getSystemClassLoader()); + return new org.codehaus.plexus.classworlds.ClassWorld("plexus.core", ClassLoader.getSystemClassLoader()); } protected abstract Invoker createInvoker(ClassWorld classWorld); diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/resident/ResidentMavenInvokerTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/resident/ResidentMavenInvokerTest.java index 56b9a105583c..6169e8215707 100644 --- a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/resident/ResidentMavenInvokerTest.java +++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/resident/ResidentMavenInvokerTest.java @@ -24,12 +24,12 @@ import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; +import org.apache.maven.api.classworlds.ClassWorld; import org.apache.maven.api.cli.Invoker; import org.apache.maven.api.cli.Parser; import org.apache.maven.cling.invoker.ProtoLookup; import org.apache.maven.cling.invoker.mvn.MavenInvokerTestSupport; import org.apache.maven.cling.invoker.mvn.MavenParser; -import org.codehaus.plexus.classworlds.ClassWorld; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/impl/maven-core/pom.xml b/impl/maven-core/pom.xml index 8c9291ac7ded..fa10ffb64f0e 100644 --- a/impl/maven-core/pom.xml +++ b/impl/maven-core/pom.xml @@ -152,8 +152,12 @@ under the License. - org.codehaus.plexus - plexus-classworlds + org.apache.maven + maven-api-classworlds + + + org.apache.maven + maven-classworlds javax.inject @@ -404,6 +408,40 @@ under the License. org.apache.maven.toolchain.ToolchainManagerPrivate org.apache.maven.toolchain.ToolchainPrivate org.apache.maven.toolchain.ToolchainsBuilder + + org.apache.maven.classrealm.ClassRealmManager#createExtensionRealm(org.apache.maven.model.Plugin,java.util.List):METHOD_RETURN_TYPE_CHANGED + org.apache.maven.classrealm.ClassRealmManager#createPluginRealm(org.apache.maven.model.Plugin,java.lang.ClassLoader,java.util.List,java.util.Map,java.util.List):METHOD_RETURN_TYPE_CHANGED + org.apache.maven.classrealm.ClassRealmManager#createProjectRealm(org.apache.maven.model.Model,java.util.List):METHOD_RETURN_TYPE_CHANGED + org.apache.maven.classrealm.ClassRealmManager#getCoreRealm():METHOD_RETURN_TYPE_CHANGED + org.apache.maven.classrealm.ClassRealmManager#getMavenApiRealm():METHOD_RETURN_TYPE_CHANGED + org.apache.maven.classrealm.ClassRealmManagerDelegate#setupRealm(org.codehaus.plexus.classworlds.realm.ClassRealm,org.apache.maven.classrealm.ClassRealmRequest):METHOD_REMOVED + org.apache.maven.classrealm.DefaultClassRealmManager#createExtensionRealm(org.apache.maven.model.Plugin,java.util.List):METHOD_RETURN_TYPE_CHANGED + org.apache.maven.classrealm.DefaultClassRealmManager#createPluginRealm(org.apache.maven.model.Plugin,java.lang.ClassLoader,java.util.List,java.util.Map,java.util.List):METHOD_RETURN_TYPE_CHANGED + org.apache.maven.classrealm.DefaultClassRealmManager#createProjectRealm(org.apache.maven.model.Model,java.util.List):METHOD_RETURN_TYPE_CHANGED + org.apache.maven.classrealm.DefaultClassRealmManager#getCoreRealm():METHOD_RETURN_TYPE_CHANGED + org.apache.maven.classrealm.DefaultClassRealmManager#getMavenApiRealm():METHOD_RETURN_TYPE_CHANGED + org.apache.maven.plugin.BuildPluginManager#getPluginRealm(org.apache.maven.execution.MavenSession,org.apache.maven.plugin.descriptor.PluginDescriptor):METHOD_RETURN_TYPE_CHANGED + org.apache.maven.plugin.DefaultBuildPluginManager#getPluginRealm(org.apache.maven.execution.MavenSession,org.apache.maven.plugin.descriptor.PluginDescriptor):METHOD_RETURN_TYPE_CHANGED + org.apache.maven.plugin.ExtensionRealmCache#put(org.apache.maven.plugin.ExtensionRealmCache$Key,org.codehaus.plexus.classworlds.realm.ClassRealm,org.apache.maven.project.ExtensionDescriptor,java.util.List):METHOD_REMOVED + org.apache.maven.plugin.ExtensionRealmCache$CacheRecord#getRealm():METHOD_RETURN_TYPE_CHANGED + org.apache.maven.plugin.PluginContainerException#getPluginRealm():METHOD_RETURN_TYPE_CHANGED + org.apache.maven.plugin.PluginContainerException#PluginContainerException(org.apache.maven.model.Plugin,org.codehaus.plexus.classworlds.realm.ClassRealm,java.lang.String,org.codehaus.plexus.configuration.PlexusConfigurationException):CONSTRUCTOR_REMOVED + org.apache.maven.plugin.PluginContainerException#PluginContainerException(org.apache.maven.plugin.descriptor.MojoDescriptor,org.codehaus.plexus.classworlds.realm.ClassRealm,java.lang.String,java.lang.Throwable):CONSTRUCTOR_REMOVED + org.apache.maven.plugin.PluginContainerException#PluginContainerException(org.apache.maven.model.Plugin,org.codehaus.plexus.classworlds.realm.ClassRealm,java.lang.String,org.codehaus.plexus.component.repository.exception.ComponentRepositoryException):CONSTRUCTOR_REMOVED + org.apache.maven.plugin.PluginContainerException#PluginContainerException(org.apache.maven.model.Plugin,org.codehaus.plexus.classworlds.realm.ClassRealm,java.lang.String,java.lang.Throwable):CONSTRUCTOR_REMOVED + org.apache.maven.plugin.PluginContainerException#PluginContainerException(org.apache.maven.plugin.descriptor.MojoDescriptor,org.codehaus.plexus.classworlds.realm.ClassRealm,java.lang.String,org.codehaus.plexus.component.repository.exception.ComponentLookupException):CONSTRUCTOR_REMOVED + org.apache.maven.plugin.PluginManagerException#PluginManagerException(org.apache.maven.plugin.descriptor.MojoDescriptor,org.apache.maven.project.MavenProject,java.lang.String,org.codehaus.plexus.classworlds.realm.NoSuchRealmException):CONSTRUCTOR_REMOVED + org.apache.maven.plugin.PluginRealmCache#put(org.apache.maven.plugin.PluginRealmCache$Key,org.codehaus.plexus.classworlds.realm.ClassRealm,java.util.List):METHOD_REMOVED + org.apache.maven.plugin.PluginRealmCache$CacheRecord#getRealm():METHOD_RETURN_TYPE_CHANGED + org.apache.maven.plugin.PluginRealmCache$CacheRecord#PluginRealmCache$CacheRecord(org.codehaus.plexus.classworlds.realm.ClassRealm,java.util.List):CONSTRUCTOR_REMOVED + org.apache.maven.project.MavenProject#getClassRealm():METHOD_RETURN_TYPE_CHANGED + org.apache.maven.project.MavenProject#setClassRealm(org.codehaus.plexus.classworlds.realm.ClassRealm):METHOD_REMOVED + org.apache.maven.project.ProjectRealmCache#put(org.apache.maven.project.ProjectRealmCache$Key,org.codehaus.plexus.classworlds.realm.ClassRealm,org.eclipse.aether.graph.DependencyFilter):METHOD_REMOVED + org.apache.maven.project.ProjectRealmCache$CacheRecord#getRealm():METHOD_RETURN_TYPE_CHANGED + + org.apache.maven.plugin.DefaultExtensionRealmCache#put(org.apache.maven.plugin.ExtensionRealmCache$Key,org.codehaus.plexus.classworlds.realm.ClassRealm,org.apache.maven.project.ExtensionDescriptor,java.util.List):METHOD_REMOVED + org.apache.maven.plugin.DefaultPluginRealmCache#put(org.apache.maven.plugin.PluginRealmCache$Key,org.codehaus.plexus.classworlds.realm.ClassRealm,java.util.List):METHOD_REMOVED + org.apache.maven.project.DefaultProjectRealmCache#put(org.apache.maven.project.ProjectRealmCache$Key,org.codehaus.plexus.classworlds.realm.ClassRealm,org.eclipse.aether.graph.DependencyFilter):METHOD_REMOVED diff --git a/impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java b/impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java index 4a64c009a2a6..ec2ed28d3e3c 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java +++ b/impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java @@ -464,7 +464,9 @@ protected Collection getProjectScopedExtensionComponents(Collection apiV4Imports = new HashMap<>(); - apiV4Imports.put("org.apache.maven.api", containerRealm); - apiV4Imports.put("org.slf4j", containerRealm); + apiV4Imports.put("org.apache.maven.api", containerRealm.getClassLoader()); + apiV4Imports.put("org.slf4j", containerRealm.getClassLoader()); this.maven4ApiRealm = createRealm(API_V4_REALMID, RealmType.Core, null, null, apiV4Imports, null); this.providedArtifacts = exports.getExportedArtifacts(); @@ -219,7 +219,7 @@ public ClassRealm getCoreRealm() { public ClassRealm createProjectRealm(Model model, List artifacts) { Objects.requireNonNull(model, "model cannot be null"); - ClassLoader parent = getMavenApiRealm(); + ClassLoader parent = getMavenApiRealm().getClassLoader(); return createRealm(getKey(model), RealmType.Project, parent, null, null, artifacts); } @@ -232,7 +232,8 @@ private static String getKey(Model model) { public ClassRealm createExtensionRealm(Plugin plugin, List artifacts) { Objects.requireNonNull(plugin, "plugin cannot be null"); - Map foreignImports = Collections.singletonMap("", getMavenApiRealm()); + Map foreignImports = + Collections.singletonMap("", getMavenApiRealm().getClassLoader()); return createRealm( getKey(plugin, true), RealmType.Extension, PARENT_CLASSLOADER, null, foreignImports, artifacts); @@ -353,7 +354,7 @@ private void wireRealm(ClassRealm classRealm, List parentImports, Map exportedArtifacts, Set exportedPackages) { this.artifacts = Collections.unmodifiableSet(new HashSet<>(exportedArtifacts)); this.packages = exportedPackages.stream() - .collect(collectingAndThen(toMap(identity(), v -> realm), Collections::unmodifiableMap)); + .collect(collectingAndThen( + toMap(identity(), v -> realm.getClassLoader()), Collections::unmodifiableMap)); } /** diff --git a/impl/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExtensionEntry.java b/impl/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExtensionEntry.java index f19a5bf6ef57..03f7dbdcc6e9 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExtensionEntry.java +++ b/impl/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExtensionEntry.java @@ -29,10 +29,10 @@ import java.util.LinkedHashSet; import java.util.Set; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.api.xml.XmlNode; import org.apache.maven.project.ExtensionDescriptor; import org.apache.maven.project.ExtensionDescriptorBuilder; -import org.codehaus.plexus.classworlds.realm.ClassRealm; /** * Provides information about artifacts (identified by groupId:artifactId string key) and classpath elements exported by @@ -107,7 +107,7 @@ public static CoreExtensionEntry discoverFrom(ClassRealm loader) { Set packages = new LinkedHashSet<>(); try { - Enumeration urls = loader.getResources(BUILDER.getExtensionDescriptorLocation()); + Enumeration urls = loader.getClassLoader().getResources(BUILDER.getExtensionDescriptorLocation()); while (urls.hasMoreElements()) { try (InputStream is = urls.nextElement().openStream()) { diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/CoreRealm.java b/impl/maven-core/src/main/java/org/apache/maven/internal/CoreRealm.java index b27cec352ffe..150d8101cfb5 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/internal/CoreRealm.java +++ b/impl/maven-core/src/main/java/org/apache/maven/internal/CoreRealm.java @@ -20,8 +20,8 @@ import org.apache.maven.api.annotations.Experimental; import org.apache.maven.api.annotations.Nonnull; -import org.codehaus.plexus.classworlds.ClassWorld; -import org.codehaus.plexus.classworlds.realm.ClassRealm; +import org.apache.maven.api.classworlds.ClassRealm; +import org.apache.maven.api.classworlds.ClassWorld; /** * Access to core {@link ClassRealm}. diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProject.java b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProject.java index 5c53a246a5c5..5283ae430bd6 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProject.java +++ b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProject.java @@ -58,7 +58,11 @@ public DefaultProject(InternalMavenSession session, MavenProject project) { this.project = project; ClassLoader ttcl = Thread.currentThread().getContextClassLoader(); try { - Thread.currentThread().setContextClassLoader(project.getClassRealm()); + Thread.currentThread() + .setContextClassLoader( + project.getClassRealm() != null + ? project.getClassRealm().getClassLoader() + : null); this.packaging = session.requirePackaging(project.getPackaging()); } finally { Thread.currentThread().setContextClassLoader(ttcl); diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/internal/DefaultCoreRealm.java b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/internal/DefaultCoreRealm.java index 5d85bab5b668..2d0f7bf48bd3 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/internal/DefaultCoreRealm.java +++ b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/internal/DefaultCoreRealm.java @@ -22,9 +22,9 @@ import javax.inject.Named; import javax.inject.Singleton; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.internal.CoreRealm; import org.codehaus.plexus.PlexusContainer; -import org.codehaus.plexus.classworlds.realm.ClassRealm; @Named @Singleton diff --git a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolver.java b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolver.java index f41b00f9056a..e516fc575362 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolver.java +++ b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolver.java @@ -33,6 +33,7 @@ import java.util.stream.Collectors; import org.apache.maven.RepositoryUtils; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.api.services.MessageBuilderFactory; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.ArtifactUtils; @@ -112,9 +113,12 @@ public void resolveProjectDependencies( throws LifecycleExecutionException { ClassLoader tccl = Thread.currentThread().getContextClassLoader(); try { - ClassLoader projectRealm = project.getClassRealm(); - if (projectRealm != null && projectRealm != tccl) { - Thread.currentThread().setContextClassLoader(projectRealm); + ClassRealm projectRealm = project.getClassRealm(); + if (projectRealm != null) { + ClassLoader projectClassLoader = projectRealm.getClassLoader(); + if (projectClassLoader != tccl) { + Thread.currentThread().setContextClassLoader(projectClassLoader); + } } if (project.getDependencyArtifacts() == null) { diff --git a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/BuilderCommon.java b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/BuilderCommon.java index f94271ed1f22..30bb7ddccfd6 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/BuilderCommon.java +++ b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/BuilderCommon.java @@ -28,6 +28,7 @@ import java.util.Set; import org.apache.maven.api.MonotonicClock; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.artifact.Artifact; import org.apache.maven.execution.BuildFailure; import org.apache.maven.execution.ExecutionEvent; @@ -55,7 +56,6 @@ import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; import org.apache.maven.plugin.version.PluginVersionResolutionException; import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -201,7 +201,7 @@ public void handleBuildError( public static void attachToThread(MavenProject currentProject) { ClassRealm projectRealm = currentProject.getClassRealm(); if (projectRealm != null) { - Thread.currentThread().setContextClassLoader(projectRealm); + Thread.currentThread().setContextClassLoader(projectRealm.getClassLoader()); } } diff --git a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java index 7e8428a6b168..fa4a871a19a4 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java +++ b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java @@ -43,6 +43,7 @@ import org.apache.maven.api.Lifecycle; import org.apache.maven.api.MonotonicClock; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.api.services.LifecycleRegistry; import org.apache.maven.api.services.MavenException; import org.apache.maven.api.xml.XmlNode; @@ -84,7 +85,6 @@ import org.apache.maven.plugin.descriptor.Parameter; import org.apache.maven.plugin.descriptor.PluginDescriptor; import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.eclipse.aether.repository.RemoteRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -1100,7 +1100,7 @@ private MojoExecutionConfigurator mojoExecutionConfigurator(MojoExecution mojoEx public static void attachToThread(MavenProject currentProject) { ClassRealm projectRealm = currentProject.getClassRealm(); if (projectRealm != null) { - Thread.currentThread().setContextClassLoader(projectRealm); + Thread.currentThread().setContextClassLoader(projectRealm.getClassLoader()); } } diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/BuildPluginManager.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/BuildPluginManager.java index b3741041e08b..729f656372ec 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/BuildPluginManager.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/BuildPluginManager.java @@ -20,11 +20,11 @@ import java.util.List; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Plugin; import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.plugin.descriptor.PluginDescriptor; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.repository.RemoteRepository; diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java index 6d9c76100d8e..1b9af3c86bad 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java @@ -27,6 +27,7 @@ import java.util.List; import org.apache.maven.api.Project; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.api.services.MavenException; import org.apache.maven.execution.MavenSession; import org.apache.maven.execution.MojoExecutionEvent; @@ -40,7 +41,6 @@ import org.apache.maven.plugin.descriptor.PluginDescriptor; import org.apache.maven.plugin.logging.Log; import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.repository.RemoteRepository; import org.slf4j.LoggerFactory; @@ -110,7 +110,7 @@ public void executeMojo(MavenSession session, MojoExecution mojoExecution) } ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(pluginRealm); + Thread.currentThread().setContextClassLoader(pluginRealm.getClassLoader()); MavenSession oldSession = legacySupport.getSession(); @@ -164,7 +164,7 @@ public void executeMojo(MavenSession session, MojoExecution mojoExecution) PrintStream ps = new PrintStream(os); ps.println( "A required class was missing while executing " + mojoDescriptor.getId() + ": " + e.getMessage()); - pluginRealm.display(ps); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm).display(ps); Exception wrapper = new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), e); throw new PluginExecutionException(mojoExecution, project, wrapper); } catch (LinkageError e) { @@ -174,7 +174,7 @@ public void executeMojo(MavenSession session, MojoExecution mojoExecution) PrintStream ps = new PrintStream(os); ps.println("An API incompatibility was encountered while executing " + mojoDescriptor.getId() + ": " + e.getClass().getName() + ": " + e.getMessage()); - pluginRealm.display(ps); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm).display(ps); Exception wrapper = new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), e); throw new PluginExecutionException(mojoExecution, project, wrapper); } catch (ClassCastException e) { @@ -184,7 +184,7 @@ public void executeMojo(MavenSession session, MojoExecution mojoExecution) PrintStream ps = new PrintStream(os); ps.println("A type incompatibility occurred while executing " + mojoDescriptor.getId() + ": " + e.getMessage()); - pluginRealm.display(ps); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm).display(ps); throw new PluginExecutionException(mojoExecution, project, os.toString(), e); } catch (RuntimeException e) { mojoExecutionListener.afterExecutionFailure( diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java index b20211525854..0319c101861f 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java @@ -28,11 +28,11 @@ import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; +import org.apache.maven.api.classworlds.ClassRealm; +import org.apache.maven.api.classworlds.NoSuchRealmException; import org.apache.maven.artifact.Artifact; import org.apache.maven.project.ExtensionDescriptor; import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.classworlds.realm.ClassRealm; -import org.codehaus.plexus.classworlds.realm.NoSuchRealmException; import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable; /** diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginRealmCache.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginRealmCache.java index 681955f21db3..ca25a22638b0 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginRealmCache.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginRealmCache.java @@ -29,11 +29,11 @@ import java.util.concurrent.ConcurrentHashMap; import org.apache.maven.RepositoryUtils; +import org.apache.maven.api.classworlds.ClassRealm; +import org.apache.maven.api.classworlds.NoSuchRealmException; import org.apache.maven.artifact.Artifact; import org.apache.maven.model.Plugin; import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.classworlds.realm.ClassRealm; -import org.codehaus.plexus.classworlds.realm.NoSuchRealmException; import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.graph.DependencyFilter; diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/ExtensionRealmCache.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/ExtensionRealmCache.java index 22bb57ed2d1b..6ba35ae771ac 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/ExtensionRealmCache.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/ExtensionRealmCache.java @@ -20,10 +20,10 @@ import java.util.List; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.artifact.Artifact; import org.apache.maven.project.ExtensionDescriptor; import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.classworlds.realm.ClassRealm; /** * Caches extension class realms. Warning: This is an internal utility interface that is only public diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginContainerException.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginContainerException.java index 55ac337cddb2..63eed33efadc 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginContainerException.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginContainerException.java @@ -18,9 +18,9 @@ */ package org.apache.maven.plugin; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.model.Plugin; import org.apache.maven.plugin.descriptor.MojoDescriptor; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.codehaus.plexus.component.repository.exception.ComponentLookupException; import org.codehaus.plexus.component.repository.exception.ComponentRepositoryException; import org.codehaus.plexus.configuration.PlexusConfigurationException; diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginManagerException.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginManagerException.java index f3fd390fc0de..4b47a6fd5c66 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginManagerException.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginManagerException.java @@ -18,13 +18,13 @@ */ package org.apache.maven.plugin; +import org.apache.maven.api.classworlds.NoSuchRealmException; import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; import org.apache.maven.model.Plugin; import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.plugin.descriptor.PluginDescriptor; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.PlexusContainerException; -import org.codehaus.plexus.classworlds.realm.NoSuchRealmException; import org.codehaus.plexus.component.repository.exception.ComponentRepositoryException; import org.codehaus.plexus.configuration.PlexusConfigurationException; diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginRealmCache.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginRealmCache.java index 7fcbd87949a6..6fb5535e4a1a 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginRealmCache.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginRealmCache.java @@ -21,10 +21,10 @@ import java.util.List; import java.util.Map; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.artifact.Artifact; import org.apache.maven.model.Plugin; import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.graph.DependencyFilter; import org.eclipse.aether.repository.RemoteRepository; diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java index 7d55730f5d96..d0915cee2046 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java @@ -48,6 +48,7 @@ import org.apache.maven.api.PathType; import org.apache.maven.api.Project; import org.apache.maven.api.Session; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.api.plugin.descriptor.Resolution; import org.apache.maven.api.services.DependencyResolver; import org.apache.maven.api.services.DependencyResolverResult; @@ -103,7 +104,6 @@ import org.apache.maven.session.scope.internal.SessionScopeModule; import org.codehaus.plexus.DefaultPlexusContainer; import org.codehaus.plexus.PlexusContainer; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.codehaus.plexus.component.composition.CycleDetectedInComponentGraphException; import org.codehaus.plexus.component.configurator.ComponentConfigurationException; import org.codehaus.plexus.component.configurator.ComponentConfigurator; @@ -357,10 +357,10 @@ public void setupPluginRealm( List pluginArtifacts = extensionRecord.getArtifacts(); for (ComponentDescriptor componentDescriptor : pluginDescriptor.getComponents()) { - componentDescriptor.setRealm(pluginRealm); + componentDescriptor.setRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm); } - pluginDescriptor.setClassRealm(pluginRealm); + pluginDescriptor.setClassRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm); pluginDescriptor.setArtifacts(pluginArtifacts); } else { boolean v4api = pluginDescriptor.getMojos().stream().anyMatch(MojoDescriptor::isV4Api); @@ -381,10 +381,10 @@ public void setupPluginRealm( pluginDescriptor.getClassRealm(), pluginDescriptor.getArtifacts()); }); - pluginDescriptor.setClassRealm(cacheRecord.getRealm()); + pluginDescriptor.setClassRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) cacheRecord.getRealm()); pluginDescriptor.setArtifacts(new ArrayList<>(cacheRecord.getArtifacts())); for (ComponentDescriptor componentDescriptor : pluginDescriptor.getComponents()) { - componentDescriptor.setRealm(cacheRecord.getRealm()); + componentDescriptor.setRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) cacheRecord.getRealm()); } pluginRealmCache.register(project, cacheKey, cacheRecord); @@ -427,7 +427,7 @@ private void createPluginRealm( discoverPluginComponents(pluginRealm, plugin, pluginDescriptor); pluginDescriptor.setDependencyNode(result.getRoot()); - pluginDescriptor.setClassRealm(pluginRealm); + pluginDescriptor.setClassRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm); pluginDescriptor.setArtifacts(pluginArtifacts); } @@ -439,16 +439,16 @@ private void discoverPluginComponents( if (pluginDescriptor != null) { for (MojoDescriptor mojo : pluginDescriptor.getMojos()) { if (!mojo.isV4Api()) { - mojo.setRealm(pluginRealm); + mojo.setRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm); container.addComponentDescriptor(mojo); } } } - Thread.currentThread().setContextClassLoader(pluginRealm); + Thread.currentThread().setContextClassLoader(pluginRealm.getClassLoader()); ((DefaultPlexusContainer) container) .discoverComponents( - pluginRealm, + (org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm, new SessionScopeModule(container.lookup(SessionScope.class)), new MojoExecutionScopeModule(container.lookup(MojoExecutionScope.class)), new PluginConfigurationModule(plugin.getDelegate()), @@ -479,12 +479,14 @@ private Map calcImports( MavenProject project, ClassLoader parent, List imports, boolean v4api) { Map foreignImports = new HashMap<>(); - ClassLoader projectRealm = project.getClassRealm(); + ClassRealm projectRealm = project.getClassRealm(); if (projectRealm != null) { - foreignImports.put("", projectRealm); + foreignImports.put("", projectRealm.getClassLoader()); } else { foreignImports.put( - "", v4api ? classRealmManager.getMaven4ApiRealm() : classRealmManager.getMavenApiRealm()); + "", + (v4api ? classRealmManager.getMaven4ApiRealm() : classRealmManager.getMavenApiRealm()) + .getClassLoader()); } if (parent != null && imports != null) { @@ -523,10 +525,11 @@ public T getConfiguredMojo(Class mojoInterface, MavenSession session, Moj // We are forcing the use of the plugin realm for all lookups that might occur during // the lifecycle that is part of the lookup. Here we are specifically trying to keep // lookups that occur in contextualize calls in line with the right realm. - ClassRealm oldLookupRealm = container.setLookupRealm(pluginRealm); + ClassRealm oldLookupRealm = + container.setLookupRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm); ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(pluginRealm); + Thread.currentThread().setContextClassLoader(pluginRealm.getClassLoader()); try { if (mojoDescriptor.isV4Api()) { @@ -536,7 +539,7 @@ public T getConfiguredMojo(Class mojoInterface, MavenSession session, Moj } } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); - container.setLookupRealm(oldLookupRealm); + container.setLookupRealm((org.codehaus.plexus.classworlds.realm.ClassRealm) oldLookupRealm); } } @@ -558,7 +561,7 @@ private T loadV4Mojo( LoggerFactory.getLogger(mojoExecution.getMojoDescriptor().getFullGoalName())); try { Injector injector = Injector.create(); - injector.discover(pluginRealm); + injector.discover(pluginRealm.getClassLoader()); // Add known classes // TODO: get those from the existing plexus scopes ? injector.bindInstance(Session.class, sessionV4); @@ -699,7 +702,7 @@ private T loadV3Mojo( ps.println("Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '" + pluginDescriptor.getId() + "'. A required class is missing: " + cause.getMessage()); - pluginRealm.display(ps); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm).display(ps); throw new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), cause); } else if (cause instanceof LinkageError) { @@ -708,7 +711,7 @@ private T loadV3Mojo( ps.println("Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '" + pluginDescriptor.getId() + "' due to an API incompatibility: " + e.getClass().getName() + ": " + cause.getMessage()); - pluginRealm.display(ps); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm).display(ps); throw new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), cause); } @@ -810,7 +813,12 @@ private void populateMojoExecutionFields( + configuratorId + " configurator -->"); } - configurator.configureComponent(mojo, configuration, expressionEvaluator, pluginRealm, validator); + configurator.configureComponent( + mojo, + configuration, + expressionEvaluator, + (org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm, + validator); logger.debug("-- end configuration --"); @@ -846,7 +854,7 @@ private void populateMojoExecutionFields( PrintStream ps = new PrintStream(os); ps.println("A required class was missing during configuration of mojo " + mojoDescriptor.getId() + ": " + e.getMessage()); - pluginRealm.display(ps); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm).display(ps); throw new PluginConfigurationException(mojoDescriptor.getPluginDescriptor(), os.toString(), e); } catch (LinkageError e) { @@ -854,7 +862,7 @@ private void populateMojoExecutionFields( PrintStream ps = new PrintStream(os); ps.println("An API incompatibility was encountered during configuration of mojo " + mojoDescriptor.getId() + ": " + e.getClass().getName() + ": " + e.getMessage()); - pluginRealm.display(ps); + ((org.codehaus.plexus.classworlds.realm.ClassRealm) pluginRealm).display(ps); throw new PluginConfigurationException(mojoDescriptor.getPluginDescriptor(), os.toString(), e); } finally { diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java index f8f77c7ceab3..e58a3f4e569b 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java +++ b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingHelper.java @@ -33,6 +33,7 @@ import java.util.Set; import org.apache.maven.RepositoryUtils; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.api.xml.XmlNode; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.InvalidRepositoryException; @@ -49,7 +50,6 @@ import org.apache.maven.plugin.PluginManagerException; import org.apache.maven.plugin.PluginResolutionException; import org.apache.maven.plugin.version.PluginVersionResolutionException; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.codehaus.plexus.util.xml.Xpp3Dom; import org.eclipse.aether.graph.DependencyFilter; import org.eclipse.aether.util.filter.ExclusionsDependencyFilter; @@ -239,7 +239,7 @@ public synchronized ProjectRealmCache.CacheRecord createProjectRealm( } for (String export : exports) { - projectRealm.importFrom(extensionRealm, export); + projectRealm.importFrom(extensionRealm.getClassLoader(), export); } } @@ -258,13 +258,16 @@ record = projectRealmCache.put(projectRealmKey, projectRealm, extensionArtifactF @Override public void selectProjectRealm(MavenProject project) { - ClassLoader projectRealm = project.getClassRealm(); + ClassRealm projectRealm = project.getClassRealm(); + ClassLoader classLoader; if (projectRealm == null) { - projectRealm = classRealmManager.getCoreRealm(); + classLoader = classRealmManager.getCoreRealm().getClassLoader(); + } else { + classLoader = projectRealm.getClassLoader(); } - Thread.currentThread().setContextClassLoader(projectRealm); + Thread.currentThread().setContextClassLoader(classLoader); } private List toAetherArtifacts(final List pluginArtifacts) { diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java index 82a1a814c1b0..b9dc37f764c2 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java +++ b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java @@ -27,8 +27,8 @@ import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; -import org.codehaus.plexus.classworlds.realm.ClassRealm; -import org.codehaus.plexus.classworlds.realm.NoSuchRealmException; +import org.apache.maven.api.classworlds.ClassRealm; +import org.apache.maven.api.classworlds.NoSuchRealmException; import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable; import org.eclipse.aether.graph.DependencyFilter; diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/MavenProject.java b/impl/maven-core/src/main/java/org/apache/maven/project/MavenProject.java index 3b934c922e9b..5483d62286b8 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/project/MavenProject.java +++ b/impl/maven-core/src/main/java/org/apache/maven/project/MavenProject.java @@ -43,6 +43,7 @@ import org.apache.maven.api.ProjectScope; import org.apache.maven.api.SourceRoot; import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.ArtifactUtils; import org.apache.maven.artifact.DependencyResolutionRequiredException; @@ -79,7 +80,6 @@ import org.apache.maven.model.io.xpp3.MavenXpp3Writer; import org.apache.maven.model.root.RootLocator; import org.apache.maven.project.artifact.InvalidDependencyVersionException; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.codehaus.plexus.util.xml.Xpp3Dom; import org.eclipse.aether.graph.DependencyFilter; import org.eclipse.aether.repository.RemoteRepository; diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectRealmCache.java b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectRealmCache.java index dd07b49842d1..05c204e1475a 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectRealmCache.java +++ b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectRealmCache.java @@ -20,7 +20,7 @@ import java.util.List; -import org.codehaus.plexus.classworlds.realm.ClassRealm; +import org.apache.maven.api.classworlds.ClassRealm; import org.eclipse.aether.graph.DependencyFilter; /** diff --git a/impl/maven-core/src/main/resources/META-INF/maven/extension.xml b/impl/maven-core/src/main/resources/META-INF/maven/extension.xml index 9b05f7a4ada4..2a68baef5e35 100644 --- a/impl/maven-core/src/main/resources/META-INF/maven/extension.xml +++ b/impl/maven-core/src/main/resources/META-INF/maven/extension.xml @@ -76,12 +76,9 @@ under the License. org.eclipse.aether.version org.eclipse.aether.util - + org.codehaus.plexus.classworlds - - org.codehaus.classworlds - org.codehaus.plexus.util.xml.Xpp3Dom org.codehaus.plexus.util.xml.Xpp3DomBuilder @@ -153,7 +150,7 @@ under the License. org.apache.maven:maven-api-xml classworlds:classworlds - org.codehaus.plexus:plexus-classworlds + org.apache.maven:maven-classworlds org.codehaus.plexus:plexus-component-api org.codehaus.plexus:plexus-container-default plexus:plexus-container-default diff --git a/impl/maven-core/src/test/java/org/apache/maven/classrealm/DefaultClassRealmManagerTest.java b/impl/maven-core/src/test/java/org/apache/maven/classrealm/DefaultClassRealmManagerTest.java index c90e75bece85..3a1cb57c791b 100644 --- a/impl/maven-core/src/test/java/org/apache/maven/classrealm/DefaultClassRealmManagerTest.java +++ b/impl/maven-core/src/test/java/org/apache/maven/classrealm/DefaultClassRealmManagerTest.java @@ -23,13 +23,13 @@ import java.util.HashSet; import java.util.List; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.extension.internal.CoreExports; import org.apache.maven.internal.impl.internal.DefaultCoreRealm; import org.apache.maven.model.Model; import org.codehaus.plexus.DefaultPlexusContainer; import org.codehaus.plexus.PlexusContainer; import org.codehaus.plexus.PlexusContainerException; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.eclipse.aether.artifact.Artifact; import org.junit.jupiter.api.Test; import org.mockito.InOrder; @@ -59,7 +59,11 @@ private DefaultClassRealmManager newDefaultClassRealmManager(PlexusContainer con return new DefaultClassRealmManager( new DefaultCoreRealm(container), new ArrayList(), - new CoreExports(new ClassRealm(null, "test", null), new HashSet(), exportedPackages)); + new CoreExports( + new org.codehaus.plexus.classworlds.realm.ClassRealm( + new org.codehaus.plexus.classworlds.ClassWorld(), "test", null), + new HashSet(), + exportedPackages)); } private List newTestArtifactList() { diff --git a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/BuildPluginManagerStub.java b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/BuildPluginManagerStub.java index 9566f569d1c7..6fb7c3617757 100644 --- a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/BuildPluginManagerStub.java +++ b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/BuildPluginManagerStub.java @@ -20,13 +20,13 @@ import java.util.List; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Plugin; import org.apache.maven.plugin.BuildPluginManager; import org.apache.maven.plugin.MojoExecution; import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.plugin.descriptor.PluginDescriptor; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.repository.RemoteRepository; diff --git a/impl/maven-core/src/test/java/org/apache/maven/plugin/PluginManagerTest.java b/impl/maven-core/src/test/java/org/apache/maven/plugin/PluginManagerTest.java index f1621347257f..4290108a4dd8 100644 --- a/impl/maven-core/src/test/java/org/apache/maven/plugin/PluginManagerTest.java +++ b/impl/maven-core/src/test/java/org/apache/maven/plugin/PluginManagerTest.java @@ -23,6 +23,7 @@ import java.util.List; import org.apache.maven.AbstractCoreMavenComponentTestCase; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.repository.DefaultRepositoryRequest; import org.apache.maven.artifact.repository.RepositoryRequest; @@ -31,7 +32,6 @@ import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.plugin.descriptor.PluginDescriptor; import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.codehaus.plexus.component.repository.ComponentDescriptor; import org.junit.jupiter.api.Test; diff --git a/impl/pom.xml b/impl/pom.xml index 93b8fbc7ba1d..ed5e6c6e7fe0 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -31,6 +31,7 @@ under the License. Maven 4 Implementation Modules + maven-classworlds maven-support maven-impl maven-di diff --git a/its/core-it-suite/src/test/resources/mng-5530-mojo-execution-scope/extension/src/main/java/org/apache/maven/its/mng5530/mojoexecutionscope/extension/TestClassRealmManagerDelegate.java b/its/core-it-suite/src/test/resources/mng-5530-mojo-execution-scope/extension/src/main/java/org/apache/maven/its/mng5530/mojoexecutionscope/extension/TestClassRealmManagerDelegate.java index 6c064d273132..af8955c21a8b 100644 --- a/its/core-it-suite/src/test/resources/mng-5530-mojo-execution-scope/extension/src/main/java/org/apache/maven/its/mng5530/mojoexecutionscope/extension/TestClassRealmManagerDelegate.java +++ b/its/core-it-suite/src/test/resources/mng-5530-mojo-execution-scope/extension/src/main/java/org/apache/maven/its/mng5530/mojoexecutionscope/extension/TestClassRealmManagerDelegate.java @@ -35,10 +35,10 @@ import javax.inject.Named; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.classrealm.ClassRealmManagerDelegate; import org.apache.maven.classrealm.ClassRealmRequest; import org.apache.maven.classrealm.ClassRealmRequest.RealmType; -import org.codehaus.plexus.classworlds.realm.ClassRealm; @Named public class TestClassRealmManagerDelegate implements ClassRealmManagerDelegate { diff --git a/its/core-it-suite/src/test/resources/mng-5771-core-extensions/repo-src/maven-it-core-extensions-no-descriptor/src/main/java/org/apache/maven/its/core_extensions/TestClassRealmManagerDelegate.java b/its/core-it-suite/src/test/resources/mng-5771-core-extensions/repo-src/maven-it-core-extensions-no-descriptor/src/main/java/org/apache/maven/its/core_extensions/TestClassRealmManagerDelegate.java index edb262ce2dc6..44dd46fdd3aa 100644 --- a/its/core-it-suite/src/test/resources/mng-5771-core-extensions/repo-src/maven-it-core-extensions-no-descriptor/src/main/java/org/apache/maven/its/core_extensions/TestClassRealmManagerDelegate.java +++ b/its/core-it-suite/src/test/resources/mng-5771-core-extensions/repo-src/maven-it-core-extensions-no-descriptor/src/main/java/org/apache/maven/its/core_extensions/TestClassRealmManagerDelegate.java @@ -18,9 +18,9 @@ */ package org.apache.maven.its.core_extensions; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.classrealm.ClassRealmManagerDelegate; import org.apache.maven.classrealm.ClassRealmRequest; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.codehaus.plexus.component.annotations.Component; @Component(role = ClassRealmManagerDelegate.class, hint = "TestClassRealmManagerDelegate") diff --git a/its/core-it-suite/src/test/resources/mng-5771-core-extensions/repo-src/maven-it-core-extensions/src/main/java/org/apache/maven/its/core_extensions/TestClassRealmManagerDelegate.java b/its/core-it-suite/src/test/resources/mng-5771-core-extensions/repo-src/maven-it-core-extensions/src/main/java/org/apache/maven/its/core_extensions/TestClassRealmManagerDelegate.java index edb262ce2dc6..44dd46fdd3aa 100644 --- a/its/core-it-suite/src/test/resources/mng-5771-core-extensions/repo-src/maven-it-core-extensions/src/main/java/org/apache/maven/its/core_extensions/TestClassRealmManagerDelegate.java +++ b/its/core-it-suite/src/test/resources/mng-5771-core-extensions/repo-src/maven-it-core-extensions/src/main/java/org/apache/maven/its/core_extensions/TestClassRealmManagerDelegate.java @@ -18,9 +18,9 @@ */ package org.apache.maven.its.core_extensions; +import org.apache.maven.api.classworlds.ClassRealm; import org.apache.maven.classrealm.ClassRealmManagerDelegate; import org.apache.maven.classrealm.ClassRealmRequest; -import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.codehaus.plexus.component.annotations.Component; @Component(role = ClassRealmManagerDelegate.class, hint = "TestClassRealmManagerDelegate") diff --git a/its/core-it-support/core-it-plugins/maven-it-plugin-configuration/src/main/java/org/apache/maven/plugin/coreit/CustomComponentConfigurator.java b/its/core-it-support/core-it-plugins/maven-it-plugin-configuration/src/main/java/org/apache/maven/plugin/coreit/CustomComponentConfigurator.java index 299168546461..4f3406951ad0 100644 --- a/its/core-it-support/core-it-plugins/maven-it-plugin-configuration/src/main/java/org/apache/maven/plugin/coreit/CustomComponentConfigurator.java +++ b/its/core-it-support/core-it-plugins/maven-it-plugin-configuration/src/main/java/org/apache/maven/plugin/coreit/CustomComponentConfigurator.java @@ -18,7 +18,7 @@ */ package org.apache.maven.plugin.coreit; -import org.codehaus.plexus.classworlds.realm.ClassRealm; +import org.apache.maven.api.classworlds.ClassRealm; import org.codehaus.plexus.component.annotations.Component; import org.codehaus.plexus.component.configurator.AbstractComponentConfigurator; import org.codehaus.plexus.component.configurator.ComponentConfigurationException; diff --git a/pom.xml b/pom.xml index 0fe79e32ae12..b2103cb693b4 100644 --- a/pom.xml +++ b/pom.xml @@ -145,7 +145,6 @@ under the License. 3.27.3 9.8 1.17.6 - 2.9.0 1.9.0 5.1.0 33.4.8-jre @@ -235,6 +234,11 @@ under the License. maven-api-annotations ${project.version} + + org.apache.maven + maven-api-classworlds + ${project.version} + org.apache.maven maven-api-model @@ -418,9 +422,9 @@ under the License. 1 - org.codehaus.plexus - plexus-classworlds - ${classWorldsVersion} + org.apache.maven + maven-classworlds + ${project.version} org.codehaus.plexus