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

org.babyfish.hibernate.ejb.PersistenceUnitReader Maven / Gradle / Ivy

The newest version!
/*
 * BabyFish, Object Model Framework for Java and JPA.
 * https://github.com/babyfish-ct/babyfish
 *
 * Copyright (c) 2008-2015, Tao Chen
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * Please visit "http://opensource.org/licenses/LGPL-3.0" to know more.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 */
package org.babyfish.hibernate.ejb;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.persistence.PersistenceException;
import javax.persistence.SharedCacheMode;
import javax.persistence.ValidationMode;
import javax.persistence.spi.ClassTransformer;
import javax.persistence.spi.PersistenceUnitInfo;
import javax.persistence.spi.PersistenceUnitTransactionType;
import javax.sql.DataSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.babyfish.collection.ArrayList;
import org.babyfish.collection.LinkedHashMap;
import org.babyfish.collection.MACollections;
import org.babyfish.collection.XOrderedMap;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.EntityReference;
import org.w3c.dom.Node;
import org.w3c.dom.Text;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

/**
 * @author Tao Chen
 */
public class PersistenceUnitReader {
    
    public interface Resource {
        
        String getLocation();
        
        InputStream getInputStream();
    }
    
    public interface DataSourceLookup {
        DataSource getDataSource(String dataSourceName);
    }
    
    private static final String PERSISTENCE_VERSION = "version";

    private static final String PERSISTENCE_UNIT = "persistence-unit";

    private static final String UNIT_NAME = "name";

    private static final String MAPPING_FILE_NAME = "mapping-file";

    private static final String JAR_FILE_URL = "jar-file";

    private static final String MANAGED_CLASS_NAME = "class";

    private static final String PROPERTIES = "properties";

    private static final String PROVIDER = "provider";

    private static final String TRANSACTION_TYPE = "transaction-type";

    private static final String JTA_DATA_SOURCE = "jta-data-source";

    private static final String NON_JTA_DATA_SOURCE = "non-jta-data-source";

    private static final String EXCLUDE_UNLISTED_CLASSES = "exclude-unlisted-classes";

    private static final String SHARED_CACHE_MODE = "shared-cache-mode";

    private static final String VALIDATION_MODE = "validation-mode";

    //private static final String META_INF = "META-INF";

    public XOrderedMap read(String ... resources) {
        Resource[] resourceObjs = new Resource[resources.length];
        for (int i = 0; i < resources.length; i++) {
            final String location = resources[i];
            resourceObjs[i] = new Resource() {

                @Override
                public String getLocation() {
                    return location;
                }

                @Override
                public InputStream getInputStream() {
                    InputStream inputStream = 
                            PersistenceUnitInfo
                            .class
                            .getClassLoader()
                            .getResourceAsStream(location);
                    if (inputStream == null) {
                        throw new PersistenceException();
                    }
                    return inputStream;
                }
            };
        }
        return this.read(resourceObjs);
    }

    public XOrderedMap read(Resource ... resources) {
        ErrorHandler handler = new ErrorHandler() {

            @Override
            public void warning(SAXParseException exception) throws SAXException {
                
            }

            @Override
            public void error(SAXParseException exception) throws SAXException {
                // TODO Auto-generated method stub
                
            }

            @Override
            public void fatalError(SAXParseException exception) throws SAXException {
                
            }
            
        };
        XOrderedMap infos = new LinkedHashMap<>();
        for (Resource resource : resources) {
            try {
                InputStream stream = resource.getInputStream();
                try {
                    Document document = buildDocument(handler, stream);
                    parseDocument(resource, document, infos);
                }
                finally {
                    stream.close();
                }
            }
            catch (IOException ex) {
                throw new IllegalArgumentException("Cannot parse persistence unit from " + resource.getLocation(), ex);
            }
            catch (SAXException ex) {
                throw new IllegalArgumentException("Invalid XML in persistence unit from " + resource.getLocation(), ex);
            }
            catch (ParserConfigurationException ex) {
                throw new IllegalArgumentException("Internal error parsing persistence unit from " + resource.getLocation());
            }
        }
        return infos;
    }

