blob: 9f9242d21692739f8d530904f1cf404616d6eec8 [file] [log] [blame]
/*
* Copyright (c) 2014, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2017 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
// 12/19/2014 - Dalia Abo Sheasha
// - 454917 : Added a test to use the IDENTITY strategy to generate values
// 02/16/2017 - Jody Grassel
// - 512255 : Eclipselink JPA/Auditing capablity in EE Environment fails with JNDI name parameter type
// 04/24/2017-2.6 Jody Grassel
// - 515712: ServerSession numberOfNonPooledConnectionsUsed can become invalid when Exception is thrown connecting accessor
package org.eclipse.persistence.jpa.test.basic;
import java.io.Serializable;
import java.lang.management.ManagementFactory;
import java.rmi.Remote;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import javax.naming.InitialContext;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.PersistenceException;
import jakarta.persistence.RollbackException;
import javax.sql.DataSource;
import org.eclipse.persistence.config.EntityManagerProperties;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.connwrapper.DriverWrapper;
import org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl;
import org.eclipse.persistence.jpa.test.basic.model.Dog;
import org.eclipse.persistence.jpa.test.basic.model.Employee;
import org.eclipse.persistence.jpa.test.basic.model.Person;
import org.eclipse.persistence.jpa.test.basic.model.XmlFish;
import org.eclipse.persistence.jpa.test.framework.DDLGen;
import org.eclipse.persistence.jpa.test.framework.Emf;
import org.eclipse.persistence.jpa.test.framework.EmfRunner;
import org.eclipse.persistence.jpa.test.framework.Property;
import org.eclipse.persistence.jpa.test.framework.SQLListener;
import org.eclipse.persistence.sessions.UnitOfWork;
import org.eclipse.persistence.sessions.server.ConnectionPolicy;
import org.eclipse.persistence.sessions.server.ServerSession;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(EmfRunner.class)
public class TestBasicPersistence {
@Emf(createTables = DDLGen.DROP_CREATE, classes = { Dog.class, XmlFish.class, Person.class, Employee.class },
properties = {@Property(name = "eclipselink.cache.shared.default", value = "false")},
mappingFiles = { "META-INF/fish-orm.xml" })
private EntityManagerFactory emf;
@SQLListener
List<String> _sql;
private static final int rmiPort;
private static final String dsName = "mockDataSource";
private static final BogusDataSource mockDataSource = new BogusDataSource("tmpDataSourceImp getConnection called");
private static Registry rmiRegistry = null;
private static JMXConnectorServer connector = null;
static {
int rmiPortVal = 1099;
String rmiPortProp = System.getProperty("rmi.port");
if (!(rmiPortProp == null || rmiPortProp.isEmpty())) {
try {
rmiPortVal = Integer.valueOf(rmiPortProp);
} catch (NumberFormatException nfe) {
// Use default value.
}
}
rmiPort = rmiPortVal;
}
@BeforeClass
public static void beforeClass() throws Exception {
rmiRegistry = LocateRegistry.createRegistry(rmiPort);
// Create and Bind Mock Data Source
rmiRegistry.bind(dsName, mockDataSource);
connector = JMXConnectorServerFactory.newJMXConnectorServer(new JMXServiceURL("service:jmx:rmi://localhost:" + rmiPort),
new HashMap<String, Object>(), ManagementFactory.getPlatformMBeanServer());
}
@AfterClass
public static void afterClass() throws Exception {
if (rmiRegistry != null) {
rmiRegistry.unbind(dsName);
}
if (connector != null) {
connector.stop();
}
}
@Test
public void activeTransaction() {
Assert.assertNotNull(emf);
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
}
@Test
public void testNonNullEmf() {
Assert.assertNotNull(emf);
}
@Test
public void persistTest() {
if (emf == null)
return;
EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
Person p = new Person();
Dog d = new Dog("Bingo");
p.setDog(d);
d.setOwner(p);
em.persist(p);
em.persist(d);
em.persist(new XmlFish());
em.getTransaction().commit();
em.clear();
Dog foundDog = em.find(Dog.class, d.getId());
foundDog.getOwner();
Assert.assertTrue(_sql.size() > 0);
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
em.close();
}
}
@Test
public void identityStrategyTest() {
if (emf == null)
return;
EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
Employee e = new Employee();
em.persist(e);
em.getTransaction().commit();
em.clear();
Employee foundEmp = em.find(Employee.class, e.getId());
Assert.assertNotNull(foundEmp);
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
em.close();
}
}
@Test
public void testNonJTADataSourceOverride() throws Exception {
if (emf == null)
return;
InitialContext ic = new InitialContext();
Assert.assertNotNull(ic.lookup(dsName));
EntityManager em = null;
boolean pass = false;
Map<String, Object> properties = new HashMap<>();
properties.put(PersistenceUnitProperties.NON_JTA_DATASOURCE, dsName);
properties.put("eclipselink.jdbc.exclusive-connection.mode", "Always");
try {
em = emf.createEntityManager(properties);
em.clear();
em.find(Dog.class, 1);
} catch (RuntimeException expected) {
pass = expected.getMessage().indexOf("tmpDataSourceImp getConnection called") != -1;
if (!pass) {
throw expected;
}
} finally {
if (em != null) {
em.close();
}
}
Assert.assertTrue("Non JTA datasource was not set or accessed as expected through map of properties", pass);
}
/*
* Verify that the number of non pooled connections used is accounted for accurately in regular non-error scenario usage.
*/
@Test
public void testNonpooledConnection() throws Exception {
System.out.println("********************");
System.out.println("*BEGIN testNonpooledConnection()");
if (emf == null)
return;
final EntityManagerFactoryImpl emfi = emf.unwrap(EntityManagerFactoryImpl.class);
Assert.assertNotNull(emfi);
// Create an em with a unpooled connection policy, idea taken from EntityManagerJUnitTestSuite.testNonPooledConnection()
final ServerSession ss = emfi.getServerSession();
// Clone the connection policy and set the pool name to null to emulate using non-pooled connections
final ConnectionPolicy connectionPolicy = (ConnectionPolicy)ss.getDefaultConnectionPolicy().clone();
connectionPolicy.setLogin(ss.getLogin());
connectionPolicy.setPoolName(null);
final Map<String, Object> properties = new HashMap<>();
properties.put(EntityManagerProperties.CONNECTION_POLICY, connectionPolicy);
final int initialNonPooledConnections = ss.getNumberOfNonPooledConnectionsUsed();
final int maxNonPooledConnections = ss.getMaxNumberOfNonPooledConnections();
System.out.println("initialNonPooledConnections = " + initialNonPooledConnections);
System.out.println("maxNonPooledConnections = " + maxNonPooledConnections);
if (maxNonPooledConnections != -1 && initialNonPooledConnections >= maxNonPooledConnections) {
Assert.fail("initialNonPooledConnections >= maxNonPooledConnections, other tests may be leaking.");
}
EntityManager em = emf.createEntityManager(properties);
EntityTransaction et = em.getTransaction();
try {
et.begin();
Person p = new Person();
Dog d = new Dog("Bingo");
p.setDog(d);
d.setOwner(p);
em.persist(p);
em.persist(d);
em.persist(new XmlFish());
em.flush();
int nonPooledConnectionsAfterFlush = ss.getNumberOfNonPooledConnectionsUsed();
System.out.println("nonPooledConnectionsAfterFlush = " + nonPooledConnectionsAfterFlush);
Assert.assertTrue("Test problem: connection should be not pooled", em.unwrap(UnitOfWork.class).getParent().getAccessor().getPool() == null);
Assert.assertEquals(initialNonPooledConnections + 1, nonPooledConnectionsAfterFlush);
et.commit();
em.clear();
int nonPooledConnectionsAfterCommit = ss.getNumberOfNonPooledConnectionsUsed();
System.out.println("nonPooledConnectionsAfterCommit = " + nonPooledConnectionsAfterCommit);
Assert.assertEquals(initialNonPooledConnections, nonPooledConnectionsAfterCommit);
Dog foundDog = em.find(Dog.class, d.getId());
foundDog.getOwner();
Assert.assertTrue(_sql.size() > 0);
int nonPooledConnectionsAfterFind = ss.getNumberOfNonPooledConnectionsUsed();
System.out.println("nonPooledConnectionsAfterFind = " + nonPooledConnectionsAfterFind);
Assert.assertEquals(initialNonPooledConnections, nonPooledConnectionsAfterFind);
} finally {
if (et.isActive()) {
et.rollback();
}
em.close();
System.out.println("*END testNonpooledConnection()");
}
}
@Test
public void testNonpooledConnectionWithErrorOnAcquireConnection() throws Exception {
System.out.println("********************");
System.out.println("*BEGIN testNonpooledConnectionWithErrorOnAcquireConnection()");
if (emf == null)
return;
final EntityManagerFactoryImpl emfi = emf.unwrap(EntityManagerFactoryImpl.class);
Assert.assertNotNull(emfi);
// Create an em with a unpooled connection policy, idea taken from EntityManagerJUnitTestSuite.testNonPooledConnection()
final ServerSession ss = emfi.getServerSession();
// Set up the DriverWrapper/ConnectionWrapper so we can emulate database connection failure
// cache the original driver name and connection string.
try {
setupDriverWrapper(ss);
// Clone the connection policy and set the pool name to null to emulate using non-pooled connections
final ConnectionPolicy connectionPolicy = (ConnectionPolicy)ss.getDefaultConnectionPolicy().clone();
connectionPolicy.setLogin(ss.getLogin());
connectionPolicy.setPoolName(null);
final Map<String, Object> properties = new HashMap<>();
properties.put(EntityManagerProperties.CONNECTION_POLICY, connectionPolicy);
// Validate against initial non-pooled connection count
final int initialNonPooledConnections = ss.getNumberOfNonPooledConnectionsUsed();
final int maxNonPooledConnections = ss.getMaxNumberOfNonPooledConnections();
System.out.println("initialNonPooledConnections = " + initialNonPooledConnections);
System.out.println("maxNonPooledConnections = " + maxNonPooledConnections);
if (maxNonPooledConnections != -1 && initialNonPooledConnections >= maxNonPooledConnections) {
Assert.fail("initialNonPooledConnections >= maxNonPooledConnections, other tests may be leaking.");
}
EntityManager em = emf.createEntityManager(properties);
EntityTransaction et = em.getTransaction();
try {
Person p = new Person();
Dog d = new Dog("Bingo");
p.setDog(d);
d.setOwner(p);
et.begin();
final int nonPooledConnectionsBeforePersist = ss.getNumberOfNonPooledConnectionsUsed();
System.out.println("nonPooledConnectionsBeforePersist = " + nonPooledConnectionsBeforePersist);
em.persist(p);
em.persist(d);
em.persist(new XmlFish());
final int nonPooledConnectionsBeforeFlush = ss.getNumberOfNonPooledConnectionsUsed();
System.out.println("nonPooledConnectionsBeforeFlush = " + nonPooledConnectionsBeforeFlush);
try {
DriverWrapper.breakNewConnections(); // .breakDriver(); // would be ideal, but results in bug #515961
em.flush();
Assert.fail("No PersistenceException was thrown.");
} catch (PersistenceException pe) {
// Expected
} finally {
DriverWrapper.repairAll();
}
final int nonPooledConnectionsAfterFlush = ss.getNumberOfNonPooledConnectionsUsed();
System.out.println("nonPooledConnectionsAfterFlush = " + nonPooledConnectionsAfterFlush);
Assert.assertEquals("nonPooledConnectionsBeforeFlush == nonPooledConnectionsAfterFlush", nonPooledConnectionsBeforeFlush, nonPooledConnectionsAfterFlush);
try {
et.rollback();
} catch (Throwable t) {
// Some databases such as mysql may see the Connection as still set to autocommit=true, and
// the DriverWrapper was set to make new connections broken, but the real Connection is
// still established, but being "Broken" prevents the Connection from being set autocommit=false.
}
final int nonPooledConnectionsAfterRollback = ss.getNumberOfNonPooledConnectionsUsed();
System.out.println("nonPooledConnectionsAfterRollback = " + nonPooledConnectionsAfterRollback);
Assert.assertTrue("initialNonPooledConnections >= nonPooledConnectionsAfterRollback", initialNonPooledConnections >= nonPooledConnectionsAfterRollback);
} finally {
if (et.isActive()) {
et.rollback();
}
em.close();
}
} finally {
DriverWrapper.repairAll();
System.out.println("*END testNonpooledConnectionWithErrorOnAcquireConnection()");
}
}
@Test
public void testNonpooledConnectionWithErrorOnReleaseConnection() throws Exception {
System.out.println("********************");
System.out.println("*BEGIN testNonpooledConnectionWithErrorOnReleaseConnection()");
if (emf == null)
return;
final EntityManagerFactoryImpl emfi = emf.unwrap(EntityManagerFactoryImpl.class);
Assert.assertNotNull(emfi);
// Create an em with a unpooled connection policy, idea taken from EntityManagerJUnitTestSuite.testNonPooledConnection()
final ServerSession ss = emfi.getServerSession();
// Set up the DriverWrapper/ConnectionWrapper so we can emulate database connection failure
// cache the original driver name and connection string.
try {
setupDriverWrapper(ss);
DriverWrapper.repairAll();
// Clone the connection policy and set the pool name to null to emulate using non-pooled connections
final ConnectionPolicy connectionPolicy = (ConnectionPolicy)ss.getDefaultConnectionPolicy().clone();
connectionPolicy.setLogin(ss.getLogin());
connectionPolicy.setPoolName(null);
final Map<String, Object> properties = new HashMap<>();
properties.put(EntityManagerProperties.CONNECTION_POLICY, connectionPolicy);
// Validate against initial non-pooled connection count
final int initialNonPooledConnections = ss.getNumberOfNonPooledConnectionsUsed();
final int maxNonPooledConnections = ss.getMaxNumberOfNonPooledConnections();
System.out.println("initialNonPooledConnections = " + initialNonPooledConnections);
System.out.println("maxNonPooledConnections = " + maxNonPooledConnections);
if (maxNonPooledConnections != -1 && initialNonPooledConnections >= maxNonPooledConnections) {
Assert.fail("initialNonPooledConnections >= maxNonPooledConnections, other tests may be leaking.");
}
EntityManager em = emf.createEntityManager(properties);
EntityTransaction et = em.getTransaction();
try {
et.begin();
Person p = new Person();
Dog d = new Dog("Bingo");
p.setDog(d);
d.setOwner(p);
em.persist(p);
em.persist(d);
em.persist(new XmlFish());
final int nonPooledConnectionsBeforeFlush = ss.getNumberOfNonPooledConnectionsUsed();
System.out.println("nonPooledConnectionsBeforeFlush = " + nonPooledConnectionsBeforeFlush);
em.flush();
final int nonPooledConnectionsAfterFlush = ss.getNumberOfNonPooledConnectionsUsed();
System.out.println("nonPooledConnectionsAfterFlush = " + nonPooledConnectionsAfterFlush);
Assert.assertEquals("nonPooledConnectionsBeforeFlush + 1 == nonPooledConnectionsAfterFlush", nonPooledConnectionsBeforeFlush + 1, nonPooledConnectionsAfterFlush);
// The non-pooled Connection would be released after the commit.
DriverWrapper.breakOldConnections();
try {
et.commit();
Assert.fail("No RollbackException was thrown.");
} catch (RollbackException re) {
// Expected
} finally {
DriverWrapper.repairAll();
}
int nonPooledConnectionsAfterCommit = ss.getNumberOfNonPooledConnectionsUsed();
System.out.println("nonPooledConnectionsAfterCommit = " + nonPooledConnectionsAfterCommit);
Assert.assertTrue("initialNonPooledConnections >= nonPooledConnectionsAfterCommit", initialNonPooledConnections >= nonPooledConnectionsAfterCommit);
} finally {
DriverWrapper.repairAll();
if (et.isActive()) {
et.rollback();
}
em.close();
}
} finally {
DriverWrapper.repairAll();
System.out.println("*END testNonpooledConnectionWithErrorOnReleaseConnection()");
}
}
private void setupDriverWrapper(ServerSession ss) {
// Set up the DriverWrapper/ConnectionWrapper so we can emulate database connection failure
// cache the original driver name and connection string.
String originalDriverName = ss.getLogin().getDriverClassName();
String originalConnectionString = ss.getLogin().getConnectionString();
if (DriverWrapper.class.getName().equals(originalDriverName)) {
// DriverWrapper is already set up, so just return.
return;
}
// the new driver name and connection string to be used by the test
String newDriverName = DriverWrapper.class.getName();
String newConnectionString = DriverWrapper.codeUrl(originalConnectionString);
// setup the wrapper driver
DriverWrapper.initialize(originalDriverName);
ss.logout();
ss.getLogin().setDriverClassName(newDriverName);
ss.getLogin().setConnectionHealthValidatedOnError(true);
ss.getLogin().setConnectionString(newConnectionString);
ss.login();
}
/*
* Taken from org.eclipse.persistence.testing.tests.jpa.validation.ValidationTestSuite
*/
public static class BogusDataSource implements DataSource, Remote, Serializable {
private static final long serialVersionUID = 1L;
private String text = "foo";
public BogusDataSource(String text){
super();
this.text = text;
}
@Override
public Connection getConnection() throws SQLException {
RuntimeException exception = new RuntimeException(text);
throw exception;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return getConnection();
}
//rest are ignored
@Override
public java.io.PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(java.io.PrintWriter out) throws SQLException{}
@Override
public void setLoginTimeout(int seconds) throws SQLException{}
@Override
public int getLoginTimeout() throws SQLException { return 1; }
@Override
public <T> T unwrap(Class<T> iface) throws SQLException { return null; }
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException { return false; }
@Override
public Logger getParentLogger() { return null; }
}
}