 |
|
난이도 : 초급 James M. Snell, Software Engineer, IBM
2004 년 10 월 19 일 검증된 웹 애플리케이션 디자인 전략을 웹 서비스에 적용하는 방법을 배운다. Java Messaging Service(JMS)큐를 사용하여 비동기식 쿼리 작동을 구현하는 방법을 설명한다.
SOAP을 이용하여 웹 서비스를 구현하는 것에 대해 생각한다면 동기식 요청-응답 유형의 작동을 가장 먼저 떠올리게 된다. 하지만 진정한 객체 지향 아키텍쳐(SOA)는 훨씬 더 광범위한 메시징 패턴과 디자인 전략을 수용할 수 있어야 한다. 이 시리즈를 통해 기본적인 웹 애플리케이션 디자인 패턴과 웹 서비스 애플리케이션을 설명하고자 한다. 여기에 제시된 패턴들은 새로운 것은 아니다. 사실 여러분들도 그동안 전통적인 웹 애플리케이션에 익숙해졌다. 하지만 많은 개발자들은 웹 서비스 차원에서 그와 같은 전략을 구현하는 방법을 잘 모를 뿐만 아니라 전략을 적용하는 방법도 익숙하지 않다. 이 글을 통해 간단하고 단순한 디자인 방식을 요청-응답 모델에 적용해 볼 것이다. 이 글을 이해하려면 기본적으로 J2EE 환경에서 웹 서비스를 구현하는 방법을 알아야 한다.
비동기식 쿼리 패턴
시작하기에 앞서, 코드 실행 시 타임아웃이나 긴 행업(hang-up)을 피하기 위해 장기 실행되는 작동을 중지시키기 위한 비동기식 요청과 응답 작동 구현을 살펴보겠다. 전통적인 HTTP와 HTML 웹 애플리케이션에서 비동기식 쿼리를 구현한 경험이 있다면 내가 구현하는 이 패턴들이 익숙할 것이다.
그림 1. 비동기식 쿼리 패턴
이 패턴의 플로우는 간단하다:
- 요청자는 서비스 공급자(Service Provider)에게 요청을 제출한다. 서비스 공급자는 메시지를 대기열에 넣고(queue) 요청자(Requester)가 요청 상태를 확인할 때 사용할 수 있는 코릴레이션 ID를 리턴한다.
- 요청 프로세서는 요청을 대기열에서 빼고(dequeue) 이를 처리한다. 일반적으로 이 요청 프로세싱은 장기 실행이다. 일단 프로세싱이 완료되면 프로세서(Processor)는 응답 메시지를 대기열에 넣는다.
- 어떤 지점에서 그 요청에 대한 응답이 준비가 되면 요청자는 서비스 공급자에게 요청한다. 응답이 대기열에 들어가면 공급자는 요청자에게 응답을 리턴한다. 응답을 사용할 수 없다면 공급자는 이를 요청자에게 보고한다. 요청자는 요청을 취소하거나 계속 기다릴 수 있다. 선택된 기간 동안, 응답이 사용 가능하게 될 때까지 공급자는 폴링(polling)한다.
Kyle Brown은 Java Ranch(자바 개발 리소스 웹 사이트)에 J2EE Servlet 애플리케이션에서 이 패턴을 적용하는 것에 대한 글을 써왔다.(참고자료) Kyle은 이 패턴이 기본 구현에 개입된 동기와 기본 디자인 문제를 적절하게 다루었다. 이 패턴의 웹 서비스 구현이라고 해서 구현이 많이 달라지는 것은 아니다.
서비스 인터페이스 설계하기
비동기식 쿼리 패턴을 설명하기 위해 간단한 웹 서비스 샘플을 구현했다. 이 패턴의 개념을 설명할 목적 그 이상도 이하도 아니다. 서비스가 하는 일이란, 장기 실행 프로세스를 시뮬레이션 하기 위해 실행 중 10초간의 중지 후에 세 개의 소문자 String 인풋 값을 대문자로 변형하는 것이다.
이 서비스를 구현하려면 두 개의 웹 서비스 연산이 필요하다: submitRequest와 checkResponse. 각 연산이 수행하는 것은 보기만 해도 알 수 있다. Listing 1 은 서비스 인터페이스를 설명하는 WSDL 이다.
Listing 1. AsyncService.wsdl
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions
targetNamespace="http://one.wspattern.developerworks.ibm.com"
xmlns:impl="http://one.wspattern.developerworks.ibm.com"
xmlns:intf="http://one.wspattern.developerworks.ibm.com"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<schema
targetNamespace="http://one.wspattern.developerworks.ibm.com"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:impl="http://one.wspattern.developerworks.ibm.com"
xmlns:intf="http://one.wspattern.developerworks.ibm.com"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<complexType name="ResponseCheck">
<sequence>
<element name="correlationID" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
<element name="ResponseCheck" nillable="true" type="impl:ResponseCheck"/>
<complexType name="Response">
<sequence>
<element name="type" type="xsd:int"/>
<element name="correlationID" nillable="true" type="xsd:string"/>
<element name="refresh" type="xsd:int"/>
<element name="a" nillable="true" type="xsd:string"/>
<element name="b" nillable="true" type="xsd:string"/>
<element name="c" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
<element name="Response" nillable="true" type="impl:Response"/>
<complexType name="Request">
<sequence>
<element name="a" nillable="true" type="xsd:string"/>
<element name="b" nillable="true" type="xsd:string"/>
<element name="c" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
<element name="Request" nillable="true" type="impl:Request"/>
</schema>
</wsdl:types>
<wsdl:message name="submitRequestRequest">
<wsdl:part name="request" type="intf:Request"/>
</wsdl:message>
<wsdl:message name="checkResponseResponse">
<wsdl:part name="checkResponseReturn" type="intf:Response"/>
</wsdl:message>
<wsdl:message name="checkResponseRequest">
<wsdl:part name="check" type="intf:ResponseCheck"/>
</wsdl:message>
<wsdl:message name="submitRequestResponse">
<wsdl:part name="submitRequestReturn" type="intf:Response"/>
</wsdl:message>
<wsdl:portType name="AsyncService">
<wsdl:operation name="checkResponse" parameterOrder="check">
<wsdl:input
message="intf:checkResponseRequest"
name="checkResponseRequest"/>
<wsdl:output
message="intf:checkResponseResponse"
name="checkResponseResponse"/>
</wsdl:operation>
<wsdl:operation name="submitRequest" parameterOrder="request">
<wsdl:input
message="intf:submitRequestRequest"
name="submitRequestRequest"/>
<wsdl:output
message="intf:submitRequestResponse"
name="submitRequestResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="AsyncServiceSoapBinding" type="intf:AsyncService">
<wsdlsoap:binding
style="rpc"
transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="checkResponse">
<wsdlsoap:operation soapAction=""/>
<wsdl:input name="checkResponseRequest">
<wsdlsoap:body
namespace="http://one.wspattern.developerworks.ibm.com"
use="literal"/>
</wsdl:input>
<wsdl:output name="checkResponseResponse">
<wsdlsoap:body
namespace="http://one.wspattern.developerworks.ibm.com"
use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="submitRequest">
<wsdlsoap:operation soapAction=""/>
<wsdl:input name="submitRequestRequest">
<wsdlsoap:body
namespace="http://one.wspattern.developerworks.ibm.com"
use="literal"/>
</wsdl:input>
<wsdl:output name="submitRequestResponse">
<wsdlsoap:body
namespace="http://one.wspattern.developerworks.ibm.com"
use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="AsyncServiceService">
<wsdl:port binding="intf:AsyncServiceSoapBinding" name="AsyncService">
<wsdlsoap:address
location="http://localhost:9080/WSPattern1/services/AsyncService"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
|
다음은 이 인터페이스에서 주의해야 할 것들이다:
- 이 서비스는 RPC/Literal 인코딩 스타일을 사용한다.
submitRequest와 checkResponse 연산자는 Response라는 객체를 리턴한다. 유형속성(type property)에 의해 구분된 Response는 두 가지 의미가 있다. 유형 속성 값 0은 Refresh Response를 참조하고, 유형 속성 값 1은 Request Response을 참조한다. Refresh Response는 응답을 아직 사용할 수 없다는 것과 리프레시 속성(Kyle Brown이 그의 글에서 말한 HTTP META Refresh메커니즘에 해당)이 값을 지정하면 요청자가 새로운 checkResponse 연산자를 제출해야 한다는 것을 나타내고 있다. Request Response에는 세 개의 대문자 인풋 스트링이 포함되어 있고 요청 프로세싱의 완료를 나타낸다.
- Refresh Response에는
Correlation ID 속성이 포함되어 있다. 이 속성의 값은 checkResponse 연산자의 인풋으로서 사용된다. 이 식별자는 클라이언트가 처음 요청을 응답에 코릴레이션 할 수 있는 유일한 수단이다. 이를 구현하는 또 다른 방법이 있다.
Listing 2 는 이 서비스의 전형적인 메시지 교환을 보여주고 있다.
Listing 2. AsyncService 메시지 교환
Initial Request
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SOAP-ENV:Body>
<m:submitRequest
xmlns:m="http://one.wspattern.developerworks.ibm.com">
<request>
<a>String</a>
<b>String</b>
<c>String</c>
</request>
</m:submitRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
submitRequest Response
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header/>
<soapenv:Body>
<p155:submitRequestResponse
xmlns:p155="http://one.wspattern.developerworks.ibm.com">
<submitRequestReturn>
<type>0</type>
<correlationID>1097517621904</correlationID>
<refresh>10000</refresh>
<a xsi:nil="true"/>
<b xsi:nil="true"/>
<c xsi:nil="true"/>
</submitRequestReturn>
</p155:submitRequestResponse>
</soapenv:Body>
</soapenv:Envelope>
Initial checkResponse attempt, no response available, submitted after 10000 miliseconds
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header/>
<soapenv:Body>
<p155:checkResponseResponse
xmlns:p155="http://one.wspattern.developerworks.ibm.com">
<checkResponseReturn>
<type>0</type>
<correlationID>1097517621904</correlationID>
<refresh>10000</refresh>
<a xsi:nil="true"/>
<b xsi:nil="true"/>
<c xsi:nil="true"/>
</checkResponseReturn>
</p155:checkResponseResponse>
</soapenv:Body>
</soapenv:Envelope>
Second checkResponse attempt, submitted after 10000 miliseconds
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header/>
<soapenv:Body>
<p155:checkResponseResponse
xmlns:p155="http://one.wspattern.developerworks.ibm.com">
<checkResponseReturn>
<type>1</type>
<correlationID xsi:nil="true"/>
<refresh>0</refresh>
<a>STRING</a>
<b>STRING</b>
<c>STRING</c>
</checkResponseReturn>
</p155:checkResponseResponse>
</soapenv:Body>
</soapenv:Envelope>
|
 |

