Package org.cometd.oort

Source Code of org.cometd.oort.OortMulticastConfigurer$MulticastReceiver

/*
* Copyright (c) 2008-2014 the original author or authors.
*
* 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.cometd.oort;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.DatagramChannel;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import org.cometd.client.BayeuxClient;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class OortMulticastConfigurer extends AbstractLifeCycle
{
    private static final AtomicInteger ids = new AtomicInteger();

    private final Logger logger;
    private final Oort oort;
    private InetAddress bindAddress;
    private InetAddress groupAddress;
    private List<NetworkInterface> groupInterfaces;
    private int groupPort = 5577;
    private int timeToLive = 1;
    private long advertiseInterval = 2000;
    private long connectTimeout = 2000;
    private int maxTransmissionLength = 1400;
    private Thread receiverThread;
    private Thread senderThread;
    private volatile boolean active;

    public OortMulticastConfigurer(Oort oort)
    {
        this.logger = LoggerFactory.getLogger(getClass() + "." + Oort.replacePunctuation(oort.getURL(), '_'));
        this.oort = oort;
    }

    /**
     * @return the address to bind the receiver multicast socket to
     */
    public InetAddress getBindAddress()
    {
        return bindAddress;
    }

    /**
     * @param bindAddress the address to bind the receiver multicast socket to
     */
    public void setBindAddress(InetAddress bindAddress)
    {
        this.bindAddress = bindAddress;
    }

    /**
     * @return the multicast address onto which Oort URLs are advertised
     */
    public InetAddress getGroupAddress()
    {
        return groupAddress;
    }

    /**
     * @param groupAddress the multicast address onto which Oort URLs are advertised
     */
    public void setGroupAddress(InetAddress groupAddress)
    {
        this.groupAddress = groupAddress;
    }

    /**
     * @return the port the receiver multicast socket listens to
     */
    public int getGroupPort()
    {
        return groupPort;
    }

    /**
     * @param groupPort the port the receiver multicast socket listens to
     */
    public void setGroupPort(int groupPort)
    {
        this.groupPort = groupPort;
    }

    /**
     * @return the interfaces that receive multicast messages
     */
    public List<NetworkInterface> getGroupInterfaces()
    {
        return groupInterfaces;
    }

    /**
     * @param groupInterfaces the interfaces that receive multicast messages
     */
    public void setGroupInterfaces(List<NetworkInterface> groupInterfaces)
    {
        this.groupInterfaces = groupInterfaces;
    }

    /**
     * @return the multicast time-to-live
     */
    public int getTimeToLive()
    {
        return timeToLive;
    }

    /**
     * @param timeToLive the multicast time-to-live
     */
    public void setTimeToLive(int timeToLive)
    {
        this.timeToLive = timeToLive;
    }

    /**
     * @return the advertisement interval in milliseconds
     */
    public long getAdvertiseInterval()
    {
        return advertiseInterval;
    }

    /**
     * @param advertiseInterval the advertisement interval in milliseconds
     */
    public void setAdvertiseInterval(long advertiseInterval)
    {
        this.advertiseInterval = advertiseInterval;
    }

    /**
     * @return the timeout to connect to another Oort node
     */
    public long getConnectTimeout()
    {
        return connectTimeout;
    }

    /**
     * @param connectTimeout the timeout to connect to another Oort node
     */
    public void setConnectTimeout(long connectTimeout)
    {
        this.connectTimeout = connectTimeout;
    }

    /**
     * @return the max Oort URL length (must be smaller than the max transmission unit)
     */
    public int getMaxTransmissionLength()
    {
        return maxTransmissionLength;
    }

    /**
     * @param maxTransmissionLength the max Oort URL length (must be smaller than the max transmission unit)
     */
    public void setMaxTransmissionLength(int maxTransmissionLength)
    {
        this.maxTransmissionLength = maxTransmissionLength;
    }

    @Override
    protected void doStart() throws Exception
    {
        // Bind sender to an ephemeral port and set the TTL
        DatagramChannel sender = DatagramChannel.open();
        sender.setOption(StandardSocketOptions.IP_MULTICAST_TTL, getTimeToLive());

        // Bind receiver to the given port and bind address
        InetAddress bindTo = getBindAddress();
        InetSocketAddress bindSocketAddress = bindTo == null ? new InetSocketAddress(groupPort) : new InetSocketAddress(bindTo, groupPort);
        DatagramChannel receiver = DatagramChannel.open()
                .setOption(StandardSocketOptions.SO_REUSEADDR, true)
                .bind(bindSocketAddress);

        if (groupAddress == null)
            groupAddress = InetAddress.getByName("239.255.0.1");

        List<NetworkInterface> groupInterfaces = getGroupInterfaces();
        if (groupInterfaces == null)
            groupInterfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
        for (NetworkInterface groupInterface : groupInterfaces)
        {
            if (groupInterface.isLoopback() || groupInterface.isPointToPoint() || !groupInterface.supportsMulticast())
                continue;
            receiver.join(groupAddress, groupInterface);
        }

        active = true;

        senderThread = new Thread(new MulticastSender(sender), "Oort-Multicast-Sender-" + ids.incrementAndGet());
        senderThread.setDaemon(true);
        senderThread.start();

        receiverThread = new Thread(new MulticastReceiver(receiver), "Oort-Multicast-Receiver-" + ids.incrementAndGet());
        receiverThread.setDaemon(true);
        receiverThread.start();
    }

    @Override
    protected void doStop() throws Exception
    {
        active = false;
        senderThread.interrupt();
        receiverThread.interrupt();
    }

    public boolean join(long timeout)
    {
        try
        {
            senderThread.join(timeout);
            receiverThread.join(timeout);
            return true;
        }
        catch (InterruptedException x)
        {
            return false;
        }
    }

    protected void receive(String cometURL)
    {
        if (!oort.getKnownComets().contains(cometURL))
        {
            if (logger.isDebugEnabled())
                logger.debug("Received comet URL via multicast: {}", cometURL);
            OortComet oortComet = oort.observeComet(cometURL);
            if (oortComet != null)
            {
                boolean elapsed = !oortComet.waitFor(getConnectTimeout(), BayeuxClient.State.CONNECTED, BayeuxClient.State.DISCONNECTED);
                // If we could not connect, let's disconnect, we will be advertised again
                if (elapsed)
                {
                    if (logger.isDebugEnabled())
                        logger.debug("Interrupting attempts to connect to {}", cometURL);
                    oort.deobserveComet(cometURL);
                }
            }
        }
    }

    private void close(DatagramChannel channel)
    {
        try
        {
            channel.close();
        }
        catch (IOException x)
        {
            if (logger.isDebugEnabled())
                logger.debug("Could not close " + channel, x);
        }
    }

    private class MulticastReceiver implements Runnable
    {
        private final DatagramChannel channel;

        public MulticastReceiver(DatagramChannel channel)
        {
            this.channel = channel;
        }

        public void run()
        {
            try
            {
                if (logger.isDebugEnabled())
                    logger.debug("Entering multicast receiver thread on {}", channel.getLocalAddress());

                ByteBuffer buffer = ByteBuffer.allocate(getMaxTransmissionLength());
                String url = null;
                while (active)
                {
                    if (url != null)
                        OortMulticastConfigurer.this.receive(url);
                    url = receive(buffer);
                    // Avoid to process our own URL
                    if (oort.getURL().equals(url))
                        url = null;
                }
            }
            catch (ClosedByInterruptException x)
            {
                // Do nothing, we're stopping
            }
            catch (IOException x)
            {
                logger.warn("Unexpected exception", x);
            }
            finally
            {
                if (logger.isDebugEnabled())
                    logger.debug("Exiting multicast receiver thread");
                close(channel);
            }
        }

        private String receive(ByteBuffer buffer) throws IOException
        {
            buffer.clear();
            channel.receive(buffer);
            buffer.flip();
            return StandardCharsets.UTF_8.decode(buffer).toString();
        }
    }

    private class MulticastSender implements Runnable
    {
        private final DatagramChannel channel;

        public MulticastSender(DatagramChannel channel)
        {
            this.channel = channel;
        }

        public void run()
        {
            if (logger.isDebugEnabled())
                logger.debug("Entering multicast sender thread");
            try
            {
                final String cometURL = oort.getURL();
                byte[] cometURLBytes = cometURL.getBytes("UTF-8");
                if (cometURLBytes.length > getMaxTransmissionLength())
                {
                    logger.warn("Oort URL {} exceeds max transmission unit and will not be advertised", cometURL);
                    return;
                }

                ByteBuffer buffer = ByteBuffer.wrap(cometURLBytes);
                InetSocketAddress address = new InetSocketAddress(getGroupAddress(), getGroupPort());

                while (active)
                {
                    buffer.clear();
                    channel.send(buffer, address);
                    Thread.sleep(getAdvertiseInterval());
                }
            }
            catch (InterruptedException | ClosedByInterruptException x)
            {
                // Do nothing, we're stopping
            }
            catch (IOException x)
            {
                logger.warn("Unexpected exception", x);
            }
            finally
            {
                if (logger.isDebugEnabled())
                    logger.debug("Exiting multicast sender thread");
                close(channel);
            }
        }
    }
}
TOP

Related Classes of org.cometd.oort.OortMulticastConfigurer$MulticastReceiver

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.