/* * 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.openjpa.persistence; import java.io.File; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.net.URLDecoder; import java.security.AccessController; import java.security.PrivilegedActionException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; 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 org.apache.openjpa.lib.conf.Configuration; import org.apache.openjpa.lib.conf.Configurations; import org.apache.openjpa.lib.conf.ProductDerivations; import org.apache.openjpa.lib.meta.SourceTracker; import org.apache.openjpa.lib.util.J2DoPrivHelper; import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.lib.util.MultiClassLoader; import org.apache.openjpa.util.ClassResolver; /** * Implementation of the {@link PersistenceUnitInfo} interface used by OpenJPA * when parsing persistence configuration information. * */ public class PersistenceUnitInfoImpl implements PersistenceUnitInfo, SourceTracker { public static final String PERSISTENCE_VERSION = "PersistenceVersion"; private static final Localizer s_loc = Localizer.forPackage (PersistenceUnitInfoImpl.class); private String _name; private final HashMap<Object,Object> _props = new HashMap<Object, Object>(); private PersistenceUnitTransactionType _transType = PersistenceUnitTransactionType.RESOURCE_LOCAL; private String _providerClassName; private List<String> _mappingFileNames; private List<String> _entityClassNames; private List<URL> _jarFiles; private List<String> _jarFileNames; private String _jtaDataSourceName; private DataSource _jtaDataSource; private String _nonJtaDataSourceName; private DataSource _nonJtaDataSource; private boolean _excludeUnlisted; private URL _persistenceXmlFile; private String _schemaVersion = "1.0"; private ValidationMode _validationMode; private SharedCacheMode _sharedCacheMode; // A persistence unit is defined by a persistence.xml file. The jar // file or directory whose META-INF directory contains the // persistence.xml file is termed the root of the persistence unit. // // In Java EE, the root of a persistence unit may be one of the following: // - an EJB-JAR file // - the WEB-INF/classes directory of a WAR file[38] // - a jar file in the WEB-INF/lib directory of a WAR file // - a jar file in the root of the EAR // - a jar file in the EAR library directory // - an application client jar file private URL _root; public ClassLoader getClassLoader() { return null; } public ClassLoader getNewTempClassLoader() { return AccessController.doPrivileged(J2DoPrivHelper .newTemporaryClassLoaderAction(AccessController .doPrivileged(J2DoPrivHelper.getContextClassLoaderAction()))); } public String getPersistenceUnitName() { return _name; } public void setPersistenceUnitName(String emName) { _name = emName; } public String getPersistenceProviderClassName() { return _providerClassName; } public void setPersistenceProviderClassName(String providerClassName) { _providerClassName = providerClassName; } public PersistenceUnitTransactionType getTransactionType() { return _transType; } public void setTransactionType(PersistenceUnitTransactionType transType) { _transType = transType; } public String getJtaDataSourceName() { return _jtaDataSourceName; } public void setJtaDataSourceName(String jta) { _jtaDataSourceName = jta; if (jta != null) _jtaDataSource = null; } public DataSource getJtaDataSource() { return _jtaDataSource; } public void setJtaDataSource(DataSource ds) { _jtaDataSource = ds; if (ds != null) _jtaDataSourceName = null; } public String getNonJtaDataSourceName() { return _nonJtaDataSourceName; } public void setNonJtaDataSourceName(String nonJta) { _nonJtaDataSourceName = nonJta; if (nonJta != null) _nonJtaDataSource = null; } public DataSource getNonJtaDataSource() { return _nonJtaDataSource; } public void setNonJtaDataSource(DataSource ds) { _nonJtaDataSource = ds; if (ds != null) _nonJtaDataSourceName = null; } public URL getPersistenceUnitRootUrl() { return _root; } public void setPersistenceUnitRootUrl(URL root) { _root = root; } public boolean excludeUnlistedClasses() { return _excludeUnlisted; } public void setExcludeUnlistedClasses(boolean excludeUnlisted) { _excludeUnlisted = excludeUnlisted; } public List<String> getMappingFileNames() { if (_mappingFileNames == null) return Collections.emptyList(); return _mappingFileNames; } public void addMappingFileName(String name) { if (_mappingFileNames == null) _mappingFileNames = new ArrayList<String>(); _mappingFileNames.add(name); } public List<URL> getJarFileUrls() { if (_jarFiles == null) return Collections.emptyList(); return _jarFiles; } public void addJarFile(URL jar) { if (_jarFiles == null) _jarFiles = new ArrayList<URL>(); _jarFiles.add(jar); } public void addJarFileName(String name) { // Defer searching the classpath for jar files referenced by the jar-file element until after // the XML has been parsed and it has been confirmed that OpenJPA is the desired JPA provider. if (_jarFileNames == null) { _jarFileNames = new ArrayList<String>(); } _jarFileNames.add(name); } /** * Process jar-file elements. An IllegalArgumentException may be thrown if the jar file does not exist in the * classpath. */ public void processJarFileNames() { if (_jarFileNames != null) { for (String name : _jarFileNames) { validateJarFileName(name); } _jarFileNames.clear(); } } public void validateJarFileName(String name) { ClassLoader contextClassLoader = AccessController.doPrivileged(J2DoPrivHelper.getContextClassLoaderAction()); MultiClassLoader loader = AccessController .doPrivileged(J2DoPrivHelper.newMultiClassLoaderAction()); loader.addClassLoader(contextClassLoader); loader.addClassLoader(getClass().getClassLoader()); loader.addClassLoader(MultiClassLoader.THREAD_LOADER); URL url = AccessController.doPrivileged( J2DoPrivHelper.getResourceAction(loader, name)); if (url != null) { addJarFile(url); return; } // jar file is not a resource; check classpath String classPath = null; //first check if the classpath is set from ant class loader if (contextClassLoader instanceof MultiClassLoader) { for (ClassLoader classLoader : ((MultiClassLoader) contextClassLoader).getClassLoaders()){ try { Method getClassPathMethod = classLoader.getClass().getMethod("getClasspath", new Class[]{}); classPath = (String) getClassPathMethod.invoke(classLoader, new Object[]{}); if (classPath != null) break; } catch (Exception e) { //do nothing } } } if (classPath == null) { classPath = AccessController.doPrivileged( J2DoPrivHelper.getPropertyAction("java.class.path")); } String[] cp = classPath.split(J2DoPrivHelper.getPathSeparator()); for (int i = 0; i < cp.length; i++) { if (cp[i].equals(name) || cp[i].endsWith(File.separatorChar + name)) { try { addJarFile(AccessController .doPrivileged(J2DoPrivHelper .toURLAction(new File(cp[i])))); return; } catch (PrivilegedActionException pae) { break; } catch (MalformedURLException mue) { break; } } } throw new IllegalArgumentException(s_loc.get("bad-jar-name", name). getMessage()); } public List<String> getManagedClassNames() { if (_entityClassNames == null) return Collections.emptyList(); return _entityClassNames; } public void addManagedClassName(String name) { if (_entityClassNames == null) _entityClassNames = new ArrayList<String>(); _entityClassNames.add(name); } public Properties getProperties() { Properties copy = new Properties(); copy.putAll(_props); return copy; } public void setProperty(String key, String value) { _props.put(key, value); } public void addTransformer(ClassTransformer transformer) { throw new UnsupportedOperationException(); } /** * The location of the persistence.xml resource. May be null. */ public URL getPersistenceXmlFileUrl() { return _persistenceXmlFile; } /** * The location of the persistence.xml resource. May be null. */ public void setPersistenceXmlFileUrl(URL url) { _persistenceXmlFile = url; } /** * Load the given user-supplied map of properties into this persistence * unit. */ public void fromUserProperties(Map map) { if (map == null) return; Object key; Object val; for (Object o : map.entrySet()) { key = ((Map.Entry) o).getKey(); val = ((Map.Entry) o).getValue(); if (JPAProperties.PROVIDER.equals(key)) setPersistenceProviderClassName((String) val); else if (JPAProperties.TRANSACTION_TYPE.equals(key)) { setTransactionType(JPAProperties.getEnumValue(PersistenceUnitTransactionType.class, val)); } else if (JPAProperties.DATASOURCE_JTA.equals(key)) { if (val instanceof String) { setJtaDataSourceName((String) val); } else { setJtaDataSource((DataSource) val); } } else if (JPAProperties.DATASOURCE_NONJTA.equals(key)) { if (val instanceof String) { setNonJtaDataSourceName((String) val); } else { setNonJtaDataSource((DataSource) val); } } else if (JPAProperties.VALIDATE_MODE.equals(key)) { setValidationMode(JPAProperties.getEnumValue(ValidationMode.class, val)); } else if (JPAProperties.CACHE_MODE.equals(key)) { setSharedCacheMode(JPAProperties.getEnumValue(SharedCacheMode.class, val)); } else { _props.put(key, val); } } } /** * Return a {@link Map} containing the properties necessary to create * a {@link Configuration} that reflects the information in this * persistence unit info. */ public Map toOpenJPAProperties() { return toOpenJPAProperties(this); } /** * Return a {@link Map} containing the properties necessary to create * a {@link Configuration} that reflects the information in the given * persistence unit info. */ public static Map toOpenJPAProperties(PersistenceUnitInfo info) { Map map = new HashMap<String,Object>(); Set<String> added = new HashSet<String>(); if (info.getTransactionType() == PersistenceUnitTransactionType.JTA) replaceAsOpenJPAProperty(map, added, "TransactionMode", "managed"); boolean hasJta = false; DataSource ds = info.getJtaDataSource(); if (ds != null) { replaceAsOpenJPAProperty(map, added, "ConnectionFactory", ds); replaceAsOpenJPAProperty(map, added, "ConnectionFactoryMode", "managed"); hasJta = true; } else if (info instanceof PersistenceUnitInfoImpl && ((PersistenceUnitInfoImpl) info).getJtaDataSourceName() != null){ replaceAsOpenJPAProperty(map, added, "ConnectionFactoryName", ((PersistenceUnitInfoImpl)info).getJtaDataSourceName()); replaceAsOpenJPAProperty(map, added, "ConnectionFactoryMode", "managed"); hasJta = true; } ds = info.getNonJtaDataSource(); if (ds != null) { replaceAsOpenJPAProperty(map, added, hasJta ? "ConnectionFactory2" : "ConnectionFactory", ds); } else if (info instanceof PersistenceUnitInfoImpl && ((PersistenceUnitInfoImpl) info).getNonJtaDataSourceName() != null) { String nonJtaName = ((PersistenceUnitInfoImpl) info).getNonJtaDataSourceName(); replaceAsOpenJPAProperty(map, added, hasJta ? "ConnectionFactory2Name" : "ConnectionFactoryName", nonJtaName); } if (info.getClassLoader() != null) replaceAsOpenJPAProperty(map, added, "ClassResolver", new ClassResolverImpl(info.getClassLoader())); Properties props = info.getProperties(); if (props != null) { // remove any of the things that were set above for (String key : added) { if (Configurations.containsProperty(key, props)) Configurations.removeProperty(key, props); } // add all the non-conflicting props in the <properties> section map.putAll(props); // this isn't a real config property; remove it map.remove(PersistenceProviderImpl.CLASS_TRANSFORMER_OPTIONS); } if (!Configurations.containsProperty("Id", map)) map.put("openjpa.Id", info.getPersistenceUnitName()); Properties metaFactoryProps = new Properties(); if (info.getManagedClassNames() != null && !info.getManagedClassNames().isEmpty()) { StringBuilder types = new StringBuilder(); for (String type : info.getManagedClassNames()) { if (types.length() > 0) types.append(';'); types.append(type); } metaFactoryProps.put("Types", types.toString()); } if (info.getJarFileUrls() != null && !info.getJarFileUrls().isEmpty() || (!info.excludeUnlistedClasses() && info.getPersistenceUnitRootUrl() != null)) { StringBuilder jars = new StringBuilder(); String file = null; if (!info.excludeUnlistedClasses() && info.getPersistenceUnitRootUrl() != null) { URL url = info.getPersistenceUnitRootUrl(); if ("file".equals(url.getProtocol())) // exploded jar? file = URLDecoder.decode(url.getPath()); else jars.append(url); } for (URL jar : info.getJarFileUrls()) { if (jars.length() > 0) jars.append(';'); jars.append(jar); } if (file != null) metaFactoryProps.put("Files", file); if (jars.length() != 0) metaFactoryProps.put("URLs", jars.toString()); } if (info.getMappingFileNames() != null && !info.getMappingFileNames().isEmpty()) { StringBuilder rsrcs = new StringBuilder(); for (String rsrc : info.getMappingFileNames()) { if (rsrcs.length() > 0) rsrcs.append(';'); rsrcs.append(rsrc); } metaFactoryProps.put("Resources", rsrcs.toString()); } // set persistent class locations as properties of metadata factory, // combining them with any existing metadata factory props if (!metaFactoryProps.isEmpty()) { String key = ProductDerivations.getConfigurationKey ("MetaDataFactory", map); map.put(key, Configurations.combinePlugins((String) map.get(key), Configurations.serializeProperties(metaFactoryProps))); } // always record provider name for product derivations to access if (info.getPersistenceProviderClassName() != null) map.put(JPAProperties.PROVIDER, info.getPersistenceProviderClassName()); // convert validation-mode enum to a StringValue if (info.getValidationMode() != null) map.put(JPAProperties.VALIDATE_MODE, info.getValidationMode()); if (info.getPersistenceXMLSchemaVersion() != null) { map.put(PERSISTENCE_VERSION, info.getPersistenceXMLSchemaVersion()); } if (info.getSharedCacheMode() != null) { map.put(JPAProperties.CACHE_MODE, info.getSharedCacheMode()); } return map; } /** * Adds the given key-val to the given map after adding "openjpa." prefix to the key. * Tracks this addition in the given set of added keys. */ private static void replaceAsOpenJPAProperty(Map map, Set<String> added, String key, Object val) { map.put("openjpa." + key, val); added.add(key); } // -------------------- public File getSourceFile() { if (_persistenceXmlFile == null) return null; try { return new File(_persistenceXmlFile.toURI()); } catch (URISyntaxException e) { throw new IllegalStateException(e); } } public Object getSourceScope() { return null; } public int getSourceType() { return SRC_XML; } public int getLineNumber() { return 0; } public int getColNumber() { return 0; } public String getResourceName() { return "PersistenceUnitInfo:" + _name; } /** * Simple class resolver built around the persistence unit loader. */ public static class ClassResolverImpl implements ClassResolver { private final ClassLoader _loader; public ClassResolverImpl(ClassLoader loader) { _loader = loader; } public ClassLoader getClassLoader(Class ctx, ClassLoader env) { return _loader; } } public String getPersistenceXMLSchemaVersion() { return _schemaVersion; } public void setPersistenceXMLSchemaVersion(String version) { _schemaVersion = version; } public ValidationMode getValidationMode() { return _validationMode; } public void setValidationMode(ValidationMode mode) { _validationMode = mode; } public SharedCacheMode getSharedCacheMode() { return _sharedCacheMode; } public void setSharedCacheMode(SharedCacheMode mode) { _sharedCacheMode = mode; } }