/*
 * Hibernate Validator, declare and validate application constraints
 *
 * License: Apache License, Version 2.0
 * See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
 */
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
/** * 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.
/** * 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<String, Schema> schemaCache = new ConcurrentHashMap<String, Schema>( 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.

Params:
  • resourceName – The name of the represented XML resource.
  • xmlEventReader – An STAX event reader
Returns: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.
/** * Retrieves the schema version applying for the given XML input stream as * represented by the "version" attribute of the root element of the stream. * <p> * 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.
Params:
  • schemaResource – the resource name identifying the schema.
Returns:the schema identified by the given resource name or null if the resource was not found or could not be loaded.
/** * 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.

/** * Runs the given privileged action, using a privileged block if required. * <p> * <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 Exception { return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); } }