blob: a675bfc373a1c3585456fc56664eb39f8634b77f [file] [log] [blame]
/*
* Copyright (c) 2014, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021 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:
// 11/04/2014 - Rick Curtis
// - 450010 : Add java se test bucket
// 01/13/2015 - Rick Curtis
// - 438871 : Add support for writing statement terminator character(s) when generating ddl to script.
package org.eclipse.persistence.jpa.test.framework;
import java.io.File;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.internal.databaseaccess.DatabaseCall;
import org.eclipse.persistence.internal.jpa.deployment.SEPersistenceUnitInfo;
import org.eclipse.persistence.jpa.JpaEntityManagerFactory;
import org.eclipse.persistence.queries.Call;
import org.eclipse.persistence.sessions.SessionEvent;
import org.eclipse.persistence.sessions.SessionEventAdapter;
public class EmfRunnerInjector {
private boolean _debug = false;
private final Map<String, EntityManagerFactory> _emfs;
public EmfRunnerInjector() {
_emfs = new HashMap<String, EntityManagerFactory>();
}
public void close() {
for (EntityManagerFactory emf : _emfs.values()) {
try {
d("Closing [ " + emf + " ]");
emf.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* This method will create / inject EntityManagerFactory and SQLListeners into test instances
*
*/
public void inject(Object testInstance) throws Exception {
if (testInstance == null) {
return;
}
Class<?> cls = testInstance.getClass();
// Check for duplicate injected EMFs
Set<EntityManagerFactory> injectedEmfs = new HashSet<EntityManagerFactory>();
Set<Field> injectedSqlListeneFields = new HashSet<Field>();
// PU name -> Field
Map<String, Field> annotatedSqlListenerFields = getSqlListenerFieldMap(cls);
Map<String, Field> annotatedSqlCallListenerFields = getSqlCallListenerFieldMap(cls);
for (Field emfField : cls.getDeclaredFields()) {
Emf emfAnno = emfField.getAnnotation(Emf.class);
if (emfAnno == null) {
continue;
}
String emfName = emfAnno.name();
SqlCollector sqlCollector = null;
Field sqlListenerField = annotatedSqlListenerFields.get(emfName);
if (sqlListenerField != null) {
sqlCollector = new SqlCollector();
}
SqlCallCollector sqlCallCollector = null;
Field sqlCallListenerField = annotatedSqlCallListenerFields.get(emfName);
if (sqlCallListenerField != null) {
sqlCallCollector = new SqlCallCollector();
}
Map<String,Object> props = null;
if(testInstance instanceof PUPropertiesProvider) {
props = ((PUPropertiesProvider)testInstance).getAdditionalPersistenceProperties(emfName);
}
EntityManagerFactory factory = getEntityManagerFactory(emfAnno, props);
if (!injectedEmfs.add(factory)) {
throw new RuntimeException("Attempted to inject the same EntityManagerFactory multiple times into "
+ testInstance + ". Please remove the duplicate @Emf annotation, or change the name so that each "
+ "annotation is for a distinct EntityManagerFactory.");
}
emfField.setAccessible(true);
emfField.set(testInstance, factory);
d("Injected " + factory + " into " + testInstance);
if (sqlCollector != null) {
((JpaEntityManagerFactory) factory).getServerSession().getEventManager().addListener(sqlCollector);
sqlCollector.inject(testInstance, sqlListenerField);
injectedSqlListeneFields.add(sqlListenerField);
d("Injected " + sqlCollector + " into " + sqlListenerField);
}
if (sqlCallCollector != null) {
((JpaEntityManagerFactory) factory).getServerSession().getEventManager().addListener(sqlCallCollector);
sqlCallCollector.inject(testInstance, sqlCallListenerField);
injectedSqlListeneFields.add(sqlCallListenerField);
d("Injected " + sqlCallCollector + " into " + sqlCallListenerField);
}
}
// Check for @SQLListeners that weren't injected
validateSQLListenerAnnotations(annotatedSqlListenerFields, injectedSqlListeneFields);
}
private void validateSQLListenerAnnotations(Map<String, Field> sqlListeners, Set<Field> injectedSqlListeners) {
boolean die = false;
List<Field> uninjectedSqlFields = new ArrayList<Field>();
for (Entry<String, Field> entry : sqlListeners.entrySet()) {
String puName = entry.getKey();
Field annotatedField = entry.getValue();
if (!injectedSqlListeners.contains(annotatedField)) {
System.err.println("Encountered @SQLListener annotation for pu [" + puName + "] on field ["
+ annotatedField + "], but did not find a corresponding @Emf annotation for that pu name.");
uninjectedSqlFields.add(annotatedField);
die = true;
}
}
if (die) {
throw new RuntimeException("Error injecting @SQLListeners, review previous error messages.");
}
}
private Map<String, Field> getSqlListenerFieldMap(Class<?> cls) {
Map<String, Field> res = new HashMap<String, Field>();
for (Field field : cls.getDeclaredFields()) {
SQLListener listener = field.getAnnotation(SQLListener.class);
if (listener != null) {
if (!List.class.isAssignableFrom(field.getType())) {
throw new RuntimeException("@SQLListener anntation can only be placed on a List<String> field. Found on [" + field + "].");
}
String puName = listener.name();
Field old = res.put(puName, field);
if (old != null) {
// Found two SQLListener annotations for the same PU
throw new RuntimeException("Found multiple fields [" + old + ", " + field
+ "] annotated as a SQLListener for pu [" + puName + "]. Remove one of the anntations.");
}
}
}
return res;
}
private Map<String, Field> getSqlCallListenerFieldMap(Class<?> cls) {
Map<String, Field> res = new HashMap<String, Field>();
for (Field field : cls.getDeclaredFields()) {
SQLCallListener listener = field.getAnnotation(SQLCallListener.class);
if (listener != null) {
if (!List.class.isAssignableFrom(field.getType())) {
throw new RuntimeException("@SQLCallListener anntation can only be placed on a List<String> field. Found on [" + field + "].");
}
String puName = listener.name();
Field old = res.put(puName, field);
if (old != null) {
// Found two SQLCallListener annotations for the same PU
throw new RuntimeException("Found multiple fields [" + old + ", " + field
+ "] annotated as a SQLCallListener for pu [" + puName + "]. Remove one of the anntations.");
}
}
}
return res;
}
private EntityManagerFactory getEntityManagerFactory(Emf anno, Map<String, Object> additionalProperties) {
String name = anno.name();
// Check cache
EntityManagerFactory emf = _emfs.get(name);
if (emf != null) {
return emf;
}
List<String> mappingFiles = Arrays.asList(anno.mappingFiles());
List<String> classes = new ArrayList<String>();
Properties persistenceProperties = new Properties();
for (Class<?> cls : anno.classes()) {
classes.add(cls.getName());
}
for (Property prop : anno.properties()) {
persistenceProperties.put(prop.name(), prop.value());
}
if (additionalProperties != null) {
persistenceProperties.putAll(additionalProperties);
}
if (anno.createTables() != DDLGen.NONE) {
persistenceProperties.put(PersistenceUnitProperties.DDL_GENERATION, anno.createTables().toString());
}
SEPersistenceUnitInfo pu = createSEPUInfo(anno.name(), classes, mappingFiles, persistenceProperties);
Properties props = new Properties();
props.put(PersistenceUnitProperties.ECLIPSELINK_SE_PUINFO, pu);
emf = Persistence.createEntityManagerFactory(name, props);
_emfs.put(name, emf);
return emf;
}
private SEPersistenceUnitInfo createSEPUInfo(String puName, List<String> classes, List<String> mappingFiles,
Properties persistenceProperties) {
SEPersistenceUnitInfo pu = new SEPersistenceUnitInfo();
pu.setClassLoader(Thread.currentThread().getContextClassLoader());
pu.setPersistenceUnitName(puName);
pu.setManagedClassNames(classes);
pu.setMappingFileNames(mappingFiles);
try {
pu.setPersistenceUnitRootUrl(new File(".").toURI().toURL());
} catch (MalformedURLException e) {
e.printStackTrace();
}
pu.setProperties(persistenceProperties);
return pu;
}
private void d(String msg) {
if (_debug) {
System.err.println(msg);
}
}
private class SqlCollector extends SessionEventAdapter {
private List<String> _sql;
SqlCollector() {
_sql = new ArrayList<String>();
}
@Override
public void preExecuteQuery(SessionEvent event) {
super.preExecuteQuery(event);
_sql.add(event.getQuery().getSQLString());
}
private void inject(Object instance, Field into) {
into.setAccessible(true);
try {
into.set(instance, _sql);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
private class SqlCallCollector extends SessionEventAdapter {
private List<String> _sql;
SqlCallCollector() {
_sql = new ArrayList<String>();
}
@Override
public void preExecuteCall(SessionEvent event) {
super.preExecuteQuery(event);
Call call = event.getCall();
if(call instanceof DatabaseCall) {
_sql.add(((DatabaseCall)call).getSQLString());
}
}
private void inject(Object instance, Field into) {
into.setAccessible(true);
try {
into.set(instance, _sql);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}