Hi everybody,
I'm trying to migrate my webapp from Jetty to Undertow.

The stack is composed by Undertow Guice and Struts2.

I developed the following class to configure undertow:

package com.billdrawer.website.server;

import io.undertow.Undertow;
import io.undertow.predicate.Predicates;
import io.undertow.server.HandlerWrapper;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.PathHandler;
import io.undertow.server.handlers.PredicateHandler;
import io.undertow.server.handlers.resource.ResourceHandler;
import io.undertow.servlet.api.ConfidentialPortManager;
import io.undertow.servlet.api.Deployment;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.DeploymentManager;
import io.undertow.servlet.api.FilterInfo;
import io.undertow.servlet.api.ListenerInfo;
import io.undertow.servlet.api.LoginConfig;
import io.undertow.servlet.api.SecurityConstraint;
import io.undertow.servlet.api.ServletContainer;
import io.undertow.servlet.api.WebResourceCollection;

import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.EnumSet;

import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.DispatcherType;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;

import org.apache.log4j.Logger;
import org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter;
import org.keycloak.adapters.AdapterConstants;
import org.keycloak.adapters.undertow.KeycloakServletExtension;

import com.billdrawer.website.common.Config;
import com.billdrawer.website.listener.WebsiteServletContextListener;
import com.google.inject.servlet.GuiceFilter;

public class UndertowServer implements Server {
    private Logger _logger = Logger.getLogger(UndertowServer.class);
    private Undertow _server;
    private DeploymentManager _manager;
    private int _port;
    private String _host;

    @Inject
    public UndertowServer(@Named(Config.STAGE) String stage, @Named(Config.SERVER_HOST) String host, @Named(Config.SERVER_PORT) int port,
            @Named(Config.SERVER_TIMEOUT) int timeout) {
        _port = port;
        _host = host;

        DeploymentInfo deploymentInfo = getDeploymentInfo(stage);

        ServletContainer _container = ServletContainer.Factory.newInstance();
        _manager = _container.addDeployment(deploymentInfo);
        _manager.deploy();

        Deployment deployment = _manager.getDeployment();
        
        KeycloakServletExtension keycloak = new KeycloakServletExtension();
        deploymentInfo.addServletExtension(keycloak);
        deploymentInfo.addInitParameter(AdapterConstants.AUTH_DATA_PARAM_NAME, getKeyloakJson());
        keycloak.handleDeployment(deploymentInfo, _manager.getDeployment().getServletContext());

        final ServletContext servletContext = deployment.getServletContext();
        _logger.debug("Context initialized:"+servletContext.getContextPath());
    }

    protected String getKeyloakJson() {
        String keycloak = "";
        URL url = getClassLoader().getResource("keycloak.json");
        if (url != null) {
            try {
                byte[] encoded = Files.readAllBytes(Paths.get(url.toURI()));
                keycloak = new String(encoded, "utf-8");
            } catch (Exception e) {
                _logger.error("Can't read keycloak.json", e);
            }
        }
        return keycloak;
    }

    protected DeploymentInfo getDeploymentInfo(String stage) {
        final DeploymentInfo deploymentInfo = new DeploymentInfo();
        deploymentInfo.setClassLoader(getClassLoader());
        deploymentInfo.setContextPath("/");
        deploymentInfo.setDefaultEncoding("UTF-8");
        deploymentInfo.setDeploymentName("website.war");
        deploymentInfo.setDisplayName("WebsiteServer");
        deploymentInfo.setUrlEncoding("UTF-8");

        deploymentInfo.addListener(new ListenerInfo(WebsiteServletContextListener.class));

        FilterInfo guiceFilter = new FilterInfo("GuiceFilter", GuiceFilter.class);
        deploymentInfo.addFilter(guiceFilter);
        for (DispatcherType dispatcher : EnumSet.allOf(DispatcherType.class)) {
            deploymentInfo.addFilterUrlMapping(guiceFilter.getName(), "/*", dispatcher);
        }

        FilterInfo strutsPrepareAndExecuteFilter = new FilterInfo("StrutsFilter", StrutsPrepareAndExecuteFilter.class);
        deploymentInfo.addFilter(strutsPrepareAndExecuteFilter);
        for (DispatcherType dispatcher : EnumSet.allOf(DispatcherType.class)) {
            deploymentInfo.addFilterUrlMapping(strutsPrepareAndExecuteFilter.getName(), "/*", dispatcher);
        }

        deploymentInfo.addInitialHandlerChainWrapper(new HandlerWrapper() {
            @Override
            public HttpHandler wrap(final HttpHandler handler) {
                final ResourceHandler resourceHandler = new ResourceHandler(deploymentInfo.getResourceManager());
                PredicateHandler predicateHandler = new PredicateHandler(Predicates.suffixes(".css",".html",".js"),resourceHandler, handler);
                return predicateHandler;
            }
        });

        configureSecurity(deploymentInfo);

        return deploymentInfo;
    }

    protected void configureSecurity(DeploymentInfo deploymentInfo) {
        deploymentInfo.setLoginConfig(new LoginConfig("KEYCLOAK", "billdrawer"));
        deploymentInfo.setConfidentialPortManager(new ConfidentialPortManager() {
            @Override
            public int getConfidentialPort(HttpServerExchange exchange) {
                return _port;
            }
        });
        SecurityConstraint securityConstraint = new SecurityConstraint();
        WebResourceCollection webResourceCollection = new WebResourceCollection();
        webResourceCollection.addUrlPattern("/dashboard/*");
        securityConstraint.addWebResourceCollection(webResourceCollection);
        securityConstraint.addRoleAllowed("user");
        deploymentInfo.addSecurityConstraint(securityConstraint);
    }

    protected ClassLoader getClassLoader() {
        return getClass().getClassLoader();
    }

    public void start() {
        _logger.debug("starting...");
        try {
            Undertow.Builder builder = Undertow.builder();
            builder.addHttpListener(_port, _host);

            final PathHandler rootHandler = new PathHandler();
            rootHandler.addPrefixPath(_manager.getDeployment().getDeploymentInfo().getContextPath(), _manager.start());

            builder.setHandler(rootHandler);
            _server = builder.build();
            _server.start();
        } catch (ServletException e) {
            _logger.error("Unexpected exception", e);
        }
        _logger.info("###START###");
    }

    public void stop() {
        _logger.debug("stopping...");
        try {
            _manager.stop();
            _server.stop();
        } catch (ServletException e) {
            _logger.error("Unexpected exception", e);
        }
        _logger.info("###STOP###");
    }
}
Everything is working until I do not try to load resources from context.
I need some help before rollback to Jetty :(

--
Davide