I just moved my Spring application which is running fine on localhost to a shared web hosting server on mochahost, and I’m seeing the below exception (truncated) in the root cause:
root cause
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'adDaoImpl': Injection of autowired dependencies failed; nested exception is java.lang.NullPointerException
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:288)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1074)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
(truncated)
and below that:
root cause
java.lang.NullPointerException
com.adsense.connection.MySqlDBPooling.getConnection(MySqlDBPooling.java:28)
com.adsense.dao.impl.AdDaoImpl.setDataSource(AdDaoImpl.java:21)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:597)
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:588)
(truncated)
AdDaoImpl.java
@Repository
public class AdDaoImpl implements AdDao{
Connection conn;
@Autowired
public void setDataSource(){
try{
conn = (new MySqlDBPooling()).getConnection();
}catch(SQLException e){
e.printStackTrace();
}
}
...
...
}
MySqlDBPooling.java
package com.adsense.connection;
import java.sql.Connection;
import java.sql.SQLException;
import javax.naming.*;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.sql.DataSource;
public class MySqlDBPooling implements ServletContextListener{
private static DataSource ds;
public MySqlDBPooling(){
}
public void contextInitialized(ServletContextEvent sce){
try{
Context envCtx = (Context)(new InitialContext()).lookup("java:comp/env");
ds = (DataSource)envCtx.lookup("jdbc/AdSenseDB");
System.out.println((new StringBuilder("MySqlDBPooling is set to ")).append(ds.toString()).toString());
}catch(NamingException e){
e.printStackTrace();
}
}
public Connection getConnection() throws SQLException{
return ds.getConnection(); // getting NullPointerException here
}
public void contextDestroyed(ServletContextEvent servletcontextevent){
}
}
Context.xml
<?xml version="1.0" encoding="UTF-8"?>
<Context antiResourceLocking="false" privileged="true" version="5.0">
<Resource
name="jdbc/AdSenseDB"
auth="Container"
type="javax.sql.DataSource"
removeAbandoned="true"
removeAbandonedTimeout="30"
maxActive="300"
maxIdle="300"
maxWait="1000"
username="<username here>"
password="<password here>"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/adsense_adsense"/>
</Context>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>Adsense</display-name>
<listener>
<listener-class>com.adsense.connection.MySqlDBPooling</listener-class>
</listener>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
<url-pattern>*.js</url-pattern>
<url-pattern>*.jpg</url-pattern>
<url-pattern>*.jpeg</url-pattern>
<url-pattern>*.png</url-pattern>
<url-pattern>*.gif</url-pattern>
<url-pattern>*.ico</url-pattern>
</servlet-mapping>
</web-app>
Some points:
-
I have placed my jars (including mysql-connector-java-5.1.10.jar) at /WEB-INF/lib. On localhost, the mysqlconnector is placed in tomcat libs. But since this is a shared server, i have no access to tomcat libs. WEB-INF/lib might be a wrong place for mysql connector.
-
WEB-INF/lib might be a wrong place for placing libs. In my other app (not related to this) running on Amazon web server, I have placed all libraries in tomcat libs folder.
-
In context.xml, i even changed url to jdbc:mysql://domain.com:3306/adsense_adsense i.e. replaced localhost to actual domain, but that didn’t help.
-
In MySqlDBPooling.java, if ds is null, why did it not throw NullPointerException earlier at
System.out.println((new StringBuilder(“MySqlDBPooling is set to
“)).append(ds.toString()).toString());
which is executed during server start (MySqlDBPooling class is added as a listener in web.xml)
Thanks for your attention.
James
EDIT:
It worked after I updated MySqlDBPooling.java as suggested (get datasource using Spring) by @JB Nizet to:
package com.adsense.connection;
import java.sql.Connection;
import java.sql.SQLException;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
public class MySqlDBPooling implements ServletContextListener{
private static DriverManagerDataSource ds;
public MySqlDBPooling(){
}
public void contextInitialized(ServletContextEvent sce){
try{
ds = new DriverManagerDataSource();
ds.setDriverClassName( "com.mysql.jdbc.Driver");
ds.setUrl( "jdbc:mysql://localhost:3306/adsense_adsense");
ds.setUsername( "USERNAME");
ds.setPassword( "PASSWORD");
System.out.println("Datasource is: "+ds.toString());
}catch(Exception e){
e.printStackTrace();
}
}
public Connection getConnection() throws SQLException{
return ds.getConnection();
}
public void contextDestroyed(ServletContextEvent servletcontextevent){
}
}
If you declare a DataSource in the context.xml, Tomcat tries to instantiate it using its own classloader, and not the classloader of the webapp. So the driver jar in WEB-INF/lib is not in its classpath and the driver can’t be loaded.
The reason you don’t see an NPE sooner is that the JNDI lookup probably fails with a NamingException. You should see the stack trace of this exception in the log, at deployment time.
Since you’re using Spring, you could create the DataSource from Spring (using a stand-alone connection pool) instead of letting Tomcat create it. See http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/htmlsingle/spring-framework-reference.html#jdbc-datasource