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