Tomcat7 使用 Scheduler

Java EE Server 上寫一個排程工作,只要加一個 @Schedule 就可以了,Tomcat 除了用 Java SE 的 TimerTask 來排程外的另一個選擇就是 quartz,看了一下有支援 Thread Pool,設定檔不用寫在程式碼上,之後要寫 ant 來置換比較方便,就決定用 quartz。

使用版本

Tomcat7 + quartz 2.1.7

步驟

http://www.quartz-scheduler.org/documentation/quartz-2.1.x/quick-start 是一個官網的簡單使用說明,下面是我自己使用的筆記。

1) 安裝

官網下載,解壓縮後 lib 下的 jar 檔都加入專案的 build path。

2) 啟用 quartz

web.xml 加入 QuartzInitializerListener,QuartzInitializerListener 實作了 javax.servlet.ServletContextListener,QuartzInitializerListener 在 Web App 啟動後就會初始化 quartz 相關的設定。

WebContent/WEB-INF/web.xml


3) 設定 quartz

WebContent/WEB-INF/classes/quartz.properties

4) 設定排程工作

quartz_data.xml 內,
  • 建立一個 Job,取名並指定實作類別。
  • 建立一個 Trigger,取名,指定 Job 和執行時間。
關於 Trigger 的執行時間設定可參考:http://www.quartz-scheduler.org/documentation/quartz-2.x/tutorials/crontrigger

WebContent/WEB-INF/classes/quartz_data.xml

5) 實作排程工作

FooJob.java
public class FooJob implements Job
{
  @Override
  public void execute(final JobExecutionContext ctx) throws JobExecutionException 
  {
    // To something here!
    }
}

問題

1) 執行時發生 java.lang.NoClassDefFoundError: javax/transaction/UserTransaction

嚴重: Exception sending context initialized event to listener instance of class org.quartz.ee.servlet.QuartzInitializerListener

java.lang.NoClassDefFoundError: javax/transaction/UserTransaction

下載 jta-1.1.jar 後,加入專案的 build path。


2) Tomcat7 關閉 Web App 出現 memory leak 警告。

[DefaultQuartzScheduler_Worker-5] but has failed to stop it. This is very likely to create a memory leak

這是因為 Tomcat7 關閉 Web App 時,沒有正確關閉 quartz 的關係,在QuartzInitializerListener 加入下列初始參數。

WebContent/WEB-INF/web.xml

參考

Tomcat7 使用 Logging

專案有需要產生 Log 檔案的需求,以便未來服務發生錯誤有個記錄,所以又要看看 Tomcat7 是如何產生 Log。Tomcat7 預設使用 java.util.logging,目前開發的功能很小,就不打算要另外安裝 log4j,才不會每台機器要裝一次 log4j,PG 兼 SA 就只好挑重點做。

1) 程式碼取得 Logger

Logger logger = Logger.getLogger("com.xyz.foo");

2) 程式碼中使用 Logger

logger.info("Hi!");

3) 替專案設定 Handler

到 ${專案路徑}/WebContent/WEB-INF/classes/ 下,新增或編輯 logging.properties,加入 Handler 並設定相關資訊,是要用 Console 還是要 File,等級多少要印出來。

logging.properties

4) 替專案設定 Logger

通過 Logger Level 的訊息才傳給上一步設定的 Handler。

經過測試,有點討厭的是,Logger 要到負責啟動專案的 JRE 上來設定才有作用。以我使用 macbook 舉例,編輯  Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/jre/lib/logging.properties,拉到最下面可以替 Logger 設定等級。

比如你專案中取得 Logger 是用

Logger.getLogger("com.xyz.foo");

那你可以在此新增

com.xyz.foo.level = FINE

這樣專案中透過 com.xyz.foo Logger,訊息的等級是 FINE 以上才會送給 Handler。而在 logging.properties 可以發現預設的 Logger Level 是 INFO。

.level = INFO

補充

JAVA Logging Level

SEVERE > WARNING > INFO > CONFIG > FINE > FINER > FINEST

Log 輸出

一條 Log 會
  • 第一關,先檢查 Logger Level 後才送到 Handler。
  • 第二關,再檢查 Handler Level 才決定是否要印在螢幕或是檔案上。
我個人習慣是
  • Handler Level 開到 FINEST(第二關永遠放行)。
  • 然後用 Logger Level 來決定是否要印在螢幕或是檔案上(第一關卡控)。
  • 如此,只要檢查 Logger Level 就能判斷會不會被印出來。

Tomcat7 使用 DataSource(MySQL)

之前比較好命有 JBoss 可以用,現在只有 Tomcat ,有些預設沒開的功能就要想辦法整合進來了,這陣子弄了些有的沒的,趕快記錄一下,免得忘記。

1) 在 Tomcat 下設定

到 $tomcat安裝路徑/conf/context.xml 新增 DataSource 的設定。

context.xml

2) 記得下載 mysql-connector.jar 放到 $tomcat安裝路徑/lib 底下。

3) 重新啟動 Tomcat 就應該可以看到有無連上 MySQL 。

4) 程式碼就可以透過 JNDI 用 jdbc/TestDB (上面設定檔替 DataSource 取的名稱)拿到 DataSource。

Eclipse 整合 Tomcat 遇到問題

但是從 Eclipse 下直接啟動 Tomcat 就會發現一直拿不到 DaraSource,是因為 Eclipse 開啓 Tomcat 時,會用預設的設定檔去覆蓋 Tomcat 的設定檔造成的。

其中一個解法是,直接修改 $eclipse使用的workspace/Servers/Tomcat/context.xml,把上一段做的變動改寫過來即可。

但這樣 Tomcat 和 Eclipse 會有兩個 context.xml (Don't Repeat Yourself),而用 Eclipse 啟動又會覆寫 Tomcat 原本的 context.xml (容易讓人搞不清楚到底以哪個為準)。所以就用了下面的做法。

綁定專案設定

直接修改 $project路徑/WebContent/META-INF/context.xml,把上一段做的變動改寫過來即可。Tomcat 再載入專案時就會加上專案裡新增的 DataSource。

這樣做之後接手的人,只需要部署 war 檔,不用特別到 Tomcat 下做設定,比較符合我個人開發專案的習慣。