blob: 4f12be315f1ae15fdb1524042dffa69af7162bd8 [file] [log] [blame]
/*
* 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<>();
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;
@Override
public ClassLoader getClassLoader() {
return null;
}
@Override
public ClassLoader getNewTempClassLoader() {
return AccessController.doPrivileged(J2DoPrivHelper
.newTemporaryClassLoaderAction(AccessController
.doPrivileged(J2DoPrivHelper.getContextClassLoaderAction())));
}
@Override
public String getPersistenceUnitName() {
return _name;
}
public void setPersistenceUnitName(String emName) {
_name = emName;
}
@Override
public String getPersistenceProviderClassName() {
return _providerClassName;
}
public void setPersistenceProviderClassName(String providerClassName) {
_providerClassName = providerClassName;
}
@Override
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;
}
@Override
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;
}
@Override
public DataSource getNonJtaDataSource() {
return _nonJtaDataSource;
}
public void setNonJtaDataSource(DataSource ds) {
_nonJtaDataSource = ds;
if (ds != null)
_nonJtaDataSourceName = null;
}
@Override
public URL getPersistenceUnitRootUrl() {
return _root;
}
public void setPersistenceUnitRootUrl(URL root) {
_root = root;
}
@Override
public boolean excludeUnlistedClasses() {
return _excludeUnlisted;
}
public void setExcludeUnlistedClasses(boolean excludeUnlisted) {
_excludeUnlisted = excludeUnlisted;
}
@Override
public List<String> getMappingFileNames() {
if (_mappingFileNames == null)
return Collections.emptyList();
return _mappingFileNames;
}
public void addMappingFileName(String name) {
if (_mappingFileNames == null)
_mappingFileNames = new ArrayList<>();
_mappingFileNames.add(name);
}
@Override
public List<URL> getJarFileUrls() {
if (_jarFiles == null)
return Collections.emptyList();
return _jarFiles;
}
public void addJarFile(URL jar) {
if (_jarFiles == null)
_jarFiles = new ArrayList<>();
_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<>();
}
_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 (String s : cp) {
if (s.equals(name)
|| s.endsWith(File.separatorChar + name)) {
try {
addJarFile(AccessController
.doPrivileged(J2DoPrivHelper
.toURLAction(new File(s))));
return;
}
catch (PrivilegedActionException | MalformedURLException pae) {
break;
}
}
}
throw new IllegalArgumentException(s_loc.get("bad-jar-name", name).
getMessage());
}
@Override
public List<String> getManagedClassNames() {
if (_entityClassNames == null)
return Collections.emptyList();
return _entityClassNames;
}
public void addManagedClassName(String name) {
if (_entityClassNames == null)
_entityClassNames = new ArrayList<>();
_entityClassNames.add(name);
}
@Override
public Properties getProperties() {
Properties copy = new Properties();
copy.putAll(_props);
return copy;
}
public void setProperty(String key, String value) {
_props.put(key, value);
}
@Override
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<>();
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);
}
// --------------------
@Override
public File getSourceFile() {
if (_persistenceXmlFile == null)
return null;
try {
return new File(_persistenceXmlFile.toURI());
} catch (URISyntaxException e) {
throw new IllegalStateException(e);
}
}
@Override
public Object getSourceScope() {
return null;
}
@Override
public int getSourceType() {
return SRC_XML;
}
@Override
public int getLineNumber() {
return 0;
}
@Override
public int getColNumber() {
return 0;
}
@Override
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;
}
@Override
public ClassLoader getClassLoader(Class ctx, ClassLoader env) {
return _loader;
}
}
@Override
public String getPersistenceXMLSchemaVersion() {
return _schemaVersion;
}
public void setPersistenceXMLSchemaVersion(String version) {
_schemaVersion = version;
}
@Override
public ValidationMode getValidationMode() {
return _validationMode;
}
public void setValidationMode(ValidationMode mode) {
_validationMode = mode;
}
@Override
public SharedCacheMode getSharedCacheMode() {
return _sharedCacheMode;
}
public void setSharedCacheMode(SharedCacheMode mode) {
_sharedCacheMode = mode;
}
}