通向架构师的道路(第十二天)之Axis2 Web Service(三)

一、SOAPIn Axis2

在前两天的教程中,我们学习到了用Axis2如何进行复杂数据、简单数据进行传输。

正如我在前一天教程中所说,在web service的世界里,一切都是基于SOAP的,因此在今天我们将学习Axis2中的SOAP特性。

今天的课程将用3个例子来完成即:

  1. 客户端与服务端使用SOAP进行通讯
  2. 服务端将Exception以SOAPFault的形式抛给客户端
  3. 使用SWA(Soap With Attachment)来进行附件传送

二、客户端与服务端使用SOAP进行通讯

来看下面这个Web Service:

 

下面是Service端的源码

org.sky.axis2.soap.SoapService

package org.sky.axis2.soap;

 

import org.apache.axiom.om.OMAbstractFactory;

import org.apache.axiom.om.OMElement;

import org.apache.axiom.om.OMFactory;

import org.apache.axiom.om.OMNamespace;

import java.util.*;

 

public class SoapService {

 

         public static OMElement requestSoap = null;

 

         public OMElement request(OMElement soapBody) {

                   requestSoap = soapBody;

 

                   Iterator it = requestSoap.getChildElements();

                   OMElement issuerElement = (OMElement) it.next();

                   OMElement serialElement = (OMElement) it.next();

                   OMElement revocationDateElement = (OMElement) it.next();

 

                   String issuer = issuerElement.getText();

                   String serial = serialElement.getText();

                   String revocationDate = revocationDateElement.getText();

                   System.out.println("issuer=====" + issuer);

                   System.out.println("serial=====" + serial);

                   System.out.println("revocationDate=====" + revocationDate);

                   OMFactory soapFactory = OMAbstractFactory.getOMFactory();

                   OMNamespace omNs = soapFactory.createOMNamespace(

                                     "http://soap.axis2.sky.org", "");

                   OMElement soapResponse = soapFactory.createOMElement("SoapResponse",

                                     omNs);

 

                   OMElement soapIssuer = soapFactory.createOMElement("Issuer", omNs);

                   soapIssuer.setText("issuer: " + issuer);

                   soapResponse.addChild(soapIssuer);

 

                   OMElement soapSerial = soapFactory.createOMElement("Serial", omNs);

                   soapSerial.setText("serial: " + serial);

                   soapResponse.addChild(soapSerial);

 

                   OMElement soapRevokeDate = soapFactory.createOMElement("RevokeDate",

                                     omNs);

                   soapRevokeDate.setText("RevocationDate: " + revocationDate);

                   soapResponse.addChild(soapRevokeDate);

                   soapResponse.build();

 

                   return soapResponse;

         }

 

}

来看它的service.xml的描述

<service name="SoapService">

         <description>

                   This is the service for revoking certificate.

         </description>

         <parameter name="ServiceClass" locked="false">

                   org.sky.axis2.soap.SoapService

         </parameter>

         <operation name="request">

                   <messageReceiver

                            class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver" />

                   <actionMapping>urn:request</actionMapping>

         </operation>

</service>

该Web Service接受一个Soap请求,该请求为如下格式:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://soap.axis2.sky.org">

   <soapenv:Header/>

   <soapenv:Body>

      <soap:request>

           <soap:request>?</soap:request>

      </soap:request>

   </soapenv:Body>

</soapenv:Envelope>

其中<soap:request></soap:request>中间的内容,应该如下所示:

<Request xmlns="http://10.225.104.122">

    <Issuer>1234567890</Issuer>

    <Serial>11111111</Serial>

    <RevokeDate>2007-01-01</RevokeDate>

</ Response >

我们假设它是一个购买图书的定单,服务端收到这个请求后会返回一个定单信息给调用它的客户端,服务端将返回如下内容(此处不做任何业务处理,只是很简单的传值回客户端)。

<SoapResponse xmlns="http://soap.axis2.sky.org">

    <Issuer>issuer: Wrox</Issuer>

    <Serial>serial: 1111111111ISBN</Serial>

    <RevokeDate>RevocationDate: 2012-07-29</RevokeDate>

