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

com.impetus.kundera.loader.PersistenceXMLLoader Maven / Gradle / Ivy

There is a newer version: 2.9
Show newest version
/*******************************************************************************
 * * Copyright 2012 Impetus Infotech.
 *  *
 *  * Licensed 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 com.impetus.kundera.loader;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.PersistenceException;
import javax.persistence.spi.PersistenceUnitTransactionType;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import com.impetus.kundera.metadata.model.PersistenceUnitMetadata;
import com.impetus.kundera.utils.InvalidConfigurationException;

/**
 * The Class PersistenceXMLLoader.
 * 
 * @author amresh.singh
 */
public class PersistenceXMLLoader
{
    /** The log. */
    private static Logger log = LoggerFactory.getLogger(PersistenceXMLLoader.class);

    /**
     * Instantiates a new persistence xml loader.
     */
    private PersistenceXMLLoader()
    {
    }

    /**
     * Gets the document.
     * 
     * @param configURL
     *            the config url
     * @return the document
     * @throws Exception
     *             the exception
     */
    private static Document getDocument(URL configURL) throws InvalidConfigurationException
    {
        InputStream is = null;
        Document doc;
        try
        {
            is = null;
            if (configURL != null)
            {
                URLConnection conn = configURL.openConnection();
                conn.setUseCaches(false); // avoid JAR locking on Windows and
                                          // Tomcat
                is = conn.getInputStream();
            }
            if (is == null)
            {
                throw new IOException("Failed to obtain InputStream from url: " + configURL);
            }

            DocumentBuilderFactory docBuilderFactory = null;
            docBuilderFactory = DocumentBuilderFactory.newInstance();
            docBuilderFactory.setNamespaceAware(true);

            final Schema v2Schema = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(
                    new StreamSource(getStreamFromClasspath("persistence_2_0.xsd")));
            final Validator v2Validator = v2Schema.newValidator();
            final Schema v1Schema = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(
                    new StreamSource(getStreamFromClasspath("persistence_1_0.xsd")));
            final Validator v1Validator = v1Schema.newValidator();

            InputSource source = new InputSource(is);
            DocumentBuilder docBuilder = null;
            try
            {
                docBuilder = docBuilderFactory.newDocumentBuilder();
            }
            catch (ParserConfigurationException e)
            {
                log.error("Error during parsing, Caused by: " + e.getMessage());
                throw new PersistenceLoaderException(e);
            }

            List errors = new ArrayList();
            docBuilder.setErrorHandler(new ErrorLogger("XML InputStream", errors));
            doc = docBuilder.parse(source);

            if (errors.size() == 0)
            {
                v2Validator.setErrorHandler(new ErrorLogger("XML InputStream", errors));
                v2Validator.validate(new DOMSource(doc));
                boolean isV1Schema = false;
                if (errors.size() != 0)
                {
                    // v2 fails, it could be because the file is v1.
                    Exception exception = (Exception) errors.get(0);
                    final String errorMessage = exception.getMessage();
                    // is it a validation error due to a v1 schema validated by
                    // a v2
                    isV1Schema = errorMessage.contains("1.0") && errorMessage.contains("2.0")
                            && errorMessage.contains("version");
                }
                if (isV1Schema)
                {
                    errors.clear();
                    v1Validator.setErrorHandler(new ErrorLogger("XML InputStream", errors));
                    v1Validator.validate(new DOMSource(doc));
                }
            }
            else
            {
                throw new InvalidConfigurationException("invalid persistence.xml", (Throwable) errors.get(0));
            }
        }
        catch (IOException e)
        {
            throw new InvalidConfigurationException(e);
        }
        catch (SAXException e)
        {
            throw new InvalidConfigurationException(e);
        }
        finally
        {
            try
            {
                is.close();
            }
            catch (IOException e)
            {
                throw new InvalidConfigurationException(e);
            }
        }

        return doc;
    }

    /**
     * Get stream from classpath.
     * 
     * @param fileName
     *            the file name
     * @return the stream
     * @throws Exception
     *             the exception
     */
    private static InputStream getStreamFromClasspath(String fileName)
    {
        String path = fileName;
        InputStream dtdStream = PersistenceXMLLoader.class.getClassLoader().getResourceAsStream(path);
        return dtdStream;
    }

    /**
     * Find persistence units.
     * 
     * @param url
     *            the url
     * @return the list
     * @throws Exception
     *             the exception
     */
    public static List findPersistenceUnits(URL url) throws Exception
    {
        return findPersistenceUnits(url, PersistenceUnitTransactionType.JTA);
    }

