Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allows to register a filtered class realm #70

Merged
merged 4 commits into from
Nov 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@
<report>summary</report>
<report>index</report>
<report>dependencies</report>
<report>issue-tracking</report>
<report>issue-management</report>
<report>scm</report>
</reports>
</reportSet>
Expand Down
49 changes: 45 additions & 4 deletions src/main/java/org/codehaus/plexus/classworlds/ClassWorld.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@
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;

/**
Expand Down Expand Up @@ -64,7 +66,40 @@ public ClassRealm newRealm( String id )
return newRealm( id, getClass().getClassLoader() );
}

public synchronized ClassRealm newRealm( String id, ClassLoader classLoader )
public ClassRealm newRealm( String id, ClassLoader classLoader )
throws DuplicateRealmException
{
return newRealm( id, classLoader, null );
}

/**
* Shortcut for {@link #newRealm(String, ClassLoader, Predicate)} with the class loader of the current class.
* @param id The identifier for this realm, must not be <code>null</code>.
* @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, Predicate<String> filter )
throws DuplicateRealmException
{
return newRealm( id, getClass().getClassLoader(), filter );
}

/**
* 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 <code>null</code>.
* @param classLoader The base class loader for this realm, may be <code>null</code> 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<String> filter )
throws DuplicateRealmException
{
if ( realms.containsKey( id ) )
Expand All @@ -74,8 +109,14 @@ public synchronized ClassRealm newRealm( String id, ClassLoader classLoader )

ClassRealm realm;

realm = new ClassRealm( this, id, classLoader );

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 )
Expand All @@ -85,7 +126,7 @@ public synchronized ClassRealm newRealm( String id, ClassLoader classLoader )

return realm;
}

public synchronized void disposeRealm( String id )
throws NoSuchRealmException
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ public ClassRealm getParentRealm()
public ClassRealm createChildRealm( String id )
throws DuplicateRealmException
{
ClassRealm childRealm = getWorld().newRealm( id, null );
ClassRealm childRealm = getWorld().newRealm( id, (ClassLoader) null );

childRealm.setParentRealm( this );

Expand Down Expand Up @@ -272,7 +272,8 @@ private Class<?> unsynchronizedLoadClass( String name, boolean resolve )
}
}

// java11
// 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 )
Expand All @@ -281,7 +282,7 @@ protected Class<?> findClass( String moduleName, String name )
}
try
{
return super.findClass( name );
return findClassInternal( name );
}
catch ( ClassNotFoundException e )
{
Expand All @@ -306,6 +307,12 @@ protected Class<?> findClass( String name )
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 );
Expand Down Expand Up @@ -422,7 +429,7 @@ public Class<?> loadClassFromSelf( String name )

if ( clazz == null )
{
clazz = super.findClass( name );
clazz = findClassInternal( name );
}

return clazz;
Expand Down Expand Up @@ -495,7 +502,7 @@ public URL loadResourceFromImport( String name )

public URL loadResourceFromSelf( String name )
{
return super.findResource( name );
return findResource( name );
}

public URL loadResourceFromParent( String name )
Expand Down Expand Up @@ -539,7 +546,7 @@ public Enumeration<URL> loadResourcesFromSelf( String name )
{
try
{
return super.findResources( name );
return findResources( name );
}
catch ( IOException e )
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* 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<String> 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 <code>null</code>.
* @param id The identifier for this realm, must not be <code>null</code>.
* @param baseClassLoader The base class loader for this realm, may be <code>null</code> to use the bootstrap class
* loader.
*/
public FilteredClassRealm( Predicate<String> 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<URL> findResources( String name )
throws IOException
{
if ( !filter.test( name ) )
{
return Collections.emptyEnumeration();
}
return super.findResources( name );
}
}
12 changes: 9 additions & 3 deletions src/site/xdoc/apiusage.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ ClassWorld world = new ClassWorld();
<p>
Once a <code>ClassWorld</code> is created, realms within it
can be created. These realms effectively only allow loading
of the core JVM classes.
of the core JVM classes initially.
</p>

<source><![CDATA[
Expand All @@ -42,15 +42,21 @@ ClassRealm logComponentRealm = world.newRealm( "logComponent" );
]]></source>

<p>
In order to make each <code>ClassRealm</code> useful, constituent
must be added to that each can provide certain classes.
In order to make each <code>ClassRealm</code> useful, constituents
in form of URLs must be added to it where each can provide certain classes.
The URL must return either a JAR or a directory on the default file system.
</p>

<source><![CDATA[
containerRealm.addURL( containerJarUrl );
logComponentRealm.addURL( logComponentJarUrl );
]]></source>

<p>
<code>ClassRealm</code>s can optionally be filtered to further restrict which classes/resources
are exposed. The filter is provided as additional argument to <code>world.newRealm( "filteredcontainer", myPredicate );</code>
</p>

<p>
Now, links between the various realms need to be created to allow
classes loaded from one to be available to classes loaded in another.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ public void testLoadClass_ClassWorldsClassRepeatedly()
}

@Test
public void testLoadClass_Java11()
public void testLoadClassWithModuleName_Java9()
{
final ExtendedClassRealm mainRealm = new ExtendedClassRealm( world );
mainRealm.addURL( getJarUrl( "a.jar" ) );
Expand Down Expand Up @@ -503,13 +503,17 @@ public void testGetResources_SelfBeforeParent()
assertEquals( Arrays.asList( childUrl, parentUrl ), urls );
}

// simulate new loadClass(Module,String) from java11
// it is reversed in terms of inheritance but enables to simulate the same behavior in these tests
/**
* 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 <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/ClassLoader.html#findClass(java.lang.String,java.lang.String)">ClassLoader#findClass(String,String)</a>
*/
private static class ExtendedClassRealm extends ClassRealm
{

public ExtendedClassRealm(final ClassWorld world)
{
super( world, "java11", Thread.currentThread().getContextClassLoader() );
super( world, "java9", Thread.currentThread().getContextClassLoader() );
}

public Class<?> simulateLoadClassFromModule(final String name)
Expand Down
Loading