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

org.piax.gtrans.impl.OneToOneMappingTransport Maven / Gradle / Ivy

The newest version!
/*
 * OneToOneMappingTransport.java
 * 
 * Copyright (c) 2012-2015 National Institute of Information and 
 * Communications Technology
 * 
 * You can redistribute it and/or modify it under either the terms of
 * the AGPLv3 or PIAX binary code license. See the file COPYING
 * included in the PIAX package for more in detail.
 * 
 * $Id: OneToOneMappingTransport.java 718 2013-07-07 23:49:08Z yos $
 */

package org.piax.gtrans.impl;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.piax.common.Endpoint;
import org.piax.common.ObjectId;
import org.piax.common.PeerId;
import org.piax.common.TransportId;
import org.piax.gtrans.Channel;
import org.piax.gtrans.ChannelListener;
import org.piax.gtrans.ChannelTransport;
import org.piax.gtrans.IdConflictException;
import org.piax.gtrans.ProtocolUnsupportedException;
import org.piax.gtrans.ReceivedMessage;
import org.piax.gtrans.TransOptions;
import org.piax.gtrans.Transport;
import org.piax.gtrans.TransportListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 下位層にTransportを持ち、send, channel.send をそのまま下位層のTransportに流すような
 * Transportを作成するためのテンプレートクラス
 * 
 * 
 */
public abstract class OneToOneMappingTransport extends
        ChannelTransportImpl implements TransportListener,
        ChannelListener {
    /*--- logger ---*/
    private static final Logger logger = LoggerFactory
            .getLogger(OneToOneMappingTransport.class);

    /**
     * OneToOneMappingTransportの内部で使用する下位層のchannelをラップするだけの
     * ChannelImplクラス
     */
    protected static class OneToOneChannel extends ChannelImpl {
        final Channel lowerCh;
        // new client channel
        OneToOneChannel(Channel lowerCh, ChannelTransport mother,
                ObjectId localObjId, ObjectId remoteObjId, E remote) {
            super(mother, localObjId, remoteObjId, remote, true);
            this.lowerCh = lowerCh;
        }

        // new accepted channel
        OneToOneChannel(Channel lowerCh, PeerId creator,
                ChannelTransport mother, ObjectId localObjId,
                ObjectId remoteObjId, E remote) {
            super(creator, mother, localObjId, remoteObjId, remote, true);
            this.lowerCh = lowerCh;
        }

        @Override
        public void close() {
        	 /* onReceiveの処理途中でclose処理が入ると、accept channelと間違うなど
        	  * おかしな動作をするので、lowerChで排他処理を行う。
        	  * XXX これは十分ではない?
        	  */
        	 synchronized (lowerCh) {
	            super.close();
	            ((OneToOneMappingTransport) mother).removeCh(lowerCh);
	            lowerCh.close();
        	 }
        }

        @Override
        public void send(Object msg) throws IOException {
            ((OneToOneMappingTransport) mother).chSend(this, msg);
        }
    };

    /** 下位層のchannelからOneToOneChannelを引くためのmap */
    private final Map, OneToOneChannel> relatedChMap = 
            new HashMap, OneToOneChannel>();

    protected OneToOneMappingTransport(TransportId transId,
            ChannelTransport lowerTrans) throws IdConflictException {
        super(lowerTrans.getPeer(), transId, lowerTrans, lowerTrans.supportsDuplex());
        lowerTrans.setListener(transId, this);
        lowerTrans.setChannelListener(transId, this);
    }

    @Override
    public E getEndpoint() {
        return getLowerTransport().getEndpoint();
    }

    @Override
    public int getMTU() {
        return lowerTrans.getMTU();
    }
    
    /*
     * 下位層にセットするlowerTransは、ChannelTransportでかつ、
     * destination が E と一致することに注意 
     */
    @Override
    public ChannelTransport getLowerTransport() {
        @SuppressWarnings("unchecked")
        ChannelTransport lower = (ChannelTransport) lowerTrans;
        return lower;
    }

    @Override
    public Channel newChannel(ObjectId sender, ObjectId receiver, E dst,
            boolean isDuplex, int timeout) throws ProtocolUnsupportedException,
            IOException {
        Channel lowerCh = getLowerTransport().newChannel(transId, dst,
                isDuplex, timeout);
        OneToOneChannel ch = new OneToOneChannel(lowerCh, this, sender,
                receiver, dst);
        if (lowerCh instanceof ChannelImpl) {
            ch.setChannelNo(lowerCh.getChannelNo());
        }
        putCh(lowerCh, ch);
        return ch;
    }

    protected OneToOneChannel putCh(Channel lowerCh, OneToOneChannel ch) {
        synchronized (relatedChMap) {
            return relatedChMap.put(lowerCh, ch);
        }
    }

    protected void removeCh(Channel lowerCh) {
        synchronized (relatedChMap) {
            relatedChMap.remove(lowerCh);
        }
    }

    protected OneToOneChannel getCh(Channel lowerCh) {
        synchronized (relatedChMap) {
            return relatedChMap.get(lowerCh);
        }
    }

    public boolean onAccepting(Channel lowerCh) {
        // log出力の便宜のため、currentThread名にpeerIdを付与する
        peer.concatPeerId2ThreadName();
        if (!this.isActive) {
            logger.info("receive event purged");
            return false;
        }
        return true;
    }

    public void onClosed(Channel lowerCh) {
        // log出力の便宜のため、currentThread名にpeerIdを付与する
        peer.concatPeerId2ThreadName();
        if (!this.isActive) {
            logger.info("receive event purged");
            return;
        }
        ChannelImpl ch = getCh(lowerCh);
        if (ch == null) {
            // 制御用等に使われているlowerChは当該channelとの紐付けがないため、無視される
            return;
        }
        ch.close();
        // ** raise notification
        // このTransportが作成したchannelについては無視する
        if (!ch.remoteObjectId.equals(transId)) {
            ChannelListener listener = getChannelListener(ch.localObjectId);
            if (listener != null)
                listener.onClosed(ch);
        }
    }

    public void onFailure(Channel lowerCh, Exception cause) {
        // log出力の便宜のため、currentThread名にpeerIdを付与する
        peer.concatPeerId2ThreadName();
        if (!this.isActive) {
            logger.info("receive event purged");
            return;
        }
        ChannelImpl ch = getCh(lowerCh);
        if (ch == null) {
            // 制御用等に使われているlowerChは当該channelとの紐付けがないため、無視される
            return;
        }
        // ** raise notification
        // このTransportが作成したchannelについては無視する
        if (!ch.remoteObjectId.equals(transId)) {
            ChannelListener listener = getChannelListener(ch.localObjectId);
            if (listener != null)
                listener.onFailure(ch, cause);
        }
    }

    // *** about message sending ***

    /*
     * サブクラスで送信時のロジックを埋めるための用いる。
     * 返り値には変換後のmsgを返す。
     * nullを返すと処理を中断する
     */
    protected Object _preSend(ObjectId sender, ObjectId receiver,
            E dst, Object msg) throws IOException {
        return msg;
    };
    
    protected void lowerSend(ObjectId sender, ObjectId receiver,
            E dst, NestedMessage nmsg, TransOptions opts) throws ProtocolUnsupportedException,
            IOException {
        @SuppressWarnings("unchecked")
        ChannelTransport lower = (ChannelTransport) lowerTrans;
        lower.send(sender, receiver, dst, nmsg, opts);
    }

    protected void lowerChSend(Channel ch, NestedMessage nmsg) throws IOException {
        ch.send(nmsg);
    }
    
    @Override
    public void send(ObjectId sender, ObjectId receiver, E dst, Object msg, TransOptions opts)
            throws ProtocolUnsupportedException, IOException {
        logger.trace("ENTRY:");
        // サブクラスで定義する送信前処理
        Object _msg = _preSend(sender, receiver, dst, msg);
        // _msgがnullの場合は処理を中断
        if (_msg == null) return;
        // msgをネストさせる
        NestedMessage nmsg = new NestedMessage(sender, receiver, null,
                getEndpoint(), _msg);
        // 下位層のsendを呼ぶ
        lowerSend(transId, transId, dst, nmsg, opts);
    }

    protected void chSend(OneToOneChannel ch, Object msg)
            throws IOException {
        logger.trace("ENTRY:");

        // サブクラスで定義する送信前処理
        Object _msg = _preSend(ch.localObjectId, ch.remoteObjectId, ch.remote, msg);
        // _msgがnullの場合は処理を中断
        if (_msg == null) return;
        // msgをネストさせる
        NestedMessage nmsg = new NestedMessage(ch.localObjectId,
                ch.remoteObjectId, getPeerId(), getEndpoint(), _msg);
        // 下位層のchannel.sendを呼ぶ
        lowerChSend(ch.lowerCh, nmsg);
    }

    // *** about message receiving ***

    /*
     * サブクラスで受信時のロジックを埋めるための用いる。
     * 返り値には変換後のmsgを返す。
     * nullを返すと処理を中断する
     */
    protected Object _postReceive(ObjectId sender, ObjectId receiver,
            E src, Object msg) {
        return msg;
    }

    @SuppressWarnings("unchecked")
    private OneToOneChannel newAcceptCh(Channel lowerCh, NestedMessage nmsg) {
        OneToOneChannel ch = new OneToOneChannel(lowerCh, nmsg.srcPeerId, this,
                nmsg.receiver, nmsg.sender, (E) nmsg.src);
        ch.setChannelNo(lowerCh.getChannelNo());
        if (putCh(lowerCh, ch) != null) {
            logger.error("invaild lower channel accepted");
        }
        return ch;
    }

    public void onReceive(Channel lowerCh) {
        // log出力の便宜のため、currentThread名にpeerIdを付与する
        peer.concatPeerId2ThreadName();
        if (!this.isActive) {
            logger.info("receive msg purged");
            return;
        }
        NestedMessage nmsg = null;
        OneToOneChannel ch = null;
        synchronized (lowerCh) {
            /*
             * lowerCh.receive()で受理したメッセージを、ch.putReceiveQueueへ渡す順序を
             * 保存するため、synchronized にしている
             */
            if (lowerCh.isClosed()) return; // 既にcloseされていたらなにもしない。
            nmsg = (NestedMessage) lowerCh.receive();
            if (nmsg == null) {
                logger.error("null message received");
                return;
            }
            ch = _putReceiveQueue(lowerCh, nmsg);
        }
        if (ch != null && nmsg != null) {
            _onReceive(ch, nmsg);
        }
    }
    
    // Put the received nested message on the corresponding channel queue.
    // Returns true if successfully finished.
    @SuppressWarnings("unchecked")
    protected OneToOneChannel _putReceiveQueue(Channel lowerCh, NestedMessage nmsg) {
        /*
         * TODO think!
         * この処理では、下位から受信したメッセージを一回lowerCh.receiveしてから、
         * このTransportの持つChannelのBlockingQueueにputしている。
         * この場合は、下位層のBlockingQueueを共有することが効率的であるが、要検討
         */
        logger.trace("ENTRY:");
        OneToOneChannel ch = getCh(lowerCh);
        if (ch == null) {
            // channel未登録であることから、accept channelの処理を行う
            ch = newAcceptCh(lowerCh, nmsg);
            // listenerにdispatchする
            ChannelListener listener = getChannelListener(nmsg.receiver);
            if (listener != null) {
                if (!listener.onAccepting(ch)) {
                    // listenerにacceptを拒否された場合
                    removeCh(lowerCh);
                    /*
                     * TODO 単純にメッセージを破棄する。
                     * acceptの拒否を送信元に送る必要があるが、このためには、newChannel時に
                     * 制御メッセージを交換する必要があるため、ペンディングにしておく。
                     */
                    return null;
                }
            }
        }
        if (lowerCh.isClosed()) return null;
        // サブクラスで定義する受信後処理を呼ぶ
        Object _msg = _postReceive(nmsg.sender, nmsg.receiver, (E) nmsg.src,
                nmsg.getInner());
        // _msgがnullの場合は処理を中断
        if (_msg == null) return null;
        // chにinner msgをputする
        ch.putReceiveQueue(_msg);
        return ch;
    }

    protected void _onReceive(Channel ch, NestedMessage nmsg) {
        if (ch != null) {
            // listenerにdispatchする
            ChannelListener listener = getChannelListener(nmsg.receiver);
            if (listener != null) {
                listener.onReceive(ch);
            }
        }
    }

    public void onReceive(Transport lowerTrans, ReceivedMessage rmsg) {
        // log出力の便宜のため、currentThread名にpeerIdを付与する
        peer.concatPeerId2ThreadName();
        if (!this.isActive) {
            logger.info("received msg purged {}", rmsg);
            return;
        }
        NestedMessage nmsg = (NestedMessage) rmsg.getMessage();
        _onReceive(lowerTrans, nmsg);
    }

    @SuppressWarnings("unchecked")
    protected void _onReceive(Transport lowerTrans, NestedMessage nmsg) {
        logger.trace("ENTRY:");
        // サブクラスで定義する受信後処理を呼ぶ
        Object _msg = _postReceive(nmsg.sender, nmsg.receiver, (E) nmsg.src,
                nmsg.getInner());
        // _msgがnullの場合は処理を中断
        if (_msg == null) return;
        // listenerにdispatchする
        TransportListener listener = getListener(nmsg.receiver);
        if (listener != null) {
            listener.onReceive(this, new ReceivedMessage(nmsg.sender,
                    nmsg.src, _msg));
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy