2014-03-26 114 views
0

我想爲Tomcat 7,MySQL應用程序(和Eclipse)構建一個簡單的郵件程序守護進程。這是我第一次嘗試使用ServletContextListener。ServletContextListener在重啓時丟失JNDI連接

一切正常。除此之外,如果我更改我的郵件程序代碼,並且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(); 
      } 

    } 

} 
+0

調查更多...我不知道重新啓動是否不關閉線程?所以連接重置,但舊線程仍在嘗試訪問它。那麼如何讓線程處於上下文監聽器的控制之下呢? – PrecisionPete

回答

1

嘗試在MailerDaemon類的catch塊中添加break語句。

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

請注意,在引發InterruptedException時會清除中斷狀態。所以在contextInitialized中創建的線程永遠不會跳出循環。

查看javadoc here

希望這會有所幫助。

+0

謝謝。仍然不確定JNDI引用如何丟失。但休息確實解決了這個問題,所以我想我沒有那麼擔心。 – PrecisionPete