blob: bca362c2f86432d8ce5513bd0245fd9916eb769e [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.io.IOException;
import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Set;
import javax.persistence.SharedCacheMode;
import javax.persistence.ValidationMode;
import javax.persistence.spi.PersistenceUnitInfo;
import javax.persistence.spi.PersistenceUnitTransactionType;
import org.apache.openjpa.conf.Compatibility;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.conf.OpenJPAConfigurationImpl;
import org.apache.openjpa.conf.OpenJPAProductDerivation;
import org.apache.openjpa.conf.Specification;
import org.apache.openjpa.datacache.DataCacheMode;
import org.apache.openjpa.kernel.MixedLockLevels;
import org.apache.openjpa.kernel.QueryHints;
import org.apache.openjpa.lib.conf.AbstractProductDerivation;
import org.apache.openjpa.lib.conf.Configuration;
import org.apache.openjpa.lib.conf.ConfigurationProvider;
import org.apache.openjpa.lib.conf.Configurations;
import org.apache.openjpa.lib.conf.MapConfigurationProvider;
import org.apache.openjpa.lib.conf.ProductDerivations;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.lib.meta.XMLMetaDataParser;
import org.apache.openjpa.lib.meta.XMLVersionParser;
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.lib.util.StringUtil;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
/**
* Sets JPA specification defaults and parses JPA specification XML files.
*
* For globals, looks in <code>openjpa.properties</code> system property for
* the location of a file to parse. If no system property is defined, the
* default resource location of <code>META-INF/openjpa.xml</code> is used.
*
* For defaults, looks for <code>META-INF/persistence.xml</code>.
* Within <code>persistence.xml</code>, look for the named persistence unit, or
* if no name given, an OpenJPA unit (preferring an unnamed OpenJPA unit to
* a named one).
*
* @author Abe White
*/
public class PersistenceProductDerivation
extends AbstractProductDerivation
implements OpenJPAProductDerivation {
public static final Specification SPEC_JPA = new Specification("jpa 2");
public static final Specification ALIAS_EJB = new Specification("ejb 3");
public static final String RSRC_GLOBAL = "META-INF/openjpa.xml";
public static final String RSRC_DEFAULT = "META-INF/persistence.xml";
public static final BigDecimal VERSION_1_0 = BigDecimal.valueOf(1.0);
private static final Localizer _loc = Localizer.forPackage
(PersistenceProductDerivation.class);
private HashMap<String, PUNameCollision> _puNameCollisions
= new HashMap<>();
public static final String PREFIX = "javax.persistence";
// These are properties that are invalid to be configured at the provider level.
private static final String[] _invalidPersistenceProperties =
new String[] { PREFIX + ".cache.storeMode", PREFIX + ".cache.retrieveMode" };
private static Set<String> _hints = new HashSet<>();
// Provider name to filter out PUs that don't belong to this derivation.
protected String _providerImplName;
static {
_hints.add("javax.persistence.lock.timeout");
_hints.add("javax.persistence.query.timeout");
_hints.add("openjpa.FetchPlan.ExtendedPathLookup");
_hints.add("openjpa.FetchBatchSize");
_hints.add("openjpa.FetchPlan.FetchBatchSize");
_hints.add("openjpa.MaxFetchDepth");
_hints.add("openjpa.FetchPlan.MaxFetchDepth");
_hints.add("openjpa.LockTimeout");
_hints.add("openjpa.FetchPlan.LockTimeout");
_hints.add("openjpa.QueryTimeout");
_hints.add("openjpa.FetchPlan.QueryTimeout");
_hints.add("openjpa.FlushBeforeQueries");
_hints.add("openjpa.FetchPlan.FlushBeforeQueries");
_hints.add("openjpa.ReadLockLevel");
_hints.add("openjpa.FetchPlan.ReadLockLevel");
_hints.add("openjpa.WriteLockLevel");
_hints.add("openjpa.FetchPlan.WriteLockLevel");
_hints.add("openjpa.FetchPlan.FetchBatchSize");
_hints.add("openjpa.FetchPlan.LockScope");
_hints.add("openjpa.FetchPlan.LockTimeout");
_hints.add("openjpa.FetchPlan.MaxFetchDepth");
_hints.add("openjpa.FetchPlan.QueryTimeout");
_hints.add("openjpa.FetchPlan.ReadLockMode");
_hints.add("openjpa.FetchPlan.WriteLockMode");
_hints.add(QueryHints.HINT_AGGREGATE_LISTENER);
_hints.add(QueryHints.HINT_AGGREGATE_LISTENERS);
_hints.add(QueryHints.HINT_FILTER_LISTENER);
_hints.add(QueryHints.HINT_FILTER_LISTENERS);
_hints.add(QueryHints.HINT_IGNORE_FINDER);
_hints.add(QueryHints.HINT_IGNORE_PREPARED_QUERY);
_hints.add(QueryHints.HINT_INVALIDATE_FINDER);
_hints.add(QueryHints.HINT_INVALIDATE_PREPARED_QUERY);
_hints.add(QueryHints.HINT_PARAM_MARKER_IN_QUERY);
_hints.add(QueryHints.HINT_RECACHE_FINDER);
_hints.add(QueryHints.HINT_RESULT_COUNT);
_hints.add(QueryHints.HINT_SUBCLASSES);
_hints.add(QueryHints.HINT_RELAX_BIND_PARAM_TYPE_CHECK);
_hints.add(QueryHints.HINT_USE_LITERAL_IN_SQL);
_hints = Collections.unmodifiableSet(_hints);
}
public PersistenceProductDerivation() {
_providerImplName = PersistenceProviderImpl.class.getName();
}
@Override
public void putBrokerFactoryAliases(Map<String, String> m) {
}
@Override
public int getType() {
return TYPE_SPEC;
}
@Override
public String getConfigurationPrefix() {
return PREFIX;
}
@Override
public Set<String> getSupportedQueryHints() {
return _hints;
}
@Override
public void validate()
throws Exception {
// make sure JPA is available
AccessController.doPrivileged(J2DoPrivHelper.getClassLoaderAction(
javax.persistence.EntityManagerFactory.class));
}
@Override
public boolean beforeConfigurationLoad(Configuration c) {
if (!(c instanceof OpenJPAConfigurationImpl))
return false;
OpenJPAConfigurationImpl conf = (OpenJPAConfigurationImpl) c;
conf.metaFactoryPlugin.setAlias(ALIAS_EJB.getName(), PersistenceMetaDataFactory.class.getName());
conf.metaFactoryPlugin.setAlias(SPEC_JPA.getName(), PersistenceMetaDataFactory.class.getName());
conf.addValue(new EntityManagerFactoryValue());
conf.readLockLevel.setAlias("optimistic", String.valueOf(MixedLockLevels.LOCK_OPTIMISTIC));
conf.readLockLevel.setAlias("optimistic-force-increment", String
.valueOf(MixedLockLevels.LOCK_OPTIMISTIC_FORCE_INCREMENT));
conf.readLockLevel.setAlias("pessimistic-read", String
.valueOf(MixedLockLevels.LOCK_PESSIMISTIC_READ));
conf.readLockLevel.setAlias("pessimistic-write", String
.valueOf(MixedLockLevels.LOCK_PESSIMISTIC_WRITE));
conf.readLockLevel.setAlias("pessimistic-force-increment", String
.valueOf(MixedLockLevels.LOCK_PESSIMISTIC_FORCE_INCREMENT));
conf.writeLockLevel.setAlias("optimistic", String
.valueOf(MixedLockLevels.LOCK_OPTIMISTIC));
conf.writeLockLevel.setAlias("optimistic-force-increment", String
.valueOf(MixedLockLevels.LOCK_OPTIMISTIC_FORCE_INCREMENT));
conf.writeLockLevel.setAlias("pessimistic-read", String
.valueOf(MixedLockLevels.LOCK_PESSIMISTIC_READ));
conf.writeLockLevel.setAlias("pessimistic-write", String
.valueOf(MixedLockLevels.LOCK_PESSIMISTIC_WRITE));
conf.writeLockLevel.setAlias("pessimistic-force-increment", String
.valueOf(MixedLockLevels.LOCK_PESSIMISTIC_FORCE_INCREMENT));
configureBeanValidation(conf);
conf.dataCacheMode = conf.addString(JPAProperties.CACHE_MODE);
conf.dataCacheMode.setDefault(DataCacheMode.UNSPECIFIED.toString());
conf.dataCacheMode.set(DataCacheMode.UNSPECIFIED.toString());
return true;
}
/**
* Bean Validation configuration is unusual because its usage of enums and keys that
* do not have counterparts in kernel.
* Hence the plugins are defined in product derivation instead of the kernel's
* core configuration.
*
* @param conf
*/
private void configureBeanValidation(OpenJPAConfigurationImpl conf) {
// Validation defines/adds the following plugins to OpenJPA Configuration
conf.validationFactory = conf.addObject(JPAProperties.VALIDATE_FACTORY);
conf.validator = conf.addObject("Validator");
conf.validationMode = conf.addString(JPAProperties.VALIDATE_MODE);
conf.validationGroupPrePersist = conf.addString(JPAProperties.VALIDATE_PRE_PERSIST);
conf.validationGroupPreUpdate = conf.addString(JPAProperties.VALIDATE_PRE_UPDATE);
conf.validationGroupPreRemove = conf.addString(JPAProperties.VALIDATE_PRE_REMOVE);
conf.validationMode.setDynamic(true);
String[] aliases = new String[] {
String.valueOf(ValidationMode.AUTO),
String.valueOf(ValidationMode.AUTO).toLowerCase(Locale.ENGLISH),
String.valueOf(ValidationMode.CALLBACK),
String.valueOf(ValidationMode.CALLBACK).toLowerCase(Locale.ENGLISH),
String.valueOf(ValidationMode.NONE),
String.valueOf(ValidationMode.NONE).toLowerCase(Locale.ENGLISH)
};
conf.validationMode.setAliases(aliases);
conf.validationMode.setAliasListComprehensive(true);
conf.validationMode.setDefault(aliases[0]);
conf.validationGroupPrePersist.setString(JPAProperties.VALIDATE_GROUP_DEFAULT);
conf.validationGroupPrePersist.setDefault("");
conf.validationGroupPrePersist.setDynamic(true);
conf.validationGroupPreUpdate.setString(JPAProperties.VALIDATE_GROUP_DEFAULT);
conf.validationGroupPreUpdate.setDefault("");
conf.validationGroupPreUpdate.setDynamic(true);
conf.validationGroupPreRemove.setDefault("");
conf.validationGroupPreRemove.setDynamic(true);
conf.validationFactory.setInstantiatingGetter("getValidationFactoryInstance");
conf.validationFactory.setDynamic(true);
conf.validator.setInstantiatingGetter("getValidatorInstance");
conf.validator.setDynamic(true);
conf.validator.makePrivate();
}
@Override
public boolean afterSpecificationSet(Configuration c) {
if (!OpenJPAConfigurationImpl.class.isInstance(c)
&& !SPEC_JPA.isSame(((OpenJPAConfiguration) c).getSpecification()))
return false;
OpenJPAConfigurationImpl conf = (OpenJPAConfigurationImpl) c;
conf.metaFactoryPlugin.setDefault(SPEC_JPA.getName());
conf.metaFactoryPlugin.setString(SPEC_JPA.getName());
conf.nontransactionalWrite.setDefault("true");
conf.nontransactionalWrite.set(true);
Specification spec = ((OpenJPAConfiguration) c).getSpecificationInstance();
int specVersion = spec.getVersion();
Compatibility compatibility = conf.getCompatibilityInstance();
spec.setCompatibility(compatibility);
if (specVersion < 2) {
compatibility.setFlushBeforeDetach(true);
compatibility.setCopyOnDetach(true);
compatibility.setPrivatePersistentProperties(true);
compatibility.setIgnoreDetachedStateFieldForProxySerialization(true);
// Disable bean validation for spec level < 2 configurations
conf.validationMode.set(String.valueOf(ValidationMode.NONE));
} else {
compatibility.setAbstractMappingUniDirectional(true);
compatibility.setNonDefaultMappingAllowed(true);
}
return true;
}
/**
* Load configuration from the given persistence unit with the specified
* user properties.
*/
public ConfigurationProvider load(PersistenceUnitInfo pinfo, Map m)
throws IOException {
if (pinfo == null)
return null;
if (!isOpenJPAPersistenceProvider(pinfo, null)) {
warnUnknownProvider(pinfo);
return null;
}
ConfigurationProviderImpl cp = new ConfigurationProviderImpl();
cp.addProperties(PersistenceUnitInfoImpl.toOpenJPAProperties(pinfo));
cp.addProperties(m);
if (pinfo instanceof PersistenceUnitInfoImpl) {
PersistenceUnitInfoImpl impl = (PersistenceUnitInfoImpl) pinfo;
if (impl.getPersistenceXmlFileUrl() != null)
cp.setSource(impl.getPersistenceXmlFileUrl().toString());
}
return cp;
}
/**
* Load configuration from the given resource and unit names, which may
* be null.
*/
public ConfigurationProvider load(String rsrc, String name, Map m)
throws IOException {
boolean explicit = !StringUtil.isEmpty(rsrc);
if (!explicit)
rsrc = RSRC_DEFAULT;
ConfigurationProviderImpl cp = new ConfigurationProviderImpl();
Boolean ret = load(cp, rsrc, name, m, null, explicit);
if (ret != null)
return (ret) ? cp : null;
if (explicit)
return null;
// persistence.xml does not exist; just load map
PersistenceUnitInfoImpl pinfo = new PersistenceUnitInfoImpl();
pinfo.fromUserProperties(m);
if (!isOpenJPAPersistenceProvider(pinfo, null)) {
warnUnknownProvider(pinfo);
return null;
}
cp.addProperties(pinfo.toOpenJPAProperties());
return cp;
}
@Override
public ConfigurationProvider load(String rsrc, String anchor,
ClassLoader loader)
throws IOException {
if (rsrc != null && !rsrc.endsWith(".xml"))
return null;
ConfigurationProviderImpl cp = new ConfigurationProviderImpl();
if (load(cp, rsrc, anchor, null, loader, true) == Boolean.TRUE)
return cp;
return null;
}
@Override
public ConfigurationProvider load(File file, String anchor)
throws IOException {
if (!file.getName().endsWith(".xml"))
return null;
ConfigurationParser parser = new ConfigurationParser(null);
parser.parse(file);
return load(findUnit((List<PersistenceUnitInfoImpl>)
parser.getResults(), anchor, null), null);
}
@Override
public String getDefaultResourceLocation() {
return RSRC_DEFAULT;
}
@Override
public List<String> getAnchorsInFile(File file) throws IOException {
ConfigurationParser parser = new ConfigurationParser(null);
try {
parser.parse(file);
return getUnitNames(parser);
} catch (IOException e) {
// not all configuration files are XML; return null if unparsable
return null;
}
}
private List<String> getUnitNames(ConfigurationParser parser) {
List<PersistenceUnitInfoImpl> units = parser.getResults();
List<String> names = new ArrayList<>();
for (PersistenceUnitInfoImpl unit : units){
String provider = unit.getPersistenceProviderClassName();
// Only add the PU name if the provider it is ours or not specified.
if (provider == null || provider.equals(_providerImplName)) {
names.add(unit.getPersistenceUnitName());
} else {
// Should trace something, but logging isn't configured yet.
// Swallow.
}
}
return names;
}
@Override
public List getAnchorsInResource(String resource) throws Exception {
ConfigurationParser parser = new ConfigurationParser(null);
try {
List results = new ArrayList();
ClassLoader loader = AccessController.doPrivileged(
J2DoPrivHelper.getContextClassLoaderAction());
List<URL> urls = getResourceURLs(resource, loader);
if (urls != null) {
for (URL url : urls) {
parser.parse(url);
results.addAll(getUnitNames(parser));
}
}
return results;
} catch (IOException e) {
// not all configuration files are XML; return null if unparsable
return null;
}
}
@Override
public ConfigurationProvider loadGlobals(ClassLoader loader)
throws IOException {
String[] prefixes = ProductDerivations.getConfigurationPrefixes();
String rsrc = null;
for (int i = 0; i < prefixes.length && StringUtil.isEmpty(rsrc); i++)
rsrc = AccessController.doPrivileged(J2DoPrivHelper
.getPropertyAction(prefixes[i] + ".properties"));
boolean explicit = !StringUtil.isEmpty(rsrc);
String anchor = null;
int idx = (!explicit) ? -1 : rsrc.lastIndexOf('#');
if (idx != -1) {
// separate name from <resrouce>#<name> string
if (idx < rsrc.length() - 1)
anchor = rsrc.substring(idx + 1);
rsrc = rsrc.substring(0, idx);
}
if (StringUtil.isEmpty(rsrc))
rsrc = RSRC_GLOBAL;
else if (!rsrc.endsWith(".xml"))
return null;
ConfigurationProviderImpl cp = new ConfigurationProviderImpl();
if (load(cp, rsrc, anchor, null, loader, explicit) == Boolean.TRUE)
return cp;
return null;
}
@Override
public ConfigurationProvider loadDefaults(ClassLoader loader)
throws IOException {
ConfigurationProviderImpl cp = new ConfigurationProviderImpl();
if (load(cp, RSRC_DEFAULT, null, null, loader, false) == Boolean.TRUE)
return cp;
return null;
}
/**
* This method checks to see if the provided <code>puName</code> was
* detected in multiple resources. If a collision is detected, a warning
* will be logged and this method will return <code>true</code>.
* <p>
*/
public boolean checkPuNameCollisions(Log logger,String puName){
PUNameCollision p = _puNameCollisions.get(puName);
if (p != null){
p.logCollision(logger);
return true;
}
return false;
}
private static List<URL> getResourceURLs(String rsrc, ClassLoader loader)
throws IOException {
Enumeration<URL> urls = null;
try {
urls = AccessController.doPrivileged(J2DoPrivHelper.getResourcesAction(loader, rsrc));
if (!urls.hasMoreElements()) {
if (!rsrc.startsWith("META-INF"))
urls = AccessController.doPrivileged(J2DoPrivHelper.getResourcesAction(loader, "META-INF/" + rsrc));
if (!urls.hasMoreElements())
return null;
}
} catch (PrivilegedActionException pae) {
throw (IOException) pae.getException();
}
return Collections.list(urls);
}
/**
* Looks through the resources at <code>rsrc</code> for a configuration
* file that matches <code>name</code> (or an unnamed one if
* <code>name</code> is <code>null</code>), and loads the XML in the
* resource into a new {@link PersistenceUnitInfo}. Then, applies the
* overrides in <code>m</code>.
*
* @return {@link Boolean#TRUE} if the resource was loaded, null if it
* does not exist, or {@link Boolean#FALSE} if it is not for OpenJPA
*/
private Boolean load(ConfigurationProviderImpl cp, String rsrc,
String name, Map m, ClassLoader loader, boolean explicit)
throws IOException {
ClassLoader contextLoader = null;
if (loader == null) {
contextLoader = AccessController.doPrivileged(J2DoPrivHelper.getContextClassLoaderAction());
loader = contextLoader;
}
List<URL> urls = getResourceURLs(rsrc, loader);
if (urls == null || urls.size() == 0)
return null;
ConfigurationParser parser = new ConfigurationParser(m);
PersistenceUnitInfoImpl pinfo = parseResources(parser, urls, name, loader);
if (pinfo == null) {
if (!explicit)
return Boolean.FALSE;
throw new MissingResourceException(_loc.get("missing-xml-config",
rsrc, String.valueOf(name)).getMessage(), getClass().getName(), rsrc);
} else if (!isOpenJPAPersistenceProvider(pinfo, loader)) {
if (!explicit) {
warnUnknownProvider(pinfo);
return Boolean.FALSE;
}
throw new MissingResourceException(_loc.get("unknown-provider",
rsrc, name, pinfo.getPersistenceProviderClassName()).
getMessage(), getClass().getName(), rsrc);
}
// Process jar-file references after confirming OpenJPA is the desired JPA provider.
if ( loader != contextLoader && loader instanceof MultiClassLoader) {
// combine the MultiClassLoader and set to the context
// so that it could be used in the jar validation
MultiClassLoader mutliClassLoader = (MultiClassLoader) loader;
contextLoader = (contextLoader != null) ? contextLoader
: AccessController.doPrivileged(J2DoPrivHelper.getContextClassLoaderAction());
mutliClassLoader.addClassLoader(contextLoader);
AccessController.doPrivileged(J2DoPrivHelper.setContextClassLoaderAction(mutliClassLoader));
}
pinfo.processJarFileNames();
if (contextLoader != null) {
//restore the context loader
AccessController.doPrivileged(J2DoPrivHelper.setContextClassLoaderAction(contextLoader));
}
cp.addProperties(pinfo.toOpenJPAProperties());
cp.setSource(pinfo.getPersistenceXmlFileUrl().toString());
return Boolean.TRUE;
}
/**
* Parse resources at the given location. Searches for a
* PersistenceUnitInfo with the requested name, or an OpenJPA unit if
* no name given (preferring an unnamed OpenJPA unit to a named one).
*/
private PersistenceUnitInfoImpl parseResources(ConfigurationParser parser,
List<URL> urls, String name, ClassLoader loader)
throws IOException {
List<PersistenceUnitInfoImpl> pinfos = new ArrayList<>();
for (URL url : urls) {
parser.parse(url);
pinfos.addAll((List<PersistenceUnitInfoImpl>) parser.getResults());
}
return findUnit(pinfos, name, loader);
}
/**
* Find the unit with the given name, or an OpenJPA unit if no name is
* given (preferring an unnamed OpenJPA unit to a named one).
*/
private PersistenceUnitInfoImpl findUnit(List<PersistenceUnitInfoImpl>
pinfos, String name, ClassLoader loader) {
PersistenceUnitInfoImpl ojpa = null;
PersistenceUnitInfoImpl result = null;
for (PersistenceUnitInfoImpl pinfo : pinfos) {
// found named unit?
if (name != null) {
if (name.equals(pinfo.getPersistenceUnitName())){
if (result != null){
this.addPuNameCollision(name, result.getPersistenceXmlFileUrl().toString(),
pinfo.getPersistenceXmlFileUrl().toString());
} else {
// Grab a ref to the pinfo that matches the name we're
// looking for. Keep going to look for duplicate pu names.
result = pinfo;
}
}
continue;
}
if (isOpenJPAPersistenceProvider(pinfo, loader)) {
// if no name given and found unnamed unit, return it.
// otherwise record as default unit unless we find a better match later
if (StringUtil.isEmpty(pinfo.getPersistenceUnitName()))
return pinfo;
if (ojpa == null)
ojpa = pinfo;
}
}
if(result!=null){
return result;
}
return ojpa;
}
/**
* Return whether the given persistence unit uses an OpenJPA provider.
*/
private static boolean isOpenJPAPersistenceProvider(PersistenceUnitInfo pinfo, ClassLoader loader) {
String provider = pinfo.getPersistenceProviderClassName();
if (StringUtil.isEmpty(provider) || PersistenceProviderImpl.class.getName().equals(provider))
return true;
if (loader == null)
loader = AccessController.doPrivileged(J2DoPrivHelper.getContextClassLoaderAction());
try {
if (PersistenceProviderImpl.class.isAssignableFrom(Class.forName(provider, false, loader)))
return true;
} catch (Throwable t) {
log(_loc.get("unloadable-provider", provider, t).getMessage());
return false;
}
return false;
}
/**
* Warn the user that we could only find an unrecognized persistence
* provider.
*/
private static void warnUnknownProvider(PersistenceUnitInfo pinfo) {
log(_loc.get("unrecognized-provider", pinfo.getPersistenceProviderClassName()).getMessage());
}
/**
* Log a message.
*/
private static void log(String msg) {
// at this point logging isn't configured yet
// START - ALLOW PRINT STATEMENTS
System.err.println(msg);
// STOP - ALLOW PRINT STATEMENTS
}
private void addPuNameCollision(String puName, String file1, String file2){
PUNameCollision pun = _puNameCollisions.get(puName);
if (pun != null){
pun.addCollision(file1, file2);
} else {
_puNameCollisions.put(puName, new PUNameCollision(puName, file1, file2));
}
}
/**
* Custom configuration provider.
*/
public static class ConfigurationProviderImpl
extends MapConfigurationProvider {
private String _source;
public ConfigurationProviderImpl() {
}
public ConfigurationProviderImpl(Map props) {
super(props);
}
/**
* Set the source of information in this provider.
*/
public void setSource(String source) {
_source = source;
}
@Override
public void setInto(Configuration conf) {
if (conf instanceof OpenJPAConfiguration) {
OpenJPAConfiguration oconf = (OpenJPAConfiguration) conf;
Object persistenceVersion = getProperties().get(PersistenceUnitInfoImpl.PERSISTENCE_VERSION);
if (persistenceVersion == null) {
oconf.setSpecification(SPEC_JPA);
} else {
// Set the spec level based on the persistence version
oconf.setSpecification("jpa " + persistenceVersion.toString());
}
// we merge several persistence.xml elements into the
// MetaDataFactory property implicitly. if the user has a
// global openjpa.xml with this property set, its value will
// get overwritten by our implicit setting. so instead, combine
// the global value with our settings
String orig = oconf.getMetaDataFactory();
if (!StringUtil.isEmpty(orig)) {
String key = ProductDerivations.getConfigurationKey("MetaDataFactory", getProperties());
Object override = getProperties().get(key);
if (override instanceof String)
addProperty(key, Configurations.combinePlugins(orig, (String) override));
}
}
super.setInto(conf, null);
// At this point user properties have been loaded into the configuration. Apply any modifications based off
// those.
if (conf instanceof OpenJPAConfiguration) {
OpenJPAConfiguration oconf = (OpenJPAConfiguration) conf;
String dataCache = oconf.getDataCache();
String sharedDataCacheMode = oconf.getDataCacheMode();
if (DataCacheMode.NONE.toString().equals(sharedDataCacheMode)
&& dataCache != null && !"false".equals(dataCache)) {
Log log = conf.getConfigurationLog();
if (log.isWarnEnabled()) {
log.warn(_loc.get("shared-cache-mode-take-precedence", dataCache));
}
}
if ((dataCache == null || "false".equals(dataCache))
&& !DataCacheMode.NONE.toString().equals(sharedDataCacheMode)
&& !DataCacheMode.UNSPECIFIED.toString().equals(sharedDataCacheMode)){
oconf.setDataCache("true");
}
// If the datacache is enabled, make sure we have a RemoteCommitProvider
String rcp = oconf.getRemoteCommitProvider();
dataCache = oconf.getDataCache();
// If the datacache is set and is something other than false
if (dataCache != null && !"false".equals(dataCache)) {
// If RCP is null or empty, set it to sjvm.
if (rcp == null || "".equals(rcp)) {
oconf.setRemoteCommitProvider("sjvm");
}
}
}
Log log = conf.getConfigurationLog();
if (log.isWarnEnabled()) {
Map<?, ?> props = getProperties();
for (String propKey : _invalidPersistenceProperties) {
Object propValue = props.get(propKey);
if (propValue != null) {
log.warn(_loc.get("invalid-persistence-property", new Object[] { propKey, propValue }));
}
}
}
if (log.isTraceEnabled()) {
String src = (_source == null) ? "?" : _source;
log.trace(_loc.get("conf-load", src, getProperties()));
}
}
}
/**
* SAX handler capable of parsing an JPA persistence.xml file.
* Package-protected for testing.
*/
public static class ConfigurationParser
extends XMLMetaDataParser {
private static final String PERSISTENCE_XSD_2_0 = "persistence_2_0.xsd";
private static final String PERSISTENCE_XSD_2_1 = "persistence_2_1.xsd";
private static final Localizer _loc = Localizer.forPackage
(ConfigurationParser.class);
private final Map _map;
private PersistenceUnitInfoImpl _info = null;
private URL _source = null;
private String _persistenceVersion;
private String _schemaLocation;
private boolean _excludeUnlistedSet = false;
public ConfigurationParser(Map map) {
_map = map;
setCaching(false);
setValidating(true);
setParseText(true);
}
@Override
public void parse(URL url)
throws IOException {
_source = url;
// peek at the doc to determine the version
XMLVersionParser vp = new XMLVersionParser("persistence");
try {
vp.parse(url);
_persistenceVersion = vp.getVersion();
_schemaLocation = vp.getSchemaLocation();
} catch (Throwable t) {
log(_loc.get("version-check-error",
_source.toString()).toString());
}
super.parse(url);
}
@Override
public void parse(File file)
throws IOException {
try {
_source = AccessController.doPrivileged(J2DoPrivHelper
.toURLAction(file));
} catch (PrivilegedActionException pae) {
throw (MalformedURLException) pae.getException();
}
// peek at the doc to determine the version
XMLVersionParser vp = new XMLVersionParser("persistence");
try {
vp.parse(file);
_persistenceVersion = vp.getVersion();
_schemaLocation = vp.getSchemaLocation();
} catch (Throwable t) {
log(_loc.get("version-check-error", _source.toString()).toString());
}
super.parse(file);
}
@Override
protected Object getSchemaSource() {
// use the version 1 schema by default. non-versioned docs will
// continue to parse with the old xml if they do not contain a
// persistence-unit. that is currently the only significant change
// to the schema. if more significant changes are made in the
// future, the 2.0 schema may be preferable.
String persistencexsd = "persistence-xsd.rsrc";
// if the version and/or schema location is for 1.0, use the 1.0
// schema
if (XMLVersionParser.VERSION_2_0.equals(_persistenceVersion)
|| (_schemaLocation != null && _schemaLocation.indexOf(PERSISTENCE_XSD_2_0) != -1))
{
persistencexsd = "persistence_2_0-xsd.rsrc";
} else if (XMLVersionParser.VERSION_2_1.equals(_persistenceVersion)
|| (_schemaLocation != null && _schemaLocation.indexOf(PERSISTENCE_XSD_2_1) != -1))
{
persistencexsd = "persistence_2_1-xsd.rsrc";
}
return getClass().getResourceAsStream(persistencexsd);
}
@Override
protected void reset() {
super.reset();
_info = null;
_source = null;
_excludeUnlistedSet = false;
}
@Override
protected boolean startElement(String name, Attributes attrs)
throws SAXException {
if (currentDepth() == 1)
startPersistenceUnit(attrs);
else if (currentDepth() == 3 && "property".equals(name))
_info.setProperty(attrs.getValue("name"), attrs.getValue("value"));
return true;
}
@Override
protected void endElement(String name)
throws SAXException {
if (currentDepth() == 1) {
endPersistenceUnit();
_info.fromUserProperties(_map);
addResult(_info);
}
if (currentDepth() != 2)
return;
switch (name.charAt(0)) {
// cases 'name' and 'transaction-type' are handled in
// startPersistenceUnit()
// case 'property' for 'properties' is handled in startElement()
case 'c': // class
_info.addManagedClassName(currentText());
break;
case 'e': // exclude-unlisted-classes
setExcludeUnlistedClasses(currentText());
break;
case 'j':
if ("jta-data-source".equals(name))
_info.setJtaDataSourceName(currentText());
else { // jar-file
try {
_info.addJarFileName(currentText());
} catch (IllegalArgumentException iae) {
throw getException(iae.getMessage());
}
}
break;
case 'm': // mapping-file
_info.addMappingFileName(currentText());
break;
case 'n': // non-jta-data-source
_info.setNonJtaDataSourceName(currentText());
break;
case 'p':
if ("provider".equals(name))
_info.setPersistenceProviderClassName(currentText());
break;
case 's' : // shared-cache-mode
_info.setSharedCacheMode(JPAProperties.getEnumValue(SharedCacheMode.class, currentText()));
break;
case 'v': // validation-mode
_info.setValidationMode(JPAProperties.getEnumValue(ValidationMode.class, currentText()));
break;
}
}
// The default value for exclude-unlisted-classes was
// modified in JPA 2.0 from false to true. Set the default
// based upon the persistence version to preserve behavior
// of pre-JPA 2.0 applications.
private void setExcludeUnlistedClasses(String value) {
if (!_excludeUnlistedSet) {
BigDecimal version = getPersistenceVersion();
boolean excludeUnlisted;
if (version.compareTo(VERSION_1_0) > 0) {
excludeUnlisted = !("false".equalsIgnoreCase(value));
} else {
excludeUnlisted = "true".equalsIgnoreCase(value);
}
_info.setExcludeUnlistedClasses(excludeUnlisted);
_excludeUnlistedSet = true;
}
}
/**
* Parse persistence-unit element.
*/
private void startPersistenceUnit(Attributes attrs)
throws SAXException {
_excludeUnlistedSet = false;
_info = new PersistenceUnitInfoImpl();
_info.setPersistenceUnitName(attrs.getValue("name"));
_info.setPersistenceXMLSchemaVersion(_persistenceVersion);
// we only parse this ourselves outside a container, so default
// transaction type to local
String val = attrs.getValue("transaction-type");
if (val == null)
_info.setTransactionType(PersistenceUnitTransactionType.RESOURCE_LOCAL);
else
_info.setTransactionType(Enum.valueOf(PersistenceUnitTransactionType.class, val));
if (_source != null)
_info.setPersistenceXmlFileUrl(_source);
}
private void endPersistenceUnit() {
if (!_excludeUnlistedSet) {
setExcludeUnlistedClasses(null);
}
}
private BigDecimal getPersistenceVersion() {
if (_info.getPersistenceXMLSchemaVersion() != null) {
try {
return new BigDecimal(_info.getPersistenceXMLSchemaVersion());
}
catch (Throwable t) {
log(_loc.get("invalid-version-attribute",
_info.getPersistenceXMLSchemaVersion(),
VERSION_1_0.toString()).toString());
}
}
// OpenJPA supports persistence files without a version attribute.
// A persistence file without a version attribute will be considered
// a version 1.0 persistence file by default to maintain backward
// compatibility.
return VERSION_1_0;
}
}
/**
* This private class is used to hold onto information regarding
* PersistentUnit name collisions.
*/
private static class PUNameCollision{
private String _puName;
private Set<String> _resources;
PUNameCollision(String puName, String file1, String file2) {
_resources = new LinkedHashSet<>();
_resources.add(file1);
_resources.add(file2);
_puName=puName;
}
void logCollision(Log logger){
if(logger.isWarnEnabled()){
logger.warn(_loc.getFatal("dup-pu", new Object[]{_puName,_resources.toString(),
_resources.iterator().next()}));
}
}
void addCollision(String file1, String file2){
_resources.add(file1);
_resources.add(file2);
}
}
}