</SoapResponse>

为生成上述这个SoapResponse我们在Service端的核心代码如上面加粗部分的代码所示,由其注意这个“soapResponse.build();”。

下面我们来看这个客户端是怎么写的,我们这边用的是非阻塞式客户端

org.sky.axis2.soap.SoapServiceClient

package org.sky.axis2.soap;

 

import org.apache.axiom.om.OMAbstractFactory;

import org.apache.axiom.om.OMElement;

import org.apache.axiom.om.OMFactory;

import org.apache.axiom.om.OMNamespace;

import org.apache.axis2.AxisFault;

import org.apache.axis2.Constants;

import org.apache.axis2.addressing.EndpointReference;

import org.apache.axis2.client.Options;

import org.apache.axis2.client.ServiceClient;

import org.apache.axis2.client.async.AxisCallback;

import org.apache.axis2.context.MessageContext;

import javax.xml.namespace.QName;

 

public class SoapServiceClient {

         private static EndpointReference targetEPR = new EndpointReference(

                            "http://localhost:8080/Axis2Service/services/SoapService");

         public static boolean finish = false;

         public static void orderRequest() {

                   OMFactory factory = OMAbstractFactory.getOMFactory();

                   OMNamespace omNs = factory.createOMNamespace(

                                     "http://soap.axis2.sky.org", "");

                   OMElement issuer = factory.createOMElement("Issuer", omNs);

                   OMElement serial = factory.createOMElement("Serial", omNs);

                   OMElement revocationDate = factory.createOMElement("RevocationDate",

                                     omNs);

                   issuer.setText("Wrox");

                   serial.setText("1111111111ISBN");

                   revocationDate.setText("2012-07-29");

                   OMElement requestSoapMessage = factory.createOMElement("request", omNs);

                   requestSoapMessage.addChild(issuer);

                   requestSoapMessage.addChild(serial);

                   requestSoapMessage.addChild(revocationDate);

                   requestSoapMessage.build();

                   Options options = new Options();

                   options.setTo(targetEPR);

                   ServiceClient sender = null;

                   try {

                            AxisCallback callback = new AxisCallback() {

                                     public void onMessage(MessageContext msgContext) {

                                               OMElement result = msgContext.getEnvelope().getBody()

                                                                 .getFirstElement();

                                               // System.out.println(msgContext.toString());

                                               // System.out.println(msgContext.getEnvelope().toString());

                                               System.out.println(msgContext.getEnvelope().getBody()

                                                                 .getFirstElement());

                                               finish = true;

                                     }

                                     public void onFault(MessageContext msgContext) {

                                               System.out.println(msgContext.getEnvelope().getBody()

                                                                 .getFault().toString());

                                     }

                                     public void onError(Exception e) {

                                     }

                                     public void onComplete() {

                                               System.out.println("Completed!!!");

                                     }

                            };

                            sender = new ServiceClient();

                            sender.setOptions(options);

                            System.out.println("-------Invoke the service---------");

                            sender.sendReceiveNonBlocking(requestSoapMessage, callback);

                            synchronized (callback) {

                                     if (!finish) {

                                               try {

                                                        callback.wait(1000);

                                               } catch (Exception e) {

                                               }

                                     }

                                     if (!finish) {

                                               throw new AxisFault(

                                                                 "Server was shutdown as the async response take too long to complete");

                                     }

                            }

                   } catch (AxisFault e) {

                            e.printStackTrace();

                   } finally {

                            if (sender != null)

                                     try {

                                               sender.cleanup();

                                     } catch (Exception e) {

                                     }

                   }

 

         }

         public static void main(String[] args) {

                   orderRequest();

         }

}

上述代码和前两天的客户端代码没啥区别,我已经把核心代码用红色给标粗了。

运行后行得到输出

客户端运行后的输出:

 

服务端的输出:

三、服务端将Exception以SOAPFault的形式抛给客户端

上面这个例子很简单,它展示了一个客户端向服务端发送一个request,服务端接收到客户端的Request(OMElement类型)后解析并根据相应的业务逻辑向客户端再返回一个response(OMElement类型)的完整过程。

下面我们要来看的是,如果客户端在调用服务器时发生任何错误,服务端如何把这个错误经过包装后再返回给客户端的例子。

还记得我们在非阻塞式客户端中有如下这样的触发器吗?

public void onMessage(MessageContext msgContext) {

}

public void onFault(MessageContext msgContext) {

}

public void onError(Exception e) {

}

public void onComplete() {

}

此处的onFault就是用于接受从服务端抛过来的Exception的,我们把它称为SOAPFault。

下面来看一个例子,先来看Service端

org.sky.axis2.soap.SoapFaultService

package org.sky.axis2.soap;

 

import org.apache.axiom.om.OMAbstractFactory;

import org.apache.axiom.om.OMElement;

import org.apache.axiom.om.OMFactory;

import org.apache.axiom.om.OMNamespace;

import org.apache.axiom.soap.SOAPFactory;

import org.apache.axiom.soap.SOAPFault;

import org.apache.axiom.soap.SOAPFaultCode;

import org.apache.axiom.soap.SOAPFaultReason;

import org.apache.axis2.AxisFault;

import org.apache.axis2.context.MessageContext;

public class SoapFaultService {

         private int i = 0;

         public OMElement getPrice(OMElement request) throws AxisFault {

                   if (request == null) {

                            SOAPFault fault = getSOAPFault();

                            return fault;

                   }

                   OMFactory factory = OMAbstractFactory.getOMFactory();

                   OMNamespace ns = factory.createOMNamespace("", "");

                   OMElement response = factory.createOMElement("Price", ns);

                   response.setText(String.valueOf(i++));

                   return response;

         }

         private SOAPFault getSOAPFault() {

                   MessageContext context = MessageContext.getCurrentMessageContext();

                   SOAPFactory factory = null;

                   if (context.isSOAP11()) {

                            factory = OMAbstractFactory.getSOAP11Factory();

                   } else {

                            factory = OMAbstractFactory.getSOAP12Factory();

                   }

                   SOAPFault fault = factory.createSOAPFault();

                   SOAPFaultCode faultCode = factory.createSOAPFaultCode(fault);

                   faultCode.setText("13");

                   factory.createSOAPFaultValue(faultCode);

                   SOAPFaultReason faultReason = factory.createSOAPFaultReason(fault);

                   faultReason.setText("request can not be null");

                   factory.createSOAPFaultText(faultReason);

                   factory.createSOAPFaultDetail(fault);

                   return fault;

         }

}

注意加粗部分的代码,由其是标成红色的代码为核心代码。

来看Service描述:

<service name="SoapFaultService">

         <Description>

                   Please Type your service description here

         </Description>

         <parameter name="ServiceClass" locked="false">org.sky.axis2.soap.SoapFaultService

         </parameter>

         <operation name="getPrice">

                   <messageReceiver

                            class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver" />

                   <actionMapping>urn:getPrice</actionMapping>

         </operation>

</service>

上述这个WebService接受一个输入的参数,如果输入的内容为空,则返回一个SoapFault,即键值为13,内容为” request can not be null”。

我们来看客户端的代码

org.sky.axis2.soap.SoapFaultClient

package org.sky.axis2.soap;

import java.util.Iterator;

import javax.xml.namespace.QName;

import org.apache.axiom.om.OMAbstractFactory;

import org.apache.axiom.om.OMElement;

import org.apache.axiom.om.OMFactory;

import org.apache.axiom.om.OMNamespace;

import org.apache.axis2.AxisFault;

import org.apache.axis2.Constants;

import org.apache.axis2.addressing.EndpointReference;

import org.apache.axis2.client.Options;

import org.apache.axis2.client.ServiceClient;

import org.apache.axis2.client.async.AxisCallback;

import org.apache.axis2.context.MessageContext;

 

public class SoapFaultClient {

         static boolean finish = false;

         public static void main(String[] args) {

                   EndpointReference epr = new EndpointReference(

                   "http://localhost:8080/Axis2Service/services/SoapFaultService");

                   ServiceClient sender = null;

                   try {

                            OMFactory factory = OMAbstractFactory.getOMFactory();

                            OMNamespace ns = factory.createOMNamespace(

                                               "http://soap.axis2.sky.org", "");

                            OMElement request = factory.createOMElement("Price", ns);

                            Options options = new Options();

                            options.setAction("urn:getPrice");

                            options.setTo(epr);

                            options.setTransportInProtocol(Constants.TRANSPORT_HTTP);

                            options.setUseSeparateListener(true);

                            AxisCallback callback = new AxisCallback() {

                                     public void onMessage(MessageContext msgContext) {

                                               OMElement result = msgContext.getEnvelope().getBody()

                                                                 .getFirstElement();

                                               OMElement priceElement = result;

                                               System.out.println("price====" + priceElement.getText());

                                               finish = true;

                                     }

                                     public void onFault(MessageContext msgContext) {

                                               QName errorCode = new QName("faultcode");

                                               QName reason = new QName("faultstring");

                                               // System.out.println("on

                                               // fault:"+msgContext.getEnvelope().getBody().getFault().toString());

                                               OMElement fault = msgContext.getEnvelope().getBody()

                                                                 .getFault();

                                               System.out.println("ErrorCode["

                                                                 + fault.getFirstChildWithName(errorCode).getText()

                                                                 + "] caused by: "

                                                                 + fault.getFirstChildWithName(reason).getText());

                                     }

 

                                     public void onError(Exception e) {

                                     }

 

                                     public void onComplete() {

                                               System.out.println("OnComplete!!!");

                                     }

                            };

                            sender = new ServiceClient();

                            sender.setOptions(options);

                            sender.engageModule("addressing");

                            try {

 

                                     // sender.sendReceiveNonBlocking(request, callback);

                                     sender.sendReceiveNonBlocking(null, callback);

                            } catch (AxisFault e) {

                                     System.out.println("Exception occur!");

                                     System.out.println(e.getMessage());

                            }

                            synchronized (callback) {

                                     if (!finish) {

                                               try {

                                                        callback.wait(1000);

                                               } catch (Exception e) {

                                               }

                                     }

                            }

                   } catch (AxisFault e) {

                            e.printStackTrace();

                            System.out.println(e.getMessage());

                   } finally {

 

                            try {

                                     sender.cleanup();

                            } catch (Exception e) {

                            }

                   }

 

         }

}

注意红色并加粗部分的代码,为了抓到服务端抛过来的SoapFault我们必须使用非阻塞式,因此我们在onFault处,进行接受服务端错误的处理。

注意:

我们调用Service端时没有传入Service端所需要的request的参数:

// sender.sendReceiveNonBlocking(request,callback);

sender.sendReceiveNonBlocking(null,callback);

这将构成Service端抛出SoapFault。

来看运行效果:

 

四、使用SWA(Soap WithAttachment)来进行附件传送

有了上面两个例子的基础后,我们将使用这个例子来结束Axis2中的Soap特性的教学。

在Axis2中传输附件有两种形式,一种叫MTOM,一种就是SWA。

SWAP即Soap With Attachment,这是业界的标准。

所谓的SWA传输,即客户端把需要上传的文件,编译成两进制代码凌晨随着soap的request一起推送到服务端,该两进制代码以AttachmentId的形式来表示,即如下这样的一个soap body:

<soapenv:Body>

<uploadFile xmlns="http://attachment.axis2.sky.org">

<name>test.jpg</name>

<attchmentID>urn:uuid:8B43A26FEE1492F85A1343628038693</attchmentID>

</uploadFile>

</soapenv:Body>

服务端收到该soap的request可以直接使用如下的语句将这个AttachmentId还原成输出流:

DataHandler dataHandler = attachment.getDataHandler(attchmentID);

File file = new File(uploadFilePath.toString());

fileOutputStream = new FileOutputStream(file);

dataHandler.writeTo(fileOutputStream);

fileOutputStream.flush();

