NullPointerException получает соединение при развертывании на общем сервере mochahost

Я только что переместил свое приложение Spring, которое отлично работает на локальном хосте, на общий сервер веб-хостинга на mochahost, и я вижу приведенное ниже исключение (усеченное) в основной причине :

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)

и ниже этого:

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)

АдДаоИмпл.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){
    }
}

Контекст.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>

веб.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>

Некоторые моменты:

  1. Я поместил свои банки (включая mysql-connector-java-5.1.10.jar) в /WEB-INF/lib. На локальном хосте mysqlconnector помещается в библиотеки tomcat. Но так как это общий сервер, у меня нет доступа к библиотекам tomcat. WEB-INF/lib может быть неподходящим местом для коннектора mysql.

  2. WEB-INF/lib может быть неподходящим местом для размещения библиотек. В другом моем приложении (не связанном с этим), работающем на веб-сервере Amazon, я поместил все библиотеки в папку tomcat libs.

  3. В context.xml я даже изменил URL-адрес на jdbc:mysql://domain.com:3306/adsense_adsense, т.е. заменил localhost на фактический домен, но это не помогло.

  4. В MySqlDBPooling.java, если ds имеет значение null, почему он не вызывал исключение NullPointerException ранее в

System.out.println((new StringBuilder("MySqlDBPooling имеет значение ")).append(ds.toString()).toString());

который выполняется во время запуска сервера (класс MySqlDBPooling добавлен в качестве слушателя в web.xml)

Спасибо за внимание.

Джеймс

РЕДАКТИРОВАТЬ:

Это сработало после того, как я обновил MySqlDBPooling.java, как было предложено (получить источник данных с помощью Spring) @JB Nizet, чтобы:

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){
    }
}

person James    schedule 10.11.2011    source источник
comment
Он ясно говорит, что ваш DataSource ds не создается, и вы вызываете getConnection() для нулевой ссылки.   -  person Sandeep Pathak    schedule 10.11.2011
comment
Я думаю, проблема здесь ds=(DataSource)envCtx.lookup(jdbc/NepalAdzDB);   -  person Sandeep Pathak    schedule 10.11.2011
comment
Да, экземпляр DataSource ds не создается, я понимаю это, потому что ds.getConnection генерирует исключение NullPointerException. Это очень просто. Но почему ds оказывается нулевым? Дело в том, что он отлично работает на локальном хосте. Тогда почему проблема на стороннем сервере? Я думаю, что есть что-то делать с JAR.   -  person James    schedule 10.11.2011


Ответы (3)


Если вы объявляете DataSource в context.xml, Tomcat пытается создать его экземпляр, используя собственный загрузчик классов, а не загрузчик классов веб-приложения. Таким образом, jar драйвера в WEB-INF/lib не находится в его пути к классам, и драйвер не может быть загружен.

Причина, по которой вы не видите NPE раньше, заключается в том, что поиск JNDI, вероятно, завершается с ошибкой NamingException. Вы должны увидеть трассировку стека этого исключения в журнале во время развертывания.

Поскольку вы используете Spring, вы можете создать DataSource из Spring (используя автономный пул соединений) вместо того, чтобы позволить Tomcat создать его. См. http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/htmlsingle/spring-framework-reference.html#jdbc-datasource

person JB Nizet    schedule 10.11.2011
comment
В таком случае, могу ли я указать путь к папке lib где-нибудь в конфигурационных файлах? - person James; 10.11.2011
comment
АФАИК, нет. Вы должны поместить банку в каталог lib tomcat. - person JB Nizet; 10.11.2011
comment
Но я на виртуальном хостинге и не имею доступа к каталогу tomcat. Какой еще вариант вы видите возможным? - person James; 10.11.2011
comment
Тот, который я написал в своем ответе: используйте DBCP или другую реализацию пула, поместите банку пула в WEB-INF/lib вместе с драйвером БД и используйте Spring для создания источника данных. - person JB Nizet; 10.11.2011
comment
+1, так как ответ аналогичен При использовании уровня Spring JDBC вы можете либо получить источник данных из JNDI, либо настроить свой собственный, используя реализацию, предоставленную в дистрибутиве Spring. Последний пригодится для модульного тестирования вне веб-контейнера. на static.springsource.org/spring/docs/1.2.x /reference/jdbc.html, который я бы не нашел без вашего ответа. Я надеюсь, что это сработает хорошо. Тестирование... - person James; 10.11.2011

Ваш dataSource равен null, возможно, класс был перезагружен загрузчиком классов

person jmj    schedule 10.11.2011

Это просто, вы делаете new MySqlDBPooling() на AdDaoImpl. Видя, что MySqlDBPooling уже настроен в web.xml, я предполагаю, что контекст именования не загружается загрузчиком классов или Context.xml никогда не читается. Отладка и проверка того, создается ли источник данных при вызове contextInititated, будет шагом.

person Buhake Sindi    schedule 10.11.2011
comment
Внимательно прочитайте вопрос: он уже сделал это, и хотя он использует новый MySqlDBPooling, поле ds является статическим полем. - person JB Nizet; 10.11.2011
comment
Это там. Класс MySQLDBPooling выполняется при запуске сервера и заполняется объект DataSource. Позже, во время автоподключения для подключения, снова вызывается другой метод MySQLDBPooling, getConnection(), чтобы получить подключение от объекта источника данных. На локалхосте все работает. Проблема возникает только на стороннем хостинг-сервере (mochahost). - person James; 10.11.2011