0

I am getting a seemingly inexplicable nullPointerException from a .jar file that is listed in the classpath as an export dependency.

The jar file consists of a database bean that works flawlessly when it is called inside of a normal main method POJO, but when I called the same jar file in a servlet, I get null pointers from the con.prepareStatement of whatever I call.

I thought that it might be a SQLException from the pool causing it, so I moved it to our VM server where it can talk directly to the database instance across its cloud environment, and I get the same error.

The bean consists of four additional dependent jar files: guava-16.0.1.jar(for weak referenced hashmaps), postgresql-9.3-1100.jdbc41.jar, tomcat-dbcp.jar, tomcat-jdbc.jar that are all wrapped up into a single jar file using FatJar for Eclipse, which is then imported into my servlet which is hosted in a Tomcat8 servlet container (also tested in Tomcat7 and TomEE).

The servlet code is as follows:

 import java.io.IOException;
 import java.io.PrintWriter;

 import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
 import javax.servlet.annotation.WebServlet;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;

 import com.package.DBEngine;

@WebServlet("/PrimaryEngine")
public class PrimaryEngine extends HttpServlet {
  private static final long serialVersionUID = 1L;
  private DBEngine db;

  public void init(ServletConfig config) throws ServletException{
     super.init(config);
     db = new DBEngine();
  }

  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     response.setContentType("text/html;charset=UTF-8");
     PrintWriter out = response.getWriter();
     if(request.getParameterMap().containsKey("op")){
        out.println("Getting data...");
        String test = db.DBdebug();
        out.println(test);
        return;
     }

  }

  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     doGet(request, response);
  }

}

The DBEngine looks like this:

 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.Map;
 import java.util.concurrent.ConcurrentMap;
 import java.util.logging.Level;

 import com.google.common.collect.MapMaker;
 import com.sun.istack.internal.logging.Logger;

 public class DBEngine {

      private Pool pool = null;
      private Connection con = null;
      private PreparedStatement ps = null;

      //-------------------------------------------
      public DBEngine(){
           this.pool = new Pool();
           this.con = pool.getConnection();
           this.ps = null;

      }

      //---------------------------------------------------
      // Begin subsidiary functions/methods
      //---------------------------------------------------
      public String DBdebug(){

           try {
                String ps = "SELECT table_name FROM information_schema.tables";
                PreparedStatement ps2 = con.prepareStatement(ps);
                ResultSet rs = ps2.executeQuery();

                if(rs.next()){
                     return "Success";
                } else return "Fail";

           } catch (SQLException e) {
                e.printStackTrace();
           }

           return null;
      }

 }

The tomcat-dbcp pool looks like this:

 import java.sql.Connection;
 import java.sql.SQLException;

 import org.apache.tomcat.jdbc.pool.DataSource;
 import org.apache.tomcat.jdbc.pool.PoolProperties;

 public class Pool {

 private static DataSource datasource = null;

 //--------------------------------------------------
 // Sets up the database connection pool
 //--------------------------------------------------
 public Pool() { 

      PoolProperties p = new PoolProperties();
    p.setUrl("jdbc:postgresql://*URL*:5432/*databasename*");
    p.setDriverClassName("org.postgresql.Driver");
    p.setUsername("*someUserName*");
    p.setPassword("*somePassword*");
    p.setJmxEnabled(true);
    p.setTestWhileIdle(false);
    p.setTestOnBorrow(true);
    p.setValidationQuery("SELECT 1");
    p.setTestOnReturn(false);
    p.setValidationInterval(30000);
    p.setTimeBetweenEvictionRunsMillis(30000);
    p.setMaxActive(75);
    p.setMaxIdle(75);
    p.setInitialSize(10);
    p.setMaxWait(10000);
    p.setRemoveAbandonedTimeout(60);
    p.setMinEvictableIdleTimeMillis(30000);
    p.setMinIdle(10);
    p.setLogAbandoned(true);
    p.setRemoveAbandoned(true);
    p.setRemoveAbandonedTimeout(60);
    p.setAbandonWhenPercentageFull(60);
    p.setJdbcInterceptors(
      "org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"+
      "org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer;"+
       "org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer");
    datasource = new DataSource();
    datasource.setPoolProperties(p);

 }

 //-------------------------------------------------------
 // Closes an open datasource
 //-------------------------------------------------------
 public synchronized void closeDatasource() {
      try {
           datasource.close();
      } catch (Exception e) {
           e.printStackTrace();
      }
 }

 //--------------------------------------------------------
 // Get a connection
 //--------------------------------------------------------
 public Connection getConnection() {
      try {
           return datasource.getConnection();
      } catch (SQLException e) {
           e.printStackTrace();
           return null;
      }
 }

And the stack trace:

[ajp-nio-8009-exec-3] org.apache.catalina.core.StandardWrapperValve.invoke Allocate exception for servlet com.package.engine.main.PrimaryEngine
 java.lang.NullPointerException
 com.package.DBEngine.<init>(DBEngine.java:##)
 com.package.main.PrimaryEngine.init(PrimaryEngine.java:26)
 org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:503)
 org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:74)
 org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
 org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:509)
 org.apache.coyote.ajp.AbstractAjpProcessor.process(AbstractAjpProcessor.java:799)
 org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:651)
 org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1575)
 org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1533)
 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
 java.lang.Thread.run(Thread.java:722)

I am at wits end here. Everything that I know about Java says that it should work, but it does not.

Edit:

This is the only other stack trace that Tomcat outputs:

javax.servlet.ServletException: Servlet.init() for servlet com.package.main.PrimaryEngine threw exception org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:503) org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:74) org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610) org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:509) org.apache.coyote.ajp.AbstractAjpProcessor.process(AbstractAjpProcessor.java:799) org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:651) org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1575) org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1533) java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) java.lang.Thread.run(Thread.java:722)

adv
  • 357
  • 5
  • 18
  • 2
    It doesn't look like anything in `DBEngine` controller can throw a `NullPointerException`. Are you showing us everything? – Sotirios Delimanolis Feb 14 '14 at 01:54
  • AFAIK you should get a `DataSource` using JNDI but you're creating it manually. I guess that's your main problem. – Luiggi Mendoza Feb 14 '14 at 02:07
  • 1
    Your problem seems to be when you manually create the `DataSource`, which you **should not** do. Please refer to this Q/A to have more understanding about setting your database connection in tomcat: http://stackoverflow.com/q/15981244/1065197 – Luiggi Mendoza Feb 14 '14 at 02:32
  • If you get a `NullPointerException` on `con.prepareStatement` it means that `con` is null, which means that there was an error in `Pool.getConnection()`. Show us that stack trace. – David Levesque Feb 14 '14 at 02:34
  • @ Luiggi: Why does it work in my local test POJO? Is it just not using the webserver related pieces that make it crash and burn? – adv Feb 14 '14 at 03:01
  • And please don't cut the stack traces. The "caused by" sections are extremely useful. – David Levesque Feb 14 '14 at 03:03
  • @ Sotirios: The only other piece is my SQL statement library which is just a class with about 100 final static Strings containing SQL statements. – adv Feb 14 '14 at 03:09
  • @David: I added the only other trace that Tomcat outputs. I will ensure to include all of the traces in future questions. – adv Feb 14 '14 at 03:10
  • On what line exactly is the NullPointerException? – David Levesque Feb 14 '14 at 03:13
  • on the "PreparedStatement ps2 = con.prepareStatement(ps);" line, which obviously means the connection is null, but I cannot fathom why. I think Luiggi may be correct, but I have to do a bit of work to unwind my nicely encapsulated DB bean in order to test that. – adv Feb 14 '14 at 03:16
  • Really wondering if you're running the same code you think you are. What if you put an obvious fatal exception right at the start of the initalization code and deploy it. Do you see the exception? – Craig Ringer Feb 14 '14 at 13:44
  • @Craig: It fails on the line in the constructor on the exception that I added. – adv Feb 15 '14 at 01:12
  • @Luiggi: This was where I was pulling example code from for calling the datasource directly: http://www.postgresql.org/docs/7.4/static/jdbc-datasource.html . I did not do the JNDI at the time, since it seemed that a simpler way was available and the Tomcat page author had said that they did not have success with the JNDI method. – adv Feb 15 '14 at 16:32
  • Well, I have successfully used this JNDI approach in almost every web application project I've worked and having no problems related to connectivity... – Luiggi Mendoza Feb 15 '14 at 17:49
  • I like to hear that. That makes me more comfortable jumping into the JNDI deep-end for the first time. – adv Feb 15 '14 at 17:56
  • For the datasource, should I use "javax.sql.DataSource" or "org.apache.tomcat.jdbc.pool.DataSource" for the Resource-type in the context.xml and web.xml files? Or does it matter? – adv Feb 15 '14 at 19:50
  • Success. Many thanks to Luiggi for pointing out the JNDI issue. I am now running a Tomcat-wide connection pool for PostgresQL. Feel free to just put some generic JNDI answer, and I'll mark it for you. http://www.codejava.net/servers/tomcat/configuring-jndi-datasource-for-database-connection-pooling-in-tomcat – adv Feb 15 '14 at 20:50
  • The best part is that I was able to maintain the bean-encapsulation by just passing the servlet context into the bean and pulling the datasource from it inside of the bean. – adv Feb 15 '14 at 20:59

1 Answers1

1

Based on Luiggi's input regarding not manually creating the datasource in a class and recommendation to use JNDI database pooling, I was able to get the solution below to work. My adaptation is heavily based on this link: http://www.codejava.net/servers/tomcat/configuring-jndi-datasource-for-database-connection-pooling-in-tomcat

Pieces: Tomcat 8.0, postgresql-jdbc driver, DBEngine bean, servlet

The dbEngine bean consists of three classes: DBEngine.java, Pool.java, and SQLPS.java (a SQL statement library) that are all wrapped into one jar file and imported into the servlet and marked as a classpath export dependency.

The servlet calling the bean looks like:

 package com.engine.main;

 import java.io.IOException;
 import java.io.PrintWriter;
 import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.concurrent.ConcurrentMap;
 import javax.naming.InitialContext;
 import javax.naming.NamingException;
 import javax.servlet.AsyncContext;
 import javax.servlet.AsyncEvent;
 import javax.servlet.AsyncListener;
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
 import javax.servlet.annotation.WebServlet;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import sslog4java.LogLevel;
 import sslog4java.Logger;
 import com.google.gson.Gson;
 import com.DBbean.DBEngine;

 @WebServlet("/PrimaryEngine")
 public class PrimaryEngine extends HttpServlet {
      private static final long serialVersionUID = 1L;
      private DBEngine db = null;

      private static String filePath = "";
      private static String fileName = "";
      Logger fLogger = null;

      public void init(ServletConfig config) throws ServletException{
           super.init(config);
           ServletContext scxt = config.getServletContext();
           filePath = scxt.getRealPath("/WEB-INF") + "\\logs\\";
           fileName = "loggerFileName";
           fLogger = new Logger(filePath, fileName, LogLevel.DEBUG);
           try {
                // passed the servlet context into the DBengine for the pool to use
                db = new DBEngine(new InitialContext(), fLogger);
           } catch (SQLException | NamingException e) {
                e.printStackTrace();
           }
      }

      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
           PrintWriter out = response.getWriter();
           if(request.getParameterMap().containsKey("param") && request.getParameter("param").equals("paramValue")){
                test = db.DBdebug();
                out.println(test);
                return;
           }
      }
      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
           doGet(request, response);
      }
 }

The dbEngine.java looks like:

 package com.DBbean;

 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.Map;
 import java.util.concurrent.ConcurrentMap;
 import javax.naming.Context;
 import sslog4java.Logger;
 import com.google.common.collect.MapMaker;

 public class DBEngine {

      private Pool pool = null;
      private Connection con = null;
      private Logger fLogger;

      public DBEngine(Context initCtx, Logger logr) throws SQLException{
           this.fLogger = logr;
           // passed the servlet context into the Pool.java
           this.pool = new Pool(initCtx);
           this.con = pool.getConnection();
      }

      public String DBdebug(){

           Connection conn = pool.getConnection();

           try {
                String ps = SQLPS.debugSQL;
                PreparedStatement ps2 = conn.prepareStatement(ps);
                ResultSet rs = ps2.executeQuery();

                if(rs.next()){
                     return "Success";
                } else return "Fail";

           } catch (SQLException e) {
                e.printStackTrace();
           }

           return null;
      }
 }

and the Pool.java looks like:

 package com.DBbean;

 import java.sql.Connection;
 import java.sql.SQLException;
 import javax.naming.Context;
 import javax.naming.NamingException;
 import javax.sql.DataSource;

 public class Pool {

      Context ctx;

      public Pool(Context context){
           ctx = context;
      }


     public Connection getConnection() {

           try {
                DataSource ds = (DataSource) ctx.lookup("java:/comp/env/jdbc/postgres4" );
                return ds.getConnection();
           } catch (SQLException | NamingException sne) {
                sne.printStackTrace();
                return null;
           }
     }
 }

I had to add these lines to the $CATALINA_HOME/conf/context.xml:

<Resource
name="jdbc/postgres4"
auth="Container"
type="javax.sql.DataSource"
maxActive="8"
maxIdle="8"
driverClassName="org.postgresql.Driver"
url="*URL*"
username="*UserName*"
password="*Password*"
/>

and these lines to $CATALINA_HOME/conf/web.xml:

<resource-ref>
<description>postgreSQL Datasource</description>
<res-ref-name>jdbc/postgres4</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>

and the postgresql-jdbc jar went into $CATALINA_HOME/lib

adv
  • 357
  • 5
  • 18