Deploying a JSF 2.3 Application on Tomcat 9

It’s exciting to work with the latest technologies, and it’s exciting to try to set things up because eventually, it helps you learn. This is how I ran a JSF 2.3 application on a Tomcat 9.
Before getting started, you may say that this is a simple task, it is, it’s not a big deal, but I decided to write this for 2 reasons, first to simply keep a history of what I did, second the highlight some things that I have encountered along the way.
So I started by preparing my environment, I am working on a Windows 10, so I downloaded and extracted my favorite IDE, Eclipse.
The version that I used was the latest Oxygen build:
Version: Oxygen.1a Release (4.7.1a)
Build id: 20171005-1200
I configured Eclipse to use JDK 1.8, specifically jdk1.8.0_152.
I began by creating a new Maven project with no archetype. And so the hunt for dependencies begins.
I chose Tomcat 9 because I am familiar with it although Tomcat is not a full Java EE container and so it doesn’t contain JSF, JPA and so on… There is TomEE but the latest version that I found supports Java EE 7, and I’m interested in working on Java EE 8, that means Servlet 4.0 and HTTP 2.0!
So preparing now the POM file, I fetched my first 2 dependencies:
<!-- Servlet --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.0</version> <scope>provided</scope> </dependency> <!-- JSF API --> <dependency> <groupId>javax.faces</groupId> <artifactId>javax.faces-api</artifactId> <version>2.3</version> </dependency> |
These were enough to allow me to set up a ServletContextListener
in order to register the JSF Servlet famously known as FacesServlet
, so I created the following class:
package web.tier.listeners; import java.util.logging.Logger; import javax.faces.webapp.FacesServlet; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.ServletRegistration.Dynamic; import javax.servlet.annotation.WebListener; @WebListener public class MyServletContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { ServletContextListener.super.contextInitialized(sce); //Registering the JSF Servlet Dynamic facesServlet = sce.getServletContext().addServlet("FacesServlet", FacesServlet.class); //Specifying the Servlet Mapping facesServlet.addMapping("*.xhtml"); //Setting Priority, 0 or higher for eager, if negative then it's lazy facesServlet.setLoadOnStartup(1); Logger.getGlobal().info("Servlet Context has been initialized"); } } |
So I started my app on Tomcat without any web.xml
or faces-config.xml
and I received a NullPointerException
when I try to add the Servlet. According to the java doc of addServlet
this will return a null
in case this Servlet is already registered. Nice! It seems Faces Servlet was already registered. I validated this by retrieving the Servlet Registrations from the Servlet Context. It also defines mappings such as *.xhtml
. Cool! By the way, I tested GlassFish 5 and I couldn’t detect the same behavior.
So simply I removed those 3 lines from my class, and ran my application again, this time, I received that some class is not found, something related to JSF, and I remembered that I am using Tomcat, so I also need an implementation for JSF! So I added the Mojarra dependency:
<dependency> <groupId>org.glassfish</groupId> <artifactId>javax.faces</artifactId> <version>2.3.0</version> </dependency> |
And I remembred that I haven’t created yet any bean or page, so I created a very simple xhtml
page with a very simple bean, and yes we need to use CDI annotations, so I added the following dependencies to my POM. (As you can see I’m always after the latest specs of Java EE 8)
<dependency> <groupId>javax.enterprise</groupId> <artifactId>cdi-api</artifactId> <version>2.0</version> </dependecy> |
And now I started the application again, please note that this is the first time I work with CDI, previously I used @ManagedBean
and Spring. So I got an exception, and I need to write it down…
Caused by: javax.faces.FacesException: Unable to find CDI BeanManager at com.sun.faces.el.ELUtils.buildFacesResolver(ELUtils.java:259) at com.sun.faces.application.ApplicationAssociate.initializeELResolverChains(ApplicationAssociate.java:473) at com.sun.faces.application.ApplicationImpl.performOneTimeELInitialization(ApplicationImpl.java:1369) at com.sun.faces.application.ApplicationImpl.getELResolver(ApplicationImpl.java:491) at com.sun.faces.config.ConfigureListener.contextInitialized(ConfigureListener.java:256) ... 30 more |
So I went to see what’s happening inside the class ELUtils
and I saw a very strange line, which basically says, if you’re using JSF 2.3 or Servlet 4.0, then throw an exception! And I was like… WHAT THE ****!
if (beanManager == null) { // TODO: use version enum and >= if (getFacesConfigXmlVersion(facesContext).equals("2.3") || getWebXmlVersion(facesContext).equals("4.0")) { throw new FacesException("Unable to find CDI BeanManager"); } } else { CdiExtension cdiExtension = getBeanReference(beanManager, CdiExtension.class); if (cdiExtension.isAddBeansForJSFImplicitObjects()) { composite.add(beanManager.getELResolver()); return true; } } |
And so I almost arrived at a conclusion that says that I am using now Mojarra 2.3 and it’s going to throw an Exception whenever it detects Servlet 4.0, I was down, sad, until I saw that there’s a new version on Maven, and it’s 2.3.0-m05
.
I changed the version in my POM but I also got the same error, this time, the source code was changed, now it tries to detect a BeanManager
from the FacesContext
, I didn’t know what that means (I am new to CDI!) but finally at least something to try to solve.
BalusC
always comes to the rescue, he has a post on how to fix the CDI thing on Tomcat. I learned that yeah, I need an implementation for CDI, there’s one called Weld
and what made me happy more is that I saw that BalusC
updated this post a few days ago only. So I followed his tips and I added the implementation dependency in my POM as well as an empty beans.xml
file.
Finally, it was solved 😀
Thanks for the write up i did the same with tomcat 8 using weld yet am get error messsage unsatisfied dependency: no bean matches the injection point. How will i be able to fix this
actually for tomcat9, cdi can be build and deploy to tomcat lib directory, see http://tomcat.apache.org/tomcat-9.0-doc/cdi.html
Hi, I have a problem if I add Primefaces 8 in the project.
java.lang.NoSuchMethodError: ‘void javax.faces.application.Application.addSearchKeywordResolver(javax.faces.component.search.SearchKeywordResolver)’
Do you have any ideas? Thanks in advance