パッケージJava製品開発担当の大です。こんにちは。
皆様ゴールデンウィークはいかがお過ごしでしたでしょうか。
私は妻と一緒に映画「テルマエ・ロマエ」を観にいってきました。噂には聞いていましたが、本当に楽しい映画でしたよ!まだ観ていらっしゃらない方はぜひどうぞ。

さて、頭を切り替えて今日からは仕事です。

世の中クラウドが大流行りですね。広く使われるようになるにつれ、弊社にも「クラウド上に置いたデータを使用して帳票を作成したい」といったご要望も寄せられるようになってきました。

弊社の「シーオーリポーツ 帳票クリエータ Ver.2」は、データソースとしてCSV、XML、RDB、固定長のデータを簡単に扱うことができる製品です。しかし、それ以外のデータが扱えないわけではありません。少しプログラミングが必要になりますが、自前のレコードセットクラスを用意することで、さまざまなデータを扱うことができるようになっています。

今回はその一例として、代表的なクラウドサービスである「Googleドキュメント」のスプレッドシートをデータソースにして帳票を出力するサンプルを作ってみました。

Googleスプレッドシート

Googleスプレッドシート(クリックで拡大)

スプレッドシートを作成する

イチからサンプルを作るのは結構大変なので、帳票クリエータ Ver.2 のデベロッパーズマニュアルに付属のサンプル「実践的な帳票出力処理(1)単票出力」を改造することにします。今回は、フォームファイルはこれに入っているものをそのまま使用します。

まず、Googleドキュメント上でスプレッドシートを新規に作成します(Googleアカウントが必要です)。
スプレッドシート名は「保険サンプル」としておきます。

元のサンプルにはCSVのデータが用意されていますので、それをスプレッドシートにそのまま流し込みます。

GoogleスプレッドシートAPIのクライアントライブラリを用意する

Googleスプレッドシートからデータを取得するには、Googleが提供しているクライアントライブラリを使用します。

こちらからgdata-src.java-*.zipをダウンロードして解凍します。
「lib」「deps」フォルダの中にあるjar群にクラスパスを通せば、開発を行う準備は完了です。

レコードセットクラスを作成する

レコードセットクラスは、jp.co.hos.coreports.IRecordSetインタフェースを実装して作成します。

package test;

import java.io.IOException;
import java.util.Iterator;

import jp.co.hos.coreports.IRecordSet;
import jp.co.hos.coreports.events.IEventListener;

import com.google.gdata.client.spreadsheet.FeedURLFactory;
import com.google.gdata.client.spreadsheet.SpreadsheetQuery;
import com.google.gdata.client.spreadsheet.SpreadsheetService;
import com.google.gdata.data.spreadsheet.CustomElementCollection;
import com.google.gdata.data.spreadsheet.ListEntry;
import com.google.gdata.data.spreadsheet.ListFeed;
import com.google.gdata.data.spreadsheet.SpreadsheetEntry;
import com.google.gdata.data.spreadsheet.SpreadsheetFeed;
import com.google.gdata.data.spreadsheet.WorksheetEntry;
import com.google.gdata.util.ServiceException;

/**
 * Google スプレッドシートをデータソースとして使用するレコードセットです。
 * 
 */
public class GoogleSpreadsheetRecordSet implements IRecordSet {
	private SpreadsheetService service = new SpreadsheetService("Google スプレッドシート サンプル");
	private Iterator<ListEntry> listIterator;
	private CustomElementCollection current = null;
	private String spreadsheetName;
	private String userName;
	private String password;

	// スプレッドシート名を指定してデータをロードします。
	private void load() throws IOException, ServiceException {
		// 認証処理は、現在はOAuthを使用することが推奨されていますが、
		// ここでは本題ではないので簡単のためユーザ名・パスワードで認証しています。
		service.setUserCredentials(userName, password);
		SpreadsheetEntry spreadsheet = getSpreadsheet(spreadsheetName);
		// ワークシートはここではデフォルトのものを使用します。
		WorksheetEntry worksheet = spreadsheet.getDefaultWorksheet();
		ListFeed lf = service.getFeed(worksheet.getListFeedUrl(), ListFeed.class);
		listIterator = lf.getEntries().iterator();
	}

	// スプレッドシート名を指定してスプレッドシートを取得します。
	private SpreadsheetEntry getSpreadsheet(String name) throws IOException, ServiceException {
		FeedURLFactory factory = FeedURLFactory.getDefault();
		SpreadsheetQuery query = new SpreadsheetQuery(factory.getSpreadsheetsFeedUrl());
		query.setTitleQuery(name);
		SpreadsheetFeed feed = service.query(query, SpreadsheetFeed.class);
		// 見つかった最初のものを返します。同じ名前のスプレッドシートが複数ある場合は注意してください。
		return feed.getEntries().get(0);
	}

	@Override
	public Object getValue(Object key) {
		return current.getValue(String.valueOf(key));
	}

	@Override
	public boolean next() {
		if (current == null) {
			// まだロードしてなければロードする
			try {
				load();
			} catch (Exception ex) {
				throw new RuntimeException("ロードに失敗しました", ex);
			}
		}
		if (listIterator.hasNext()) {
			current = listIterator.next().getCustomElements();
			return true;
		} else {
			current = null;
			return false;
		}
	}

	@Override
	public Object getValue(int arg0) {
		// 今回は不使用
		return null;
	}

	@Override
	public void addEventListener(IEventListener arg0) {
		// 今回は不使用
	}

	// 以下getter/setter

	public String getSpreadsheetName() {
		return spreadsheetName;
	}

	public void setSpreadsheetName(String spreadsheetName) {
		this.spreadsheetName = spreadsheetName;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
}

設定ファイルを作成する

帳票クリエータの設定ファイルを作成します。これも、サンプルにちょっとだけ手を入れただけのものです。

ポイントは、RecordSetタグで指定しているレコードセットの定義部分です(25行目~30行目)。implementer属性で、上で作成したGoogleSpreadsheetRecordSetクラスを指定しています。また、Propertyタグで認証情報やスプレッドシート名等を指定します。(設定ファイルはXMLのファイルなので、パスワード等を平文で書くと危険です。運用時には設定ファイルの暗号化機能を使用して、中身を見えないようにしておきましょう。)

<?xml version="1.0" encoding="Shift_JIS"?>

<DocCreatorConf version="3.0" xmlns="http://schema.hos.co.jp/dcx/3.0/" scriptType="application/ecmascript">

  <!--  グローバル領域のスクリプト -->
  <Script import="test">
    var today = {
      year: date('yyyy'),
      month: date('MM'),
      day: date('dd')
    }
    var crCircle = new CrCircle();
    crCircle.width = 1100;
    crCircle.height = 400;
    crCircle.lineWidth = 60;
  </Script>
  
  <!-- DocCreatorの設定 -->
  <DocCreator formPath="c:\DcHokenSample" documentPath="c:\DcHokenSample">
    <FileOutJob name="保険サンプル" fileName="sample">
      <Document type="BINARY" />
    </FileOutJob>
  </DocCreator>

  <!-- レコードセットの設定 -->
  <RecordSet name="保険データ" implementer="new GoogleSpreadsheetRecordSet()" >
    <Property name="userName" value="[ユーザ名]" />
    <Property name="password" value="[パスワード]" />
    <Property name="spreadsheetName" value="保険サンプル" />
  </RecordSet>
  
  <!-- レポートの設定 -->
  <Report name="レポート1">
    <Form name="保険サンプル" recordSet="保険データ" listener="new PageOutOnFormFilled()">
      <!-- ページ出力前に実行されるスクリプト -->
      <Script on="pageBefore">
        crCircle.startY = $('新疾病2') == "" ? 21560 : 22060;
        crCircle.startX = 5300;
        crForm.draw(crCircle);
        
        crCircle.startY = $('新成人病2') == "" ? 21560 : 22060;
        crCircle.startX = 7600;
        crForm.draw(crCircle);
      </Script>
      <Layout value="HokenSample.cfx" />
      <Field name="年" valueType="SCRIPT" value="today.year" />
      <Field name="月" valueType="SCRIPT" value="today.month" />
      <Field name="日" valueType="SCRIPT" value="today.day" />
      <Field name="主契約" value="主契約" />
      <Field name="回払1" value="回払1" />
      <Field name="定期保険特約" value="定期保険特約" />
      <Field name="保険料定期一括払制度" value="保険料定期一括払制度" />
      <Field name="年間削除" value="年間削除" />
      <Field name="新疾病1" value="新疾病1" />
      <Field name="新成人病1" value="新成人病1" />
      <Field name="不支払特定部位" value="不支払特定部位" />
      <Field name="回払2" value="回払2" />
      <Field name="被保険者" value="被保険者" />
      <Field name="保険契約者" value="保険契約者" />
      <Field name="次回払込保険料" value="次回払込保険料" />
      <Field name="契約番号" value="契約番号" />
      <Field name="親権者又は後見人" value="親権者又は後見人" />
      <Field name="月度" value="月度" />
      <Field name="支店名" value="支店名" />
      <Field name="支部" value="支部" />
      <Field name="取扱者" value="取扱者" />
      <Field name="証券番号1" value="証券番号1" />
      <Field name="証券番号2" value="証券番号2" />
      <Field name="新疾病2" value="新疾病2" />
      <Field name="新成人病2" value="新成人病2" />
    </Form>
  </Report>
</DocCreatorConf>

rdcコマンドで実行する

コンパイルしたクラスファイル(GoogleSpreadsheetRecordSet.class)を、rdcコマンドのインストールされているフォルダの下のclassesにコピーします。Google Spreadsheet APIで必要なライブラリ群も、同階層のlibにコピーします。rdcコマンドは自動でこれらのフォルダをクラスパスに追加しますので、手間いらずで便利です。

rdcコマンドで実行

rdcコマンドで実行(クリックで拡大)


無事に実行されましたね!

実行結果の帳票

実行結果の帳票(クリックで拡大)

まとめ

今回は、レコードセットクラスを自作することによってクラウド上のデータをデータソースとして使用する方法をご紹介しました。

この他にも、以前「Twitter APIで遊んでみよう(1)(2)」という記事でご紹介したように、XMLを返すAPIを持つサービスであれば、データソースをXMLとして使用することを検討してみても良いでしょう。(今回使用したGoogle Spreadsheet APIも、実際にはAtomで返してきているようなので、XMLとしても使用できると思います。。。試してませんが)。ご利用のサービスに合わせて柔軟に定義していただくと良いと思います。