| /* |
| * 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); |
| } |
| } |
| } |
| } |