A Servlet is a web based component, managed by a container, that generates dynamic content. Currently, the latest release of Jakarta Servlets is 5.0 (September 7, 2020).
Jakarta Servlet version 5.0 is only a change of namespaces. Thus, migrating a Servlet 4.0 project to Servlet 5.0 and above, requires the replacement of the namespace javax.* with jakarta.*. Within this tutorial we will still use the javax namespace for compatibility with Jakarta EE 8.
Within this Servlet tutorial we will learn some basic examples of Servlets using annotations.
A Basic Servlet using annotations
package sample; import java.io.IOException; import java.io.*; import javax.servlet.*; import javax.servlet.annotation.*; import javax.servlet.http.*; @WebServlet(name="testServlet", urlPatterns={"/hello"}, initParams={ @WebInitParam(name="simpleParam", value="paramValue") } ) public class TestServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); String simpleParam = getServletConfig().getInitParameter("simpleParam"); out.println("Hello World "+simpleParam); out.close(); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request,response); } }
In the above code we have registered out TestServlet under the urlPatterns “/hello”. Additionally we’ve set an init param named “simpleParam”. No web.xml needed to run this Servlet.
Please note it’s possible to specify multiple urlPatterns for a Servlet as in this example:
@WebServlet(name="MyServlet", urlPatterns={"/foo", "/bar"}) public class SampleServlet extends HttpServlet{ public void doGet(HttpServletRequest req, HttpServletResponse res) { ... } }
Using Filters in Servlets
You can declare as well Filters using annotations, like in this example:
import java.io.*; import javax.servlet.*; import javax.servlet.annotation.*; @WebFilter(urlPatterns={"/*"}, initParams={ @WebInitParam(name="simpleParam", value="paramValue") }) public class TestFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { StringWriter sw = new StringWriter(); PrintWriter writer = new PrintWriter(sw); writer.println("==============="); writer.println("Filter intercepted!"); writer.println("==============="); // Log the resulting string writer.flush(); filterConfig.getServletContext(). log(sw.getBuffer().toString()); chain.doFilter(request, response); } private FilterConfig filterConfig = null; public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; } @Override public void destroy() { } }
A filter is a reusable piece of code that can transform the content of HTTP requests, responses, and header information. Filters do not generally create a response or respond to a request as servlets do, rather they modify or adapt the requests for a resource, and modify or adapt responses from a resource
In our example, that filter will intercept all request issued to the Web Context.
Using Listeners in Servlets
Another useful annotation introduced is the @WebListener annotation which can be used to tag a Java Class as WebListener:
package sample; import javax.servlet.*; @javax.servlet.annotation.WebListener public class SessionListener implements ServletContextListener { @Override public void contextDestroyed(ServletContextEvent sce) { System.out.println("Context destroyed!"); } @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("Context created!"); } }
Asynchronous Servlets
Asynchronous processing allows the thread to issue a call to the resource and return back to the container without getting blocked. The thread can perform other tasks, which makes Servlet 3.0’s performance more efficient. Implementing an Asynchronous Servlet requires using the AsyncContext, which can be obtainer from the HTTP request object. Here’s an example:
package sample; import java.io.IOException; import java.util.concurrent.ConcurrentLinkedQueue; import javax.servlet.*; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; import java.util.Queue; @WebServlet(urlPatterns = {"/synch"}, asyncSupported = true) public class AsynchServlet extends HttpServlet { private static final Queue queue = new ConcurrentLinkedQueue(); public void doGet(HttpServletRequest request, HttpServletResponse response) { final AsyncContext ac = request.startAsync(); ac.setTimeout(1 * 60 * 1000); queue.add(ac); ac.addListener(new AsyncListener() { @Override public void onError(AsyncEvent arg0) throws IOException { System.out.println("onError"); } public void onComplete(AsyncEvent event) throws IOException { System.out.println("onComplete"); queue.remove(ac); } public void onTimeout(AsyncEvent event) throws IOException { System.out.println("onTimeout"); queue.remove(ac); } @Override public void onStartAsync(AsyncEvent arg0) throws IOException { System.out.println("onStartAsync"); } }); queue.add(ac); } public void doPost(HttpServletRequest request, HttpServletResponse response) { doGet(request,response); } }
The Asynch listener interface needs implementing a few callback methods (onComplete, onTimeout, onStartAsync, onError) which are triggered during the lifecycle of the Asynchronous Servlet.
Programmatic Servlets/Filter setup
One of my favourite adds-on is the ability of declare Servlets or Filters programmatically: think about mapping a Servlet with a name collected by an external source:
public void contextInitialized(ServletContextEvent sce) { System.out.println("Context created!"); ServletContext sc = sce.getServletContext(); // Get it from any resource String servletMapping = "/dynamic" ServletRegistration sr = sc.addServlet ("DynaServlet","sample.DynamicServlet"); sr.setInitParameter("servletInitName", "servletInitValue"); sr.addMapping(servletMapping); }
Web Fragments
To ease Servlet development, in Servlet 3.0 has been introduced the notion of web descriptor fragments. A web fragment is a part or all of the web.xml that can be specified and included in a library or framework jar file.
Here’s an example of web fragment:
<web-fragment> <servlet> <description></description> <display-name>TestServlet</display-name> <servlet-name>TestServlet</servlet-name> <servlet-class>sample.TestServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>TestServlet</servlet-name> <url-pattern>/TestServlet</url-pattern> </servlet-mapping> </web-fragment>
Where to place web fragments ? If a framework is packaged as a jar file and has metadata information in the form of deployment descriptor then the web fragment needs to be placed in the META-INF/ folder of the jar file.
On the other hand, if a framework wants its web-fragment.xml honored in such a way that it augments a web application’s web.xml, the framework must be bundled within the web application’s WEB-INF/lib directory.
Coding a Servlet Deployment Descriptor
The deployment descriptor conveys the elements and configuration information of a web application between Application Developers, Application Assemblers, and Deployers.
For Servlet 5.0, the deployment descriptor is defined in terms of an XML schema document.
Here is a sample web.xml deployment descriptors which shows how to configure the core Servlet components:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee web-app_5_0.xsd" version="5.0"> <display-name>A Simple Application</display-name> <context-param> <param-name>Webmaster</param-name> <param-value>[email protected]</param-value> </context-param> <servlet> <servlet-name>catalog</servlet-name> <servlet-class>com.example.CatalogServlet</servlet-class> <init-param> <param-name>catalog</param-name> <param-value>Spring</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>catalog</servlet-name> <url-pattern>/catalog/*</url-pattern> </servlet-mapping> <session-config> <session-timeout>30</session-timeout> </session-config> <mime-mapping> <extension>pdf</extension> <mime-type>application/pdf</mime-type> </mime-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> <welcome-file>index.html</welcome-file> <welcome-file>index.html</welcome-file> </welcome-file-list> <error-page> <error-code>404</error-code> <location>/404.html</location> </error-page> </web-app>