Chapter 17. 电子邮件

Seam现在包含了一个用于模板和发送邮件的可选组件。

邮件支持是由 jboss-seam-mail.jar 提供的。 这个JAR包包括用于创建邮件的mail JSF控件,以及 mailSession 管理组件。

examples/mail项目包括一份实用的email支持示例。该例子示范了恰当的打包方式,并且包含了一些当前支持的关键特性。

你也可以使用Seam的集成测试环境来测试你的mail程序,参见 Section 31.3.2, “Seam Mail集成测试”

17.1. 创建一条消息

为了使用Seam Mail,你并不需要完整的学习一门模板语言—一封邮件仅仅是一个facelet!【Facelet是用来建立JSF应用程序时的一个可供选择的表现层技术】

<m:message xmlns="http://www.w3.org/1999/xhtml"
    xmlns:m="http://jboss.com/products/seam/mail"
    xmlns:h="http://java.sun.com/jsf/html">

    <m:from name="Peter" address="[email protected]" />
    <m:to name="#{person.firstname} #{person.lastname}">#{person.address}</m:to>
    <m:subject>Try out Seam!</m:subject>

    <m:body>
        <p><h:outputText value="Dear #{person.firstname}" />,</p>
        <p>You can try out Seam by visiting
        <a href="http://labs.jboss.com/jbossseam">http://labs.jboss.com/jbossseam</a>.</p>
        <p>Regards,</p>
        <p>Pete</p>
    </m:body>

</m:message>

<m:message> 标签包装整个消息,并且通知Seam开始渲染一封邮件。 在这个 <m:message> 标签内,我们使用标签 <m:from> 来设置这个消息是来自谁, 使用标签 <m:to> 来标识发送者(注意我们就和在普通的facelet里一样使用EL),和 <m:subject> 标签。

<m:body> 标签包装邮件的主体。你可以像JSF组件那样将正规的HTML标签用在邮件主体内。

好,现在你已经有了email的模板,你将如何发送它呢? 在 m:message 的结尾,mailSession 将被调用,用于发送邮件。 所以,你所有要做的仅仅是请求Seam渲染这个视图:

@In(create=true)
private Renderer renderer;

public void send() {
    try {
       renderer.render("/simple.xhtml");
       facesMessages.add("Email sent successfully");
   }
   catch (Exception e) {
       facesMessages.add("Email sending failed: " + e.getMessage());
   }
}

假如:你输入了一个无效的email地址,将会抛出一个异常。该异常将被捕捉并显示给用户。

17.1.1. 附件

Seam中邮件添加附件的操作变得轻而易举。在处理文件时,它支持绝大多数的标准Java类型。

如果你想通过邮件发送 jboss-seam-mail.jar

<m:attachment value="/WEB-INF/lib/jboss-seam-mail.jar"/>

Seam将通过classpath加载文件,并将其附件加入到到邮件中。 默认情况下,它将被像 jboss-seam-mail.jar 一样加载; 如果你想为它添加别名,只需要添加 fileName 属性即可。

<m:attachment value="/WEB-INF/lib/jboss-seam-mail.jar" fileName="this-is-so-cool.jar"/>

你同样可以附加 java.io.Filejava.net.URL

<m:attachment value="#{numbers}"/>

也可以是 byte[] 或是 java.io.InputStream

<m:attachment value="#{person.photo}" contentType="image/png"/>

你会注意到对于 byte[]java.io.InputStream, 你需要指定附件的MIME类型(因为这两种文件不带此类信息。)

更好的是,你可以附Seam产生的PDF或任意标准的JSF视图,只需将你使用的普通标签用 <m:attachment> 封装起来即可:

<m:attachment fileName="tiny.pdf">
    <p:document>
        A very tiny PDF
    </p:document>
</m:attachment>

如果你想将多个文件添加到附件中(例如从数据库加载的一套照片),你只需要使用 <ui:repeat>

<ui:repeat value="#{people}" var="person">
    <m:attachment value="#{person.photo}" contentType="image/jpeg" fileName="#{person.firstname}_#{person.lastname}.jpg"/>
</ui:repeat>

如果你想直接显示一个附上的图片:

<m:attachment
    value="#{person.photo}"
    contentType="image/jpeg"
    fileName="#{person.firstname}_#{person.lastname}.jpg"
    status="personPhoto"
    disposition="inline" />
<img src="cid:#{personPhoto.contentId}" />

你可能会问 cid:#{...} 的作用是什么。 是这样的,IETF明确规定将这个标签加入作为你图片的src(源文件),当试着定位图片(Content-ID必须匹配)时,它就能够被查找到。— 多么神奇!

在访问状态对象之前你必须声明附件。

17.1.2. HTML/Text 交替部分

尽管现在绝大多数的邮件查看器都支持HTML格式的邮件,但还是有一些不支持,所以你可以在邮件体里添加一个无格式的文本作为替换。

<m:body>
    <f:facet name="alternative">Sorry, your email reader can't show our fancy email,
please go to http://labs.jboss.com/jbossseam to explore Seam.</f:facet>
</m:body>

17.1.3. 多个收件人

很多时候你希望向一个收件组(比如你的用户们)发送邮件。 所有的收件人标签可以被放在一个 <ui:repeat> 标签中:

<ui:repeat value="#{allUsers} var="user">
    <m:to name="#{user.firstname} #{user.lastname}" address="#{user.emailAddress}" />
</ui:repeat>

17.1.4. 多条信息

有时候,你需要向每一个收件人发送一条稍微有差别的信(例如:重设密码)。 最好的方法就是将整个信息放在 <ui:repeat> 标签中:

<ui:repeat value="#{people}" var="p">
    <m:message>
        <m:from name="#{person.firstname} #{person.lastname}">#{person.address}</m:from>
        <m:to name="#{p.firstname}">#{p.address}</m:to>
            ...
    </m:message>
</ui:repeat>

17.1.5. 模板

邮件模板示例显示(facelets模板)可以和Seam的mail标签很好的结合。

我们的 template.xhtml 包括:

<m:message>
   <m:from name="Seam" address="[email protected]" />
   <m:to name="#{person.firstname} #{person.lastname}">#{person.address}</m:to>
   <m:subject>#{subject}</m:subject>
   <m:body>
       <html>
           <body>
               <ui:insert name="body">This is the default body, specified by the template.</ui:insert>
           </body>
       </html>
   </m:body>
</m:message>

我们的 templating.xhtml 包括:

<ui:param name="subject" value="Templating with Seam Mail"/>
<ui:define name="body">
    <p>This example demonstrates that you can easily use <i>facelets templating</i> in email!</p>
</ui:define>

你也可以在你的邮件中使用facelet的源标签,但你必须将它们置于一个jar包中并放在 WEB-INF/lib 目录下 - 当使用Seam Mail从 web.xml 引用 .taglib.xml 并不可靠。 (因为如果你异步的发送你的邮件,Seam Mail无法访问到完整的JSF或Servlet上下文,所以并不知道 web.xml 的配置参数)

发送邮件时,如果你需要更多的配置Facelets或JSF,你需要重载Renderer组件,并且编程式地做配置工作 - 仅限于高级用户。

17.1.6. 国际化

Seam支持发送国际化的信息。默认情况下,使用JSF提供的编码,但也可以由如下的模板重写:

<m:message charset="UTF-8">
   ...
</m:message>

邮件内容、主题和收件人(和发件人)的名称都会被编码。通过设置模板的编码,你需要确认facelets是否使用了正确的编码方式来解析你的页面。

<?xml version="1.0" encoding="UTF-8"?>

17.1.7. 其它的标识头

有时候你会想在邮件上添加其他的头信息。Seam提供了一部分支持(请看 Section 17.5, “标签”)。 例如:我们可以设置邮件的重要程度,或着请求一个阅读回执。

<m:message xmlns:m="http://jboss.com/products/seam/mail"
    importance="low"
    requestReadReceipt="true"/>

另外你也可以通过使用 <m:header> 标签,为消息添加其它任意的头信息。

<m:header name="X-Sent-From" value="JBoss Seam"/>

17.2. 接收邮件

如果你正在使用EJB,你可以使用MDB(消息驱动Bean:Message Driven Bean)来接收消息。 JBoss提供JCA适配器 —mail-ra.rar 但是跟随JBoss发布的版本有一定的限制(某些版本没有做捆绑)。 因此我们建议采用跟随推荐的Seam发布的 mail-ra.rar(不在Seam包的 mail 目录)。 mail-ra.rar 应该被放置在 $JBOSS_HOME/server/default/deploy 目录下; 如果你正在使用的JBoss版本已经有了这个文件,就替换了它。

您可以向这样配置:

@MessageDriven(activationConfig={
    @ActivationConfigProperty(propertyName="mailServer", propertyValue="localhost"),
    @ActivationConfigProperty(propertyName="mailFolder", propertyValue="INBOX"),
    @ActivationConfigProperty(propertyName="storeProtocol", propertyValue="pop3"),
    @ActivationConfigProperty(propertyName="userName", propertyValue="seam"),
    @ActivationConfigProperty(propertyName="password", propertyValue="seam")
})
@ResourceAdapter("mail-ra.rar")
@Name("mailListener")
public class MailListenerMDB implements MailListener {

    @In(create=true)
    private OrderProcessor orderProcessor;

    public void onMessage(Message message) {
       // Process the message
       orderProcessor.process(message.getSubject());
    }

}

每一个接收到的消息都将导致 onMessage(Message message) 被调用。 大多数Seam的注释将会在MDB内部运行,但你不可以访问持久上下文。

在链接 http://wiki.jboss.org/wiki/Wiki.jsp?page=InboundJavaMailmail-ra.rar 上你可以找到更多的信息。

如果你没有使用JBoss,你依然可以使用 mail-ra.rar,或许你可以在你的程序服务器上找到类似的适配器。

17.3. 配置

为了在你的应用程序中能够使用电子邮件,要确保 jboss-seam-mail.jar 包含在 WEB-INF/lib 目录中。 如果你在使用JBoss AS,则使用Seam的邮件支持不需要做更多的配置工作了。 否则你可能需要确认你是否有JavaMail的API,一个可供使用的JavaMail API的实现(JBoss AS中使用的API和实现正如作为 lib/mail.jar 跟随Seam发布的包),和一份Java Activation Framework的拷贝(作为 lib/activation.jar 跟随Seam发布)。

Seam的Email模块需要Facelets作为视图技术。将来库的版本可能会添加对JSP的支持。另外,它需要用到seam-ui包。

mailSession 组件使用JavaMail就像与'真实的'SMTP服务器通讯。

17.3.1.  mailSession

如果你在使用JEE环境工作,可以通过JNDI查找可用的JavaMail Session,你也可以使用Seam配置好的Session。

Section 28.8, “与邮件相关的组件” 中有关于邮件会话组件属性的详细介绍。

17.3.1.1. 在JBoss AS中查找JNDI

JBossAS deploy/mail-service.xml 配置JavaMail会话捆绑到JNDI。 你需要修改默认的服务配置再应用到你的网络中。这里描述了更加详细的服务 http://wiki.jboss.org/wiki/Wiki.jsp?page=JavaMail

<components xmlns="http://jboss.com/products/seam/components"
    xmlns:core="http://jboss.com/products/seam/core"
    xmlns:mail="http://jboss.com/products/seam/mail">

    <mail:mail-session session-jndi-name="java:/Mail"/>

</components>

这里我们告诉Seam在JNDI中是通过 java:/Mail 来获得邮件Session的。

17.3.1.2. Seam配置会话

邮件会话可以通过 components.xml 配置来访问的。 这里我们告诉Seam使用 smtp.example.com 作为SMTP服务器。

<components xmlns="http://jboss.com/products/seam/components"
    xmlns:core="http://jboss.com/products/seam/core"
    xmlns:mail="http://jboss.com/products/seam/mail">

    <mail:mail-session host="smtp.example.com"/>

</components>

17.4. Meldware

Seam的邮件示例采用Meldware(来自 buni.org)作为邮件服务器。 Meldware是提供 SMTPPOP3IMAP、WebMail、共享日历和图形化的管理工具的于一身的软件; 它是作为一个JEE应用程序编写的,因此可以和你的Seam程序一起部署到JBoss上。

和Seam一起分发的Meldware的版本(在文件夹mail/buni-meldware)为了开发都被特别修改过 - 邮箱、用户和别名(邮件地址)在每次程序部署的时候创建。 如果你希望在产品中不仅仅使用Meldware发送邮件,建议你使用vanilla拷贝。 你也可以使用 meldware 组件来创建邮箱,用户和别名等。

<components xmlns="http://jboss.com/products/seam/components"
    xmlns:core="http://jboss.com/products/seam/core"
    xmlns:mail="http://jboss.com/products/seam/mail">

    <mail:mail-session host="smtp.example.com"/>

    <mail:meldware>
        <mail:users>
            <value>#{duke}</value>
            <value>#{root}</value>
	    </mail:users>
    </mail:meldware>

   	<mail:meldware-user name="duke" username="duke" password="duke">
   	    <mail:aliases>
   	        <value>[email protected]</value>
   	        <value>[email protected]</value>
   	    </mail:aliases>
   	<mail:meldware-user name="root" username="root" password="root" administrator="true" />
</components>

这里我们创建了两个用户,拥有两个邮件地址的 duke 和名为 root 的管理员。

17.5. 标签

邮件通过使用命名空间 http://jboss.com/products/seam/mail 的标签生成。 文档中在消息的根部通常应该有 message 标签,message标签使Seam准备生成一封邮件。

标准的facelets的模板标签可以如同往常一样地来使用。 你可以在主体内部使用任何JSF标签;如果需要访问外部资源(stylesheets、javascript),那么就要确认是否设置了 urlBase

<m:message>

邮件消息的根标签

  • importance — 低、正常或是高。默认是正常,这是设置邮件消息重要程度的标签。

  • precedence — 设置消息的优先级(例如:突出)

  • requestReadReceipt —默认是false,如果设置,将会添加阅读回执,阅读回执将会被发给 From: 地址。

  • urlBase — 如果设置,预设的 requestContextPath 将允许你在邮件中使用形如 <h:graphicImage> 的组件。

<m:from>

设置邮件的发件地址。每封邮件只允许有一个这样的值。

  • name — 邮件应该来自的名称。

  • address — 邮件应该来自的地址。

<m:replyTo>

设置回复地址给邮件。每封邮件同样只能有一个这样的值。

  • address — 邮件来源的地址。

<m:to>

添加一个收件人到邮件。有多个收件人时使用复合的<m:to>标签。 这个标签可以被安全的放置在重复标签<ui:repeat>之类中。

  • name — 收件人的名字。

  • address — 收件人的地址。

<m:cc>

添加抄送地址到邮件。有多个抄送地址时使用复合的<m:cc>标签。这个标签可以被安全的放置在重复标签<ui:repeat>之类中。

  • name — 收件人的名字。

  • address — 收件人的邮件地址。

<m:bcc>

添加一个秘文抄送人到邮件。有多个秘密抄送地址时使用复合的<m:bcc>标签。 这个标签可以被安全的放置在重复标签<ui:repeat>之类中。

  • name — 收件人的名字。

  • address — 收件人的邮件地址。

<m:header>

向邮件添加一个头(例如:X-Sent-From: JBoss Seam)。

  • name — 要添加的头的名字(例如:X-Sent-From)。

  • value — 要添加的头的值(例如:JBoss Seam)。

<m:attachment>

添加一个附件到邮件。

  • value — 要添加的附件:

    • String — 在classpath中一个 String 作为到文件的路径被解析。

    • java.io.File —一个指向 File 对象的EL表达式。

    • java.net.URL — 一个指向URL对象的EL表达式。

    • java.io.InputStream — 一个指向 InputStream 类型的EL表达式。 这种情况下,fileNamecontentType 都必须指定。

    • byte[] — 一个指向 byte[] 类型的EL表达式。 这种情况下,fileNamecontentType 都必须指定。

    如果值属性被省略:

    • 如果这个标签包含一个 <p:document> 标签,这个被描述的文档将会被生成并且附加到邮件上。 fileName 应该被指定。

    • 如果这个标签包含其它的JSF标签,将会通过它们生成HTML文档并附加到邮件。fileName应该被指定。

  • fileName — 指定可供使用的已经被附上的文件。

  • contentType —指定已附上的文件的MIME类型。

<m:subject>

设置邮件主题。

<m:body>

设置邮件主体。支持 alternative facet。 比如生成的一个HTML邮件可能包含针对不支持html的阅读器的备选的文本。

  • type — 如果设为 plain,将会生成一份简单文本邮件,否则将会生成一份HTML邮件。