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

org.eclipse.persistence.jaxb.ValidationXMLReader Maven / Gradle / Ivy

There is a newer version: 5.0.0-B03
Show newest version
/*
 * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//      Marcel Valovy - initial API and implementation
//      Dmitry Kornilov - BeanValidationHelper refactoring
//      Miroslav Kos - BeanValidationHelper refactoring
package org.eclipse.persistence.jaxb;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.eclipse.persistence.exceptions.BeanValidationException;
import org.eclipse.persistence.internal.helper.XMLHelper;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * Detects external Bean Validation configuration.
 * 

* Strategy:
* 1. Parse validation.xml, looking for a constraints-file reference.
* 2. For each reference, if file is found, parses the constraints file and puts all classes declared under * {@literal } into {@code constraintsOnClasses} field * of {@link org.eclipse.persistence.jaxb.BeanValidationHelper} class * with {@link Boolean#TRUE} value. *

* This class contains resources-burdening instance fields (e.g. SAXParser) and as such was designed to be instantiated * once (make the instance BOUNDED) and have {@link #call()} method called on that instance once. *

* Not suitable for singleton (memory burden). The method #parse() will be invoked only once per class load of this * class. After that the instance and all its fields should be made collectible by GC. * * @author Marcel Valovy * @author Dmitry Kornilov * @author Miroslav Kos * @since 2.6 */ public class ValidationXMLReader implements Callable, Boolean>> { public static final String DEFAULT_PACKAGE_QNAME = "default-package"; public static final String BEAN_QNAME = "bean"; public static final String CONSTRAINT_MAPPING_QNAME = "constraint-mapping"; public static final String CLASS_QNAME = "class"; public static final String PACKAGE_SEPARATOR = "."; private static final String VALIDATION_XML = "META-INF/validation.xml"; private static final Logger LOGGER = Logger.getLogger(ValidationXMLReader.class.getName()); private final List constraintsFiles = new ArrayList<>(2); private final Map, Boolean> constraintsOnClasses = new HashMap<>(); // Created lazily private SAXParser saxParser; /** * Parses validation.xml. * @return returns a map with classes found in validation.xml as keys and true as a value. Never returns null. */ @Override public Map, Boolean> call() throws Exception { parseValidationXML(VALIDATION_XML, validationHandler); if (!constraintsFiles.isEmpty()) { parseConstraintFiles(); } return constraintsOnClasses; } /** * Checks if validation.xml exists. */ public static boolean isValidationXmlPresent() { return getThreadContextClassLoader().getResource(VALIDATION_XML) != null; } private void parseConstraintFiles() { final class ConstrainedClassesDetector extends DefaultHandler { private boolean defaultPackageElement = false; private String defaultPackage = ""; @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (DEFAULT_PACKAGE_QNAME.equalsIgnoreCase(qName)) { defaultPackageElement = true; } else if (BEAN_QNAME.equalsIgnoreCase(qName)) { String className = defaultPackage + PACKAGE_SEPARATOR + attributes.getValue(CLASS_QNAME); if (LOGGER.isLoggable(Level.INFO)) { String msg = "Detected external constraints on class " + className; LOGGER.info(msg); } try { Class clazz = ReflectionUtils.forName(className); constraintsOnClasses.put(clazz, Boolean.TRUE); } catch (ClassNotFoundException e) { String errMsg = "Loading found class failed. Exception: " + e.getMessage(); LOGGER.warning(errMsg); } } } @Override public void characters(char[] ch, int start, int length) throws SAXException { if (defaultPackageElement) { defaultPackage = new String(ch, start, length); defaultPackageElement = false; } } } // Parse constraints file referenced in validation.xml. Add all classes declared under to // org.eclipse.persistence.jaxb.BeanValidationHelper#constraintsOnClasses with value Boolean#TRUE. for (String file : constraintsFiles) { parseValidationXML(file, new ConstrainedClassesDetector()); } } /** * Lazy getter for SAX parser. */ private SAXParser getSaxParser() { if (saxParser == null) { try { SAXParserFactory factory = XMLHelper.createParserFactory(false); saxParser = factory.newSAXParser(); } catch (ParserConfigurationException | SAXException e) { String msg = "ValidationXMLReader initialization failed. Exception: " + e.getMessage(); LOGGER.severe(msg); throw new BeanValidationException(msg, e); } } return saxParser; } private void parseValidationXML(String constraintsFilePath, DefaultHandler handler) { try (InputStream validationXml = getThreadContextClassLoader().getResourceAsStream(constraintsFilePath)) { if (validationXml != null) { getSaxParser().parse(validationXml, handler); } } catch (SAXException | IOException ex) { LOGGER.log(Level.WARNING, "Parsing of validation.xml failed.", ex); } } private static ClassLoader getThreadContextClassLoader() { return PrivilegedAccessHelper.callDoPrivileged( () -> Thread.currentThread().getContextClassLoader() ); } private final DefaultHandler validationHandler = new DefaultHandler() { private boolean constraintsFileElement = false; @Override public void startElement(String uri, String localName, String qName, Attributes attributes) { if (CONSTRAINT_MAPPING_QNAME.equalsIgnoreCase(qName)) { constraintsFileElement = true; } } @Override public void characters(char[] ch, int start, int length) { if (constraintsFileElement) { constraintsFiles.add(new String(ch, start, length)); constraintsFileElement = false; } } }; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy