Skip to content

Commit 917a2ee

Browse files
committed
Allows to register a filtered class realm
Only those classes/resoures which match one of the given prefixes are exposed through the filtered class realm This closes #69
1 parent 1862fc6 commit 917a2ee

File tree

4 files changed

+215
-8
lines changed

4 files changed

+215
-8
lines changed

src/main/java/org/codehaus/plexus/classworlds/ClassWorld.java

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@
2323
import java.util.LinkedHashMap;
2424
import java.util.List;
2525
import java.util.Map;
26+
import java.util.Set;
2627

2728
import org.codehaus.plexus.classworlds.realm.ClassRealm;
2829
import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
30+
import org.codehaus.plexus.classworlds.realm.FilteredClassRealm;
2931
import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
3032

3133
/**
@@ -64,7 +66,39 @@ public ClassRealm newRealm( String id )
6466
return newRealm( id, getClass().getClassLoader() );
6567
}
6668

67-
public synchronized ClassRealm newRealm( String id, ClassLoader classLoader )
69+
public ClassRealm newRealm( String id, ClassLoader classLoader )
70+
throws DuplicateRealmException
71+
{
72+
return newRealm( id, classLoader, Collections.emptySet() );
73+
}
74+
75+
/**
76+
* Shortcut for {@link #newRealm(String, ClassLoader, Set)} with the class loader of the current class.
77+
* @param id
78+
* @param allowedResourceNamePrefixes
79+
* @return the created class realm
80+
* @throws DuplicateRealmException
81+
* @since 2.7.0
82+
* @see FilteredClassRealm
83+
*/
84+
public synchronized ClassRealm newRealm( String id, Set<String> allowedResourceNamePrefixes )
85+
throws DuplicateRealmException
86+
{
87+
return newRealm( id, getClass().getClassLoader(), allowedResourceNamePrefixes );
88+
}
89+
90+
/**
91+
* Adds a class realm with filtering.
92+
* Only resources/classes starting with one of the given prefixes are exposed.
93+
* @param id
94+
* @param classLoader
95+
* @param allowedResourceNamePrefixes the prefixes of resource names which should be exposed. Separator '/' is used here (even for classes).
96+
* @return the created class realm
97+
* @throws DuplicateRealmException
98+
* @since 2.7.0
99+
* @see FilteredClassRealm
100+
*/
101+
public synchronized ClassRealm newRealm( String id, ClassLoader classLoader, Set<String> allowedResourceNamePrefixes )
68102
throws DuplicateRealmException
69103
{
70104
if ( realms.containsKey( id ) )
@@ -74,8 +108,14 @@ public synchronized ClassRealm newRealm( String id, ClassLoader classLoader )
74108

75109
ClassRealm realm;
76110

77-
realm = new ClassRealm( this, id, classLoader );
78-
111+
if ( allowedResourceNamePrefixes.isEmpty() )
112+
{
113+
realm = new ClassRealm( this, id, classLoader );
114+
}
115+
else
116+
{
117+
realm = new FilteredClassRealm( allowedResourceNamePrefixes, this, id, classLoader );
118+
}
79119
realms.put( id, realm );
80120

81121
for ( ClassWorldListener listener : listeners )
@@ -85,7 +125,7 @@ public synchronized ClassRealm newRealm( String id, ClassLoader classLoader )
85125

86126
return realm;
87127
}
88-
128+
89129
public synchronized void disposeRealm( String id )
90130
throws NoSuchRealmException
91131
{

src/main/java/org/codehaus/plexus/classworlds/realm/ClassRealm.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ public ClassRealm getParentRealm()
200200
public ClassRealm createChildRealm( String id )
201201
throws DuplicateRealmException
202202
{
203-
ClassRealm childRealm = getWorld().newRealm( id, null );
203+
ClassRealm childRealm = getWorld().newRealm( id, (ClassLoader) null );
204204

205205
childRealm.setParentRealm( this );
206206

@@ -306,6 +306,12 @@ protected Class<?> findClass( String name )
306306
throw new ClassNotFoundException( name );
307307
}
308308

309+
protected Class<?> findClassInternal( String name )
310+
throws ClassNotFoundException
311+
{
312+
return super.findClass( name );
313+
}
314+
309315
public URL getResource( String name )
310316
{
311317
URL resource = super.getResource( name );
@@ -422,7 +428,7 @@ public Class<?> loadClassFromSelf( String name )
422428

423429
if ( clazz == null )
424430
{
425-
clazz = super.findClass( name );
431+
clazz = findClassInternal( name );
426432
}
427433

428434
return clazz;
@@ -495,7 +501,7 @@ public URL loadResourceFromImport( String name )
495501

496502
public URL loadResourceFromSelf( String name )
497503
{
498-
return super.findResource( name );
504+
return findResource( name );
499505
}
500506

501507
public URL loadResourceFromParent( String name )
@@ -539,7 +545,7 @@ public Enumeration<URL> loadResourcesFromSelf( String name )
539545
{
540546
try
541547
{
542-
return super.findResources( name );
548+
return findResources( name );
543549
}
544550
catch ( IOException e )
545551
{
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package org.codehaus.plexus.classworlds.realm;
2+
3+
/*
4+
* Licensed to the Apache Software Foundation (ASF) under one
5+
* or more contributor license agreements. See the NOTICE file
6+
* distributed with this work for additional information
7+
* regarding copyright ownership. The ASF licenses this file
8+
* to you under the Apache License, Version 2.0 (the
9+
* "License"); you may not use this file except in compliance
10+
* with the License. You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing,
15+
* software distributed under the License is distributed on an
16+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
* KIND, either express or implied. See the License for the
18+
* specific language governing permissions and limitations
19+
* under the License.
20+
*/
21+
22+
import java.io.IOException;
23+
import java.net.URL;
24+
import java.util.Collections;
25+
import java.util.Enumeration;
26+
import java.util.Set;
27+
28+
import org.codehaus.plexus.classworlds.ClassWorld;
29+
30+
/**
31+
* Similar to {@link ClassRealm} but only exposing some resources of the underlying URL.
32+
* Only supposed to be called from {@link ClassWorld}.
33+
*/
34+
public class FilteredClassRealm extends ClassRealm
35+
{
36+
37+
// no regular expressions for performance reasons
38+
private final Set<String> allowedResourceNamePrefixes;
39+
40+
/**
41+
* Creates a new class realm.
42+
*
43+
* @param allowedResourceNamePrefixes all resources not starting with one of the given prefixes are never exposed through this class loader
44+
* @param world The class world this realm belongs to, must not be <code>null</code>.
45+
* @param id The identifier for this realm, must not be <code>null</code>.
46+
* @param baseClassLoader The base class loader for this realm, may be <code>null</code> to use the bootstrap class
47+
* loader.
48+
*/
49+
public FilteredClassRealm( Set<String> allowedResourceNamePrefixes, ClassWorld world, String id, ClassLoader baseClassLoader )
50+
{
51+
super( world, id, baseClassLoader );
52+
this.allowedResourceNamePrefixes = allowedResourceNamePrefixes;
53+
}
54+
55+
@Override
56+
protected Class<?> findClassInternal( String name )
57+
throws ClassNotFoundException
58+
{
59+
String resourceName = name.replace( '.', '/' ).concat( ".class" );
60+
if ( !isAllowedName( resourceName ))
61+
{
62+
throw new ClassNotFoundException(name);
63+
}
64+
return super.findClassInternal( name );
65+
}
66+
67+
@Override
68+
public URL findResource( String name )
69+
{
70+
if ( !isAllowedName( name ))
71+
{
72+
return null;
73+
}
74+
return super.findResource( name );
75+
}
76+
77+
@Override
78+
public Enumeration<URL> findResources( String name )
79+
throws IOException
80+
{
81+
if ( !isAllowedName( name ))
82+
{
83+
return Collections.emptyEnumeration();
84+
}
85+
return super.findResources( name );
86+
}
87+
88+
private boolean isAllowedName( String name )
89+
{
90+
return allowedResourceNamePrefixes.stream().anyMatch( name::startsWith );
91+
}
92+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package org.codehaus.plexus.classworlds.realm;
2+
3+
/*
4+
* Licensed to the Apache Software Foundation (ASF) under one
5+
* or more contributor license agreements. See the NOTICE file
6+
* distributed with this work for additional information
7+
* regarding copyright ownership. The ASF licenses this file
8+
* to you under the Apache License, Version 2.0 (the
9+
* "License"); you may not use this file except in compliance
10+
* with the License. You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing,
15+
* software distributed under the License is distributed on an
16+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
* KIND, either express or implied. See the License for the
18+
* specific language governing permissions and limitations
19+
* under the License.
20+
*/
21+
22+
import java.util.HashSet;
23+
import java.util.Set;
24+
25+
import org.codehaus.plexus.classworlds.AbstractClassWorldsTestCase;
26+
import org.codehaus.plexus.classworlds.ClassWorld;
27+
import org.junit.Before;
28+
import org.junit.Test;
29+
30+
import static org.junit.Assert.assertFalse;
31+
import static org.junit.Assert.assertNotNull;
32+
import static org.junit.Assert.assertNull;
33+
import static org.junit.Assert.assertThrows;
34+
import static org.junit.Assert.assertTrue;
35+
36+
public class FilteredClassRealmTest extends AbstractClassWorldsTestCase
37+
{
38+
private ClassWorld world;
39+
40+
@Before
41+
public void setUp()
42+
{
43+
this.world = new ClassWorld();
44+
}
45+
46+
@Test
47+
public void testLoadClassAndResourcesFiltered()
48+
throws Exception
49+
{
50+
// only allow loading resources whose names start with "a."
51+
Set<String> allowedResourcePrefixes = new HashSet<>();
52+
allowedResourcePrefixes.add( "a." );
53+
allowedResourcePrefixes.add( "a/Aa" );
54+
ClassRealm realmA = this.world.newRealm( "realmA", allowedResourcePrefixes );
55+
56+
assertThrows( ClassNotFoundException.class, () -> realmA.loadClass( "a.Aa" ) );
57+
realmA.addURL( getJarUrl( "a.jar" ) );
58+
59+
assertNotNull( realmA.loadClass( "a.Aa" ) );
60+
assertThrows( ClassNotFoundException.class, () -> realmA.loadClass( "a.A" ) );
61+
62+
assertNull( realmA.getResource( "common.properties" ) );
63+
assertFalse( realmA.getResources( "common.properties" ).hasMoreElements() );
64+
65+
assertNotNull( realmA.getResource( "a.properties" ) );
66+
assertTrue( realmA.getResources( "a.properties" ).hasMoreElements() );
67+
}
68+
69+
}

0 commit comments

Comments
 (0)