Search code examples
jsfmojarraundertow

Does Undertow work with JSF?


I'm trying to boot-up Undertow with Mojarra, but an exception occurs. How to fix this error and work with JSF?

Here is similar question, but this solution does not affect mine.

HelloWorldServer.java (refs Undertow Servlet):

public class HelloWorldServer {

    public static void main(String[] args) throws ServletException {
        DeploymentInfo servletBuilder = Servlets.deployment()
                .setClassLoader(HelloWorldServer.class.getClassLoader())
                .setContextPath("/myapp")
                .setDeploymentName("test.war")
                .addServlets(Servlets.servlet("FacesServlet", FacesServlet.class)
                        .addMapping("*.xhtml")
                        .setLoadOnStartup(1));

        DeploymentManager manager = Servlets.defaultContainer().addDeployment(servletBuilder);
        manager.deploy();
        PathHandler path = Handlers.path(Handlers.redirect("/myapp"))
                .addPrefixPath("/myapp", manager.start());

        Undertow server = Undertow.builder()
                .addHttpListener(8080, "localhost")
                .setHandler(path)
                .build();
        server.start();
    }
}

pom.xml:

<properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <maven.compiler.source>1.8</maven.compiler.source>
  <maven.compiler.target>1.8</maven.compiler.target>
  <undertow.version>2.0.9.Final</undertow.version>
</properties>

<dependencies>
  <dependency>
    <groupId>io.undertow</groupId>
    <artifactId>undertow-core</artifactId>
    <version>${undertow.version}</version>
  </dependency>
  <dependency>
    <groupId>io.undertow</groupId>
    <artifactId>undertow-servlet</artifactId>
    <version>${undertow.version}</version>
  </dependency>
  <dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>javax.faces</artifactId>
    <version>2.3.5</version>
  </dependency>
</dependencies>

exception:

Jul 07, 2018 9:57:50 PM javax.faces.FactoryFinderInstance copyInjectionProviderFromFacesContext
SEVERE: Unable to obtain InjectionProvider from init time FacesContext. Does this container implement the Mojarra Injection SPI?
Jul 07, 2018 9:57:50 PM javax.faces.FactoryFinderInstance logNoFactory
SEVERE: Application was not properly initialized at startup, could not find Factory: javax.faces.context.FacesContextFactory. Attempting to find backup.
[WARNING] 
java.lang.IllegalStateException: Could not find backup for factory javax.faces.context.FacesContextFactory. 
    at javax.faces.FactoryFinderInstance.notNullFactory (FactoryFinderInstance.java:548)
    at javax.faces.FactoryFinderInstance.getFactory (FactoryFinderInstance.java:231)
    at javax.faces.FactoryFinder.getFactory (FactoryFinder.java:303)
    at javax.faces.webapp.FacesServlet.acquireFacesContextFactory (FacesServlet.java:524)
    at javax.faces.webapp.FacesServlet.init (FacesServlet.java:364)
    at io.undertow.servlet.core.LifecyleInterceptorInvocation.proceed (LifecyleInterceptorInvocation.java:117)
    at io.undertow.servlet.core.ManagedServlet$DefaultInstanceStrategy.start (ManagedServlet.java:300)
    at io.undertow.servlet.core.ManagedServlet.createServlet (ManagedServlet.java:140)
    at io.undertow.servlet.core.DeploymentManagerImpl$2.call (DeploymentManagerImpl.java:584)
    at io.undertow.servlet.core.DeploymentManagerImpl$2.call (DeploymentManagerImpl.java:555)
    at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call (ServletRequestContextThreadSetupAction.java:42)
    at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call (ContextClassLoaderSetupAction.java:43)
    at io.undertow.servlet.core.DeploymentManagerImpl.start (DeploymentManagerImpl.java:597)
    at com.github.yukihane.java.undertow.HelloWorldServer.main (HelloWorldServer.java:27)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:564)
    at org.codehaus.mojo.exec.ExecJavaMojo$1.run (ExecJavaMojo.java:282)
    at java.lang.Thread.run (Thread.java:844)

Solution

  • 1.

    Implement same as FacesInitializer#onStartup, so main method is like:

    DeploymentInfo servletBuilder = Servlets.deployment()
            .setClassLoader(HelloWorldServer.class.getClassLoader())
            .setContextPath("/myapp")
            .setDeploymentName("test.war")
            .addServlets(Servlets.servlet("FacesServlet", FacesServlet.class)
                    .addMappings("/faces/*", "*.jsf", "*.faces", "*.xhtml")
                    .setLoadOnStartup(1))
            .addServletContextAttribute(RIConstants.FACES_INITIALIZER_MAPPINGS_ADDED, Boolean.TRUE)
            .addListener(new ListenerInfo(com.sun.faces.config.ConfigureListener.class))
            .setResourceManager(new ClassPathResourceManager(HelloWorldServer.class.getClassLoader(), "static"));
    
    2.

    Managed Beans are scanned by AnnotationScanTask with DelegatingAnnotationProvider, that is default AnnotationProvider implementation. Because DelegatingAnnotationProvider does not scan root class path (but only /WEB-INF/classes(?)), I need implement custom AnnotationProvider.

    MyAnnotationProvider.java:

    package com.github.yukihane.java.undertow.service;
    
    public class MyAnnotationProvider extends DelegatingAnnotationProvider {
    
        public MyAnnotationProvider(ServletContext sc) {
            super(sc);
        }
    
        @Override
        public Map<Class<? extends Annotation>, Set<Class<?>>> getAnnotatedClasses(Set<URI> urls) {
            Map<Class<? extends Annotation>, Set<Class<?>>> parentRes = super.getAnnotatedClasses(urls);
    
            // simplified implementation, without annotations scan
            Map<Class<? extends Annotation>, Set<Class<?>>> ret = new HashMap<>(parentRes);
    
            Set<Class<?>> parentSet = ret.get(ManagedBean.class);
            Set<Class<?>> set = (parentSet == null) ? new HashSet<>() : new HashSet<>(parentSet);
            set.add(Hello.class);
            ret.put(ManagedBean.class, set);
    
            return ret;
        }
    
    }
    

    META-INF/services/com.sun.faces.spi.annotationprovider(refs AnnotationProviderFactory):

    com.github.yukihane.java.undertow.service.MyAnnotationProvider
    

    Here is complete sources: https://github.com/yukihane/hello-undertow-mojarra