dev2dev 首页 > 资源中心 > 技术文章
Dear John(专栏 #2):通过E-mail(SMTP)访问WebLogic Workshop Web服务
在详细讨论这个专栏之前,让我们再来回顾一下Web服务的定义。为了解释Web服务定义语言(WSDL)的规范,Web服务由一组相关的网络端点(地址)和关联的消息组成。可以推测,当一个适当的消息发送到端点时,一些有用的操作就会在端点触发。
Web服务调用消息一般根据简单对象访问协议(SOAP)来格式化。SOAP规范的早期版本将SOAP绑定到超文本传输协议(HTTP)中,并且现今存在的大多数Web服务实现都偏向于HTTP。但是Web服务理论上应该可以通过任何"标准的"Internet协议得到。
亲爱的约翰专栏本期回答了该问题:"如何通过简单邮件传输协议(SMTP)和Web服务进行通信?" 尽管SMTP(定义在RFC
821中)仅有短短20年的历史,但在Internet上从点到点传输的大部分电子邮件流依然是通过这个协议发送的。
WebLogic Workshop
7.0没有把内置的SMTP支持当作Web服务传输协议。不过,它把支持Java 消息服务(JMS)的协议当作Web服务传输协议。因此在这个例子中,我将安装自己的邮件服务器(使用Apache
Jakarta project的James,)并且将传入邮件消息的消息内容转发给WebLogicWorkshop内置的JMS队列jws.queue。如果一个格式正确的消息被发送给jws.queue,那么WebLogic
Workshop运行时间将会把它分派给Web服务对象,就像它是通过HTTP到达的一样。
我的例子中Web服务对象是EmailService.jws,它展示了helloAsync操作。helloAsync操作用JavaMail
API发送一个在WebLogic使用JavaMail的响应消息。
记住,你是通过发送XML(一般是SOAP)消息和Web服务进行通信的,本例中的作业就是把包含收件电子邮件的XML消息递送给目标Web服务。
传入邮件路由选择
正如前所述,我使用来自Apache
Jakarta项目的James邮件服务器接收传入的电子邮件消息。我选择James是因为它的安装和配置都出奇的简单。James邮件服务器很容易通过mailets扩充,mails是相对小且简单的Java类,它处理由James接收的电子邮件消息。在James的配置文件中创建一条传送消息的mailets管道。如果消息符合mailets的匹配规则,那么消息就传递到mailet服务方法进行处理。
和James一起安装的默认配置文件通过若干反垃圾邮件(anti-spam)mailets来路由消息,以免James服务器成为一个开放中继器(open
relay)。就像任何典型的邮件服务器一样,最后,消息存放在目标用户的邮箱中。我修改了这个文件配置使之不能存放消息,而是通过自定义的mailets(本人写的,权称之为ForwardToJMSQueue)路由所有消息。 下面是ForwardToJMSQueue
mailet的代码:
package com.bea.mailet;
import weblogic.jndi.Environment;
import org.apache.mailet.GenericMailet;
import org.apache.mailet.Mail;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueConnection;
import javax.jms.QueueSession;
import javax.jms.Queue;
import javax.jms.QueueSender;
import javax.jms.TextMessage;
import java.util.*;
public class ForwardToJMSQueue extends GenericMailet {
public void service(Mail mail)
{
try
{
// Use the WebLogic convenience Environment object to
// create an InitialContext
Environment env = new Environment();
Context ctx = env.getInitialContext();
// get the WebLogic Workshop QueueConnectionFactory
QueueConnectionFactory factory =
(QueueConnectionFactory)ctx.lookup
("weblogic.jws.jms.QueueConnectionFactory");
QueueConnection conn = factory.createQueueConnection();
QueueSession session = conn.createQueueSession(false, 0);
// look up the queue that is the destination for JWS requests
Queue queue = (Queue)ctx.lookup("jws.queue");
QueueSender sender = session.createSender(queue);
// get the content of the arriving e-mail message
MimeMessage mimeMsg = mail.getMessage();
String emailMsgStr = (String)mimeMsg.getContent();
// create a new TextMessage to send to the JWS JMS queue
TextMessage textMessage = session.createTextMessage(emailMsgStr);
// to target a particular JWS, pass its path as the value
// of the "URI" text property on the JMS message. In the
// scheme used by this mailet, the path to the JWS must
// arrive as the Subject: of the message.
textMessage.setStringProperty("URI", mimeMsg.getSubject());
// send the message
sender.send(textMessage);
// clean up
session.close();
conn.close();
}
catch(java.lang.Exception ex)
{
System.out.println("Exception in ForwardToJMSQueue mailet:");
ex.printStackTrace();
}
}
}
|
mailet最重要的几个方面是:
l
使用Java命名和目录接口(JNDN)获得JMS队列连接工厂和目标队列本身(jws.queue)。 l
从电子邮件消息中提取内容,该内容应该是Web服务方法调用消息(SOAP消息)。 l
创建一个包含SOAP消息的JMS
TextMessage对象。添加所需的"URI"消息属性,这个属性把期望的Web服务作为消息的发送目标。在这例子中,我期望每个到达的电子邮件消息的Subject:中包含Web服务的相关URL。对于我的例子中的Web服务来讲,它就是"/samples/EmailService.jws"。 l
发送消息。
消息一旦发送,WebLogic
Workshop分配器将收到消息并且调用Web服务。通过访问 edocs.bea.com的构建一个JMS客户端这个主题,来得到更多关于如何通过JMS调用WebLogic
Workshop Web服务的方法。
EmailService.jws Web服务
EmailService.jws
Web服务仅有惟一的方法--helloAsync,它是单任务的:发送电子邮件消息到方法参数提供的地址中。JavaMail
API是用来创建并发送发件电子邮件消息。
下面是EmailService.jws
Web服务的代码:
import weblogic.jws.control.JwsContext;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.Multipart;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMultipart;
import javax.naming.InitialContext;
import java.util.Date;
public class EmailService
{
/** @jws:context */
JwsContext context;
/**
* @jws:operation
* @jws:protocol jms-soap="true"
* @jws:conversation phase="start"
*/
public void helloAsync(String callbackAddress)
{
String dateStr = (new Date()).toString();
String addr = callbackAddress.trim();
try
{
// do JNDI lookup of mail session
InitialContext ctx = new InitialContext();
Session session = (Session)ctx.lookup("jws.mail.response");
// set up message headers
Message msg = new MimeMessage(session);
msg.setFrom();
msg.setRecipients(Message.RecipientType.TO,
InternetAddress.parse(addr, false));
msg.setSubject("EmailService.jws Response (" +
dateStr + ")");
msg.setSentDate(new Date());
// Content is stored in a MIME multi-part message
// with one body part
MimeBodyPart mbp = new MimeBodyPart();
mbp.setText(dateStr + ": " + addr);
Multipart mp = new MimeMultipart();
mp.addBodyPart(mbp);
msg.setContent(mp);
Transport.send(msg);
}
catch(Exception ex)
{
}
context.finishConversation();
}
}
|
Web服务最重要的几个方面是:
l
helloAsync方法具有@jws:protocol
jms-soap="true"属性,可以在WebLogic
Workshop中根据Web服务或方法逐个进行配置以支持协议和消息格式。默认情况下,from-get、form-post以及http-soap是启动的,而其它的协议/格式组合被禁用。我需要启用jms-soap组合,允许Web服务接收经由JMS到达的收件SOAP消息。为获得更多的关于@jws:protocol属性的内容,请参见edocs.bea.com的@jws:protocol Tag。 l
Web服务使用JNDI查找我在WebLogic服务器单独配置的JavaMail会话对象。当我配置JavaMail会话对象时,将它命名为JNDI的名称jws.mail.response。为了获得更多关于WebLogic服务器中JavaMail支持方面的信息,请参见edocs.bea.com的在服务器应用程序中使用JavaMail。
监视Web服务调用
如果你用WebLogic
Workshop开发过Web服务,那很可能是用Test View来测试该服务。Test
View是一个servlet,为所有WebLogic Workshop
Web服务提供测试工具。像调用Web服务的方法一样,每次调用的信息都添加到历史日志中,并且每个日志条目都被检查,以便了解到达的请求和响应结果。
你也许不知道Test View不管调用从哪里产生,都会显示它们。如果显示了一个Web服务的Test
View,然后从不同的客户端(比方说,在前一专栏中描述的Java客户端)调用该服务,这个调用将会显示在日志中(当你刷新浏览器时)而且可能被检测。
获得示例消息
除了监视Web服务调用之外,Test
View作为一种Web服务的方式,也被大量用来获得消息模板。 下面是EmailService.jws
Web服务的Test View的Test XML页面:
helloAsync方法的输入字段由带消息模板的Test View
servlet进行初始化(我已经用一个真实的电子邮件地址替换了参数占位符)。然而,只有SOAP消息的body才是必须发送到Web服务,以调用该服务的对象。当你从Test
View调用这种方法时,Test
View将会在SOAP消息中包装这个内容。但是如果想从外部客户端发送消息给Web服务,你需要整个SOAP消息。如果继续从Test
XML页面调用该方法,就可以看到完整的SOAP消息。下面所示的就是调用日志条目:
在服务请求部分,你可以看见发送到Web服务的完整的SOAP消息,只要从Test
View拷贝这个文本并把它作为来自你客户端测试消息的消息模块使用。本文的例子中把它作为电子邮件消息的正文使用。
此方法的局限性
本例的目的是演示使用HTTP作为Web服务调用消息的传输协议没有什么特别之处。对HTTP的历史的偏见源于两个事实:第一代Web服务实现是基于Web服务器和应用服务器,对于他们来说,HTTP是固有的协议,网络管理员舒适地管理HTTP的缺省端口80的安全,然而,管理由其它协议使用的任意端口的安全就不是那么轻松了。
本文描述的解决方案进行得不是很彻底,尽管在这个例子中,收件Web服务调用消息的路径恰好符合WebLogic
Workshop模型是因为SMTP 和
JMS都是固有的异步协议。经过JMS提交的消息从来不会进行异步响应,这是由消息队列的本性决定的:不存在返回到发送异步响应的客户端的路径(准确的说,存在响应客户端的异步响应,但是当消息被放置在队列中时该响应就立即发生,并且与void类型方法的返回值总是一样的,该返回值来自queue,而不是来自消息使用者)。电子邮件也是如此,它通过多级存储转发模式进行某些事务。因此,事实上,到达的消息经过了两次异步处理:一次是邮件服务器;而另一次是JMS分配器,而这并不表示出了问题。
然而对于发件方面,这种解决方案还是不理想的。我的示例Web服务构造了一条任意的消息,并将它发送到客户端特定的地址。换句话说,我在Web服务里面使用了一条支持路径,但在Web服务外面则采取了一条捷径。如果SMTP是一个完全支持Web服务调用的协议,WebLogic
Workshop将能够接收电子邮件地址作为一个回叫位置,并且将自动地发送错误(一般是收件消息中错误有关的SOAP故障)和回叫调用给指定的地址。
如果你对本文有什么意见和疑问,或对WebLogic
Workshop或Web服务有疑问,认为它们可以作为将来专栏的主题,请按照下面的地址发给我。
原文URL: http://dev2dev.bea.com/articles/DJ_002.jsp
| 作者简介 |
|
John Bley是Wily Technology 的软件工程师。他有丰富的 Java 编程和架构经验。为了撰写本文,他总结了 Wily 的企业客户的经验,Wily负责管理复杂的 J2EE 环境。 |
作者其它文章
|