本文讨论WebLogic Server 9(下简称WLS9)中的新特性:Java Web Service (JWS)。WLS9的Web Services子系统实现了J2EE 1.4中的Web Services的新标准: Enterprise Web Services 1.1 specification (JSR-921)。该规范定义了标准的J2EE Web Service打包格式,部署模块以及运行时服务。
JSR-921是J2EE 1.3中Web Services规范JSR-109的1.1维护版本。在Web Services 1.1中描述了J2EE Web Service的业务逻辑有两种实现方式:一种是使用Web容器中的JavaBean,另一种是使用EJB容器中的无状态Session Bean。与旧的Web Services开发方式不同,BEA建议使用JWS这种基于注释的Web Services编程方式以简化开发。这种编程方式的开发过程大概是这样,利用JDK5的新特性元数据注释(JSR-175),在编写上述的Java Bean或EJB的Bean类时在业务逻辑代码中添加一些特殊注释,然后用ANT的一些扩展任务可以生成一些相关JAVA类代码(主要是EJB的存根、桩)和配置文件,最后自动编译这些类代码并自动打包。所以可以说JWS方式是Web Services新标准的核心。JWS文件实际就是普通的.JAVA文件(扩展名依旧是.JAVA),通过添加注释来制定WS的特性。这些注释在JSR181中有完整的说明。如果Web Service用Java Bean类实现,自动打包后会包含web.xml,weblogic.xml, webservices.xml,weblogic-webservices.xml,JAX-RPC数据类型映射文件和WSDL等,并被打包成标准Web应用的WAR。如果用EJB实现,则会包含ejb-jar.xml和weblogic-ejb.jar.xml等,并被自动打包成标准EJB的JAR。下图描述了JWS文件生成可部署的J2EE模块的详细过程。后面我们会用两个例子详细说明如何开发、部署基于Java Bean和基于EJB的Web Services。
JSR181与JSR175介绍
我们先来看一段典型的使用JSR175特征的代码。
package webservices.jsr181.pojo;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService(name="StringChangerPort", targetNamespace="http://wwtt.bea.com")
public class StringChanger {
@WebMethod()
public String toUpper(String upperReqString)
{
……
这个类的源代码和普通的JAVA代码没太多区别,但和普通JAVA代码不同的是这里面还包含了很多以@开头的代码,或者称为注释,也就是在Java 5中的新语法,JSR-175。JSR-175规范只是定义这种语法规则,而用这种语法来如何将这个类生成一个Web Service接口,则需要由JSR-181规范来决定。所以这些JSR-175的注释才真正决定了这个Web Service的行为和外观。
也许你会觉得这种语法比较奇怪,有些像普通java方法的语法,只是前面多了一个@。实际在JSR175规范的早期,该语法并不是像现在这样的。如果按早期写法,@WebService()会该写为:
/**
* @WebService(......)
*/ 后来出于简化,成了现在的样子。如果使用Weblogic Platform 7.x或8.x产品,会在许多其专有的代码文件中看到这种格式的注释。由于撰写本文时Weblogic Platform 9还没有发布。发布后是否那些专有代码会使用基于JSR-175的语法还不得而知,但可以确定的是在Weblgic Server 9中这种语法已经被全面支持了,无论在Web Service中,还是在EJB中。
事实上JSR 181是BEA提出的用于加速Web Services开发的一种基于注释驱动的编程模式,并被批准纳入到J2EE 1.5标准。JSR181提供了一种简单的Web Service开发编程模型和标准的编译及部署方式。只需要编写JSR-175风格的注释就可以制定WSDL,消息产生属性,安全认证方式,以及特定的消息头。
练习的预备工作
准备必要软件。
进行练习前需要预先安装WLS9正式版,可以到BEA官方网站上免费下载。本练习可在能安装WLS9的任何操作系统上完成,为了方便起见,文中仅以在Windows XP上开发为例。此外本文中代码的开发环境为Eclipse 3.1及对应的Lomboz,当然如您所愿任何其他支持ANT的开发环境都是可以的。
配置Weblogic Server。
准备好上述软件后,首先用配置创建一个新的WLS Domain。记得用户名和密码要都设为weblogic,端口为7001。创建好后启动该Domain,并用浏览器启动相应的Console,用帐户weblogic/weblogic登录进入。
配置Eclipse。
如果希望Eclipse的配置尽量吻合本文的环境,可参考我的另一篇文章:“使用Eclipse加速Weblogic Server开发”的第一步到第二步。配置好后在Eclipse的File->New Project中选取Java Project,在Project Name中将我们这个练习的名字设为JSR-181 Web Services或任何你喜欢的名字,JDK Compliance中选择“Use a project specific compliance 5.0”,其余保持默认。一个符合我们需要的Project就建好了。
修改脚本文件。
本练习所有的构建过程都由ANT来完成,因此我们需要添加一些ANT的脚本配置文件。属性文件,宏定义文件可以完全参照《使用Eclipse加速Weblogic Server开发》,因此只需要吧那里面的那三个文件拿过来用就可以了。
需要修改的是build.xml文件,这里定义的属性主要是为后面两个例子设置一些路径。
<?xml version="1.0"?>
<project name="WLS v9 JSR-181 Web Services" basedir=".">
<property file="wls-build-tools/weblogicServer.properties" />
<import file="wls-build-tools/weblogicServer.macrodef" />
<taskdef file="wls-build-tools/weblogicTasks.properties" classpathref="jwsbuild.class.path" />
<property name="apps.src.dir" location="." />
<property name="apps.dest.dir" location="${temp.dir}" />
<property name="clients.src.dir" location="." />
<property name="clients.dest.dir" location="${temp.dir}" />
<property name="clients.package.name" value="client.test"/>
<property name="clients.package.path" value="client/test"/>
<property name="jbApp.context.name" value="jb" />
<property name="jbApp.package.name" value="webservices.jsr181.pojo"/>
<property name="jbApp.src.dir" location="${apps.src.dir}/webservices/jsr181/pojo"/>
<property name="jbApp.jwsSource.file" value="StringChanger.java" />
<property name="jbApp.dest.dir" location="${apps.dest.dir}/${jbApp.context.name}" />
<property name="jbApp.service.name" value="StringChangerService" />
<property name="jbApp.wsdl.url" value="http://${domain.address}:${domain.port}/${jbApp.context.name}/${jbApp.service.name}?WSDL" />
<property name="jbApp.deploy.name" value="WLS v9 JSR-181 Javabean Web Service" />
<property name="ejbApp.context.name" value="ejb" />
<property name="ejbApp.package.name" value="webservices.jsr181.ejb"/>
<property name="ejbApp.src.dir" location="${apps.src.dir}/webservices/jsr181/ejb"/>
<property name="ejbApp.jwsSource.file" value="PurchaseOrder.java" />
<property name="ejbApp.dest.dir" location="${apps.dest.dir}/${ejbApp.context.name}" />
<property name="ejbApp.service.name" value="PurchaseOrderService" />
<property name="ejbApp.wsdl.url" value="http://${domain.address}:${domain.port}/${ejbApp.context.name}/${ejbApp.service.name}?WSDL" />
<property name="ejbApp.wsdl.local" value="jar:file:${apps.dest.dir}/${ejbApp.context.name}/PurchaseOrder.jar!/META-INF/${ejbApp.service.name}.wsdl" />
<property name="ejbApp.deploy.name" value="WLS v9 JSR-181 EJB Web Service" />
<path id="jwsbuild.class.path">
<fileset dir="${weblogic.home}/server/lib">
<include name="weblogic.jar" />
<include name="xbean.jar" />
</fileset>
<fileset dir="${jdk5.home}/lib">
<include name="tools.jar" />
</fileset>
</path>
<path id="javabuild.class.path">
<pathelement path="${clients.dest.dir}" />
<fileset dir="${weblogic.home}/server/lib">
<include name="webserviceclient.jar" />
</fileset>
</path>
<!--后面的练习中我们将根据需要在此添加一些target。-->
</project>
如何快速查看例子的结果,而跳过开发环节。
做好1到4步骤就可以按照后面几节所述完成练习。但如果想快速看一下练习结果,将本文附件solution包括所有源代码。解压到您磁盘任何位置,用Eclipse创建好一空的名为JSR-181 Web Services的Project后,用solution解出的JSR-181 Web Services文件夹替换这个Project的文件夹。最后在Eclipse的Package Explorer中刷新一下,就浏览已经开发配置好Eclipse Project代码了。修改项目的wls-build-tools路径下weblogicServer.properties中的WLS和JDK环境变量,然后就可以执行ANT脚本中的任务来编译,部署,测试等步骤。
开发基于JavaBean的Web Services
基于JavaBean的Web Servics是最简单的一种方式。我们只需要在普通的JavaBean码中添加一些符合JSR181的注释,在build.xml中调用WLS提供的扩展ANT Task,就可以完成一个完整的Web Services。
创建一个Java Bean。
在Project的根目录下创建一个类名为StringChanger,包名为webservices.jsr181.pojo,写入如下内容:
package webservices.jsr181.pojo;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import weblogic.jws.WLHttpTransport;
@WebService(name="StringChangerPort", targetNamespace="http://bea.com")
@SOAPBinding(style=SOAPBinding.Style.RPC, use=SOAPBinding.Use.ENCODED)
@WLHttpTransport(contextPath="jb", serviceUri="StringChangerService")
public class StringChanger {
@WebMethod()
public String toUpper(String upperReqString)
{
String upper = upperReqString.toUpperCase();
System.out.println("toUpper Request: " + upperReqString);
System.out.println("toUpper Reply: " + upper);
return(upper);
}
@WebMethod(operationName="toLower")
public String toLowerCase(String lowerReqString)
{
String lower = lowerReqString.toLowerCase();
System.out.println("toLowerCase Request: " + lowerReqString);
System.out.println("toLowerCase Reply: " + lower);
return(lower);
}
}
这个类是用于实现我们这个Web Services模块的业务逻辑,以及描述这个Web Service接口。该类中包含两个public的业务方法:toUpper(String): String和toLowerCase(String): String。其中的逻辑也很简单:前者将传入的字符串全变成大写,后者则全变成小写。并且两个方法都将在终端上将操作过程打印出来。除了这两法方法,其余都是用于描述Web Service接口的JSR-181注释。
先看一些类级别的注释。@WebService()表示这个类可以生成Web Service。name用于映射到WSDL文件的<wsdl:portType>元素,可以在生成的WSDL中找到<wsdl:portType>的值为StringChangerPort。targetNamespace用于指定WSDL文件中元素的命名空间,能在生成的WSDL中找到对应的targetNamespace="http://bea.com"。@SOAPBinding()表示这个服务可以映射到一个SOAP消息中。Style用于指定SOAP消息请求和回应的编码方式为RPC。use用于指定SOAP消息请求和回应是明文还是密文,此处表示为密文。@WLHttpTransport是一个Weblogic专用的注释,用于指生成的配置文件中的上下文路径和这个Web Service的URI。contextPath用于指定上下文路径为jb,也就是说可以用 http://localhost:7001/jb/ 这个URL来调用这个Web Services应用。serviceUri表示客户端访问这个Web Service的URI为 http://localhost:7001/jb/StringChangerService。
再看一些方法级别的注释。方法级注释为@WebMethod()。operationName为操作的名字,用于映射到WSDL文件的<wsdl:operation>元素。默认值为对应的方法名。因此在这个JavaBean生成的Web Service中toUpper方法对应的操作名为toUpper,toLowerCase方法对应的操作名为toLower。
编写build.xml Target。
<target name="build.jb" depends="clean.jb">
<jwsc srcdir="${jbApp.src.dir}" destdir="${jbApp.dest.dir}" listfiles="true" classpathref="jwsbuild.class.path">
<jws file="${jbApp.jwsSource.file}" />
</jwsc>
</target>
<target name="deploy.jb" depends="check.jb" if="jb.exists">
<wls.deploy deploy.dir="${jbApp.dest.dir}" deploy.name="${jbApp.deploy.name}" />
</target>
build.jb 这个Target主要用于建立一个可部署的Web Services模块。jwsc这个任务是用于分析JWS文件中的注释,生成对应JAVA文件配置文件,编译代码,最后在destdir指定的位置生成这个Web Services的WAR包。
deploy.jb这个Target主要用于把Web Service模块部署到WLS Domain中。wls.deploy是一个在weblogicServer.macrodef中定义的Task,调用WLS的ANT扩展任务wldeploy。
编译Web Services。
现在我们在ANT View中执行任务build.ws.pojo,可以看到在Console窗口显示出对应的操作过程。
Buildfile: C:/project/eclipse31/workspace/JSR-181 Web Services/build.xml
clean.jb:
[delete] Deleting directory C:/temp/jb
[mkdir] Created dir: C:/temp/jb
build.jb:
[jwsc] 1 JWS files will be processed.
[jwsc] Processing JWS: C:/project/eclipse31/workspace/JSR-181 Web Services/webservices/jsr181/pojo/StringChanger.java
[jwsc] JWS: C:/project/eclipse31/workspace/JSR-181 Web Services/webservices/jsr181/pojo/StringChanger.java Validated.
[jwsc] Compiling 2 source files to c:/temp/_ukbwfm
[jwsc] C:/TEMP/_ukbwfm/StringChangerPortType.java
[jwsc] C:/project/eclipse31/workspace/JSR-181 Web Services/webservices/jsr181/pojo/StringChanger.java
[jwsc] Building war: C:/temp/jb/StringChanger.war
[jwsc] Created JWS deployment file: C:/temp/jb/StringChanger.war
[AntUtil.deleteDir] Deleting directory c:/temp/_ukbwfm
BUILD SUCCESSFUL
Total time: 22 seconds
打开目录C:/TEMP/jb,我们能看到生成了两个文件夹和一个WAR文件。在文件夹META-INF下的 application.xml中的<web-uri>元素的值来自JWS中的注释@WLHttpTransport。打开生成的WAR文件我们能看WEB-INF路径下已经生成了一些对应的配置文件。查看其中的WSDL文件名为StringChangerService.wsdl,这也是来自JWS中的注释@WLHttpTransport。在WSDL文件中能看到targetNamespace为http://wwtt.bea.com,这是来自JWS中的注释@WebService。同样我们能在该文件中看到service元素中port为StringChangerPort,也是来自@WebService。在这个文件中包括了两个operation元素,name分别为toLower和toUpper,对应到JWS中两个注释@WebMethod。同时我们能看到对应的style为rpc,use为encoded。这些都符合我们在JWS的@SOAPBinding注释中的设置。同时我们能看到该WAR的WEB-INF/classes/webservices/jsr181/pojo 目录中包含两个class文件:StringChanger.class内是StringChanger.java描述的业务逻辑,StringChangerPortType是为这个类生成的接口。StringChangerPortType会在后面生成Web Services代码时被自动调用。
public interface StringChangerPortType extends Remote{
public abstract String toLowerCase(String s)
throws RemoteException;
public abstract String toUpper(String s)
throws RemoteException;
}
部署Web Service
我们在ANT View中执行任务deploy.jb,可以看到在Console窗口中显示对应部署过程。
Buildfile: C:/project/eclipse31/workspace/JSR-181 Web Services/build.xml
check.jb:
deploy.jb:
[wldeploy] weblogic.Deployer -noexit -name WLS v9 JSR-181 Javabean Web Service -source C:/temp/jb -targets AdminServer -adminurl t3://localhost:7001 -user weblogic -password ******** -deploy
[wldeploy] weblogic.Deployer invoked with options: -noexit -name WLS v9 JSR-181 Javabean Web Service -source C:/temp/jb -targets AdminServer -adminurl t3://localhost:7001 -user weblogic -deploy
[wldeploy] <2005-11-28 下午06时12分15秒 CST>
[wldeploy] Task 13 initiated: [Deployer:149026]deploy application WLS v9 JSR-181 Javabean Web Service on AdminServer.
[wldeploy] Task 13 completed: [Deployer:149026]deploy application WLS v9 JSR-181 Javabean Web Service on AdminServer.
[wldeploy] Target state: deploy completed on Server AdminServer
BUILD SUCCESSFUL
Total time: 14 seconds
用浏览器打开http://localhost:7001/console/,用weblogic/webogic登录,在左边目录树的deploy展开能看到已经部署了一个名为“WLS v9 JSR-181 Javabean Web Service”的企业应用(Enterprise Application),状态为Active。
测试
用浏览器打开 http://localhost:7001/jb/StringChangerService?WSDL ,可以看到一个展开的WSDL文件。这个文件的内容和我们前面在WAR中看到的WSDL文件应该是完全一致的。到此我们已经学会了开发一个简单的基于JavaBean的Web Service应用。
基于JavaBean的Web Service开发测试客户端
从上面的例子我们已经了解了如何开发,部署及测试一个Web Services模块,下面我们要学习如何开发一个Web Service客户端来调用Web Service逻辑。
编写build.xml Target
<target name="build.jbClient">
<clientgen wsdl="${jbApp.wsdl.url}" destDir="${clients.src.dir}" packageName="${clients.package.name}" />
<javac srcdir="${clients.src.dir}" destdir="${clients.dest.dir}" includes="${clients.package.path}/*.java" debug="on" classpathref="javabuild.class.path" />
</target>
<target name="build.jbDriver">
<javac srcdir="${jbApp.src.dir}" destdir="${clients.dest.dir}" includes="*.java" classpathref="javabuild.class.path" />
</target>
build.jbClient 这个Target主要用于建立一个Web Services模块的客户端,其中包含两个任务。clientgen这个任务可以根据一个WSDL文件生成一组调用该Web Service的客户端文件。Clientgen的属性wsdl指定WSDL文件的位置,packageName指定生成的测试代码的包名。Javac这个任务将生成的客户端java代码文件编译成对应的class文件。
build.jbDriver这个Target主要用于编译测试类。
生成并编译客户端代码
现在我们在ANT View中执行任务build.jbClient,可以看到在Console窗口显示出对应的操作过程。
Buildfile: C:/project/eclipse31/workspace/JSR-181 Web Services/build.xml
build.jbClient:
[clientgen] Generating client from http://localhost:7001/jb/StringChangerService?WSDL ...
[clientgen] Getting partner link
[clientgen] Package name is client.test
[clientgen] DestDir is C:/project/eclipse31/workspace/JSR-181 Web Services
[clientgen] class name is StringChangerPort_Stub
[clientgen] service class name is StringChangerService
[clientgen] Porttype name is StringChangerPort
[clientgen] service impl name is StringChangerService_Impl
[javac] Compiling 4 source files to C:/temp
BUILD SUCCESSFUL
Total time: 20 seconds
回到Eclipse的Package Explorer视图,在“JSR-181 Web Services”上右键选Refresh,能看到在项目中增加了一个名为client.test的包。展开这个包能看到里面包含若干JAVA文件,XML文件和WSDL文件。这些文件就是自动生成的客户端代码。打开资源管理器到C:/TEMP/client/test,能看到生成的客户端代码的class文件。
我们注意到,一共生成了4个类,接口StringChangerPort和其实现类StringChangerPort_Stub是我们在StringChanger类中用@WebService(name="StringChangerPort",......)指定的,用于定义业务方法的参数类型和参数转换传输的方法。接口StringChangerService和其实现类StringChangerService_Impl是我们在StringChanger类中用@WLHttpTransport(......, serviceUri="StringChangerService")中指定的,用于获得Service连接,定义消息的收发方式。
创建并编译一个测试类
下面我们创建一个测试类TestDriver_POJO来调用刚才我们创建的Web Service客户端。和刚才创建Java Bean一样,在webservices.jsr181.pojo中创建一个类TestDriver_POJO,写入如下内容:
package webservices.jsr181.pojo;
import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;
import client.test.StringChangerPort;
import client.test.StringChangerService;
import client.test.StringChangerService_Impl;
public class TestDriver_POJO {
public static void main(String[] args) throws ServiceException, RemoteException{
String url = "http://localhost:7001/jb/StringChangerService?WSDL";
StringChangerService service = new StringChangerService_Impl(url);
StringChangerPort port = service.getStringChangerPortSoapPort();
String testString = "This is my test String ;-)";
System.out.println("toUpper request: " + testString);
System.out.println("toUpper results: " + port.toUpper(testString));
System.out.println("toLower request: " + testString);
System.out.println("toLower results: " + port.toLower(testString));
}
}
我们能看到,一个JAVA类调用一个Web Service客户端是非常简单的。首先通过提供WSDL文件的位置来获得客户端类(StringChangerService)以及SOAP接口(StringChangerPort)。然后就可以通过服务名来调用这个SOAP接口提供的各种服务(toUpper和toLower)。
运行测试类观察结果
因为测试类只是用于测试,简化起见我们直接运行这个类而不使用ANT。右键点这个类的java文件,选择“run as…=> java application”。这时我们在Console能看到运行结果。如果一切正确:
toUpper request: This is my test String ;-)
toUpper results: THIS IS MY TEST STRING ;-)
toLower request: This is my test String ;-)
toLower results: this is my test string ;-)
到此,我们已经成功创建了一个基于JSR-181的Web Service以及对应客户端。如果有兴趣,可以试着在JWS文件中修改一些代码和注释,看看这些代码和注释会影响什么。无论如何,这会加深你对JSR-181的理解。
开发基于Session EJB的Web Service
使用基于EJB的Web Servics和基于Javabean的方式类似。我们只需在Bean类中添加一些符合JSR181的注释,在build.xml中调用WLS提供的扩展ANT Task来完成Web Services。
1. 创建一个EJB。
在Project的根目录下创建一个类名为PurchaseOrder,包名为webservices.jsr181.ejb,写入如下内容:
package webservices.jsr181.ejb;
import java.rmi.RemoteException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import weblogic.ejbgen.Constants;
import weblogic.ejbgen.FileGeneration;
import weblogic.ejbgen.JndiName;
import weblogic.ejbgen.RemoteMethod;
import weblogic.ejbgen.Session;
import weblogic.jws.WLHttpTransport;
@Session(ejbName = "PurchaseOrderEJB", serviceEndpoint = "webservices.jsr181.ejb.PurchaseOrderPortType")
@JndiName(remote = "PurchaseOrderEJB.Remote")
@FileGeneration(remoteClass = Constants.Bool.TRUE, remoteClassName = "PurchaseOrderRemote", remoteHome = Constants.Bool.TRUE, remoteHomeName = "PurchaseOrderHome")
@WebService(name="PurchaseOrderPort", serviceName="PurchaseOrderService", targetNamespace="http://bea.com /PurchaseOrder")
@SOAPBinding(style=SOAPBinding.Style.DOCUMENT, use=SOAPBinding.Use.LITERAL, parameterStyle=SOAPBinding.ParameterStyle.BARE)
@WLHttpTransport(portName="PurchaseOrderPort", contextPath="ejb", serviceUri="PurchaseOrderService")
public class PurchaseOrder implements SessionBean {
private static final long serialVersionUID = 1L;
public void ejbCreate() {}
@RemoteMethod()
@WebMethod()
public String toUpper(String upperReqString)
{
String upper = upperReqString.toUpperCase();
System.out.println("toUpper Request: " + upperReqString);
System.out.println("toUpper Reply: " + upper);
return(upper);
}
public void setSessionContext(SessionContext arg0) throws EJBException,
RemoteException {}
public void ejbRemove() throws EJBException, RemoteException {}
public void ejbActivate() throws EJBException, RemoteException {}
public void ejbPassivate() throws EJBException, RemoteException {}
}
这个类是我们创建的EJB的Bean类,并通过JSR175方式描述该EJB接口以及生成的Web Service接口。该类中只包含一个public的业务方法:toUpper(String): String,其余都是EJB要求的方法,以及用于描述EJB和Web Service的JSR-175注释。
先看一些类级别的注释。和基于Javabean的JWS类似,包含了JSR181定义的三行注释@WebService(),@SOAPBinding()和@WLHttpTransport()。与JavaBean不同的是这里还包括一些用于生成EJB配置文件和接口的注释。@Session表示这是一个session bean,属性ejbName和serviceEndpoint分别表示EJB的名字和接口名。@JndiName()中属性remote表示这个EJB发布在JNDI上的地址为PurchaseOrderEJB.Remote。@FileGeneration()表示由这个Bean类生成的代码文件。属性remoteClass,remoteClassName,remoteHome和remoteHomeName分别指定使用远程接口和远程Home接口,并且这两个接口为PurchaseOrderRemote和PurchaseOrderHome。
在方法级别上,除了和Javabean类似用@WebMethod()声明暴露的Web Service操作外,用@RemoteMethod()将这个方法指定为发布到远程接口上的方法。
2. 编写build.xml Target。
<target name=”build.ejb” depends=”clean.ejb”>
<jwsc srcdir=”${ejbApp.src.dir}” destdir=”${ejbApp.dest.dir}” listfiles=”true” classpathref=”jwsbuild.class.path”>
<jws file=”${ejbApp.jwsSource.file}” />
</jwsc>
</target>
<target name=”deploy.ejb” depends=”check.ejb” if=”ejb.exists”>
<wls.deploy deploy.dir=”${ejbApp.dest.dir}” deploy.name=”${ejbApp.deploy.name}” />
</target>
build.jb和deploy.jb两个Target主要用于建立一个可部署的Web Services模块并部署到Domain中。这与前面JavaBean例子使用方式完全一致。
3. 编译Web Services。
现在我们在ANT View中执行任务build.ejb,可以看到在Console窗口显示出对应的操作过程。
Buildfile: C:/project/eclipse31/workspace/JSR-181 Web Services/build.xml
clean.ejb:
[delete] Deleting directory C:/temp/ejb
[mkdir] Created dir: C:/temp/ejb
build.ejb:
[jwsc] 1 JWS files will be processed.
[jwsc] Processing JWS: C:/project/eclipse31/workspace/JSR-181 Web Services/webservices/jsr181/ejb/PurchaseOrder.java
[jwsc] JWS: C:/project/eclipse31/workspace/JSR-181 Web Services/webservices/jsr181/ejb/PurchaseOrder.java Validated.
[jwsc] EJBGen 9.0
[jwsc] Creating c:/temp/_f26ur6/PurchaseOrderHome.java
[jwsc] Creating c:/temp/_f26ur6/PurchaseOrderRemote.java
[jwsc] Creating c:/temp/_f26ur6//ejb-jar.xml
[jwsc] Creating c:/temp/_f26ur6//weblogic-ejb-jar.xml
[jwsc] Compiling 4 source files to c:/temp/_f26ur6
[jwsc] C:/TEMP/_f26ur6/PurchaseOrderHome.java
[jwsc] C:/TEMP/_f26ur6/PurchaseOrderPortType.java
[jwsc] C:/TEMP/_f26ur6/PurchaseOrderRemote.java
[jwsc] C:/project/eclipse31/workspace/JSR-181 Web Services/webservices/jsr181/ejb/PurchaseOrder.java
[jwsc] Building jar: C:/temp/ejb/PurchaseOrder.jar
[jwsc] Created JWS deployment file: C:/temp/ejb/PurchaseOrder.jar
[AntUtil.deleteDir] Deleting directory c:/temp/_f26ur6
Creating c:/temp/_f26ur6/ejbgen-build.xml
BUILD SUCCESSFUL
Total time: 17 seconds
打开目录C:/TEMP/ejb,我们能看到生成了两个文件夹和一个jar文件。在文件夹META-INF下的。打开生成的JAR文件我们能看META-INF路径下已经生成了一些对应的配置文件。其中大部分和在Javabean中生成的配置文件类似,此外还包括一些与EJB相关的配置文件,比如weblogic-ejb-jar.xml,ejbgen-build.xml和ejb-jar.xml,没有了与Web应用相关的配置文件weblogic.xml和web.xml
部署Web Service
我们在ANT View中执行任务deploy.ejb,可以看到在Console窗口中显示对应部署过程。
Buildfile: C:/project/eclipse31/workspace/JSR-181 Web Services/build.xml
check.ejb:
deploy.ejb:
[wldeploy] weblogic.Deployer 每noexit 每name WLS v9 JSR-181 EJB Web Service 每source C:/temp/ejb 每targets AdminServer 每adminurl t3://localhost:7001 每user weblogic 每password ******** -deploy
[wldeploy] weblogic.Deployer invoked with options: -noexit 每name WLS v9 JSR-181 EJB Web Service 每source C:/temp/ejb 每targets AdminServer 每adminurl t3://localhost:7001 每user weblogic 每deploy
[wldeploy] <2005-11-28 ?B??09?C21煦47镞 CST>
[wldeploy] Task 1 initiated: [Deployer:149026]deploy application WLS v9 JSR-181 EJB Web Service on AdminServer.
[wldeploy] Task 1 completed: [Deployer:149026]deploy application WLS v9 JSR-181 EJB Web Service on AdminServer.
[wldeploy] Target state: deploy completed on Server AdminServer
BUILD SUCCESSFUL
Total time: 7 seconds
用浏览器打开http://localhost:7001/console/,用weblogic/webogic登录,在左边目录树的deploy展开能看到已经部署了一个名为“WLS v9 JSR-181 EJB Web Service”的企业应用(Enterprise Application),状态为Active。
5. 测试
用浏览器打开http://localhost:7001/ejb/StringChangerService?WSDL,可以看到一个展开的WSDL文件。这个文件的内容和我们前面在JAR中看到的WSDL文件应该是完全一致的。
为基于Session EJB的Web Service开发测试客户端
和JavaBean一样,我们可以为这个基于Session Bean的Web Service创建一个客户端,并用来测试。
1. 编写build.xml Target
<target name="build.ejbClient">
<clientgen wsdl="${ejbApp.wsdl.local}" destDir="${clients.src.dir}" packageName="${clients.package.name}"/>
<javac srcdir="${clients.src.dir}" destdir="${clients.dest.dir}" includes="${clients.package.path}/*.java" debug="on" classpathref="javabuild.class.path" />
</target>
<target name="build.ejbDriver">
<javac srcdir="${ejbApp.src.dir}" destdir="${clients.dest.dir}" includes="*.java" classpathref="javabuild.class.path" />
</target>
build.jbClient 与build.ejbDriver和Javabean中的对应目标也类似。唯一区别的是在clientgen任务中使用的WSDL文件不是来自URL,而是来自build.ejb生成的JAR包。使用local或URL方式对其他客户端代码没有影响,基本这两种方式可以互换使用。
2. 生成并编译客户端代码
现在我们在ANT View中执行任务build.jbClient,可以看到在Console窗口显示出对应的操作过程。
Buildfile: C:/project/eclipse31/workspace/JSR-181 Web Services/build.xml
build.ejbClient:
[clientgen] Generating client from jar:file:C:/temp/ejb/PurchaseOrder.jar!/META-INF/PurchaseOrderService.wsdl …
[clientgen] Getting partner link
[clientgen] Package name is client.test
[clientgen] DestDir is C:/project/eclipse31/workspace/JSR-181 Web Services
[clientgen] class name is PurchaseOrderPort_Stub
[clientgen] service class name is PurchaseOrderService
[clientgen] Porttype name is PurchaseOrderPort
[clientgen] service impl name is PurchaseOrderService_Impl
[javac] Compiling 4 source files to C:/temp
BUILD SUCCESSFUL
Total time: 11 seconds
回到Package Explorer视图的包client.test。能看到里面又增加了若干JAVA文件,XML文件和WSDL文件。这些文件就是自动生成的客户端代码。打开资源管理器到C:/TEMP/client/test,能看到生成的客户端代码的class文件。
我们注意到,一共生成了4个类,StringChangerPort和StringChangerPort_Stub是我们在StringChanger类中用@WebService(name="StringChangerPort",......)指定的。StringChangerService和StringChangerService_Impl是我们在StringChanger类中用@WLHttpTransport(......, serviceUri=”StringChangerService”)中指定的。
3. 创建并编译一个测试类
下面我们在webservices.jsr181.ejb中创建一个类TestDriver_EJB,写入如下内容:
package webservices.jsr181.ejb;
import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;
import client.test.PurchaseOrderPort;
import client.test.PurchaseOrderService;
import client.test.PurchaseOrderService_Impl;
public class TestDriver_EJB
{
public static void main(String[] args)
throws ServiceException, RemoteException{
String url = “http://localhost:7001/ejb/PurchaseOrderService?WSDL”;
PurchaseOrderService service = new PurchaseOrderService_Impl(url);
PurchaseOrderPort port = service.getPurchaseOrderPort();
String testString = “This is my test String ;-)”;
System.out.println(port.toUpper(testString));
}
}
这里面的内容和在Javabean中也没什么区别。因此我们可以看出来,在Web Service后端使用什么方式实现,在前端看是完全透明的。
4. 运行测试类观察结果
右键点文件TestDriver_EJB.java,选择“run as…=> java application”。这时我们在Console能看到运行结果。如果一切正确:
THIS IS MY TEST STRING ;-)
到此,我们完成了这个基于EJB的Web Services的例子。事实上我们可以用各种方法创建Web Services的业务逻辑,从而轻松的实现系统组件间的松耦合。
JSR-921是J2EE 1.3中Web Services规范JSR-109的1.1维护版本。在Web Services 1.1中描述了J2EE Web Service的业务逻辑有两种实现方式:一种是使用Web容器中的JavaBean,另一种是使用EJB容器中的无状态Session Bean。与旧的Web Services开发方式不同,BEA建议使用JWS这种基于注释的Web Services编程方式以简化开发。这种编程方式的开发过程大概是这样,利用JDK5的新特性元数据注释(JSR-175),在编写上述的Java Bean或EJB的Bean类时在业务逻辑代码中添加一些特殊注释,然后用ANT的一些扩展任务可以生成一些相关JAVA类代码(主要是EJB的存根、桩)和配置文件,最后自动编译这些类代码并自动打包。所以可以说JWS方式是Web Services新标准的核心。JWS文件实际就是普通的.JAVA文件(扩展名依旧是.JAVA),通过添加注释来制定WS的特性。这些注释在JSR181中有完整的说明。如果Web Service用Java Bean类实现,自动打包后会包含web.xml,weblogic.xml, webservices.xml,weblogic-webservices.xml,JAX-RPC数据类型映射文件和WSDL等,并被打包成标准Web应用的WAR。如果用EJB实现,则会包含ejb-jar.xml和weblogic-ejb.jar.xml等,并被自动打包成标准EJB的JAR。下图描述了JWS文件生成可部署的J2EE模块的详细过程。后面我们会用两个例子详细说明如何开发、部署基于Java Bean和基于EJB的Web Services。
JSR181与JSR175介绍
我们先来看一段典型的使用JSR175特征的代码。
package webservices.jsr181.pojo;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService(name="StringChangerPort", targetNamespace="http://wwtt.bea.com")
public class StringChanger {
@WebMethod()
public String toUpper(String upperReqString)
{
……
这个类的源代码和普通的JAVA代码没太多区别,但和普通JAVA代码不同的是这里面还包含了很多以@开头的代码,或者称为注释,也就是在Java 5中的新语法,JSR-175。JSR-175规范只是定义这种语法规则,而用这种语法来如何将这个类生成一个Web Service接口,则需要由JSR-181规范来决定。所以这些JSR-175的注释才真正决定了这个Web Service的行为和外观。
也许你会觉得这种语法比较奇怪,有些像普通java方法的语法,只是前面多了一个@。实际在JSR175规范的早期,该语法并不是像现在这样的。如果按早期写法,@WebService()会该写为:
/**
* @WebService(......)
*/ 后来出于简化,成了现在的样子。如果使用Weblogic Platform 7.x或8.x产品,会在许多其专有的代码文件中看到这种格式的注释。由于撰写本文时Weblogic Platform 9还没有发布。发布后是否那些专有代码会使用基于JSR-175的语法还不得而知,但可以确定的是在Weblgic Server 9中这种语法已经被全面支持了,无论在Web Service中,还是在EJB中。
事实上JSR 181是BEA提出的用于加速Web Services开发的一种基于注释驱动的编程模式,并被批准纳入到J2EE 1.5标准。JSR181提供了一种简单的Web Service开发编程模型和标准的编译及部署方式。只需要编写JSR-175风格的注释就可以制定WSDL,消息产生属性,安全认证方式,以及特定的消息头。
练习的预备工作
准备必要软件。
进行练习前需要预先安装WLS9正式版,可以到BEA官方网站上免费下载。本练习可在能安装WLS9的任何操作系统上完成,为了方便起见,文中仅以在Windows XP上开发为例。此外本文中代码的开发环境为Eclipse 3.1及对应的Lomboz,当然如您所愿任何其他支持ANT的开发环境都是可以的。
配置Weblogic Server。
准备好上述软件后,首先用配置创建一个新的WLS Domain。记得用户名和密码要都设为weblogic,端口为7001。创建好后启动该Domain,并用浏览器启动相应的Console,用帐户weblogic/weblogic登录进入。
配置Eclipse。
如果希望Eclipse的配置尽量吻合本文的环境,可参考我的另一篇文章:“使用Eclipse加速Weblogic Server开发”的第一步到第二步。配置好后在Eclipse的File->New Project中选取Java Project,在Project Name中将我们这个练习的名字设为JSR-181 Web Services或任何你喜欢的名字,JDK Compliance中选择“Use a project specific compliance 5.0”,其余保持默认。一个符合我们需要的Project就建好了。
修改脚本文件。
本练习所有的构建过程都由ANT来完成,因此我们需要添加一些ANT的脚本配置文件。属性文件,宏定义文件可以完全参照《使用Eclipse加速Weblogic Server开发》,因此只需要吧那里面的那三个文件拿过来用就可以了。
需要修改的是build.xml文件,这里定义的属性主要是为后面两个例子设置一些路径。
<?xml version="1.0"?>
<project name="WLS v9 JSR-181 Web Services" basedir=".">
<property file="wls-build-tools/weblogicServer.properties" />
<import file="wls-build-tools/weblogicServer.macrodef" />
<taskdef file="wls-build-tools/weblogicTasks.properties" classpathref="jwsbuild.class.path" />
<property name="apps.src.dir" location="." />
<property name="apps.dest.dir" location="${temp.dir}" />
<property name="clients.src.dir" location="." />
<property name="clients.dest.dir" location="${temp.dir}" />
<property name="clients.package.name" value="client.test"/>
<property name="clients.package.path" value="client/test"/>
<property name="jbApp.context.name" value="jb" />
<property name="jbApp.package.name" value="webservices.jsr181.pojo"/>
<property name="jbApp.src.dir" location="${apps.src.dir}/webservices/jsr181/pojo"/>
<property name="jbApp.jwsSource.file" value="StringChanger.java" />
<property name="jbApp.dest.dir" location="${apps.dest.dir}/${jbApp.context.name}" />
<property name="jbApp.service.name" value="StringChangerService" />
<property name="jbApp.wsdl.url" value="http://${domain.address}:${domain.port}/${jbApp.context.name}/${jbApp.service.name}?WSDL" />
<property name="jbApp.deploy.name" value="WLS v9 JSR-181 Javabean Web Service" />
<property name="ejbApp.context.name" value="ejb" />
<property name="ejbApp.package.name" value="webservices.jsr181.ejb"/>
<property name="ejbApp.src.dir" location="${apps.src.dir}/webservices/jsr181/ejb"/>
<property name="ejbApp.jwsSource.file" value="PurchaseOrder.java" />
<property name="ejbApp.dest.dir" location="${apps.dest.dir}/${ejbApp.context.name}" />
<property name="ejbApp.service.name" value="PurchaseOrderService" />
<property name="ejbApp.wsdl.url" value="http://${domain.address}:${domain.port}/${ejbApp.context.name}/${ejbApp.service.name}?WSDL" />
<property name="ejbApp.wsdl.local" value="jar:file:${apps.dest.dir}/${ejbApp.context.name}/PurchaseOrder.jar!/META-INF/${ejbApp.service.name}.wsdl" />
<property name="ejbApp.deploy.name" value="WLS v9 JSR-181 EJB Web Service" />
<path id="jwsbuild.class.path">
<fileset dir="${weblogic.home}/server/lib">
<include name="weblogic.jar" />
<include name="xbean.jar" />
</fileset>
<fileset dir="${jdk5.home}/lib">
<include name="tools.jar" />
</fileset>
</path>
<path id="javabuild.class.path">
<pathelement path="${clients.dest.dir}" />
<fileset dir="${weblogic.home}/server/lib">
<include name="webserviceclient.jar" />
</fileset>
</path>
<!--后面的练习中我们将根据需要在此添加一些target。-->
</project>
如何快速查看例子的结果,而跳过开发环节。
做好1到4步骤就可以按照后面几节所述完成练习。但如果想快速看一下练习结果,将本文附件solution包括所有源代码。解压到您磁盘任何位置,用Eclipse创建好一空的名为JSR-181 Web Services的Project后,用solution解出的JSR-181 Web Services文件夹替换这个Project的文件夹。最后在Eclipse的Package Explorer中刷新一下,就浏览已经开发配置好Eclipse Project代码了。修改项目的wls-build-tools路径下weblogicServer.properties中的WLS和JDK环境变量,然后就可以执行ANT脚本中的任务来编译,部署,测试等步骤。
开发基于JavaBean的Web Services
基于JavaBean的Web Servics是最简单的一种方式。我们只需要在普通的JavaBean码中添加一些符合JSR181的注释,在build.xml中调用WLS提供的扩展ANT Task,就可以完成一个完整的Web Services。
创建一个Java Bean。
在Project的根目录下创建一个类名为StringChanger,包名为webservices.jsr181.pojo,写入如下内容:
package webservices.jsr181.pojo;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import weblogic.jws.WLHttpTransport;
@WebService(name="StringChangerPort", targetNamespace="http://bea.com")
@SOAPBinding(style=SOAPBinding.Style.RPC, use=SOAPBinding.Use.ENCODED)
@WLHttpTransport(contextPath="jb", serviceUri="StringChangerService")
public class StringChanger {
@WebMethod()
public String toUpper(String upperReqString)
{
String upper = upperReqString.toUpperCase();
System.out.println("toUpper Request: " + upperReqString);
System.out.println("toUpper Reply: " + upper);
return(upper);
}
@WebMethod(operationName="toLower")
public String toLowerCase(String lowerReqString)
{
String lower = lowerReqString.toLowerCase();
System.out.println("toLowerCase Request: " + lowerReqString);
System.out.println("toLowerCase Reply: " + lower);
return(lower);
}
}
这个类是用于实现我们这个Web Services模块的业务逻辑,以及描述这个Web Service接口。该类中包含两个public的业务方法:toUpper(String): String和toLowerCase(String): String。其中的逻辑也很简单:前者将传入的字符串全变成大写,后者则全变成小写。并且两个方法都将在终端上将操作过程打印出来。除了这两法方法,其余都是用于描述Web Service接口的JSR-181注释。
先看一些类级别的注释。@WebService()表示这个类可以生成Web Service。name用于映射到WSDL文件的<wsdl:portType>元素,可以在生成的WSDL中找到<wsdl:portType>的值为StringChangerPort。targetNamespace用于指定WSDL文件中元素的命名空间,能在生成的WSDL中找到对应的targetNamespace="http://bea.com"。@SOAPBinding()表示这个服务可以映射到一个SOAP消息中。Style用于指定SOAP消息请求和回应的编码方式为RPC。use用于指定SOAP消息请求和回应是明文还是密文,此处表示为密文。@WLHttpTransport是一个Weblogic专用的注释,用于指生成的配置文件中的上下文路径和这个Web Service的URI。contextPath用于指定上下文路径为jb,也就是说可以用 http://localhost:7001/jb/ 这个URL来调用这个Web Services应用。serviceUri表示客户端访问这个Web Service的URI为 http://localhost:7001/jb/StringChangerService。
再看一些方法级别的注释。方法级注释为@WebMethod()。operationName为操作的名字,用于映射到WSDL文件的<wsdl:operation>元素。默认值为对应的方法名。因此在这个JavaBean生成的Web Service中toUpper方法对应的操作名为toUpper,toLowerCase方法对应的操作名为toLower。
编写build.xml Target。
<target name="build.jb" depends="clean.jb">
<jwsc srcdir="${jbApp.src.dir}" destdir="${jbApp.dest.dir}" listfiles="true" classpathref="jwsbuild.class.path">
<jws file="${jbApp.jwsSource.file}" />
</jwsc>
</target>
<target name="deploy.jb" depends="check.jb" if="jb.exists">
<wls.deploy deploy.dir="${jbApp.dest.dir}" deploy.name="${jbApp.deploy.name}" />
</target>
build.jb 这个Target主要用于建立一个可部署的Web Services模块。jwsc这个任务是用于分析JWS文件中的注释,生成对应JAVA文件配置文件,编译代码,最后在destdir指定的位置生成这个Web Services的WAR包。
deploy.jb这个Target主要用于把Web Service模块部署到WLS Domain中。wls.deploy是一个在weblogicServer.macrodef中定义的Task,调用WLS的ANT扩展任务wldeploy。
编译Web Services。
现在我们在ANT View中执行任务build.ws.pojo,可以看到在Console窗口显示出对应的操作过程。
Buildfile: C:/project/eclipse31/workspace/JSR-181 Web Services/build.xml
clean.jb:
[delete] Deleting directory C:/temp/jb
[mkdir] Created dir: C:/temp/jb
build.jb:
[jwsc] 1 JWS files will be processed.
[jwsc] Processing JWS: C:/project/eclipse31/workspace/JSR-181 Web Services/webservices/jsr181/pojo/StringChanger.java
[jwsc] JWS: C:/project/eclipse31/workspace/JSR-181 Web Services/webservices/jsr181/pojo/StringChanger.java Validated.
[jwsc] Compiling 2 source files to c:/temp/_ukbwfm
[jwsc] C:/TEMP/_ukbwfm/StringChangerPortType.java
[jwsc] C:/project/eclipse31/workspace/JSR-181 Web Services/webservices/jsr181/pojo/StringChanger.java
[jwsc] Building war: C:/temp/jb/StringChanger.war
[jwsc] Created JWS deployment file: C:/temp/jb/StringChanger.war
[AntUtil.deleteDir] Deleting directory c:/temp/_ukbwfm
BUILD SUCCESSFUL
Total time: 22 seconds
打开目录C:/TEMP/jb,我们能看到生成了两个文件夹和一个WAR文件。在文件夹META-INF下的 application.xml中的<web-uri>元素的值来自JWS中的注释@WLHttpTransport。打开生成的WAR文件我们能看WEB-INF路径下已经生成了一些对应的配置文件。查看其中的WSDL文件名为StringChangerService.wsdl,这也是来自JWS中的注释@WLHttpTransport。在WSDL文件中能看到targetNamespace为http://wwtt.bea.com,这是来自JWS中的注释@WebService。同样我们能在该文件中看到service元素中port为StringChangerPort,也是来自@WebService。在这个文件中包括了两个operation元素,name分别为toLower和toUpper,对应到JWS中两个注释@WebMethod。同时我们能看到对应的style为rpc,use为encoded。这些都符合我们在JWS的@SOAPBinding注释中的设置。同时我们能看到该WAR的WEB-INF/classes/webservices/jsr181/pojo 目录中包含两个class文件:StringChanger.class内是StringChanger.java描述的业务逻辑,StringChangerPortType是为这个类生成的接口。StringChangerPortType会在后面生成Web Services代码时被自动调用。
public interface StringChangerPortType extends Remote{
public abstract String toLowerCase(String s)
throws RemoteException;
public abstract String toUpper(String s)
throws RemoteException;
}
部署Web Service
我们在ANT View中执行任务deploy.jb,可以看到在Console窗口中显示对应部署过程。
Buildfile: C:/project/eclipse31/workspace/JSR-181 Web Services/build.xml
check.jb:
deploy.jb:
[wldeploy] weblogic.Deployer -noexit -name WLS v9 JSR-181 Javabean Web Service -source C:/temp/jb -targets AdminServer -adminurl t3://localhost:7001 -user weblogic -password ******** -deploy
[wldeploy] weblogic.Deployer invoked with options: -noexit -name WLS v9 JSR-181 Javabean Web Service -source C:/temp/jb -targets AdminServer -adminurl t3://localhost:7001 -user weblogic -deploy
[wldeploy] <2005-11-28 下午06时12分15秒 CST>
[wldeploy] Task 13 initiated: [Deployer:149026]deploy application WLS v9 JSR-181 Javabean Web Service on AdminServer.
[wldeploy] Task 13 completed: [Deployer:149026]deploy application WLS v9 JSR-181 Javabean Web Service on AdminServer.
[wldeploy] Target state: deploy completed on Server AdminServer
BUILD SUCCESSFUL
Total time: 14 seconds
用浏览器打开http://localhost:7001/console/,用weblogic/webogic登录,在左边目录树的deploy展开能看到已经部署了一个名为“WLS v9 JSR-181 Javabean Web Service”的企业应用(Enterprise Application),状态为Active。
测试
用浏览器打开 http://localhost:7001/jb/StringChangerService?WSDL ,可以看到一个展开的WSDL文件。这个文件的内容和我们前面在WAR中看到的WSDL文件应该是完全一致的。到此我们已经学会了开发一个简单的基于JavaBean的Web Service应用。
基于JavaBean的Web Service开发测试客户端
从上面的例子我们已经了解了如何开发,部署及测试一个Web Services模块,下面我们要学习如何开发一个Web Service客户端来调用Web Service逻辑。
编写build.xml Target
<target name="build.jbClient">
<clientgen wsdl="${jbApp.wsdl.url}" destDir="${clients.src.dir}" packageName="${clients.package.name}" />
<javac srcdir="${clients.src.dir}" destdir="${clients.dest.dir}" includes="${clients.package.path}/*.java" debug="on" classpathref="javabuild.class.path" />
</target>
<target name="build.jbDriver">
<javac srcdir="${jbApp.src.dir}" destdir="${clients.dest.dir}" includes="*.java" classpathref="javabuild.class.path" />
</target>
build.jbClient 这个Target主要用于建立一个Web Services模块的客户端,其中包含两个任务。clientgen这个任务可以根据一个WSDL文件生成一组调用该Web Service的客户端文件。Clientgen的属性wsdl指定WSDL文件的位置,packageName指定生成的测试代码的包名。Javac这个任务将生成的客户端java代码文件编译成对应的class文件。
build.jbDriver这个Target主要用于编译测试类。
生成并编译客户端代码
现在我们在ANT View中执行任务build.jbClient,可以看到在Console窗口显示出对应的操作过程。
Buildfile: C:/project/eclipse31/workspace/JSR-181 Web Services/build.xml
build.jbClient:
[clientgen] Generating client from http://localhost:7001/jb/StringChangerService?WSDL ...
[clientgen] Getting partner link
[clientgen] Package name is client.test
[clientgen] DestDir is C:/project/eclipse31/workspace/JSR-181 Web Services
[clientgen] class name is StringChangerPort_Stub
[clientgen] service class name is StringChangerService
[clientgen] Porttype name is StringChangerPort
[clientgen] service impl name is StringChangerService_Impl
[javac] Compiling 4 source files to C:/temp
BUILD SUCCESSFUL
Total time: 20 seconds
回到Eclipse的Package Explorer视图,在“JSR-181 Web Services”上右键选Refresh,能看到在项目中增加了一个名为client.test的包。展开这个包能看到里面包含若干JAVA文件,XML文件和WSDL文件。这些文件就是自动生成的客户端代码。打开资源管理器到C:/TEMP/client/test,能看到生成的客户端代码的class文件。
我们注意到,一共生成了4个类,接口StringChangerPort和其实现类StringChangerPort_Stub是我们在StringChanger类中用@WebService(name="StringChangerPort",......)指定的,用于定义业务方法的参数类型和参数转换传输的方法。接口StringChangerService和其实现类StringChangerService_Impl是我们在StringChanger类中用@WLHttpTransport(......, serviceUri="StringChangerService")中指定的,用于获得Service连接,定义消息的收发方式。
创建并编译一个测试类
下面我们创建一个测试类TestDriver_POJO来调用刚才我们创建的Web Service客户端。和刚才创建Java Bean一样,在webservices.jsr181.pojo中创建一个类TestDriver_POJO,写入如下内容:
package webservices.jsr181.pojo;
import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;
import client.test.StringChangerPort;
import client.test.StringChangerService;
import client.test.StringChangerService_Impl;
public class TestDriver_POJO {
public static void main(String[] args) throws ServiceException, RemoteException{
String url = "http://localhost:7001/jb/StringChangerService?WSDL";
StringChangerService service = new StringChangerService_Impl(url);
StringChangerPort port = service.getStringChangerPortSoapPort();
String testString = "This is my test String ;-)";
System.out.println("toUpper request: " + testString);
System.out.println("toUpper results: " + port.toUpper(testString));
System.out.println("toLower request: " + testString);
System.out.println("toLower results: " + port.toLower(testString));
}
}
我们能看到,一个JAVA类调用一个Web Service客户端是非常简单的。首先通过提供WSDL文件的位置来获得客户端类(StringChangerService)以及SOAP接口(StringChangerPort)。然后就可以通过服务名来调用这个SOAP接口提供的各种服务(toUpper和toLower)。
运行测试类观察结果
因为测试类只是用于测试,简化起见我们直接运行这个类而不使用ANT。右键点这个类的java文件,选择“run as…=> java application”。这时我们在Console能看到运行结果。如果一切正确:
toUpper request: This is my test String ;-)
toUpper results: THIS IS MY TEST STRING ;-)
toLower request: This is my test String ;-)
toLower results: this is my test string ;-)
到此,我们已经成功创建了一个基于JSR-181的Web Service以及对应客户端。如果有兴趣,可以试着在JWS文件中修改一些代码和注释,看看这些代码和注释会影响什么。无论如何,这会加深你对JSR-181的理解。
开发基于Session EJB的Web Service
使用基于EJB的Web Servics和基于Javabean的方式类似。我们只需在Bean类中添加一些符合JSR181的注释,在build.xml中调用WLS提供的扩展ANT Task来完成Web Services。
1. 创建一个EJB。
在Project的根目录下创建一个类名为PurchaseOrder,包名为webservices.jsr181.ejb,写入如下内容:
package webservices.jsr181.ejb;
import java.rmi.RemoteException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import weblogic.ejbgen.Constants;
import weblogic.ejbgen.FileGeneration;
import weblogic.ejbgen.JndiName;
import weblogic.ejbgen.RemoteMethod;
import weblogic.ejbgen.Session;
import weblogic.jws.WLHttpTransport;
@Session(ejbName = "PurchaseOrderEJB", serviceEndpoint = "webservices.jsr181.ejb.PurchaseOrderPortType")
@JndiName(remote = "PurchaseOrderEJB.Remote")
@FileGeneration(remoteClass = Constants.Bool.TRUE, remoteClassName = "PurchaseOrderRemote", remoteHome = Constants.Bool.TRUE, remoteHomeName = "PurchaseOrderHome")
@WebService(name="PurchaseOrderPort", serviceName="PurchaseOrderService", targetNamespace="http://bea.com /PurchaseOrder")
@SOAPBinding(style=SOAPBinding.Style.DOCUMENT, use=SOAPBinding.Use.LITERAL, parameterStyle=SOAPBinding.ParameterStyle.BARE)
@WLHttpTransport(portName="PurchaseOrderPort", contextPath="ejb", serviceUri="PurchaseOrderService")
public class PurchaseOrder implements SessionBean {
private static final long serialVersionUID = 1L;
public void ejbCreate() {}
@RemoteMethod()
@WebMethod()
public String toUpper(String upperReqString)
{
String upper = upperReqString.toUpperCase();
System.out.println("toUpper Request: " + upperReqString);
System.out.println("toUpper Reply: " + upper);
return(upper);
}
public void setSessionContext(SessionContext arg0) throws EJBException,
RemoteException {}
public void ejbRemove() throws EJBException, RemoteException {}
public void ejbActivate() throws EJBException, RemoteException {}
public void ejbPassivate() throws EJBException, RemoteException {}
}
这个类是我们创建的EJB的Bean类,并通过JSR175方式描述该EJB接口以及生成的Web Service接口。该类中只包含一个public的业务方法:toUpper(String): String,其余都是EJB要求的方法,以及用于描述EJB和Web Service的JSR-175注释。
先看一些类级别的注释。和基于Javabean的JWS类似,包含了JSR181定义的三行注释@WebService(),@SOAPBinding()和@WLHttpTransport()。与JavaBean不同的是这里还包括一些用于生成EJB配置文件和接口的注释。@Session表示这是一个session bean,属性ejbName和serviceEndpoint分别表示EJB的名字和接口名。@JndiName()中属性remote表示这个EJB发布在JNDI上的地址为PurchaseOrderEJB.Remote。@FileGeneration()表示由这个Bean类生成的代码文件。属性remoteClass,remoteClassName,remoteHome和remoteHomeName分别指定使用远程接口和远程Home接口,并且这两个接口为PurchaseOrderRemote和PurchaseOrderHome。
在方法级别上,除了和Javabean类似用@WebMethod()声明暴露的Web Service操作外,用@RemoteMethod()将这个方法指定为发布到远程接口上的方法。
2. 编写build.xml Target。
<target name=”build.ejb” depends=”clean.ejb”>
<jwsc srcdir=”${ejbApp.src.dir}” destdir=”${ejbApp.dest.dir}” listfiles=”true” classpathref=”jwsbuild.class.path”>
<jws file=”${ejbApp.jwsSource.file}” />
</jwsc>
</target>
<target name=”deploy.ejb” depends=”check.ejb” if=”ejb.exists”>
<wls.deploy deploy.dir=”${ejbApp.dest.dir}” deploy.name=”${ejbApp.deploy.name}” />
</target>
build.jb和deploy.jb两个Target主要用于建立一个可部署的Web Services模块并部署到Domain中。这与前面JavaBean例子使用方式完全一致。
3. 编译Web Services。
现在我们在ANT View中执行任务build.ejb,可以看到在Console窗口显示出对应的操作过程。
Buildfile: C:/project/eclipse31/workspace/JSR-181 Web Services/build.xml
clean.ejb:
[delete] Deleting directory C:/temp/ejb
[mkdir] Created dir: C:/temp/ejb
build.ejb:
[jwsc] 1 JWS files will be processed.
[jwsc] Processing JWS: C:/project/eclipse31/workspace/JSR-181 Web Services/webservices/jsr181/ejb/PurchaseOrder.java
[jwsc] JWS: C:/project/eclipse31/workspace/JSR-181 Web Services/webservices/jsr181/ejb/PurchaseOrder.java Validated.
[jwsc] EJBGen 9.0
[jwsc] Creating c:/temp/_f26ur6/PurchaseOrderHome.java
[jwsc] Creating c:/temp/_f26ur6/PurchaseOrderRemote.java
[jwsc] Creating c:/temp/_f26ur6//ejb-jar.xml
[jwsc] Creating c:/temp/_f26ur6//weblogic-ejb-jar.xml
[jwsc] Compiling 4 source files to c:/temp/_f26ur6
[jwsc] C:/TEMP/_f26ur6/PurchaseOrderHome.java
[jwsc] C:/TEMP/_f26ur6/PurchaseOrderPortType.java
[jwsc] C:/TEMP/_f26ur6/PurchaseOrderRemote.java
[jwsc] C:/project/eclipse31/workspace/JSR-181 Web Services/webservices/jsr181/ejb/PurchaseOrder.java
[jwsc] Building jar: C:/temp/ejb/PurchaseOrder.jar
[jwsc] Created JWS deployment file: C:/temp/ejb/PurchaseOrder.jar
[AntUtil.deleteDir] Deleting directory c:/temp/_f26ur6
Creating c:/temp/_f26ur6/ejbgen-build.xml
BUILD SUCCESSFUL
Total time: 17 seconds
打开目录C:/TEMP/ejb,我们能看到生成了两个文件夹和一个jar文件。在文件夹META-INF下的。打开生成的JAR文件我们能看META-INF路径下已经生成了一些对应的配置文件。其中大部分和在Javabean中生成的配置文件类似,此外还包括一些与EJB相关的配置文件,比如weblogic-ejb-jar.xml,ejbgen-build.xml和ejb-jar.xml,没有了与Web应用相关的配置文件weblogic.xml和web.xml
部署Web Service
我们在ANT View中执行任务deploy.ejb,可以看到在Console窗口中显示对应部署过程。
Buildfile: C:/project/eclipse31/workspace/JSR-181 Web Services/build.xml
check.ejb:
deploy.ejb:
[wldeploy] weblogic.Deployer 每noexit 每name WLS v9 JSR-181 EJB Web Service 每source C:/temp/ejb 每targets AdminServer 每adminurl t3://localhost:7001 每user weblogic 每password ******** -deploy
[wldeploy] weblogic.Deployer invoked with options: -noexit 每name WLS v9 JSR-181 EJB Web Service 每source C:/temp/ejb 每targets AdminServer 每adminurl t3://localhost:7001 每user weblogic 每deploy
[wldeploy] <2005-11-28 ?B??09?C21煦47镞 CST>
[wldeploy] Task 1 initiated: [Deployer:149026]deploy application WLS v9 JSR-181 EJB Web Service on AdminServer.
[wldeploy] Task 1 completed: [Deployer:149026]deploy application WLS v9 JSR-181 EJB Web Service on AdminServer.
[wldeploy] Target state: deploy completed on Server AdminServer
BUILD SUCCESSFUL
Total time: 7 seconds
用浏览器打开http://localhost:7001/console/,用weblogic/webogic登录,在左边目录树的deploy展开能看到已经部署了一个名为“WLS v9 JSR-181 EJB Web Service”的企业应用(Enterprise Application),状态为Active。
5. 测试
用浏览器打开http://localhost:7001/ejb/StringChangerService?WSDL,可以看到一个展开的WSDL文件。这个文件的内容和我们前面在JAR中看到的WSDL文件应该是完全一致的。
为基于Session EJB的Web Service开发测试客户端
和JavaBean一样,我们可以为这个基于Session Bean的Web Service创建一个客户端,并用来测试。
1. 编写build.xml Target
<target name="build.ejbClient">
<clientgen wsdl="${ejbApp.wsdl.local}" destDir="${clients.src.dir}" packageName="${clients.package.name}"/>
<javac srcdir="${clients.src.dir}" destdir="${clients.dest.dir}" includes="${clients.package.path}/*.java" debug="on" classpathref="javabuild.class.path" />
</target>
<target name="build.ejbDriver">
<javac srcdir="${ejbApp.src.dir}" destdir="${clients.dest.dir}" includes="*.java" classpathref="javabuild.class.path" />
</target>
build.jbClient 与build.ejbDriver和Javabean中的对应目标也类似。唯一区别的是在clientgen任务中使用的WSDL文件不是来自URL,而是来自build.ejb生成的JAR包。使用local或URL方式对其他客户端代码没有影响,基本这两种方式可以互换使用。
2. 生成并编译客户端代码
现在我们在ANT View中执行任务build.jbClient,可以看到在Console窗口显示出对应的操作过程。
Buildfile: C:/project/eclipse31/workspace/JSR-181 Web Services/build.xml
build.ejbClient:
[clientgen] Generating client from jar:file:C:/temp/ejb/PurchaseOrder.jar!/META-INF/PurchaseOrderService.wsdl …
[clientgen] Getting partner link
[clientgen] Package name is client.test
[clientgen] DestDir is C:/project/eclipse31/workspace/JSR-181 Web Services
[clientgen] class name is PurchaseOrderPort_Stub
[clientgen] service class name is PurchaseOrderService
[clientgen] Porttype name is PurchaseOrderPort
[clientgen] service impl name is PurchaseOrderService_Impl
[javac] Compiling 4 source files to C:/temp
BUILD SUCCESSFUL
Total time: 11 seconds
回到Package Explorer视图的包client.test。能看到里面又增加了若干JAVA文件,XML文件和WSDL文件。这些文件就是自动生成的客户端代码。打开资源管理器到C:/TEMP/client/test,能看到生成的客户端代码的class文件。
我们注意到,一共生成了4个类,StringChangerPort和StringChangerPort_Stub是我们在StringChanger类中用@WebService(name="StringChangerPort",......)指定的。StringChangerService和StringChangerService_Impl是我们在StringChanger类中用@WLHttpTransport(......, serviceUri=”StringChangerService”)中指定的。
3. 创建并编译一个测试类
下面我们在webservices.jsr181.ejb中创建一个类TestDriver_EJB,写入如下内容:
package webservices.jsr181.ejb;
import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;
import client.test.PurchaseOrderPort;
import client.test.PurchaseOrderService;
import client.test.PurchaseOrderService_Impl;
public class TestDriver_EJB
{
public static void main(String[] args)
throws ServiceException, RemoteException{
String url = “http://localhost:7001/ejb/PurchaseOrderService?WSDL”;
PurchaseOrderService service = new PurchaseOrderService_Impl(url);
PurchaseOrderPort port = service.getPurchaseOrderPort();
String testString = “This is my test String ;-)”;
System.out.println(port.toUpper(testString));
}
}
这里面的内容和在Javabean中也没什么区别。因此我们可以看出来,在Web Service后端使用什么方式实现,在前端看是完全透明的。
4. 运行测试类观察结果
右键点文件TestDriver_EJB.java,选择“run as…=> java application”。这时我们在Console能看到运行结果。如果一切正确:
THIS IS MY TEST STRING ;-)
到此,我们完成了这个基于EJB的Web Services的例子。事实上我们可以用各种方法创建Web Services的业务逻辑,从而轻松的实现系统组件间的松耦合。