All Downloads are FREE. Search and download functionalities are using the official Maven repository.

se.laz.casual.jca.CasualManagedConnection Maven / Gradle / Ivy

There is a newer version: 3.3.0
Show newest version
/*
 * Copyright (c) 2017 - 2024, The casual project. All rights reserved.
 *
 * This software is licensed under the MIT license, https://opensource.org/licenses/MIT
 */

package se.laz.casual.jca;

import jakarta.resource.NotSupportedException;
import jakarta.resource.ResourceException;
import jakarta.resource.spi.CommException;
import jakarta.resource.spi.ConnectionEvent;
import jakarta.resource.spi.ConnectionEventListener;
import jakarta.resource.spi.ConnectionRequestInfo;
import jakarta.resource.spi.LocalTransaction;
import jakarta.resource.spi.ManagedConnection;
import jakarta.resource.spi.ManagedConnectionMetaData;
import jakarta.resource.spi.ResourceAdapter;
import jakarta.resource.spi.work.WorkManager;
import se.laz.casual.internal.network.NetworkConnection;
import se.laz.casual.jca.event.ConnectionEventHandler;
import se.laz.casual.jca.pool.NetworkPoolHandler;
import se.laz.casual.network.outbound.NettyConnectionInformation;
import se.laz.casual.network.outbound.NettyConnectionInformationCreator;
import se.laz.casual.network.outbound.NettyNetworkConnection;
import se.laz.casual.network.outbound.NetworkListener;

import javax.security.auth.Subject;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.logging.Logger;

/**
 * CasualManagedConnection
 * The application server pools these objects
 * It contains one physical connection and is used to create connection handle objects and transaction resources.
 * These in turn knows their managed connection and can invoke network operations.
 *
 * @version $Revision: $
 */
public class CasualManagedConnection implements ManagedConnection, NetworkListener
{
    private static final Logger log = Logger.getLogger(CasualManagedConnection.class.getName());

    private PrintWriter logwriter;
    private final CasualManagedConnectionFactory mcf;
    private final ConnectionEventHandler connectionEventHandler;
    private final List connectionHandles;
    private NetworkConnection networkConnection;
    private final Object networkConnectionLock = new Object();
    private CasualXAResource xaResource;
    private int timeout;

    /**
     * Create a new managed connection with the provided factory and request information.
     *
     * @param mcf managed connection factory which created this objet.
     */
    public CasualManagedConnection(CasualManagedConnectionFactory mcf)
    {
        this.mcf = mcf;
        this.logwriter = null;
        this.connectionEventHandler = new ConnectionEventHandler();
        this.connectionHandles = Collections.synchronizedList(new ArrayList<>(1));
        xaResource = new CasualXAResource(this, mcf.getResourceId());
    }

    /**
     * Underlying physical network connection managed by this
     * managed connection instance.
     *
     * @return network connection.
     */
    public NetworkConnection getNetworkConnection()
    {
        synchronized (networkConnectionLock)
        {
            if (networkConnection == null)
            {
                if(null != mcf.getNetworkConnectionPoolName() && null == mcf.getNetworkConnectionPoolSize())
                {
                    log.warning(() -> "networkPoolName set to: " + mcf.getNetworkConnectionPoolName() + " but missing networkPoolSize!");
                }
                networkConnection = networkPoolNameAndNetworkPoolSizeSet() ? getOrCreateFromPool() : createOneToOneManagedConnection();
            }
        }
        return networkConnection;
    }

    private boolean networkPoolNameAndNetworkPoolSizeSet()
    {
        return null != mcf.getNetworkConnectionPoolSize() && null != mcf.getNetworkConnectionPoolName();
    }

    private NetworkConnection getOrCreateFromPool()
    {
        return NetworkPoolHandler.getInstance()
                                 .getOrCreate(
                                         mcf.getNetworkConnectionPoolName(),
                                         mcf.getAddress(),
                                         mcf.getCasualProtocolVersion(),
                                         this,
                                         mcf.getNetworkConnectionPoolSize());
    }

    @Override
    public Object getConnection(Subject subject,
                                ConnectionRequestInfo cxRequestInfo) throws ResourceException
    {
        try
        {
            log.finest("getConnection()");
            if (!getNetworkConnection().isActive())
            {
                closeNetworkConnection();
                throw new CommException("connection to casual is gone");
            }
            CasualConnectionImpl c = new CasualConnectionImpl(this);
            connectionHandles.add(c);
            return c;
        }
        catch (Exception e)
        {
            // Most likely what will be caught here is a Netty variant of java.net.ConnectException
            casualNotAvailable();

            throw new CommException(e);
        }
    }

    @Override
    public void associateConnection(Object connection) throws ResourceException
    {
        log.finest("associateConnection()");
        Objects.requireNonNull( connection, "Null connection handle." );

        if (!(connection instanceof CasualConnectionImpl))
        {
            throw new ResourceException("Wrong type of connection handle.");
        }
        CasualConnectionImpl handle = (CasualConnectionImpl) connection;
        handle.getManagedConnection().removeHandle(handle);
        handle.setManagedConnection(this);
        addHandle(handle);
    }

    @Override
    public void cleanup() throws ResourceException
    {
        log.finest("cleanup()");
        for(CasualConnectionImpl c : connectionHandles)
        {
            c.invalidate();
        }
        connectionHandles.clear();
    }

    @Override
    public void destroy() throws ResourceException
    {
        log.finest(() -> "destroy()" + this);
        closeNetworkConnection();
        connectionHandles.clear();
    }

    @Override
    public void addConnectionEventListener(ConnectionEventListener listener)
    {
        log.finest("addConnectionEventListener()");
        connectionEventHandler.addConnectionEventListener( listener );
    }

    @Override
    public void removeConnectionEventListener(ConnectionEventListener listener) {
        log.finest("removeConnectionEventListener()");
        connectionEventHandler.removeConnectionEventListener(listener);
    }

    @Override
    public PrintWriter getLogWriter() throws ResourceException
    {
        log.finest("getLogWriter()");
        return logwriter;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws ResourceException
    {
        log.finest("setLogWriter()");
        logwriter = out;
    }

    @Override
    public LocalTransaction getLocalTransaction() throws ResourceException
    {
        log.finest("getLocalTransaction(), this is not supported.");
        throw new NotSupportedException( "LocalTransactions are not supported." );
    }

    @Override
    public synchronized XAResource getXAResource() throws ResourceException
    {
        log.finest("getXAResource()");
        return this.xaResource;
    }

    /**
     * Help method to provide the current transaction id.
     *
     * @return current Xid or null if no XA resource present.
     */
    public Xid getCurrentXid()
    {
        return xaResource.getCurrentXid();
    }

    @Override
    public ManagedConnectionMetaData getMetaData() throws ResourceException
    {
        log.finest("getMetaData()");
        return new CasualManagedConnectionMetaData();
    }

    /**
     * Close handle
     *
     * @param handle The handle
     */
    public void closeHandle(CasualConnection handle)
    {
        ConnectionEvent event = new ConnectionEvent(this, ConnectionEvent.CONNECTION_CLOSED);
        event.setConnectionHandle(handle);
        connectionEventHandler.sendEvent(event);
    }

    private void closeNetworkConnection()
    {
        synchronized (networkConnectionLock)
        {
            if (null != networkConnection)
            {
                networkConnection.close();
                networkConnection = null;
            }
        }
    }

    private void addHandle(CasualConnectionImpl handle)
    {
        connectionHandles.add(handle);
    }

    private void removeHandle(CasualConnectionImpl handle)
    {
        connectionHandles.remove(handle);
    }

    public WorkManager getWorkManager()
    {
        ResourceAdapter ra = mcf.getResourceAdapter();
        if(ra instanceof CasualResourceAdapter)
        {
            return ((CasualResourceAdapter) ra).getWorkManager();
        }
        throw new CasualResourceAdapterException("resource adapter should be a casual resource adapter");
    }

    @Override
    public String toString()
    {
        return "CasualManagedConnection{" +
                ", xaResource=" + xaResource +
                '}';
    }

    public void casualNotAvailable()
    {
        // Mark transaction branch as read only when we fail to establish connection (i.e. casual is down)
        // If this isn't done the application server will attempt to commit this broken resource, which
        // does not make sense. This is imperative to enable retries for casual calls, because otherwise
        // the transaction is unusable.
        xaResource.setReadOnly();

        // Some application servers may reuse this resource after exception is thrown. As long as networkConnection
        // is null for the next use of this resource it should not be a problem.
        networkConnection = null;
    }

    @Override
    public void disconnected(Exception reason)
    {
        log.finest(() -> "disconnected: " + this);
        ConnectionEvent event = new ConnectionEvent(this, ConnectionEvent.CONNECTION_ERROR_OCCURRED, reason);
        connectionEventHandler.sendEvent(event);
    }

    /**
     * The domain id for this connection
     * @return connection domain id.
     */
    public DomainId getDomainId()
    {
        return getNetworkConnection().getDomainId();
    }

    public void setTransactionTimeout(int timeout)
    {
        this.timeout = timeout;
    }

    public int getTransactionTimeout()
    {
        return timeout;
    }

    private NetworkConnection createOneToOneManagedConnection()
    {
        NettyConnectionInformation ci = NettyConnectionInformationCreator.create(InetSocketAddress.createUnresolved(mcf.getHostName(), mcf.getPortNumber()), mcf.getCasualProtocolVersion());
        NetworkConnection newNetworkConnection = NettyNetworkConnection.of(ci, this);
        log.finest(() -> "created new nw connection " + this);
        return newNetworkConnection;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy