Spring MVC Executor Service Anomaly Due to Holding HttpServletRequest for too Long

Encountered a strange problem with Spring MVC running on Tomcat 7 today. I had a ThreadPoolTaskExecutor with 1 max thread capacity on my application which wasn’t working as it supposed to. My Runnable task look something like this:

public class LongRunningTask implements Runnable {

  private HttpServletRequest request;
  
  public LongRunningTask(HttpServletRequest request) {
    this.request = request;
  }
  
  @Override
  public void run() {
    // do something ..
  }

}

Notice I originally added a reference to HttpServletRequest with the intention of obtaining the remote host and address of the client. My controller class look something like this:

@Controller
public class HomeController {

  @Autowired private ThreadPoolTaskExecutor taskExecutor;

  @RequestMapping(...)
  public String schedule(HttpServletRequest req, ...) {
    taskExecutor.submit(new LongRunningTask(req));
  }
}

With the hope that user’s get their response immediately while the actual work is still queued on the background.

Turns out this caused all sorts of strange behavior. Sometime the task will run, most of the time it will just sit in the queue forever / won’t even queue at all.

After further research and trial and error, I figured out this was because my runnable is holding a reference to HttpServletRequest. When I refactored the code the task executor work as per normal again.

I’ve came into a conclusion that holding to a HttpServletRequest object for longer than necessary is not a good practice.

Advertisements

Using Maven to Include All Java EE API Into Classpath

If you use full Java EE container such as JBoss or Glassfish — and you (are fortunate enough) to use Maven. Here’s a simple way to pull all Java EE dependencies into your classpath. Add following dependency into your pom.xml:

<dependency>
  <groupId>javax</groupId>
  <artifactId>javaee-api</artifactId>
  <version>6.0</version>
  <scope>provided</scope>
</dependency>

Notice the scope is set to provided, this is important because on runtime all those classes will be provided by your container.

Also the above is for Java EE 6. You can use following for Java EE 7:

<dependency>
  <groupId>javax</groupId>
  <artifactId>javaee-api</artifactId>
  <version>7.0</version>
  <scope>provided</scope>
</dependency>

Java EE Tutorial Sample Codes

If you ever tried to obtain Java EE tutorial sample codes according to the official getting started guide you must feel how frustrating it is.

Fortunately the tutorial sample code can be easily obtained from SVN:

Thanks to the guys on this Stack Overflow Q&A.

Java EE & Spring Cheatsheet

JSTL 1.2 Core JSP Taglib (aka c tag)

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

JSTL implementation jar has to exist on the classpath, eg add following dependency (in provided scope if container doesn’t provides it)

<dependency>
  <groupId>jstl</groupId>
  <artifactId>jstl</artifactId>
  <version>1.2</version>
</dependency>

See this blog post for more info.

Spring Taglib

  • Spring:
    <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
  • Form:
    <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

JSTL fmt Taglib

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

web.xml deployment descriptor

  • v2.3
    <!DOCTYPE web-app PUBLIC
     "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     "http://java.sun.com/dtd/web-app_2_3.dtd" >
    
    <web-app>
      
    </web-app>
    
  • v3.0
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="3.0"
             xmlns="http://java.sun.com/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
        
    </web-app>
    

Commons DBCP / MySQL Spring Bean Config

<!-- JDBC Data Source -->
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  <property name="url" value="jdbc:mysql://localhost:3306/hibernatefiddle"/>
  <property name="username" value="root"/>
  <property name="password" value=""/>
  <property name="validationQuery" value="SELECT 1"/>
  <property name="testWhileIdle" value="true"/>
</bean>

Hibernate / MySQL Session Factory & Transaction Manager Spring Bean Config

<!-- Hibernate Session Factory -->
<bean id="mySessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
  <property name="dataSource" ref="myDataSource"/>
  <property name="packagesToScan">
    <array>
      <value>com.gerrytan.hibernatefiddle</value>
    </array>
  </property>
  <property name="hibernateProperties">
    <value>
      hibernate.dialect=org.hibernate.dialect.MySQLDialect
    </value>
  </property>
</bean>

<!-- Hibernate Transaction Manager -->
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
  <property name="sessionFactory" ref="mySessionFactory"/>
</bean>

Log4j Properties File

# Root logger option
log4j.rootLogger=INFO, stdout

# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c:%L - %m%n

This will print log in following format:

2013-06-26 10:15:35 DEBUG com.gerrytan.hibernatefiddle.StudentDAO:21 - StudentDAO created

To create file logger, limited to 5 file with max size 1000KB:

# Root logger option
log4j.rootLogger=WARN, file
 
# Rolling file appender
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=MyLog.log
log4j.appender.file.MaxFileSize=1000KB
log4j.appender.file.MaxBackupIndex=5
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c:%L - %m%n

Hibernate Logging

log4j.logger.org.hibernate.SQL=DEBUG
log4j.logger.org.hibernate.engine.transaction=DEBUG

On Hibernate 3 or earlier transaction logger is:

log4j.logger.org.hibernate.transaction=DEBUG

Creating New Java EE 7 Maven Eclipse Project

Still on the Java EE 7 hype, here’s a quick cheat sheet on how to create a minimal Java EE 7 ready maven project:

  1. Ensure you have Eclipse Indigo installed with m2e (maven connector). If not you can drag and drop this logo into running Eclipse instance

    (thanks m2e team)
  2. Create new maven project, tick Create a simple project (skip archetype selection), on the next screen provide Group Id, Artifact Id, and set Packaging to war. Hit Finish
    new maven proj cut
  3. Open pom.xml, switch to source view. Add Java EE 7 dependency. Set the scope to provided so that it is included at compile time, but not on the war bundle (because it should be provided by container)
    <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>com.gerrytan</groupId>
      <artifactId>jee7fiddle</artifactId>
      <version>1.0</version>
      <packaging>war</packaging>
      
      <!-- Java EE 7 dependency -->
      <dependencies>
        <dependency>
          <groupId>javax</groupId>
          <artifactId>javaee-api</artifactId>
          <version>7.0</version>
          <scope>provided</scope>
        </dependency>
      </dependencies>
    
  4. Tell Maven to compile using JDK 7, otherwise the deafault is JDK 5
      <build>
        <plugins>
          <!-- Set to compile using JDK 7 -->
          <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
              <source>1.7</source>
              <target>1.7</target>
            </configuration>
          </plugin>
    
  5. And finally prevent maven-war-plugin from complaining because of missing web.xml. New Java EE 7 style provides good annotation support, web.xml can be omitted for a simple project
          <!-- Avoid war plugin complaining missing web.xml -->
          <plugin>
            <artifactId>maven-war-plugin</artifactId>
            <version>2.3</version>
            <configuration>
              <failOnMissingWebXml>false</failOnMissingWebXml>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </project>
    
  6. Right click on the project -> Maven -> Update Project… -> OK. This will cause m2e to synchronize with all changes we made
  7. Test your new project by running clean package
    maven run cut
    You should end up with a war bundle with your project name under target directory

Working With Eclipse and Glassfish

With the new launch of Java EE 7, I felt the quickest way to get a taste of it is to use Glassfish (Java EE reference implementation). Here’s how to get started with Glassfish and Eclipse:

Eclipse GlassFish Setup

  1. Download and install JDK 7 if you haven’t done so.
    jdk download

  2. Download and install Glassfish 4.0
    glassfish download
  3. Ensure you have Eclipse installed. First we’ll add Glassfish runtime environment. On Eclipse, go to Preferences -> Server -> Runtime Environments -> Add and select Glassfish -> Glassfish 4.0 (Select Download additional server adapter if you can’t find it).

    eclipse glassfish 01

    Hit Next, select Java 7 runtime and glassfish directory (On windows this is normally C:\glassfish4\glassfish)

    glassfish eclipse setup 1

  4. Next, create a new Glassfish server. Ctrl+N (or Cmd+N on Mac) and select Server -> Server. Select server type GlassFish -> GlassFish 4.0. Accept all defaults and hit Finish. You should now have a Glassfish server on your Servers tab on eclipse:

    eclipse glassfish 2

  5. To test your server, select the Glassfish 4.0 Server from Eclipse server tab and right click -> Start. Glassfish server will output few messages to the console, and you can verify by opening http://localhost:8080 on your browser, you will see something like this:

    glassfish server running

    Right click the server -> Stop to stop the server.

Create New Glassfish Application Project

Now we have eclipse wired to Glassfish, we can begin coding the application.

  1. Create a new dynamic web project: Ctrl+N (or Cmd+N on Mac) -> Select Web -> Dynamic web project. Configure it like following:
    Screen Shot 2013-06-29 at 2.46.42 PM

    Accept all default (or configure if you need to) and hit Finish