アップクロス株式会社の西元です。
後編では、JasperReportsで帳票出力を行うプログラムについて説明していきます。
まずは、以下の3つのzipファイルをダウンロードして、開発用PCに展開してください。
帳票出力プログラム一式(JasperToPDF.zip)
※帳票出力プログラム・ソース一式には、Javaプログラムのコンパイル&実行に必要なライブラリ(jarファイル)が含まれていません。Javaプログラムを修正したりビルドし直す場合は自身で下記Jarファイルをlibフォルダにコピーしてください。
・jasperreports-5.6.1-project.zipに含まれる以下のJarファイル
jasperreports-5.6.1/lib/*.jar
jasperreports-5.6.1/dist/*.jar
・ipa_fonts.jar ・・・ 前編でiReportからエクスポートしたIPAフォントファイル
・cfx.jar ・・・ColdFusionのインストール先フォルダ wwwroot/WEB-INF/lib から取得
前編「ColdFusionからJasperReportsを利用する方法」で書いた通り、今回紹介する方法では、JasperReportsライブラリを使って帳票を作成する処理をDaemon(常駐プログラム)として実行し、ColdFusionからJava RMIを使用して帳票作成の処理を依頼します。
Javaで作成するソースファイルは以下の3ファイルです。帳票出力プログラム・ソース一式(JasperToPDF_src.zip)のsrcフォルダにあります。
いずれも100ステップに満たないコードですので、ソースを読めばすぐに分かるかと思いますが、以下に簡単な説明だけ書いておきます。
JasperToPdfServer.java
インターフェースなので、特に処理はありません。RMIでクライアントから呼び出されるdoPrintメソッドの定義があるだけです。
JasperToPdfServerImpl.java
JasperToPdfServerImplはJasperToPdfServerインターフェースのdoPrintメソッドを実装しています。また、RMIサーバにもなるためUnicastRemoteObjectを継承しています。
doPrintメソッドで行っている事は、
JasperToPdf.java
Java CFXタグとするため、CustomTagインターフェースのprocessRequestメソッドを実装しています。
processRequestメソッドでは、14〜17行目でCFXタグで指定されたパラメータ(属性)を受け取り、値の必須チェックやファイルの存在チェックを行った後で、RMIを使ってJasperToPdfServerのdoPrintメソッドをコールします。
これらをコンパイルしたクラスファイルのうち、帳票生成Daemon(常駐プログラム)となるJasperToPdfServer.jarは
JasperToPdfServer.classとJasperToPdfServerImpl.classで構成され、Java CFXタグの実体となるJasperToPdf.jarは
JasperToPdf.classとJasperToPdfServer.classで構成されています。

便宜上、この記事ではJasperToPdfServer.jarとJasperToPdf.jarを合わせて「帳票出力プログラム」と表記しています。
(1) 帳票出力プログラム一式をWebサーバにコピー
(2) 帳票生成Daemon(常駐プログラム)の実行
Webサーバでコマンドプロンプトを開き、カレントディレクトリを JasperToPDFServer.jar があるフォルダに移動してから、以下のコマンドを実行してください。

(3) Java CFXタグの登録
ブラウザを起動し、WebサーバのColdFusion Administratorにログインしてください。
左のメニューから「拡張機能>CFXタグ」を選択して、[Java CFXの登録]ボタンを押してください。


以上で、ColdFusionアプリケーションでcfx_JasperToPdfタグを記述すればJasperReportsの帳票出力が使えるようになります。
cfx_JasperToPdfタグの書式:
今回使用する帳票出力サンプル(JasperToPDF_sample.zip)は、『利用者一覧表出力画面で、性別と都道府県を選択して[印刷]ボタンを押すと、ユーザマスタ(USER_M)テーブルから対象ユーザを抽出して一覧表のPDFファイルを表示する』と言うものです。
※サンプルなのでエラー処理は考慮していません。また、抽出結果が0件の時は空白のPDFが出力されます。
ソースファイル構成
JasperToPDF_sample.zipのWebフォルダに以下のファイルがあります。
サンプル実行に必要なのは、このうち index.cfm, get_UsersList.cfm, UserList.jasper の3ファイルです。
このサンプルを実行するために、データベースに以下のテーブルを作成してください。
| 列ID | 列名称 | 型 | サイズ | NOT NULL | 備考 |
|---|---|---|---|---|---|
| ID | ID | VARCHAR2 | 8 | Yes | Primary Key |
| NAME | 氏名 | VARCHAR2 | 20 | Yes | |
| GENDER | 性別 | VARCHAR2 | 1 | M:男性/F:女性 | |
| BIRTH_DATE | 生年月日 | DATE | |||
| ZIP_CD | 郵便番号 | VARCHAR2 | 10 | ||
| STATE | 都道府県 | VARCHAR2 | 10 | ||
| TEL_NO | 電話番号 | VARCHAR2 | 20 | ||
| FAX_NO | FAX番号 | VARCHAR2 | 20 | ||
| メールーアドレス | VARCHAR2 | 60 | |||
| REGIST_DATE | 登録日 | DATE |
DROP TABLE USER_M;
CREATE TABLE USER_M (
ID VARCHAR2(8) NOT NULL,
NAME VARCHAR2(20) NOT NULL,
GENDER VARCHAR2(1),
BIRTH_DATE DATE,
ZIP_CD VARCHAR2(10),
STATE VARCHAR2(10),
TEL_NO VARCHAR2(20),
FAX_NO VARCHAR2(20),
MAIL VARCHAR2(60),
REGIST_DATE DATE,
CONSTRAINT USER_M_PK PRIMARY KEY (ID) USING INDEX
TABLESPACE USERS
)
TABLESPACE USERS;
COMMENT ON TABLE USER_M IS 'ユーザマスタ';
COMMENT ON COLUMN USER_M.ID IS 'ID';
COMMENT ON COLUMN USER_M.NAME IS '氏名';
COMMENT ON COLUMN USER_M.GENDER IS '性別';
COMMENT ON COLUMN USER_M.BIRTH_DATE IS '生年月日';
COMMENT ON COLUMN USER_M.ZIP_CD IS '郵便番号';
COMMENT ON COLUMN USER_M.STATE IS '都道府県';
COMMENT ON COLUMN USER_M.TEL_NO IS '電話番号';
COMMENT ON COLUMN USER_M.FAX_NO IS 'FAX番号';
COMMENT ON COLUMN USER_M.MAIL IS 'メールアドレス';
COMMENT ON COLUMN USER_M.REGIST_DATE IS '登録日';
DB/INS_USER_M.sql はテストデータを100件登録するINSERT文です。USER_Mテーブルを作成したら、このスクリプトを実行してください。
※USER_Mテーブルを作成したデータベースにColdFusionから接続するために、ColdFusion Administratorの「データとサービス/データソース」画面でデータソースとして登録しておいてください。
get_UsersList.cfm で行っている処理について、簡単に説明します。また、フォルダ名やデータソース名等はご自身のWebサーバの環境に合わせて変更してください。
[get_UsersList.cfm]
<cfprocessingdirective pageencoding="MS932">
<cfcontent type="text/html; charset=MS932">
<cfscript>
SetEncoding("url", "MS932");
SetEncoding("form", "MS932");
</cfscript>
<cftry>
<!--- jasperファイルのフルパス --->
<cfset jasperFile = "C:\www\sample\UserList.jasper" >
<!--- XML/PDF一時ファイルの作成フォルダ --->
<cfset tempDir = "C:\temp\" >
<!--- XML&PDFの一時ファイル名を取得 --->
<cfset xmlfile = GetTempFile(tempDir, "XML")>
<cfset pdffile = GetTempFile(tempDir, "PDF")>
<!--- 抽出条件セット(都道府県&性別) --->
<cfset cond = StructNew() >
<cfif isDefined("form.selState") >
<cfset cond.state = form.selState >
<cfelse>
<cfset cond.state = "" >
</cfif>
<cfif isDefined("form.selGender") >
<cfset cond.gender = form.selGender >
<cfelse>
<cfset cond.gender = "0" >
</cfif>
<!--- DBより帳票に出力するデータを取得 --->
<cfquery name="qry" dataSource="SAMPLE" result="res" >
SELECT ID, NAME, DECODE(GENDER, 'M', '男性', 'F', '女性', '不明') GENDER
, TO_CHAR(BIRTH_DATE, 'YYYY/MM/DD') BIRTH_DATE
, ZIP_CD, STATE, TEL_NO, FAX_NO, MAIL
, TO_CHAR(REGIST_DATE, 'YYYY/MM/DD') REGIST_DATE
, TRUNC(MONTHS_BETWEEN(SYSDATE, BIRTH_DATE)/12) AGE
FROM USER_M
WHERE (<cfqueryparam value="#cond.gender#" CFSQLType="CF_SQL_VARCHAR"> = '0'
OR GENDER = <cfqueryparam value="#cond.gender#" CFSQLType="CF_SQL_VARCHAR">)
<cfif cond.state neq "" >
AND STATE = <cfqueryparam value="#cond.state#" CFSQLType="CF_SQL_VARCHAR">
</cfif>
ORDER BY ID
</cfquery>
<!--- クエリーからXML書式のデータを作成 --->
<cfset wkColumnList = ListToArray(qry.columnList, chr(44), true) >
<cfset xmlStr = '<?xml version="1.0" encoding="Windows-31J"?>#chr(13)#' >
<cfset xmlStr = xmlStr & '<REPORTS>' & chr(13) >
<cfset xmlStr = xmlStr & '<PARAM>' & chr(13) >
<cfset xmlStr = xmlStr & chr(9) & '<SYSDATE>' & xmlformat(DateFormat(Now(), 'yyyy/mm/dd')) & '</SYSDATE>' & chr(13) >
<cfset xmlStr = xmlStr & chr(9) & '<TITLE>' & xmlformat('*** 利用者一覧表 ***') & '</TITLE>' & chr(13) >
<cfset xmlStr = xmlStr & '</PARAM>' & chr(13) >
<cfset xmlStr = xmlStr & '<ROWSET>' & chr(13) >
<cfloop query="qry" >
<cfset xmlStr = xmlStr & '<ROW num="#qry.currentRow#">' & chr(13) >
<cfloop index="i" from="1" to="#ArrayLen(wkColumnList)#" >
<cfset wkColumnName = wkColumnList[i] >
<cfset wkColumnData = qry[wkColumnName] >
<cfset xmlStr = xmlStr & chr(9) & '<#wkColumnName#>#xmlformat(wkColumnData)#</#wkColumnName#>' & chr(13) >
</cfloop>
<cfset xmlStr = xmlStr & '</ROW>' & chr(13) >
</cfloop>
<cfset xmlStr = xmlStr & '</ROWSET>' & chr(13) >
<cfset xmlStr = xmlStr & '</REPORTS>' >
<!--- XMLデータを一時ファイルに保存 --->
<cffile action="write" file="#xmlfile#" output="#xmlStr#" charset="Windows-31J">
<!--- 帳票作成処理の実行 --->
<CFX_JASPERTOPDF JASPER="#jasperFile#" DATA="#xmlfile#" XPATH="//ROW" OUT="#pdffile#">
<!--- XMLファイルの削除 --->
<cffile action="delete" file="#xmlfile#">
<!--- PDFファイルのダウンロード --->
<cfheader name="Content-Disposition" value="attachment; filename=利用者一覧表.pdf">
<cfcontent type="application/pdf" file="#pdffile#" deletefile="yes">
<cfcatch type="any">
<cfoutput>
<cfdump var="#cfcatch#">
</cfoutput>
</cfcatch>
</cftry>
ブラウザを起動して、Webサーバに置いたindex.cfmにアクセスしてください。
例)ドキュメント・ルートが C:\www で、帳票出力サンプルを C:\www\sample フォルダに置いた場合。


今回紹介した帳票出力プログラムはPDFファイルを作成する機能しか持っていませんが、JasperReportsは様々な出力形式に対応しています。JasperPrintServerImpl.javaに手を加える事で、PDF以外の出力形式にも対応できますし、直接プリンタへ出力する事もできるようになります。
また、今回はコマンドプロンプトで常駐プログラムを実行しましたが、Apacheのcommons-daemonを使用するとWindowsサービスとして実行する事も可能になります。
その他、RMIでの受け渡しをファイルパスではなくデータストリームに変更すればColdFusionと帳票作成処理を別サーバに分離する事も出来そうですし、JSON等他のデータソース形式を使えるようにするのも良いかもしれません。
Java開発に慣れている方なら、意外と簡単に作れるのではないかと思います。
それと、JasperToPdf.javaはJava CFXタグのサンプルにもなるかと思います。ColdFusionのCFMLとCFScriptだけで実現するのが困難だったり、複雑すぎてパフォーマンス的に難がある処理等はJava CFXタグで実装する事を検討しても良いのではないでしょうか。