在我们这个例子内,我们将使用客户端上传一个jpg文件,服务端收到该jpg文件(可以是任何的两进制文件)后解析后存入服务端的一个目录。

先来看服务端代码

org.sky.axis2.attachment.FileUploadService

package org.sky.axis2.attachment;

 

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

 

import javax.activation.DataHandler;

 

import org.apache.axiom.attachments.Attachments;

import org.apache.axis2.context.MessageContext;

import org.sky.axis2.util.UUID;

 

public class FileUploadService {

         public String uploadFile(String name, String attchmentID) throws Exception {

                   FileOutputStream fileOutputStream = null;

                   StringBuffer uploadFilePath = new StringBuffer();

                   String fileNamePrefix = "";

                   String fileName = "";

                   try {

                            MessageContext msgCtx = MessageContext.getCurrentMessageContext();

                            Attachments attachment = msgCtx.getAttachmentMap();

                            DataHandler dataHandler = attachment.getDataHandler(attchmentID);

                            fileNamePrefix = name.substring(name.indexOf("."), name.length());

                            fileName = UUID.getUUID();

                            System.out.println("fileName=====" + fileName);

                            System.out.println("fileNamePrefix====" + fileNamePrefix);

                            uploadFilePath.append("D:/upload/axis2/");

                            uploadFilePath.append(fileName);

                            uploadFilePath.append(fileNamePrefix);

                            System.out

                                               .println("uploadFilePath====" + uploadFilePath.toString());

                            File file = new File(uploadFilePath.toString());

                            fileOutputStream = new FileOutputStream(file);

                            dataHandler.writeTo(fileOutputStream);

                            fileOutputStream.flush();

                   } catch (Exception e) {

                            throw new Exception(e);

                   } finally {

                            try {

                                     if (fileOutputStream != null) {

                                               fileOutputStream.close();

                                               fileOutputStream = null;

                                     }

                            } catch (Exception e) {

                            }

                   }

                   return "File saved succesfully.";

         }

}

下面是服务端的描述

 

service.xml文件的内容为:

<service name="AttachmentService">

         <parameter name="ServiceClass">org.sky.axis2.attachment.FileUploadService

         </parameter>

         <operation name="uploadFile">

                   <actionMapping>urn:uploadFile</actionMapping>

                   <messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver" />

         </operation>

</service>

该服务端接受客户端上传的附件后使用UUID重新命名上传的文件名,并将其存入服务端的” D:/upload/axis2/”目录中。

来看客户端代码

org.sky.axis2.attachment.FileUploadClient

package org.sky.axis2.attachment;

 

import java.io.File;

 

import javax.activation.DataHandler;

import javax.activation.FileDataSource;

import javax.xml.namespace.QName;

 

import org.apache.axiom.om.OMAbstractFactory;

import org.apache.axiom.om.OMElement;

import org.apache.axiom.om.OMNamespace;

import org.apache.axiom.soap.SOAP11Constants;

import org.apache.axiom.soap.SOAPBody;

import org.apache.axiom.soap.SOAPEnvelope;

import org.apache.axiom.soap.SOAPFactory;

import org.apache.axis2.Constants;

import org.apache.axis2.addressing.EndpointReference;

import org.apache.axis2.client.OperationClient;

import org.apache.axis2.client.Options;

import org.apache.axis2.client.ServiceClient;

import org.apache.axis2.context.ConfigurationContext;

import org.apache.axis2.context.ConfigurationContextFactory;

import org.apache.axis2.context.MessageContext;

import org.apache.axis2.wsdl.WSDLConstants;

 

public class FileUploadClient {

         private static EndpointReference targetEPR = new EndpointReference(

                            "http://localhost:8080/Axis2Service/services/AttachmentService");

 

         public static void main(String[] args) throws Exception {

                   new FileUploadClient().transferFile();

         }

 

         public void transferFile() throws Exception {

                   String filePath = "D:/deployment/test.jpg";

                   String destFile = "test.jpg";

                   Options options = new Options();

                   options.setTo(targetEPR);

                   options.setProperty(Constants.Configuration.ENABLE_SWA,

                                     Constants.VALUE_TRUE);

                   options.setSoapVersionURI(SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI);

                   options.setTimeOutInMilliSeconds(10000);

                   options.setTo(targetEPR);

                   options.setAction("urn:uploadFile");

                   ConfigurationContext configContext = ConfigurationContextFactory

                                     .createConfigurationContextFromFileSystem(

                                                        "D:/wspace/Axis2Service/WebContent/WEB-INF/modules",

                                                        null);

 

                   ServiceClient sender = new ServiceClient(configContext, null);

                   sender.setOptions(options);

                   OperationClient mepClient = sender

                                     .createClient(ServiceClient.ANON_OUT_IN_OP);

 

                   MessageContext mc = new MessageContext();

                   FileDataSource fileDataSource = new FileDataSource(new File(filePath));

 

                   // Create a dataHandler using the fileDataSource. Any implementation of

                   // javax.activation.DataSource interface can fit here.

                   DataHandler dataHandler = new DataHandler(fileDataSource);

                   String attachmentID = mc.addAttachment(dataHandler);

 

                   SOAPFactory fac = OMAbstractFactory.getSOAP11Factory();

                   SOAPEnvelope env = fac.getDefaultEnvelope();

                   OMNamespace omNs = fac.createOMNamespace(

                                     "http://attachment.axis2.sky.org", "");

                   OMElement uploadFile = fac.createOMElement("uploadFile", omNs);

                   OMElement nameEle = fac.createOMElement("name", omNs);

                   nameEle.setText(destFile);

                   OMElement idEle = fac.createOMElement("attchmentID", omNs);

                   idEle.setText(attachmentID);

                   uploadFile.addChild(nameEle);

                   uploadFile.addChild(idEle);

                   env.getBody().addChild(uploadFile);

                   System.out.println("message====" + env);

                   mc.setEnvelope(env);

 

                   mepClient.addMessageContext(mc);

                   mepClient.execute(true);

                   MessageContext response = mepClient

                                     .getMessageContext(WSDLConstants.MESSAGE_LABEL_IN_VALUE);

                   SOAPBody body = response.getEnvelope().getBody();

                   OMElement element = body.getFirstElement().getFirstChildWithName(

                                     new QName("http://attachment.axis2.sky.org", "return"));

                   System.out.println(element.getText());

         }

}

注意红色加粗部分的代码,由其是:

FileDataSource fileDataSource = new FileDataSource(new File(filePath));

String attachmentID = mc.addAttachment(dataHandler);

这两句就是把客户端需要上传的附件转成AttachmentId的语句,然后把这个AttachementId作为一个OMElement的类型加入到客户端的soap request中去即可:

OMElement idEle = fac.createOMElement("attchmentID", omNs);

idEle.setText(attachmentID);

uploadFile.addChild(nameEle);

uploadFile.addChild(idEle);

env.getBody().addChild(uploadFile);

来看运行效果。

客户端:

上传d:/deployment/test.jpg文件

客户端收到服务端返回的”File saved successfully”即可在服务端的” D:/upload/axis2”目录中查询是否成功上传了该文件了

可以看到,由于我们使用的是UUID因此每次上传,服务端的文件名都不会重复。

附录 UUID.java

package org.sky.axis2.util;

 

public class UUID {

         protected static int count = 0;

 

         public static synchronized String getUUID() {

                   count++;

                   long time = System.currentTimeMillis();

 

                   String timePattern = Long.toHexString(time);

                   int leftBit = 14 - timePattern.length();

                   if (leftBit > 0) {

                            timePattern = "0000000000".substring(0, leftBit) + timePattern;

                   }

 

                   String uuid = timePattern

                                     + Long.toHexString(Double.doubleToLongBits(Math.random()))

                                     + Long.toHexString(Double.doubleToLongBits(Math.random()))

                                     + "000000000000000000";

 

                   uuid = uuid.substring(0, 32).toUpperCase();

 

                   return uuid;

         }

}

 



相关文章

发表评论

Comment form

(*) 表示必填项

还没有评论。

跳到底部
返回顶部