/* * 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.openejb.persistence; import org.apache.openejb.OpenEJB; import org.apache.openejb.loader.SystemInstance; import org.apache.openejb.resource.jdbc.managed.xa.DataSourceXADataSource; import org.apache.openejb.util.URLs; import org.apache.openejb.util.classloader.URLClassLoaderFirst; 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.CommonDataSource; import javax.sql.DataSource; import javax.sql.XADataSource; import javax.transaction.TransactionSynchronizationRegistry; import java.io.File; import java.io.IOException; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.List; import java.util.Properties; public class PersistenceUnitInfoImpl implements PersistenceUnitInfo { /** * External handler which handles adding a runtime ClassTransformer to the classloader. */ private final PersistenceClassLoaderHandler persistenceClassLoaderHandler; /** * The unique id of this persistence unit. */ private String id; /** * Name of this persistence unit. The JPA specification has restrictions on the * uniqueness of this name. */ private String persistenceUnitName; /** * Name of the persistence provider implementation class. */ private String persistenceProviderClassName; /** * Does this persistence unit participate in JTA transactions or does it manage * resource local transactions using the JDBC APIs. */ private PersistenceUnitTransactionType transactionType = PersistenceUnitTransactionType.JTA; /** * Data source used by jta persistence units for accessing transactional data. */ private DataSource jtaDataSource; /** * Data source used by non-jta persistence units and by jta persistence units for * non-transactional operations such as accessing a primary key table or sequence. */ private DataSource nonJtaDataSource; /** * Names if the entity-mapping.xml files relative to to the persistenceUnitRootUrl. */ private final List<String> mappingFileNames = new ArrayList<String>(); /** * The jar file locations that make up this persistence unit. */ private final List<URL> jarFileUrls = new ArrayList<URL>(); /** * Location of the root of the persistent unit. The directory in which * META-INF/persistence.xml is located. */ private URL persistenceUnitRootUrl; /** * List of the managed entity classes. */ private final List<String> managedClassNames = new ArrayList<String>(); /** * Should class not listed in the persistence unit be managed by the EntityManager? */ private boolean excludeUnlistedClasses; /** * JPA provider properties for this persistence unit. */ private Properties properties; /** * Class loader used by JPA to load Entity classes. */ private ClassLoader classLoader; // JPA 2.0 /** * Schema version of the persistence.xml file */ private String persistenceXMLSchemaVersion; /** * Second-level cache mode for the persistence unit */ private SharedCacheMode sharedCacheMode = SharedCacheMode.UNSPECIFIED; /** * The validation mode to be used for the persistence unit */ private ValidationMode validationMode; /** * just to be able to dump this PU at runtime */ private String jtaDataSourceName; /** * just to be able to dump this PU at runtime */ private String nonJtaDataSourceName; /** * does it need to be created lazily (not in constructor) */ private boolean lazilyInitialized; public PersistenceUnitInfoImpl() { this.persistenceClassLoaderHandler = null; } public PersistenceUnitInfoImpl(final PersistenceClassLoaderHandler persistenceClassLoaderHandler) { this.persistenceClassLoaderHandler = persistenceClassLoaderHandler; } public String getId() { return id; } public void setId(final String id) { this.id = id; } public String getPersistenceUnitName() { return persistenceUnitName; } public void setPersistenceUnitName(final String persistenceUnitName) { this.persistenceUnitName = persistenceUnitName; } public String getPersistenceProviderClassName() { return persistenceProviderClassName; } public void setPersistenceProviderClassName(final String persistenceProviderClassName) { this.persistenceProviderClassName = persistenceProviderClassName; } public PersistenceUnitTransactionType getTransactionType() { return transactionType; } public void setTransactionType(final PersistenceUnitTransactionType transactionType) { this.transactionType = transactionType; } public DataSource getJtaDataSource() { return jtaDataSource; } public void setJtaDataSource(final CommonDataSource jtaDataSource) { if (XADataSource.class.isInstance(jtaDataSource)) { this.jtaDataSource = new DataSourceXADataSource( jtaDataSource, OpenEJB.getTransactionManager(), SystemInstance.get().getComponent(TransactionSynchronizationRegistry.class)); } else { this.jtaDataSource = DataSource.class.cast(jtaDataSource); } } public DataSource getNonJtaDataSource() { return nonJtaDataSource; } public void setNonJtaDataSource(final CommonDataSource nonJtaDataSource) { if (XADataSource.class.isInstance(nonJtaDataSource)) { this.nonJtaDataSource = new DataSourceXADataSource( nonJtaDataSource, OpenEJB.getTransactionManager(), SystemInstance.get().getComponent(TransactionSynchronizationRegistry.class)); } else { this.nonJtaDataSource = DataSource.class.cast(nonJtaDataSource); } } public List<String> getMappingFileNames() { return mappingFileNames; } public void setMappingFileNames(final List<String> mappingFileNames) { if (mappingFileNames == null) { throw new NullPointerException("mappingFileNames is null"); } this.mappingFileNames.clear(); this.mappingFileNames.addAll(mappingFileNames); } public void addMappingFileName(final String mappingFileName) { if (mappingFileName == null) { throw new NullPointerException("mappingFileName is null"); } mappingFileNames.add(mappingFileName); } public List<URL> getJarFileUrls() { return jarFileUrls; } public URL getPersistenceUnitRootUrl() { return persistenceUnitRootUrl; } public void setRootUrlAndJarUrls(final String persistenceUnitRootUrl, final List<String> jarFiles) throws MalformedURLException { File root; try { final URI rootUri = URLs.uri(persistenceUnitRootUrl); root = new File(rootUri); } catch (final IllegalArgumentException e) { root = new File(persistenceUnitRootUrl); } this.persistenceUnitRootUrl = toUrl(root); try { if (!jarFiles.isEmpty()) { final File tmpRoot; if (root.getName().endsWith(".jar")) { tmpRoot = root.getParentFile(); // lib for a war, / of the ear otherwise, no sense in other cases } else { tmpRoot = root; } for (final String path : jarFiles) { jarFileUrls.add(toUrl(new File(tmpRoot, path).getCanonicalFile())); } } } catch (final IOException e) { throw new IllegalStateException(e); } } private URL toUrl(final File root) throws MalformedURLException { if (!root.isFile() && root.getPath().startsWith("jar:file:")) { try { final String absolutePath = root.getAbsolutePath(); final int endIndex = absolutePath.indexOf('!'); if (endIndex > 0) { final File file = new File(absolutePath.substring(0, endIndex)); if (file.isFile() && file.getName().endsWith(".jar")) { return file.toURI().toURL(); } } return new URL(root.getPath()); } catch (final MalformedURLException me) { // no-op keep previous behavior } } return root.toURI().toURL(); } public List<String> getManagedClassNames() { return managedClassNames; } public void setManagedClassNames(final List<String> managedClassNames) { if (managedClassNames == null) { throw new NullPointerException("managedClassNames is null"); } this.managedClassNames.clear(); this.managedClassNames.addAll(managedClassNames); } public void addManagedClassName(final String className) { managedClassNames.add(className); } public boolean excludeUnlistedClasses() { return excludeUnlistedClasses; } public void setExcludeUnlistedClasses(final boolean excludeUnlistedClasses) { this.excludeUnlistedClasses = excludeUnlistedClasses; } public Properties getProperties() { return properties; } public void setProperties(final Properties properties) { this.properties = properties; } public ClassLoader getClassLoader() { return classLoader; } public void setClassLoader(final ClassLoader classLoader) { this.classLoader = classLoader; } public void addTransformer(final ClassTransformer classTransformer) { if (persistenceClassLoaderHandler != null) { final PersistenceClassFileTransformer classFileTransformer = new PersistenceClassFileTransformer(classTransformer); persistenceClassLoaderHandler.addTransformer(id, classLoader, classFileTransformer); } } public ClassLoader getNewTempClassLoader() { if (persistenceClassLoaderHandler != null) { return persistenceClassLoaderHandler.getNewTempClassLoader(classLoader); } else { return null; } } // for emf in webapp of ears public boolean isLazilyInitialized() { return lazilyInitialized; } public void setLazilyInitialized(final boolean lazilyInitialized) { this.lazilyInitialized = lazilyInitialized; } public static class PersistenceClassFileTransformer implements ClassFileTransformer { private final ClassTransformer classTransformer; public PersistenceClassFileTransformer(final ClassTransformer classTransformer) { this.classTransformer = classTransformer; } public byte[] transform(final ClassLoader classLoader, final String className, final Class<?> classBeingRedefined, final ProtectionDomain protectionDomain, final byte[] classfileBuffer) throws IllegalClassFormatException { // Example code to easily debug transformation of a specific class // if ("org/apache/openejb/test/entity/cmp/BasicCmpBean".equals(className) || // "org/apache/openejb/test/entity/cmp/BasicCmp2Bean_BasicCmp2Bean".equals(className)) { // System.err.println("Loading " + className); // } if (className == null) { return classfileBuffer; } final String replace = className.replace('/', '.'); if (isServerClass(replace)) { return classfileBuffer; } return classTransformer.transform(classLoader, replace, classBeingRedefined, protectionDomain, classfileBuffer); } } // not the shouldSkip() method from UrlClassLoaderFirst since we skip more here // we just need JPA stuff so all the tricks we have for the server part are useless @SuppressWarnings("RedundantIfStatement") public static boolean isServerClass(final String name) { if (name == null) { return false; } for (final String prefix : URLClassLoaderFirst.FORCED_SKIP) { if (name.startsWith(prefix)) { return true; } } for (final String prefix : URLClassLoaderFirst.FORCED_LOAD) { if (name.startsWith(prefix)) { return false; } } if (name.startsWith("java.")) { return true; } if (name.startsWith("javax.")) { return true; } if (name.startsWith("sun.")) { return true; } if (name.startsWith("com.sun.")) { return true; } if (name.startsWith("org.")) { final String org = name.substring("org.".length()); if (org.startsWith("apache.")) { final String apache = org.substring("apache.".length()); if (apache.startsWith("bval.")) { return true; } if (apache.startsWith("openjpa.")) { return true; } if (apache.startsWith("derby.")) { return true; } if (apache.startsWith("xbean.")) { return true; } if (apache.startsWith("geronimo.")) { return true; } if (apache.startsWith("coyote")) { return true; } if (apache.startsWith("webbeans.")) { return true; } if (apache.startsWith("log4j")) { return true; } if (apache.startsWith("catalina")) { return true; } if (apache.startsWith("jasper.")) { return true; } if (apache.startsWith("tomcat.")) { return true; } if (apache.startsWith("el.")) { return true; } if (apache.startsWith("jsp")) { return true; } if (apache.startsWith("naming")) { return true; } if (apache.startsWith("taglibs.")) { return true; } if (apache.startsWith("openejb.")) { return true; } if (apache.startsWith("openjpa.")) { return true; } if (apache.startsWith("myfaces.")) { return true; } if (apache.startsWith("juli.")) { return true; } if (apache.startsWith("webbeans.")) { return true; } if (apache.startsWith("cxf.")) { return true; } if (apache.startsWith("activemq.")) { return true; } if (apache.startsWith("commons.")) { final String commons = apache.substring("commons.".length()); // don't stop on commons package since we don't bring all commons if (commons.startsWith("beanutils")) { return true; } if (commons.startsWith("cli")) { return true; } if (commons.startsWith("codec")) { return true; } if (commons.startsWith("collections")) { return true; } if (commons.startsWith("dbcp")) { return true; } if (commons.startsWith("digester")) { return true; } if (commons.startsWith("jocl")) { return true; } if (commons.startsWith("lang")) { return true; } if (commons.startsWith("logging")) { return false; } if (commons.startsWith("pool")) { return true; } if (commons.startsWith("net")) { return true; } return false; } return false; } // other org packages if (org.startsWith("codehaus.swizzle")) { return true; } if (org.startsWith("w3c.dom")) { return true; } if (org.startsWith("quartz")) { return true; } if (org.startsWith("eclipse.jdt.")) { return true; } if (org.startsWith("slf4j")) { return true; } if (org.startsWith("openejb")) { return true; // old packages } if (org.startsWith("hsqldb")) { return true; // old packages } if (org.startsWith("hibernate")) { return true; // old packages } return false; } // other packages if (name.startsWith("com.sun.")) { return true; } if (name.startsWith("jdk.")) { return true; } if (name.startsWith("javassist")) { return true; } if (name.startsWith("serp.")) { return true; } return false; } // JPA 2.0 /* (non-Javadoc) * @see javax.persistence.spi.PersistenceUnitInfo#getPersistenceXMLSchemaVersion() */ public String getPersistenceXMLSchemaVersion() { return this.persistenceXMLSchemaVersion; } /** * @param persistenceXMLSchemaVersion the persistenceXMLSchemaVersion to set */ public void setPersistenceXMLSchemaVersion(final String persistenceXMLSchemaVersion) { this.persistenceXMLSchemaVersion = persistenceXMLSchemaVersion; } /* (non-Javadoc) * @see javax.persistence.spi.PersistenceUnitInfo#getSharedCacheMode() */ public SharedCacheMode getSharedCacheMode() { return this.sharedCacheMode; } /** * @param sharedCacheMode the sharedCacheMode to set */ public void setSharedCacheMode(final SharedCacheMode sharedCacheMode) { this.sharedCacheMode = (null != sharedCacheMode ? sharedCacheMode : SharedCacheMode.UNSPECIFIED); } /* (non-Javadoc) * @see javax.persistence.spi.PersistenceUnitInfo#getValidationMode() */ public ValidationMode getValidationMode() { return this.validationMode; } /** * @param validationMode the validationMode to set */ public void setValidationMode(final ValidationMode validationMode) { this.validationMode = validationMode; } public String getJtaDataSourceName() { return jtaDataSourceName; } public void setJtaDataSourceName(final String jtaDataSourceName) { this.jtaDataSourceName = jtaDataSourceName; } public String getNonJtaDataSourceName() { return nonJtaDataSourceName; } public void setNonJtaDataSourceName(final String nonJtaDataSourceName) { this.nonJtaDataSourceName = nonJtaDataSourceName; } }