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
018 package org.fusesource.hawtdispatch.transport;
019
020 import org.fusesource.hawtdispatch.*;
021
022 import java.io.IOException;
023 import java.net.*;
024 import java.nio.channels.SelectionKey;
025 import java.nio.channels.ServerSocketChannel;
026 import java.nio.channels.SocketChannel;
027
028 /**
029 * A TCP based implementation of {@link TransportServer}
030 *
031 * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
032 */
033
034 public class TcpTransportServer implements TransportServer {
035
036 private final String bindScheme;
037 private final InetSocketAddress bindAddress;
038
039 private int backlog = 100;
040
041 private ServerSocketChannel channel;
042 private TransportServerListener listener;
043 private DispatchQueue dispatchQueue;
044 private DispatchSource acceptSource;
045 private int receiveBufferSize = 64*1024;
046
047 public TcpTransportServer(URI location) throws UnknownHostException {
048 bindScheme = location.getScheme();
049 String host = location.getHost();
050 host = (host == null || host.length() == 0) ? "::" : host;
051 bindAddress = new InetSocketAddress(InetAddress.getByName(host), location.getPort());
052 }
053
054 public void setTransportServerListener(TransportServerListener listener) {
055 this.listener = listener;
056 }
057
058 public InetSocketAddress getSocketAddress() {
059 return (InetSocketAddress) channel.socket().getLocalSocketAddress();
060 }
061
062 public DispatchQueue getDispatchQueue() {
063 return dispatchQueue;
064 }
065
066 public void setDispatchQueue(DispatchQueue dispatchQueue) {
067 this.dispatchQueue = dispatchQueue;
068 }
069
070 public void suspend() {
071 acceptSource.suspend();
072 }
073
074 public void resume() {
075 acceptSource.resume();
076 }
077
078 @Deprecated
079 public void start(Runnable onCompleted) throws Exception {
080 start(new TaskWrapper(onCompleted));
081 }
082 @Deprecated
083 public void stop(Runnable onCompleted) throws Exception {
084 stop(new TaskWrapper(onCompleted));
085 }
086
087 public void start(Task onCompleted) throws Exception {
088
089 try {
090 channel = ServerSocketChannel.open();
091 channel.configureBlocking(false);
092 try {
093 channel.socket().setReceiveBufferSize(receiveBufferSize);
094 } catch (SocketException ignore) {
095 }
096 channel.socket().bind(bindAddress, backlog);
097 } catch (IOException e) {
098 throw new IOException("Failed to bind to server socket: " + bindAddress + " due to: " + e);
099 }
100
101 acceptSource = Dispatch.createSource(channel, SelectionKey.OP_ACCEPT, dispatchQueue);
102 acceptSource.setEventHandler(new Task() {
103 public void run() {
104 try {
105 SocketChannel client = channel.accept();
106 while( client!=null ) {
107 handleSocket(client);
108 client = channel.accept();
109 }
110 } catch (Exception e) {
111 listener.onAcceptError(e);
112 }
113 }
114 });
115 acceptSource.setCancelHandler(new Task() {
116 public void run() {
117 try {
118 channel.close();
119 } catch (IOException e) {
120 }
121 }
122 });
123 acceptSource.resume();
124 if( onCompleted!=null ) {
125 dispatchQueue.execute(onCompleted);
126 }
127 }
128
129 public String getBoundAddress() {
130 try {
131 return new URI(bindScheme, null, bindAddress.getAddress().getHostAddress(), channel.socket().getLocalPort(), null, null, null).toString();
132 } catch (URISyntaxException e) {
133 throw new RuntimeException(e);
134 }
135 }
136
137 public void stop(final Task onCompleted) throws Exception {
138 if( acceptSource.isCanceled() ) {
139 onCompleted.run();
140 } else {
141 acceptSource.setCancelHandler(new Task() {
142 public void run() {
143 try {
144 channel.close();
145 } catch (IOException e) {
146 }
147 onCompleted.run();
148 }
149 });
150 acceptSource.cancel();
151 }
152 }
153
154 public int getBacklog() {
155 return backlog;
156 }
157
158 public void setBacklog(int backlog) {
159 this.backlog = backlog;
160 }
161
162 protected final void handleSocket(SocketChannel socket) throws Exception {
163 TcpTransport transport = createTransport();
164 transport.connected(socket);
165 listener.onAccept(transport);
166 }
167
168 protected TcpTransport createTransport() {
169 return new TcpTransport();
170 }
171
172 /**
173 * @return pretty print of this
174 */
175 public String toString() {
176 return getBoundAddress();
177 }
178
179 public int getReceiveBufferSize() {
180 return receiveBufferSize;
181 }
182
183 public void setReceiveBufferSize(int receiveBufferSize) {
184 this.receiveBufferSize = receiveBufferSize;
185 }
186
187 }