Introduction
In April 2018, I wrote a quick tutorial article on creating a web application using Spring Boot, packaged in WAR archive. It runs with a embedded Tomcat Application Server. This article will use the same code base but hosted with an embedded Jetty Application Server. You might ask, what is the significance by duplicating the same article? The significance is when you finish this tutorial, you can easily swap out the embedded Tomcat server and use embedded Jetty server. It is always better to have choices than being stuck with just one option.
Quick Review of the Source Code
I didn't make any changes with the actual source code from the twin article. If you are lazy and not wanting to flip between this tutorial and the other tutorial, just go through this section quickly, then we will jump into the good stuff on swapping out the embedded Tomcat server with the embedded Jetty server.
Here is the code for the main entry:
package org.hanbo.boot.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication
public class App extends SpringBootServletInitializer
{
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder appBuilder)
{
return appBuilder.sources(App.class);
}
public static void main(String[] args) throws Exception
{
SpringApplication.run(App.class, args);
}
}
The main entry sets up the application to be run as a web application. The class extends from SpringBootServletInitializer
which allows the application to be packaged in a WAR archive. The embedded server would use the WAR archive file structure to run the application. It is an ideal approach for hosting a Spring MVC application, with JSP template and all.
Every MVC application has a controller. Here it is:
package org.hanbo.boot.app.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class HelloController
{
@RequestMapping(value="/meow", method = RequestMethod.GET)
public ModelAndView hello(
@RequestParam("sayit")
String sayit
)
{
ModelAndView retVal = new ModelAndView();
retVal.setViewName("testme");
retVal.addObject("mymessage", sayit);
return retVal;
}
}
All this controller does is set up the web page that echoes back some input. Here is a URL:
http://localhost:8080/meow?sayit=This+is+pretty+crazy
As long as the application runs, and when you paste the URL in the browser and hit Enter, you will see a page displaying:
What did you say?
I said: "This is pretty crazy."
I need to let the web application know where to find the JSP template, the view name which is called "testme
". I need to let the web application know what the file extension of the view template file. I have specified these in application.properties
:
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
This looks familiar, right? Just a hint: org.springframework.web.servlet.view.InternalResourceViewResolver
.
Finally, the view template JSP file is called "testme.jsp":
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html lang="en">
<head>
<c:url value="/assets/css/index.css" var="jstlCss" />
<link href="${jstlCss}" rel="stylesheet" />
</head>
<body>
<p>What did you say?</p>
<p>I said: <span class="text-underline">"${mymessage}."</span></p>
<script type="text/javascript" src="/assets/js/test.js">
</script>
</body>
</html>
This web application also contains some static content, which can be found in src/main/resources/static/** directory. By convention, Spring Boot looks for the static content here.
Anyways, I will describe how to test this application later. It is time to show the good stuff. The only change I have to make for the application to run successfully with embedded Jetty server is in the maven POM file.
The Maven POM File for Jetty
The Maven POM file is used for dependency management, for building and WAR packaging. By default, it packages embedded Tomcat server in the WAR archive, that is, all the jar dependencies in the WAR archive. Now if I need to replace the embedded Tomcat server with the embedded Jetty server, I need to first exclude all the Tomcat embedded server jars and add the embedded Jetty server jars. Because Jetty is not the default application container used by Spring Boot, and with Spring MVC, JSP/JSTL enabled for this Spring application. At the time, it is all quite overwhelming. This is why I document this to save everyone the trouble, and I liked the challenge. Now I will tell you how this is done.
The first thing I do is to exclude the Tomcat server jars. Here is how I do it:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
Artifact "spring-boot-starter-web
" is to include all the jar files needed for web applications, which also included Tomcat. It is why I need to have a little section to exclude the artifact "spring-boot-starter-tomcat". I didn't come up with this by myself. You can find this in one of the tutorials provided by Baeldung. Next is to add the self-hosting Jetty dependencies. Here it is:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
Again, I didn't come up with this by myself. If you guess, yeah, from the same tutorial provided by Baeldung. However, that is all I can get from Baeldung. The two moves I've just done only supports the basic web application functionality. If you run the application just like this with no additional jars, the application just won't work. It does not help with my little application here because I need JSP/JSTL support, and I need to package the application as a WAR file. So with a little research, I came up with a solution that just works. And it is so simple it just blows my mind. This solution is to add three more artifacts, like this:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>apache-jsp</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>apache-jstl</artifactId>
</dependency>
The most important ones are the last two artifacts, once you have it in the POM file, it will bring in all the necessary jar files for embedded Jetty server. Before you move on, take a careful look at the entire maven POM file. In it, you may find the way of packing the file as a war, and use Spring Boot build plugin to build the application. Now it is time to run the application.
Testing the Application
Before you build and run the application, please go into the static web content folders and rename all the *.sj file to *.js files.
To build the application, first go to the base directory of the project (where the POM file located), then use the following Maven build command:
mvn clean install
After clicking Enter on the command line prompt, it will download all the dependencies, which will take some time, so be patient. Once the build succeeded, you can run the application with just one command. Assuming you are still at the base directory of the project, type in command:
java -jar target\boot-war-1.0.0.war
When the command runs, it will spit out a lot of non-sense, look for segments like this:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.5.RELEASE)
2019-02-11 23:06:59.055 INFO 12404 --- [ main] org.hanbo.boot.app.App
: Starting App v1.0.0 on U3DTEST-PC with PID 12404 (C:\Users\
u3dadmin\workspace-mars8\SpringBootJettyWar\target\boot-war-1.0.0.war started by
u3dadmin in C:\Users\u3dadmin\workspace-mars8\SpringBootJettyWar)
2019-02-11 23:06:59.132 INFO 12404 --- [ main] org.hanbo.boot.app.App
: No active profile set, falling back to default profiles: de
fault
2019-02-11 23:06:59.584 INFO 12404 --- [ main] ConfigServletWebServer
ApplicationContext : Refreshing org.springframework.boot.web.servlet.context.Ann
otationConfigServletWebServerApplicationContext@6aceb1a5: startup date [Mon Feb
11 23:06:59 EST 2019]; root of context hierarchy
2019-02-11 23:07:04.389 INFO 12404 --- [ main] o.s.b.w.e.j.JettyServl
etWebServerFactory : Server initialized with port: 8080
....
2019-02-11 23:07:09.103 INFO 12404 --- [ main] o.e.jetty.server.handl
er.ContextHandler : Started o.s.b.w.e.j.JettyEmbeddedWebAppContext@6293abcc{app
lication,/,[org.springframework.boot.web.embedded.jetty.JettyServletWebServerFac
tory$LoaderHidingResource@7995092a],AVAILABLE}
2019-02-11 23:07:09.106 INFO 12404 --- [ main] org.eclipse.jetty.serv
er.Server : Started @16076ms
....
2019-02-11 23:07:10.454 INFO 12404 --- [ main] o.e.j.s.h.ContextHandl
er.application : Initializing Spring FrameworkServlet 'dispatcherServlet'
2019-02-11 23:07:10.454 INFO 12404 --- [ main] o.s.web.servlet.Dispat
cherServlet : FrameworkServlet 'dispatcherServlet': initialization starte
d
2019-02-11 23:07:10.479 INFO 12404 --- [ main] o.s.web.servlet.Dispat
cherServlet : FrameworkServlet 'dispatcherServlet': initialization comple
ted in 20 ms
2019-02-11 23:07:10.844 INFO 12404 --- [ main] o.e.jetty.server.Abstr
actConnector : Started ServerConnector@1bd4fdd{HTTP/1.1,[http/1.1]}{0.0.0.
0:8080}
2019-02-11 23:07:10.849 INFO 12404 --- [ main] o.s.b.web.embedded.jet
ty.JettyWebServer : Jetty started on port(s) 8080 (http/1.1) with context path
'/'
2019-02-11 23:07:10.854 INFO 12404 --- [ main] org.hanbo.boot.app.App
: Started App in 15.3 seconds (JVM running for 17.828)
The first segment is the start of the application run. Somewhere at the very end, you can see the embedded Jetty server initialization. In the middle of the output, there is more of Jetty server starting up. At the very end, we see that the application is running on port 8080 and is ready to accept user request. If you don't see any exception output, and see the last line there, you have started the application successfully.
To test the application, just copy and paste the following link to browser:
http://localhost:8080/meow?sayit=This+is+pretty+crazy
Then you will see a simple page displaying:
What did you say?
I said: "This is pretty crazy."
Or try something new:
http://localhost:8080/meow?sayit=Hello+World
Then you will see a simple page displaying:
What did you say?
I said: "Hello World."
Summary
This is a very straight forward tutorial on Spring Boot. I took one article which I submitted last year and removed the use of embedded Tomcat server and replaced it with embedded Jetty server. It is fairly straight forward, all one needs to do is exclude the artifact "spring-boot-starter-tomcat
". Then add the equivalent artifact for embedded Jetty server. To support JSP/JSTL for Spring MVC, one must add at least two more Jetty related artifacts for JSP/JSTL. Once you know how to do this, you can use the same approach to replace all embedded Tomcat servers from the sample projects of my Spring Boot tutorials from 2018. Enjoy!
History
- 02/11/2019 - Initial draft