All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.cxf.transport.udp.UDPConduit Maven / Gradle / Ivy

There is a newer version: 3.0.0-milestone2
Show newest version
/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF 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.apache.cxf.transport.udp;

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.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

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.workqueue.WorkQueue;
import org.apache.cxf.workqueue.WorkQueueManager;
import org.apache.cxf.ws.addressing.EndpointReferenceType;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.transport.socket.DatagramSessionConfig;
import org.apache.mina.transport.socket.nio.NioDatagramConnector;

/**
 * 
 */
public class UDPConduit extends AbstractConduit {
    private static final String CXF_MESSAGE_ATTR = "CXFMessage";
    private static final String MULTI_RESPONSE_TIMEOUT = "udp.multi.response.timeout";
    private static final String HOST_PORT = UDPConduit.class + ".host:port";
    private static final Logger LOG = LogUtils.getL7dLogger(UDPDestination.class); 

    Bus bus;
    NioDatagramConnector connector = new NioDatagramConnector();
    ConcurrentHashMap> connections 
        = new ConcurrentHashMap>();
    
    public UDPConduit(EndpointReferenceType t, 
                      final Bus bus) {
        super(t);
        this.bus = bus;
        connector.getSessionConfig().setReadBufferSize(64 * 1024);
        connector.getSessionConfig().setSendBufferSize(64 * 1024);
        connector.setHandler(new IoHandlerAdapter() {
            public void messageReceived(IoSession session, Object buf) {
                Message message = (Message)session.getAttribute(CXF_MESSAGE_ATTR);
                dataReceived(message, (IoBuffer)buf, true);
            }
        });
    }

    private void dataReceived(Message message, IoBuffer buf, boolean async) {
        if (message.getExchange().getInMessage() == null) {
            final Message inMessage = new MessageImpl();
            inMessage.setExchange(message.getExchange());
            message.getExchange().setInMessage(inMessage);
            
            IoSessionInputStream ins = new IoSessionInputStream(buf);
            inMessage.setContent(InputStream.class, ins);
            inMessage.put(IoSessionInputStream.class, ins);
            
            if (async) {
                WorkQueueManager queuem = bus.getExtension(WorkQueueManager.class);
                WorkQueue queue = queuem.getNamedWorkQueue("udp-conduit");
                if (queue == null) {
                    queue = queuem.getAutomaticWorkQueue();
                }
                queue.execute(new Runnable() {
                    public void run() {
                        incomingObserver.onMessage(inMessage);
                    }
                });
            } else {
                incomingObserver.onMessage(inMessage);
                if (!message.getExchange().isSynchronous()) {
                    message.getExchange().setInMessage(null);
                }
            }
        } else {
            IoSessionInputStream ins = message.getExchange().getInMessage().get(IoSessionInputStream.class);
            ins.setBuffer((IoBuffer)buf);
        }
    }
    
