??? 在企业应用开发中,为了实现应用之间的松耦合,避免应用之间的过度依赖,可以使用WebService和socket通信这两种跨平台的解决方案。因为openjweb框架中整合了apache cxf框架,所以openjweb快速开发平台本身也是一个WebService服务器。在openjweb平台中为各子系统设计cxf webservice服务器端接口服务,可以实现其他异构应用系统请求openjweb各子系统提供的服务。比如其他应用系统从openjweb中获取openjweb系统里的部门信息。
??? 在openjweb中用cxf开发webservice非常简单,一般可针对每个子系统设计一个webservice 服务器端组件,具体接口功能可视具体应用而定。下面介绍在openjweb中开发cxf服务端接口的过程:
??? (1)首先在D:projectopenjwebsrcjavaapplicationContext-cxf.xml中,定义一个webservice服务器端组件(当前示例为门店子系统定义一个服务器端组件):
???????????<!--门店服务端-->
?
?<jaxws:endpoint id="IStoreWS" implementor="com.tzsoft.store.ws.StoreWSImpl" address="/storeWS">
????? <jaxws:inInterceptors>
???? <bean? class="org.openjweb.core.webservice.cxf.service.ServerAuthIntercepter"></bean>
??????? </jaxws:inInterceptors>
?</jaxws:endpoint>
??????? 这里要实现一个门店应用的WebService服务器端接口类IStoreWS.java和实现类StoreWSImpl.java,注意IStoreWS.java和应用系统自己的业务逻辑接口不同,IStoreWS.java
声明的只是为了给外部调用的WebService接口,在职责上和子系统内部声明的业务逻辑接口是有不同用途的。前者是粗粒度的,后者是细粒度的。下面是IStoreWS.java代码:
package com.tzsoft.store.ws;
import javax.jws.WebParam;
import javax.jws.WebService;
/**
?* WebService接口定义类.
?*
?* 使用@WebService将接口中的所有方法输出为Web Service.
?* 可用annotation对设置方法、参数和返回值在WSDL中的定义.
?*/
@WebService
public interface IStoreWS
{
?/**
? * 上传会员卡号
? * @param comId 单位编码
? * @param deptId 部门编码
? * @param memCard
? * @return
? */
?
?String uploadMemCard(
???@WebParam(name = "comId")String comId,
???@WebParam(name = "deptId")String deptId,
???@WebParam(name = "memCard")String memCard) ;
}
?
?
下面是实现类:
package com.tzsoft.store.ws;
import javax.annotation.Resource;
import javax.jws.WebService;
import com.tzsoft.store.service.IStoreService;
//import org.openjweb.core.service.IUserAuthService;
@WebService(endpointInterface = "com.tzsoft.store.ws.IStoreWS")
public class StoreWSImpl implements IStoreWS
{
?@Resource(name = "IStoreService")
?IStoreService storeService;
?
?public String uploadMemCard(String comId,String deptId,String memCard)?
?{
??String s = "success";
??try
??{
???System.out.println("下面会员卡调用.....");
???
???storeService.receiveMemCard(comId,deptId,memCard);
??}
??catch(Exception ex)
??{
???
??}
??return "哈哈 ,成功了耶!";
?}
?
}
注意代码里的@WebService注解的使用。
因为WebService是跨平台的调用,所以可以向指定的WebService地址发送SOAP包就可以了。下面介绍如何向门店WebService地址发送调用请求并返回调用结果。
操作过程:
(1)建立一个批处理文件,内容:
??????? java -classpath . SOAPClient4XG http://localhost:8088/portal/services/storeWS cxf4.xml
(2)创建一个SOAPClient4XG.java类并编译,此类用于将基于SOAP格式的xml请求文件发送到WebService地址执行WebService调用,并返回执行结果:
?????
import java.io.*;
import java.net.*;
public class SOAPClient4XG {
??? public static void main(String[] args) throws Exception {
??????? if (args.length? < 2) {
??????????? System.err.println("Usage:? java SOAPClient4XG " +
?????????????????????????????? "http://soapURL soapEnvelopefile.xml" +
?????????????????????????????? " [SOAPAction]");
????System.err.println("SOAPAction is optional.");
??????????? System.exit(1);
??????? }
??????? String SOAPUrl????? = args[0];
??????? String xmlFile2Send = args[1];
??? String SOAPAction = "";
??????? if (args.length? > 2)
????SOAPAction = args[2];
????
??????? // Create the connection where we're going to send the file.
??????? URL url = new URL(SOAPUrl);
??????? URLConnection connection = url.openConnection();
??????? HttpURLConnection httpConn = (HttpURLConnection) connection;
??????? // Open the input file. After we copy it to a byte array,we can see
??????? // how big it is so that we can set the HTTP Cotent-Length
??????? // property. (See complete e-mail below for more on this.)
??????? FileInputStream fin = new FileInputStream(xmlFile2Send);
??????? ByteArrayOutputStream bout = new ByteArrayOutputStream();
???
??????? // Copy the SOAP file to the open connection.
??????? copy(fin,bout);
??????? fin.close();
??????? byte[] b = bout.toByteArray();
??????? // Set the appropriate HTTP parameters.
??????? httpConn.setRequestProperty( "Content-Length",
???????????????????????????????????? String.valueOf( b.length ) );
??????? httpConn.setRequestProperty("Content-Type","text/xml; charset=utf-8");
??? httpConn.setRequestProperty("SOAPAction",SOAPAction);
??????? httpConn.setRequestMethod( "POST" );
??????? httpConn.setDoOutput(true);
??????? httpConn.setDoInput(true);
??????? // Everything's set up; send the XML that was read in to b.
??????? OutputStream out = httpConn.getOutputStream();
??????? out.write( b );???
??????? out.close();
??????? // Read the response and write it to standard out.
??????? InputStreamReader isr =
??????????? new InputStreamReader(httpConn.getInputStream());
??????? BufferedReader in = new BufferedReader(isr);
??????? String inputLine;
??????? while ((inputLine = in.readLine()) != null)
??????????? System.out.println(inputLine);
??????? in.close();
??? }
? // copy method from From E.R. Harold's book "Java I/O"
? public static void copy(InputStream in,OutputStream out)
?? throws IOException {
??? // do not allow other threads to read from the
??? // input or write to the output while copying is
??? // taking place
??? synchronized (in) {
????? synchronized (out) {
??????? byte[] buffer = new byte[256];
??????? while (true) {
????????? int bytesRead = in.read(buffer);
????????? if (bytesRead == -1) break;
????????? out.write(buffer,bytesRead);
??????? }
????? }
??? }
? }
}
(3)创建一个xml请求调用文件,格式是cxf的客户端xml书写格式:
???????
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
??? <soap:Header>
? <auth:authentication xmlns:auth="http://localhost:8088/portal//authentication">
??<auth:systemID>1</auth:systemID>
??<auth:userID>test</auth:userID>
??<auth:password>test</auth:password>
?</auth:authentication>
?</soap:Header>
??? <soap:Body>
??????? <ns1:uploadMemCard xmlns:ns1="http://ws.store.tzsoft.com/">
??????????? <comId>C0001</comId>
??????????? <deptId>C0001</deptId>
??????????? <memCard>0001</memCard>
??????? </ns1:uploadMemCard>
??? </soap:Body>
</soap:Envelope>
上面的uploadMemCard是调用门店webService的上传会员卡号的方法,注意xmlns:ns1=http://ws.store.tzsft.com/"这个对应的IStoreWS的目录路径,不过是反着写的。
(4)执行批处理文件会返回调用结果:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:uploadMemCardResponse xmlns:ns2="http://ws.store.tzsoft.com/"><return>哈哈
?,成功了耶!</return>
</ns2:uploadMemCardResponse>
</soap:Body>
</soap:Envelope>
?
这种调用方式是发送xml流的方式,特点是不受编程语言的限制。但是需要知道发送 的SOAP包的具体格式。另外对响应包也要进行xml解析才能取得具体结果。
?
如果使用Java客户端调用,可参考下面的例子(这个例子带了一个加密头,即SOAP Header中嵌入用户名和密码):
public class TestCxfClient
{
public static String testStoreWS(String wsAddr,String comId,String memCard,boolean isSecret)
?{
??JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
??AddSoapHeader ash = new AddSoapHeader();
??if(isSecret)
??{
???logger.info("带加密头a............!");
???ArrayList list = new ArrayList();
????? // 添加soap header 信息
????? list.add(ash);
????? //注入拦截器,getOutInterceptors代表调用服务端时触发,getInInterceptors就是被调用才触发
?????? factory.setOutInterceptors(list);
??}
??else
??{
???System.out.println("不带加密头,如果不带加密头,需去掉applicationContext-cxf.xml中配置的拦截器............");
??}
??
??factory.setServiceClass(IStoreWS.class);//实例化ws
??factory.setAddress(wsAddr);
??Object obj = factory.create();
??IStoreWS service = (IStoreWS)obj;?
??//下面这行代码是具体调用服务段的deleteTeskTask()
??String msg = "";
???? try
???? {
???? ?msg = service.uploadMemCard(comId,memCard);
???? }
???? catch(Exception ex)
???? {
???? ?ex.printStackTrace();
???? ?logger.error(ex.toString());
???? }
??return msg;
?}
}
为了在客户端调用时增加SOAP Header以便在 SOAP Header中写入用户名和口令,需要在applicationContext-cxf.xml配置文件中加入拦截器:
??? <jaxws:client id="clientTestService" serviceClass="org.openjweb.core.webservice.cxf.service.TestCxfClient"
??????? address="http://localhost:8088/portal/services/cxfDemoWS">??????? <jaxws:outInterceptors>????? <bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor" />?? ??????????? <ref bean="wsOutInterceptor"/>??????? </jaxws:outInterceptors>??? </jaxws:client>