/*
* COMSAT
* Copyright (C) 2014-2016, Parallel Universe Software Co. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 3.0
* as published by the Free Software Foundation.
*/
package co.paralleluniverse.embedded.containers;
import io.undertow.Handlers;
import io.undertow.Undertow;
import io.undertow.server.DefaultByteBufferPool;
import io.undertow.server.HttpHandler;
import io.undertow.server.handlers.RequestLimit;
import io.undertow.servlet.Servlets;
import io.undertow.servlet.api.*;
import io.undertow.servlet.core.CompositeThreadSetupAction;
import io.undertow.servlet.util.ConstructorInstanceFactory;
import io.undertow.websockets.jsr.JsrWebSocketFilter;
import io.undertow.websockets.jsr.ServerWebSocketContainer;
import java.util.Collections;
import javax.servlet.DispatcherType;
import javax.servlet.Servlet;
import javax.servlet.ServletContextListener;
import org.xnio.OptionMap;
import org.xnio.Options;
import org.xnio.Xnio;
import org.xnio.XnioWorker;
public final class UndertowServer extends AbstractEmbeddedServer {
private static final String ANY_LOCAL_ADDRESS = "0.0.0.0"; // not "localhost"!
private DeploymentInfo deployment;
private Undertow server;
private void build() {
if (deployment != null)
return;
this.deployment =
Servlets.deployment().setDeploymentName("")
.setClassLoader(ClassLoader.getSystemClassLoader())
.setContextPath("/");
}
@Override
public final ServletDesc addServlet(String name, Class<? extends Servlet> servletClass, String mapping) {
build();
final ServletInfo info = Servlets.servlet(name, servletClass).addMapping(mapping).setAsyncSupported(true);
deployment.addServlet(info);
return new UndertowServletDesc(info);
}
@Override
public final void start() throws Exception {
final DeploymentManager servletsContainer = Servlets.defaultContainer().addDeployment(deployment);
servletsContainer.deploy();
HttpHandler handler = servletsContainer.start();
handler = Handlers.requestLimitingHandler(new RequestLimit(maxConn), handler);
this.server = Undertow.builder()
.setHandler(handler)
.setIoThreads(nThreads)
.addHttpListener(port, ANY_LOCAL_ADDRESS)
.build();
new Thread(new Runnable() {
@Override
public final void run() {
server.start();
}
}).start();
waitUrlAvailable("http://localhost:" + port);
}
@Override
public final void stop() throws Exception {
if (server != null)
server.stop();
}
@Override
public final void addServletContextListener(Class<? extends ServletContextListener> scl) {
build();
final ListenerInfo li = Servlets.listener(scl);
deployment.addListener(li);
}
@Override
public final void enableWebsockets() throws Exception {
final Xnio xnio = Xnio.getInstance("nio", this.getClass().getClassLoader());
final XnioWorker worker = xnio.createWorker(OptionMap.builder()
.set(Options.WORKER_IO_THREADS, 8)
.set(Options.CONNECTION_HIGH_WATER, 1000000)
.set(Options.CONNECTION_LOW_WATER, 1000000)
.set(Options.WORKER_TASK_CORE_THREADS, 30)
.set(Options.WORKER_TASK_MAX_THREADS, 30)
.set(Options.TCP_NODELAY, true)
.set(Options.CORK, true)
.getMap());
final ClassIntrospecter ci = new ClassIntrospecter() {
@Override
public final <T> InstanceFactory<T> createInstanceFactory(final Class<T> clazz) {
try {
return new ConstructorInstanceFactory<>(clazz.getDeclaredConstructor());
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
};
//noinspection unchecked
final ServerWebSocketContainer wsc =
new ServerWebSocketContainer (
ci, worker, new DefaultByteBufferPool(true, 100),
new CompositeThreadSetupAction(Collections.EMPTY_LIST), false, false
);
final FilterInfo fi = new FilterInfo("filter", JsrWebSocketFilter.class);
fi.setAsyncSupported(true);
deployment
.addFilter(fi)
.addFilterUrlMapping("filter", "/*", DispatcherType.REQUEST)
.addServletContextAttribute(javax.websocket.server.ServerContainer.class.getName(), wsc);
}
@Override
public final void setResourceBase(String resourceBaseUrl) {
// TODO
throw new UnsupportedOperationException("Not supported yet.");
}
private static final class UndertowServletDesc implements ServletDesc {
private final ServletInfo impl;
public UndertowServletDesc(ServletInfo info) {
this.impl = info;
}
@Override
public final ServletDesc setInitParameter(String name, String value) {
impl.addInitParam(name, value);
return this;
}
@Override
public final ServletDesc setLoadOnStartup(int load) {
impl.setLoadOnStartup(load);
return this;
}
}
}