| /** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.dbcp2.managed; |
| |
| import java.lang.reflect.InvocationHandler; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Proxy; |
| import java.sql.Connection; |
| import java.sql.SQLException; |
| import java.util.ArrayList; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.concurrent.atomic.AtomicInteger; |
| |
| import javax.sql.ConnectionEvent; |
| import javax.sql.ConnectionEventListener; |
| import javax.sql.StatementEventListener; |
| import javax.sql.XAConnection; |
| import javax.transaction.xa.XAResource; |
| |
| /** |
| * Basic XAConnection. getConnection() returns a handle on a physical |
| * Connection. Closing the handle does not close the physical connection, you |
| * have to close the XAConnection for that (PooledConnection behavior). |
| * XA behavior is implemented through a LocalXAResource. |
| */ |
| public class TesterBasicXAConnection implements XAConnection { |
| |
| public Connection conn; |
| |
| public ConnectionHandle handle; |
| |
| public List<ConnectionEventListener> listeners = new LinkedList<>(); |
| |
| public AtomicInteger closeCounter; |
| |
| public TesterBasicXAConnection(final Connection conn, final AtomicInteger closeCounter) { |
| this.conn = conn; |
| this.closeCounter = closeCounter; |
| } |
| |
| public TesterBasicXAConnection(final Connection conn) { |
| this(conn, null); |
| } |
| |
| @Override |
| public Connection getConnection() throws SQLException { |
| if (conn == null) { |
| final SQLException e = new SQLException("XAConnection closed"); |
| notifyConnectionErrorOccurred(e); |
| throw e; |
| } |
| try { |
| if (handle != null) { |
| // only one handle at a time on the XAConnection |
| closeHandle(); |
| conn.clearWarnings(); |
| } |
| } catch (final SQLException e) { |
| notifyConnectionErrorOccurred(e); |
| throw e; |
| } |
| handle = new ConnectionHandle(conn, this); |
| final Connection proxy = (Connection) Proxy.newProxyInstance( |
| getClass().getClassLoader(), new Class[] { Connection.class }, |
| handle); |
| return proxy; |
| } |
| |
| protected void closeHandle() throws SQLException { |
| handle.closeHandle(); |
| if (!conn.getAutoCommit()) { |
| try { |
| conn.rollback(); |
| } catch (final SQLException e) { |
| e.printStackTrace(); |
| } |
| } |
| handle = null; |
| } |
| |
| @Override |
| public void close() throws SQLException { |
| if (handle != null) { |
| closeHandle(); |
| } |
| try { |
| conn.close(); |
| if (closeCounter != null) { |
| closeCounter.incrementAndGet(); |
| } |
| } finally { |
| conn = null; |
| } |
| } |
| |
| @Override |
| public XAResource getXAResource() throws SQLException { |
| return new LocalXAConnectionFactory.LocalXAResource(conn); |
| } |
| |
| @Override |
| public void addConnectionEventListener( |
| final ConnectionEventListener connectionEventListener) { |
| listeners.add(connectionEventListener); |
| } |
| |
| @Override |
| public void removeConnectionEventListener( |
| final ConnectionEventListener connectionEventListener) { |
| listeners.remove(connectionEventListener); |
| } |
| |
| @Override |
| public void addStatementEventListener(final StatementEventListener listener) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public void removeStatementEventListener(final StatementEventListener listener) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| protected void notifyConnectionClosed() { |
| final ConnectionEvent event = new ConnectionEvent(this); |
| final List<ConnectionEventListener> copy = new ArrayList<>( |
| listeners); |
| for (final ConnectionEventListener listener : copy) { |
| listener.connectionClosed(event); |
| } |
| } |
| |
| protected void notifyConnectionErrorOccurred(final SQLException e) { |
| final ConnectionEvent event = new ConnectionEvent(this, e); |
| final List<ConnectionEventListener> copy = new ArrayList<>( |
| listeners); |
| for (final ConnectionEventListener listener : copy) { |
| listener.connectionErrorOccurred(event); |
| } |
| } |
| |
| /** |
| * Delegates everything to a Connection, except for close() which just |
| * notifies the parent XAConnection. |
| */ |
| public static class ConnectionHandle implements InvocationHandler { |
| |
| public Connection conn; |
| |
| public TesterBasicXAConnection xaconn; |
| |
| public ConnectionHandle(final Connection conn, final TesterBasicXAConnection xaconn) { |
| this.conn = conn; |
| this.xaconn = xaconn; |
| } |
| |
| @Override |
| public Object invoke(final Object proxy, final Method method, final Object[] args) |
| throws Throwable { |
| final String methodName = method.getName(); |
| if (methodName.equals("hashCode")) { |
| return Integer.valueOf(System.identityHashCode(proxy)); |
| } |
| if (methodName.equals("equals")) { |
| return Boolean.valueOf(proxy == args[0]); |
| } |
| if (methodName.equals("isClosed")) { |
| return Boolean.valueOf(conn == null); |
| } |
| if (methodName.equals("close")) { |
| return close(); |
| } |
| if (conn == null) { |
| throw new SQLException("Connection closed"); |
| } |
| try { |
| return method.invoke(conn, args); |
| } catch (final InvocationTargetException e) { |
| final Throwable te = e.getTargetException(); |
| if (te instanceof SQLException) { |
| xaconn.notifyConnectionErrorOccurred((SQLException) te); |
| } |
| throw te; |
| } |
| } |
| |
| protected Object close() throws SQLException { |
| if (conn != null) { |
| conn.clearWarnings(); |
| conn = null; |
| xaconn.handle = null; |
| xaconn.notifyConnectionClosed(); |
| } |
| return null; |
| } |
| |
| public void closeHandle() { |
| conn = null; |
| } |
| } |
| } |
| |