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

org.wildfly.transaction.client.provider.remoting.TransactionClientChannel Maven / Gradle / Ivy

/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2016 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed 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.wildfly.transaction.client.provider.remoting;

import static org.xnio.IoUtils.safeClose;

import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.ThreadLocalRandom;

import javax.transaction.SystemException;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;

import org.jboss.remoting3.Channel;
import org.jboss.remoting3.ClientServiceHandle;
import org.jboss.remoting3.Connection;
import org.jboss.remoting3.ConnectionPeerIdentity;
import org.jboss.remoting3.MessageInputStream;
import org.jboss.remoting3.MessageOutputStream;
import org.jboss.remoting3._private.IntIndexHashMap;
import org.jboss.remoting3._private.IntIndexMap;
import org.jboss.remoting3.util.BlockingInvocation;
import org.jboss.remoting3.util.InvocationTracker;
import org.jboss.remoting3.util.StreamUtils;
import org.wildfly.common.annotation.NotNull;
import org.wildfly.common.rpc.RemoteExceptionCause;
import org.wildfly.transaction.client.SimpleXid;
import org.wildfly.transaction.client._private.Log;
import org.wildfly.transaction.client.spi.SimpleTransactionControl;
import org.xnio.FinishedIoFuture;
import org.xnio.IoFuture;
import org.xnio.OptionMap;

/**
 * @author David M. Lloyd
 */
final class TransactionClientChannel implements RemotingOperations {
    private final Channel channel;
    private final InvocationTracker invocationTracker;
    private final IntIndexMap peerTransactionMap = new IntIndexHashMap(RemotingRemoteTransactionHandle::getId);
    private final Channel.Receiver receiver = new ReceiverImpl();

    private static final ClientServiceHandle CLIENT_SERVICE_HANDLE = new ClientServiceHandle<>("txn", TransactionClientChannel::construct);

    TransactionClientChannel(final Channel channel) {
        this.channel = channel;
        invocationTracker = new InvocationTracker(channel);
    }

    private static IoFuture construct(final Channel channel) {
        // future protocol versions might have to negotiate a version or capabilities before proceeding
        final TransactionClientChannel clientChannel = new TransactionClientChannel(channel);
        channel.receiveMessage(clientChannel.getReceiver());
        return new FinishedIoFuture<>(clientChannel);
    }

    @NotNull
    public SimpleTransactionControl begin(final ConnectionPeerIdentity peerIdentity) throws SystemException {
        int id;
        final ThreadLocalRandom random = ThreadLocalRandom.current();
        final IntIndexMap map = this.peerTransactionMap;
        RemotingRemoteTransactionHandle handle;
        do {
            id = random.nextInt();
        } while (map.containsKey(id) || map.putIfAbsent(handle = new RemotingRemoteTransactionHandle(id, this)) != null);
        return handle;
    }

    public void rollback(final Xid xid, final ConnectionPeerIdentity peerIdentity) throws XAException {
        final InvocationTracker invocationTracker = getInvocationTracker();
        final BlockingInvocation invocation = invocationTracker.addInvocation(BlockingInvocation::new);
        // write request
        try (MessageOutputStream os = invocationTracker.allocateMessage(invocation)) {
            os.writeShort(invocation.getIndex());
            os.writeByte(Protocol.M_XA_ROLLBACK);
            Protocol.writeParam(Protocol.P_XID, os, xid);
            final int peerIdentityId = peerIdentity.getId();
            if (peerIdentityId != 0) Protocol.writeParam(Protocol.P_SEC_CONTEXT, os, peerIdentityId, Protocol.UNSIGNED);
        } catch (IOException e) {
            throw Log.log.failedToSendXA(e, XAException.XAER_RMERR);
        }
        try (BlockingInvocation.Response response = invocation.getResponse()) {
            try (MessageInputStream is = response.getInputStream()) {
                if (is.readUnsignedByte() != Protocol.M_RESP_XA_ROLLBACK) {
                    throw Log.log.unknownResponseXa(XAException.XAER_RMERR);
                }
                int id = is.read();
                if (id == Protocol.P_XA_ERROR) {
                    int len = StreamUtils.readPackedSignedInt32(is);
                    int error = is.readInt();
                    final XAException xa = Log.log.peerXaException(error);
                    xa.initCause(RemoteExceptionCause.readFromStream(is));
                    if ((id = is.read()) != -1) {
                        XAException ex = Log.log.unrecognizedParameter(XAException.XAER_RMFAIL, id);
                        ex.addSuppressed(Log.log.peerXaException(error));
                        throw ex;
                    } else {
                        throw xa;
                    }
                } else if (id == Protocol.P_SEC_EXC) {
                    int len = StreamUtils.readPackedSignedInt32(is);
                    final SecurityException sx = Log.log.peerSecurityException();
                    sx.initCause(RemoteExceptionCause.readFromStream(is));
                    if ((id = is.read()) != -1) {
                        XAException ex = Log.log.unrecognizedParameter(XAException.XAER_RMFAIL, id);
                        ex.addSuppressed(Log.log.peerSecurityException());
                        throw ex;
                    } else {
                        throw sx;
                    }
                } else if (id != -1) {
                    throw Log.log.unrecognizedParameter(XAException.XAER_RMFAIL, id);
                }
            } catch (IOException e) {
                throw Log.log.responseFailedXa(e, XAException.XAER_RMERR);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw Log.log.interruptedXA(XAException.XAER_RMERR);
        } catch (IOException e) {
            // failed to close the response, but we don't care too much
            Log.log.inboundException(e);
        }
    }

    public void setRollbackOnly(final Xid xid, final ConnectionPeerIdentity peerIdentity) throws XAException {
        // write rollback-only request
        final InvocationTracker invocationTracker = getInvocationTracker();
        final BlockingInvocation invocation = invocationTracker.addInvocation(BlockingInvocation::new);
        // write request
        try (MessageOutputStream os = invocationTracker.allocateMessage(invocation)) {
            os.writeShort(invocation.getIndex());
            os.writeByte(Protocol.M_XA_RB_ONLY);
            Protocol.writeParam(Protocol.P_XID, os, xid);
            final int peerIdentityId = peerIdentity.getId();
            if (peerIdentityId != 0) Protocol.writeParam(Protocol.P_SEC_CONTEXT, os, peerIdentityId, Protocol.UNSIGNED);
        } catch (IOException e) {
            throw Log.log.failedToSendXA(e, XAException.XAER_RMERR);
        }
        try (BlockingInvocation.Response response = invocation.getResponse()) {
            try (MessageInputStream is = response.getInputStream()) {
                if (is.readUnsignedByte() != Protocol.M_RESP_XA_RB_ONLY) {
                    throw Log.log.unknownResponseXa(XAException.XAER_RMERR);
                }
                int id = is.read();
                if (id == Protocol.P_XA_ERROR) {
                    int len = StreamUtils.readPackedSignedInt32(is);
                    int error = is.readInt();
                    final XAException xa = Log.log.peerXaException(error);
                    xa.initCause(RemoteExceptionCause.readFromStream(is));
                    if ((id = is.read()) != -1) {
                        XAException ex = Log.log.unrecognizedParameter(XAException.XAER_RMFAIL, id);
                        ex.addSuppressed(xa);
                        throw ex;
                    } else {
                        throw xa;
                    }
                } else if (id == Protocol.P_SEC_EXC) {
                    int len = StreamUtils.readPackedSignedInt32(is);
                    final SecurityException sx = Log.log.peerSecurityException();
                    sx.initCause(RemoteExceptionCause.readFromStream(is));
                    if ((id = is.read()) != -1) {
                        XAException ex = Log.log.unrecognizedParameter(XAException.XAER_RMFAIL, id);
                        ex.addSuppressed(sx);
                        throw ex;
                    } else {
                        throw sx;
                    }
                } else if (id != -1) {
                    throw Log.log.unrecognizedParameter(XAException.XAER_RMFAIL, id);
                }
            } catch (IOException e) {
                throw Log.log.responseFailedXa(e, XAException.XAER_RMERR);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw Log.log.interruptedXA(XAException.XAER_RMERR);
        } catch (IOException e) {
            // failed to close the response, but we don't care too much
            Log.log.inboundException(e);
        }
    }

    public void beforeCompletion(final Xid xid, final ConnectionPeerIdentity peerIdentity) throws XAException {
        final InvocationTracker invocationTracker = getInvocationTracker();
        final BlockingInvocation invocation = invocationTracker.addInvocation(BlockingInvocation::new);
        // write request
        try (MessageOutputStream os = invocationTracker.allocateMessage(invocation)) {
            os.writeShort(invocation.getIndex());
            os.writeByte(Protocol.M_XA_BEFORE);
            Protocol.writeParam(Protocol.P_XID, os, xid);
            final int peerIdentityId = peerIdentity.getId();
            if (peerIdentityId != 0) Protocol.writeParam(Protocol.P_SEC_CONTEXT, os, peerIdentityId, Protocol.UNSIGNED);
        } catch (IOException e) {
            throw Log.log.failedToSendXA(e, XAException.XAER_RMERR);
        }
        try (BlockingInvocation.Response response = invocation.getResponse()) {
            try (MessageInputStream is = response.getInputStream()) {
                if (is.readUnsignedByte() != Protocol.M_RESP_XA_BEFORE) {
                    throw Log.log.unknownResponseXa(XAException.XAER_RMERR);
                }
                int id = is.read();
                if (id == Protocol.P_XA_ERROR) {
                    int len = StreamUtils.readPackedSignedInt32(is);
                    int error = is.readInt();
                    final XAException xa = Log.log.peerXaException(error);
                    xa.initCause(RemoteExceptionCause.readFromStream(is));
                    if ((id = is.read()) != -1) {
                        XAException ex = Log.log.unrecognizedParameter(XAException.XAER_RMFAIL, id);
                        ex.addSuppressed(xa);
                        throw ex;
                    } else {
                        throw xa;
                    }
                } else if (id == Protocol.P_SEC_EXC) {
                    int len = StreamUtils.readPackedSignedInt32(is);
                    final SecurityException sx = Log.log.peerSecurityException();
                    sx.initCause(RemoteExceptionCause.readFromStream(is));
                    if ((id = is.read()) != -1) {
                        XAException ex = Log.log.unrecognizedParameter(XAException.XAER_RMFAIL, id);
                        ex.addSuppressed(sx);
                        throw ex;
                    } else {
                        throw sx;
                    }
                } else if (id != -1) {
                    throw Log.log.unrecognizedParameter(XAException.XAER_RMFAIL, id);
                }
            } catch (IOException e) {
                throw Log.log.responseFailedXa(e, XAException.XAER_RMERR);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw Log.log.interruptedXA(XAException.XAER_RMERR);
        } catch (IOException e) {
            // failed to close the response, but we don't care too much
            Log.log.inboundException(e);
        }
    }

    public int prepare(final Xid xid, final ConnectionPeerIdentity peerIdentity) throws XAException {
        boolean readOnly = false;
        final InvocationTracker invocationTracker = getInvocationTracker();
        final BlockingInvocation invocation = invocationTracker.addInvocation(BlockingInvocation::new);
        // write request
        try (MessageOutputStream os = invocationTracker.allocateMessage(invocation)) {
            os.writeShort(invocation.getIndex());
            os.writeByte(Protocol.M_XA_PREPARE);
            Protocol.writeParam(Protocol.P_XID, os, xid);
            final int peerIdentityId = peerIdentity.getId();
            if (peerIdentityId != 0) Protocol.writeParam(Protocol.P_SEC_CONTEXT, os, peerIdentityId, Protocol.UNSIGNED);
        } catch (IOException e) {
            throw Log.log.failedToSendXA(e, XAException.XAER_RMERR);
        }
        try (BlockingInvocation.Response response = invocation.getResponse()) {
            try (MessageInputStream is = response.getInputStream()) {
                if (is.readUnsignedByte() != Protocol.M_RESP_XA_PREPARE) {
                    throw Log.log.unknownResponseXa(XAException.XAER_RMERR);
                }
                int id = is.read();
                if (id == Protocol.P_XA_ERROR) {
                    int len = StreamUtils.readPackedSignedInt32(is);
                    int error = is.readInt();
                    final XAException xa = Log.log.peerXaException(error);
                    xa.initCause(RemoteExceptionCause.readFromStream(is));
                    if ((id = is.read()) != -1) {
                        XAException ex = Log.log.unrecognizedParameter(XAException.XAER_RMFAIL, id);
                        ex.addSuppressed(xa);
                        throw ex;
                    } else {
                        throw xa;
                    }
                } else if (id == Protocol.P_SEC_EXC) {
                    int len = StreamUtils.readPackedSignedInt32(is);
                    final SecurityException sx = Log.log.peerSecurityException();
                    sx.initCause(RemoteExceptionCause.readFromStream(is));
                    if ((id = is.read()) != -1) {
                        XAException ex = Log.log.unrecognizedParameter(XAException.XAER_RMFAIL, id);
                        ex.addSuppressed(sx);
                        throw ex;
                    } else {
                        throw sx;
                    }
                } else if (id == Protocol.P_XA_RDONLY) {
                    readOnly = true;
                } else if (id != -1) {
                    throw Log.log.unrecognizedParameter(XAException.XAER_RMFAIL, id);
                }
            } catch (IOException e) {
                throw Log.log.responseFailedXa(e, XAException.XAER_RMERR);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw Log.log.interruptedXA(XAException.XAER_RMERR);
        } catch (IOException e) {
            // failed to close the response, but we don't care too much
            Log.log.inboundException(e);
        }
        return readOnly ? XAResource.XA_RDONLY : XAResource.XA_OK;
    }

    public void forget(final Xid xid, final ConnectionPeerIdentity peerIdentity) throws XAException {
        final InvocationTracker invocationTracker = getInvocationTracker();
        final BlockingInvocation invocation = invocationTracker.addInvocation(BlockingInvocation::new);
        // write request
        try (MessageOutputStream os = invocationTracker.allocateMessage(invocation)) {
            os.writeShort(invocation.getIndex());
            os.writeByte(Protocol.M_XA_FORGET);
            Protocol.writeParam(Protocol.P_XID, os, xid);
            final int peerIdentityId = peerIdentity.getId();
            if (peerIdentityId != 0) Protocol.writeParam(Protocol.P_SEC_CONTEXT, os, peerIdentityId, Protocol.UNSIGNED);
        } catch (IOException e) {
            throw Log.log.failedToSendXA(e, XAException.XAER_RMERR);
        }
        try (BlockingInvocation.Response response = invocation.getResponse()) {
            try (MessageInputStream is = response.getInputStream()) {
                if (is.readUnsignedByte() != Protocol.M_RESP_XA_FORGET) {
                    throw Log.log.unknownResponseXa(XAException.XAER_RMERR);
                }
                int id = is.read();
                if (id == Protocol.P_XA_ERROR) {
                    int len = StreamUtils.readPackedSignedInt32(is);
                    int error = is.readInt();
                    final XAException xa = Log.log.peerXaException(error);
                    xa.initCause(RemoteExceptionCause.readFromStream(is));
                    if ((id = is.read()) != -1) {
                        XAException ex = Log.log.unrecognizedParameter(XAException.XAER_RMFAIL, id);
                        ex.addSuppressed(xa);
                        throw ex;
                    } else {
                        throw xa;
                    }
                } else if (id == Protocol.P_SEC_EXC) {
                    int len = StreamUtils.readPackedSignedInt32(is);
                    final SecurityException sx = Log.log.peerSecurityException();
                    sx.initCause(RemoteExceptionCause.readFromStream(is));
                    if ((id = is.read()) != -1) {
                        XAException ex = Log.log.unrecognizedParameter(XAException.XAER_RMFAIL, id);
                        ex.addSuppressed(sx);
                        throw ex;
                    } else {
                        throw sx;
                    }
                } else if (id != -1) {
                    throw Log.log.unrecognizedParameter(XAException.XAER_RMFAIL, id);
                }
            } catch (IOException e) {
                throw Log.log.responseFailedXa(e, XAException.XAER_RMERR);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw Log.log.interruptedXA(XAException.XAER_RMERR);
        } catch (IOException e) {
            // failed to close the response, but we don't care too much
            Log.log.inboundException(e);
        }
    }

    public void commit(final Xid xid, final boolean onePhase, final ConnectionPeerIdentity peerIdentity) throws XAException {
        final InvocationTracker invocationTracker = getInvocationTracker();
        final BlockingInvocation invocation = invocationTracker.addInvocation(BlockingInvocation::new);
        // write request
        try (MessageOutputStream os = invocationTracker.allocateMessage(invocation)) {
            os.writeShort(invocation.getIndex());
            os.writeByte(Protocol.M_XA_COMMIT);
            Protocol.writeParam(Protocol.P_XID, os, xid);
            final int peerIdentityId = peerIdentity.getId();
            if (peerIdentityId != 0) Protocol.writeParam(Protocol.P_SEC_CONTEXT, os, peerIdentityId, Protocol.UNSIGNED);
            if (onePhase) Protocol.writeParam(Protocol.P_ONE_PHASE, os);
        } catch (IOException e) {
            throw Log.log.failedToSendXA(e, XAException.XAER_RMERR);
        }
        try (BlockingInvocation.Response response = invocation.getResponse()) {
            try (MessageInputStream is = response.getInputStream()) {
                if (is.readUnsignedByte() != Protocol.M_RESP_XA_COMMIT) {
                    throw Log.log.unknownResponseXa(XAException.XAER_RMERR);
                }
                int id = is.read();
                if (id == Protocol.P_XA_ERROR) {
                    int len = StreamUtils.readPackedSignedInt32(is);
                    int error = is.readInt();
                    final XAException xa = Log.log.peerXaException(error);
                    xa.initCause(RemoteExceptionCause.readFromStream(is));
                    if ((id = is.read()) != -1) {
                        XAException ex = Log.log.unrecognizedParameter(XAException.XAER_RMFAIL, id);
                        ex.addSuppressed(xa);
                        throw ex;
                    } else {
                        throw xa;
                    }
                } else if (id == Protocol.P_SEC_EXC) {
                    int len = StreamUtils.readPackedSignedInt32(is);
                    final SecurityException sx = Log.log.peerSecurityException();
                    sx.initCause(RemoteExceptionCause.readFromStream(is));
                    if ((id = is.read()) != -1) {
                        XAException ex = Log.log.unrecognizedParameter(XAException.XAER_RMFAIL, id);
                        ex.addSuppressed(sx);
                        throw ex;
                    } else {
                        throw sx;
                    }
                } else if (id != -1) {
                    throw Log.log.unrecognizedParameter(XAException.XAER_RMFAIL, id);
                }
            } catch (IOException e) {
                throw Log.log.responseFailedXa(e, XAException.XAER_RMFAIL);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw Log.log.interruptedXA(XAException.XAER_RMERR);
        } catch (IOException e) {
            // failed to close the response, but we don't care too much
            Log.log.inboundException(e);
        }
    }

    @NotNull
    public Xid[] recover(final int flag, final String parentName, final ConnectionPeerIdentity peerIdentity) throws XAException {
        if (flag != XAResource.TMSTARTRSCAN) {
            return SimpleXid.NO_XIDS;
        }
        final InvocationTracker invocationTracker = getInvocationTracker();
        final BlockingInvocation invocation = invocationTracker.addInvocation(BlockingInvocation::new);
        // write request
        try (MessageOutputStream os = invocationTracker.allocateMessage(invocation)) {
            os.writeShort(invocation.getIndex());
            os.writeByte(Protocol.M_XA_RECOVER);
            final int peerIdentityId = peerIdentity.getId();
            if (peerIdentityId != 0) Protocol.writeParam(Protocol.P_SEC_CONTEXT, os, peerIdentityId, Protocol.UNSIGNED);
            Protocol.writeParam(Protocol.P_PARENT_NAME, os, parentName);
        } catch (IOException e) {
            throw Log.log.failedToSendXA(e, XAException.XAER_RMERR);
        }
        final ArrayList recoveryList = new ArrayList<>();
        try (BlockingInvocation.Response response = invocation.getResponse()) {
            try (MessageInputStream is = response.getInputStream()) {
                if (is.readUnsignedByte() != Protocol.M_RESP_XA_RECOVER) {
                    throw Log.log.unknownResponseXa(XAException.XAER_RMERR);
                }
                int id = is.read();
                for (;;) {
                    if (id == Protocol.P_XID) {
                        recoveryList.add(Protocol.readXid(is, StreamUtils.readPackedUnsignedInt32(is)));
                    } else if (id == Protocol.P_XA_ERROR) {
                        int len = StreamUtils.readPackedSignedInt32(is);
                        int error = is.readInt();
                        final XAException xa = Log.log.peerXaException(error);
                        xa.initCause(RemoteExceptionCause.readFromStream(is));
                        if ((id = is.read()) != -1) {
                            XAException ex = Log.log.unrecognizedParameter(XAException.XAER_RMFAIL, id);
                            ex.addSuppressed(xa);
                            throw ex;
                        } else {
                            throw xa;
                        }
                    } else if (id == Protocol.P_SEC_EXC) {
                        int len = StreamUtils.readPackedSignedInt32(is);
                        final SecurityException sx = Log.log.peerSecurityException();
                        sx.initCause(RemoteExceptionCause.readFromStream(is));
                        if ((id = is.read()) != -1) {
                            XAException ex = Log.log.unrecognizedParameter(XAException.XAER_RMFAIL, id);
                            ex.addSuppressed(sx);
                            throw ex;
                        } else {
                            throw sx;
                        }
                    } else if (id != -1) {
                        throw Log.log.unrecognizedParameter(XAException.XAER_RMFAIL, id);
                    } else {
                        break;
                    }
                    id = is.read();
                }
            }
            return recoveryList.toArray(SimpleXid.NO_XIDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw Log.log.interruptedXA(XAException.XAER_RMERR);
        } catch (IOException e) {
            throw Log.log.responseFailedXa(e, XAException.XAER_RMERR);
        }
    }

    InvocationTracker getInvocationTracker() {
        return invocationTracker;
    }

    static TransactionClientChannel forConnection(final Connection connection) throws IOException {
        return CLIENT_SERVICE_HANDLE.getClientService(connection, OptionMap.EMPTY).get();
    }

    Channel.Receiver getReceiver() {
        return receiver;
    }

    Connection getConnection() {
        return channel.getConnection();
    }

    class ReceiverImpl implements Channel.Receiver {
        public void handleError(final Channel channel, final IOException error) {
            handleEnd(channel);
        }

        public void handleEnd(final Channel channel) {
            for (RemotingRemoteTransactionHandle transaction : peerTransactionMap) {
                transaction.disconnect();
            }
        }

        public void handleMessage(final Channel channel, final MessageInputStream message) {
            try {
                channel.receiveMessage(this);
                final int invId;
                try {
                    invId = message.readUnsignedShort();
                } catch (IOException e) {
                    // can't do much about this, but it's really unlikely anyway
                    Log.log.inboundException(e);
                    return;
                }
                invocationTracker.signalResponse(invId, 0, message, true);
            } finally {
                safeClose(message);
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy