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

org.jgroups.fork.ForkChannel Maven / Gradle / Ivy

There is a newer version: 5.3.13.Final
Show newest version
package org.jgroups.fork;

import org.jgroups.*;
import org.jgroups.protocols.FORK;
import org.jgroups.stack.AddressGenerator;
import org.jgroups.stack.Protocol;
import org.jgroups.stack.ProtocolStack;
import org.jgroups.util.Util;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;

/**
 * Implementation of a ForkChannel, which is a light-weight channel. Not all methods are supported,
 * UnsupportedOperationExceptions will be thrown if an unsupported operation is called.
 * See doc/design/FORK.txt for details
 * @author Bela Ban
 * @since  3.4
 */
public class ForkChannel extends JChannel implements ChannelListener {
    protected final JChannel          main_channel;
    protected final String            fork_channel_id;
    protected static final Field[]    copied_fields;

    static {
        String[] fields={"state", "local_addr", "name", "cluster_name", "view"};
        copied_fields=new Field[fields.length];
        for(int i=0; i < fields.length; i++) {
            Field field=Util.getField(JChannel.class, fields[i]);
            if(field == null)
                throw new IllegalStateException("field \"" + fields[i] + "\" not found in JChannel");
            copied_fields[i]=field;
        }
    }


    /**
     * Creates a new fork-channel from a main-channel. The channel is unconnected and {@link ForkChannel#connect(String)}
     * needs to be called to send and receive messages.
     * @param main_channel The main-channel. The lifetime of the newly created channel will be less than or equal to
     *                     the main-channel
     * @param fork_stack_id The ID to associate the fork-stack with in FORK
     * @param fork_channel_id The ID used to map fork-channel IDs to ForkChannels in the fork-channels protocol stack
     * @param create_fork_if_absent If true, and FORK doesn't exist, a new FORK protocol will be created and inserted
     *                              into the main-stack at the given position. If false, and FORK doesn't exist, an
     *                              exception will be thrown
     * @param position The position at which the newly created FORK will be inserted. {@link ProtocolStack.Position#ABOVE} or
     *                 {@link ProtocolStack.Position#BELOW} are accepted. Ignored if create_fork_if_absent is false.
     * @param neighbor The class of the neighbor protocol below or above which the newly created FORK protocol will
     *                 be inserted. Ignored if create_fork_if_absent is false.
     * @param protocols A list of protocols (from bottom to top !) to insert as the fork_stack in FORK under the
     *                  given fork_stack_id. If the fork-stack with fork_stack_id already exists, an exception will be
     *                  thrown.
     *                  Can be null if no protocols should be added. This may be the case when an app only wants to use
     *                  a ForkChannel to mux/demux messages, but doesn't need a different protocol stack.
     *
     * @throws Exception
     */
    public ForkChannel(final JChannel main_channel, String fork_stack_id, String fork_channel_id,
                       boolean create_fork_if_absent, ProtocolStack.Position position, Class neighbor,
                       Protocol ... protocols) throws Exception {

        super(false);
        if(main_channel == null)    throw new IllegalArgumentException("main channel cannot be null");
        if(fork_stack_id == null)   throw new IllegalArgumentException("fork_stack_id cannot be null");
        if(fork_channel_id == null) throw new IllegalArgumentException("fork_channel_id cannot be null");

        this.main_channel=main_channel;
        this.fork_channel_id=fork_channel_id;

        FORK fork;
        // To prevent multiple concurrent FORK creations https://issues.redhat.com/browse/JGRP-1842
        synchronized(this.main_channel) {
            fork=getFORK(main_channel, position, neighbor, create_fork_if_absent);
        }

        // Returns the existing fork stack for fork_stack_id, or creates a new one
        prot_stack=fork.createForkStack(fork_stack_id, protocols == null? null : Arrays.asList(protocols), true);
        flush_supported=main_channel.flushSupported();
        state=State.OPEN;
    }

    /**
     * Creates a new fork-channel from a main-channel. The channel is unconnected and {@link ForkChannel#connect(String)}
     * needs to be called to send and receive messages. If FORK is not found in the stack, an exception will be thrown.
     * @param main_channel The main-channel. The lifetime of the newly created channel will be less than or equal to
     *                     the main-channel
     * @param fork_stack_id The ID to associate the fork-stack with in FORK
     * @param fork_channel_id The ID used to map fork-channel IDs to ForkChannels in the fork-channels protocol stack
     * @param protocols A list of protocols (from bottom to top !) to insert as the fork_stack in FORK under the
     *                  given fork_stack_id. If the fork-stack with fork_stack_id already exists, an exception will be
     *                  thrown.
     *                  Can be null if no protocols should be added. This may be the case when an app only wants to use
     *                  a ForkChannel to mux/demux messages, but doesn't need a different protocol stack.
     * @throws Exception
     */
    public ForkChannel(final JChannel main_channel, String fork_stack_id, String fork_channel_id,
                       Protocol ... protocols) throws Exception {
        this(main_channel, fork_stack_id, fork_channel_id, false, ProtocolStack.Position.ABOVE, null, protocols);
    }

    @Override
    public ForkChannel setName(String name) {
        log.error("name (%s) cannot be set in a fork-channel", name);
        return this;
    }

    @Override
    public JChannel name(String name) {
        log.error("name (%s) cannot be set in a fork-channel", name);
        return this;
    }

    @Override public void channelConnected(JChannel channel) {
        copyFields();
        if(local_addr == null) return;
        if(up_handler != null)
            up_handler.setLocalAddress(local_addr);
    }

    @Override public void channelDisconnected(JChannel channel) {
        copyFields();
    }

    @Override public void channelClosed(JChannel channel) {
        copyFields();
    }

    /**
     * Connects the fork-channel, which will be operational after this. Note that the fork-channel will
     * have the same state as the main-channel, ie. if the main-channel is disconnected, so will the fork-channel be,
     * even if connect() was called. This connect() method essentially adds the fork-channel to the fork-stack's hashmap,
     * ready to send/receive messages as soon as the main-channel has been connected.

* This method does not affect the main-channel. * @param cluster_name Ignored, will be the same as the main-channel's cluster name * @throws Exception */ @Override public ForkChannel connect(String cluster_name) throws Exception { if(!this.main_channel.isConnected()) throw new IllegalStateException("main channel is not connected"); if(state == State.CONNECTED) return this; if(state == State.CLOSED) throw new IllegalStateException("a closed fork channel cannot reconnect"); state=State.CONNECTING; this.main_channel.addChannelListener(this); copyFields(); JChannel existing_ch=((ForkProtocolStack)prot_stack).putIfAbsent(fork_channel_id,this); if(existing_ch != null && existing_ch != this) throw new IllegalArgumentException("fork-channel with id=" + fork_channel_id + " is already present"); setLocalAddress(local_addr); prot_stack.startStack(); prot_stack.down(new Event(Event.CONNECT, cluster_name)); View current_view=main_channel.getView(); if(current_view != null) { up(new Event(Event.VIEW_CHANGE, current_view)); prot_stack.down(new Event(Event.VIEW_CHANGE, current_view)); } state=State.CONNECTED; notifyChannelConnected(this); return this; } @Override public ForkChannel connect(String cluster_name, Address target, long timeout) throws Exception { connect(cluster_name); main_channel.getState(target, timeout); return this; } /** Removes the fork-channel from the fork-stack's hashmap and resets its state. Does not affect the * main-channel */ @Override public ForkChannel disconnect() { if(state != State.CONNECTED) return this; prot_stack.down(new Event(Event.DISCONNECT, local_addr)); // will be discarded by ForkProtocol prot_stack.stopStack(cluster_name); ((ForkProtocolStack)prot_stack).remove(fork_channel_id); nullFields(); state=State.OPEN; notifyChannelDisconnected(this); return this; } /** Closes the fork-channel, essentially setting its state to CLOSED. Note that - contrary to a regular channel - * a closed fork-channel can be connected again: this means re-attaching the fork-channel to the main-channel*/ @Override public void close() { ((ForkProtocolStack)prot_stack).remove(fork_channel_id); if(state == State.CLOSED) return; disconnect(); // leave cluster if connected prot_stack.destroy(); state=State.CLOSED; notifyChannelClosed(this); } @Override public Object down(Message msg) { setHeader(msg); return super.down(msg); } @Override public ForkChannel send(Message msg) throws Exception { checkClosedOrNotConnected(); FORK.ForkHeader hdr=msg.getHeader(FORK.ID); if(hdr != null) hdr.setForkChannelId(fork_channel_id); else { hdr=new FORK.ForkHeader(null, fork_channel_id); msg.putHeader(FORK.ID, hdr); } prot_stack.down(msg); return this; } @Override public ForkChannel startFlush(List

flushParticipants, boolean automatic_resume) throws Exception { throw new UnsupportedOperationException(); } @Override public ForkChannel startFlush(boolean automatic_resume) throws Exception { throw new UnsupportedOperationException(); } @Override public ForkChannel stopFlush() { throw new UnsupportedOperationException(); } @Override public ForkChannel stopFlush(List
flushParticipants) { throw new UnsupportedOperationException(); } @Override public ForkChannel getState(Address target, long timeout) throws Exception { main_channel.getState(target, timeout); return this; } @Override public ForkChannel addAddressGenerator(AddressGenerator address_generator) { main_channel.addAddressGenerator(address_generator); return this; } protected ForkChannel setLocalAddress(Address local_addr) { if(local_addr != null) { ((ForkProtocolStack)prot_stack).setLocalAddress(local_addr); // sets the address only in the protocols managed by the fork-prot-stack if(up_handler != null) up_handler.setLocalAddress(local_addr); } return this; } /** * Creates a new FORK protocol, or returns the existing one, or throws an exception. Never returns null. */ protected static FORK getFORK(JChannel ch, ProtocolStack.Position position, Class neighbor, boolean create_fork_if_absent) throws Exception { ProtocolStack stack=ch.getProtocolStack(); FORK fork=stack.findProtocol(FORK.class); if(fork == null) { if(!create_fork_if_absent) throw new IllegalArgumentException("FORK not found in main stack"); fork = new FORK(); fork.setProtocolStack(stack); stack.insertProtocol(fork, position, neighbor); } return fork; } protected void setHeader(Message msg) { FORK.ForkHeader hdr=msg.getHeader(FORK.ID); if(hdr != null) hdr.setForkChannelId(fork_channel_id); else msg.putHeader(FORK.ID, new FORK.ForkHeader(null, fork_channel_id)); } /** Copies state from main-channel to this fork-channel */ protected void copyFields() { for(Field field: copied_fields) { Object value=Util.getField(field,main_channel); Util.setField(field, this, value); } } protected void nullFields() { for(Field field: copied_fields) Util.setField(field, this, null); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy