001    /**
002     * Copyright (C) 2012 FuseSource, Inc.
003     * http://fusesource.com
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *    http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.fusesource.hawtdispatch.transport;
018    
019    import org.fusesource.hawtdispatch.DispatchQueue;
020    import org.fusesource.hawtdispatch.Task;
021    
022    import java.net.*;
023    import java.nio.channels.DatagramChannel;
024    
025    /**
026     * <p>
027     * </p>
028     *
029     * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
030     */
031    public class UdpTransportServer extends ServiceBase implements TransportServer {
032    
033        private final String bindScheme;
034        private final InetSocketAddress bindAddress;
035    
036        private DatagramChannel channel;
037        private TransportServerListener listener;
038        private DispatchQueue dispatchQueue;
039    
040        public UdpTransportServer(URI location) throws UnknownHostException {
041            bindScheme = location.getScheme();
042            String host = location.getHost();
043            host = (host == null || host.length() == 0) ? "::" : host;
044            bindAddress = new InetSocketAddress(InetAddress.getByName(host), location.getPort());
045        }
046    
047        private  UdpTransport transport;
048    
049        public void setTransportServerListener(TransportServerListener listener) {
050            this.listener = listener;
051        }
052    
053        public InetSocketAddress getSocketAddress() {
054            return (InetSocketAddress) channel.socket().getLocalSocketAddress();
055        }
056    
057        public DispatchQueue getDispatchQueue() {
058            return dispatchQueue;
059        }
060    
061        public void setDispatchQueue(DispatchQueue dispatchQueue) {
062            this.dispatchQueue = dispatchQueue;
063        }
064    
065        @Override
066        protected void _start(Task onCompleted) {
067            accept();
068            if( onCompleted!=null ) {
069                dispatchQueue.execute(onCompleted);
070            }
071        }
072    
073        private void queueAccept() {
074            dispatchQueue.execute(new Task() {
075                public void run() {
076                    accept();
077                }
078            });
079        }
080    
081        private void accept() {
082            if (getServiceState().isStarted() || getServiceState().isStarting()) {
083                try {
084                    UdpTransport udpTransport = createTransport();
085                    transport = udpTransport;
086                    transport.onDispose = new Task() {
087                        public void run() {
088                            queueAccept();
089                        }
090                    };
091                    channel = DatagramChannel.open();
092                    channel.socket().bind(bindAddress);
093                    transport.connected(channel);
094                    listener.onAccept(transport);
095                } catch (Exception e) {
096                    listener.onAcceptError(e);
097                }
098            }
099        }
100    
101        protected UdpTransport createTransport() {
102            return new UdpTransport();
103        }
104    
105        @Override
106        protected void _stop(Task onCompleted) {
107            transport.stop(onCompleted);
108        }
109    
110        public void suspend() {
111            dispatchQueue.suspend();
112        }
113    
114        public void resume() {
115            dispatchQueue.resume();
116        }
117    
118        public String getBoundAddress() {
119            try {
120                String host = bindAddress.getAddress().getHostAddress();
121                int port = channel.socket().getLocalPort();
122                return new URI(bindScheme, null, host, port, null, null, null).toString();
123            } catch (URISyntaxException e) {
124                throw new RuntimeException(e);
125            }
126        }
127    
128        /**
129         * @return pretty print of this
130         */
131        public String toString() {
132            return getBoundAddress();
133        }
134    
135    }