package org.jboss.resteasy.plugins.server.netty;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandler;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.DefaultChannelGroup;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.execution.ExecutionHandler;
import org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor;
import org.jboss.resteasy.core.SynchronousDispatcher;
import org.jboss.resteasy.plugins.server.embedded.EmbeddedJaxrsServer;
import org.jboss.resteasy.plugins.server.embedded.SecurityDomain;
import org.jboss.resteasy.spi.ResteasyDeployment;
import javax.net.ssl.SSLContext;
import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
/**
* An HTTP server that sends back the content of the received HTTP request
* in a pretty plaintext form.
*
* @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
* @author Andy Taylor (andy.taylor@jboss.org)
* @author <a href="http://gleamynode.net/">Trustin Lee</a>
* @author Norman Maurer
* @version $Rev: 2080 $, $Date: 2010-01-26 18:04:19 +0900 (Tue, 26 Jan 2010) $
*/
public class NettyJaxrsServer implements EmbeddedJaxrsServer
{
protected ServerBootstrap bootstrap;
protected Channel channel;
protected String hostname = null;
protected int configuredPort = 8080;
protected int runtimePort = -1;
protected ResteasyDeployment deployment = new ResteasyDeployment();
protected String root = "";
protected SecurityDomain domain;
private int ioWorkerCount = Runtime.getRuntime().availableProcessors() * 2;
private int executorThreadCount = 16;
private SSLContext sslContext;
private int maxRequestSize = 1024 * 1024 * 10;
private boolean isKeepAlive = true;
private List<ChannelHandler> channelHandlers = Collections.emptyList();
private Map<String, Object> channelOptions = Collections.emptyMap();
static final ChannelGroup allChannels = new DefaultChannelGroup("NettyJaxrsServer");
public void setSSLContext(SSLContext sslContext)
{
this.sslContext = sslContext;
}
/**
* Specify the worker count to use. For more informations about this please see the javadocs of {@link NioServerSocketChannelFactory}
*
* @param ioWorkerCount
*/
public void setIoWorkerCount(int ioWorkerCount)
{
this.ioWorkerCount = ioWorkerCount;
}
/**
* Set the number of threads to use for the Executor. For more informations please see the javadocs of {@link OrderedMemoryAwareThreadPoolExecutor}.
* If you want to disable the use of the {@link ExecutionHandler} specify a value <= 0. This should only be done if you are 100% sure that you don't have any blocking
* code in there.
*
*
* @param executorThreadCount
*/
public void setExecutorThreadCount(int executorThreadCount)
{
this.executorThreadCount = executorThreadCount;
}
/**
* Set the max. request size in bytes. If this size is exceed we will send a "413 Request Entity Too Large" to the client.
*
* @param maxRequestSize the max request size. This is 10mb by default.
*/
public void setMaxRequestSize(int maxRequestSize)
{
this.maxRequestSize = maxRequestSize;
}
public void setKeepAlive(boolean isKeepAlive)
{
this.isKeepAlive = isKeepAlive;
}
public String getHostname() {
return hostname;
}
public void setHostname(String hostname) {
this.hostname = hostname;
}
public int getPort()
{
return runtimePort > 0 ? runtimePort : configuredPort;
}
public void setPort(int port)
{
this.configuredPort = port;
}
/**
* Add additional {@link org.jboss.netty.channel.ChannelHandler}s to the {@link org.jboss.netty.bootstrap.ServerBootstrap}.
* <p>The additional channel handlers are being added <em>before</em> the HTTP handling.</p>
*
* @param channelHandlers the additional {@link org.jboss.netty.channel.ChannelHandler}s.
*/
public void setChannelHandlers(final List<ChannelHandler> channelHandlers) {
this.channelHandlers = channelHandlers == null ? Collections.<ChannelHandler>emptyList() : channelHandlers;
}
/**
* Add channel options to Netty {@link org.jboss.netty.bootstrap.ServerBootstrap}.
*
* @param channelOptions a {@link java.util.Map} containing the Netty bootstrap options.
* @see org.jboss.netty.bootstrap.ServerBootstrap#setOptions(java.util.Map)
*/
public void setChannelOptions(final Map<String, Object> channelOptions) {
this.channelOptions = channelOptions == null ? Collections.<String, Object>emptyMap() : channelOptions;
}
@Override
public void setDeployment(ResteasyDeployment deployment)
{
this.deployment = deployment;
}
@Override
public void setRootResourcePath(String rootResourcePath)
{
root = rootResourcePath;
if (root != null && root.equals("/")) root = "";
}
@Override
public ResteasyDeployment getDeployment()
{
return deployment;
}
@Override
public void setSecurityDomain(SecurityDomain sc)
{
this.domain = sc;
}
@Override
public void start()
{
deployment.start();
RequestDispatcher dispatcher = new RequestDispatcher((SynchronousDispatcher)deployment.getDispatcher(), deployment.getProviderFactory(), domain);
// Configure the server.
bootstrap = new ServerBootstrap(
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool(),
ioWorkerCount));
ChannelPipelineFactory factory;
if (sslContext == null) {
factory = new HttpServerPipelineFactory(dispatcher, root, executorThreadCount, maxRequestSize, isKeepAlive, channelHandlers);
} else {
factory = new HttpsServerPipelineFactory(dispatcher, root, executorThreadCount, maxRequestSize, isKeepAlive, channelHandlers, sslContext);
}
// Set up the event pipeline factory.
bootstrap.setPipelineFactory(factory);
// Add custom bootstrap options
bootstrap.setOptions(channelOptions);
// Bind and start to accept incoming connections.
final InetSocketAddress socketAddress;
if(null == hostname || hostname.isEmpty()) {
socketAddress = new InetSocketAddress(configuredPort);
} else {
socketAddress = new InetSocketAddress(hostname, configuredPort);
}
channel = bootstrap.bind(socketAddress);
allChannels.add(channel);
runtimePort = ((InetSocketAddress) channel.getLocalAddress()).getPort();
}
@Override
public void stop()
{
runtimePort = -1;
allChannels.close().awaitUninterruptibly();
if (bootstrap != null) {
bootstrap.releaseExternalResources();
}
deployment.stop();
}
}