org.hibernate.validator.internal.xml.XmlParserHelper Maven / Gradle / Ivy
Show all versions of hibernate-validator Show documentation
/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or .
*/
package org.hibernate.validator.internal.xml;
import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES;
import java.io.InputStream;
import java.lang.invoke.MethodHandles;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.hibernate.validator.internal.util.Contracts;
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;
import org.hibernate.validator.internal.util.privilegedactions.GetClassLoader;
import org.hibernate.validator.internal.util.privilegedactions.GetResource;
import org.hibernate.validator.internal.util.privilegedactions.NewSchema;
/**
* Provides common functionality used within the different XML descriptor
* parsers.
*
* @author Gunnar Morling
*/
public class XmlParserHelper {
private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
/**
* The expected number of XML schemas managed by this class. Used to set the
* initial cache size.
*/
private static final int NUMBER_OF_SCHEMAS = 4;
private static final String DEFAULT_VERSION = "1.0";
private static final QName VERSION_QNAME = new QName( "version" );
// xmlInputFactory used to be static in order to cache the factory, but that introduced a leakage of
// class loader in WildFly. See HV-842
private final XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
private static final ConcurrentMap schemaCache = new ConcurrentHashMap(
NUMBER_OF_SCHEMAS
);
/**
* Retrieves the schema version applying for the given XML input stream as
* represented by the "version" attribute of the root element of the stream.
*
* The given reader will be advanced to the root element of the given XML
* structure. It can be used for unmarshalling from there.
*
* @param resourceName The name of the represented XML resource.
* @param xmlEventReader An STAX event reader
*
* @return The value of the "version" attribute. For compatibility with BV
* 1.0, "1.0" will be returned if the given stream doesn't have a
* "version" attribute.
*/
public String getSchemaVersion(String resourceName, XMLEventReader xmlEventReader) {
Contracts.assertNotNull( xmlEventReader, MESSAGES.parameterMustNotBeNull( "xmlEventReader" ) );
try {
StartElement rootElement = getRootElement( xmlEventReader );
return getVersionValue( rootElement );
}
catch (XMLStreamException e) {
throw LOG.getUnableToDetermineSchemaVersionException( resourceName, e );
}
}
public synchronized XMLEventReader createXmlEventReader(String resourceName, InputStream xmlStream) {
try {
return xmlInputFactory.createXMLEventReader( xmlStream );
}
catch (Exception e) {
throw LOG.getUnableToCreateXMLEventReader( resourceName, e );
}
}
private String getVersionValue(StartElement startElement) {
if ( startElement == null ) {
return null;
}
Attribute versionAttribute = startElement.getAttributeByName( VERSION_QNAME );
return versionAttribute != null ? versionAttribute.getValue() : DEFAULT_VERSION;
}
private StartElement getRootElement(XMLEventReader xmlEventReader) throws XMLStreamException {
XMLEvent event = xmlEventReader.peek();
while ( event != null && !event.isStartElement() ) {
xmlEventReader.nextEvent();
event = xmlEventReader.peek();
}
return event == null ? null : event.asStartElement();
}
/**
* Returns the XML schema identified by the given resource name.
*
* @param schemaResource
* the resource name identifying the schema.
* @return the schema identified by the given resource name or {@code null} if the resource was not found or could
* not be loaded.
*/
public Schema getSchema(String schemaResource) {
Schema schema = schemaCache.get( schemaResource );
if ( schema != null ) {
return schema;
}
schema = loadSchema( schemaResource );
if ( schema != null ) {
Schema previous = schemaCache.putIfAbsent( schemaResource, schema );
return previous != null ? previous : schema;
}
else {
return null;
}
}
private Schema loadSchema(String schemaResource) {
ClassLoader loader = run( GetClassLoader.fromClass( XmlParserHelper.class ) );
URL schemaUrl = run( GetResource.action( loader, schemaResource ) );
SchemaFactory sf = SchemaFactory.newInstance( javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI );
Schema schema = null;
try {
schema = run( NewSchema.action( sf, schemaUrl ) );
}
catch (Exception e) {
LOG.unableToCreateSchema( schemaResource, e.getMessage() );
}
return schema;
}
/**
* Runs the given privileged action, using a privileged block if required.
*
* NOTE: This must never be changed into a publicly available method to avoid execution of arbitrary
* privileged actions within HV's protection domain.
*/
private T run(PrivilegedAction action) {
return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run();
}
private T run(PrivilegedExceptionAction action) throws Exception {
return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run();
}
}