package org.rakam.kume.join.multicast; import org.rakam.kume.Cluster; import org.rakam.kume.Member; import org.rakam.kume.transport.MulticastPacket; import org.rakam.kume.transport.Operation; import org.rakam.kume.transport.serialization.Serializer; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.InternetProtocolFamily; import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.util.NetUtil; import org.rakam.kume.InternalService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.InetSocketAddress; import java.net.NetworkInterface; public class MulticastServerHandler { final static Logger LOGGER = LoggerFactory.getLogger(MulticastServerHandler.class); private final Member localMember; private NioDatagramChannel server; InetSocketAddress address; Bootstrap handler; static NetworkInterface multicastInterface = NetUtil.LOOPBACK_IF; private boolean joinGroup; public MulticastServerHandler(Cluster cluster, InetSocketAddress address) throws InterruptedException { this.address = address; handler = new Bootstrap() .channelFactory(() -> new NioDatagramChannel(InternetProtocolFamily.IPv4)) .localAddress(address) .group(new NioEventLoopGroup()) .option(ChannelOption.SO_REUSEADDR, true) .option(ChannelOption.IP_MULTICAST_IF, multicastInterface) .option(ChannelOption.AUTO_READ, false) .handler(new ChannelInitializer<NioDatagramChannel>() { @Override public void initChannel(NioDatagramChannel ch) throws Exception { ch.pipeline().addLast(new MulticastChannelAdapter(cluster)); } }); localMember = cluster.getLocalMember(); } public MulticastServerHandler start() throws InterruptedException { server = (NioDatagramChannel) handler.bind(address.getPort()).sync().channel(); server.joinGroup(address, multicastInterface).sync(); // why netty doesn't have a get method for group memberships? joinGroup = true; return this; } public void setAutoRead(boolean bool) { server.config().setAutoRead(bool); } public void sendMulticast(Operation req) { ByteBuf buf = Unpooled.wrappedBuffer(Serializer.toByteBuf(new MulticastPacket(req, localMember))); server.writeAndFlush(new DatagramPacket(buf, address, localMember.getAddress())); } public void send(InetSocketAddress address, Operation<InternalService> req) { ByteBuf buf = Unpooled.wrappedBuffer(Serializer.toByteBuf(new MulticastPacket(req, localMember))); server.writeAndFlush(new DatagramPacket(buf, address, localMember.getAddress())); } public void close() throws InterruptedException { server.leaveGroup(address, NetUtil.LOOPBACK_IF).sync(); server.close().sync(); } public void setJoinGroup(boolean joinGroup) { try { if(this.joinGroup && !joinGroup) server.leaveGroup(address, multicastInterface).sync(); else if(!this.joinGroup && joinGroup) server.joinGroup(address, multicastInterface).sync(); } catch (InterruptedException e) { LOGGER.error("couldn't change multicast server state.", e); } this.joinGroup = joinGroup; } }