| /* |
| * 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<>(); |
| |
| /** |
| * The jar file locations that make up this persistence unit. |
| */ |
| private final List<URL> jarFileUrls = new ArrayList<>(); |
| |
| /** |
| * 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<>(); |
| |
| /** |
| * 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 input) { |
| |
| String name = input; |
| |
| if (name == null) { |
| return false; |
| } |
| |
| if (name.startsWith("openejb.shade.")) { |
| name = name.substring("openejb.shade.".length()); |
| } |
| |
| 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("jakarta.")) { |
| 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; |
| } |
| } |