2010년 6월 29일 화요일

MTOM 가능한 웹서비스 개발(CXF)

Resources:
JAX-WS에서 MTOM 사용 가능

바이너리 파일 첨부를 사용하는 서비스에서 요청 객체에 DataHandler를 프로퍼티를 사용하여 바이너리 데이터를 전송할 수 있다. 해당 프로퍼티에 XmlMimeType을 "application/octet-stream"으로 지정한다.

요청 객체 샘플


package mypackage;

import javax.activation.DataHandler;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlMimeType;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
@XmlType
public class UploadImeiFileRequest {

private String uploadUserId;


private DataHandler imeiFile;

public String getUploadUserId() {
return uploadUserId;
}
public void setUploadUserId(String uploadUserId) {
this.uploadUserId = uploadUserId;
}
@XmlMimeType("application/octet-stream")
public DataHandler getImeiFile() {
return imeiFile;
}
public void setImeiFile(DataHandler imeiFile) {
this.imeiFile = imeiFile;
}

}



Service Endpoint Interface


CXF에서는 BindingType을 어노테이션을 반드시 지정해 주어야 할 필요는 없지만 웹서비스를 명시적으로 MTOM 가능하도록 나타내도록 한다. 또한 MTOM 어노테이션을 사용하기도 한다.
package com.mycompany.ws;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.xml.ws.BindingType;

import com.sds.shp.webservice.message.UploadImeiFileRequest;
import com.sds.shp.webservice.message.UploadImeiFileResponse;

@WebService(name="imeiFileWebService", 
serviceName="imeiFileWebService",
targetNamespace="http://mocompany.com/ws/fileupload")
@BindingType(javax.xml.ws.soap.SOAPBinding.SOAP11HTTP_MTOM_BINDING)  
@javax.xml.ws.soap.MTOM
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)  
public interface FileUploadWebService{

@WebMethod(operationName="uploadImeiFile")
@WebResult(name="UploadUserImageResponse")
UploadImeiFileResponse uploadImeiFile(@WebParam(name="UploadImeiFile") UploadImeiFileRequest request) throws Exception;


}



서비스 cxf 설정 파일


jaxws:endpoint의 jaxws:properties 의 "mtom-enabled" 속성을 "true"로 지정한다.

<jaxws:endpoint id="fileUploadService" 
implementor="com.mycompany.ws.FileUploadWebServiceImpl"
address="/fileUploadWebService">
<jaxws:properties>
<entry key="mtom-enabled" value="true"/>
</jaxws:properties>
</jaxws:endpoint>


클라이언트 샘플1. Dispatch 사용


CXF를 전혀 사용하지 않고 Java 표준 api만으로 파일 첨부 클라이언트를 구현한 예제다.

클라이언트 샘플2. CXF의 JaxWsProxyFactoryBean 사용


CXF 환경 설정 없이 SEI 만으로 클라이언트를 생성하여 사용한다.

클라이언트 샘플3. 스프링 빈 등록 후 클라이언트 사용


스프링 설정 파일에 클라이언트를 등록한 후 Injection 객체를 사용한다.

package com.sds.shp.webservice.server;

import java.io.File;
import java.net.URL;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.annotation.Resource;
import javax.xml.namespace.QName;
import javax.xml.soap.AttachmentPart;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPBodyElement;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPBinding;

import org.apache.cxf.common.util.SOAPConstants;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import mypackage.LogManager;
import mypackage.PropertyNames;
import mypackage.UploadImeiFileRequest;
import mypackage.UploadImeiFileResponse;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:/spring/context-*.xml" })
public class ImeiFileWebServiceClientTest {


String endpointAddress = "http://mycompany.com/endpointuri";

String nsURI = "http://mycompany.com/ws/fileupload";

@Test
public void testDispatch() throws Exception {
MessageFactory factory = MessageFactory.newInstance();

QName serviceName = new QName(nsURI, "imeiFileWebService");
QName portName = new QName(nsURI, "imeiFileWebServicePort");

Service service = Service.create(new URL(endpointAddress + "?wsdl"), serviceName);
service.addPort(portName, SOAPBinding.SOAP11HTTP_MTOM_BINDING, endpointAddress);

SOAPMessage soapRequest = factory.createMessage();
SOAPBody body = soapRequest.getSOAPBody();

QName uploadImeiFileQName = new QName(nsURI, "uploadImeiFile", "ime");
SOAPBodyElement payload = body.addBodyElement(uploadImeiFileQName);

QName inParamName = new QName("UploadImeiFile");
SOAPElement inParamElement = payload.addChildElement(inParamName);

QName imeiName = new QName("imeiFile");
SOAPElement imeiElement = inParamElement.addChildElement(imeiName);

QName xopIncludeName = new QName("http://www.w3.org/2004/08/xop/include", "Include", "xop");
SOAPElement includeElement = imeiElement.addChildElement(xopIncludeName);

String cid = "" + System.currentTimeMillis();
includeElement.addAttribute(new QName("href"), "cid:" + cid);

QName uploadUserIdName = new QName("uploadUserId");
SOAPElement uploadUserIdElement = inParamElement.addChildElement(uploadUserIdName);
uploadUserIdElement.setTextContent("xxxx");



Dispatch dispatch = service.createDispatch(portName, SOAPMessage.class, Service.Mode.MESSAGE);

BindingProvider bp = (BindingProvider) dispatch;
((SOAPBinding) bp.getBinding()).setMTOMEnabled(true);

DataSource ds = new FileDataSource("d:/test2.zip");
DataHandler dataHandler = new DataHandler(ds);

AttachmentPart attachmentPart = soapRequest.createAttachmentPart(dataHandler);
attachmentPart.setContentId(cid);
soapRequest.addAttachmentPart(attachmentPart);


//    System.out.println("=== SOAP REQUEST ===");
//    soapRequest.writeTo(System.out);
//    System.out.println("=====================");

long start = System.currentTimeMillis();

dispatch.invoke(soapRequest);

System.out.println("==========> elapsed: " + (System.currentTimeMillis() - start));
}

@Test
public void testClientProxyFactoryBean() throws Exception {
long start = System.currentTimeMillis();
JaxWsProxyFactoryBean factoryBean = new JaxWsProxyFactoryBean();

java.util.Map props = new java.util.HashMap();
props.put(SOAPConstants.MTOM_ENABLED, Boolean.TRUE.toString());

factoryBean.setProperties(props);
factoryBean.setServiceClass(IImeiFileWebService.class);
factoryBean.setAddress(endpointAddress);

IImeiFileWebService service = (IImeiFileWebService) factoryBean.create();


// start.

boolean useProxy = false;

if(useProxy) {
String httpProxyServer = "proxy-ip";
int httpProxyServerPort = 8080;
int httpConnectionTimeout = 30000;
boolean httpAllowChunking = false;


Client client = ClientProxy.getClient(service);
client.getEndpoint().put( "mtom-enabled", "true" );

if(LogManager.isDebugEnabled()) {
String ln = System.getProperty("line.separator");
StringBuffer sb = new StringBuffer(ln);

sb.append("###############################################").append(ln);
sb.append(PropertyNames.HTTP_PROXY_SERVER).append(": ").append(httpProxyServer).append(ln);
sb.append(PropertyNames.HTTP_PROXY_SERVER_PORT).append(": ").append(httpProxyServerPort).append(ln);
sb.append(PropertyNames.HTTP_CONNECTION_TIMEOUT).append(": ").append(httpConnectionTimeout).append(ln);
sb.append(PropertyNames.HTTP_ALLOW_CHUNKING).append(": ").append(httpAllowChunking).append(ln);
sb.append("###############################################").append(ln);

LogManager.debug(getClass(), sb.toString());
}

HTTPConduit http = (HTTPConduit) client.getConduit();
HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
httpClientPolicy.setConnectionTimeout(httpConnectionTimeout);
httpClientPolicy.setAllowChunking(httpAllowChunking);
httpClientPolicy.setProxyServer(httpProxyServer);
httpClientPolicy.setProxyServerPort(httpProxyServerPort);
http.setClient(httpClientPolicy);

}   

// end.

UploadImeiFileRequest request = new UploadImeiFileRequest();
request.setUploadUserId("abc");

//    DataSource dataSource = new FileDataSource(new File("d:/test2.zip"));
DataSource dataSource = new FileDataSource(new File("d:/test2.zip"));
DataHandler dataHandler = new DataHandler(dataSource);

request.setImeiFile(dataHandler);

UploadImeiFileResponse response = service.uploadImeiFile(request);
System.out.println((System.currentTimeMillis() - start) + " milli seconds" );
}

@Resource(name="imeiFileClient")
IImeiFileWebService client;
@Test
public void testImeiFileWebServiceClient() throws Exception {
try {
long start = System.currentTimeMillis();
UploadImeiFileRequest request = new UploadImeiFileRequest();
request.setUploadUserId("xx");

DataSource dataSource = new FileDataSource(new File("d:/test2.zip"));
DataHandler dataHandler = new DataHandler(dataSource);

request.setImeiFile(dataHandler);

UploadImeiFileResponse response = client.uploadImeiFile(request);

System.out.println((System.currentTimeMillis() - start) + " milli seconds" );

} catch(Exception ex) {
ex.printStackTrace();
throw ex;
}
}

}



Injection된 객체를 사용하기 위해서는 스프링 설정파일에 아래와 같이 클라이언트를 등록해야 한다.

아래와 같이 클라이언트 빈을 지정해 주면 된다.
mtom-enabled속성을 true로 지정한다.
주의할 점은 endpointName과 serviceName을 namespace를 포함하여 반드시 지정하기를 권장한다. 개발 환경(개발자피씨)에서는 endpointName(PortName)와 serviceName을 지정하지 않아도 서비스와 연동하는데 문제가 되지 않지만, 외부 서버로 서비스를 디플로이 한 후에 연동하고자 할 때 문제가 발생한다.

<jaxws:client id="imeiFileClient" xmlns:tns="http://mocompany.com/ws/fileupload"
serviceClass="com.mycompany.ws.FileUploadWebService"
address="http://mycompany.com/ws/fileUploadWebService"
endpointName="tns:imeiFileWebServicePort"
serviceName="tns:imeiFileWebService">
<jaxws:properties>
<entry key="mtom-enabled" value="true"/>
</jaxws:properties>

</jaxws:client>




Sample Soap message


Payload: 
--uuid:08654ae5-c060-47dd-9bee-48dff0090a65
Content-Type: application/xop+xml; charset=UTF-8; type="text/xml";
Content-Transfer-Encoding: binary
Content-ID: <root.message@cxf.apache.org>

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:uploadImeiFile 
xmlns:ns2="http://socialhub.samsungmobile.com/store/ws/imeifile" 
xmlns:ns3="http://socialhub.samsungmobile.com/store/ws">
<UploadImeiFile><imeiFile>
<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" 
href="cid:3381b3c2-d57e-490a-bccb-7c91487f24b2-1@http%3A%2F%2Fcxf.apache.org%2F"/></imeiFile>
<uploadUserId>xx</uploadUserId></UploadImeiFile>
</ns2:uploadImeiFile></soap:Body>
</soap:Envelope>
--uuid:08654ae5-c060-47dd-9bee-48dff0090a65
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
Content-ID: <3381b3c2-d57e-490a-bccb-7c91487f24b2-1@http://cxf.apache.org/>

0? ?0? ??      K? ?0   *?H?8   

--uuid:08654ae5-c060-47dd-9bee-48dff0090a65--

댓글 없음:

댓글 쓰기