Skip to content

Commit

Permalink
HV-1007 Using XMLReaderEvent#peek to look ahead for schema version in…
Browse files Browse the repository at this point in the history
…stead of mark&reset of stream
  • Loading branch information
hferentschik committed Jul 24, 2015
1 parent 4c206b7 commit 8d73c0e
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 144 deletions.
Expand Up @@ -5,14 +5,11 @@
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator.internal.util.logging;

import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.IllegalFormatException;
import java.util.List;
import java.util.Set;
import java.util.regex.PatternSyntaxException;
Expand Down Expand Up @@ -205,15 +202,6 @@ UnexpectedTypeException getNoValidatorFoundForTypeException(String constraintTyp
@Message(id = 48, value = "Unable to expand group sequence.")
GroupDefinitionException getUnableToExpandGroupSequenceException();

@Message(id = 49, value = "The given index must be between %1$s and %2$s.")
IndexOutOfBoundsException getInvalidIndexException(String lowerBound, String upperBound);

@Message(id = 50, value = "Missing format string in template: %s.")
ValidationException getMissingFormatStringInTemplateException(String expression);

@Message(id = 51, value = "Invalid format: %s.")
ValidationException getInvalidFormatException(String message, @Cause IllegalFormatException e);

@Message(id = 52,
value = "Default group sequence and default group sequence provider cannot be defined at the same time.")
GroupDefinitionException getInvalidDefaultGroupSequenceDefinitionException();
Expand Down Expand Up @@ -416,9 +404,6 @@ UnexpectedTypeException getNoValidatorFoundForTypeException(String constraintTyp
@Message(id = 122, value = "Unsupported schema version for %s: %s.")
ValidationException getUnsupportedSchemaVersionException(String file, String version);

@Message(id = 123, value = "Unable to parse %s.")
ValidationException getUnableToResetXmlInputStreamException(String file, @Cause IOException e);

@Message(id = 124, value = "Found multiple group conversions for source group %s: %s.")
ConstraintDeclarationException getMultipleGroupConversionsForSameSourceException(Class<?> from, Set<Class<?>> tos);

Expand Down Expand Up @@ -647,4 +632,7 @@ UnexpectedTypeException getNoValidatorFoundForTypeException(String constraintTyp
@Message(id = 189,
value = "The configuration of value unwrapping for property '%s' of bean '%s' is inconsistent between the field and its getter.")
ConstraintDeclarationException getInconsistentValueUnwrappingConfigurationBetweenFieldAndItsGetterException(String property, String clazz);

@Message(id = 190, value = "Unable to parse %s.")
ValidationException getUnableToCreateXMLEventReader(String file, @Cause Exception e);
}
Expand Up @@ -7,11 +7,10 @@
package org.hibernate.validator.internal.util.privilegedactions;

import java.security.PrivilegedExceptionAction;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.Source;
import javax.xml.stream.XMLEventReader;

/**
* Unmarshals the given source.
Expand All @@ -21,21 +20,21 @@
public final class Unmarshal<T> implements PrivilegedExceptionAction<JAXBElement<T>> {

private final Unmarshaller unmarshaller;
private final Source source;
private final XMLEventReader xmlEventReader;
private final Class<T> clazz;

public static <T> Unmarshal<T> action(Unmarshaller unmarshaller, Source source, Class<T> clazz) {
return new Unmarshal<T>( unmarshaller, source, clazz );
public static <T> Unmarshal<T> action(Unmarshaller unmarshaller, XMLEventReader xmlEventReader, Class<T> clazz) {
return new Unmarshal<T>( unmarshaller, xmlEventReader, clazz );
}

private Unmarshal(Unmarshaller unmarshaller, Source source, Class<T> clazz) {
private Unmarshal(Unmarshaller unmarshaller, XMLEventReader xmlEventReader, Class<T> clazz) {
this.unmarshaller = unmarshaller;
this.source = source;
this.xmlEventReader = xmlEventReader;
this.clazz = clazz;
}

@Override
public JAXBElement<T> run() throws JAXBException {
return unmarshaller.unmarshal( source, clazz );
return unmarshaller.unmarshal( xmlEventReader, clazz );
}
}
Expand Up @@ -22,7 +22,7 @@
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import javax.xml.stream.XMLEventReader;
import javax.xml.validation.Schema;

import org.hibernate.validator.internal.util.logging.Log;
Expand Down Expand Up @@ -74,10 +74,11 @@ public final BootstrapConfiguration parseValidationXml() {
// this avoids accessing javax.xml.stream.* (which does not exist on Android) when not actually
// working with the XML configuration
XmlParserHelper xmlParserHelper = new XmlParserHelper();
XMLEventReader xmlEventReader = xmlParserHelper.createXmlEventReader( VALIDATION_XML_FILE, inputStream );

String schemaVersion = xmlParserHelper.getSchemaVersion( VALIDATION_XML_FILE, inputStream );
String schemaVersion = xmlParserHelper.getSchemaVersion( VALIDATION_XML_FILE, xmlEventReader );
Schema schema = getSchema( xmlParserHelper, schemaVersion );
ValidationConfigType validationConfig = unmarshal( inputStream, schema );
ValidationConfigType validationConfig = unmarshal( xmlEventReader, schema );

return createBootstrapConfiguration( validationConfig );
}
Expand Down Expand Up @@ -109,7 +110,7 @@ private Schema getSchema(XmlParserHelper xmlParserHelper, String schemaVersion)
return xmlParserHelper.getSchema( schemaResource );
}

private ValidationConfigType unmarshal(InputStream inputStream, Schema schema) {
private ValidationConfigType unmarshal(XMLEventReader xmlEventReader, Schema schema) {
log.parsingXMLFile( VALIDATION_XML_FILE );

try {
Expand All @@ -118,11 +119,10 @@ private ValidationConfigType unmarshal(InputStream inputStream, Schema schema) {
JAXBContext jc = run( NewJaxbContext.action( ValidationConfigType.class ) );
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setSchema( schema );
StreamSource stream = new StreamSource( inputStream );

// Unmashaller#unmarshal() requires several permissions internally and doesn't use any privileged blocks
// itself; Wrapping it here avoids that all calling code bases need to have these permissions as well
JAXBElement<ValidationConfigType> root = run( Unmarshal.action( unmarshaller, stream, ValidationConfigType.class ) );
JAXBElement<ValidationConfigType> root = run( Unmarshal.action( unmarshaller, xmlEventReader, ValidationConfigType.class ) );
return root.getValue();
}
catch ( Exception e ) {
Expand Down
Expand Up @@ -6,12 +6,10 @@
*/
package org.hibernate.validator.internal.xml;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.util.Collections;
import java.util.List;
Expand All @@ -25,7 +23,7 @@
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import javax.xml.stream.XMLEventReader;
import javax.xml.validation.Schema;

import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions;
Expand Down Expand Up @@ -131,14 +129,21 @@ public final void parse(Set<InputStream> mappingStreams) {

Set<String> alreadyProcessedConstraintDefinitions = newHashSet();
for ( InputStream in : mappingStreams ) {
String schemaVersion = xmlParserHelper.getSchemaVersion( "constraint mapping file", in );
// 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 );
}

XMLEventReader xmlEventReader = xmlParserHelper.createXmlEventReader( "constraint mapping file", in );
String schemaVersion = xmlParserHelper.getSchemaVersion( "constraint mapping file", xmlEventReader );
String schemaResourceName = getSchemaResourceName( schemaVersion );
Schema schema = xmlParserHelper.getSchema( schemaResourceName );

Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setSchema( schema );

ConstraintMappingsType mapping = getValidationConfig( in, unmarshaller );
ConstraintMappingsType mapping = getValidationConfig( xmlEventReader, unmarshaller );
String defaultPackage = mapping.getDefaultPackage();

parseConstraintDefinitions(
Expand All @@ -148,54 +153,23 @@ public final void parse(Set<InputStream> mappingStreams) {
);

for ( BeanType bean : mapping.getBean() ) {
Class<?> beanClass = classLoadingHelper.loadClass( bean.getClazz(), defaultPackage );
checkClassHasNotBeenProcessed( processedClasses, beanClass );

// update annotation ignores
annotationProcessingOptions.ignoreAnnotationConstraintForClass(
beanClass,
bean.getIgnoreAnnotations()
processBeanType(
constrainedTypeBuilder,
constrainedFieldBuilder,
constrainedExecutableBuilder,
constrainedGetterBuilder,
defaultPackage,
bean
);
}

ConstrainedType constrainedType = constrainedTypeBuilder.buildConstrainedType(
bean.getClassType(),
beanClass,
defaultPackage
);
if ( constrainedType != null ) {
addConstrainedElement( beanClass, constrainedType );
if ( markSupported ) {
try {
in.reset();
}
catch ( IOException e ) {
log.debug( "Unable to reset input stream." );
}

Set<ConstrainedField> constrainedFields = constrainedFieldBuilder.buildConstrainedFields(
bean.getField(),
beanClass,
defaultPackage
);
addConstrainedElements( beanClass, constrainedFields );

Set<ConstrainedExecutable> constrainedGetters = constrainedGetterBuilder.buildConstrainedGetters(
bean.getGetter(),
beanClass,
defaultPackage

);
addConstrainedElements( beanClass, constrainedGetters );

Set<ConstrainedExecutable> constrainedConstructors = constrainedExecutableBuilder.buildConstructorConstrainedExecutable(
bean.getConstructor(),
beanClass,
defaultPackage
);
addConstrainedElements( beanClass, constrainedConstructors );

Set<ConstrainedExecutable> constrainedMethods = constrainedExecutableBuilder.buildMethodConstrainedExecutable(
bean.getMethod(),
beanClass,
defaultPackage
);
addConstrainedElements( beanClass, constrainedMethods );

processedClasses.add( beanClass );
}
}
}
Expand Down Expand Up @@ -225,6 +199,57 @@ public final List<Class<?>> getDefaultSequenceForClass(Class<?> beanClass) {
return defaultSequences.get( beanClass );
}

private void processBeanType(ConstrainedTypeBuilder constrainedTypeBuilder, ConstrainedFieldBuilder constrainedFieldBuilder, ConstrainedExecutableBuilder constrainedExecutableBuilder, ConstrainedGetterBuilder constrainedGetterBuilder, String defaultPackage, BeanType bean) {
Class<?> beanClass = classLoadingHelper.loadClass( bean.getClazz(), defaultPackage );
checkClassHasNotBeenProcessed( processedClasses, beanClass );

// update annotation ignores
annotationProcessingOptions.ignoreAnnotationConstraintForClass(
beanClass,
bean.getIgnoreAnnotations()
);

ConstrainedType constrainedType = constrainedTypeBuilder.buildConstrainedType(
bean.getClassType(),
beanClass,
defaultPackage
);
if ( constrainedType != null ) {
addConstrainedElement( beanClass, constrainedType );
}

Set<ConstrainedField> constrainedFields = constrainedFieldBuilder.buildConstrainedFields(
bean.getField(),
beanClass,
defaultPackage
);
addConstrainedElements( beanClass, constrainedFields );

Set<ConstrainedExecutable> constrainedGetters = constrainedGetterBuilder.buildConstrainedGetters(
bean.getGetter(),
beanClass,
defaultPackage

);
addConstrainedElements( beanClass, constrainedGetters );

Set<ConstrainedExecutable> constrainedConstructors = constrainedExecutableBuilder.buildConstructorConstrainedExecutable(
bean.getConstructor(),
beanClass,
defaultPackage
);
addConstrainedElements( beanClass, constrainedConstructors );

Set<ConstrainedExecutable> constrainedMethods = constrainedExecutableBuilder.buildMethodConstrainedExecutable(
bean.getMethod(),
beanClass,
defaultPackage
);
addConstrainedElements( beanClass, constrainedMethods );

processedClasses.add( beanClass );
}

@SuppressWarnings("unchecked")
private void parseConstraintDefinitions(List<ConstraintDefinitionType> constraintDefinitionList,
String defaultPackage,
Expand Down Expand Up @@ -312,36 +337,19 @@ private void addConstrainedElements(Class<?> beanClass, Set<? extends Constraine
}
}

private ConstraintMappingsType getValidationConfig(InputStream in, Unmarshaller unmarshaller) {
private ConstraintMappingsType getValidationConfig(XMLEventReader xmlEventReader, Unmarshaller unmarshaller) {
ConstraintMappingsType constraintMappings;
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 );
}

StreamSource stream = new StreamSource( new CloseIgnoringInputStream( in ) );

// Unmashaller#unmarshal() requires several permissions internally and doesn't use any privileged blocks
// itself; Wrapping it here avoids that all calling code bases need to have these permissions as well
JAXBElement<ConstraintMappingsType> root = run(
Unmarshal.action(
unmarshaller,
stream,
xmlEventReader,
ConstraintMappingsType.class
)
);
constraintMappings = root.getValue();

if ( markSupported ) {
try {
in.reset();
}
catch ( IOException e ) {
log.debug( "Unable to reset input stream." );
}
}
}
catch ( Exception e ) {
throw log.getErrorParsingMappingFileException( e );
Expand All @@ -365,10 +373,6 @@ private String getSchemaResourceName(String schemaVersion) {
* <b>NOTE:</b> This must never be changed into a publicly available method to avoid execution of arbitrary
* privileged actions within HV's protection domain.
*/
private <T> T run(PrivilegedAction<T> action) {
return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run();
}

private <T> T run(PrivilegedExceptionAction<T> action) throws JAXBException {
try {
return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run();
Expand All @@ -380,16 +384,4 @@ private <T> T run(PrivilegedExceptionAction<T> action) throws JAXBException {
throw log.getErrorParsingMappingFileException( e );
}
}

// JAXB closes the underlying input stream
private static class CloseIgnoringInputStream extends FilterInputStream {
public CloseIgnoringInputStream(InputStream in) {
super( in );
}

@Override
public void close() {
// do nothing
}
}
}

0 comments on commit 8d73c0e

Please sign in to comment.