Skip to content

Commit

Permalink
Fixed createEntityManagerFactory(PersistenceConfiguration) in Persist…
Browse files Browse the repository at this point in the history
…enceProvider. (#2036)

Signed-off-by: Tomáš Kraus <tomas.kraus@oracle.com>
  • Loading branch information
Tomas-Kraus committed Jan 16, 2024
1 parent 12d8dd8 commit c2a63e3
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2023 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2024 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2023 IBM Corporation and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
Expand Down Expand Up @@ -87,6 +87,9 @@ public class ExceptionLocalizationResource extends ListResourceBundle {
{ "argument_collection_was_null", "Argument collection was null" },
{ "entity_manager_with_connection_failed", "Execution of user code failed: {0}"},
{ "custom_pu_name_conflict", "Cannot create custom persistence unit with name {0}. This name was found in xml configuration."},
{ "custom_pu_create_error", "Cannot create custom persistence unit with name {0}."},
{ "custom_pu_create_error_no_caller", "Cannot create custom persistence unit with name {0}. No caller method was found in stack."},
{ "custom_pu_create_error_no_caller_class_url", "Cannot create custom persistence unit with name {0}. No caller class {1} URL was found."},
{ "configured_pu_name_conflict", "Cannot create configured persistence unit with name {0}. This name was found in custom persistence units."},
{ "no_entities_retrieved_for_get_single_result", "getSingleResult() did not retrieve any entities." },
{ "no_entities_retrieved_for_get_reference", "Could not find Entity for id: {0}" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -35,7 +36,6 @@
import jakarta.persistence.PersistenceException;
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;
Expand Down Expand Up @@ -166,15 +166,16 @@ private SEPersistenceUnitInfo findPersistenceUnitInfoImpl(String puName, Map<?,
* If no cached instance exists, create and cache a new one.
*
* @param configuration configuration of the persistence unit
* @param rootURL root {@link URL} of the persistence unit
* @return {@link PersistenceUnitInfo} matching provided configuration
*/
public SEPersistenceUnitInfo customPersistenceUnitInfo(PersistenceConfiguration configuration) {
public SEPersistenceUnitInfo customPersistenceUnitInfo(PersistenceConfiguration configuration, URL rootURL) {
validateCustomPersistenceUnitName(configuration);
SEPersistenceUnitInfo persistenceUnitInfo = customPuInfos.get(configuration.name());
if (persistenceUnitInfo != null) {
return persistenceUnitInfo;
}
persistenceUnitInfo = new SEPersistenceUnitInfo(configuration);
persistenceUnitInfo = new SEPersistenceUnitInfo(configuration, rootURL);
customPuInfos.put(configuration.name(), persistenceUnitInfo);
return persistenceUnitInfo;
}
Expand Down Expand Up @@ -284,7 +285,14 @@ public boolean isPersistenceUnitUniquelyDefinedByName() {
* this method is called to generate a unique name.
*/
public String createUniquePersistenceUnitName(PersistenceUnitInfo puInfo) {
return PersistenceUnitProcessor.buildPersistenceUnitName(puInfo.getPersistenceUnitRootUrl(), puInfo.getPersistenceUnitName());
if (puInfo instanceof SEPersistenceUnitInfo sePUInfo) {
return PersistenceUnitProcessor.buildPersistenceUnitName(sePUInfo.getPersistenceUnitRootUrl(),
sePUInfo.getPersistenceUnitName(),
sePUInfo.getConfigHash());
}
return PersistenceUnitProcessor.buildPersistenceUnitName(puInfo.getPersistenceUnitRootUrl(),
puInfo.getPersistenceUnitName(),
null);
}

public EntityManagerSetupImpl extractInitialEmSetupImpl(String puName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -710,16 +710,34 @@ public static void setArchiveFactory(ArchiveFactory factory){
ARCHIVE_FACTORY = factory;
}

private static final String PU_NAME_SEPARATOR = "_";
private static final String PU_HASH_SEPARATOR = "?";

/**
* Build the unique persistence name by concatenating the decoded URL with the persistence unit name.
* A decoded URL is required while persisting on a multi-bytes OS.
* @return String
*
* @param rootURL root {@link URL} of the persistence unit
* @param puName name of the persistence unit
* @param hash programmatically defined persistence unit hash
* or {@code null} when {@code persistence.xml} is source of the persistence unit
* @return unique persistence unit name
*/
public static String buildPersistenceUnitName(URL url, String puName){
String fullPuName = null;
// append the persistence unit name to the decoded URL
fullPuName = URLDecoder.decode(url.toString(), StandardCharsets.UTF_8)+"_"+puName;
return fullPuName;
public static String buildPersistenceUnitName(URL rootURL, String puName, String hash) {
String urlPrefix = URLDecoder.decode(rootURL.toString(), StandardCharsets.UTF_8);
StringBuilder fullPuName = new StringBuilder(
urlPrefix.length()
+ PU_NAME_SEPARATOR.length()
+ puName.length()
+ (hash != null ? (PU_HASH_SEPARATOR.length() + hash.length()) : 0))
.append(urlPrefix)
.append(PU_NAME_SEPARATOR)
.append(puName);
if (hash != null) {
fullPuName.append(PU_HASH_SEPARATOR)
.append(hash);
}
return fullPuName.toString();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,21 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Properties;

import javax.sql.DataSource;

import jakarta.persistence.PersistenceConfiguration;
import jakarta.persistence.PersistenceException;
import jakarta.persistence.PersistenceUnitTransactionType;
import jakarta.persistence.SharedCacheMode;
import jakarta.persistence.ValidationMode;
import jakarta.persistence.spi.ClassTransformer;
import jakarta.persistence.spi.PersistenceUnitInfo;
import jakarta.persistence.PersistenceUnitTransactionType;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.internal.jpa.jdbc.DataSourceImpl;

import javax.sql.DataSource;

/**
* Internal implementation of the PersistenceUnitInfo detailed in the EJB 3.0 specification
* Used by our Java SE implementation so common method calls can be used in setting
Expand All @@ -58,7 +59,11 @@ public class SEPersistenceUnitInfo implements jakarta.persistence.spi.Persistenc
private final Collection<String> jarFiles = new ArrayList<>();
protected List<URL> jarFileUrls;
protected List<String> managedClassNames;
// persistence.xml root URL
protected URL persistenceUnitRootUrl;
// PersistenceConfiguration hash of programmatically defined PU, value is null for PU defined in persistence.xml
private String configHash;

protected boolean excludeUnlistedClasses = true;

// Persistence.xml loaded from the canonical model processor will
Expand All @@ -84,8 +89,9 @@ public SEPersistenceUnitInfo(){
* from {@link PersistenceConfiguration} content.
*
* @param configuration configuration of a persistence unit
* @param rootURL root {@link URL} of the persistence unit
*/
public SEPersistenceUnitInfo(PersistenceConfiguration configuration) {
public SEPersistenceUnitInfo(PersistenceConfiguration configuration, URL rootURL) {
persistenceUnitName = configuration.name();
persistenceProviderClassName = configuration.provider();
if (configuration.jtaDataSource() != null && !configuration.jtaDataSource().isEmpty()) {
Expand All @@ -109,6 +115,22 @@ public SEPersistenceUnitInfo(PersistenceConfiguration configuration) {
} else {
realClassLoader = Thread.currentThread().getContextClassLoader();
}
persistenceUnitRootUrl = rootURL;
configHash = Integer.toString(persistenceConfigurationHashCode(configuration));
}

// TODO: Remove when fixed by implementing hashCode in jakarta.persistence API
private static int persistenceConfigurationHashCode(PersistenceConfiguration configuration) {
return Objects.hash(configuration.name(),
configuration.provider(),
configuration.jtaDataSource(),
configuration.nonJtaDataSource(),
configuration.sharedCacheMode(),
configuration.validationMode(),
configuration.transactionType(),
configuration.managedClasses(),
configuration.mappingFiles(),
configuration.properties());
}

/**
Expand Down Expand Up @@ -295,6 +317,15 @@ public void setPersistenceUnitRootUrl(URL persistenceUnitRootUrl){
this.persistenceUnitRootUrl = persistenceUnitRootUrl;
}

/**
* Get programmatically defined persistence unit hash.
*
* @return persistence unit hash or {@code null} if this unit was not defined programmatically
*/
String getConfigHash() {
return configHash;
}

/**
* @return The list of the names of the classes that the
* persistence provider must add it to its set of managed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
// - New Jakarta Persistence 3.2 Features
package org.eclipse.persistence.jpa;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
Expand All @@ -44,7 +47,6 @@
import jakarta.persistence.spi.LoadState;
import jakarta.persistence.spi.PersistenceUnitInfo;
import jakarta.persistence.spi.ProviderUtil;

import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.config.SystemProperties;
import org.eclipse.persistence.exceptions.PersistenceUnitLoadingException;
Expand All @@ -55,6 +57,8 @@
import org.eclipse.persistence.internal.jpa.deployment.JavaSECMPInitializer;
import org.eclipse.persistence.internal.jpa.deployment.PersistenceUnitProcessor;
import org.eclipse.persistence.internal.jpa.deployment.SEPersistenceUnitInfo;
import org.eclipse.persistence.internal.localization.ExceptionLocalization;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.weaving.PersistenceWeaved;

/**
Expand Down Expand Up @@ -185,11 +189,50 @@ public EntityManagerFactory createEntityManagerFactory(String emName, Map<?, ?>
return null;
}

// Never call this method from this class because of stack frames removal.
@Override
public EntityManagerFactory createEntityManagerFactory(PersistenceConfiguration configuration) {
JPAInitializer initializer = getInitializer(configuration.name(), configuration.properties());
// Root URL from method caller
StackWalker stackWalker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
StackWalker.StackFrame frame = stackWalker.walk(stream -> stream
.dropWhile(f -> PersistenceProvider.class.getName().equals(f.getClassName()))
.dropWhile(f -> f.getClassName().startsWith("jakarta.persistence"))
.findFirst()
.orElse(null));
URL rootURL;
if (frame != null) {
// getProtectionDomain() may be restricted by SecurityManager
try {
rootURL = PrivilegedAccessHelper.callDoPrivileged(
() -> frame.getDeclaringClass().getProtectionDomain().getCodeSource().getLocation()
);
// fallback Root URL retrieval (unreliable and worse performance), remove when SecurityManager is no longer in Java
} catch (SecurityException e) {
Class<?> callerClass = frame.getDeclaringClass();
rootURL = callerClass.getResource(callerClass.getSimpleName() + ".class");
if (rootURL == null) {
throw new PersistenceException(
ExceptionLocalization.buildMessage("custom_pu_create_error_no_caller_class_url",
new String[] {configuration.name(), callerClass.getName()}));
}
String classSuffix = callerClass.getName().replaceAll("\\.", "/")+".class";
try {
rootURL = PersistenceUnitProcessor.computePURootURL(rootURL, classSuffix);
} catch (IOException | URISyntaxException ex) {
throw new PersistenceException(
ExceptionLocalization.buildMessage("custom_pu_create_error",
new String[] {configuration.name()}),
ex);
}
}
} else {
throw new PersistenceException(
ExceptionLocalization.buildMessage("custom_pu_create_error_no_caller",
new String[] {configuration.name()}));
}
return createEntityManagerFactoryImpl(
initializer.customPersistenceUnitInfo(configuration),
initializer.customPersistenceUnitInfo(configuration, rootURL),
Collections.emptyMap(),
true);
}
Expand Down Expand Up @@ -273,7 +316,6 @@ public JPAInitializer getInitializer(String emName, Map m){
return JavaSECMPInitializer.getJavaSECMPInitializer(classLoader);
}


/**
* Need to check that the provider property is null or set for EclipseLink
*/
Expand Down Expand Up @@ -334,7 +376,9 @@ protected EntityManagerFactory createContainerEntityManagerFactoryImpl(Persisten
} else {
boolean isNew = false;
ClassTransformer transformer = null;
String uniqueName = PersistenceUnitProcessor.buildPersistenceUnitName(info.getPersistenceUnitRootUrl(), info.getPersistenceUnitName());
String uniqueName = PersistenceUnitProcessor.buildPersistenceUnitName(info.getPersistenceUnitRootUrl(),
info.getPersistenceUnitName(),
null);
String sessionName = EntityManagerSetupImpl.getOrBuildSessionName(nonNullProperties, info, uniqueName);
synchronized (EntityManagerFactoryProvider.emSetupImpls) {
emSetupImpl = EntityManagerFactoryProvider.getEntityManagerSetupImpl(sessionName);
Expand Down

0 comments on commit c2a63e3

Please sign in to comment.