    public void close(Message msg) throws IOException {
        super.close(msg);
        if (msg.getExchange().isOneWay() 
            || msg.getExchange().getInMessage() == msg
            || msg.getExchange().getInFaultMessage() == msg) {
            String s = (String)msg.getExchange().get(HOST_PORT);
            ConnectFuture c = msg.getExchange().get(ConnectFuture.class);
            if (s != null && c != null) {
                c.getSession().removeAttribute(CXF_MESSAGE_ATTR);
    
                Queue q = connections.get(s);
                if (q == null) {
                    connections.putIfAbsent(s, new ArrayBlockingQueue(10));
                    q = connections.get(s);
                }
                if (!q.offer(c)) {
                    c.getSession().close(false);
                }
            }
        }
    }
    public void close() {
        super.close();
        for (Queue f : connections.values()) {
            for (ConnectFuture cf : f) {
                cf.getSession().close(false);
            }
        }
        connections.clear();
        connector.dispose();
        connector = 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())) {
                //NIO doesn't support broadcast, we need to drop down to raw
                //java.io for these
                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);
                sendViaBroadcast(message, null, port);
            } else {
                InetSocketAddress isa = null;
                String hp = ""; 

                isa = new InetSocketAddress(uri.getHost(), uri.getPort());
                hp = uri.getHost() + ":" + uri.getPort();
                
                if (isa.getAddress().isMulticastAddress()) {
                    sendViaBroadcast(message, isa, isa.getPort());
                    return;
                }
                
                Queue q = connections.get(hp);
                ConnectFuture connFuture = null;
                if (q != null) {
                    connFuture = q.poll();
                }
                if (connFuture == null) {
                    connFuture = connector.connect(isa);
                    connFuture.await();
                    ((DatagramSessionConfig)connFuture.getSession().getConfig()).setSendBufferSize(64 * 1024);
                    ((DatagramSessionConfig)connFuture.getSession().getConfig()).setReceiveBufferSize(64 * 1024);
                }
                connFuture.getSession().setAttribute(CXF_MESSAGE_ATTR, message);
                message.setContent(OutputStream.class, new UDPConduitOutputStream(connector, connFuture, message));
                message.getExchange().put(ConnectFuture.class, connFuture);
                message.getExchange().put(HOST_PORT, uri.getHost() + ":" + uri.getPort());
            }
        } catch (Exception ex) {
            throw new IOException(ex);
        }
    }

    private void sendViaBroadcast(Message message, InetSocketAddress isa, int port) {
        message.setContent(OutputStream.class, 
                           new UDPBroadcastOutputStream(port, isa, message));

    }

    private final class UDPBroadcastOutputStream extends LoadingByteArrayOutputStream {
        private final int port;
        private final Message message;
        private final InetSocketAddress multicast;

        private UDPBroadcastOutputStream(int port, InetSocketAddress isa, Message message) {
            this.port = port;
            this.message = message;
            this.multicast = isa;
        }

        public void close() throws IOException {
            super.close();
            DatagramSocket socket;
            if (multicast != null) {
                socket = new MulticastSocket(null);
            } else {
                socket = new DatagramSocket();
            }
            socket.setSendBufferSize(this.size());
            socket.setReceiveBufferSize(64 * 1024);
            socket.setBroadcast(true);
            
            if (multicast == null) {
                Enumeration 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
                        }
                    }
                }
            } else {
                DatagramPacket sendPacket = new DatagramPacket(this.getRawBytes(), 
                                                               0,
                                                               this.size(),
                                                               multicast);
                
                try {
                    socket.send(sendPacket);
                } catch (Exception e) {
                    //ignore
                }
            }
            
            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, IoBuffer.wrap(bytes, 0, p.getLength()), false);
                } else {
                    socket.setSoTimeout(i);
                    boolean found = false;
                    try {
                        while (true) {
                            socket.receive(p);
                            dataReceived(message, IoBuffer.wrap(bytes, 0, p.getLength()), false);
                            found = true;
                        }
                    } catch (java.net.SocketTimeoutException ex) {
                        if (!found) {
                            throw ex;
                        }
                    }
                }
            }
            socket.close();
        }

        public void flush() throws IOException {
        }
    }

    public class UDPConduitOutputStream extends OutputStream {
        final ConnectFuture future;
        final NioDatagramConnector connector;
        final Message message;
        IoBuffer buffer = IoBuffer.allocate(64 * 1024 - 42); //max size
        boolean closed;
        
        public UDPConduitOutputStream(NioDatagramConnector connector,
                                      ConnectFuture connFuture,
                                      Message m) {
            this.connector = connector;
            this.future = connFuture;
            this.message = m;
        }

        public void write(int b) throws IOException {
            buffer.put(new byte[] {(byte)b}, 0, 1);
        }
        public void write(byte b[], int off, int len) throws IOException {
            while (len > buffer.remaining()) {
                int nlen = buffer.remaining();
                buffer.put(b, off, nlen);
                len -= nlen;
                off += nlen;
                send();
                buffer = IoBuffer.allocate((64 * 1024) - 42);
            }
            buffer.put(b, off, len);
        }
        private void send() throws IOException {
            try {
                future.await();
            } catch (InterruptedException e) {
                if (future.getException() != null) {
                    throw new IOException(future.getException());
                }
                throw new IOException(e);
            }
            if (future.getException() != null) {
                throw new IOException(future.getException());
            }
            buffer.flip();
            future.getSession().write(buffer);
        }
        public void close() throws IOException {
            if (closed) {
                return;
            }
            closed = true;
            send();
        }
    }
    
    protected Logger getLogger() {
        return LOG;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy