/*
* Copyright 2012 The Netty Project
*
* The Netty Project 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.jboss.netty.channel.socket;
import static org.junit.Assert.*;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.util.TestUtil;
import org.junit.Test;
public abstract class AbstractDatagramMulticastTest {
protected abstract DatagramChannelFactory newServerSocketChannelFactory(Executor executor);
protected abstract DatagramChannelFactory newClientSocketChannelFactory(Executor executor);
@Test
public void testMulticast() throws Throwable {
ConnectionlessBootstrap sb = new ConnectionlessBootstrap(
newServerSocketChannelFactory(Executors.newCachedThreadPool()));
ConnectionlessBootstrap cb = new ConnectionlessBootstrap(
newClientSocketChannelFactory(Executors.newCachedThreadPool()));
DatagramChannel sc = null;
DatagramChannel cc = null;
try {
MulticastTestHandler mhandler = new MulticastTestHandler();
cb.getPipeline().addFirst("handler", mhandler);
sb.getPipeline().addFirst("handler", new SimpleChannelUpstreamHandler());
int port = TestUtil.getFreePort();
NetworkInterface loopbackIf;
try {
loopbackIf = NetworkInterface.getByInetAddress(InetAddress.getLocalHost());
} catch (SocketException e) {
loopbackIf = null;
}
// check if the NetworkInterface is null, this is the case on my ubuntu dev machine but not on osx and windows.
// if so fail back the the first interface
if (loopbackIf == null) {
for (Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
e.hasMoreElements();) {
NetworkInterface nif = e.nextElement();
if (nif.isLoopback()) {
loopbackIf = nif;
break;
}
}
}
sb.setOption("networkInterface", loopbackIf);
sb.setOption("reuseAddress", true);
sc = (DatagramChannel) sb.bind(new InetSocketAddress(port));
String group = "230.0.0.1";
InetSocketAddress groupAddress = new InetSocketAddress(group, port);
cb.setOption("networkInterface", loopbackIf);
cb.setOption("reuseAddress", true);
cc = (DatagramChannel) cb.bind(new InetSocketAddress(port));
assertTrue(cc.joinGroup(groupAddress, loopbackIf).awaitUninterruptibly().isSuccess());
assertTrue(sc.write(wrapInt(1), groupAddress).awaitUninterruptibly().isSuccess());
assertTrue(mhandler.await());
assertTrue(sc.write(wrapInt(1), groupAddress).awaitUninterruptibly().isSuccess());
// leave the group
assertTrue(cc.leaveGroup(groupAddress, loopbackIf).awaitUninterruptibly().isSuccess());
// sleep a second to make sure we left the group
Thread.sleep(1000);
// we should not receive a message anymore as we left the group before
assertTrue(sc.write(wrapInt(1), groupAddress).awaitUninterruptibly().isSuccess());
} finally {
if (sc != null) {
sc.close().awaitUninterruptibly();
}
if (cc != null) {
cc.close().awaitUninterruptibly();
}
sb.releaseExternalResources();
cb.releaseExternalResources();
}
}
private static ChannelBuffer wrapInt(int value) {
ChannelBuffer buf = ChannelBuffers.buffer(4);
buf.writeInt(value);
return buf;
}
private static final class MulticastTestHandler extends SimpleChannelUpstreamHandler {
private final CountDownLatch latch = new CountDownLatch(1);
private boolean done;
private volatile boolean fail;
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
super.messageReceived(ctx, e);
if (done) {
fail = true;
}
assertEquals(1, ((ChannelBuffer) e.getMessage()).readInt());
latch.countDown();
// mark the handler as done as we only are supposed to receive one message
done = true;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
throws Exception {
e.getCause().printStackTrace();
}
public boolean await() throws Exception {
boolean success = latch.await(10, TimeUnit.SECONDS);
if (fail) {
// fail if we receive an message after we are done
fail();
}
return success;
}
}
}