Package org.apache.geronimo.farm.discovery.multicast

Source Code of org.apache.geronimo.farm.discovery.multicast.MulticastDiscoveryAgent$Broadcaster

/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.geronimo.farm.discovery.multicast;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.geronimo.farm.discovery.DiscoveryAgent;
import org.apache.geronimo.farm.discovery.DiscoveryListener;
import org.apache.geronimo.farm.service.NodeService;
import org.apache.geronimo.farm.service.NodeServiceVitals;
import org.apache.geronimo.farm.service.NodeServiceVitalsFactory;
import org.apache.geronimo.gbean.GBeanLifecycle;
import org.apache.geronimo.gbean.annotation.GBean;
import org.apache.geronimo.gbean.annotation.ParamAttribute;
import org.apache.geronimo.gbean.annotation.ParamReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @version $Rev$ $Date$
*/
@GBean
public class MulticastDiscoveryAgent implements DiscoveryAgent, GBeanLifecycle {

    private static final Logger log = LoggerFactory.getLogger(MulticastDiscoveryAgent.class);

    private static final int BUFF_SIZE = 8192;


    private AtomicBoolean started = new AtomicBoolean(false);
    private MulticastSocket multicast;

    private String host = "239.255.3.2";
    private int port = 6142;

    private int timeToLive = 1;
    private boolean loopbackMode = false;
    private SocketAddress address;

    private Map<String, NodeService> registeredServices = new ConcurrentHashMap<String, NodeService>();

    private int maxMissedHeartbeats = 10;
    private long heartRate = 500;

    private final Listener listener;

    private final NodeServiceVitalsFactory serviceVitalsFactory;



    public MulticastDiscoveryAgent(@ParamReference(name="MulticastLocation")MulticastLocation location,
                                   @ParamAttribute(name="heartRate") long heartRate,
                                   @ParamAttribute(name="maxMissedHeartbeats") int maxMissedHeartbeats,
                                   @ParamAttribute(name="loopbackMode"boolean loopbackMode,
                                   @ParamReference(name="NodeServiceVitalsFactory") NodeServiceVitalsFactory serviceVitalsFactory
) {
        this.serviceVitalsFactory = serviceVitalsFactory;
        this.host = location.getHost();
        this.port = location.getPort();
        this.heartRate = heartRate;
        this.maxMissedHeartbeats = maxMissedHeartbeats;
        this.loopbackMode = loopbackMode;

        listener = new Listener();
    }


    public String getIP() {
        return host;
    }

    public String getName() {
        return "multicast";
    }

    public int getPort() {
        return port;
    }

    public void setDiscoveryListener(DiscoveryListener listener) {
        this.listener.setDiscoveryListener(listener);
    }

    public void registerService(URI serviceUri) throws IOException {
        NodeService service = new NodeService(serviceUri);
        this.registeredServices.put(service.getUriString(), service);
    }

    public void unregisterService(URI serviceUri) throws IOException {
        NodeService service = new NodeService(serviceUri);
        this.registeredServices.remove(service.getUriString());
    }

    public void reportFailed(URI serviceUri) throws IOException {
        listener.reportFailed(serviceUri);
    }


    private boolean isSelf(NodeService service) {
        return isSelf(service.getUriString());
    }

    private boolean isSelf(String service) {
        return registeredServices.keySet().contains(service);
    }

    public static void main(String[] args) throws Exception {
    }

    /**
     * start the discovery agent
     *
     * @throws Exception on error
     */
    public void doStart() throws Exception {
            if (started.compareAndSet(false, true)) {

                newSocket();

                Thread listenerThread = new Thread(listener);
                listenerThread.setName("MulticastDiscovery: Listener");
                listenerThread.setDaemon(true);
                listenerThread.start();

                Broadcaster broadcaster = new Broadcaster();

                Timer timer = new Timer("MulticastDiscovery: Broadcaster", true);
                timer.scheduleAtFixedRate(broadcaster, 0, heartRate);
            }
    }

    private void newSocket() throws IOException {
        InetAddress inetAddress = InetAddress.getByName(host);

        this.address = new InetSocketAddress(inetAddress, port);

        multicast = new MulticastSocket(port);
        multicast.setLoopbackMode(loopbackMode);
        multicast.setTimeToLive(timeToLive);
        multicast.joinGroup(inetAddress);
        multicast.setSoTimeout((int) heartRate);
    }

    /**
     * stop the channel
     *
     * @throws Exception pm error
     */
    public void doStop() throws Exception {
        if (started.compareAndSet(true, false)) {
            multicast.close();
        }
    }

    /**
     * Fails the GBean.  This informs the GBean that it is about to transition to the failed state.
     */
    public void doFail() {
        try {
            doStop();
        } catch (Exception e) {
            //ignore
        }
    }


 


    class Listener implements Runnable {
        private Map<String, NodeServiceVitals> discoveredServices = new ConcurrentHashMap<String, NodeServiceVitals>();
        private DiscoveryListener discoveryListener;

        public void setDiscoveryListener(DiscoveryListener discoveryListener) {
            this.discoveryListener = discoveryListener;
        }

        public void run() {
            byte[] buf = new byte[BUFF_SIZE];
            DatagramPacket packet = new DatagramPacket(buf, 0, buf.length);
            while (started.get()) {
                checkServices();
                try {
                    multicast.receive(packet);
                    if (packet.getLength() > 0) {
                        String str = new String(packet.getData(), packet.getOffset(), packet.getLength());
                       System.out.println("read = " + str);
                        processData(str);
                    }
                } catch (SocketTimeoutException se) {
                    // ignore
                } catch (IOException e) {
                    if (started.get()) {
                        log.error("failed to process packet: " + e);
                    }
                }
            }
        }

        private void processData(String uriString) {
            if (discoveryListener == null) {
                return;
            }
            if (isSelf(uriString)) {
                return;
            }

            NodeServiceVitals vitals = discoveredServices.get(uriString);

            if (vitals == null) {
                try {
                    vitals = serviceVitalsFactory.createSerivceVitals(new NodeService(uriString));

                    discoveredServices.put(uriString, vitals);

                    fireServiceAddEvent(vitals.getService().getUri());
                } catch (URISyntaxException e) {
                    // don't continuously log this
                }

            } else {
                vitals.heartbeat();

                if (vitals.doRecovery()) {
                    fireServiceAddEvent(vitals.getService().getUri());
                }
            }
        }

        private void checkServices() {
            long expireTime = System.currentTimeMillis() - (heartRate * maxMissedHeartbeats);
            for (NodeServiceVitals serviceVitals : discoveredServices.values()) {
                if (serviceVitals.getLastHeartbeat() < expireTime && !isSelf(serviceVitals.getService())) {

                    NodeServiceVitals vitals = discoveredServices.remove(serviceVitals.getService().getUriString());
                    if (vitals != null && !vitals.isDead()) {
                        fireServiceRemovedEvent(vitals.getService().getUri());
                    }
                }
            }
        }

        private final Executor executor = new ThreadPoolExecutor(1, 1, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
            public Thread newThread(Runnable runable) {
                Thread t = new Thread(runable, "Multicast Discovery Agent Notifier");
                t.setDaemon(true);
                return t;
            }
        });

        private void fireServiceRemovedEvent(final URI uri) {
            if (discoveryListener != null) {
                final DiscoveryListener discoveryListener = this.discoveryListener;

                // Have the listener process the event async so that
                // he does not block this thread since we are doing time sensitive
                // processing of events.
                executor.execute(new Runnable() {
                    public void run() {
                        if (discoveryListener != null) {
                            discoveryListener.serviceRemoved(uri);
                        }
                    }
                });
            }
        }

        private void fireServiceAddEvent(final URI uri) {
            System.out.println("discoveryAgent.fireServiceAddEvent");
            if (discoveryListener != null) {
                final DiscoveryListener discoveryListener = this.discoveryListener;

                // Have the listener process the event async so that
                // he does not block this thread since we are doing time sensitive
                // processing of events.
                executor.execute(new Runnable() {
                    public void run() {
                        if (discoveryListener != null) {
                            discoveryListener.serviceAdded(uri);
                        }
                    }
                });
            }
        }

        public void reportFailed(URI serviceUri) {
            final NodeService service = new NodeService(serviceUri);
            NodeServiceVitals serviceVitals = discoveredServices.get(service.getUriString());
            if (serviceVitals != null && serviceVitals.pronounceDead()) {
                fireServiceRemovedEvent(service.getUri());
            }
        }
    }

    class Broadcaster extends TimerTask {
        private IOException failed;

        public void run() {
            if (started.get()) {
                heartbeat();
            }
        }

        private void heartbeat() {
            for (String uri : registeredServices.keySet()) {
                try {
                    byte[] data = uri.getBytes();
                    DatagramPacket packet = new DatagramPacket(data, 0, data.length, address);
                    System.out.println("heart beat  = " + uri);
                    multicast.send(packet);
                    failed = null;
                } catch (IOException e) {
                    // If a send fails, chances are all subsequent sends will fail
                    // too.. No need to keep reporting the
                    // same error over and over.
                    try {
                        newSocket();
                    } catch (IOException e1) {
                        //ignore
                    }
                    if (failed == null) {
                        failed = e;

                        log.error("Failed to advertise our service: " + uri, e);
                        if ("Operation not permitted".equals(e.getMessage())) {
                            log.error("The 'Operation not permitted' error has been know to be caused by improper firewall/network setup.  "
                                    + "Please make sure that the OS is properly configured to allow multicast traffic over: " + multicast.getLocalAddress());
                        }
                    }
                }
            }
        }
    }

}
TOP

Related Classes of org.apache.geronimo.farm.discovery.multicast.MulticastDiscoveryAgent$Broadcaster

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.