/*
* JBoss, Home of Professional Open Source.
* Copyright 2013, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.wsf.stack.cxf.addons.transports.udp;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.InterfaceAddress;
import java.net.MulticastSocket;
import java.net.NetworkInterface;
import java.net.URI;
import java.util.Enumeration;
import java.util.logging.Logger;
import java.util.logging.Level;
import org.apache.cxf.Bus;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.helpers.LoadingByteArrayOutputStream;
import org.apache.cxf.message.Message;
import org.apache.cxf.message.MessageImpl;
import org.apache.cxf.transport.AbstractConduit;
import org.apache.cxf.ws.addressing.EndpointReferenceType;
/**
* A modified version of Apache CXF org.apache.cxf.transport.udp.UDPConduit
* that does not rely on Apache Mina and directly uses basic java.io for
* any type of datagram.
*
* @author alessio.soldano@jboss.com
*/
public class UDPConduit extends AbstractConduit
{
private static final String MULTI_RESPONSE_TIMEOUT = "udp.multi.response.timeout";
private static final Logger LOG = LogUtils.getL7dLogger(UDPDestination.class);
Bus bus;
public UDPConduit(EndpointReferenceType t, final Bus bus)
{
super(t);
this.bus = bus;
}
private void dataReceived(Message message, byte bytes[], boolean async)
{
final Message inMessage = new MessageImpl();
inMessage.setExchange(message.getExchange());
message.getExchange().setInMessage(inMessage);
inMessage.setContent(InputStream.class, new ByteArrayInputStream(bytes));
incomingObserver.onMessage(inMessage);
if (!message.getExchange().isSynchronous())
{
message.getExchange().setInMessage(null);
}
}
public void prepare(final Message message) throws IOException
{
try
{
String address = (String) message.get(Message.ENDPOINT_ADDRESS);
if (StringUtils.isEmpty(address))
{
address = this.getTarget().getAddress().getValue();
}
URI uri = new URI(address);
if (StringUtils.isEmpty(uri.getHost())) { //broadcast
String s = uri.getSchemeSpecificPart();
if (s.startsWith("//:"))
{
s = s.substring(3);
}
if (s.indexOf('/') != -1)
{
s = s.substring(0, s.indexOf('/'));
}
int port = Integer.parseInt(s);
send(message, null, port);
} else {
InetSocketAddress isa = new InetSocketAddress(uri.getHost(), uri.getPort());
send(message, isa, isa.getPort());
}
}
catch (Exception ex)
{
throw new IOException(ex);
}
}
private void send(Message message, InetSocketAddress isa, int port)
{
message.setContent(OutputStream.class, new SocketOutputStream(port, isa, message));
}
private final class SocketOutputStream extends LoadingByteArrayOutputStream
{
private final int port;
private final Message message;
private final InetSocketAddress isa;
private SocketOutputStream(int port, InetSocketAddress isa, Message message)
{
this.port = port;
this.message = message;
this.isa = isa;
}
public void close() throws IOException
{
super.close();
DatagramSocket socket = (isa != null) ? new MulticastSocket(null) : new DatagramSocket();
socket.setSendBufferSize(this.size());
socket.setReceiveBufferSize(64 * 1024);
socket.setBroadcast(true);
if (isa == null) //broadcast
{
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements())
{
NetworkInterface networkInterface = interfaces.nextElement();
if (!networkInterface.isUp() || networkInterface.isLoopback())
{
continue;
}
for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses())
{
InetAddress broadcast = interfaceAddress.getBroadcast();
if (broadcast == null)
{
continue;
}
DatagramPacket sendPacket = new DatagramPacket(this.getRawBytes(), 0, this.size(), broadcast, port);
try
{
socket.send(sendPacket);
}
catch (Exception e)
{
//ignore
LOG.log(Level.FINEST, e.getMessage(), e);
}
}
}
}
else
{
DatagramPacket sendPacket = new DatagramPacket(this.getRawBytes(), 0, this.size(), isa);
try
{
socket.send(sendPacket);
}
catch (Exception e)
{
//ignore
LOG.log(Level.FINEST, e.getMessage(), e);
}
}
if (!message.getExchange().isOneWay())
{
byte bytes[] = new byte[64 * 1024];
DatagramPacket p = new DatagramPacket(bytes, bytes.length);
Object to = message.getContextualProperty(MULTI_RESPONSE_TIMEOUT);
Integer i = null;
if (to instanceof String)
{
i = Integer.parseInt((String) to);
}
else if (to instanceof Integer)
{
i = (Integer) to;
}
if (i == null || i <= 0 || message.getExchange().isSynchronous())
{
socket.setSoTimeout(30000);
socket.receive(p);
dataReceived(message, bytes, false);
}
else
{
socket.setSoTimeout(i);
boolean found = false;
try
{
while (true)
{
socket.receive(p);
dataReceived(message, bytes, false);
found = true;
}
}
catch (java.net.SocketTimeoutException ex)
{
if (!found)
{
throw ex;
}
}
}
}
socket.close();
}
}
protected Logger getLogger()
{
return LOG;
}
}