    protected Document buildDocument(ErrorHandler handler, InputStream stream)
            throws ParserConfigurationException, SAXException, IOException {

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        DocumentBuilder parser = dbf.newDocumentBuilder();
        parser.setErrorHandler(handler);
        return parser.parse(stream);
    }

    protected void parseDocument(
            Resource resource, Document document, Map infos) throws IOException {
        Element persistence = document.getDocumentElement();
        String version = persistence.getAttribute(PERSISTENCE_VERSION);
        URL rootUrl = determinePersistenceUnitRootUrl(resource);
        for (Element element : getChildElements(persistence, PERSISTENCE_UNIT)) {
            PersistenceUnitInfo info = parsePersistenceUnitInfo(element, version, rootUrl);
            if (infos.put(info.getPersistenceUnitName(), info) != null) {
                throw new IllegalArgumentException();
            }
        }
    }
    
    protected DataSourceLookup getDataSourceLookup() {
        return null;
    }

    protected URL determinePersistenceUnitRootUrl(Resource resource) throws IOException {
//      URL originalURL = resource.getURL();
//
//      // If we get an archive, simply return the jar URL (section 6.2 from the JPA spec)
//      if (ResourceUtils.isJarURL(originalURL)) {
//          return ResourceUtils.extractJarFileURL(originalURL);
//      }
//
//      // check META-INF folder
//      String urlToString = originalURL.toExternalForm();
//      if (!urlToString.contains(META_INF)) {
//          if (logger.isInfoEnabled()) {
//              logger.info(resource.getFilename() +
//                      " should be located inside META-INF directory; cannot determine persistence unit root URL for " +
//                      resource);
//          }
//          return null;
//      }
//      if (urlToString.lastIndexOf(META_INF) == urlToString.lastIndexOf('/') - (1 + META_INF.length())) {
//          if (logger.isInfoEnabled()) {
//              logger.info(resource.getFilename() +
//                      " is not located in the root of META-INF directory; cannot determine persistence unit root URL for " +
//                      resource);
//          }
//          return null;
//      }
//
//      String persistenceUnitRoot = urlToString.substring(0, urlToString.lastIndexOf(META_INF));
//      if (persistenceUnitRoot.endsWith("/")) {
//          persistenceUnitRoot = persistenceUnitRoot.substring(0, persistenceUnitRoot.length() - 1);
//      }
//      return new URL(persistenceUnitRoot);
        return null;
    }

    protected PersistenceUnitInfo parsePersistenceUnitInfo(Element persistenceUnit, String version, URL rootUrl)
            throws IOException {

        PersistenceUnitInfoImpl unitInfo = new PersistenceUnitInfoImpl();

        // set JPA version (1.0 or 2.0)
        unitInfo.persistenceXMLSchemaVersion = version;
        unitInfo.persistenceUnitRootUrl = rootUrl;
        unitInfo.persistenceUnitName = nullOrTrim(persistenceUnit.getAttribute(UNIT_NAME));
        String txType = nullOrTrim(persistenceUnit.getAttribute(TRANSACTION_TYPE));
        if (txType != null) {
            unitInfo.transactionType = PersistenceUnitTransactionType.valueOf(txType);
        }

        // data-source
        DataSourceLookup dataSourceLookup = this.getDataSourceLookup();
        String jtaDataSource = nullOrTrim(getChildElementValue(persistenceUnit, JTA_DATA_SOURCE));
        if (jtaDataSource != null) {
            if (dataSourceLookup == null) {
                throw new IllegalStateException(); //TODO 2013-11-14
            }
            unitInfo.jtaDataSource = dataSourceLookup.getDataSource(jtaDataSource);
        }

        String nonJtaDataSource = nullOrTrim(getChildElementValue(persistenceUnit, NON_JTA_DATA_SOURCE));
        if (nonJtaDataSource != null) {
            if (dataSourceLookup == null) {
                throw new IllegalStateException(); //TODO 2013-11-14
            }
            unitInfo.nonJtaDataSource = dataSourceLookup.getDataSource(nonJtaDataSource);
        }

        // provider
        String provider = nullOrTrim(getChildElementValue(persistenceUnit, PROVIDER));
        if (provider != null) {
            unitInfo.persistenceProviderClassName = provider;
        }

        // exclude unlisted classes
        Element excludeUnlistedClasses = getChildElement(persistenceUnit, EXCLUDE_UNLISTED_CLASSES);
        if (excludeUnlistedClasses != null) {
            unitInfo.excludeUnlistedClasses = true;
        }

        // set JPA 2.0 shared cache mode
        String sharedCacheMode = nullOrTrim(getChildElementValue(persistenceUnit, SHARED_CACHE_MODE));
        if (sharedCacheMode != null) {
            unitInfo.sharedCacheMode = SharedCacheMode.valueOf(sharedCacheMode);
        }

        // set JPA 2.0 validation mode
        String validationMode = nullOrTrim(getChildElementValue(persistenceUnit, VALIDATION_MODE));
        if (validationMode != null) {
            unitInfo.validationMode = ValidationMode.valueOf(validationMode);
        }

        parseProperties(persistenceUnit, unitInfo);
        parseManagedClasses(persistenceUnit, unitInfo);
        parseMappingFiles(persistenceUnit, unitInfo);
        parseJarFiles(persistenceUnit, unitInfo);

        return unitInfo;
    }

    private void parseProperties(Element persistenceUnit, PersistenceUnitInfoImpl unitInfo) {
        Element propRoot = getChildElement(persistenceUnit, PROPERTIES);
        Properties props = new Properties();
        if (propRoot != null) {
            List properties = getChildElements(propRoot, "property");
            for (Element property : properties) {
                String name = property.getAttribute("name");
                String value = property.getAttribute("value");
                props.put(name, value);
            }
        }
        //TODO: how to readonly?
        unitInfo.properties = props;
    }

    private void parseManagedClasses(Element persistenceUnit, PersistenceUnitInfoImpl unitInfo) {
        List classes = getChildElements(persistenceUnit, MANAGED_CLASS_NAME);
        List managedClassNames = new ArrayList<>(classes.size());
        for (Element element : classes) {
            String value = nullOrTrim(getValue(element));
            if (value != null) {
                managedClassNames.add(value);
            }
        }
        unitInfo.managedClassNames = MACollections.unmodifiable(managedClassNames);
    }

    private void parseMappingFiles(Element persistenceUnit, PersistenceUnitInfoImpl unitInfo) {
        List files = getChildElements(persistenceUnit, MAPPING_FILE_NAME);
        List mappingFileNames = new ArrayList<>(files.size());
        for (Element element : files) {
            String value = nullOrTrim(getValue(element).trim());
            if (value != null) {
                mappingFileNames.add(value);
            }
        }
        unitInfo.mappingFileNames = MACollections.unmodifiable(mappingFileNames);
    }
    
    private void parseJarFiles(Element persistenceUnit, PersistenceUnitInfoImpl unitInfo) throws IOException {
        List jars = getChildElements(persistenceUnit, JAR_FILE_URL);
        List jarFileUrls = new ArrayList<>();
        URL rootURL = unitInfo.getPersistenceUnitRootUrl();
        for (Element element : jars) {
            String value = nullOrTrim(getValue(element));
            if (value != null) {
                URL url = this.getJarFileUrl(value, rootURL);
                jarFileUrls.add(url);
            }
        }
        unitInfo.jarFileUrls = MACollections.unmodifiable(jarFileUrls);
    }
    
    protected URL getJarFileUrl(String value, URL rootURL) throws MalformedURLException {
        if (new File(value).exists()) {
            return new URL(value);
        }
        return new URL(rootURL, value);
    }
    
    private static String getChildElementValue(Element element, String childElementName) {
        Element childElement = getChildElement(element, childElementName);
        if (childElement != null) {
            return getValue(childElement);
        }
        return null;
    }
    
    private static Element getChildElement(Element element, String childElementName) {
        for (Node childNode = element.getFirstChild(); 
                childNode != null;
                childNode = childNode.getNextSibling()) {
            if (childNode instanceof Element && childNode.getLocalName().equals(childElementName)) {
                return (Element)childNode;
            }
        }
        return null;
    }
    
    private static List getChildElements(Element element, String childElementName) {
        List elements = new ArrayList<>();
        for (Node childNode = element.getFirstChild(); 
                childNode != null;
                childNode = childNode.getNextSibling()) {
            if (childNode instanceof Element && childNode.getLocalName().equals(childElementName)) {
                elements.add((Element)childNode);
            }
        }
        return elements;
    }
    
    private static String getValue(Element element) {
        StringBuilder builder = new StringBuilder();
        for (Node childNode = element.getFirstChild();
                childNode != null;
                childNode = childNode.getNextSibling()) {
            if (childNode instanceof Text || childNode instanceof EntityReference) {
                builder.append(childNode.getNodeValue());
            }
        }
        return builder.toString();
    }
    
    private static String nullOrTrim(String value) {
        if (value != null) {
            value = value.trim();
            if (value.isEmpty()) {
                return null;
            }
        }
        return value;
    }
    
    private static class PersistenceUnitInfoImpl implements PersistenceUnitInfo {
        
        private String persistenceUnitName;

        private String persistenceProviderClassName;

        private PersistenceUnitTransactionType transactionType;

        private DataSource nonJtaDataSource;

        private DataSource jtaDataSource;

        private List mappingFileNames;

        private URL persistenceUnitRootUrl;

        private List managedClassNames;
        
        private List jarFileUrls;

        private boolean excludeUnlistedClasses = false;

        private Properties properties;

        private String persistenceXMLSchemaVersion = "1.0";
        
        private SharedCacheMode sharedCacheMode;
        
        private ValidationMode validationMode;

        @Override
        public String getPersistenceUnitName() {
            return this.persistenceUnitName;
        }

        @Override
        public String getPersistenceProviderClassName() {
            return this.persistenceProviderClassName;
        }

        @Override
        public PersistenceUnitTransactionType getTransactionType() {
            if (this.transactionType != null) {
                return this.transactionType;
            }
            else {
                return this.jtaDataSource != null ?
                        PersistenceUnitTransactionType.JTA : 
                        PersistenceUnitTransactionType.RESOURCE_LOCAL;
            }
        }

        @Override
        public DataSource getJtaDataSource() {
            return this.jtaDataSource;
        }

        @Override
        public DataSource getNonJtaDataSource() {
            return this.nonJtaDataSource;
        }

        @Override
        public List getMappingFileNames() {
            return this.mappingFileNames;
        }

        @Override
        public List getJarFileUrls() {
            return this.jarFileUrls;
        }

        @Override
        public URL getPersistenceUnitRootUrl() {
            return this.persistenceUnitRootUrl;
        }

        @Override
        public List getManagedClassNames() {
            return this.managedClassNames;
        }

        @Override
        public boolean excludeUnlistedClasses() {
            return this.excludeUnlistedClasses;
        }

        @Override
        public Properties getProperties() {
            return this.properties;
        }

        @Override
        public String getPersistenceXMLSchemaVersion() {
            return this.persistenceXMLSchemaVersion;
        }

        @Override
        public ClassLoader getClassLoader() {
            return PersistenceUnitInfo.class.getClassLoader();
        }

        /**
         * This implementation throws an UnsupportedOperationException.
         */
        public ClassLoader getNewTempClassLoader() {
            return PersistenceUnitInfo.class.getClassLoader();
        }
        
        @Override
        public void addTransformer(ClassTransformer arg0) {
            // TODO Auto-generated method stub
            
        }

        @Override
        public SharedCacheMode getSharedCacheMode() {
            return this.sharedCacheMode;
        }

        @Override
        public ValidationMode getValidationMode() {
            return this.validationMode;
        }
        
        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append("PersistenceUnitInfo: name '");
            builder.append(this.persistenceUnitName);
            builder.append("', root URL [");
            builder.append(this.persistenceUnitRootUrl);
            builder.append("]");
            return builder.toString();
        }
    }

}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy