Setting up HTTP auth protecting a Tomcat servlet

In my last blog post I covered a custom Java servlet running on Tomcat that gives statistics on current active users using a Java application running on the Tomcat server.

You may realize that this servlet, or any Tomcat servlet/application is world-readable, and you may want to protect it with a password.

There are several ways you can password protect it. For example, you could simply create an input form where a password is entered, and only then display the information. Or add an authentication string to the URL, or add custom HTTP headers which the servlet reads.

This blog post covers the simple old school HTTP authentication, where the browser pops up a username/password dialog box to the user.

You can adopt the code below in your Tomcat servlet class:

public class MyClass extends HttpServlet {
 private HashMap < String, String > validUsers = new HashMap < String, String > ();

 public void init(final ServletConfig config) throws ServletException {
  validUsers.put("myuser:mypassword", "authorized");
 }

 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  String auth = request.getHeader("Authorization");
  if (!authenticate(auth)) {
   response.setHeader("WWW-Authenticate", "BASIC realm=\"My Website Auth\"");
   response.sendError(HttpServletResponse.SC_UNAUTHORIZED);

  } else {
   //User authenticated, allow them in and serve protected content here
  }
 }

 private boolean authenticate(String auth) {
  if (auth == null)
   return false;

  if (!auth.toUpperCase().startsWith("BASIC "))
   return false;

  String userpassEncoded = auth.substring(6);
  String userpassDecoded = new String(DatatypeConverter.parseBase64Binary(userpassEncoded));

  if ("authorized".equals(validUsers.get(userpassDecoded)))
   return true;
  else
   return false;
 }

}

As you can see, the servlet code first looks for an “Authorization” HTTP header, if not present it sends a standard response code, which causes the browser to prompt the user. The username/password are then transmitted to the servlet, which supports the standard BASIC authentication, with the username/password base64 encoded. The servlet then matches the username/password to a HashMap of configured users we’ve defined in the init() method.

Pretty simple!

Creating a servlet to get the active number of sessions in Tomcat over the web

Last two blog posts I explored how to get the number of active sessions either directly through Tomcat (https://adeelscorner.wordpress.com/2016/03/13/getting-the-number-of-active-sessions-in-tomcat/), or through the application (particularly in Vaadin, https://adeelscorner.wordpress.com/2016/03/20/getting-the-number-of-active-sessions-in-vaadin/).

This blog post will explore putting both together and getting the session data through a servlet mapped to a URL. This way an admin can get the stats for a particular server right through a browser.

Let’s start with defining a servlet in web.xml:

  <servlet>
    <servlet-name>SessionStats</servlet-name>
    <servlet-class>com.perthera.session.SessionStats</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>SessionStats</servlet-name>
    <url-pattern>/SessionStats</url-pattern>
  </servlet-mapping>

Now you have a servlet called SessionStats defined to the /SessionStats on your Tomcat server, which will map to the com.company.session.SessionStats class.

In the SessionStats we’ll query the Tomcat level sessions, and the sessions the application is tracking, and return them as easy to read HTML:

public class SessionStats extends HttpServlet {
 private String message;

 public void init(final ServletConfig config) throws ServletException {}

 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  getSessionStats();

  response.setContentType("text/html");
  PrintWriter out = response.getWriter();
  out.println(message);
 }

 private void getSessionStats() throws Exception {
  message = "";

  final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
  ObjectName objectName = new ObjectName("Catalina:type=Manager,context=/,host=localhost");

  int activeSessions = (Integer) mBeanServer.getAttribute(objectName, "activeSessions");

  message += "Active Sessions Reported By Tomcat: " + activeSessions + ".
";
  message += "
";
  message += "Active/Idle User Sessions Reported By Application: " + ApplicationLevelTracker.browserTabUniqueIDToIdleTimeHashMap.size() + ".
";
  message += "
";

  Set keySet = ApplicationLevelTracker.browserTabUniqueIDToIdleTimeHashMap.keySet();
  for (String key: keySet) {
   Pair < Boolean, Long > pair = ApplicationLevelTracker.browserTabUniqueIDToIdleTimeHashMap.get(key);
   String userId = key.split(":")[0];
   String activeOrIdle = (pair.getElement0() ? "idle" : "active");
   Date timeStamp = new Date(pair.getElement1());
   message += "User ID " + userId + " is logged in with a tab open that was last reported as " + activeOrIdle + " at " + timeStamp + "
";
  }
  message += "
";
 }
}

So as you can see, the Tomcat session stats are queried, and displayed. And then the ApplicationLevelTracker is used to loop through a HashMap of users logged in and their idle/active as-of timestamp is displayed. This information can be useful to a system administrator in many ways, such as when determining if a server is in use and should not be taken offline. And it can be accessed through a browser by visiting: http://hostname/SessionStats

Getting the number of active sessions in Tomcat

This is really useful when you’re trying to determine if your Java application running on a Tomcat server is currently serving any clients. For example, if your server is under use, you can avoid taking it offline for maintenance or upgrade. Or, you can use it to determine the load on a server.

Essentially Tomcat has some internal statistics that can be accessed using JMX (Java Management Extension). More information and details for that are available here: https://tomcat.apache.org/tomcat-8.0-doc/monitoring.html

The Tomcat JMX stats can be accessed in several ways. This article will cover how to access them from within the tomcat serer, which was our requirement. Since the application deployed to the tomcat server will be running locally on the JVM running tomcat, JMX can be accessed locally from the Java code, and no remote setup is required.

The code is as simple as this:

MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName objectName = new ObjectName("Catalina:type=Manager,context=/,host=localhost");
int activeSessions = (Integer) mBeanServer.getAttribute(objectName, "activeSessions");

And that’s it! You’ll get the number of active sessions tomcat is reporting. And since this is running within the application deployed to tomcat, no remote setup or custom JVM arguments for tomcat are required.

Getting rid of Jetty related exceptions when using Vaadin Push with Tomcat

At least in Vaadin 7.3.x and 7.4.x, running on Tomcat with Push enabled, you may see several exceptions in your console having to do with Jetty. Even though you’re not using Jetty, this is happening because Vaadin is getting tricked into assuming that Jetty is being used, since a Jetty library is found in the Java classpath.

Vaadin does realize after the exceptions that Jetty isn’t actually there and continues gracefully. But though these exceptions are harmless, they’re still unsettling to constantly in your tomcat logs. As I mentioned, this is happening because there is some Jetty library in your Java classpath. So thus the obvious solution get to rid of these exceptions is to remove all Jetty libraries from your Java classpath. Search for any .jar’s that have “jetty” in the name, and remove them.

If you’re using Ivy or Maven dependency management, the library may be downloaded as a sub-dependency from another dependency. You will need to go through all your dependencies to check which ones also reference Jetty. The culprits in our case were vaadin-client-compiler and HtmlUnit (both reference Jetty for Jetty related things, but since we’re not using Jetty anyway, getting rid of the Jetty library should cause no harm).

We use Ivy, and I found how to exclude certain sub-dependencies, which is simple. Use the  tag in your ivy.xml wherever jetty is a sub-dependency. A example follows:

<exclude org="org.eclipse.jetty" name="*" />

And viola! No more random Jetty based exceptions from Vaadin.