/*
* Copyright to 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.rioproject.util;
import java.io.InterruptedIOException;
import java.io.IOException;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.SocketException;
import java.net.DatagramPacket;
/**
* Class which can be used to determine whether multicast is available. Credit
* goes to Alexander V. Konstantinou
*/
public class MulticastStatus {
private static final Object lock = new Object();
/**
* Determines if a multicast socket can be created and a Request
* announcement can be sent. Note that it is still possible that multicast
* does not work outside the host.
*
* @param timeout The timeout to use when checking the status
*
* @throws IOException If there are errors interfacing ith the network
* @throws IllegalArgumentException if the timeout is < 0
*/
public static void checkMulticast(final int timeout) throws IOException {
if(timeout < 0)
throw new IllegalArgumentException("Invalid timeout = " + timeout);
/** Use Jini's multicast group */
final InetAddress group = net.jini.discovery.Constants.getRequestAddress();
/** Use a different port from Jini discovery */
final short port = net.jini.discovery.Constants.discoveryPort + 1;
final long endMillis = System.currentTimeMillis() + timeout;
final MulticastSocket msocket = new MulticastSocket(port);
msocket.setTimeToLive(1); // XXX - could it be 0 ?
msocket.joinGroup(group);
msocket.setSoTimeout(timeout);
StringBuilder builder = new StringBuilder();
builder.append(MulticastStatus.class.getName()).append(".ping(").append(System.currentTimeMillis()).append(")");
final byte[] messageBytes = builder.toString().getBytes();
DatagramPacket packet = new DatagramPacket(messageBytes, messageBytes.length, group, port);
final Boolean[] received = new Boolean[]{Boolean.FALSE};
/**
* Receive thread waits for msocket SOTIMEOUT to receive a packet. The
* exception catching code is needed to handle Java multicast exceptions
* caused by bug 4190513.
*/
Thread receiveThread = new Thread("MulticastStatus") {
public void run() {
byte[] buf = new byte[1024];
DatagramPacket recv = new DatagramPacket(buf, buf.length);
do {
try {
msocket.receive(recv);
synchronized(lock) {
received[0] = Boolean.TRUE;
}
return;
} catch(InterruptedIOException e) {
return;
} catch(Throwable e) {
long remainingMillis = endMillis
- System.currentTimeMillis();
if(remainingMillis <= 0) {
return;
} else {
try {
msocket.setSoTimeout((int)remainingMillis);
} catch(SocketException e2) {
return;
}
}
}
} while (true);
}
};
receiveThread.start();
// Start sending packets every 500 msec until the timeout expires
// or a packet is received
do {
try {
msocket.send(packet);
synchronized(lock) {
if(received[0]) {
msocket.leaveGroup(group);
msocket.close();
return;
}
}
try {
Thread.sleep(500);
} catch(InterruptedException e) {
e.printStackTrace();
}
} catch (IOException e) {
throw new IOException("Problem creating or sending multicast packets", e);
} finally {
msocket.leaveGroup(group);
msocket.close();
}
} while (System.currentTimeMillis() < endMillis);
throw new IOException("Multicast packets were not received in the allotted time");
}
public static void main(String[] args) {
try {
checkMulticast(1000 * 5);
System.out.println("======================\nMulticast check successful\n======================");
} catch(IOException ioe) {
System.out.println("======================\nMulticast check FAILED\n======================");
ioe.printStackTrace();
}
}
}