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

org.ow2.petals.bc.gateway.commons.AbstractDomain Maven / Gradle / Ivy

There is a newer version: 1.2.0
Show newest version
/**
 * Copyright (c) 2016 Linagora
 * 
 * This program/library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or (at your
 * option) any later version.
 * 
 * This program/library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program/library; If not, see http://www.gnu.org/licenses/
 * for the GNU Lesser General Public License version 2.1.
 */
package org.ow2.petals.bc.gateway.commons;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Logger;

import javax.jbi.messaging.MessageExchange;
import javax.jbi.messaging.MessagingException;

import org.eclipse.jdt.annotation.Nullable;
import org.ow2.petals.bc.gateway.JBISender;
import org.ow2.petals.bc.gateway.commons.messages.TransportedException;
import org.ow2.petals.bc.gateway.commons.messages.TransportedForExchange;
import org.ow2.petals.bc.gateway.commons.messages.TransportedMessage;
import org.ow2.petals.bc.gateway.utils.BcGatewayJbiHelper.Pair;
import org.ow2.petals.commons.log.FlowAttributes;
import org.ow2.petals.commons.log.Level;
import org.ow2.petals.commons.log.PetalsExecutionContext;
import org.ow2.petals.component.framework.api.message.Exchange;
import org.ow2.petals.component.framework.su.ServiceUnitDataHandler;

import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;

public abstract class AbstractDomain {

    // this is not really used as an exception for knowing where it happened
    // we can thus reuse it and avoid the overhead of creating the exception
    public static final MessagingException TIMEOUT_EXCEPTION = new MessagingException(
            "A timeout happened while the BC Gateway sent an exchange to a JBI service");

    static {
        TIMEOUT_EXCEPTION.setStackTrace(new StackTraceElement[0]);
    }

    protected final Logger logger;

    protected final ServiceUnitDataHandler handler;

    private final JBISender sender;

    /**
     * Exchange is added
     */
    private final ConcurrentMap> exchangesInProgress = new ConcurrentHashMap<>();

    public AbstractDomain(final JBISender sender, final ServiceUnitDataHandler handler, final Logger logger) {
        this.sender = sender;
        this.handler = handler;
        this.logger = logger;
    }

    public ServiceUnitDataHandler getSUHandler() {
        return handler;
    }

    public abstract String getId();

    protected abstract void logAfterReceivingFromChannel(TransportedMessage m);

    public void receiveFromChannel(final ChannelHandlerContext ctx, final TransportedForExchange m) {
        // in all case where I receive something, I remove the exchange I stored before!
        // if it has to come back (e.g., for InOptOut fault after out) it will be put back
        final Pair stored = exchangesInProgress.remove(m.exchangeId);

        // let's get the flow attribute from the received exchange and put them in context as soon as we get it
        // TODO add tests!
        if (stored != null) {
            PetalsExecutionContext.putFlowAttributes(stored.getB());
        } else {
            PetalsExecutionContext.initFlowAttributes();
        }

        if (m instanceof TransportedException) {
            assert stored != null;
            logger.log(Level.WARNING,
                    "Received an exception from the other side, this is purely informative, we can't do anything about it",
                    ((TransportedException) m).cause);
        } else if (m instanceof TransportedMessage) {
            final TransportedMessage tm = (TransportedMessage) m;

            assert tm.step == 1 ^ stored != null;

            // this will do logs
            logAfterReceivingFromChannel(tm);

            sendToNMR(ctx, tm, stored != null ? stored.getA() : null);
        } else {
            throw new IllegalArgumentException("Impossible case");
        }
    }

    private void sendToNMR(final ChannelHandlerContext ctx, final TransportedMessage m,
            final @Nullable Exchange exchange) {

        this.sender.sendToNMR(getContext(this, ctx, m), exchange);
    }

    private static DomainContext getContext(final AbstractDomain domain, final ChannelHandlerContext ctx,
            final TransportedMessage m) {
        return new DomainContext() {
            @Override
            public void sendToChannel(final Exchange exchange) {
                domain.sendFromNMRToChannel(ctx, m, exchange);
            }

            @Override
            public void sendToChannel(final Exception e) {
                domain.sendFromNMRToChannel(ctx, m, e);
            }

            @Override
            public void sendTimeoutToChannel() {
                domain.sendTimeoutFromNMRToChannel(ctx, m);
            }

            @Override
            public TransportedMessage getMessage() {
                return m;
            }
        };
    }

    private void sendFromNMRToChannel(final ChannelHandlerContext ctx, final TransportedMessage m,
            final Exception e) {

        logger.log(Level.FINE, "Exception caught", e);

        final TransportedForExchange msg;
        if (m.last) {
            msg = new TransportedException(m, e);
        } else {
            m.exchange.setError(e);
            msg = TransportedMessage.lastMessage(m, m.exchange);
        }

        sendToChannel(ctx, msg);
    }

    private void sendFromNMRToChannel(final ChannelHandlerContext ctx, final TransportedMessage m,
            final Exchange exchange) {
        assert !m.last;

        final TransportedMessage msg;
        final MessageExchange mex = exchange.getMessageExchange();
        assert mex != null;

        if (exchange.isActiveStatus()) {
            // we will be expecting an answer
            msg = TransportedMessage.middleMessage(m, mex);
        } else {
            msg = TransportedMessage.lastMessage(m, mex);
        }

        sendToChannel(ctx, msg, exchange);
    }

    private void sendTimeoutFromNMRToChannel(final ChannelHandlerContext ctx, final TransportedMessage m) {
        m.exchange.setError(TIMEOUT_EXCEPTION);
        sendToChannel(ctx, TransportedMessage.lastMessage(m, m.exchange));
    }

    protected void sendToChannel(final ChannelHandlerContext ctx, final TransportedMessage m, final Exchange exchange) {
        if (!m.last) {
            // the current flow is either the provide step or the consume ext step
            final FlowAttributes fa = PetalsExecutionContext.getFlowAttributes();
            // it was set by the CDK (or us if it didn't have the time to go through the NMR)
            assert fa != null;
            final Pair prev = exchangesInProgress.putIfAbsent(m.exchangeId,
                    Pair.of(exchange, fa));
            assert prev == null;
        }

        sendToChannel(ctx, m);
    }

    protected abstract void logBeforeSendingToChannel(TransportedMessage m);

    private void sendToChannel(final ChannelHandlerContext ctx, final TransportedForExchange m) {

        if (m instanceof TransportedMessage) {
            logBeforeSendingToChannel((TransportedMessage) m);
        }

        ctx.writeAndFlush(m).addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(final @Nullable ChannelFuture future) throws Exception {
                assert future != null;
                // TODO add tests for these use cases!
                if (!future.isSuccess()) {
                    // TODO introduce some basic retrying before cancelling the send
                    // Careful because if we reconnect, I guess the channel is not the same one?!?!
                    final Throwable cause = future.cause();
                    // TODO is the channel notified of the error too?
                    if (m instanceof TransportedMessage && !((TransportedMessage) m).last
                            && cause instanceof Exception) {
                        final TransportedMessage tm = (TransportedMessage) m;
                        tm.exchange.setError((Exception) cause);
                        // TODO what about the other side waiting for this exchange?! it should be removed there... but
                        // if there is a connection problem, then maybe it is simply that it was stopped?
                        logger.log(Level.WARNING,
                                "Can't send message over the channel, sending back the error over the NMR: " + m,
                                cause);
                        // TODO there will be double copy of the error in the exchange again by the JBI Sender...
                        // improve that!
                        receiveFromChannel(ctx, TransportedMessage.lastMessage(tm, tm.exchange));
                    } else {
                        logger.log(Level.WARNING, "Can't send message over the channel but nothing I can do now: " + m,
                                cause);
                    }
                }
            }
        });
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy