ServletContextListener在重启时丢失JNDI连接

时间:2014-03-26 17:19:53

标签: java tomcat servlets servletcontextlistener

我正在尝试为Tomcat 7,MySQL应用程序(和Eclipse)构建一个简单的邮件程序守护程序。这是我第一次尝试使用ServletContextListener。

一切都很完美。除非,如果我更改我的邮件代码,Tomcat重新加载该类。然后它会在JNDI异常中轰炸,无法找到数据库。我不习惯按原样使用它。我不希望类重新加载来杀死服务器上的任务。

重启后和重新加载之前一切正常。所以我必须错过某些东西或以错误的顺序做事。

数据库连接在DAO中完成。所以重新启动后,DAO必须被切断?

任何帮助都会非常感激......

我得到的错误是:

Name [comp/env/jdbc/somedb] is not bound in this Context. Unable to find [comp].
javax.naming.NameNotFoundException: Name [comp/env/jdbc/somedb] is not bound in this Context. Unable to find [comp].
PooledConnection has already been closed.
    at org.apache.naming.NamingContext.lookup(NamingContext.java:819)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:167)
    at org.apache.naming.SelectorContext.lookup(SelectorContext.java:156)
    at javax.naming.InitialContext.lookup(Unknown Source)
    at util.DbUtil.getConnection(DbUtil.java:23)
    at dao.NoticeDao.getNoticesByEvent(NoticeDao.java:49)
    at dao.NoticeDao.getNoticesByStatus(NoticeDao.java:46)
    at util.AppMailer.sendMailQueue(AppMailer.java:88)
    at util.AppMailer.run(AppMailer.java:71)
    at java.lang.Thread.run(Unknown Source)
java.sql.SQLException: PooledConnection has already been closed.
    at org.apache.tomcat.jdbc.pool.DisposableConnectionFacade.invoke(DisposableConnectionFacade.java:86)
    at com.sun.proxy.$Proxy7.prepareStatement(Unknown Source)
    at dao.NoticeDao.getNoticesByEvent(NoticeDao.java:60)
    at dao.NoticeDao.getNoticesByStatus(NoticeDao.java:46)
    at util.AppMailer.sendMailQueue(AppMailer.java:88)
    at util.AppMailer.run(AppMailer.java:71)
    at java.lang.Thread.run(Unknown Source)

更新:对于第二次尝试,我简化并将守护进程与应用程序逻辑分开。应用程序逻辑现在完全独立。但我有同样的问题。

public class AppMailerRunner implements ServletContextListener {
    private ServletContext context = null;
    private Thread mailerThread;

    public AppMailerRunner() {}

    @Override
    public void contextInitialized(ServletContextEvent event) {
        this.context = event.getServletContext();
        System.out.printf("Starting: %s\n",this.getClass());

        mailerThread = new Thread(new MailerDaemon()); 
        mailerThread.setDaemon(true);
        mailerThread.start();

    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        System.out.printf("Stopping: %s\n",this.getClass());
        mailerThread.interrupt();
        this.context = null;
    }

    class MailerDaemon implements Runnable {
        @Override
        public void run() {
            AppMailer appMailer = new AppMailer();
            while(!Thread.currentThread().isInterrupted()){
                try {               
                    appMailer.sendMailQueue();
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

}

...

<listener>
    <listener-class>util.AppMailerRunner</listener-class>
</listener>

...

public class AppMailer{
    private NoticeDao noticeDao;
    private Session mailSession;
    private Boolean sending;
    ...

    public AppMailer() {
        super();

        noticeDao = new NoticeDao();

        sending = false;
    }

 do stuff...

...

public class NoticeDao {

    public NoticeDao() {
    }

    ...

    public List<Notice> getNotices() {
        Connection conn = DbUtil.getConnection();
        List<Notice> notices = new ArrayList<Notice>();
        try {

            PreparedStatement ps = conn.prepareStatement("SELECT * FROM notices");
            ResultSet rs = ps.executeQuery(); 
            while (rs.next()) {
                Notice notice = mapFields(rs);
                notices.add(notice);
            }

        } catch (SQLException e) {
            System.out.println(e.getMessage());
            e.printStackTrace();

        } finally {
            DbUtil.close(conn);
        }       
        return notices;
    }

    private static Notice mapFields(ResultSet rs) throws SQLException {
        Notice notice = new Notice();               
        notice.setId(       rs.getLong("id"));               
        notice.setItemid(   rs.getLong("itemid"));
        notice.setItemtype( rs.getString("itemtype"));
        notice.setTestmode( rs.getBoolean("testmode"));
        notice.setName(     rs.getString("name"));
        notice.setStatus(   rs.getString("status"));
        notice.setError(    rs.getString("error"));
        notice.setCreated(  rs.getDate("created"));
        notice.setModified( rs.getDate("modified"));
        notice.setLog(      rs.getString("log"));
        return notice;
    }

    ...

}

...

public class DbUtil {

    private static Connection conn = null;

    public DbUtil() {
    }

    public static Connection getConnection() {

        InitialContext ctx;
        try {
            ctx = new InitialContext();
            DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/somedb");
            conn = ds.getConnection();

        } catch (NamingException e) {
            System.out.println(e.getMessage());
            e.printStackTrace();

        } catch (SQLException e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
        return conn;

    }

    public static void close(Connection conn){
        if(conn!=null)
            try {
                conn.close();
            } catch (SQLException e) {
                System.out.println(e.getMessage());
                e.printStackTrace();
            }

    }

}

1 个答案:

答案 0 :(得分:1)

尝试在MailerDaemon类的catch块中添加break语句。

    Thread.sleep(10000);
} catch (InterruptedException e) {
    e.printStackTrace();
    break;
}

请注意,抛出InterruptedException时会清除中断状态。所以在contextInitialized中创建的线程永远不会脱离循环。

请参阅javadoc here

希望这有帮助。