package org.testfun.jee.runner; import org.apache.log4j.Logger; import javax.sql.DataSource; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class SingletonDataSource { public static DataSource getDataSource() { return INSTANCE.dataSource; } private static final SingletonDataSource INSTANCE = new SingletonDataSource(); private DataSource dataSource; private Connection delegateConnection; private SingletonDataSource() { try { Connection connection = DriverManager.getConnection(PersistenceXml.getInstnace().getConnectionUrl()); connection.setAutoCommit(false); dataSource = (DataSource) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{DataSource.class}, new NotClosableDataSource(connection)); Logger.getLogger(SingletonDataSource.class).info("Data source initialized successfully"); } catch (SQLException e) { Logger.getLogger(SingletonDataSource.class).error("Data source initialization failed", e); throw new EjbWithMockitoRunnerException("Data source initialization failed", e); } } /** * A DataSource proxy that always return the same JDBC connection which doesn't close when "close" is called. * This is needed so JDBC calls will be using the same connection and transaction as JPA calls. * Note that the connection returned from the data-source is never closed as it is up to the entity manager to * close its connection */ private class NotClosableDataSource implements InvocationHandler { private NotClosableDataSource(Connection connection) throws SQLException { delegateConnection = connection; } @Override public Object invoke(Object proxy, Method method, Object... args) throws Throwable { if ("getConnection".equals(method.getName())) { return getNotClosableConnection(); } else if ("toString".equals(method.getName())) { return "NotClosableDataSource"; } else { throw new IllegalArgumentException("Unsupported method: " + method.getName()); } } private Object getNotClosableConnection() { return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Connection.class}, new NotClosableConnectionProxy(delegateConnection)); } } /** * A JDBC Connection proxy that ignores calls to close() - used when the connection is retrieved from the entity manager. */ private class NotClosableConnectionProxy implements InvocationHandler { private Connection delegate; private NotClosableConnectionProxy(Connection delegate) { this.delegate = delegate; } @Override public Object invoke(Object proxy, Method method, Object... args) throws Throwable { if ("close".equals(method.getName())) { return null; } else if ("toString".equals(method.getName())) { return "NotClosableConnectionProxy{" + delegate + "} - " + (delegate.isClosed() ? "closed" : "opened"); } else { return method.invoke(delegate, args); } } } }