blob: e5671e6c19af9ea046f07d3d55ae4977377d1bef [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2018 IBM Corporation. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// tware, ssmith = 1.0 - Generic JPA deployment (OSGI, EE, SE)
// 11/04/2014 - Rick Curtis
// - 450010 : Add java se test bucket
// 08/29/2016 Jody Grassel
// - 500441: Eclipselink core has System.getProperty() calls that are not potentially executed under doPriv()
package org.eclipse.persistence.internal.jpa.deployment;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import jakarta.persistence.spi.ClassTransformer;
import jakarta.persistence.spi.PersistenceUnitInfo;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.internal.jpa.EntityManagerFactoryProvider;
import org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.jpa.Archive;
import org.eclipse.persistence.jpa.PersistenceProvider;
import org.eclipse.persistence.logging.AbstractSessionLog;
import org.eclipse.persistence.logging.SessionLog;
/**
* Base class for all JPA initialization classes. This is an abstract class that provides the framework
* for JPA initialization (finding and initializing persistence units). Subclasses implement the abstract methods
* to provide customized functionality
*
* @see JavaSECMPInitializer
* @author tware
*
*/
public abstract class JPAInitializer {
// The internal loader is used by applications that do weaving to pre load classes
// When this flag is set to false, we will not be able to weave.
protected boolean shouldCreateInternalLoader = true;
protected ClassLoader initializationClassloader = null;
// Cache the initial puInfos - those used by initialEmSetupImpls
protected Map<String, SEPersistenceUnitInfo> initialPuInfos;
// Cache the initial emSetupImpls - those created and predeployed by JavaSECMPInitializer.initialize method.
protected Map<String, EntityManagerSetupImpl> initialEmSetupImpls;
// Initializers keyed by their initializationClassloaders
protected static Map<ClassLoader, JPAInitializer> initializers = new Hashtable<>();
protected JPAInitializer() {
}
/**
* Initialize the logging file if it is specified by the system property.
*/
public static void initializeTopLinkLoggingFile() {
String loggingFile = PrivilegedAccessHelper.getSystemProperty(PersistenceUnitProperties.LOGGING_FILE);
try {
if (loggingFile != null) {
AbstractSessionLog.getLog().setWriter(new FileWriter(loggingFile));
}
} catch (IOException e) {
AbstractSessionLog.getLog().log(SessionLog.WARNING, "cmp_init_default_logging_file_is_invalid",loggingFile,e);
}
}
/**
* predeploy (with deploy) is one of the two steps required in deployment of entities
* This method will prepare to call predeploy, call it and finally register the
* transformer returned to be used for weaving.
*/
public EntityManagerSetupImpl callPredeploy(SEPersistenceUnitInfo persistenceUnitInfo, Map m, String persistenceUnitUniqueName, String sessionName) {
AbstractSessionLog.getLog().log(SessionLog.FINER, SessionLog.JPA, "cmp_init_invoke_predeploy", persistenceUnitInfo.getPersistenceUnitName());
Map mergedProperties = EntityManagerFactoryProvider.mergeMaps(m, persistenceUnitInfo.getProperties());
// Bug#4452468 When globalInstrumentation is null, there is no weaving
checkWeaving(mergedProperties);
Set<String> tempLoaderSet = PersistenceUnitProcessor.buildClassSet(persistenceUnitInfo, m);
// Create the temp loader that will not cache classes for entities in our persistence unit
ClassLoader tempLoader = createTempLoader(tempLoaderSet);
persistenceUnitInfo.setNewTempClassLoader(tempLoader);
EntityManagerSetupImpl emSetupImpl = new EntityManagerSetupImpl(persistenceUnitUniqueName, sessionName);
// A call to predeploy will partially build the session we will use
final ClassTransformer transformer = emSetupImpl.predeploy(persistenceUnitInfo, mergedProperties);
// After preDeploy it's impossible to weave again - so may substitute the temporary classloader with the real one.
// The temporary classloader could be garbage collected even if the puInfo is cached for the future use by other emSetupImpls.
persistenceUnitInfo.setNewTempClassLoader(persistenceUnitInfo.getClassLoader());
registerTransformer(transformer, persistenceUnitInfo, m);
return emSetupImpl;
}
/**
* Check whether weaving is possible and update the properties and variable as appropriate
* @param properties The list of properties to check for weaving and update if weaving is not needed
*/
public abstract void checkWeaving(Map properties);
/**
* Create a temporary class loader that can be used to inspect classes and then
* thrown away. This allows classes to be introspected prior to loading them
* with application's main class loader enabling weaving.
*/
protected abstract ClassLoader createTempLoader(Collection col);
protected abstract ClassLoader createTempLoader(Collection col, boolean shouldOverrideLoadClassForCollectionMembers);
/**
* Find PersistenceUnitInfo corresponding to the persistence unit name.
* Returns null if either persistence unit either not found or provider is not supported.
*/
public SEPersistenceUnitInfo findPersistenceUnitInfo(String puName, Map m) {
SEPersistenceUnitInfo persistenceUnitInfo = null;
if(initialPuInfos != null) {
persistenceUnitInfo = initialPuInfos.get(puName);
}
if(persistenceUnitInfo != null) {
return persistenceUnitInfo;
}
persistenceUnitInfo = (SEPersistenceUnitInfo) m.get(PersistenceUnitProperties.ECLIPSELINK_SE_PUINFO);
if (persistenceUnitInfo != null) {
return persistenceUnitInfo;
}
return findPersistenceUnitInfoInArchives(puName, m);
}
/**
* Find PersistenceUnitInfo corresponding to the persistence unit name.
* Returns null if either persistence unit either not found or provider is not supported.
*/
protected SEPersistenceUnitInfo findPersistenceUnitInfoInArchives(String puName, Map m) {
SEPersistenceUnitInfo persistenceUnitInfo = null;
// mkeith - get resource name from prop and include in subsequent call
String descriptorPath = (String) m.get(PersistenceUnitProperties.ECLIPSELINK_PERSISTENCE_XML);
final Set<Archive> pars;
if (descriptorPath != null) {
pars = PersistenceUnitProcessor.findPersistenceArchives(initializationClassloader, descriptorPath);
} else {
pars = PersistenceUnitProcessor.findPersistenceArchives(initializationClassloader);
}
try {
for (Archive archive: pars) {
persistenceUnitInfo = findPersistenceUnitInfoInArchive(puName, archive, m);
if(persistenceUnitInfo != null) {
break;
}
}
} finally {
for (Archive archive: pars) {
archive.close();
}
}
return persistenceUnitInfo;
}
/**
* Find PersistenceUnitInfo corresponding to the persistence unit name in the archive.
* Returns null if either persistence unit either not found or provider is not supported.
*/
protected SEPersistenceUnitInfo findPersistenceUnitInfoInArchive(String puName, Archive archive, Map m){
Iterator<SEPersistenceUnitInfo> persistenceUnits = PersistenceUnitProcessor.getPersistenceUnits(archive, initializationClassloader).iterator();
while (persistenceUnits.hasNext()) {
SEPersistenceUnitInfo persistenceUnitInfo = persistenceUnits.next();
if(isPersistenceProviderSupported(persistenceUnitInfo.getPersistenceProviderClassName()) && persistenceUnitInfo.getPersistenceUnitName().equals(puName)) {
return persistenceUnitInfo;
}
}
return null;
}
/**
* Returns whether the given persistence provider class is supported by this implementation
*/
public boolean isPersistenceProviderSupported(String providerClassName){
return (providerClassName == null) || providerClassName.equals("") || providerClassName.equals(EntityManagerFactoryProvider.class.getName()) || providerClassName.equals(PersistenceProvider.class.getName());
}
/**
* Create a list of java.lang.Class that contains the classes of all the entities
* that we will be deploying.
*/
protected Set loadEntityClasses(Collection entityNames, ClassLoader classLoader) {
Set<Object> entityClasses = new HashSet<>();
// Load the classes using the loader passed in
AbstractSessionLog.getLog().log(SessionLog.FINER, SessionLog.JPA, "cmp_loading_entities_using_loader", classLoader);
for (Iterator iter = entityNames.iterator(); iter.hasNext();) {
String entityClassName = (String)iter.next();
try {
entityClasses.add(classLoader.loadClass(entityClassName));
} catch (ClassNotFoundException cnfEx) {
throw ValidationException.entityClassNotFound(entityClassName, classLoader, cnfEx);
}
}
return entityClasses;
}
/**
* Register a transformer. This method should be overridden to provide the appropriate transformer
* registration for the environment
*/
public abstract void registerTransformer(final ClassTransformer transformer, PersistenceUnitInfo persistenceUnitInfo, Map properties);
/**
* Indicates whether puName uniquely defines the persistence unit.
*/
public boolean isPersistenceUnitUniquelyDefinedByName() {
return true;
}
/**
* In case persistence unit is not uniquely defined by name
* this method is called to generate a unique name.
*/
public String createUniquePersistenceUnitName(PersistenceUnitInfo puInfo) {
return PersistenceUnitProcessor.buildPersistenceUnitName(puInfo.getPersistenceUnitRootUrl(), puInfo.getPersistenceUnitName());
}
public EntityManagerSetupImpl extractInitialEmSetupImpl(String puName) {
if(this.initialEmSetupImpls != null) {
return this.initialEmSetupImpls.remove(puName);
} else {
return null;
}
}
/**
* This method initializes the container. Essentially, it will try to load the
* class that contains the list of entities and reflectively call the method that
* contains that list. It will then initialize the container with that list.
*/
public void initialize(Map m) {
boolean keepInitialMaps = keepAllPredeployedPersistenceUnits();
if(keepInitialMaps) {
this.initialPuInfos = new HashMap<>();
}
// always create initialEmSetupImpls - it's used to check for puName uniqueness in initPersistenceUnits
this.initialEmSetupImpls = new HashMap<>();
// ailitchev - copied from findPersistenceUnitInfoInArchives: mkeith - get resource name from prop and include in subsequent call
String descriptorPath = (String) m.get(PersistenceUnitProperties.ECLIPSELINK_PERSISTENCE_XML);
final Set<Archive> pars;
if (descriptorPath != null) {
pars = PersistenceUnitProcessor.findPersistenceArchives(initializationClassloader, descriptorPath);
} else {
pars = PersistenceUnitProcessor.findPersistenceArchives(initializationClassloader);
}
try {
for (Archive archive: pars) {
AbstractSessionLog.getLog().log(SessionLog.FINER, SessionLog.JPA, "cmp_init_initialize", archive);
initPersistenceUnits(archive, m);
}
} finally {
for (Archive archive: pars) {
archive.close();
}
this.initialEmSetupImpls = null;
}
}
/**
* Initialize all persistence units found on initializationClassLoader.
* Initialization is a two phase process. First the predeploy process builds the metadata
* and creates any required transformers.
* Second the deploy process creates an EclipseLink session based on that metadata.
*/
protected void initPersistenceUnits(Archive archive, Map m){
Iterator<SEPersistenceUnitInfo> persistenceUnits = PersistenceUnitProcessor.getPersistenceUnits(archive, initializationClassloader).iterator();
while (persistenceUnits.hasNext()) {
SEPersistenceUnitInfo persistenceUnitInfo = persistenceUnits.next();
if(isPersistenceProviderSupported(persistenceUnitInfo.getPersistenceProviderClassName())) {
// puName uniquely defines the pu on a class loader
String puName = persistenceUnitInfo.getPersistenceUnitName();
// don't add puInfo that could not be used standalone (only as composite member).
if (EntityManagerSetupImpl.mustBeCompositeMember(persistenceUnitInfo)) {
continue;
}
// If puName is already in the map then there are two jars containing persistence units with the same name.
// Because both are loaded from the same classloader there is no way to distinguish between them - throw exception.
EntityManagerSetupImpl anotherEmSetupImpl = null;
if (initialEmSetupImpls != null){
anotherEmSetupImpl = this.initialEmSetupImpls.get(puName);
}
if(anotherEmSetupImpl != null) {
EntityManagerSetupImpl.throwPersistenceUnitNameAlreadyInUseException(puName, persistenceUnitInfo, anotherEmSetupImpl.getPersistenceUnitInfo());
}
// Note that session name is extracted only from puInfo, the passed properties ignored.
String sessionName = EntityManagerSetupImpl.getOrBuildSessionName(Collections.emptyMap(), persistenceUnitInfo, puName);
EntityManagerSetupImpl emSetupImpl = callPredeploy(persistenceUnitInfo, m, puName, sessionName);
if (initialEmSetupImpls != null){
this.initialEmSetupImpls.put(puName, emSetupImpl);
}
if (initialPuInfos != null){
this.initialPuInfos.put(puName, persistenceUnitInfo);
}
}
}
}
/**
* Indicates whether initialPuInfos and initialEmSetupImpls are used.
*/
protected boolean keepAllPredeployedPersistenceUnits() {
return false;
}
public ClassLoader getInitializationClassLoader() {
return this.initializationClassloader;
}
}