このシリーズの第2回目では、JavaMailとXSLTを使ってe-zineの発行を自動化する方法を扱います。第1回では、DocBook XML文書をテキスト形式に変換し、e-mail発行の厳しい要件を満たす方法を学びました。そこでは3つのステップが関係していました。
- XSLTを使ってDocBookをテキスト・マークアップ言語へすぐに変換する
- カスタム・メードのJavaアプリケーションを使ってテキスト・マークアップ言語をプレーン・テキストへ直す
- さまざまなSAXフィルターを利用してテキストを整形する
このアプローチの利点の1つとして、複雑なプロセスをいくつかのステップに区分できることをあげられます。XSLTスタイル・シートを使うことで柔軟性が非常に高まります。DocBook以外のボキャブラリー向けに書かれた文書を発行すると決めている場合は、スタイル・シートを変更するだけで済むわけです。さらに、SAXフィルターを使って変換コンポーネントをモジュール化できるため、読みやすくなりますし、維持管理も簡単です。
第2回目は、JavaMail (標準Java言語e-mail API) を使って、どのようにこのプロセスをラップし、ネットワークでe-zineを送信するか説明します。また、プロセスではSAXイベント処理についてもう一度触れます。そして結論部分では、大規模なアプリケーションでXSLT処理をラップする方法を紹介します。第1回の図1には、すべての部品同士の相関関係が示されています。
先に進む前に、JavaMailについて思い起こしてみましょう。あなたがすでにJavaMailに精通しておられるなら、次のセクションにスキップしても構いません。
E-mailは、これからも最もポピュラーなインターネット・アプリケーションの1つであり続けるでしょう。e-mailというと必ずEudora、Outlook、またはNetscapeといったe-mailクライアントと関連付けて考えてしまいがちですが、多くのアプリケーションには、e-mailを自動的に送信したり取り出したりする機能があります。最近、オンラインでものを購入したときのことを思い出してみてください。オーダーを完了して数分以内に、確認のe-mailが届けられたと思います。これは電子商店によって自動的に送信されたものです。これは人間が関係しておらず、Eudoraなどのe-mailクライアントを必要としません。
Javaアプリケーションでしばしばe-mailの送受信を行なう必要性を見て取ったSunは、e-mailサービス用の標準APIとしてJavaMailを開発しました。JavaMailを使うと、Javaアプリケーションでe-mailを送信したり、メールボックスを (e-mailクライアントを使わずに) 直接にチェックすることができます。JavaMailはSun JDKとは別にダウンロードする点にご注意ください (参考文献を参照)。
リスト1 にリストされているSendMessage.javaは、JavaMailを説明するための簡単なアプリケーションです。javax.mail およびjavax.mail.internet パッケージをインポートしている点にお気づきでしょう。
e-mailを送信するときの最初のステップは、Session.getDefaultInstance() を使って、Session オブジェクトを要求することです。Session.getDefaultInstance() はProperties オブジェクトを持ち、これには、少なくともmail.smtp.host プロパティーが必ず含まれます。このプロパティーは、ご使用のSMTPホストを指していなければなりません。そうでないと、何も機能しません (サイドバーのSMTPホストを参照)。
次に、Message を作成し、そのいくつかのプロパティーをセットします。プロパティーとしては、送受信側のアドレス (注意点として、複数の受信側があっても構いませんが、送信側は1つだけです)、件名、日付、およびメッセージの本文などがあります。
最後のステップは、Transport.send() を使って、メッセージをネットワークで送信することです。SendMessage.javaでは、簡単なテキストe-mailが作成されるだけです。次のセクションでは、いわゆるマルチパートe-mailをどのように作成するか説明しましょう。
リスト1. SendMessage.java
package com.psol.xslist;
import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
public class SendMessage
{
public static final void main(String[] args)
{
try
{
if(args.length < 5)
{
System.out.println("java com.psol.xslist.SendMessage" +
" from@domain.com to@domain.com mailhost.domain.com" +
" subject \"mail content\"");
return;
}
Properties props = System.getProperties();
props.put("mail.smtp.host",args[2]);
Session session = Session.getDefaultInstance(props);
Message message = new MimeMessage(session);
InternetAddress from = new InternetAddress(args[0]);
InternetAddress to[] = InternetAddress.parse(args[1]);
message.setFrom(from);
message.setRecipients(Message.RecipientType.TO,to);
message.setSubject(args[3]);
message.setSentDate(new Date());
message.setText(args[4]);
Transport.send(message);
}
catch(MessagingException e)
{
System.err.println(e.getMessage());
}
}
}
|
第1回で紹介したJavaMailとテキスト・フォーマッターで武装すれば、すぐにでもe-zineを送信できます。SendMessage.javaのsetText() を更新して、第1回で紹介したテキスト変換の結果を使うことができます。ただし、もっと良い方法があります。具体的に言うと、multipart/alternative e-mailを使うことができます (サイドバーのmultipart/alternative を参照)。
ここでは、リスト2 の中のconfig.xml のようなXML構成ファイルに、e-mail情報を保管することにします。このファイルの構造は次のとおりです。
- ・
cfg:emailは構成ファイルのルートです。これには、SMTPホストに対して1つの属性smtpがあります。テストの前に属性のパラメータを変更する必要があるため、リストの中の属性を太字にしておきました (サイドバーのSMTPホストを参照)。 - ・
cfg:headerには、送受信側のアドレスが示されると同時に、属性としてのe-mail件名が示されます (それぞれは、from、toおよびsubjectです)。テストのときには、自分自身のe-mailアドレスを使ってください。 - ・
cfg:bodyは、source属性の中で、ソースXML文書を示します。 - ・
cfg:textとcfg:partは、cfg:bodyで囲まれていて、さまざまな本文部分を作成する方法を制御します。cfg:textは、第1回で紹介されたテキスト変換を使い、テキスト本文部分を作成するのに対し、cfg:bodyは、簡単なXSLTスタイル・シートに適用され、HTMLバージョンの本文を作成します。
このような要素はすべて、http://www.psol.com/xns/xslist/config 名前空間にあります。思い出していただきたいのですが、XML名前空間はURLの形式になっていますが、IDとして使われているに過ぎません。ですから、ブラウザーで名前空間を指定してみても、Webサイトを見つけることはできません。
最終的に、構成ファイルに受信側アドレスが1つだけ示されていることを確認してください。なぜ1つだけで良いかというと、ほとんどの人はメーリング・リストを配信するときに、TopicaやSparkLIST (参考文献を参照) などの特別なサーバーを経由しますが、そのサーバーが購読契約およびその解除を管理するからです。メーリング・リスト・サーバーにe-zineを送信すると、ただちにすべての読者へ配信されます。
このサンプル・コードを使ってみる前に、smtp、from、およびto 属性を必ずあなた自身のものに変更してください。
リスト2. 仮メーリングのconfig.xml
<?xml version="1.0"?>
<cfg:email xmlns:cfg="http://www.psol.com/xns/xslist/config"
smtp="mailandnews.com">
<cfg:header from="username@mailandnews.com"
to="nobody@example.com"
subject="XSL -- First Step in Learning XML"/>
<cfg:body source="article.xml">
<cfg:text styleSheet="text.xsl" contentType="text/plain"/>
<cfg:part styleSheet="html.xsl" contentType="text/html"/>
</cfg:body>
|
構成ファイルの処理は、リスト3 のConfigHandler.javaで行われます。これはSAXDefaultHandler を継承していて、パーサーとのインターフェースをインプリメントしています。第1回で予告したように、このセクションではSAXイベント処理について検討します。SAXイベント処理に精通しておられるならば、ご自由にこのセクションを飛ばすことができます。
SAXはイベント・ベースのAPIなので、XMLファイルの処理を進めてゆくときに、パーサーは (AWTイベントに似た) イベントをアプリケーションへ送信します。アプリケーションでは、イベントを無視することもできますし、処理することもできます。ほとんどのイベント・ハンドラーでは、少なくとも次のイベントを処理します。
- ・パーサーが開始タグを読み込むときに呼び出す
startElement() - ・パーサーが終了タグに達したときに呼び出す
endElement() - ・パーサーが文字データを認識するときに呼び出す
characters()。ConfigHandler.javaの特徴は、characters()イベントを無視することです。
SAXでは他のイベントも多数定義していますが、それらは主要なイベントではありません。他のSAXイベントの詳細は、参考文献を参照してください。
SAXを理解するために、XML文書は要素の階層だということを思い出してください。つまり、ツリー構造です。パーサーはこのツリーを読み取り、イベントを使ってアプリケーションへ通知します。パーサーは、startElement() を呼び出して、新しいブランチを見つけたことをアプリケーションへ通知します。endElement() は、現在のブランチの終了を示します。
SAXハンドラーを作成することの難しい点は、特定の要素 (cfg:body など) を処理するコードが、複数のイベント (startElement() やendElement() など) に分けられることです。
さらに、パーサーはコンテキスト情報を戻してこないので、イベントを相互に対応させるのはアプリケーションになります。ConfigHandler.javaでは、構成内の読み取られた部分を知るために、state 変数を使います。
文書の作業を進めてゆくときに、ConfigHandler.javaはマルチパートのMessage を作成し、適切なスタイル・シートを適用して、異なるバージョンのメッセージ (テキストとHTML) を作成します。文書の最後にきたら、ConfigHandler.javaはメッセージを送信します。
異なるスタイル・シートを使うことにより、さまざまなバージョンの本文を好きなだけ作成できるので、このメカニズムは非常に柔軟なものだと言えるでしょう。3つ目のバージョンが (たとえば、WML (Wireless Markup Language) で) 欲しいという場合は、別のスタイル・シートを1つ指定し、構成ファイルをそれに適合させるだけでよいのです。
config.xml (リスト2) は、リスト4 で示されているhtml.xsl スタイル・シートを参照します。スタイル・シートhtml.xsl は、典型的なスタイル・シートの例で、第1回で紹介したarticle.xml をHTMLへ変換します。
リスト4. html.xslがarticle.xml (このシリーズの第1回のもの) をHTMLへ変換する
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<head>
<title><xsl:value-of
select="article/articleinfo/title"/></title>
</head>
<xsl:apply-templates/>
</html>
</xsl:template>
<xsl:template match="article">
<body>
<xsl:apply-templates/>
</body>
</xsl:template>
<xsl:template match="articleinfo/title">
<h1><xsl:apply-templates/></h1>
</xsl:template>
<xsl:template match="sect1/title">
<h2><xsl:apply-templates/></h2>
</xsl:template>
<xsl:template match="ulink">
<a href="{@url}"><xsl:apply-templates/></a>
</xsl:template>
<xsl:template match="emphasis">
<b><xsl:apply-templates/></b>
</xsl:template>
<xsl:template match="para">
<p><xsl:apply-templates/></p>
</xsl:template>
<xsl:template match="author">
<p>by <xsl:value-of select="firstname"/>
<xsl:text> </xsl:text>
<xsl:value-of select="surname"/></p>
</xsl:template>
</xsl:stylesheet>
|
最後の欠落している部分は、main() メソッドです。これは、リスト5 のXslList.javaにあります。ConfigHandler.javaは、e-mailの作成と送信を受け持つものですので、main() はとてもシンプルです。これは、SAXパーサーを作成し、ConfigHandler.javaをイベント・ハンドラーとして登録し、parse() を呼び出して構文解析を立ち上げます。
もちろん、パーサーが文書をデコードするときにはたくさんのことが生じます。(メッセージの作成や送信など) どのようなヘビー・デューティーな処理が行われるときも、パーサーはいつもConfigHandler.javaへイベントを送信します。parse() が戻ったときには、2つのスタイル・シートが適用されていて、e-mailが送信されているのです!
リスト5. XslList.java
package com.psol.xslist;
import java.io.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
public class XsList
{
protected static final String
PARSER_NAME = "org.apache.xerces.parsers.SAXParser";
public static void main(String[] args)
{
try
{
if(args.length < 1)
{
System.out.println("java com.psol.xslist.XsList input.xml");
return;
}
ConfigHandler configHandler = new ConfigHandler();
XMLReader parser =
XMLReaderFactory.createXMLReader(PARSER_NAME);
parser.setFeature("http://xml.org/sax/features/namespaces",
true);
parser.setContentHandler(configHandler);
parser.parse(args[0]);
}
catch(IOException e)
{
System.err.println(e.getMessage());
}
catch(SAXException e)
{
System.err.println(e.getMessage());
}
}
}
|
私がこの記事を準備したとき、3つの目標がありました。まず、ブラウザーを使わないXMLアプリケーションを作成しようと思いました。ブラウザーでXMLを使うことは、アプレットでJavaを使うようなものです。この記事をお読みになり、便利なXMLではブラウザーを必要とはしないということを、はっきりと理解していただけたなら幸いです。
次に、XSLTとSAXイベント処理を組み合わせることが、それぞれの良さを合せたものよりもどれほどすばらしいか示したいと思いました。第1回では、SAXハンドラーに送られるマークアップを簡単にするため、どのようにXSLTを使うかを理解しました。
最後に、私は、ほとんどのプログラマーが例を学習することで理解を深めることを知っていましたので、完全なアプリケーションを提示したいと思いました。ついでながら、このアプローチを気に入っていただけたのであれば、拙著のApplied XML Solutions (参考文献を参照) も気に入っていただけるでしょう。ここには、別の例が8つ掲載されています。
それでは、今度はご自分で試してみてください。このアプリケーションは、e-zine発行という特定の作業で役に立ちますが、示されたXMLテクニック (XSLT、DocBook、SAXフィルター、JavaMailなど) は、e-zine発行だけに限定されているわけではありません。コードを学習し、ご自分の作業でどのように役立つかをご覧になってください。
- Dr. Ralph Wilson氏が、e-mailクライアントの概要について書いています。HTML e-mailのサポートについてもレポートしています。
- メーリング・リスト・サーバーの契約を必要としているのであれば、2つのポピュラーなベンダー、Topica およびSparkLIST をお調べください。これらは、購読契約およびその解除を含めたリストを管理します。また、David Strom氏のWeb Informant もご覧ください。そこでは、メーリング・リスト・サービスの最新比較情報が載せられています。
-
JAXP (Java API for XML) は、SAX (XML構文解析) とTrAX (XSLT変換) を統合したものです。このサイトには、Stuart Halloway氏による、SAX tutorial があります。
-
SAXの詳細は、David Megginson氏のWebサイトをご覧ください。Megginson氏は、SAX APIの保守担当者です。
-
JavaMail は、e-mail用の標準Java APIです。(JDKとは別にダウンロードするものです。)
-
この2回にわたるシリーズの第1回目では、XMLからテキストへの変換について論じられています。また、このアプリケーションのアーキテクチャーについても述べられています。
- この記事を気に入っていただけたのであれば、この記事の著者によるApplied XML Solutions をご覧になると、さらに良質な8つの例があります。

Benoit Marchal氏は、ベルギーのナミュールを拠点にしたコンサルタントおよび著述家です。彼の著作には、XML by Example(Que社、邦訳: インプレス社「実例で学ぶXML」) と、Applied XML Solutions とがあります。Gamelanにコラムを寄せています。 Ben氏は、1998年にPineapplesoft Linkを設立したときに、e-zineの発行についてじかに学びました。www.marchal.com のサイトで、彼のe-zineを購読したり、彼の最新プロジェクトを詳しく見ることができます。