|
서비스 구현하기
비동기식 쿼리 패턴 서비스를 구현하는 것은 자바 메시징 서비스(Java Messaging Service)의 단순한 적용이다. 이 예제의 목적을 위해 오픈 소스 OpenJMS open source JMS provider implementation(참고자료)와 IBM® WebSphere® Application Server V5 (Application Server)를 사용한다. 디폴트 OpenJMS 설정을 사용하고 JSR-109에 순응하는 J2EE 웹 서비스를 구현한다. WebSphere Studio Application Developer (Application Developer) V5.1로 이 예제용 코드를 작성했다. developerWorks에서 다운로드도 가능하다. (참고자료) Application Developer에 액세스 할 수 없을 경우를 대비해 EAR 파일 (see 참고자료)도 준비했다.
두 개의 서버측 컴포넌트들이 구현되어야 한다: 요청 프로세서와 웹 서비스 구현. 요청 프로세서는 큐에서 요청을 가져와 "장기 실행" 10초 대문자화 프로세스를 실행한다. 서비스 구현은 웹 서비스 클라이언트에서 요청을 받아 이를 대기열에 넣고 응답을 처리하여 checkResponse 연산에 따라 클라이언트에 응답한다.
전형적인 J2EE 애플리케이션에서 요청 프로세서는 JMS 스팩에 따라 Message Driven Bean 으로서 구현된다. 이 예제에서 JMS MessageListener 인터페이스를 구현하는 간단한 HTTP 서블릿을 사용한다. 이 서블릿은 서버 시작 시 init 으로 설정되며 리스너가 들어오는 모든 요청에 대해 작동하도록 한다. 요청이 대기열로 들어오면 서블릿을 리스닝(listening)하기 시작한다.
Listing 3. JNDIListenerServlet.java
package com.ibm.developerworks.wspattern.one.helper;
import java.util.Enumeration;
import java.util.Hashtable;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueReceiver;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.naming.Context;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
public class JNDIListenerServlet
extends HttpServlet
implements Servlet, MessageListener {
private Context context;
private QueueConnection connection;
private QueueSession session;
private Queue queue;
private QueueReceiver receiver;
public void init()
throws ServletException {
super.init();
try {
context = JNDIHelper.getInitialContext();
connection = JNDIHelper.getConnection(context);
session = JNDIHelper.getSession(connection);
queue = JNDIHelper.getQueue(context);
receiver = JNDIHelper.getQueueReceiver(session, queue);
receiver.setMessageListener(this);
System.out.println("Listener servlet is Listening");
} catch (Exception e) {}
}
public void destroy() {
try {
connection.close();
} catch (Exception e) {}
}
public void onMessage(Message message) {
try {
System.out.println("Processing message " + message.getJMSCorrelationID());
Thread.sleep(10 * 1000); // sleep for ten seconds
Queue responseQueue = JNDIHelper.getResponseQueue(context);
QueueSender sender = JNDIHelper.getQueueSender(session,responseQueue);
MapMessage request = (MapMessage)message;
MapMessage response = session.createMapMessage();
response.setJMSCorrelationID(request.getJMSCorrelationID());
for (Enumeration e = request.getMapNames(); e.hasMoreElements();) {
String name = (String) e.nextElement();
try {
response.setString(
name,
request.getString(name).toUpperCase());
} catch (Exception ex) {}
}
sender.send(response);
} catch (Exception e) {
System.out.println("==================");
try {
System.out.println(
"THERE WAS AN ERROR PROCESSING THE MESSAGE! " +
message.getJMSCorrelationID());
} catch (Exception ex) {}
e.printStackTrace(System.out);
System.out.println("==================");
}
}
}
|
JNDIListenerServlet와 서비스 구현 모두 이 애플리케이션을 위해 만들어진 헬퍼 클래스를 사용하는데 이는 JMS 커넥션과 세션을 초기화할 때의 세부사항을 숨긴다.
Listing 4. JNDIHelper.java
package com.ibm.developerworks.wspattern.one.helper;
import java.util.Hashtable;
import javax.jms.JMSException;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueReceiver;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class JNDIHelper {
private static Context context;
public static Context getInitialContext()
throws NamingException {
if (context == null) {
Hashtable properties = new Hashtable();
properties.put(
Context.INITIAL_CONTEXT_FACTORY,
"org.exolab.jms.jndi.InitialContextFactory");
properties.put(
Context.PROVIDER_URL,
"rmi://localhost:1099");
context = new InitialContext(properties);
}
return context;
}
public static QueueConnection getConnection(
Context context)
throws NamingException,
JMSException {
QueueConnectionFactory factory =
(QueueConnectionFactory) context.lookup(
"JmsQueueConnectionFactory");
QueueConnection connection = factory.createQueueConnection();
connection.start();
return connection;
}
public static QueueSession getSession(QueueConnection connection)
throws JMSException {
QueueSession session =
connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
return session;
}
public static Queue getQueue(Context context)
throws NamingException {
Queue queue = (Queue) context.lookup("queue1");
return queue;
}
public static Queue getResponseQueue(Context context)
throws NamingException {
Queue queue = (Queue) context.lookup("queue2");
return queue;
}
public static QueueSender getQueueSender(
QueueSession session,
Queue queue)
throws JMSException {
QueueSender sender = session.createSender(queue);
return sender;
}
public static QueueReceiver getQueueReceiver(
QueueSession session,
Queue queue)
throws JMSException {
QueueReceiver receiver = session.createReceiver(queue);
return receiver;
}
public static QueueReceiver getQueueReceiver(
QueueSession session,
Queue queue,
String selector)
throws JMSException {
QueueReceiver receiver = session.createReceiver(queue, selector);
return receiver;
}
}
|
일단 서블릿이 만들어지면 웹 애플리케이션의 web.xml 을 편집하여 서블릿이 서버 시작 시 초기화하도록 설정한다. 서블릿이 초기화할 때 JMS 연결을 열고 이를 OpenJMS의 default message queue에 리스너로서 등록한다.
두 번째 단계는 서비스 구현을 만드는 것이다. JSR-109 웹 서비스를 실행하기 위해 만들어져야 하는 다양한 파생물 때문에 Application Developer는 편리하다. 여기서 집중할 것은 서비스 구현 클래스이다. 이 글 상단과 하단의 Code 아이콘을 클릭하여 코드를 다운로드 받아 참조하기 바란다. 애플리케이션 서버에 필요한 다양한 자바 및 XML 설정 파일을 볼 수 있다.
Listing 5. AsyncService.java
package com.ibm.developerworks.wspattern.one;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueReceiver;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.naming.Context;
import com.ibm.developerworks.wspattern.one.helper.JNDIHelper;
public class AsyncService {
public Response submitRequest(Request request) {
Response response = null;
try {
Context context = JNDIHelper.getInitialContext();
QueueConnection connection = JNDIHelper.getConnection(context);
QueueSession session = JNDIHelper.getSession(connection);
Queue queue = JNDIHelper.getQueue(context);
QueueSender sender = JNDIHelper.getQueueSender(session,queue);
MapMessage message = session.createMapMessage();
String corrID = Long.toString(System.currentTimeMillis());
message.setJMSCorrelationID(corrID);
message.setString("one", request.getA());
message.setString("two", request.getB());
message.setString("three", request.getC());
sender.send(message);
response = new Response();
response.setType(Response.TYPE_REFRESH);
response.setCorrelationID(corrID);
response.setRefresh(10 * 1000);
return response;
} catch (Exception e) {
response = new Response();
response.setType(Response.TYPE_RESPONSE);
response.setA(e.getMessage());
}
return response;
}
public Response checkResponse(ResponseCheck check) {
String corrID = check.getCorrelationID();
Response response = null;
try {
Context context = JNDIHelper.getInitialContext();
QueueConnection connection = JNDIHelper.getConnection(context);
QueueSession session = JNDIHelper.getSession(connection);
Queue queue = JNDIHelper.getResponseQueue(context);
String selector = "JMSCorrelationID = '" + corrID + "'";
QueueReceiver receiver = JNDIHelper.getQueueReceiver(session, queue, selector);
Message message = receiver.receiveNoWait();
if (message == null) {
response = new Response();
response.setType(Response.TYPE_REFRESH);
response.setRefresh(10 * 1000);
response.setCorrelationID(corrID);
} else {
MapMessage resp = (MapMessage)message;
response = new Response();
response.setType(Response.TYPE_RESPONSE);
response.setA(resp.getString("one"));
response.setB(resp.getString("two"));
response.setC(resp.getString("three"));
}
} catch (Exception e) {}
return response;
}
}
|
여기에서는 놀랄 것이 없다. submitRequest 메소드는 인풋 매개변수에 근거하여 JMS MapMessage 를 준비한다. 이 맵 메시지는 세 개의 스트링 값으로 구성된다. 메시지는 큐로 보내지고 코릴레이션 ID를 포함하고 있는 Refresh Response가 준비되어 클라이언트에 리턴된다.
checkResponse 연산은 인풋 매개변수에서 코릴레이션 ID를 취하고 응답 큐로 연결을 개방하면서 적절한 코릴레이션 ID로 어느 메시지나 전달할 것을 요청한다. 메시지가 존재하지 않으면 이 연산은 기다리지 않는다; 새로운 리프레시 인터벌 기간과 함께 또 다른 Refresh Response를 준비하고 이를 다시 콜러에게 리턴한다. 메시지가 전달되면 연산은 적절한 Request Response을 준비하여 리턴한다.
웹 서비스를 전개하고 OpenJMS와 WebSphere servers를 시작하면 비동기식 쿼리 패턴 웹 서비스가 실행된다.
요약
비동기식 쿼리 패턴의 성공의 핵심은 웹 서비스 클라이언트와 서비스 공급자가 요청과 응답을 코릴레이션 할 수 있는가 이다. 이 글의 예제에서 간단한 일회용 코릴레이션 ID를 만들고 이 예제 애플리케이션만을 위한 리프레시 타이머 메커니즘을 만들 수 있다. WS-* 스팩을 조합해서 사용해도 같은 결과를 볼 수 있다. WS-Addressing Endpoint Reference 또는 WS-Transaction Coordination Context는 코릴레이션 ID와 리프레시 타이머 값을 쉽게 포함했다. 여하튼, 이 패턴의 사용은 특정 애플리케이션에만 국한된 것이고 표준 SOAP 헤더 엘리먼트와 다양한 WS-* 스팩을 사용하는지의 여부는 관계없다. 각 연산이 구현하는 작동은 정의 및 문서화가 잘 되어 있어야 한다.
여기에서 구현된 예제는 전통적인 SOAP 요청-응답 메시징 패턴을 사용하여 요청을 제출하고 응답을 받는다. 요청이 서블릿으로 HTTP POST 되고 응답이 HTTP GET 요청을 사용하여 검색되는 곳에서는 REST-style 모델을 사용할 수도 있다. 두 접근 방식 모두 유효하며 애플리케이션에 따라 각 장단점이 있다. 예를 들어 checkResponse 연산이 WS-Security 기반의 인증을 필요로한다면 REST-style 인터랙션은 지양해야 한다.
마지막으로 이 예제를 확장하여 요청자가 장기 실행 연산에 대한 상세한 상태 쿼리를 제출하거나 진행중인 작동을 취소하도록 하는 상상을 할 수도 있다. 그러한 기능들은 이 글의 범위를 벗어나므로 여러분들 스스로 해보길 바란다.
다운로드 하십시오 | 설명 | 이름 | 크기 | 다운로드 방식 |
|---|
| WebSphere deployable EAR file | ws-tip-altdesign1ear.ear | 701 KB |
FTP |
|---|
| Example program source files | ws-tip-altdesign1code.zip | 719 KB |
FTP |
|---|
참고자료
필자소개  | 
|  | James Snell은 IBM Emerging Technologies Toolkit 팀의 소프트웨어 엔지니어이다. |
기사에 대한 평가
 |
| 이 문서 북마킹 하기
|
|