blob: 5f234b769ea3e11df2bee2246150dbecfff7ed1e [file] [log] [blame]
/*
* Copyright (c) 2007-2013, 2015 Eike Stepper (Berlin, Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eike Stepper - initial API and implementation
* Caspar De Groot - maintenance
*/
package org.eclipse.net4j.internal.tcp;
import org.eclipse.net4j.internal.tcp.bundle.OM;
import org.eclipse.net4j.tcp.ITCPAcceptor;
import org.eclipse.net4j.tcp.ITCPPassiveSelectorListener;
import org.eclipse.net4j.tcp.ITCPSelector;
import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;
import org.eclipse.net4j.util.concurrent.Worker;
import org.eclipse.net4j.util.io.IOUtil;
import org.eclipse.net4j.util.om.trace.ContextTracer;
import org.eclipse.spi.net4j.Acceptor;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.text.MessageFormat;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* @author Eike Stepper
*/
public class TCPAcceptor extends Acceptor implements ITCPAcceptor, ITCPPassiveSelectorListener
{
public static final boolean DEFAULT_START_SYNCHRONOUSLY = true;
public static final long DEFAULT_SYNCHRONOUS_START_TIMEOUT = Worker.DEFAULT_TIMEOUT;
private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, TCPAcceptor.class);
private TCPSelector selector;
@ExcludeFromDump
private SelectionKey selectionKey;
private boolean startSynchronously = DEFAULT_START_SYNCHRONOUSLY;
private long synchronousStartTimeout = DEFAULT_SYNCHRONOUS_START_TIMEOUT;
@ExcludeFromDump
private CountDownLatch startLatch;
private ServerSocketChannel serverSocketChannel;
private String address = DEFAULT_ADDRESS;
private int port = DEFAULT_PORT;
public TCPAcceptor()
{
}
public String getAddress()
{
return address;
}
public void setAddress(String address)
{
this.address = address;
}
public int getPort()
{
return port;
}
public void setPort(int port)
{
this.port = port;
}
public TCPSelector getSelector()
{
return selector;
}
public void setSelector(TCPSelector selector)
{
this.selector = selector;
}
public boolean isStartSynchronously()
{
return startSynchronously;
}
public void setStartSynchronously(boolean startSynchronously)
{
this.startSynchronously = startSynchronously;
}
public SelectionKey getSelectionKey()
{
return selectionKey;
}
public long getSynchronousStartTimeout()
{
return synchronousStartTimeout;
}
public void setSynchronousStartTimeout(long synchronousStartTimeout)
{
this.synchronousStartTimeout = synchronousStartTimeout;
}
public void handleRegistration(ITCPSelector selector, ServerSocketChannel serverSocketChannel)
{
InetSocketAddress addr = null;
try
{
if (address != null)
{
addr = new InetSocketAddress(InetAddress.getByName(address), port);
}
ServerSocket socket = serverSocketChannel.socket();
socket.setReuseAddress(true);
socket.bind(addr);
if (addr == null)
{
address = socket.getInetAddress().toString();
if (address.startsWith("/")) //$NON-NLS-1$
{
address = address.substring(1);
}
int colon = address.indexOf(':');
if (colon != -1)
{
port = Integer.parseInt(address.substring(colon + 1));
address = address.substring(0, colon);
}
}
// [MACOSX] Must occur AFTER binding!
selectionKey = serverSocketChannel.register(selector.getSocketSelector(), SelectionKey.OP_ACCEPT, this);
}
catch (Exception ex)
{
OM.LOG.error("Problem while binding " + addr, ex);
deactivateAsync();
}
finally
{
if (startLatch != null)
{
startLatch.countDown();
}
}
}
public void handleAccept(ITCPSelector selector, ServerSocketChannel serverSocketChannel)
{
try
{
SocketChannel socketChannel = serverSocketChannel.accept();
if (socketChannel != null)
{
if (TRACER.isEnabled())
{
TRACER.trace("Accepted socketChannel " + socketChannel); //$NON-NLS-1$
}
// socketChannel.socket().setReuseAddress(true);
// socketChannel.socket().setKeepAlive(true);
socketChannel.configureBlocking(false);
TCPConnector connector = createConnector();
prepareConnector(connector);
connector.setSocketChannel(socketChannel);
connector.setSelector(selector);
connector.activate();
}
}
catch (ClosedChannelException ex)
{
deactivateAsync();
}
catch (Exception ex)
{
if (isActive())
{
OM.LOG.error(ex);
}
deactivateAsync();
}
}
@Override
public String toString()
{
return MessageFormat.format("TCPAcceptor[{0}:{1}]", address, port); //$NON-NLS-1$
}
protected TCPConnector createConnector()
{
return new TCPServerConnector(this);
}
@Override
protected void doBeforeActivate() throws Exception
{
super.doBeforeActivate();
if (selector == null)
{
throw new IllegalStateException("selector == null"); //$NON-NLS-1$
}
if (startSynchronously)
{
startLatch = new CountDownLatch(1);
}
}
@Override
protected void doActivate() throws Exception
{
super.doActivate();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
selector.orderRegistration(serverSocketChannel, this);
if (startLatch != null)
{
if (!startLatch.await(synchronousStartTimeout, TimeUnit.MILLISECONDS))
{
startLatch = null;
IOUtil.closeSilent(serverSocketChannel);
throw new IOException("Registration with selector timed out after " + synchronousStartTimeout + " millis"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
@Override
protected void doDeactivate() throws Exception
{
if (startLatch != null)
{
startLatch.countDown();
}
cancelSelectionKey();
IOUtil.closeSilent(serverSocketChannel);
serverSocketChannel = null;
super.doDeactivate();
}
protected void deactivateAsync()
{
// Cancel the selection immediately
cancelSelectionKey();
// Do the rest of the deactivation asynchronously
getConfig().getReceiveExecutor().execute(new Runnable()
{
public void run()
{
deactivate();
}
});
}
private void cancelSelectionKey()
{
if (selectionKey != null)
{
selectionKey.cancel();
selectionKey = null;
}
}
}