ItemWriterの出力先をメールにするには? | Java Springの逆引きメモ

Java Springの逆引きメモ

JavaのSpring frameworkのメモを書いていきます!
初心者の勉強ノートなので間違いがあるかもしれませんが、何かヒントになることがあれば幸いです。

Springの掲示板を読んでいたら、面白い記事がありましたので触れてみたいと思います。

まさに、この記事のタイトルの内容です。



http://forum.springsource.org/showthread.php?t=48530


それほど難しくないので、まずはサンプルを見てみましょう!



※ここではItemWriterだけを記述します。

ItemReaderなどの設定は他の記事を参照してください。

  ・Stepを使って処理を書くには?

  ・DBにデータを読み書きするには?


※あと、ライブラリとしてmail.jarとactivation.jarが必要になるので、ダウンロードして配置してください

ちなみに、Java1.6以降ではactivation.jarは必要ないようです。




【メール送信ItemWriterクラス】
package sample;

import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.file.transform.LineAggregator;
import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;

public class MailItemWriter<T> implements ItemWriter<T> {
 private static final String DEFAULT_LINE_SEPARATOR = System.getProperty("line.separator");
 protected static final Log log = LogFactory.getLog(MailItemWriter.class);
 
 private int maxSendCount = 10;
 private int sendedCount = 0;
 private LineAggregator<T> lineAggregator;
 private String lineSeparator = DEFAULT_LINE_SEPARATOR;
 private MailSender mailSender;
 private String mailSubject;
 private String mailFrom;
 private String[] mailTo;
 
 
 
 public int getMaxSendCount() {
  return maxSendCount;
 }
 public void setMaxSendCount(int maxSendCount) {
  this.maxSendCount = maxSendCount;
 }
 public LineAggregator<T> getLineAggregator() {
  return lineAggregator;
 }
 public void setLineAggregator(LineAggregator<T> lineAggregator) {
  this.lineAggregator = lineAggregator;
 }
 public String getLineSeparator() {
  return lineSeparator;
 }
 public void setLineSeparator(String lineSeparator) {
  this.lineSeparator = lineSeparator;
 }
 public MailSender getMailSender() {
  return mailSender;
 }
 public void setMailSender(MailSender mailSender) {
  this.mailSender = mailSender;
 }
 
 public String getMailSubject() {
  return mailSubject;
 }
 public void setMailSubject(String mailSubject) {
  this.mailSubject = mailSubject;
 }
 public String getMailFrom() {
  return mailFrom;
 }
 public void setMailFrom(String mailFrom) {
  this.mailFrom = mailFrom;
 }
 public String[] getMailTo() {
  return mailTo;
 }
 public void setMailTo(String[] mailTo) {
  this.mailTo = mailTo;
 }
 @Override
 public void write(List<? extends T> items) throws Exception {
  StringBuilder buf = new StringBuilder();
  if(this.sendedCount > this.maxSendCount) 
   throw new IndexOutOfBoundsException("送信数がmaxSendCountを超えました");
  
  try{
   for (T item : items) {
    buf.append(this.lineAggregator.aggregate(item) + this.lineSeparator);
   }
   
   //
   sendMail(buf.toString());
   
  }catch(Exception e){
   log.error("item書き込み&送信中にエラーが発生しました。", e);
   throw e;
  }
  
 }
 
 protected void sendMail(String text) throws Exception {
  SimpleMailMessage msg = new SimpleMailMessage();
  msg.setSubject(this.mailSubject + "[" + this.sendedCount + "]");
  msg.setFrom(this.mailFrom);
  msg.setTo(this.mailTo);
  msg.setText(text);
  
  try{
   this.mailSender.send(msg);
   ++this.sendedCount;
   
  }catch(Exception e){
   log.error("エラー発生時メールの送信に失敗", e);
   throw e;
  }
 }
}





【Springの設定ファイル一部抜粋】


 <!-- メール送信テスト -->
 <bean id="mailItemWriter" class="sample.MailItemWriter">
  <property name="lineAggregator">
   <bean class="org.springframework.batch.item.file.transform.FormatterLineAggregator">
    <property name="fieldExtractor">
     <bean class="org.springframework.batch.item.file.transform.PassThroughFieldExtractor" >
     </bean>
    </property>
    <property name="format" value="%2$s,%1$03d,%3$d" />
   </bean>
  </property>
  <property name="mailSender" ref="mailSender" />
  <property name="mailFrom" value="test@test.co.jp" />
  <property name="mailTo" value="xxx@mail.co.jp" />
  <property name="mailSubject" value="mailCsv" />
 </bean>

<!-- メールセンダー -->
 <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
  <property name="defaultEncoding" value="ISO-2022-JP" />
  <property name="host" value="smtp.XXX.ne.jp" />
  <property name="username" value="Xxxxx" />
 </bean>






【説明】


メール送信のための機構もSpringは用意しています。

それがMailSenderです。

設定ファイルにそれを書くだけでOKです。


実際のデータ作成とメール送信する実装はItemWriterにあります。



再掲:ItemWriter


public void write(List<? extends T> items) throws Exception {
 StringBuilder buf = new StringBuilder();
 if(this.sendedCount > this.maxSendCount)
   throw new IndexOutOfBoundsException("送信数がmaxSendCountを超えました");

 try{
   for (T item : items) {
     buf.append(this.lineAggregator.aggregate(item) + this.lineSeparator);
   }

   //
   sendMail(buf.toString());

 }catch(Exception e){
   log.error("item書き込み&送信中にエラーが発生しました。", e);
   throw e;
 }
}

データの作成は、FlatFileItemWriterクラスで使用していたlineAggregatorを流用しています。

これでFormatter変換も、区切り文字も自在にできます。

しかも、何も自作しなくていいですね!にひひ


そして、作成したデータをmailSenderに送っているだけです。



ちなみに、メールを何回も送ると負荷がかかって危ないので、最大送信数を儲けています。


何かあっさりできましたね(笑)

Springがいろいろ用意してくれているのと、DIのおかげですね。



【拡張など】

もっと複数のデータ処理結果を送りたい場合、もしくは、ヘッダー・フッターなどを付けたい場合。

それほど難しくないです。

まずヘッダー・フッターについてはFlatFileItemWriterの真似をしましょう。

FlatFileItemWriterではheaderCallbackプロパティを持っていますよね。これをつけて、内部で処理の最初に

呼び出してあげればいいですよね。


また、複数のデータ処理結果を送りたい場合は、それぞれの処理結果をファイルに保存しましょう。

ItemReaderの中には複数のファイルをまとめて扱うクラスがあります。

MultiResourceItemReaderです。

readerにこのクラスを指定すれば、複数になってしまった処理結果ファイルをひとまとめにできます。

あとはwriterに上のメールItemWriterを指定すればよいでしょう。


結構、再利用でき、便利ですね!



【補足】

念のため補足しておきます。

上記のサンプルは簡単にするため、restartできるようには考えていません

なぜならJobが終了したときにWriterクラス内部で持っているsendedCount も消えてしまうからです。

restartしたときにメールのタイトルが「mailCsv[0]」から始まってしまいます。

Javaが終了するので内部のインスタンスが消えてしまうので当たり前ですよね。

(このサンプルでは問題はメールタイトルだけですが、restartできるようにする方法は知っておきましょう)

しかし、値をを保持し、restartできるようにすることは簡単です。

StepExectutionの中に持っているExecutionContextに値を保存し、そこから値を取得してメールのタイトルに使用すればOKです。

もっと詳しいことは他の記事で書いてみようと思います。

  ・自作クラスをrestartできるようにするには?



参照:

・トップ

・SpringBatch機能について

・WEB上で定期的に何か処理をするには?

・実際に作成するものとは(Stepの概念について)

・Spring Batchを使えるようにするには? (準備編)

・Spring Batchを起動するには? (基本編)

・Stepを使って処理を書くには?

・複数のStepをひとまとまりで扱うには?(flow)

・条件分岐をさせるには?

・既に用意されている機能(クラス)を探すには?

・DBにデータを読み書きするには?

・データの書き込み(コミット)タイミングを制御するには? (restart機能もついでに見る!)

・バッチ処理を非同期で起動するには?