質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Java EE

Java EE(Java Enterprise Edition)はJavaベースのテクノロジーとその相互運用の仕様をまとめたものです。サーバとクライアントのアーキテクチャを規定し、特定アプリケーションのクラス用に定義されたテクノロジー設定のプロファイルを使用します。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

マルチスレッド

マルチスレッドは、どのように機能がコンピュータによって実行したのかを、(一般的にはスレッドとして参照される)実行の複合的な共同作用するストリームへ区分することが出来ます。

servlet

Servletとは、Webページの動的な生成やデータ処理などをサーバ上で実行するために、Javaで作成されたプログラムです。 ショッピングサイトやオンラインバンキングといった、動的なウェブサイトの構築に用いられています。

Tomcat

TomcatはApache Software Foundation (ASF)で開発されたオープンソースのWebコンテナです。

Q&A

解決済

3回答

2501閲覧

TomcatにWebアプリをリリースした後に定期処理を一定間隔で実行し、再デプロイ時に定期処理を初期化したい

fsk5303

総合スコア21

Java EE

Java EE(Java Enterprise Edition)はJavaベースのテクノロジーとその相互運用の仕様をまとめたものです。サーバとクライアントのアーキテクチャを規定し、特定アプリケーションのクラス用に定義されたテクノロジー設定のプロファイルを使用します。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

マルチスレッド

マルチスレッドは、どのように機能がコンピュータによって実行したのかを、(一般的にはスレッドとして参照される)実行の複合的な共同作用するストリームへ区分することが出来ます。

servlet

Servletとは、Webページの動的な生成やデータ処理などをサーバ上で実行するために、Javaで作成されたプログラムです。 ショッピングサイトやオンラインバンキングといった、動的なウェブサイトの構築に用いられています。

Tomcat

TomcatはApache Software Foundation (ASF)で開発されたオープンソースのWebコンテナです。

0グッド

0クリップ

投稿2018/08/07 06:36

編集2018/08/24 08:01

前提・実現したいこと

TomcatにJavaで作ったwebアプリをデプロイし公開する。
そのwebアプリをリリースして以降、ある処理を一定間隔で実行し続けたい。
そのwebアプリをアンデプロイした際は定期処理を終了したい。
そのwebアプリを再デプロイした際は定期処理を初期化し、再度実行を開始したい。

発生している問題

・Tomcatからwebアプリをアンデプロイした際にその処理が止まらない
・Tomcatに同じwebアプリを再デプロイした際にスレッドが重複していってしまう。

該当のソースコード

webアプリをデプロイした際に初期処理をするクラス

Java

1public class InitialServlet extends HttpServlet { 2 // 初期処理を記述して下さい。 3 public void init() throws ServletException { 4 ScheduleManager schedule = ScheduleManager.getInstance(); 5 schedule.start(); 6 }

定期処理を開始するクラス

Java

1public class ScheduleManager extends Thread { 2 3 private static ScheduleManager instance = new ScheduleManager(); 4 private static Timer timer = new Timer(); 5 6 private ScheduleManager() { 7 } 8 9 @Override 10 public synchronized void run() { 11 if (timer != null) { 12 // 既にtimerがあれば停止する 13 timer.cancel(); 14 System.out.println("stop run"); 15 } 16 // timerの開始 17 System.out.println("start run"); 18 timer = new Timer(); 19 timer.schedule(Schedule.getInstance(), 0, 2000); 20 } 21 22 public static ScheduleManager getInstance() { 23 return instance; 24 } 25}

定期処理の実装

Java

1class Schedule extends TimerTask { 2 3 private static Schedule instance = new Schedule(); 4 private Schedule() { 5 } 6 int testCount = 0; 7 8 @Override 9 public void run() { 10 System.out.println(testCount + "回目"); 11 testCount++; 12 } 13 14 public static Schedule getInstance() { 15 return instance; 16 } 17}

試したこと

ScheduleManagerクラスやScheduleクラスをシングルトンクラスにしてみたりしましたが、実行結果が以下のようになってしまいます。
webアプリを再配置する毎に処理が重複されて定期実行されてしまいます。
なお、Tomcatを再起動すればまた"1回目"から始まりますが、運用上Tomcatを止める事は出来ません。

113回目 63回目 91回目 114回目 64回目 92回目 115回目 65回目 93回目 116回目 66回目 94回目

望んでいる実行結果

1回目 2回目 3回目 4回目←ここでwebアプリを再配置したとすると stop run start run 1回目 2回目 3回目 4回目

FW/ツールのバージョンなど

apache-tomcat-7.0.78

補足

お恥ずかしいですが独学で学んでいるため、笑ってしまうような実装があるかと思います。
出来れば今回の質問の本題以外のご指摘もありましたら是非宜しくお願いします。
また、質問内容に不備がありましたら追記致します。
お手数お掛け致しますが助言を宜しくお願いします。


内容から見るとデプロイされている間というのはそのデプロイ中(初期化処理中)ではなくリリース完了後以降(初期化終了後)と読み替えていいですか?

仰る通りです。質問内容を訂正します。

またさらに内容見ると、どちらかというと定期処理をデプロイ時に初期化したいと読み取れるのですが、あってますか?

仰る通りです。質問内容を訂正します。


以下のようなリスナーを追加したところ、もしかしたらうまく動いているかもしれません。
その他の解決方法やご指摘等ありましたら宜しくお願い致します。

Java

1public class InitializationListener implements ServletContextListener { 2 3 public void contextInitialized(ServletContextEvent event) { 4 // 起動を検知 5 System.out.println("/////////////////////////////////////////////////////////////////"); 6 System.out.println("Initialized"); 7 System.out.println("/////////////////////////////////////////////////////////////////"); 8 ScheduleOutput.getInstance().startTimer(); 9 } 10 11 @Override 12 public void contextDestroyed(ServletContextEvent event) { 13 //終了を検知 14 System.out.println("/////////////////////////////////////////////////////////////////"); 15 System.out.println("Destroyed"); 16 System.out.println("/////////////////////////////////////////////////////////////////"); 17 ScheduleOutput.getInstance().stopTimer(); 18 }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

namda

2018/08/07 07:10

内容から見るとデプロイされている間というのはそのデプロイ中(初期化処理中)ではなくリリース完了後以降(初期化終了後)と読み替えていいですか?
namda

2018/08/07 07:10

またさらに内容見ると、どちらかというと定期処理をデプロイ時に初期化したいと読み取れるのですが、あってますか?
fsk5303

2018/08/07 07:21

①はい、仰る通り、リリース完了後以降に定期処理を行い続けるという意味です。②はい、仰る通り、デプロイ時に定期処理を初期化したいです。私の今の状態だと初期化されずに定期処理が重複されて実行されていってしまいます。
退会済みユーザー

退会済みユーザー

2018/08/07 22:48

まあ、 InitialServlet をよくみたら destroy してないな
guest

回答3

0

ベストアンサー

こんにちは、
質問の内容を、以下だと仮定して回答いたします。

 ・Webアプリケーション以外に定期処理(スレッド)を実行したい
・Webアプリケーション開始時に定期処理(スレッド)を開始したい
・Webアプリケーション終了時にはその定期処理(スレッド)は終了させたい

多分、ご質問の端的な答えとしてはWebアプリケーション起動時と終了時に実行されるインターフェースが
あるため、それをフィルターに設定すれば良いのですが、あまりお勧めはできません。

上記が質問意図であれば、以前自分も同じようなシステム構成に悩みました。
Webアプリケーションを起動すれば定期実行バッチスレッドも起動すればいいよねと。

結論から言うと、Webアプリ開始時にスレッドを起動、ではなく、Webアプリと
定期実行処理のプロセスを別にした方がよいです。実際にはWebアプリは普通に起動させ、
手動かどうかは置いて置きますが、スレッドを起動するバッチプログラムを別に用意します。

古い情報ですがServlet3.0からサーブレットからのスレッド起動は一応、可能と言う見解ですが、
システム的に見ればお悩みのように、Webアプリ終了時に必ずスレッドが終了するという
補償ができません。何らかの理由によりスレッドがゾンビ化してしまった場合、Webアプリケーション
のみならずTomcatも停止する必要性が出てきます。(質問に記載はありませんでしたが、その場合、
Tomcat上で動いている別アプリケーションも死にます)

そのためシステムの安全性を考えた場合Webアプリケーションとスレッド処理は別のプロセスに
分ける方法が良いと思います
上記の方法であれば、万が一Webアプリ終了時にスレッドがゾンビ化した場合、JEEコンテナ
本体の終了などをする必要もなく単にバッチプログラムのプロセス終了で良くなります。

ちょっと面倒ですが、Tomcatを起動(Webアプリをデプロイ)その後、スレッド処理を別プロセスで
起動、終了の際は、Webアプリをアンデプロイ(停止)、スレッド処理を終了と言う認識です。

投稿2018/08/07 15:49

編集2018/08/07 15:53
arcanum_jp

総合スコア94

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fsk5303

2018/08/24 08:06

とても丁寧にありがとうございます。 都合により返信ができずにおりました。申し訳ありません。 質問文の一番最後に追記しました通り、ServletContextListenerインターフェースを実装したInitializationListenerクラスにて、contextInitialized()メソッドとcontextDestroyed()メソッドを用意し、それらの中でスレッドの開始と終了を行う事で、一応目的は達成できました。しかし、この方法ですと、arcanum_jpさんが仰るように「Webアプリ終了時に必ずスレッドが終了するという補償ができていない」という事になるのでしょうか・・・。 その点を保証しようとしたら、arcanum_jpさんが仰るようにプロセスを別にするしかないという事になるのでしょうか。
arcanum_jp

2018/08/30 10:23

割り切りの問題かと。 自分の場合、Webアプリ起動時ではなく、管理画面からスレッドを起動するという形でしたがご質問者が作られているWebアプリも多分アプリのどこかにスレッドへの参照を持っているのだと思います。それらが何らかのバグにより切れてしまった場合、当然スレッドが終了できません。 そんなレアケースなど、それはテストでやればよいというのであればそれは割り切りの問題だと思いますが。TomcatにWebアプリが1つ乗っている状態であれば杞憂だと思います。 あと、質問や回答では触れられておりませんが、スレッド内でJDBCのコネクションへのプール参照を行う場合など結構ピーキーな問題が発生しますので、そういう場合などはやはりスレッドとWebアプリは別にした方が良いと思います。 やらない方がいい理由を述べてしまいましたが割り切りが大きいのかなと思いました。 参考(超古いですが) http://frmmpgit.blog.fc2.com/blog-entry-93.html
guest

0

Servletのinit(ServletConfig config) はデフォルトの設定だと、ホットデプロイ時には実行されません。

アプリケーションのホットデプロイ後にinitメソッドを実行させたい場合は、

java

1@WebServlet(urlPatterns="/", loadOnStartup=1)

サーブレットにloadOnStartup属性をつけ、-1以外にするとよいでしょう。

投稿2018/08/07 12:51

A-pZ

総合スコア12011

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fsk5303

2018/08/24 08:08

丁寧にありがとうございます。 都合により返信ができずにおりました。申し訳ありません。 質問文の一番最後に追記しました通り、ServletContextListenerインターフェースを実装したInitializationListenerクラスにて、contextInitialized()メソッドとcontextDestroyed()メソッドを用意し、それらの中でスレッドの開始と終了を行う事で、一応目的は達成できました。 A-pZさんに教えて頂いた方法をまだ試せていないので、こちらも試してみて勉強させていただきます。ありがとうございました。
guest

0

定期処理を開始するクラスですが

if (timer != null) { としていますが
この場合
private static Timer timer = new Timer();
としているので常にTimerインスタンスが作られており、この判定でtrueとなることはありません。

ですので
private static Timer timer;
とし

java

1if (timer != null) { 2 timer.cancel();// 既にtimerがあれば停止する 3 System.out.println("stop run"); 4 timer = null; 5} 6 7if (timer == null) { 8 System.out.println("start run"); 9 timer = new Timer();// timerの開始 10 timer.schedule(Schedule.getInstance(), 0, 2000); 11}

とすれば動くと思います

投稿2018/08/07 09:51

編集2018/08/07 09:59
namda

総合スコア705

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

namda

2018/08/07 10:00

synchronized に修正を加えましたが、ちょっと動くか自信ないので駄目ならエラー状況教えてください。
fsk5303

2018/08/24 08:16 編集

丁寧にありがとうございます。 都合により返信ができずにおりました。申し訳ありません。 質問文の一番最後に追記しました通り、ServletContextListenerインターフェースを実装したInitializationListenerクラスにて、contextInitialized()メソッドとcontextDestroyed()メソッドを用意し、それらの中でスレッドの開始と終了を行う事で、一応目的は達成できました。 namdaさんに教えて頂いた方法をまだ試せていないので、こちらも試してみて勉強させていただきます。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問