    /**
     * Find persistence units.
     * 
     * @param url
     *            the url
     * @param defaultTransactionType
     *            the default transaction type
     * @return the list
     * @throws Exception
     *             the exception
     */
    public static List findPersistenceUnits(final URL url,
            PersistenceUnitTransactionType defaultTransactionType) throws InvalidConfigurationException
    {

        Document doc;
        try
        {
            doc = getDocument(url);
        }
        catch (InvalidConfigurationException e)
        {
            throw e;
        }
        doc.getXmlVersion();
        Element top = doc.getDocumentElement();

        String versionName = top.getAttribute("version");

        NodeList children = top.getChildNodes();
        ArrayList units = new ArrayList();

        // parse for persistenceUnitRootInfoURL.
        for (int i = 0; i < children.getLength(); i++)
        {
            if (children.item(i).getNodeType() == Node.ELEMENT_NODE)
            {
                Element element = (Element) children.item(i);
                String tag = element.getTagName();
                // look for "persistence-unit" element
                if (tag.equals("persistence-unit"))
                {
                    PersistenceUnitMetadata metadata = parsePersistenceUnit(url, element, versionName);
                    // metadata.setPersistenceUnitRootUrl(getPersistenceRootUrl(url));
                    units.add(metadata);
                }
            }
        }
        return units;
    }

    /**
     * Parses the persistence unit.
     * 
     * @param top
     *            the top
     * @return the persistence metadata
     * @throws Exception
     *             the exception
     */
    private static PersistenceUnitMetadata parsePersistenceUnit(final URL url, Element top, final String versionName)
    {
        PersistenceUnitMetadata metadata = new PersistenceUnitMetadata(versionName, getPersistenceRootUrl(url));

        String puName = top.getAttribute("name");
        if (!isEmpty(puName))
        {
            log.trace("Persistent Unit name from persistence.xml: " + puName);
            metadata.setPersistenceUnitName(puName);
            String transactionType = top.getAttribute("transaction-type");
            if (StringUtils.isEmpty(transactionType)
                    || PersistenceUnitTransactionType.RESOURCE_LOCAL.name().equals(transactionType))
            {

                metadata.setTransactionType(PersistenceUnitTransactionType.RESOURCE_LOCAL);

            }
            else if (PersistenceUnitTransactionType.JTA.name().equals(transactionType))
            {
                metadata.setTransactionType(PersistenceUnitTransactionType.JTA);
            }
        }

        NodeList children = top.getChildNodes();
        for (int i = 0; i < children.getLength(); i++)
        {
            if (children.item(i).getNodeType() == Node.ELEMENT_NODE)
            {
                Element element = (Element) children.item(i);
                String tag = element.getTagName();

                if (tag.equals("provider"))
                {
                    metadata.setProvider(getElementContent(element));
                }

                /*
                 * else if (tag.equals("transaction-type")) { String
                 * transactionType = getElementContent(element);
                 * 
                 * if (StringUtils.isEmpty(transactionType) ||
                 * PersistenceUnitTransactionType
                 * .RESOURCE_LOCAL.name().equals(transactionType)) {
                 * 
                 * metadata.setTransactionType(PersistenceUnitTransactionType.
                 * RESOURCE_LOCAL);
                 * 
                 * } else if
                 * (PersistenceUnitTransactionType.JTA.name().equals(transactionType
                 * )) {
                 * metadata.setTransactionType(PersistenceUnitTransactionType
                 * .JTA); }
                 * 
                 * }
                 */else if (tag.equals("properties"))
                {
                    NodeList props = element.getChildNodes();
                    for (int j = 0; j < props.getLength(); j++)
                    {
                        if (props.item(j).getNodeType() == Node.ELEMENT_NODE)
                        {
                            Element propElement = (Element) props.item(j);
                            // if element is not "property" then skip
                            if (!"property".equals(propElement.getTagName()))
                            {
                                continue;
                            }

                            String propName = propElement.getAttribute("name").trim();
                            String propValue = propElement.getAttribute("value").trim();
                            if (isEmpty(propValue))
                            {
                                propValue = getElementContent(propElement, "");
                            }
                            metadata.getProperties().put(propName, propValue);
                        }
                    }
                }
                // Kundera doesn't support "class", "jar-file" and
                // "excluded-unlisted-classes" for now.. but will someday.
                // let's parse it for now.
                else if (tag.equals("class"))
                {
                    metadata.getClasses().add(getElementContent(element));
                }
                else if (tag.equals("jar-file"))
                {
                    metadata.addJarFile(getElementContent(element));
                }
                else if (tag.equals("exclude-unlisted-classes"))
                {
                    metadata.setExcludeUnlistedClasses(true);
                }
            }
        }
        PersistenceUnitTransactionType transactionType = getTransactionType(top.getAttribute("transaction-type"));
        if (transactionType != null)
        {
            metadata.setTransactionType(transactionType);
        }

        return metadata;
    }

    /**
     * Gets the transaction type.
     * 
     * @param elementContent
     *            the element content
     * @return the transaction type
     */
    public static PersistenceUnitTransactionType getTransactionType(String elementContent)
    {

        if (elementContent == null || elementContent.isEmpty())
        {
            return null;
        }
        else if (elementContent.equalsIgnoreCase("JTA"))
        {
            return PersistenceUnitTransactionType.JTA;
        }
        else if (elementContent.equalsIgnoreCase("RESOURCE_LOCAL"))
        {
            return PersistenceUnitTransactionType.RESOURCE_LOCAL;
        }
        else
        {
            throw new PersistenceException("Unknown TransactionType: " + elementContent);
        }
    }

    /**
     * The Class ErrorLogger.
     */
    public static class ErrorLogger implements ErrorHandler
    {

        /** The file. */
        private String file;

        /** The errors. */
        private List errors;

        /**
         * Instantiates a new error logger.
         * 
         * @param file
         *            the file
         * @param errors
         *            the errors
         */
        ErrorLogger(String file, List errors)
        {
            this.file = file;
            this.errors = errors;
        }

        /* @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException) */
        /*
         * (non-Javadoc)
         * 
         * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException)
         */
        public void error(SAXParseException error)
        {
            log.error("Error parsing XML: " + file + '(' + error.getLineNumber() + ") " + error.getMessage());
            errors.add(error);
        }

        /*
         * @see
         * org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException)
         */
        /*
         * (non-Javadoc)
         * 
         * @see
         * org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException)
         */
        public void fatalError(SAXParseException error)
        {
            log.error("Error parsing XML: " + file + '(' + error.getLineNumber() + ") " + error.getMessage());
            errors.add(error);
        }

        /* @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException) */
        /*
         * (non-Javadoc)
         * 
         * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException)
         */
        public void warning(SAXParseException warn)
        {
            log.warn("Warning parsing XML: " + file + '(' + warn.getLineNumber() + ") " + warn.getMessage());
        }
    }

    /**
     * Checks if is empty.
     * 
     * @param str
     *            the str
     * @return true, if is empty
     */
    private static boolean isEmpty(String str)
    {
        return null == str || str.isEmpty();
    }

    /**
     * Gets the element content.
     * 
     * @param element
     *            the element
     * @return the element content
     * @throws Exception
     *             the exception
     */
    public static String getElementContent(final Element element)
    {
        return getElementContent(element, null);
    }

    /**
     * Get the content of the given element.
     * 
     * @param element
     *            The element to get the content for.
     * @param defaultStr
     *            The default to return when there is no content.
     * @return The content of the element or the default.
     * @throws Exception
     *             the exception
     */
    private static String getElementContent(Element element, String defaultStr)
    {
        if (element == null)
        {
            return defaultStr;
        }

        NodeList children = element.getChildNodes();
        StringBuilder result = new StringBuilder("");
        for (int i = 0; i < children.getLength(); i++)
        {
            if (children.item(i).getNodeType() == Node.TEXT_NODE
                    || children.item(i).getNodeType() == Node.CDATA_SECTION_NODE)
            {
                result.append(children.item(i).getNodeValue());
            }
        }
        return result.toString().trim();
    }

    /**
     * Returns persistence unit root url
     * 
     * @param url
     *            raw url
     * @return rootUrl rootUrl
     */
    private static URL getPersistenceRootUrl(URL url)
    {

        String f = url.getFile();
        f = parseFilePath(f);
        URL jarUrl = url;
        try
        {
            if (AllowedProtocol.isJarProtocol(url.getProtocol()))
            {
                jarUrl = new URL(f);
                if (jarUrl.getProtocol() != null
                        && AllowedProtocol.FILE.name().equals(jarUrl.getProtocol().toUpperCase())
                        && StringUtils.contains(f, " "))
                {
                    jarUrl = new File(f).toURI().toURL();
                }
            }
            else if (AllowedProtocol.isValidProtocol(url.getProtocol()))
            {
                if (StringUtils.contains(f, " "))
                {
                    jarUrl = new File(f).toURI().toURL();
                }
                else
                {
                    jarUrl = new File(f).toURL();
                }
            }
        }
        catch (MalformedURLException mex)
        {
            log.error("Error during getPersistenceRootUrl(), Caused by: " + mex.getMessage());
            throw new IllegalArgumentException("invalid jar URL[] provided!" + url);
        }

        return jarUrl;
    }

    /**
     * Parse and exclude path till META-INF
     * 
     * @param file
     *            raw file path.
     * @return extracted/parsed file path.
     */
    private static String parseFilePath(String file)
    {
        final String excludePattern = "/META-INF/persistence.xml";
        file = file.substring(0, file.length() - excludePattern.length());

        // in case protocol is "file".
        file = file.endsWith("!") ? file.substring(0, file.length() - 1) : file;

        return file;
    }

    /**
     * Allowed protocols
     */
    public enum AllowedProtocol
    {
        WSJAR, JAR, ZIP, FILE, VFSZIP;

        /**
         * In case it is jar protocol
         * 
         * @param protocol
         * @return
         */
        public static boolean isJarProtocol(String protocol)
        {
            return protocol != null
                    && (protocol.toUpperCase().equals(JAR.name()) || protocol.toUpperCase().equals(WSJAR.name()));
        }

        /**
         * If provided protocol is within allowed protocol.
         * 
         * @param protocol
         *            protocol
         * @return true, if it is in allowed protocol.
         */
        public static boolean isValidProtocol(String protocol)
        {
            try
            {
                AllowedProtocol.valueOf(protocol.toUpperCase());
                return true;
            }
            catch (IllegalArgumentException iex)
            {
                return false;
            }
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy