Mantis - Resin
Viewing Issue Advanced Details
1507 major always 12-13-06 03:06 05-30-07 17:30
KnisterPeter  
ferg  
normal  
closed 3.1.0  
fixed  
none    
none 3.1.2  
0001507: JPA implementation is limited to amber
The JPA implementation of resin 3.1 is limited to amber, since the PersistenceProvider facility is not used by resin. The amber EntityManagerFactory is not created using the PersistenceProvider interface. This leads to problems when one wants to change the JPA implementation.
Please update the implementation so one can use his favorite JPA (we tried this, since amber is by far no means feature complete and spec compliant).

Notes
(0001651)
KnisterPeter   
12-13-06 04:04   
Just to give some more motivation for this bug.
Supporting third-party PersistenceProviders is required by JSR-220 spec 0000005.8 and 0000005.9
(0001658)
KnisterPeter   
12-18-06 04:19   
I've implemented a patch to support other JPA implementations. Tested with Oracle Toplink Essentials.

Below is the source for the patched InjectIntrospector.java and an implementation class. Feel free to use this a contribution to resin if you are interested.

/*
 * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source 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, or any warranty
 * of NON-INFRINGEMENT. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 * Free Software Foundation, Inc.
 * 59 Temple Place, Suite 330
 * Boston, MA 02111-1307 USA
 *
 * @author Scott Ferguson
 */

package com.caucho.config.j2ee;

import java.beans.Introspector;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.annotation.Resource;
import javax.annotation.Resources;
import javax.ejb.EJB;
import javax.naming.InitialContext;
import javax.naming.NameClassPair;
import javax.naming.NamingEnumeration;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceUnit;
import javax.transaction.UserTransaction;
import javax.xml.ws.Service;
import javax.xml.ws.WebServiceRef;

import com.caucho.config.BuilderProgram;
import com.caucho.config.ConfigException;
import com.caucho.naming.Jndi;
import com.caucho.util.L10N;
import com.caucho.util.Log;

/**
 * Analyzes a bean for
 *
 * @Inject tags.
 */
public class InjectIntrospector {

    private static final L10N L = new L10N(InjectIntrospector.class);

    private static final Logger log = Log.open(InjectIntrospector.class);

    /**
     * Analyzes a bean for
     *
     * @Inject tags, building an init program for them.
     */
    public static void configure(Object obj) throws Throwable {
        if (obj != null) {
            for (BuilderProgram program : introspect(obj.getClass())) {
                program.configure(obj);
            }
        }
    }

    /**
     * Analyzes a bean for
     *
     * @Inject tags, building an init program for them.
     */
    public static InjectProgram introspectProgram(Class type)
            throws ConfigException {
        return new InjectProgram(introspect(type));
    }

    /**
     * Analyzes a bean for
     *
     * @Inject tags, building an init program for them.
     */
    public static ArrayList<BuilderProgram> introspectStatic(Class type)
            throws ConfigException {
        return introspect(type);
    }

    /**
     * Analyzes a bean for
     *
     * @Inject tags, building an init program for them.
     */
    public static ArrayList<BuilderProgram> introspect(Class type)
            throws ConfigException {
        ArrayList<BuilderProgram> initList = new ArrayList<BuilderProgram>();

        try {
            introspectImpl(initList, type);
        } catch (ClassNotFoundException e) {
        } catch (Error e) {
        }

        return initList;
    }

    private static void introspectImpl(ArrayList<BuilderProgram> initList,
            Class type) throws ConfigException, ClassNotFoundException {
        if (type == null || type.equals(Object.class))
            return;

        introspectImpl(initList, type.getSuperclass());

        configureClassResources(initList, type);

        for (Method method : type.getDeclaredMethods()) {
            String fieldName = method.getName();
            Class[] param = method.getParameterTypes();

            if (param.length != 1)
                continue;

            if (fieldName.startsWith("set") && fieldName.length() > 3) {
                fieldName = fieldName.substring(3);

                char ch = fieldName.charAt(0);

                if (Character.isUpperCase(ch)
                        && (fieldName.length() == 1 || Character
                                .isLowerCase(fieldName.charAt(1)))) {
                    fieldName = Character.toLowerCase(ch)
                            + fieldName.substring(1);
                }
            }

            configure(initList, method, fieldName, param[0]);
        }
    }

    public static void configureClassResources(
            ArrayList<BuilderProgram> initList, Class type)
            throws ConfigException {
        Resources resources = (Resources) type.getAnnotation(Resources.class);
        if (resources != null) {
            for (Resource resource : resources.value()) {
                introspectClassResource(initList, type, resource);
            }
        }

        Resource resource = (Resource) type.getAnnotation(Resource.class);
        if (resource != null) {
            introspectClassResource(initList, type, resource);
        }

        for (Field field : type.getDeclaredFields()) {
            configure(initList, field, field.getName(), field.getType());
        }
    }

    private static void introspectClassResource(
            ArrayList<BuilderProgram> initList, Class type, Resource resource)
            throws ConfigException {
        String name = resource.name();

        Field field = findField(type, name);

        if (field != null) {
            initList.add(configureResource(field, field.getName(), field
                    .getType(), resource.name(), resource.type().getName(),
                    resource.name()));

            return;
        }

        Method method = findMethod(type, name);

        if (method != null) {
            initList.add(configureResource(method, method.getName(), method
                    .getParameterTypes()[0], resource.name(), resource.type()
                    .getName(), resource.name()));

            return;
        }
    }

    private static Field findField(Class type, String name) {
        for (Field field : type.getDeclaredFields()) {
            if (field.getName().equals(name))
                return field;
        }

        return null;
    }

    private static Method findMethod(Class type, String name) {
        for (Method method : type.getDeclaredMethods()) {
            if (method.getParameterTypes().length != 1)
                continue;

            String methodName = method.getName();
            if (!methodName.startsWith("set"))
                continue;

            methodName = Introspector.decapitalize(methodName.substring(3));

            if (name.equals(methodName))
                return method;
        }

        return null;
    }

    public static void configure(ArrayList<BuilderProgram> initList,
            AccessibleObject field, String fieldName, Class fieldType)
            throws ConfigException {
        if (field.isAnnotationPresent(Resource.class))
            configureResource(initList, field, fieldName, fieldType);
        else if (field.isAnnotationPresent(EJB.class))
            configureEJB(initList, field, fieldName, fieldType);
        else if (field.isAnnotationPresent(PersistenceUnit.class))
            configurePersistenceUnit(initList, field, fieldName, fieldType);
        else if (field.isAnnotationPresent(PersistenceContext.class))
            configurePersistenceContext(initList, field, fieldName, fieldType);
        else if (field.isAnnotationPresent(WebServiceRef.class))
            configureWebServiceRef(initList, field, fieldName, fieldType);
    }

    private static void configureResource(ArrayList<BuilderProgram> initList,
            AccessibleObject field, String fieldName, Class fieldType)
            throws ConfigException {
        Resource resource = field.getAnnotation(Resource.class);

        initList.add(configureResource(field, fieldName, fieldType, resource
                .name(), resource.type().getName(), resource.name()));
    }

    public static BuilderProgram introspectResource(AccessibleObject field,
            String fieldName, Class fieldType) throws ConfigException {
        Resource resource = field.getAnnotation(Resource.class);

        if (resource != null)
            return configureResource(field, fieldName, fieldType, resource
                    .name(), resource.type().getName(), resource.name());
        else
            return null;
    }

    private static void configureEJB(ArrayList<BuilderProgram> initList,
            AccessibleObject field, String fieldName, Class fieldType)
            throws ConfigException {
        EJB ejb = (EJB) field.getAnnotation(javax.ejb.EJB.class);

        initList.add(configureResource(field, fieldName, fieldType, ejb
                .beanName(), "javax.ejb.EJBLocalObject", ejb.name()));
    }

    private static void configureWebServiceRef(
            ArrayList<BuilderProgram> initList, AccessibleObject field,
            String fieldName, Class fieldType) throws ConfigException {
        WebServiceRef ref = (WebServiceRef) field
                .getAnnotation(WebServiceRef.class);

        String name = ref.name();
        name = ref.name();

        if ("".equals(name))
            name = fieldName;

        name = toFullName(name);
        // XXX: types

        AccessibleInject inject;

        if (field instanceof Field)
            inject = new FieldInject((Field) field);
        else
            inject = new PropertyInject((Method) field);

        BuilderProgram program;

        if (Service.class.isAssignableFrom(fieldType)) {
            program = new ServiceInjectProgram(name, fieldType, inject);
        } else {
            program = new ServiceProxyInjectProgram(name, fieldType, inject);
        }

        initList.add(program);
    }

    private static void configurePersistenceUnit(
            ArrayList<BuilderProgram> initList, AccessibleObject field,
            String fieldName, Class fieldType) throws ConfigException {
        PersistenceUnit pUnit = field.getAnnotation(PersistenceUnit.class);

        String jndiPrefix = "java:comp/env/persistence/_amber_PersistenceUnit";

        String jndiName = null;
        String unitName = pUnit.unitName();

        try {
            if (!unitName.equals(""))
                jndiName = jndiPrefix + '/' + unitName;
            else {
                InitialContext ic = new InitialContext();

                NamingEnumeration<NameClassPair> iter = ic.list(jndiPrefix);

                if (iter == null) {
                    log.warning("Can't find configured PersistenceUnit");
                    return; // XXX: error?
                }

                String ejbJndiName = null;
                while (iter.hasMore()) {
                    NameClassPair pair = iter.next();

                    if (pair.getName().equals("resin-ejb"))
                        ejbJndiName = jndiPrefix + '/' + pair.getName();
                    else {
                        jndiName = jndiPrefix + '/' + pair.getName();
                        break;
                    }
                }

                if (jndiName == null)
                    jndiName = ejbJndiName;
            }

            initList.add(configureResource(field, fieldName, fieldType,
                    unitName, "javax.persistence.EntityManagerFactory",
                    jndiName));
        } catch (Throwable e) {
            log.log(Level.WARNING, e.toString(), e);
        }
    }

    private static void configurePersistenceContext(
            ArrayList<BuilderProgram> initList, AccessibleObject field,
            String fieldName, Class fieldType) throws ConfigException {
        PersistenceContext pContext = field
                .getAnnotation(PersistenceContext.class);

        String jndiPrefix = "java:comp/env/persistence";

        String jndiName = null;
        String unitName = pContext.unitName();

        try {
            if (!unitName.equals(""))
                jndiName = jndiPrefix + '/' + unitName;
            else {
                InitialContext ic = new InitialContext();

                NamingEnumeration<NameClassPair> iter = ic.list(jndiPrefix);

                if (iter == null) {
                    log.warning("Can't find configured PersistenceContext");
                    return; // XXX: error?
                }

                String ejbJndiName = null;
                while (iter.hasMore()) {
                    NameClassPair pair = iter.next();

                    // Skip reserved prefixes.
                    // See com.caucho.amber.manager.AmberContainer
                    if (pair.getName().startsWith("_amber"))
                        continue;

                    if (pair.getName().equals("resin-ejb"))
                        ejbJndiName = jndiPrefix + '/' + pair.getName();
                    else {
                        jndiName = jndiPrefix + '/' + pair.getName();
                        break;
                    }
                }

                if (jndiName == null)
                    jndiName = ejbJndiName;
            }

            // markusw (20061213)
            // Added for third-party PersistenceProviders
            if (Jndi.lookup(jndiName) == null) {
                EntityManagerFactory emf = ThirdPartyPersistenceProviderConfigurator
                        .createFactory(unitName);
                if (emf != null) {
                    log
                            .info("Binding ThirdParty EntityManagerFactory to JNDI: "
                                    + jndiName);
                    Jndi.bindDeep(jndiName, emf.createEntityManager());
                }
            }

            initList.add(configureResource(field, fieldName, fieldType,
                    unitName, "javax.persistence.EntityManager", jndiName));
        } catch (Throwable e) {
            log.log(Level.WARNING, e.toString(), e);
        }
    }

    private static BuilderProgram configureResource(AccessibleObject field,
            String fieldName, Class fieldType, String name,
            String resourceType, String jndiName) throws ConfigException {
        String prefix = "";

        if (name.equals(""))
            name = fieldName;

        if (resourceType.equals("") || resourceType.equals("java.lang.Object"))
            resourceType = fieldType.getName();

        if (resourceType.equals("javax.sql.DataSource"))
            prefix = "jdbc/";
        else if (resourceType.startsWith("javax.jms."))
            prefix = "jms/";
        else if (resourceType.startsWith("javax.mail."))
            prefix = "mail/";
        else if (resourceType.equals("java.net.URL"))
            prefix = "url/";
        else if (resourceType.startsWith("javax.ejb."))
            prefix = "ejb/";

        if (!jndiName.equals("")) {
        } else if (UserTransaction.class.equals(fieldType)) {
            jndiName = "java:comp/UserTransaction";
        } else if ("java.util.concurrent.Executor".equals(resourceType)) {
            jndiName = "java:comp/ThreadPool";
        } else {
            jndiName = prefix + name;
        }

        int colon = jndiName.indexOf(':');
        int slash = jndiName.indexOf('/');

        if (colon < 0 || slash > 0 && slash < colon)
            jndiName = "java:comp/env/" + jndiName;

        BuilderProgram program;

        if (field instanceof Method)
            program = new JndiInjectProgram(jndiName, (Method) field);
        else
            program = new JndiFieldInjectProgram(jndiName, (Field) field);

        if (log.isLoggable(Level.FINEST))
            log.log(Level.FINEST, String.valueOf(program));

        return program;
    }

    private static String toFullName(String jndiName) {
        int colon = jndiName.indexOf(':');
        int slash = jndiName.indexOf('/');

        if (colon < 0 || slash > 0 && slash < colon)
            jndiName = "java:comp/env/" + jndiName;

        return jndiName;
    }
}


/*
 * Copyright (c) 2006, New Media Markets & Networks NMMN -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source 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, or any warranty
 * of NON-INFRINGEMENT. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 * Free SoftwareFoundation, Inc.
 * 59 Temple Place, Suite 330
 * Boston, MA 02111-1307 USA
 *
 * @author markusw
 */
package com.caucho.config.j2ee;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.persistence.EntityManagerFactory;
import javax.persistence.spi.ClassTransformer;
import javax.persistence.spi.PersistenceProvider;
import javax.persistence.spi.PersistenceUnitTransactionType;
import javax.sql.DataSource;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import com.caucho.loader.DynamicClassLoader;
import com.caucho.naming.Jndi;

public class ThirdPartyPersistenceProviderConfigurator {

    private static final Logger logger = Logger
            .getLogger(ThirdPartyPersistenceProviderConfigurator.class
                    .getName());

    static class PersistenceUnitInfo implements
            javax.persistence.spi.PersistenceUnitInfo {

        private String name;

        private PersistenceUnitTransactionType transactionType;

        private String provider;

        private boolean excludeUnlistedClasses;

        private List<URL> jarFileUrls = new ArrayList<URL>();

        private DataSource jtaDataSource;

        private DataSource nonJtaDataSource;

        private List<String> managedClassNames = new ArrayList<String>();

        private List<String> mappingFileNames = new ArrayList<String>();

        private URL persistenceUnitRootUrl;

        private Properties properties = new Properties();

        public PersistenceUnitInfo(String name,
                PersistenceUnitTransactionType transactionType) {
            super();
            this.name = name.trim();
            this.transactionType = transactionType;
        }

        public void addTransformer(ClassTransformer transformer) {
            // TODO: Implement
        }

        public boolean excludeUnlistedClasses() {
            return excludeUnlistedClasses;
        }

        public void setExcludeUnlistedClasses(boolean excludeUnlistedClasses) {
            this.excludeUnlistedClasses = excludeUnlistedClasses;
        }

        public ClassLoader getClassLoader() {
            return Thread.currentThread().getContextClassLoader();
        }

        public ClassLoader getNewTempClassLoader() {
            ClassLoader cl = Thread.currentThread().getContextClassLoader();
            if (cl instanceof DynamicClassLoader) {
                return ((DynamicClassLoader) cl).getNewTempClassLoader();
            }
            return new URLClassLoader(null, cl);
        }

        public List<URL> getJarFileUrls() {
            return jarFileUrls;
        }

        public DataSource getJtaDataSource() {
            return jtaDataSource;
        }

        public void setJtaDataSource(DataSource jtaDataSource) {
            this.jtaDataSource = jtaDataSource;
        }

        public List<String> getManagedClassNames() {
            return managedClassNames;
        }

        public List<String> getMappingFileNames() {
            return mappingFileNames;
        }

        public DataSource getNonJtaDataSource() {
            return nonJtaDataSource;
        }

        public void setNonJtaDataSource(DataSource nonJtaDataSource) {
            this.nonJtaDataSource = nonJtaDataSource;
        }

        public String getPersistenceProviderClassName() {
            return provider;
        }

        public void setPersistenceProviderClassName(String provider) {
            this.provider = provider.trim();
        }

        public String getPersistenceUnitName() {
            return name;
        }

        public URL getPersistenceUnitRootUrl() {
            return persistenceUnitRootUrl;
        }

        public void setPersistenceUnitRootUrl(URL persistenceUnitRootUrl) {
            this.persistenceUnitRootUrl = persistenceUnitRootUrl;
        }

        public Properties getProperties() {
            return properties;
        }

        public PersistenceUnitTransactionType getTransactionType() {
            return transactionType;
        }

        /**
         * @see java.lang.Object#toString()
         */
        @Override
        public String toString() {
            return getClass().getName() + "[name=" + name + ", provider="
                    + provider + "]";
        }

    }

    static class PersistenceXmlHandler extends DefaultHandler {

        private List<PersistenceUnitInfo> infos = new ArrayList<PersistenceUnitInfo>();

        private PersistenceUnitInfo info;

        private StringBuilder chars = new StringBuilder();

        public PersistenceUnitInfo[] getPersistenceUnitInfos() {
            return infos.toArray(new PersistenceUnitInfo[infos.size()]);
        }

        /**
         * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String,
         * java.lang.String, java.lang.String, org.xml.sax.Attributes)
         */
        @Override
        public void startElement(String uri, String localName, String qName,
                Attributes attributes) throws SAXException {
            logger.finest("persistence.xml: <" + qName + ">");
            if (qName.equals("persistence-unit")) {
                logger
                        .warning("Created PersistenceUnitInfo for persistence-unit "
                                + attributes.getValue("name"));
                info = new PersistenceUnitInfo(attributes.getValue("name"),
                        PersistenceUnitTransactionType.valueOf(attributes
                                .getValue("transaction-type")));
                infos.add(info);
            } else if (qName.equals("property")) {
                info.getProperties().put(attributes.getValue("name"),
                        attributes.getValue("value"));
            }
        }

        /**
         * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
         */
        @Override
        public void characters(char[] ch, int start, int length)
                throws SAXException {
            chars.append(ch, start, length);
        }

        /**
         * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String,
         * java.lang.String, java.lang.String)
         */
        @Override
        public void endElement(String uri, String localName, String qName)
                throws SAXException {
            logger.finest("persistence.xml: </" + qName + ">");
            if (qName.equals("provider")) {
                info.setPersistenceProviderClassName(chars.toString().trim());
            } else if (qName.equals("jta-data-source")) {
                info.setJtaDataSource((DataSource) Jndi.lookup(chars.toString()
                        .trim()));
            } else if (qName.equals("non-jta-data-source")) {
                info.setNonJtaDataSource((DataSource) Jndi.lookup(chars
                        .toString().trim()));
            } else if (qName.equals("mapping-file")) {
                info.getMappingFileNames().add(chars.toString().trim());
            } else if (qName.equals("jar-file")) {
                try {
                    info.getJarFileUrls().add(new URL(chars.toString().trim()));
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                }
            } else if (qName.equals("class")) {
                info.getManagedClassNames().add(chars.toString().trim());
            } else if (qName.equals("exclude-unlisted-classes")) {
                info.setExcludeUnlistedClasses(Boolean.parseBoolean(chars
                        .toString().trim()));
            }
            chars.setLength(0);
        }

    }

    /**
     * Added from Sun RI (Toplink Essentials) Persistence.java
     */
    private static Set<PersistenceProvider> providers = new HashSet<PersistenceProvider>();

    private static HashMap<String, PersistenceUnitInfo> infos = new HashMap<String, PersistenceUnitInfo>();

    public static EntityManagerFactory createFactory(String unitName)
            throws IOException {
        if (providers.size() == 0) {
            findAllProviders();
        }
        if (infos.size() == 0) {
            parsePersistenceXmls();
        }
        logger.config("Searching PersistenceProvider");
        for (PersistenceProvider provider : providers) {
            PersistenceUnitInfo info = infos.get(unitName);
            logger.config("PersistenceUnitInfo: " + info);
            if (info != null
                    && info.getPersistenceProviderClassName().equals(
                            provider.getClass().getName())) {
                logger.config("Creating EntityManagerFactory from "
                        + provider.getClass().getName());
                EntityManagerFactory emf = provider
                        .createContainerEntityManagerFactory(info, null);
                if (emf != null) {
                    logger.config("Returning EntityManagerFactory");
                    return emf;
                }
            }
        }
        return null;
    }

    /**
     * Added from Sun RI (Toplink Essentials) Persistence.java
     */
    private static void findAllProviders() throws IOException {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        Enumeration<URL> resources = loader.getResources("META-INF/services/"
                + PersistenceProvider.class.getName());
        Set<String> names = new HashSet<String>();
        while (resources.hasMoreElements()) {
            URL url = resources.nextElement();
            InputStream is = url.openStream();
            try {
                names.addAll(providerNamesFromReader(new BufferedReader(
                        new InputStreamReader(is))));
            } finally {
                is.close();
            }
        }
        for (String s : names) {
            try {
                providers.add((PersistenceProvider) loader.loadClass(s)
                        .newInstance());
            } catch (ClassNotFoundException exc) {
            } catch (InstantiationException exc) {
            } catch (IllegalAccessException exc) {
            }
        }
    }

    /**
     * Added from Sun RI (Toplink Essentials) Persistence.java
     */
    private static final Pattern nonCommentPattern = Pattern
            .compile("^([^#]+)");

    /**
     * Added from Sun RI (Toplink Essentials) Persistence.java
     */
    private static Set<String> providerNamesFromReader(BufferedReader reader)
            throws IOException {
        Set<String> names = new HashSet<String>();
        String line;
        while ((line = reader.readLine()) != null) {
            line = line.trim();
            Matcher m = nonCommentPattern.matcher(line);
            if (m.find()) {
                names.add(m.group().trim());
            }
        }
        return names;
    }

    private static void parsePersistenceXmls() throws IOException {
        // Search jar files
        parsePersistenceXml("META-INF/persistence.xml");
        // Search extracted webapps
        parsePersistenceXml("../../META-INF/persistence.xml");
    }

    private static void parsePersistenceXml(String location) throws IOException {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        Enumeration<URL> resources = loader.getResources(location);
        while (resources.hasMoreElements()) {
            URL resource = resources.nextElement();
            InputStream in = resource.openStream();
            try {
                URL puRootUrl = getPersistenceUnitRootUrl(resource);
                logger.config("PersistenceUnitRootUrl: " + puRootUrl);

                PersistenceUnitInfo[] unitInfos = parsePersistenceXml(in);
                for (PersistenceUnitInfo info : unitInfos) {
                    info.setPersistenceUnitRootUrl(puRootUrl);
                    infos.put(info.getPersistenceUnitName(), info);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                in.close();
            }
        }
    }

    private static PersistenceUnitInfo[] parsePersistenceXml(InputStream in)
            throws Exception {
        PersistenceXmlHandler handler = new PersistenceXmlHandler();

        SAXParserFactory spf = SAXParserFactory.newInstance();
        SAXParser sp = spf.newSAXParser();
        XMLReader parser = sp.getXMLReader();
        parser.setContentHandler(handler);
        parser.parse(new InputSource(in));
        return handler.getPersistenceUnitInfos();
    }

    private static URL getPersistenceUnitRootUrl(URL resource)
            throws IOException, URISyntaxException {
        URLConnection conn = resource.openConnection();
        if (conn instanceof JarURLConnection) {
            return ((JarURLConnection) conn).getJarFileURL();
        }
        return new File(new File(conn.getURL().toURI()).getParentFile()
                .getParentFile(), "WEB-INF/classes").toURL();
    }

}
(0001659)
KnisterPeter   
12-19-06 07:48   
There is another issue related to this.
Amber should only created PersistenceUnits where Amber or no provider is specified. Otherwise third-party providers has to override the created PersistenceUnits and this is ugly and leads to bad performance.
(0001660)
ferg   
12-19-06 10:20   
Thanks. I don't think this can make it into 3.1.0, unfortunately, since it's going out this week and I'm not sure this is exactly the way we want to configure it the other implementations (i.e. we'd probably prefer to have something like a <persistence-manager> which specified the implementation to use or which specified using persistence.xml to select the driver, like the code above.)
(0001662)
KnisterPeter   
12-19-06 10:35   
Either way would be ok for us, since having third-party support is all that we require.
But wouldn't it the best way to get a provider without configuring anything? Placing e.g. toplink.jar into the classpath could be recognized by resin (the META-INF/services/...) and the standard persistence.xml could specify a provider class for the given persistence unit.
That way it is plugable out-of-the-box.

The implementation by us is quite simple and doesn't fit into resin. We contributed it for the case that someone does need this. :)
(0001976)
ferg   
05-30-07 17:30   
jpa/0s71