//package com.lts.core;
//
//import com.lts.core.cluster.Node;
//import com.lts.core.commons.utils.StringUtils;
//import com.lts.core.constant.Constants;
//import com.lts.core.factory.NamedThreadFactory;
//import com.lts.core.logger.Logger;
//import com.lts.core.logger.LoggerFactory;
//import com.lts.core.registry.FailbackRegistry;
//import com.lts.core.registry.NodeRegistryUtils;
//import com.lts.core.registry.NotifyListener;
//
//import java.io.IOException;
//import java.net.*;
//import java.util.*;
//import java.util.concurrent.*;
//
///**
// * @author Robert HG (254963746@qq.com) on 9/10/15.
// */
//public class MulticastRegistry extends FailbackRegistry {
//
//    // 日志输出
//    private static final Logger logger = LoggerFactory.getLogger(MulticastRegistry.class);
//
//    private static final int DEFAULT_MULTICAST_PORT = 1234;
//
//    private final InetAddress multicastAddress;
//
//    private final MulticastSocket multicastSocket;
//
//    private final int multicastPort;
//
//    private final ConcurrentMap<Node, Set<Node>> received = new ConcurrentHashMap<Node, Set<Node>>();
//
//    private final ScheduledExecutorService cleanExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("LTSMulticastRegistryCleanTimer", true));
//
//    private final ScheduledFuture<?> cleanFuture;
//
//    private final int cleanPeriod;
//
//    public MulticastRegistry(Application application) {
//        super(application);
//
//        String address = NodeRegistryUtils.getRealRegistryAddress(application.getConfig().getRegistryAddress());
//        String host = address.split(":")[0];
//        Integer port = Integer.parseInt(address.split(":")[1]);
//
//        if (!isMulticastAddress(host)) {
//            throw new IllegalArgumentException("Invalid multicast address " + host + ", scope: 224.0.0.0 - 239.255.255.255");
//        }
//        try {
//            multicastAddress = InetAddress.getByName(host);
//            multicastPort = port <= 0 ? DEFAULT_MULTICAST_PORT : port;
//            multicastSocket = new MulticastSocket(multicastPort);
//            multicastSocket.setLoopbackMode(false);
//            multicastSocket.joinGroup(multicastAddress);
//            Thread thread = new Thread(new Runnable() {
//                public void run() {
//                    byte[] buf = new byte[2048];
//                    DatagramPacket recv = new DatagramPacket(buf, buf.length);
//                    while (!multicastSocket.isClosed()) {
//                        try {
//                            multicastSocket.receive(recv);
//                            String msg = new String(recv.getData()).trim();
//                            int i = msg.indexOf('\n');
//                            if (i > 0) {
//                                msg = msg.substring(0, i).trim();
//                            }
//                            MulticastRegistry.this.receive(msg, (InetSocketAddress) recv.getSocketAddress());
//                            Arrays.fill(buf, (byte) 0);
//                        } catch (Throwable e) {
//                            if (!multicastSocket.isClosed()) {
//                                logger.error(e.getMessage(), e);
//                            }
//                        }
//                    }
//                }
//            }, "LTSMulticastRegistryReceiver");
//            thread.setDaemon(true);
//            thread.start();
//        } catch (IOException e) {
//            throw new IllegalStateException(e.getMessage(), e);
//        }
//
//        this.cleanPeriod = application.getConfig().getParameter(Constants.SESSION_TIMEOUT_KEY, Constants.DEFAULT_SESSION_TIMEOUT);
//
//        boolean admin = true;
//        if (admin) {
//            this.cleanFuture = cleanExecutor.scheduleWithFixedDelay(new Runnable() {
//                public void run() {
//                    try {
//                        clean(); // 清除过期者
//                    } catch (Throwable t) { // 防御性容错
//                        logger.error("Unexpected exception occur at clean expired provider, cause: " + t.getMessage(), t);
//                    }
//                }
//            }, cleanPeriod, cleanPeriod, TimeUnit.MILLISECONDS);
//        } else {
//            this.cleanFuture = null;
//        }
//    }
//
//    private static boolean isMulticastAddress(String ip) {
//        int i = ip.indexOf('.');
//        if (i > 0) {
//            String prefix = ip.substring(0, i);
//            if (StringUtils.isInteger(prefix)) {
//                int p = Integer.parseInt(prefix);
//                return p >= 224 && p <= 239;
//            }
//        }
//        return false;
//    }
//
//    private void clean() {
//        for (Set<Node> providers : new HashSet<Set<Node>>(received.values())) {
//            for (Node node : new HashSet<Node>(providers)) {
//                if (isExpired(node)) {
//                    if (logger.isWarnEnabled()) {
//                        logger.warn("Clean expired provider " + node);
//                    }
//                    doUnregister(node);
//                }
//            }
//        }
//    }
//
//    private boolean isExpired(Node node) {
//
//        Socket socket = null;
//        try {
//            socket = new Socket(node.getIp(), node.getPort());
//        } catch (Throwable e) {
//            try {
//                Thread.sleep(100);
//            } catch (Throwable ignored) {
//            }
//            Socket socket2 = null;
//            try {
//                socket2 = new Socket(node.getIp(), node.getPort());
//            } catch (Throwable e2) {
//                return true;
//            } finally {
//                if (socket2 != null) {
//                    try {
//                        socket2.close();
//                    } catch (Throwable ignored) {
//                    }
//                }
//            }
//        } finally {
//            if (socket != null) {
//                try {
//                    socket.close();
//                } catch (Throwable ignored) {
//                }
//            }
//        }
//        return false;
//    }
//
//    private void receive(String msg, InetSocketAddress remoteAddress) {
//        if (logger.isInfoEnabled()) {
//            logger.info("Receive multicast message: " + msg + " from " + remoteAddress);
//        }
//        if (msg.startsWith(Constants.REGISTER)) {
//            URL url = URL.valueOf(msg.substring(Constants.REGISTER.length()).trim());
//            registered(url);
//        } else if (msg.startsWith(Constants.UNREGISTER)) {
//            URL url = URL.valueOf(msg.substring(Constants.UNREGISTER.length()).trim());
//            unregistered(url);
//        } else if (msg.startsWith(Constants.SUBSCRIBE)) {
//            URL url = URL.valueOf(msg.substring(Constants.SUBSCRIBE.length()).trim());
//            Set<Node> urls = getRegistered();
//            if (urls != null && urls.size() > 0) {
//                for (URL u : urls) {
//                    if (UrlUtils.isMatch(url, u)) {
//                        String host = remoteAddress != null && remoteAddress.getAddress() != null
//                                ? remoteAddress.getAddress().getHostAddress() : url.getIp();
//                        broadcast(Constants.REGISTER + " " + u.toFullString());
//                    }
//                }
//            }
//        }/* else if (msg.startsWith(UNSUBSCRIBE)) {
//        }*/
//    }
//
//    private void broadcast(String msg) {
//        if (logger.isInfoEnabled()) {
//            logger.info("Send broadcast message: " + msg + " to " + multicastAddress + ":" + multicastPort);
//        }
//        try {
//            byte[] data = (msg + "\n").getBytes();
//            DatagramPacket hi = new DatagramPacket(data, data.length, multicastAddress, multicastPort);
//            multicastSocket.send(hi);
//        } catch (Exception e) {
//            throw new IllegalStateException(e.getMessage(), e);
//        }
//    }
//
//    protected void doRegister(Node node) {
//        broadcast(Constants.REGISTER + " " + node.toFullString());
//    }
//
//    protected void doUnregister(Node node) {
//        broadcast(Constants.UNREGISTER + " " + node.toFullString
//
//    protected void doSubscribe(URL url, NotifyListener listener) {
//        if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {
//            admin = true;
//        }
//        broadcast(Constants.SUBSCRIBE + " " + url.toFullString());
//        synchronized (listener) {
//            try {
//                listener.wait(url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT));
//            } catch (InterruptedException ignored) {
//            }
//        }
//    }
//
//    protected void doUnsubscribe(URL url, NotifyListener listener) {
//        if (!Constants.ANY_VALUE.equals(url.getServiceInterface())
//                && url.getParameter(Constants.REGISTER_KEY, true)) {
//            unregister(url);
//        }
//        broadcast(Constants.UNSUBSCRIBE + " " + url.toFullString());
//    }
//
//    public boolean isAvailable() {
//        try {
//            return multicastSocket != null;
//        } catch (Throwable t) {
//            return false;
//        }
//    }
//
//    public void destroy() {
//        super.destroy();
//        try {
//            if (cleanFuture != null) {
//                cleanFuture.cancel(true);
//            }
//        } catch (Throwable t) {
//            logger.warn(t.getMessage(), t);
//        }
//        try {
//            multicastSocket.leaveGroup(multicastAddress);
//            multicastSocket.close();
//        } catch (Throwable t) {
//            logger.warn(t.getMessage(), t);
//        }
//    }
//
//    @Override
//    protected void doUnRegister(Node node) {
//
//    }
//
//    @Override
//    protected void doSubscribe(Node node, NotifyListener listener) {
//
//    }
//
//    @Override
//    protected void doUnsubscribe(Node node, NotifyListener listener) {
//
//    }
//
//    protected void registered(URL url) {
//        for (Map.Entry<URL, Set<NotifyListener>> entry : getSubscribed().entrySet()) {
//            URL key = entry.getKey();
//            if (UrlUtils.isMatch(key, url)) {
//                Set<URL> urls = received.get(key);
//                if (urls == null) {
//                    received.putIfAbsent(key, new ConcurrentHashSet<URL>());
//                    urls = received.get(key);
//                }
//                urls.add(url);
//                List<URL> list = toList(urls);
//                for (NotifyListener listener : entry.getValue()) {
//                    notify(key, listener, list);
//                    synchronized (listener) {
//                        listener.notify();
//                    }
//                }
//            }
//        }
//    }
//
//    protected void unregistered(URL url) {
//        for (Map.Entry<URL, Set<NotifyListener>> entry : getSubscribed().entrySet()) {
//            URL key = entry.getKey();
//            if (UrlUtils.isMatch(key, url)) {
//                Set<URL> urls = received.get(key);
//                if (urls != null) {
//                    urls.remove(url);
//                }
//                List<URL> list = toList(urls);
//                for (NotifyListener listener : entry.getValue()) {
//                    notify(key, listener, list);
//                }
//            }
//        }
//    }
//
//    protected void subscribed(URL url, NotifyListener listener) {
//        List<URL> urls = lookup(url);
//        notify(url, listener, urls);
//    }
//
//    private List<URL> toList(Set<URL> urls) {
//        List<URL> list = new ArrayList<URL>();
//        if (urls != null && urls.size() > 0) {
//            for (URL url : urls) {
//                list.add(url);
//            }
//        }
//        return list;
//    }
//
//    public void register(URL url) {
//        super.register(url);
//        registered(url);
//    }
//
//    public void unregister(URL url) {
//        super.unregister(url);
//        unregistered(url);
//    }
//
//    public void subscribe(URL url, NotifyListener listener) {
//        super.subscribe(url, listener);
//        subscribed(url, listener);
//    }
//
//    public void unsubscribe(URL url, NotifyListener listener) {
//        super.unsubscribe(url, listener);
//        received.remove(url);
//    }
//
//    public List<URL> lookup(URL url) {
//        List<URL> urls = new ArrayList<URL>();
//        Map<String, List<URL>> notifiedUrls = getNotified().get(url);
//        if (notifiedUrls != null && notifiedUrls.size() > 0) {
//            for (List<URL> values : notifiedUrls.values()) {
//                urls.addAll(values);
//            }
//        }
//        if (urls == null || urls.size() == 0) {
//            List<URL> cacheUrls = getCacheUrls(url);
//            if (cacheUrls != null && cacheUrls.size() > 0) {
//                urls.addAll(cacheUrls);
//            }
//        }
//        if (urls == null || urls.size() == 0) {
//            for (URL u : getRegistered()) {
//                if (UrlUtils.isMatch(url, u)) {
//                    urls.add(u);
//                }
//            }
//        }
//        if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {
//            for (URL u : getSubscribed().keySet()) {
//                if (UrlUtils.isMatch(url, u)) {
//                    urls.add(u);
//                }
//            }
//        }
//        return urls;
//    }
//
//    public MulticastSocket getMutilcastSocket() {
//        return multicastSocket;
//    }
//
//    public Map<Node, Set<Node>> getReceived() {
//        return received;
//    }
//
//}
