sysopmin
114
2015-05-24 05:11:41
3
5842

쿼츠 Quartz 서블릿 ibatis 연동에서 에러...


현재, Tomcat 6.0.x  Spring 2.5 Struts 2.x ibats mysql 연동된 애에서 테스트 중입니다.

Mysql 에 주기적으로 insert update 등등의 쿼리를 was 단에서 스케쥴러 실행되게 하려는데..

정말 안되네요.. 5일 동안 헤매고 있네요. 
쿼츠 설정 선배님들의 조언 바랍니다.


1. WEB-INF/lib 밑에 quartz-1.8.6.jar, slf4j-api-1.6.0.jar, slf4j-log4j12-1.6.0.jar, 

   log4j-1.2.14.jar 올린다. 


2. 톰캣 정지


3. WEB-INF/web.xml에 내용추가 - WAS 시작시 특정 서블릿 호출

==========================================================

...

    <servlet>

        <servlet-name>QuartzCronTrigger</servlet-name>

        <servlet-class>com.hostel.servlet.QuartzCronTrigger</servlet-class>

        <init-param>

            <param-name>shutdown-on-unload</param-name>

            <param-value>true</param-value>

        </init-param>

        <load-on-startup>2</load-on-startup>

    </servlet>

...

==========================================================


4. servlet 으로 스케쥴 작성 (QuartzCronTrigger.java)  

========================================================================

package com.hostel.servlet;


import java.text.ParseException;

import javax.servlet.ServletConfig;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;


import org.quartz.CronTrigger;

import org.quartz.JobDetail;

import org.quartz.Scheduler;

import org.quartz.SchedulerException;

import org.quartz.SchedulerFactory;

import org.quartz.impl.StdSchedulerFactory;


public class QuartzCronTrigger extends HttpServlet {

    private static final long serialVersionUID = 1L;


    private SchedulerFactory schedFact;

    private Scheduler sched;


    @Override

    public void init(ServletConfig cfg) throws ServletException {

        super.init(cfg);

            

        try {

            // 스케쥴 생성후 시작

            schedFact = new StdSchedulerFactory();

            sched = schedFact.getScheduler();

            sched.start();

                   

            // Job1 생성 (Parameter : 1.Job Name, 2.Job Group Name, 3.Job Class)

            JobDetail job1 = new JobDetail("job1", "group1", QuartzJob.class);

                   

            // Trigger1 생성 (Parameter : 1.Trigger Name, 2.Trigger Group Name, 3.Cron Expression)

            CronTrigger trigger1 = new CronTrigger("trigger1", "group1", "0 * * * * ?");

                   

            // 아래는 trigger 가 misfire 되었을 경우에 대한 처리를 설정한다. 주석 풀고 해도 잘됨.

            // trigger1.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);


            // 실행

            sched.scheduleJob(job1, trigger1);

        } catch(SchedulerException e) {

            e.printStackTrace();

        } catch (ParseException e) {

            e.printStackTrace();

        }

    }

};

=========================================================================


5. Mysql 연동 관련 파일들

(1) QuartzJob.java  (주기적으로 실행될 클래스)

======================================================================

package com.hostel.servlet;


import java.util.Date;

import org.quartz.Job;

import org.quartz.JobExecutionContext;

import org.quartz.JobExecutionException;

import com.hostel.servlet.MysqlDao;

 

public class QuartzJob implements Job {

    private MysqlDao dao;

    

    public void setDao(MysqlDao dao) {

        this.dao = dao;

    }


    @Override

    public void execute(JobExecutionContext arg0) throws JobExecutionException {

        System.out.println("쿼츠 잡스케줄 실행합니다." + new Date());

        dao.insertOrderDel();

        System.out.println("인서트를 잘 한듯..." + new Date());

    }

};

======================================================================


(2) MysqlDao.java

============================================================

package com.hostel.servlet;


import org.springframework.dao.DataAccessException;


public interface MysqlDao {

    public void insertOrderDel() throws DataAccessException;

};

============================================================


(3) MysqlDaoImpl.java

==============================================================================

package com.hostel.servlet;


import javax.annotation.Resource;


import org.springframework.dao.DataAccessException;

import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;


import com.ibatis.sqlmap.client.SqlMapClient;


public class MysqlDaoImpl extends SqlMapClientDaoSupport implements MysqlDao {

    @Resource(name="sqlMapClient")

    public void initDAO(SqlMapClient sqlMapClient) {

        super.setSqlMapClient(sqlMapClient);

    }

    @Override

    public void insertOrderDel() throws DataAccessException {

        getSqlMapClientTemplate().insert("QUARTZ.insertOrderDel");

    }

};

==============================================================================


(4) QUARTZ.xml

===========================================================================

<?xml version="1.0" encoding="UTF-8" ?>


<!DOCTYPE sqlMap

  PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"

  "http://ibatis.apache.org/dtd/sql-map-2.dtd">


<sqlMap namespace="QUARTZ">    

    <insert id="insertOrderDel">

      INSERT INTO tb_order_del (

          nos, biztel, rooms, passport, nationality

        , begindate, enddate, curcode, totalprice, finalprice

        , reservprice, reservroute, reservdate, payfinish, payfinishdate

        , memos, verdate, modids, moddate, regids

        , regdate, delids, delname, deldate)

      SELECT 

             b.nos, b.biztel, b.rooms, b.passport, b.nationality

           , b.begindate, b.enddate, b.curcode, b.totalprice, b.finalprice

           , b.reservprice, b.reservroute, b.reservdate, b.payfinish, b.payfinishdate

           , b.memos, b.verdate, b.modids, b.moddate, b.regids

           , b.regdate, 'supermin', 'Manager', SYSDATE()

        FROM (

          SELECT nos, biztel, rooms, passport, nationality

               , begindate, enddate, curcode, totalprice, finalprice

               , reservprice, reservroute, reservdate, payfinish, payfinishdate

               , memos, verdate, modids, moddate, regids

               , regdate

            FROM tb_order

        ) b, (

          SELECT biztel

            FROM (

              SELECT biztel, enddate 

                FROM tb_member

               GROUP BY biztel, enddate

            ) a

           WHERE a.enddate <![CDATA[ < ]]> DATE_FORMAT(LAST_DAY(SYSDATE()),'%Y%m%d')

        ) c

       WHERE b.biztel = c.biztel

     </insert>

</sqlMap>

=============================================================================


(5) iBatis 설정 파일 - 커넥션, 트랜잭션.. (sqlmap-config.xml)

=============================================

...

..

<sqlMapConfig>

    ...

    <sqlMap resource="sqlmap/QUARTZ.xml" />

</sqlMapConfig>

=============================================


6. 톰캣 실행

1분마다 쿼츠 실행은 되는데 아래 에러 메세지 뜨며 쿼리 실행은 아예 안됩니다.

{DEBUG}[2015-05-24 05:05:00,046][org.quartz.simpl.SimpleJobFactory]:Producing instance of Job 'group1.job1', class=com.hostel.servlet.QuartzJob

{DEBUG}[2015-05-24 05:05:00,093][org.quartz.core.JobRunShell]:Calling execute on job group1.job1

쿼츠 잡스케줄 실행합니다.Sun May 24 05:05:00 KST 2015

{ERROR}[2015-05-24 05:05:00,234][org.quartz.core.JobRunShell]:Job group1.job1 threw an unhandled Exception: 

java.lang.NullPointerException

at com.hostel.servlet.QuartzJob.execute(QuartzJob.java:19)

at org.quartz.core.JobRunShell.run(JobRunShell.java:223)

at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:549)

{ERROR}[2015-05-24 05:05:00,250][org.quartz.core.ErrorLogger]:Job (group1.job1 threw an exception.

org.quartz.SchedulerException: Job threw an unhandled exception. [See nested exception: java.lang.NullPointerException]

at org.quartz.core.JobRunShell.run(JobRunShell.java:234)

at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:549)

Caused by: java.lang.NullPointerException

at com.hostel.servlet.QuartzJob.execute(QuartzJob.java:19)

at org.quartz.core.JobRunShell.run(JobRunShell.java:223)

... 1 more


왜 ? dao.insertOrderDel();  얘가 안 먹히는지 감이 정말 안오네요.




#########################################################
디비 연동 없이 그냥 테스크로 글씨 뿌리는 아래는 잘 됩니다.

이클립스에서 테스트 함.


1. WEB-INF/lib 밑에 quartz-1.8.6.jar, slf4j-api-1.6.0.jar, slf4j-log4j12-1.6.0.jar, 

   log4j-1.2.14.jar 올린다. 


2. 톰캣 정지


3. WEB-INF/web.xml에 내용추가 - WAS 시작시 특정 서블릿 호출

==========================================================

...

    <servlet>

        <servlet-name>QuartzCronTrigger</servlet-name>

        <servlet-class>com.hostel.servlet.QuartzCronTrigger</servlet-class>

        <init-param>

            <param-name>shutdown-on-unload</param-name>

            <param-value>true</param-value>

        </init-param>

        <load-on-startup>2</load-on-startup>

    </servlet>

...

==========================================================


4. servlet 으로 스케쥴 작성 (QuartzCronTrigger.java)

=========================================================================

package com.hostel.servlet;


import java.text.ParseException;

import javax.servlet.ServletConfig;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;


import org.quartz.CronTrigger;

import org.quartz.JobDetail;

import org.quartz.Scheduler;

import org.quartz.SchedulerException;

import org.quartz.SchedulerFactory;

import org.quartz.impl.StdSchedulerFactory;


public class QuartzCronTrigger extends HttpServlet {

    private static final long serialVersionUID = 1L;


    private SchedulerFactory schedFact;

    private Scheduler sched;


    @Override

    public void init(ServletConfig cfg) throws ServletException {

        super.init(cfg);

            

        try {

            // 스케쥴 생성후 시작

            schedFact = new StdSchedulerFactory();

            sched = schedFact.getScheduler();

            sched.start();

                   

            // Job1 생성 (Parameter : 1.Job Name, 2.Job Group Name, 3.Job Class)

            JobDetail job1 = new JobDetail("job1", "group1", QuartzJob.class);

                   

            // Trigger1 생성 (Parameter : 1.Trigger Name, 2.Trigger Group Name, 3.Cron Expression)

            CronTrigger trigger1 = new CronTrigger("trigger1", "group1", "0 * * * * ?");

                   

            // 아래는 trigger 가 misfire 되었을 경우에 대한 처리를 설정한다. 주석 풀고 해도 잘됨.

            // trigger1.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);


            // 실행

            sched.scheduleJob(job1, trigger1);

        } catch(SchedulerException e) {

            e.printStackTrace();

        } catch (ParseException e) {

            e.printStackTrace();

        }

    }

};

=========================================================================


5. QuartzJob.java 

===============================================================================

package com.hostel.servlet;

 

import org.quartz.Job;

import org.quartz.JobExecutionContext;

import org.quartz.JobExecutionException;

 

public class QuartzJob implements Job {

    @Override

    public void execute(JobExecutionContext arg0) throws JobExecutionException {

        System.out.println("쿼츠 잡스케줄 실행합니다.");

    }

};

============================================================================== 


6. 톰캣 실행

Console 창에 에러 메시지 뜨는 지도 점검하고 매 분 0초에 

"쿼츠 잡스케줄 실행합니다" 라는 메시지가 뜨는 지 점검 한다.

매우 잘된다.



0
0
  • 답변 3

  • sysopmin
    114
    2015-05-24 05:47:12
    참고로.. 웹호스팅으로 의뢰해서 뭔 서비스 하려니까 그쪽 호스팅사에서 MySQL 프로시저 이벤트 생성권한 안주고 또한 리눅스 일반 시스템 계정에는 크론 생성 권한 안준다고 해서 부득히... 쿼츠 설정을 하게 되었습니다.
    0
  • 제타건담
    5k
    2015-05-24 15:26:30

    일단 제가 말씀드리는거를 보고 판단을 해보세요..

    Spring을 사용할땐 아주 중요한 전제가 하나 있습니다..

    Spring에서 Bean을 Injection을 받을려면 Injection을 받고자 하는 객체도 Spring에 등록되어 있어야 한다..는 거죠..


    Spring을 사용중이신데..

    일단 사용중인 Spring 설정이 올라와 있는게 없어서 알기는 어렵습니다만..

    일단 에러문구만 보면 이런 상황이지 않을까 싶어서 이렇게 답변을 드린거에요..

    에러 문구를 보면  

    com.hostel.servlet.QuartzJob.execute(QuartzJob.java:19)

    이 문구가 있죠..

    말씀하신 내용으로 보면 dao.insertOrderDel(); 이 부분이 19번째 라인 같습니다..

    여기서 Null Pointer Exception이 던져질 상황은 멤버변수 dao가 null이기 때문에 이 예외가 던져지는 걸꺼에요..

    그러면 생각해볼수 있는건 이게 Injection을 받게끔 셋팅이 되어 있느냐..겠죠..

    이 부분은 Spring 설정 파일을 보지 않고서는 머라 말씀드릴수 없어서 알수가 없고..

    그리고 또 하나는..


    JobDetail job1 = new JobDetail("job1", "group1", QuartzJob.class);


    서블릿의 이 부분..

    Struts와 연동을 하는 거 같은데..제가 일단 Spring과 Struts 연동은 알지 못한다는 전제하에서 말씀드리면..

    만약 Spring과 Struts을 연동해서 서블릿을 연동했을때도 저런식으로 Spring의 Bean을 바로 사용할 수 있다면 모르겠지만..

    그렇지 않은 경우라면..QuartzJob.class는 클래스 타입을 가지고 자신이 내부에서 new를 이용해서 생성하는 구조로 갈껍니다..

    그러나 그렇게 가면 안되요..

    왜냐면 Servlet은 Spring의 bean으로 등록된게 아닙니다..

    그래서 Spring Context를 가져온뒤 QuartJob 클래스로 등록된 Bean 객체를 Injection 받은뒤에

    JobDetail 객체에 넣어주는 작업으로 진행하셔야 할꺼에요..


    http://www.mkyong.com/spring/spring-how-to-do-dependency-injection-in-your-session-listener/


    이 링크는 글쓴분과 완전히 똑같은 상황은 아니지만..

    Spring에 등록되지 않는 클래스에서 Spring Bean을 가져오는 방법으로는 설명이 될 수 있어서 링크했습니다..

    이 링크에서 printCounter란 메소드를 보시면 WebApplicationContextUtils란 클래스를 이용해서 Spring Bean을 가져오고 있습니다..

    암튼..일단 DAO 껀이 해결된 뒤에 서블릿에서도 별 문제 없으면 다행이지만..

    서블릿에서도 문제가 있을 경우엔 지금 설명한 내용도 참고하셨음 하네요..

    0
  • sysopmin
    114
    2015-05-24 15:51:19

    답변 주셔서 감사드립니다.
    빈 등록이 서블릿 단에서만 되는 거고 스프링에 안먹히니... 그런 듯합니다.

    개인적으로 만약 스프링 안 사용했을때... 서블릿 + 자바 로 톰캣의 mysql jar 파일 콜해서 하는 방법

    좀 해볼려고 했었는데... 넘 많이 고생해서 포기합니다.

    그래서, 그냥 일반적으로 웹에 나와있는 스프링 빈 만들어서 거기서 처리했습니다.

    감사합니다.

    0
  • 로그인을 하시면 답변을 등록할 수 있습니다.