All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.bval.jsr.xml.ValidationParser Maven / Gradle / Ivy

There is a newer version: 10.0.0-M3
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.bval.jsr.xml;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.validation.ConstraintValidatorFactory;
import javax.validation.MessageInterpolator;
import javax.validation.ParameterNameProvider;
import javax.validation.TraversableResolver;
import javax.validation.ValidationException;
import javax.validation.executable.ExecutableType;
import javax.validation.spi.ValidationProvider;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.apache.bval.jsr.BootstrapConfigurationImpl;
import org.apache.bval.jsr.ConfigurationImpl;
import org.apache.bval.jsr.util.IOs;
import org.apache.bval.util.reflection.Reflection;
import org.apache.commons.weaver.privilizer.Privileged;
import org.apache.commons.weaver.privilizer.Privilizing;
import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
import org.xml.sax.SAXException;

/**
 * Description: uses jaxb to parse validation.xml
*/ @Privilizing(@CallTo(Reflection.class)) public class ValidationParser { private static final String DEFAULT_VALIDATION_XML_FILE = "META-INF/validation.xml"; private static final String VALIDATION_CONFIGURATION_XSD = "META-INF/validation-configuration-1.1.xsd"; private static final Logger log = Logger.getLogger(ValidationParser.class.getName()); private static final ConcurrentMap SCHEMA_CACHE = new ConcurrentHashMap(1); private ValidationConfigType xmlConfig; private BootstrapConfigurationImpl bootstrap; private Collection exceptions = new CopyOnWriteArrayList(); private ValidationParser() { // no-op } public void applyConfigWithInstantiation(ConfigurationImpl targetConfig) { if (xmlConfig == null) { return; } applyProviderClass(xmlConfig, targetConfig); applyMessageInterpolator(xmlConfig, targetConfig); applyTraversableResolver(xmlConfig, targetConfig); applyConstraintFactory(xmlConfig, targetConfig); applyParameterNameProvider(xmlConfig, targetConfig); applyMappingStreams(xmlConfig, targetConfig); applyProperties(xmlConfig, targetConfig); } public BootstrapConfigurationImpl getBootstrap() { return bootstrap; } public static String getValidationXmlFile(String file) { if (file == null) { return DEFAULT_VALIDATION_XML_FILE; } return file; } public static ValidationParser processValidationConfig(final String file, final ConfigurationImpl targetConfig, final boolean ignoreXml) { final ValidationParser parser = new ValidationParser(); if (!ignoreXml) { parser.xmlConfig = parseXmlConfig(file); } if (parser.xmlConfig != null) { if (parser.xmlConfig.getExecutableValidation() == null) { final ExecutableValidationType value = new ExecutableValidationType(); value.setEnabled(true); final DefaultValidatedExecutableTypesType defaultValidatedExecutableTypes = new DefaultValidatedExecutableTypesType(); value.setDefaultValidatedExecutableTypes(defaultValidatedExecutableTypes); defaultValidatedExecutableTypes.getExecutableType().add(ExecutableType.CONSTRUCTORS); defaultValidatedExecutableTypes.getExecutableType().add(ExecutableType.NON_GETTER_METHODS); parser.xmlConfig.setExecutableValidation(value); } applySimpleConfig(parser.xmlConfig, targetConfig); parser.bootstrap = new BootstrapConfigurationImpl( parser.xmlConfig.getDefaultProvider(), parser.xmlConfig.getConstraintValidatorFactory(), parser.xmlConfig.getMessageInterpolator(), parser.xmlConfig.getTraversableResolver(), parser.xmlConfig.getParameterNameProvider(), new CopyOnWriteArraySet(parser.xmlConfig.getConstraintMapping()), parser.xmlConfig.getExecutableValidation().getEnabled(), new CopyOnWriteArraySet(targetConfig.getExecutableValidation()), toMap(parser.xmlConfig.getProperty())); } else { // default config final CopyOnWriteArraySet executableTypes = new CopyOnWriteArraySet(); executableTypes.add(ExecutableType.CONSTRUCTORS); executableTypes.add(ExecutableType.NON_GETTER_METHODS); parser.bootstrap = new BootstrapConfigurationImpl( null, null, null, null, null, new CopyOnWriteArraySet(), true, executableTypes, new HashMap()); targetConfig.setExecutableValidation(executableTypes); } return parser; } private static Map toMap(final List property) { final Map map = new HashMap(); if (property != null) { for (final PropertyType p : property) { map.put(p.getName(), p.getValue()); } } return map; } @Privileged private static ValidationConfigType parseXmlConfig(final String validationXmlFile) { InputStream inputStream = null; try { inputStream = getInputStream(getValidationXmlFile(validationXmlFile)); if (inputStream == null) { log.log(Level.FINEST, String.format("No %s found. Using annotation based configuration only.", validationXmlFile)); return null; } log.log(Level.FINEST, String.format("%s found.", validationXmlFile)); Schema schema = getSchema(); JAXBContext jc = JAXBContext.newInstance(ValidationConfigType.class); Unmarshaller unmarshaller = jc.createUnmarshaller(); unmarshaller.setSchema(schema); StreamSource stream = new StreamSource(inputStream); JAXBElement root = unmarshaller.unmarshal(stream, ValidationConfigType.class); return root.getValue(); } catch (JAXBException e) { throw new ValidationException("Unable to parse " + validationXmlFile, e); } catch (IOException e) { throw new ValidationException("Unable to parse " + validationXmlFile, e); } finally { IOs.closeQuietly(inputStream); } } protected static InputStream getInputStream(final String path) throws IOException { final ClassLoader loader = Reflection.getClassLoader(ValidationParser.class); final InputStream inputStream = loader.getResourceAsStream(path); if (inputStream != null) { // spec says: If more than one META-INF/validation.xml file // is found in the classpath, a ValidationException is raised. final Enumeration urls = loader.getResources(path); if (urls.hasMoreElements()) { final String url = urls.nextElement().toString(); while (urls.hasMoreElements()) { if (!url.equals(urls.nextElement().toString())) { // complain when first duplicate found throw new ValidationException("More than one " + path + " is found in the classpath"); } } } } return IOs.convertToMarkableInputStream(inputStream); } private static Schema getSchema() { return getSchema(VALIDATION_CONFIGURATION_XSD); } static Schema getSchema(final String xsd) { final Schema schema = SCHEMA_CACHE.get(xsd); if (schema != null) { return schema; } final ClassLoader loader = Reflection.getClassLoader(ValidationParser.class); final SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); final URL schemaUrl = loader.getResource(xsd); try { Schema s = sf.newSchema(schemaUrl); final Schema old = SCHEMA_CACHE.putIfAbsent(xsd, s); if (old != null) { s = old; } return s; } catch (SAXException e) { log.log(Level.WARNING, String.format("Unable to parse schema: %s", xsd), e); return null; } } public static void applySimpleConfig(ValidationConfigType xmlConfig, ConfigurationImpl targetConfig) { applyExecutableValidation(xmlConfig, targetConfig); } private static void applyProperties(ValidationConfigType xmlConfig, ConfigurationImpl target) { for (final PropertyType property : xmlConfig.getProperty()) { target.addProperty(property.getName(), property.getValue()); } } private static void applyExecutableValidation(final ValidationConfigType xmlConfig, final ConfigurationImpl targetConfig) { final CopyOnWriteArrayList executableTypes = new CopyOnWriteArrayList(); if (xmlConfig.getExecutableValidation() != null && xmlConfig.getExecutableValidation().getEnabled() && xmlConfig.getExecutableValidation().getDefaultValidatedExecutableTypes() != null) { executableTypes.addAll(xmlConfig.getExecutableValidation().getDefaultValidatedExecutableTypes().getExecutableType()); } if (executableTypes.contains(ExecutableType.ALL)) { executableTypes.clear(); executableTypes.add(ExecutableType.CONSTRUCTORS); executableTypes.add(ExecutableType.NON_GETTER_METHODS); executableTypes.add(ExecutableType.GETTER_METHODS); } else if (executableTypes.contains(ExecutableType.NONE)) { // if both are present ALL gains executableTypes.clear(); } targetConfig.setExecutableValidation(executableTypes); } private void applyParameterNameProvider(final ValidationConfigType xmlConfig, final ConfigurationImpl targetConfig) { final String parameterNameProvider = xmlConfig.getParameterNameProvider(); if (targetConfig.getParameterNameProvider() == targetConfig.getDefaultParameterNameProvider()) { // ref == if (parameterNameProvider != null) { final Class loaded = loadClass(parameterNameProvider); if (loaded == null) { log.log(Level.SEVERE, "Can't load " + parameterNameProvider); } else { final Class clazz = loaded.asSubclass(ParameterNameProvider.class); targetConfig.parameterNameProviderClass(clazz); log.log(Level.INFO, String.format("Using %s as validation provider.", parameterNameProvider)); } } } } @SuppressWarnings("unchecked") private void applyProviderClass(ValidationConfigType xmlConfig, ConfigurationImpl target) { String providerClassName = xmlConfig.getDefaultProvider(); if (providerClassName != null) { Class> clazz = (Class>) loadClass(providerClassName); target.setProviderClass(clazz); log.log(Level.INFO, String.format("Using %s as validation provider.", providerClassName)); } } @SuppressWarnings("unchecked") private void applyMessageInterpolator(ValidationConfigType xmlConfig, ConfigurationImpl target) { String messageInterpolatorClass = xmlConfig.getMessageInterpolator(); if (target.getMessageInterpolator() == target.getDefaultMessageInterpolator()) { // ref == if (messageInterpolatorClass != null) { Class clazz = (Class) loadClass(messageInterpolatorClass); target.messageInterpolatorClass(clazz); log.log(Level.INFO, String.format("Using %s as message interpolator.", messageInterpolatorClass)); } } } @SuppressWarnings("unchecked") private void applyTraversableResolver(ValidationConfigType xmlConfig, ConfigurationImpl target) { String traversableResolverClass = xmlConfig.getTraversableResolver(); if (target.getTraversableResolver() == target.getDefaultTraversableResolver() && traversableResolverClass != null) { Class clazz = (Class) loadClass(traversableResolverClass); target.traversableResolverClass(clazz); log.log(Level.INFO, String.format("Using %s as traversable resolver.", traversableResolverClass)); } } @SuppressWarnings("unchecked") private void applyConstraintFactory(ValidationConfigType xmlConfig, ConfigurationImpl target) { String constraintFactoryClass = xmlConfig.getConstraintValidatorFactory(); if (target.getConstraintValidatorFactory() == target.getDefaultConstraintValidatorFactory() && constraintFactoryClass != null) { Class clazz = (Class) loadClass(constraintFactoryClass); target.constraintValidatorFactoryClass(clazz); log.log(Level.INFO, String.format("Using %s as constraint factory.", constraintFactoryClass)); } } private static void applyMappingStreams(ValidationConfigType xmlConfig, ConfigurationImpl target) { for (String rawMappingFileName : xmlConfig.getConstraintMapping()) { String mappingFileName = rawMappingFileName; if (mappingFileName.startsWith("/")) { // Classloader needs a path without a starting / mappingFileName = mappingFileName.substring(1); } log.log(Level.FINEST, String.format("Trying to open input stream for %s", mappingFileName)); InputStream in; try { in = getInputStream(mappingFileName); if (in == null) { throw new ValidationException( "Unable to open input stream for mapping file " + mappingFileName); } } catch (IOException e) { throw new ValidationException("Unable to open input stream for mapping file " + mappingFileName, e); } target.addMapping(in); } } private Class loadClass(final String className) { final ClassLoader loader = Reflection.getClassLoader(ValidationParser.class); try { return Class.forName(className, true, loader); } catch (final ClassNotFoundException ex) { // TCK check BootstrapConfig is present in all cases // so throw next exception later exceptions.add(new ValidationException("Unable to load class: " + className, ex)); return null; } } public void ensureValidatorFactoryCanBeBuilt() { if (!exceptions.isEmpty()) { throw exceptions.iterator().next(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy