blob: 27a89fffab37e95588b7cdb3ad0d67be91d26c4b [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.
*/
//
// This source code implements specifications defined by the Java
// Community Process. In order to remain compliant with the specification
// DO NOT add / change / or delete method signatures!
//
package javax.persistence;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.spi.LoadState;
import javax.persistence.spi.PersistenceProvider;
import javax.persistence.spi.PersistenceProviderResolver;
import javax.persistence.spi.PersistenceProviderResolverHolder;
import javax.persistence.spi.ProviderUtil;
import org.apache.geronimo.osgi.locator.ProviderLocator;
/**
* Bootstrap class to obtain {@link javax.persistence.EntityManagerFactory}
* references.
*
* Contains Geronimo implemented code as required by the JPA spec.
*
* @version $Rev$ $Date$
*/
public class Persistence {
// The following variable is only here for TCK backward compatibility
@Deprecated
protected static final Set<PersistenceProvider> providers = new HashSet<PersistenceProvider>();
// The following string is only here for TCK backward compatibility
@Deprecated
public static final String PERSISTENCE_PROVIDER = "javax.persistence.spi.PeristenceProvider";
static final String PERSISTENCE_PROVIDER_PROPERTY = "javax.persistence.provider";
static final String PERSISTENCE_PROVIDER_SERVICE = "META-INF/services/"
+ PersistenceProvider.class.getName();
public static EntityManagerFactory createEntityManagerFactory(
String persistenceUnitName) {
return createEntityManagerFactory(persistenceUnitName, Collections.EMPTY_MAP);
}
/**
* Geronimo implementation specific code
*/
public static EntityManagerFactory createEntityManagerFactory(
String persistenceUnitName, Map properties) {
EntityManagerFactory factory = null;
Map props = properties;
if (props == null) {
props = Collections.EMPTY_MAP;
}
// get the discovered set of providers
List<PersistenceProvider> providers = getProviders();
/*
* Geronimo/OpenJPA 1.0 unique behavior - Start by loading a provider
* explicitly specified in the properties and return any exceptions.
* The spec doesn't forbid providers that aren't a service - it only
* states that they "should" be implemented as services in Sect. 9.2.
*
* For 2.0 - We only perform the above behavior if the specified
* provider is not in the discovered list.
*
* Note: This special non-spec defined case will rethrow any encountered
* Exceptions as a PersistenceException.
*/
Object propVal = props.get(PERSISTENCE_PROVIDER_PROPERTY);
if ((propVal != null) && (propVal instanceof String)) {
boolean isLoaded = false;
String providerName = propVal.toString();
// search the discovered providers for this explicit provider
for (PersistenceProvider provider : providers) {
if (provider.getClass().getName().compareTo(providerName) == 0) {
isLoaded = true;
break;
}
}
/*
* Only try to explicitly create this provider if we didn't
* find it as a service, while rethrowing any exceptions to
* match the old 1.0 behavior
*/
if (!isLoaded) {
factory = createFactory(
providerName.toString(),
persistenceUnitName,
props);
if (factory != null) {
return factory;
}
}
}
/*
* Now, the default JPA2 behavior of loading a provider from our resolver
*
* Note: Change in behavior from 1.0, which always returned exceptions:
* Spec states that a provider "must" return null if it
* cannot fulfill an EMF request, so that if we have more than one
* provider, then the other providers have a chance to return an EMF.
* Now, we will return any exceptions wrapped in a
* PersistenceException to match 1.0 behavior and provide more
* diagnostics to the end user.
*/
// capture any provider returned exceptions
Map<String, Throwable> exceptions = new HashMap<String, Throwable>();
// capture the provider names to use in the exception text if needed
StringBuffer foundProviders = null;
for (PersistenceProvider provider : providers) {
String providerName = provider.getClass().getName();
try {
factory = provider.createEntityManagerFactory(persistenceUnitName, props);
} catch (Exception e) {
// capture the exception details and give other providers a chance
exceptions.put(providerName, e);
}
if (factory != null) {
// we're done
return factory;
} else {
// update the list of providers we have tried
if (foundProviders == null) {
foundProviders = new StringBuffer(providerName);
} else {
foundProviders.append(", ");
foundProviders.append(providerName);
}
}
}
// make sure our providers list is initialized for the exceptions below
if (foundProviders == null) {
foundProviders = new StringBuffer("NONE");
}
/*
* Spec doesn't mention any exceptions thrown by this method if no emf
* returned, but old 1.0 behavior always generated an EMF or exception.
*/
if (exceptions.isEmpty()) {
// throw an exception with the PU name and providers we tried
throw new PersistenceException("No persistence providers available for \"" + persistenceUnitName +
"\" after trying the following discovered implementations: " + foundProviders);
} else {
// we encountered one or more exceptions, so format and throw as a single exception
throw createPersistenceException(
"Explicit persistence provider error(s) occurred for \"" + persistenceUnitName +
"\" after trying the following discovered implementations: " + foundProviders,
exceptions);
}
}
/*
* Geronimo/OpenJPA private helper code for PERSISTENCE_PROVIDER_PROPERTY
* @return EntityManagerFactory or null
* @throws PersistenceException
*/
private static EntityManagerFactory createFactory(String providerName,
String persistenceUnitName, Map properties)
throws PersistenceException {
Class<?> providerClass;
// get our class loader
ClassLoader cl = PrivClassLoader.get(null);
if (cl == null)
cl = PrivClassLoader.get(Persistence.class);
try {
providerClass = ProviderLocator.loadClass(providerName, Persistence.class, cl);
} catch (Exception e) {
throw new PersistenceException("Invalid or inaccessible explicit provider class: " +
providerName, e);
}
try {
PersistenceProvider provider = (PersistenceProvider) providerClass.newInstance();
return provider.createEntityManagerFactory(persistenceUnitName, properties);
} catch (Exception e) {
throw new PersistenceException("Explicit error returned from provider: " +
providerName + " for PU: " + persistenceUnitName, e);
}
}
/**
* Geronimo/OpenJPA private helper code for creating a PersistenceException
* @param msg String to use as the exception message
* @param failures Persistence provider exceptions to add to the exception message
* @return PersistenceException
*/
private static PersistenceException createPersistenceException(String msg, Map<String, Throwable> failures) {
String newline = System.getProperty("line.separator");
StringWriter strWriter = new StringWriter();
strWriter.append(msg);
if (failures.size() <= 1) {
// we caught an exception, so include it as the cause
Throwable t = null;
for (String providerName : failures.keySet()) {
t = failures.get(providerName);
strWriter.append(" from provider: ");
strWriter.append(providerName);
break;
}
return new PersistenceException(strWriter.toString(), t);
} else {
// we caught multiple exceptions, so format them into the message string and don't set a cause
strWriter.append(" with the following failures:");
strWriter.append(newline);
for (String providerName : failures.keySet()) {
strWriter.append(providerName);
strWriter.append(" returned: ");
failures.get(providerName).printStackTrace(new PrintWriter(strWriter));
}
strWriter.append(newline);
return new PersistenceException(strWriter.toString());
}
}
public static PersistenceUtil getPersistenceUtil() {
return new PersistenceUtilImpl();
}
public static void generateSchema(String persistenceUnitName, Map properties) {
final List<PersistenceProvider> providers = getProviders();
for (final PersistenceProvider provider : providers) {
if (provider.generateSchema( persistenceUnitName, properties)) {
return;
}
}
throw new PersistenceException("No provider for schema generation of unit '" + persistenceUnitName + "'");
}
private static List<PersistenceProvider> getProviders() {
// get the discovered set of providers
PersistenceProviderResolver resolver =
PersistenceProviderResolverHolder.getPersistenceProviderResolver();
// following will throw PersistenceExceptions for invalid services
return resolver.getPersistenceProviders();
}
/**
* Geronimo implementation specific code
*/
private static class PersistenceUtilImpl implements PersistenceUtil {
/**
* Determines the load state of the attribute of an entity
* @see javax.persistence.PersistenceUtil#isLoaded(java.lang.Object, java.lang.String)
*/
public boolean isLoaded(Object entity, String attributeName) {
boolean isLoaded = true;
// Get the list of persistence providers from the resolver
List<PersistenceProvider> pps = getProviders();
// Iterate through the list using ProviderUtil.isLoadedWithoutReference()
for (PersistenceProvider pp : pps) {
try {
ProviderUtil pu = pp.getProviderUtil();
LoadState ls = pu.isLoadedWithoutReference(entity, attributeName);
if (ls == LoadState.LOADED)
return true;
if (ls == LoadState.NOT_LOADED)
return false;
}
catch (Throwable t) {
// JPA 1.0 providers will not implement the getProviderUtil
// method. Eat the exception and try the next provider.
}
}
// Iterate through the list a second time using ProviderUtil.isLoadedWithReference()
for (PersistenceProvider pp : pps) {
try {
ProviderUtil pu = pp.getProviderUtil();
LoadState ls = pu.isLoadedWithReference(entity, attributeName);
if (ls == LoadState.LOADED)
return true;
if (ls == LoadState.NOT_LOADED)
return false;
}
catch (Throwable t) {
// JPA 1.0 providers will not implement the getProviderUtil
// method. Eat the exception and try the next provider.
}
}
// All providers returned a load state of unknown. Return true.
return true;
}
public boolean isLoaded(Object entity) {
// Get the list of persistence providers from the resolver
List<PersistenceProvider> pps = getProviders();
// Iterate through the list of providers, using ProviderUtil to
// determine the load state
for (PersistenceProvider pp : pps) {
try {
ProviderUtil pu = pp.getProviderUtil();
LoadState ls = pu.isLoaded(entity);
if (ls == LoadState.LOADED)
return true;
if (ls == LoadState.NOT_LOADED)
return false;
// Otherwise, load state is unknown. Query the next provider.
}
catch (Throwable t) {
// JPA 1.0 providers will not implement the getProviderUtil
// method. Eat the exception and try the next provider.
}
}
// All providers returned a load state of unknown. Return true.
return true;
}
}
/**
* Geronimo/OpenJPA private helper code for handling class loaders
*/
private static class PrivClassLoader implements PrivilegedAction<ClassLoader> {
private final Class<?> c;
public static ClassLoader get(Class<?> c) {
final PrivClassLoader action = new PrivClassLoader(c);
if (System.getSecurityManager() != null)
return AccessController.doPrivileged(action);
else
return action.run();
}
private PrivClassLoader(Class<?> c) {
this.c = c;
}
public ClassLoader run() {
if (c != null)
return c.getClassLoader();
else
return Thread.currentThread().getContextClassLoader();
}
}
}