Blog-Archiv

Freitag, 3. November 2023

A Minimal Java Servlet with Filters

I took out a servlet filter that had no filter-mapping from a web.xml of a (very old) web-app, and - BOOM! - the app did not work any more.

One would assume that a filter without filter-mapping should be ignored by the web-container, but I could not verify this in the servlet 4.0 specification. I couldn't believe that removing a filter without filter-mapping can affect a web-app in any way, so I wrote a minimal servlet application to try this out. Environment was Tomcat 9, Servlet 4.0, Maven 3.6.3.

Objectives of this article are

  1. showing how a minimal Java servlet looks like
  2. trying out whether a filter without filter-mapping is applied or ignored by the web-container.

Application Specification

The servlet outputs ">Hallo Welt<" into the browser, where "Hallo" is determined by one filter, and "Welt" by another filter. So, if I remove the filter-mapping of the "Welt" filter, the output should be ">Hallo null<". This has to be proven.

Maven Project

I recommend to first create the directories and files of the project, then compile it with Maven, then import it as "Maven Project" into Eclipse.

I named the Maven artifact testServlet40, which designates the servlet's context-path. I also named the root directory of the project testServlet40. Thus the web-app is available in a web-browser using this address:

  • http://localhost:8080/testServlet40

The file pom.xml describes the Maven project object model (dependencies).
The file web.xml is the Servlet deloyment descriptor that controls the interaction between the servlet-container (Tomcat, Jetty, ...) and the web-app (servlet).

Following are the project folders and source files. Click on the expand controls to see collapsed sources.

testServlet40
pom.xml
Depends on servlet-api only, because I don't even use JSP here:
<project 
    xmlns="http://maven.apache.org/POM/4.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>fri</groupId>
  <artifactId>testServlet40</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>Minimal Servlet 4.0 Example3</name>

  <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <!-- without this, Eclipse complains about "Dynamic Web Module 4.0 requires Java 1.8 or newer" -->
  </properties>
    
  <dependencies>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>
    
  </dependencies>

</project>
src
main
java
fri
testServlet40
HelloWorldServlet.java
Builds together two request attribute values that were set by filters, see lines 23 and 27:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package fri.testServlet40;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HelloWorldServlet extends HttpServlet
{
    public static final String HELLO_ATTRIBUTENAME = "hello";
    public static final String WORLD_ATTRIBUTENAME = "world";
    
    @Override
    protected void doGet(
            HttpServletRequest request, 
            HttpServletResponse response)
        throws ServletException, IOException 
    {
        System.err.println("HelloWorldServlet start request on contextPath '"+request.getContextPath()+"'");
        
        final Object hello = request.getAttribute(HELLO_ATTRIBUTENAME);
        final Object world = request.getAttribute(WORLD_ATTRIBUTENAME);
        
        final PrintWriter out = response.getWriter();
        out.println(">"+hello+" "+world+"<");
        out.close();
        
        System.err.println("HelloWorldServlet end request on contextPath '"+request.getContextPath()+"'");
    }
}
HelloFilter.java
Sets the "hello" request attribute value for the servlet, see line 21:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package fri.testServlet40;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class HelloFilter implements Filter
{
    @Override
    public void doFilter(
            ServletRequest request, 
            ServletResponse response, 
            FilterChain chain)
        throws IOException, ServletException
    {
        System.err.println("HelloFilter start");
        
        request.setAttribute(HelloWorldServlet.HELLO_ATTRIBUTENAME, "Hallo");
        
        chain.doFilter(request, response);
        
        System.err.println("HelloFilter end");
    }
}
WorldFilter.java
Sets the "world" request attribute value for the servlet, see line 21:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package fri.testServlet40;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class WorldFilter implements Filter
{
    @Override
    public void doFilter(
            ServletRequest request, 
            ServletResponse response, 
            FilterChain chain)
        throws IOException, ServletException
    {
        System.err.println("WorldFilter start");
        
        request.setAttribute(HelloWorldServlet.WORLD_ATTRIBUTENAME, "Welt");
        
        chain.doFilter(request, response);
        
        System.err.println("WorldFilter end");
    }
}
webapp
WEB-INF
web.xml
Binds together the servlet and its filters. Mind that the servlet-name is not what appears in the URL address, moreover the URL consists of context-path (basename of .war file, Maven artifact name) and the url-pattern defined here:
<?xml version="1.0"?>
<web-app version="4.0"
        xmlns="http://xmlns.jcp.org/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="
            http://xmlns.jcp.org/xml/ns/javaee 
            http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd">

    <servlet>
        <servlet-name>helloworld-servlet</servlet-name>
        <servlet-class>fri.testServlet40.HelloWorldServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>helloworld-servlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

    <!-- Filters are called in the order they appear here -->
    
    <filter>
        <filter-name>world-filter</filter-name>
        <filter-class>fri.testServlet40.WorldFilter</filter-class>
    </filter>

    <filter>
        <filter-name>hello-filter</filter-name>
        <filter-class>fri.testServlet40.HelloFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>world-filter</filter-name>
        <servlet-name>helloworld-servlet</servlet-name>
    </filter-mapping>

    <filter-mapping>
        <filter-name>hello-filter</filter-name>
        <servlet-name>helloworld-servlet</servlet-name>
    </filter-mapping>

</web-app>

Running the Application

You can compile and pack the web-application by this Maven commandline:

cd testServlet40
mvn install

Then deploy the created testServlet40-0.0.1-SNAPSHOT.war file into your favorite web-server and call the URL

  • http://localhost:8080/testServlet40

in your favorite web-browser.
On the server console you will see this:

WorldFilter start
HelloFilter start
HelloWorldServlet start request on contextPath '/testServlet40'
HelloWorldServlet end request on contextPath '/testServlet40'
HelloFilter end
WorldFilter end

This proves that filters are called in the order they appear in web.xml: first WorldFilter, then HelloFilter. Then the servlet is called, and afterwards the filters can do cleanup, but in reverse order.

Note that any filter can prevent the execution of other filters and the servlet by not calling chain.doFilter(request, response).

Result

In the browser I can see the expected result:

>Hallo Welt<

When I remove the filter-mapping of WorldFilter in web.xml and pack and deploy the web-app again, the output changes:

>Hallo null<

Thus it is true that a filter without filter-mapping is ignored.




Keine Kommentare: