如何在Java EE应用程序中接收电子邮件

15
很明显,通过JavaMail从Java EE应用程序发送电子邮件并不困难。我感兴趣的是最佳模式以接收电子邮件(主要是通知反弹)?我不感兴趣基于IMAP/POP3的方法(轮询收件箱)-我的应用程序应该对入站电子邮件进行响应
我能想到的一种方法是:
  • 保留现有的MTA(postfix在我的情况下为linux) -> 运维团队已经知道如何配置/操作它
  • 对于每个到达的邮件,生成一个Java应用程序来接收数据并通过JMS发送。我可以通过/etc/aliases中的条目做到这一点,例如myuser: "|/path/to/javahelper",其中javahelper调用Java应用程序,并传递STDIN。
  • MDB (Java EE应用程序的一部分)接收JMS消息,解析它,检测反弹消息并相应地处理。
另一种方法可能是:
  • 在Java EE应用程序容器的端口25上打开一个监听网络套接字。
  • 将SessionBean与套接字关联。Bean是Java EE应用程序的一部分,可以直接解析/检测反弹/处理消息。
  • 将现有的MTA作为入站中继保留,执行所有安全/垃圾邮件过滤,但将通过过滤器的电子邮件转发到Java EE应用程序容器的端口25上的myuser
我以前做过第一种方法(尽管是不同的语言/设置)。
从性能和(感知上的)清洁度的角度来看,我认为第二种方法更好,但它需要我提供一个适当的SMTP传输实现。此外,我不知道是否可能将网络套接字连接到Bean...
你有什么建议吗?你有关于第二种方法的详细信息吗?

你最终选择了哪种方法? - Theo
这个项目曾经暂停了很长一段时间。现在我又开始着手处理它了,但是我还没有实现接收部分。目前为止,我的计划是按照sleske的建议,通过IMAP定期扫描电子邮件收件箱。 - Hank
将Op 1中的JMS去掉,改用生成的curl/http发送到REST端点,这样就可以减少一个配置/复杂性部分(JMS/MDB)。 - alphazero
@alphazero - 谢谢,但JMS不是问题,OP是关于接收电子邮件的最佳模式。 - Hank
@hank - 已了解。指出JMS不是必需的。这肯定了模式1:使用(活动的)外部系统通过REST将邮件推送到JEE容器。 - alphazero
FYI:最终我采用了第二种方法:Java AS利用SubEtha SMTP实现了SMTP MTA。 - Hank
5个回答

8
我认为第二种方法并不“更加干净”。相反,它需要你实现标准MTA的一个重要部分,因此我建议不要使用这种方法。我认为轮询POP / IMAP服务器实际上是最干净的方法。你为什么决定不使用它?如果POP / IMAP服务器和你的服务在同一局域网(甚至在同一台机器上),则轮询将非常廉价。你可以每10-20秒进行一次轮询以获得最小的延迟时间,这应该不会引起问题。虽然这可能看起来有点技术上不太优雅,但你将使用标准的互操作协议(POP3 / IMAP),这样可以在避免重新实现邮件服务器的同时提供灵活性。
生成Java应用程序的方法似乎也可行,但我更喜欢轮询,因为:
a)你使用的接口(POP3 / IMAP)更加标准化,而你用于“插入”邮件服务器的接口将是特定于服务器的(在Unix上,你可以使用例如procmail,但你仍然依赖于特定的软件)
b)每封邮件启动一个单独的进程可能比轮询的开销更大。
顺便说一句:第三种方法是以某种方式将传入的邮件转储为文件到“传入”目录中(许多邮件服务器都可以这样做),然后轮询该目录。轮询目录甚至比轮询服务器更便宜。只需注意同步问题(读取半写邮件,几个并发读取器读取相同的邮件文件...)
我的经验:
我已经实现了使用两种方法的系统(IMAP轮询和生成单独进程)。对于处理人们发送到邮箱的数据的相当大的Java应用程序,我没有遇到任何与轮询相关的问题。生成进程的方法是为一个小Perl脚本而设计的;我只是这样做是因为这是一个简单的程序,每天只处理几封邮件,并且插入邮件服务器比在Perl中进行IMAP更容易。

@sleske:非常感谢您详细的回复!不幸的是,MTA今天没有提供POP3/IMAP设施...我会考虑哪一部分更容易。 - Hank
@Hank,你解决了吗? - Diego Ramos
1
是的,我们采用了第二种方法,在SubEtha SMTP之上实现了一个SMTP服务器。 - Hank

8
根据Java EE架构,"正确"的方式是使用JCA连接器来进行与SMTP服务器的入站/出站连接。
JCA连接器可以执行任何您想要的操作,包括线程和使用套接字连接到外部系统。实际上,JMS只是一种特殊类型的JCA连接器,它连接到JMS代理并将消息传递给"常规"MDB。然后,JCA连接器可以轮询SMTP服务器并将消息传递给自定义MDB。
关于JCA的最佳文档是使用J2EE连接器架构1.5创建资源适配器,它确实使用了电子邮件传递的示例。我建议您查看一下。该代码可以作为Java EE示例的一部分找到,并使用JavaMail,但我不知道它是否可以用于生产。
相关:

看起来相当复杂...为什么不只实现一个无状态的EJB,定期使用计时器服务检查新邮件呢?在性能、可靠性等方面,JCA方法是否带来了额外的优势? - Theo
@Theo 您确实可以使用EJB Timer或从 ServletContextListener 中派生一个线程。 我曾经使用过这两种方式来处理后台作业,它们可以正常工作,但是根据 JEE "哲学",此类基础设施应该由应用服务器本身或JCA连接器处理,而不应该成为应用程序代码的一部分。 同样,诸如 SMTP 设置之类的基础设施设置不应视为 env-entry 的 "业务" 设置。 但最终,这是设计纯度的问题,以及您想要完成工作的方式。 因此,请选择对您来说最简单的方式。 - ewernli
ewernli 在这里是100%正确的(事实上,这听起来像一个非常棒的潜在开源项目)。在容器管理的功能集之外做任何事情都不受 JEE 应用服务器 SLA 的保护。如果您开始跨容器分发,则这变得尤为关键。 - alphazero
1
我以前在生产中使用过JCA入站邮件连接器的示例。 (交互式营销,每天数百万封电子邮件)通过自己的代码审查进行了一些小的更改,主要是关于错误处理和输入验证。 例如,如果没有DKIM标头,则抛出异常。 它运行良好。 需要记住JCA规范涵盖许多类型的集成和连接场景,这就是为什么它看起来如此复杂的原因。 - Chris

6

对我来说,这是最好的解决方案。 - Hank

4

Apache James是个怎样的项目呢?

它实现了所有堆栈,并且通过类似Servlet的方式响应传入的邮件;它是开源的,完全符合Apache许可证(因此可以在商业产品中使用),并已经经过多年测试。


太棒了,我会研究一下!听起来好像可以实现我的基于事件的方法。 - Hank

2

使用一个ESB - 独立或内嵌。

"ESB将流程相关的概念,如转换和路由,引入到面向服务的架构中。ESB也可以为端点提供抽象。这促进了传输层的灵活性,并使服务之间的连接具有松散耦合和易于连通性."

例如 MULE "Mule ESB是最广泛使用的开源ESB。作为轻量级的集成平台和服务容器,Mule ESB提供了Web服务、消息路由、调解、转换和事务管理等功能,帮助开发人员在几小时内整合应用程序,而不是几周时间"

如何通过Mule接收电子邮件 http://books.dzone.com/articles/mule-messaging-chapter?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+zones%2Fsoa+(SOA+Zone)

以下仅为配置,以在收到消息时发送JMS消息(这就是你需要的全部) - 定义入站(imap/pop3/等)- 定义出站。

<imap:inbound-endpoint user="bob" password="password" host="localhost" port="65433" checkFrequency="3000"/> 
<jms:outbound-endpoint queue="my.destination" connector-ref="jmsQueueConnector"/>

您使用 imap:inbound-endpoint 的示例将通过 IMAP 检索电子邮件,这正是 OP 明确拒绝的。 - sleske
1
事实上,从文档中看,Mule似乎甚至不支持传入的SMTP,只支持传出的。因此,Mule可能对此无济于事... - sleske
虽然这不是原帖作者所寻找的,但我认为这更适合企业,因为它将电子邮件处理与应用程序解耦,应用程序可以为消息公开服务端点。 - Archimedes Trajano

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接