Skip to content

Commit 39c7b9f

Browse files
gunnarmorlinggsmet
authored andcommitted
HV-1280 Setting context class loader to HV's defining CL before calling into JAXB/JAXP;
This makes sure we don't get in touch with a JAXP implementation provided as part of the deployed application when running on WF.
1 parent 7fa6c6e commit 39c7b9f

File tree

7 files changed

+248
-10
lines changed

7 files changed

+248
-10
lines changed

documentation/src/main/asciidoc/ch01.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ The following shows how to do this via a http://docs.oracle.com/javase/8/docs/te
104104
grant codeBase "file:path/to/hibernate-validator-{hvVersion}.jar" {
105105
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
106106
permission java.lang.RuntimePermission "accessDeclaredMembers";
107+
permission java.lang.RuntimePermission "setContextClassLoader";
107108
108109
// Only needed when working with XML descriptors (validation.xml or XML constraint mappings)
109110
permission java.util.PropertyPermission "mapAnyUriToUri", "read";
@@ -321,4 +322,3 @@ To learn more about the validation of beans and properties, just continue readin
321322
<<chapter-bean-constraints>>. If you are interested in using Bean Validation for the validation of
322323
method pre- and postcondition refer to <<chapter-method-constraints>>. In case your application has
323324
specific validation requirements have a look at <<validator-customconstraints>>.
324-
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Hibernate Validator, declare and validate application constraints
3+
*
4+
* License: Apache License, Version 2.0
5+
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
6+
*/
7+
package org.hibernate.validator.internal.util.privilegedactions;
8+
9+
import java.security.PrivilegedAction;
10+
11+
import org.hibernate.validator.internal.util.Contracts;
12+
13+
/**
14+
* Privileged action used to set the Thread context class loader.
15+
*
16+
* @author Guillaume Smet
17+
*/
18+
public final class SetContextClassLoader implements PrivilegedAction<Void> {
19+
private final ClassLoader classLoader;
20+
21+
public static SetContextClassLoader action(ClassLoader classLoader) {
22+
Contracts.assertNotNull( classLoader, "class loader must not be null" );
23+
return new SetContextClassLoader( classLoader );
24+
}
25+
26+
private SetContextClassLoader(ClassLoader classLoader) {
27+
this.classLoader = classLoader;
28+
}
29+
30+
@Override
31+
public Void run() {
32+
Thread.currentThread().setContextClassLoader( classLoader );
33+
return null;
34+
}
35+
}

engine/src/main/java/org/hibernate/validator/internal/xml/ValidationXmlParser.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.io.IOException;
1010
import java.io.InputStream;
1111
import java.security.AccessController;
12+
import java.security.PrivilegedAction;
1213
import java.security.PrivilegedExceptionAction;
1314
import java.util.EnumSet;
1415
import java.util.HashMap;
@@ -27,7 +28,9 @@
2728

2829
import org.hibernate.validator.internal.util.logging.Log;
2930
import org.hibernate.validator.internal.util.logging.LoggerFactory;
31+
import org.hibernate.validator.internal.util.privilegedactions.GetClassLoader;
3032
import org.hibernate.validator.internal.util.privilegedactions.NewJaxbContext;
33+
import org.hibernate.validator.internal.util.privilegedactions.SetContextClassLoader;
3134
import org.hibernate.validator.internal.util.privilegedactions.Unmarshal;
3235

3336
/**
@@ -69,7 +72,11 @@ public final BootstrapConfiguration parseValidationXml() {
6972
return BootstrapConfigurationImpl.getDefaultBootstrapConfiguration();
7073
}
7174

75+
ClassLoader previousTccl = run( GetClassLoader.fromContext() );
76+
7277
try {
78+
run( SetContextClassLoader.action( ValidationXmlParser.class.getClassLoader() ) );
79+
7380
// HV-970 The parser helper is only loaded if there actually is a validation.xml file;
7481
// this avoids accessing javax.xml.stream.* (which does not exist on Android) when not actually
7582
// working with the XML configuration
@@ -83,6 +90,7 @@ public final BootstrapConfiguration parseValidationXml() {
8390
return createBootstrapConfiguration( validationConfig );
8491
}
8592
finally {
93+
run( SetContextClassLoader.action( previousTccl ) );
8694
closeStream( inputStream );
8795
}
8896
}
@@ -191,6 +199,16 @@ private EnumSet<ExecutableType> getValidatedExecutableTypes(DefaultValidatedExec
191199
return executableTypes;
192200
}
193201

202+
/**
203+
* Runs the given privileged action, using a privileged block if required.
204+
* <p>
205+
* <b>NOTE:</b> This must never be changed into a publicly available method to avoid execution of arbitrary
206+
* privileged actions within HV's protection domain.
207+
*/
208+
private static <T> T run(PrivilegedAction<T> action) {
209+
return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run();
210+
}
211+
194212
/**
195213
* Runs the given privileged action, using a privileged block if required.
196214
* <p>

engine/src/main/java/org/hibernate/validator/internal/xml/XmlMappingParser.java

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.io.InputStream;
1212
import java.lang.annotation.Annotation;
1313
import java.security.AccessController;
14+
import java.security.PrivilegedAction;
1415
import java.security.PrivilegedExceptionAction;
1516
import java.util.Collections;
1617
import java.util.List;
@@ -37,7 +38,9 @@
3738
import org.hibernate.validator.internal.metadata.raw.ConstrainedType;
3839
import org.hibernate.validator.internal.util.logging.Log;
3940
import org.hibernate.validator.internal.util.logging.LoggerFactory;
41+
import org.hibernate.validator.internal.util.privilegedactions.GetClassLoader;
4042
import org.hibernate.validator.internal.util.privilegedactions.NewJaxbContext;
43+
import org.hibernate.validator.internal.util.privilegedactions.SetContextClassLoader;
4144
import org.hibernate.validator.internal.util.privilegedactions.Unmarshal;
4245

4346
import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList;
@@ -137,15 +140,7 @@ public final void parse(Set<InputStream> mappingStreams) {
137140
in.mark( Integer.MAX_VALUE );
138141
}
139142

140-
XMLEventReader xmlEventReader = xmlParserHelper.createXmlEventReader( "constraint mapping file", new CloseIgnoringInputStream( in ) );
141-
String schemaVersion = xmlParserHelper.getSchemaVersion( "constraint mapping file", xmlEventReader );
142-
String schemaResourceName = getSchemaResourceName( schemaVersion );
143-
Schema schema = xmlParserHelper.getSchema( schemaResourceName );
144-
145-
Unmarshaller unmarshaller = jc.createUnmarshaller();
146-
unmarshaller.setSchema( schema );
147-
148-
ConstraintMappingsType mapping = getValidationConfig( xmlEventReader, unmarshaller );
143+
ConstraintMappingsType mapping = unmarshal( jc, in );
149144
String defaultPackage = mapping.getDefaultPackage();
150145

151146
parseConstraintDefinitions(
@@ -180,6 +175,27 @@ public final void parse(Set<InputStream> mappingStreams) {
180175
}
181176
}
182177

178+
private ConstraintMappingsType unmarshal(JAXBContext jc, InputStream in) throws JAXBException {
179+
ClassLoader previousTccl = run( GetClassLoader.fromContext() );
180+
181+
try {
182+
run( SetContextClassLoader.action( XmlMappingParser.class.getClassLoader() ) );
183+
184+
XMLEventReader xmlEventReader = xmlParserHelper.createXmlEventReader( "constraint mapping file", new CloseIgnoringInputStream( in ) );
185+
String schemaVersion = xmlParserHelper.getSchemaVersion( "constraint mapping file", xmlEventReader );
186+
String schemaResourceName = getSchemaResourceName( schemaVersion );
187+
Schema schema = xmlParserHelper.getSchema( schemaResourceName );
188+
189+
Unmarshaller unmarshaller = jc.createUnmarshaller();
190+
unmarshaller.setSchema( schema );
191+
192+
return getValidationConfig( xmlEventReader, unmarshaller );
193+
}
194+
finally {
195+
run( SetContextClassLoader.action( previousTccl ) );
196+
}
197+
}
198+
183199
public final Set<Class<?>> getXmlConfiguredClasses() {
184200
return processedClasses;
185201
}
@@ -369,6 +385,16 @@ private String getSchemaResourceName(String schemaVersion) {
369385
return schemaResource;
370386
}
371387

388+
/**
389+
* Runs the given privileged action, using a privileged block if required.
390+
* <p>
391+
* <b>NOTE:</b> This must never be changed into a publicly available method to avoid execution of arbitrary
392+
* privileged actions within HV's protection domain.
393+
*/
394+
private static <T> T run(PrivilegedAction<T> action) {
395+
return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run();
396+
}
397+
372398
/**
373399
* Runs the given privileged action, using a privileged block if required.
374400
* <p>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Hibernate Validator, declare and validate application constraints
3+
*
4+
* License: Apache License, Version 2.0
5+
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
6+
*/
7+
package org.hibernate.validator.integration.wildfly.xml;
8+
9+
/**
10+
* @author Gunnar Morling
11+
*/
12+
public class Camera {
13+
14+
public String brand = null;
15+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
* Hibernate Validator, declare and validate application constraints
3+
*
4+
* License: Apache License, Version 2.0
5+
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
6+
*/
7+
package org.hibernate.validator.integration.wildfly.xml;
8+
9+
import static org.junit.Assert.assertEquals;
10+
import static org.junit.Assert.assertSame;
11+
12+
import java.util.Set;
13+
14+
import javax.inject.Inject;
15+
import javax.validation.ConstraintViolation;
16+
import javax.validation.Validator;
17+
import javax.validation.constraints.NotNull;
18+
19+
import org.jboss.arquillian.container.test.api.Deployer;
20+
import org.jboss.arquillian.container.test.api.Deployment;
21+
import org.jboss.arquillian.container.test.api.RunAsClient;
22+
import org.jboss.arquillian.junit.Arquillian;
23+
import org.jboss.arquillian.junit.InSequence;
24+
import org.jboss.arquillian.test.api.ArquillianResource;
25+
import org.jboss.shrinkwrap.api.Archive;
26+
import org.jboss.shrinkwrap.api.ShrinkWrap;
27+
import org.jboss.shrinkwrap.api.asset.Asset;
28+
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
29+
import org.jboss.shrinkwrap.api.asset.StringAsset;
30+
import org.jboss.shrinkwrap.api.spec.WebArchive;
31+
import org.jboss.shrinkwrap.descriptor.api.Descriptors;
32+
import org.jboss.shrinkwrap.descriptor.api.validationConfiguration11.ValidationConfigurationDescriptor;
33+
import org.jboss.shrinkwrap.descriptor.api.validationMapping11.ValidationMappingDescriptor;
34+
import org.jboss.shrinkwrap.resolver.api.maven.Maven;
35+
import org.junit.Test;
36+
import org.junit.runner.RunWith;
37+
38+
/**
39+
* Test for https://hibernate.atlassian.net/browse/HV-1280. To reproduce the issue, the deployment must be done twice
40+
* (it will only show up during the 2nd deploy), which is why the test is managing the deployment itself via client-side
41+
* test methods.
42+
*
43+
* @author Gunnar Morling
44+
*/
45+
@RunWith(Arquillian.class)
46+
public class JaxpContainedInDeploymentIT {
47+
48+
private static final String WAR_FILE_NAME = JaxpContainedInDeploymentIT.class.getSimpleName() + ".war";
49+
50+
@ArquillianResource
51+
private Deployer deployer;
52+
53+
@Inject
54+
private Validator validator;
55+
56+
@Deployment(name = "jaxpit", managed = false)
57+
public static Archive<?> createTestArchive() {
58+
return ShrinkWrap
59+
.create( WebArchive.class, WAR_FILE_NAME )
60+
.addClass( Camera.class )
61+
.addAsResource( validationXml(), "META-INF/validation.xml" )
62+
.addAsResource( mappingXml(), "META-INF/my-mapping.xml" )
63+
.addAsLibrary( Maven.resolver().resolve( "xerces:xercesImpl:2.9.1" ).withoutTransitivity().asSingleFile() )
64+
.addAsResource( "log4j.properties" )
65+
.addAsWebInfResource( EmptyAsset.INSTANCE, "beans.xml" );
66+
}
67+
68+
private static Asset validationXml() {
69+
String validationXml = Descriptors.create( ValidationConfigurationDescriptor.class )
70+
.version( "1.1" )
71+
.constraintMapping( "META-INF/my-mapping.xml" )
72+
.exportAsString();
73+
return new StringAsset( validationXml );
74+
}
75+
76+
private static Asset mappingXml() {
77+
String mappingXml = Descriptors.create( ValidationMappingDescriptor.class )
78+
.version( "1.1" )
79+
.createBean()
80+
.clazz( Camera.class.getName() )
81+
.createField()
82+
.name( "brand" )
83+
.createConstraint()
84+
.annotation( "javax.validation.constraints.NotNull" )
85+
.up()
86+
.up()
87+
.up()
88+
.exportAsString();
89+
return new StringAsset( mappingXml );
90+
}
91+
92+
@Test
93+
@RunAsClient
94+
@InSequence(0)
95+
public void deploy1() throws Exception {
96+
deployer.deploy( "jaxpit" );
97+
}
98+
99+
@Test
100+
@InSequence(1)
101+
public void test1() throws Exception {
102+
doTest();
103+
}
104+
105+
@Test
106+
@RunAsClient
107+
@InSequence(2)
108+
public void undeploy1() throws Exception {
109+
deployer.undeploy( "jaxpit" );
110+
}
111+
112+
@Test
113+
@RunAsClient
114+
@InSequence(3)
115+
public void deploy2() throws Exception {
116+
deployer.deploy( "jaxpit" );
117+
}
118+
119+
@Test
120+
@InSequence(4)
121+
public void test2() throws Exception {
122+
doTest();
123+
}
124+
125+
@Test
126+
@RunAsClient
127+
@InSequence(5)
128+
public void undeploy2() throws Exception {
129+
deployer.undeploy( "jaxpit" );
130+
}
131+
132+
private void doTest() {
133+
Set<ConstraintViolation<Camera>> violations = validator.validate( new Camera() );
134+
135+
assertEquals( 1, violations.size() );
136+
assertSame( NotNull.class, violations.iterator()
137+
.next()
138+
.getConstraintDescriptor()
139+
.getAnnotation()
140+
.annotationType() );
141+
}
142+
}

tck-runner/src/test/resources/test.policy

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
grant codeBase "file:${localRepository}/org/hibernate/hibernate-validator/${project.version}/hibernate-validator-${project.version}.jar" {
2727
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
2828
permission java.lang.RuntimePermission "accessDeclaredMembers";
29+
permission java.lang.RuntimePermission "setContextClassLoader";
2930

3031
// JAXB
3132
permission java.util.PropertyPermission "mapAnyUriToUri", "read";
@@ -36,6 +37,7 @@ grant codeBase "file:${localRepository}/org/hibernate/hibernate-validator/${proj
3637
grant codeBase "file:${basedir}/../engine/target/hibernate-validator-${project.version}.jar" {
3738
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
3839
permission java.lang.RuntimePermission "accessDeclaredMembers";
40+
permission java.lang.RuntimePermission "setContextClassLoader";
3941

4042
// JAXB
4143
permission java.util.PropertyPermission "mapAnyUriToUri", "read";

0 commit comments

Comments
 (0)