diff --git a/hibernate-validator/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java b/hibernate-validator/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java
index dd1fd6019f..ec956b05cc 100644
--- a/hibernate-validator/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java
+++ b/hibernate-validator/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java
@@ -16,6 +16,7 @@
*/
package org.hibernate.validator.internal.engine;
+import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
@@ -135,11 +136,20 @@ public final ConfigurationImpl constraintValidatorFactory(ConstraintValidatorFac
return this;
}
- public final HibernateValidatorConfiguration addMapping(InputStream stream) {
+ /**
+ * {@inheritDoc}
+ *
+ * @param stream XML mapping stream.
+ *
+ * Note: In order to reuse the {@code ConfigurationInstance} to build multiple
+ * validator factories a {@link java.io.BufferedInputStream} has to be provided.
+ *
+ */
+ public final HibernateValidatorConfiguration addMapping(InputStream stream) {
Contracts.assertNotNull( stream, MESSAGES.parameterMustNotBeNull( "stream" ) );
- validationBootstrapParameters.addMapping( stream );
+ validationBootstrapParameters.addMapping( stream.markSupported() ? stream : new BufferedInputStream(stream) );
return this;
}
@@ -201,8 +211,6 @@ public final ValidatorFactory buildValidatorFactory() {
}
}
- // reset the param holder
- validationBootstrapParameters = new ValidationBootstrapParameters();
return factory;
}
diff --git a/hibernate-validator/src/main/java/org/hibernate/validator/internal/metadata/provider/XmlConfigurationMetaDataProvider.java b/hibernate-validator/src/main/java/org/hibernate/validator/internal/metadata/provider/XmlConfigurationMetaDataProvider.java
index bcb1d5a224..bca0183748 100644
--- a/hibernate-validator/src/main/java/org/hibernate/validator/internal/metadata/provider/XmlConfigurationMetaDataProvider.java
+++ b/hibernate-validator/src/main/java/org/hibernate/validator/internal/metadata/provider/XmlConfigurationMetaDataProvider.java
@@ -46,13 +46,15 @@
* XML descriptors as defined by the Bean Validation API.
*
* @author Gunnar Morling
+ * @author Hardy Ferentschik
*/
public class XmlConfigurationMetaDataProvider extends MetaDataProviderImplBase {
private final AnnotationIgnores annotationIgnores;
/**
- * @param mappingStreams
+ * @param constraintHelper the constraint helper utility
+ * @param mappingStreams the defined mapping streams
*/
public XmlConfigurationMetaDataProvider(ConstraintHelper constraintHelper, Set mappingStreams) {
@@ -66,7 +68,7 @@ public XmlConfigurationMetaDataProvider(ConstraintHelper constraintHelper, Set>> constraintsByLocation = partition(
mappingParser.getConstraintsForClass( clazz ), byLocation()
);
- Set cascades = getCascades( mappingParser, clazz );
+ Set cascades = getCascades( mappingParser, clazz );
Set constrainedElements = getConstrainedElements( constraintsByLocation, cascades );
@@ -85,7 +87,7 @@ public XmlConfigurationMetaDataProvider(ConstraintHelper constraintHelper, Set getConstrainedElements(Map>> constraintsByLocation, Set cascades) {
+ private Set getConstrainedElements(Map>> constraintsByLocation, Set cascades) {
Set configuredLocations = new HashSet( cascades );
configuredLocations.addAll( constraintsByLocation.keySet() );
@@ -129,20 +131,20 @@ else if ( oneConfiguredLocation.getElementType() == ElementType.TYPE ) {
}
/**
- * @param mappingParser
- * @param clazz
+ * @param mappingParser the xml parser
+ * @param clazz the type for which to retrieve cascaded members
*
- * @return
+ * @return returns a set of cascaded constraints
*/
- private Set getCascades(XmlMappingParser mappingParser, Class> clazz) {
+ private Set getCascades(XmlMappingParser mappingParser, Class> clazz) {
- Set theValue = newHashSet();
+ Set cascadedConstraintSet = newHashSet();
for ( Member member : mappingParser.getCascadedMembersForClass( clazz ) ) {
- theValue.add( new BeanConstraintLocation( member ) );
+ cascadedConstraintSet.add( new BeanConstraintLocation( member ) );
}
- return theValue;
+ return cascadedConstraintSet;
}
public AnnotationIgnores getAnnotationIgnores() {
@@ -156,5 +158,4 @@ public ConstraintLocation getPartition(MetaConstraint> constraint) {
}
};
}
-
}
diff --git a/hibernate-validator/src/main/java/org/hibernate/validator/internal/xml/XmlMappingParser.java b/hibernate-validator/src/main/java/org/hibernate/validator/internal/xml/XmlMappingParser.java
index d21b04f0e7..58d01c4bfb 100644
--- a/hibernate-validator/src/main/java/org/hibernate/validator/internal/xml/XmlMappingParser.java
+++ b/hibernate-validator/src/main/java/org/hibernate/validator/internal/xml/XmlMappingParser.java
@@ -16,6 +16,8 @@
*/
package org.hibernate.validator.internal.xml;
+import java.io.FilterInputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.annotation.Annotation;
@@ -577,12 +579,27 @@ private ConstraintMappingsType getValidationConfig(InputStream in) {
ConstraintMappingsType constraintMappings;
Schema schema = getMappingSchema();
try {
+ // check whether mark is supported, if so we can reset the stream in order to allow reuse of Configuration
+ boolean markSupported = in.markSupported();
+ if ( markSupported ) {
+ in.mark( Integer.MAX_VALUE );
+ }
+
JAXBContext jc = JAXBContext.newInstance( ConstraintMappingsType.class );
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setSchema( schema );
- StreamSource stream = new StreamSource( in );
+ StreamSource stream = new StreamSource( new CloseIgnoringInputStream( in ) );
JAXBElement root = unmarshaller.unmarshal( stream, ConstraintMappingsType.class );
constraintMappings = root.getValue();
+
+ if ( markSupported ) {
+ try {
+ in.reset();
+ }
+ catch ( IOException e ) {
+ log.debug( "Unable to reset input stream." );
+ }
+ }
}
catch ( JAXBException e ) {
throw log.getErrorParsingMappingFileException( e );
@@ -603,4 +620,16 @@ private Schema getMappingSchema() {
}
return schema;
}
+
+ // JAXB closes the underlying input stream
+ public class CloseIgnoringInputStream extends FilterInputStream {
+ public CloseIgnoringInputStream(InputStream in) {
+ super( in );
+ }
+
+ @Override
+ public void close() {
+ // do nothing
+ }
+ }
}
diff --git a/hibernate-validator/src/test/java/org/hibernate/validator/test/internal/engine/ConfigurationImplTest.java b/hibernate-validator/src/test/java/org/hibernate/validator/test/internal/engine/ConfigurationImplTest.java
new file mode 100644
index 0000000000..6cb0028c72
--- /dev/null
+++ b/hibernate-validator/src/test/java/org/hibernate/validator/test/internal/engine/ConfigurationImplTest.java
@@ -0,0 +1,126 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2012, Red Hat, Inc. and/or its affiliates, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* 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.
+*/
+package org.hibernate.validator.test.internal.engine;
+
+import java.io.InputStream;
+import java.lang.annotation.ElementType;
+import javax.validation.Configuration;
+import javax.validation.Path;
+import javax.validation.TraversableResolver;
+import javax.validation.ValidationException;
+import javax.validation.ValidatorFactory;
+
+import org.testng.annotations.Test;
+
+import org.hibernate.validator.HibernateValidatorConfiguration;
+import org.hibernate.validator.testutil.TestForIssue;
+
+import static org.hibernate.validator.testutil.ValidatorUtil.getConfiguration;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNotSame;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * @author Hardy Ferentschik
+ */
+public class ConfigurationImplTest {
+
+ @Test
+ @TestForIssue(jiraKey = "HV-563")
+ public void testCallBuildValidatorFactoryMultipleTimes() {
+ final Configuration configuration = getConfiguration();
+
+ ValidatorFactory factory1 = configuration.buildValidatorFactory();
+ assertNotNull( factory1 );
+
+ ValidatorFactory factory2 = configuration.buildValidatorFactory();
+ assertNotNull( factory2 );
+
+ assertNotSame( factory1, factory2 );
+ }
+
+ @Test
+ @TestForIssue(jiraKey = "HV-563")
+ public void testConfigurationReusableAndMutable() {
+ final Configuration configuration = getConfiguration();
+
+ ValidatorFactory factory1 = configuration.buildValidatorFactory();
+ assertNotNull( factory1 );
+
+ configuration.traversableResolver( new TestTraversableResolver() );
+ ValidatorFactory factory2 = configuration.buildValidatorFactory();
+ assertNotNull( factory2 );
+
+ assertNotSame( factory1.getTraversableResolver(), factory2.getTraversableResolver() );
+ assertTrue( factory2.getTraversableResolver() instanceof TestTraversableResolver );
+ }
+
+
+ @Test
+ @TestForIssue(jiraKey = "HV-563")
+ public void testReusableConfigurationWithInputStream() throws Exception {
+ final Configuration configuration = getConfiguration();
+
+ InputStream mappingStream = ConfigurationImplTest.class.getResourceAsStream( "mapping.xml" );
+
+ try {
+ configuration.addMapping( mappingStream );
+ ValidatorFactory factory1 = configuration.buildValidatorFactory();
+ assertNotNull( factory1 );
+
+ ValidatorFactory factory2 = configuration.buildValidatorFactory();
+ assertNotNull( factory2 );
+
+ assertNotSame( factory1, factory2 );
+ }
+ finally {
+ mappingStream.close();
+ }
+ }
+
+ @Test(expectedExceptions = ValidationException.class, expectedExceptionsMessageRegExp = "HV000115.*")
+ @TestForIssue(jiraKey = "HV-563")
+ public void testReusableConfigurationWithClosedInputStream() throws Exception {
+ final Configuration configuration = getConfiguration();
+
+ InputStream mappingStream = ConfigurationImplTest.class.getResourceAsStream( "mapping.xml" );
+
+ try {
+ configuration.addMapping( mappingStream );
+ ValidatorFactory factory1 = configuration.buildValidatorFactory();
+ assertNotNull( factory1 );
+ }
+ finally {
+ mappingStream.close();
+ }
+
+ configuration.buildValidatorFactory();
+ }
+
+ public class TestTraversableResolver implements TraversableResolver {
+
+ public boolean isReachable(Object traversableObject, Path.Node traversableProperty, Class> rootBeanType, Path pathToTraversableObject, ElementType elementType) {
+ return true;
+ }
+
+ public boolean isCascadable(Object traversableObject, Path.Node traversableProperty, Class> rootBeanType, Path pathToTraversableObject, ElementType elementType) {
+ return true;
+ }
+ }
+}
+
+
diff --git a/hibernate-validator/src/test/java/org/hibernate/validator/test/internal/xml/XmlMappingTest.java b/hibernate-validator/src/test/java/org/hibernate/validator/test/internal/xml/XmlMappingTest.java
index c8d5c16841..8ea6a984b5 100644
--- a/hibernate-validator/src/test/java/org/hibernate/validator/test/internal/xml/XmlMappingTest.java
+++ b/hibernate-validator/src/test/java/org/hibernate/validator/test/internal/xml/XmlMappingTest.java
@@ -33,6 +33,7 @@
import org.hibernate.validator.HibernateValidatorConfiguration;
import org.hibernate.validator.cfg.ConstraintMapping;
import org.hibernate.validator.cfg.defs.SizeDef;
+import org.hibernate.validator.testutil.TestForIssue;
import org.hibernate.validator.testutil.ValidatorUtil;
import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertCorrectConstraintViolationMessages;
@@ -45,9 +46,7 @@
public class XmlMappingTest {
@Test
- /**
- * HV-214
- */
+ @TestForIssue(jiraKey = "HV-214")
public void testConstraintInheritanceWithXmlConfiguration() {
final Configuration> configuration = ValidatorUtil.getConfiguration();
@@ -62,9 +61,7 @@ public void testConstraintInheritanceWithXmlConfiguration() {
}
@Test
- /**
- * HV-252
- */
+ @TestForIssue(jiraKey = "HV-252")
public void testListOfString() {
final Configuration> configuration = ValidatorUtil.getConfiguration();
@@ -86,9 +83,7 @@ public void testListOfString() {
}
@Test
- /**
- * HV-262
- */
+ @TestForIssue(jiraKey = "HV-262")
public void testInterfaceConfiguration() {
final Configuration> configuration = ValidatorUtil.getConfiguration();
@@ -102,9 +97,7 @@ public void testInterfaceConfiguration() {
}
@Test
- /**
- * HV-262
- */
+ @TestForIssue(jiraKey = "HV-262")
public void testInterfaceImplementationConfiguration() {
final Configuration> configuration = ValidatorUtil.getConfiguration();
@@ -118,9 +111,7 @@ public void testInterfaceImplementationConfiguration() {
}
@Test
- /**
- * HV-263
- */
+ @TestForIssue(jiraKey = "HV-263")
public void testEmptyInterfaceConfiguration() {
final Configuration> configuration = ValidatorUtil.getConfiguration();
@@ -133,10 +124,8 @@ public void testEmptyInterfaceConfiguration() {
assertEquals( violations.size(), 0 );
}
- /**
- * HV-480
- */
@Test
+ @TestForIssue(jiraKey = "HV-480")
public void testConstraintsFromXmlAndProgrammaticApiAddUp() {
//given
diff --git a/hibernate-validator/src/test/resources/org/hibernate/validator/test/internal/engine/mapping.xml b/hibernate-validator/src/test/resources/org/hibernate/validator/test/internal/engine/mapping.xml
new file mode 100644
index 0000000000..10a8443e4d
--- /dev/null
+++ b/hibernate-validator/src/test/resources/org/hibernate/validator/test/internal/engine/mapping.xml
@@ -0,0 +1,8 @@
+
+
+ org.hibernate.validator.test.internal.engine
+
\ No newline at end of file