문제

I'm trying to build a simple mailer daemon for a Tomcat 7, MySQL app (and Eclipse). This the first time I have tried to use a ServletContextListener.

Everything works perfectly. Except, if I change my mailer code, and Tomcat reloads the class. It then bombs with a JNDI exception where it can't find the database. I'm not comfortable using it as-is. I don't want a class reload to kill the task on the server.

Everything works fine after a restart and before the reload. So I must be missing something or doing things in the wrong order.

The database connection is done in the DAO. So after restarting, the DAO must be getting severed?

Any help would be most appreciated...

The error I am getting is:

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)

Update: For a second attempt, I simplified and separated the daemon stuff from the application logic. The app logic is now fully standalone. But I have the same problem.

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();
            }

    }

}
도움이 되었습니까?

해결책

Try adding break statement in the catch block in your MailerDaemon class.

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

Note that interruption status is cleared when InterruptedException is thrown. So the thread created in the contextInitialized will never break out of the loop.

See the javadoc here.

Hope this helps.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top