2014-03-26 75 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