SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Google App Engine for Javaを使ってみよう!

Google App Engine for Javaを使ってみよう!
(7)App Engine Cron Service

Google App Engine for Javaを使ってみよう! (7)

  • X ポスト
  • このエントリーをはてなブックマークに追加

App Engine Cron Serviceを利用する

 App Engine Cron Serviceは管理コンソールのVersionsでデフォルトアプリケーションに設定されているアプリケーションのcron.xmlのみが有効になります。

 今回のサンプルは、「データストアに保存したステータスを管理者に毎分メールする」というものなので、サンプルをデプロイしたまま放置するとメールボックスに大量のメールが来てしまいます。サンプルの動作確認が終わったらすぐに、デフォルトアプリケーションを切り替えて、App Engine Cron Serviceを停止できるように、先にEclipseで適当なGoogle App Engine用のWeb Applicationプロジェクトを作成してデプロイしておいてください。

プロジェクトの作成

 以下の設定でGWTプロジェクトを作成してください。GWTプロジェクトの作成手順については、第1回の解説を参照してください。今回は日本語(2バイトコード)を扱いたいので、言語設定を必ず[UTF-8]に設定してください。

プロジェクトの設定(言語設定はUTF-8に)
Project name Cron
Package com.daisukeyamashita.test.cron

プログラムの作成

 データストアに保存するデータクラス(ここではStateDataクラス)を定義します。

com.daisukeyamashita.test.cron.server.StateDataクラス
package com.daisukeyamashita.test.cron.server;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class StateData {
	@PrimaryKey
	@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
	private Long id;
	@Persistent
	private String name;

	public StateData(String name) {
		this.name = name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}
}

 データの読み書きを簡単にするために、第5回で利用したヘルパークラスを定義します。

com.daisukeyamashita.test.cron.server.PMFクラス
package com.daisukeyamashita.test.cron.server;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;

public class PMF {

	private static final PersistenceManagerFactory pmfInstance = JDOHelper
			.getPersistenceManagerFactory("transactions-optional");

	private PMF() {
	}

	public static PersistenceManagerFactory get() {
		return pmfInstance;
	}
}

続いて、GWTのAjax通信用のクライアントサイドのソースコードを定義します。

com.daisukeyamashita.test.cron.client.GreetingServiceクラス
package com.daisukeyamashita.test.cron.client;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

/**
 * The client side stub for the RPC service.
 */
@RemoteServiceRelativePath("greet")
public interface GreetingService extends RemoteService {
	void store(String name);
}
com.daisukeyamashita.test.cron.client.GreetingServiceAsyncクラス
package com.daisukeyamashita.test.cron.client;

import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * The async counterpart of <code>GreetingService</code>.
 */
public interface GreetingServiceAsync {
	void store(String name, AsyncCallback callback);
}

 GWTのAjax通信のサーバサイド部分のソースコードを定義します。ここで、データをデータクラスに設定して、永続化しています。

com.daisukeyamashita.test.cron.server.GreetingServiceImplクラス
package com.daisukeyamashita.test.cron.server;

import java.util.List;
import javax.jdo.PersistenceManager;
import com.daisukeyamashita.test.cron.client.GreetingService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/** * The server side implementation of the RPC service. */
@SuppressWarnings("serial")
public class GreetingServiceImpl extends RemoteServiceServlet implements GreetingService {
	@SuppressWarnings("unchecked")
	public void store(String name) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		String query = "select from " + StateData.class.getName();
		List<StateData> list = (List<StateData>) pm.newQuery(query).execute();

		StateData data;
		if (list.size() == 0) {
			data = new StateData(name);
		} else {
			data = list.get(0);
			data.setName(name);
		}

		try {
			pm.makePersistent(data);
		} finally {
			pm.close();
		}
	}
}

 クライアント部分のソースコードを定義します。

com.daisukeyamashita.test.cron.client.Cronクラス
package com.daisukeyamashita.test.cron.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class Cron implements EntryPoint {
	/**
	 * Create a remote service proxy to talk to the server-side Greeting service.
	 */
	private final GreetingServiceAsync greetingService = GWT.create(GreetingService.class);

	/**
	 * This is the entry point method.
	 */
	public void onModuleLoad() {
		final TextBox tbName = new TextBox();
		tbName.setText("晴れ");

		final Button btnStore = new Button("store");

		final DialogBox dialogBox = new DialogBox();
		dialogBox.setText("Remote Procedure Call");
		dialogBox.setAnimationEnabled(true);
		final Button closeButton = new Button("Close");
		// We can set the id of a widget by accessing its Element
		closeButton.getElement().setId("closeButton");
		final Label lblServerResponse = new Label();
		VerticalPanel dialogVPanel = new VerticalPanel();
		dialogVPanel.addStyleName("dialogVPanel");
		dialogVPanel.add(lblServerResponse);
		dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
		dialogVPanel.add(closeButton);
		dialogBox.setWidget(dialogVPanel);

		// Add a handler to close the DialogBox
		closeButton.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				dialogBox.hide();
				btnStore.setEnabled(true);
				btnStore.setFocus(true);
			}
		});

		btnStore.addClickHandler(new ClickHandler() {

			public void onClick(ClickEvent event) {
				btnStore.setEnabled(false);
				btnStore.setFocus(false);
				greetingService.store(tbName.getText(), new AsyncCallback() {

					public void onSuccess(Object result) {
						lblServerResponse.setText("Success");
						dialogBox.center();
					}

					public void onFailure(Throwable caught) {
						lblServerResponse.setText("Failure");
						caught.printStackTrace();
					}
				});
			}
		});
		RootPanel.get("nameFieldContainer").add(tbName);
		RootPanel.get("sendButtonContainer").add(btnStore);
	}
}

 App Engine Cron Serviceで動かすプログラムです。「test@example.com」の部分を自分のメールアドレスに変更してください。

com.daisukeyamashita.test.cron.server.MailServletクラス
package com.daisukeyamashita.test.cron.server;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Properties;

import javax.jdo.PersistenceManager;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MailServlet extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
			IOException {

		PersistenceManager pm = PMF.get().getPersistenceManager();
		String query = "select from " + StateData.class.getName();
		List<StateData> list = (List<StateData>) pm.newQuery(query).execute();
		if (list.size() == 0) {
			return;
		}
		StateData data = list.get(0);
		Properties props = new Properties();
		Session session = Session.getDefaultInstance(props, null);

		try {
			InternetAddress address = new InternetAddress("test@example.com", "管理者", "iso-2022-jp");

			MimeMessage message = new MimeMessage(session);
			message.setFrom(address); // 送信元アドレス
			message.addRecipient(Message.RecipientType.TO, address); // あて先アドレス

			message.setSubject("Google App Engineからのメールです", "ISO-2022-JP"); // タイトル
			message.setText("現在のステータスは『" + data.getName() + "』です。"); // メール本文

			Transport.send(message);

		} catch (UnsupportedEncodingException e) {
			//error
			e.printStackTrace();
		} catch (MessagingException e) {
			//error
			e.printStackTrace();
		}
	}

}

 「war/WEB-INF/web.xml」の一番下にある</web-app>タグの直前に以下のタグを追加してください。

  <servlet>
    <servlet-name>mailServlet</servlet-name>
    <servlet-class>com.daisukeyamashita.test.cron.server.MailServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>mailServlet</servlet-name>
    <url-pattern>/cron/mail</url-pattern>
  </servlet-mapping>

 最後に、「war/WEB-INF/cron.xml」を新規作成します。最初に書いたように、毎分/cron/mailサーブレットにアクセスし、管理者にメールを送信する定義になっています。

war/WEB-INF/cron.xml
<?xml version="1.0" encoding="UTF-8"?>
<cronentries>
  <cron>
    <url>/cron/mail</url>
    <description>every 1 minutes</description>
    <schedule>every 1 minutes</schedule>
  </cron>
</cronentries>

次のページ
実行する

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Google App Engine for Javaを使ってみよう!連載記事一覧

もっと読む

この記事の著者

山下 大介(ヤマシタ ダイスケ)

  京都大学を中心とした、産官学共同プロジェクトのSOBAプロジェクトに参加後、同プロジェクトで開発したP2P配信によるVoIP技術を2005年に商業化。オープンソース、VoIP、P2P、クラウドコンピューティングに精通。趣味はGoogleの追っかけ。現在は、株式会社SOBAプロジェクト取締役。ブログ:『~ ミネルヴァの梟は黄昏とともに飛び始める ~』所属:株式会社SOBAプロジェクトGoogle Developer Day 2008サポーター, 2009サポーターGoogle App Engine API Expert

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/3918 2009/06/18 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング