/** * Copyright (C) 2012 FuseSource, Inc. * http://fusesource.com * * 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.fusesource.hawtdispatch.transport; import org.fusesource.hawtdispatch.*; import java.io.IOException; import java.net.*; import java.nio.channels.SelectionKey; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.concurrent.Executor; /** * A TCP based implementation of {@link TransportServer} * * @author <a href="http://hiramchirino.com">Hiram Chirino</a> */ public class TcpTransportServer implements TransportServer { protected final String bindScheme; protected final InetSocketAddress bindAddress; protected int backlog = 100; protected ServerSocketChannel channel; protected TransportServerListener listener; protected DispatchQueue dispatchQueue; protected DispatchSource acceptSource; protected int receiveBufferSize = 64*1024; protected int sendBufferSize = 64*1024; protected Executor blockingExecutor; public TcpTransportServer(URI location) throws UnknownHostException { bindScheme = location.getScheme(); String host = location.getHost(); host = (host == null || host.length() == 0) ? "::" : host; bindAddress = new InetSocketAddress(InetAddress.getByName(host), location.getPort()); } public void setTransportServerListener(TransportServerListener listener) { this.listener = listener; } public InetSocketAddress getSocketAddress() { return (InetSocketAddress) channel.socket().getLocalSocketAddress(); } public DispatchQueue getDispatchQueue() { return dispatchQueue; } public void setDispatchQueue(DispatchQueue dispatchQueue) { this.dispatchQueue = dispatchQueue; } public void suspend() { acceptSource.suspend(); } public void resume() { acceptSource.resume(); } @Deprecated public void start(Runnable onCompleted) throws Exception { start(new TaskWrapper(onCompleted)); } @Deprecated public void stop(Runnable onCompleted) throws Exception { stop(new TaskWrapper(onCompleted)); } public void start(Task onCompleted) throws Exception { try { channel = ServerSocketChannel.open(); channel.configureBlocking(false); try { channel.socket().setReceiveBufferSize(receiveBufferSize); } catch (SocketException ignore) { } try { channel.socket().setReceiveBufferSize(sendBufferSize); } catch (SocketException ignore) { } channel.socket().bind(bindAddress, backlog); } catch (IOException e) { throw new IOException("Failed to bind to server socket: " + bindAddress + " due to: " + e); } acceptSource = Dispatch.createSource(channel, SelectionKey.OP_ACCEPT, dispatchQueue); acceptSource.setEventHandler(new Task() { public void run() { try { SocketChannel client = channel.accept(); while( client!=null ) { handleSocket(client); client = channel.accept(); } } catch (Exception e) { listener.onAcceptError(e); } } }); acceptSource.setCancelHandler(new Task() { public void run() { try { channel.close(); } catch (IOException e) { } } }); acceptSource.resume(); if( onCompleted!=null ) { dispatchQueue.execute(onCompleted); } } public String getBoundAddress() { try { return new URI(bindScheme, null, bindAddress.getAddress().getHostAddress(), channel.socket().getLocalPort(), null, null, null).toString(); } catch (URISyntaxException e) { throw new RuntimeException(e); } } public void stop(final Task onCompleted) throws Exception { if( acceptSource.isCanceled() ) { onCompleted.run(); } else { acceptSource.setCancelHandler(new Task() { public void run() { try { channel.close(); } catch (IOException e) { } onCompleted.run(); } }); acceptSource.cancel(); } } public int getBacklog() { return backlog; } public void setBacklog(int backlog) { this.backlog = backlog; } protected final void handleSocket(SocketChannel socket) throws Exception { TcpTransport transport = createTransport(); transport.connected(socket); listener.onAccept(transport); } protected TcpTransport createTransport() { final TcpTransport rc = new TcpTransport(); rc.setBlockingExecutor(blockingExecutor); rc.setDispatchQueue(dispatchQueue); return rc; } /** * @return pretty print of this */ public String toString() { return getBoundAddress(); } public int getReceiveBufferSize() { return receiveBufferSize; } public void setReceiveBufferSize(int receiveBufferSize) { this.receiveBufferSize = receiveBufferSize; if( channel!=null ) { try { channel.socket().setReceiveBufferSize(receiveBufferSize); } catch (SocketException ignore) { } } } public int getSendBufferSize() { return sendBufferSize; } public void setSendBufferSize(int sendBufferSize) { this.sendBufferSize = sendBufferSize; if( channel!=null ) { try { channel.socket().setReceiveBufferSize(sendBufferSize); } catch (SocketException ignore) { } } } public Executor getBlockingExecutor() { return blockingExecutor; } public void setBlockingExecutor(Executor blockingExecutor) { this.blockingExecutor = blockingExecutor; } }