JAX-WS Provider/Dispatch API 활용한 RESTful 서비스 구현
표준 API를 사용하여 간단한 RESTful 서비스를 작성할 수 있게 하지만, HTTP Binding 방식에 비해 유연한 구조가 아니다.
JAX-WS Provider/Dispatch API의 특징
을 살펴보면 다음과 같다.
- JAX-WS Provider(javax.xml.ws.Provider)와 Dispatch(javax.xml.ws.Dispatch) API를 사용하여 간단한 RESTful 서비스를 구현할 수 있다.
- Web Service로 노출시킬 서비스를 Provider 인터페이스 클래스를 구현하여 작성하고 invoke method를 구현한다.
다음은 JAX-WS Provider와 Dispatch API를 사용하여 RESTful 서비스 구현 시 Server와 Client 단에서 어떻게 사용해야 하는지에 대한 사용법이다.
Server Configuration
Movie Service를 Provider 인터페이스 클래스를 구현한 MovieServicePayloadProvider 클래스로
작성하고 invoke method를 구현함으로써 RESTful Web Services로 노출시켜 보도록 한다.
Samples
다음은 Provider 인터페이스 클래스를 구현한 MovieServicePayloadProvider 클래스 정의에 대한 예제이다.
서버 구동을 위해서 javax.xml.ws.Endpoint 클래스를 이용하고 있다.
Provider 인터페이스 클래스 구현(MovieServicePayloadProvider)
다음은 Provider 인터페이스 클래스를 구현한 MovieServicePayloadProvider 클래스를 작성한 MovieServicePayloadProvider.java
의 일부이다.
Annotation 설정에 유의하도록 한다.
import javax.xml.ws.ServiceMode;
import javax.xml.ws.WebServiceProvider;
@WebServiceProvider()
@ServiceMode(value = Service.Mode.PAYLOAD)
public class MovieServicePayloadProvider implements Provider<DOMSource> {
@Resource
protected WebServiceContext wsContext;
public MovieServicePayloadProvider() {
}
public DOMSource invoke(DOMSource request) {
MessageContext mc = wsContext.getMessageContext();
String path = (String) mc.get(Message.PATH_INFO);
String query = (String) mc.get(Message.QUERY_STRING);
String httpMethod = (String) mc.get(Message.HTTP_REQUEST_METHOD);
if (httpMethod.equalsIgnoreCase("POST")) {
return updateMovie(request);
} else if (httpMethod.equalsIgnoreCase("GET")) {
if (path.equals("/movieservice/movies") && query == null) {
return findMovieListAll();
} else if (path.equals("/movieservice/movies") && query != null) {
return findMovie(query);
}
}
중략...
Test case
다음은 서버 사이드의 서비스를 Web Services로 노출시키는 서버를 구동하는 코드를 작성한 ProviderDispatchTest.java
의 일부이다.
setUp() 메소드 내에서 MovieServicePayloadProvider 클래스 생성 후 javax.xml.ws.Endpoint 클래스를 생성해 준 다음,
접근 주소를 설정하여 publish 시킨다.
이때 Endpoint Binding Id를 HTTP Binding ID로 설정하고 있음에 유의하도록 한다.
public class ProviderDispatchTest extends RemotingTestCase {
// ==============================================================
// ====== TestCase 수행에 필요한 사전 작업 정의 ====================
// ==============================================================
public void setUp() throws Exception {
System.out.println("Starting Server");
Endpoint e = Endpoint.create(HTTPBinding.HTTP_BINDING,
new MovieServicePayloadProvider());
String address = "http://localhost:9002/movieservice/movies";
e.publish(address);
}
중략...
Client Configuration
Web Services에 접근하기 위한 클라이언트를 작성한다. HttpClient 혹은 URL 클래스를 이용하여 RESTful WebService에 접근할 수도 있고,
Dispatch API를 이용하여 접근해볼 수도 있다.
Samples
다음은 org.apache.commons.httpclient.HttpClient를 이용하여 RESTful Web Services로 노출된 Movie Service에 접근하는 예제이다.
이때, RESTful WebService의 결과 값이 XML 형태로 리턴되기 때문에 실제 클라이언트 코드에서 JavaBeans 객체로 변경하여 사용해야 하는 작업이 추가된다.
XML을 JavaBeans 객체로 변경하는 일을 JAXB를 통해서 수행하고 있다.
JAXB 활용
JAXB를 활용하여 서비스 결과 XML을 JavaBeans로 변경시키기 위해서는 다음과 같이 각 JavaBeans 객체에 Annotation을 설정하고 jaxb.index 파일을
추가 생성시켜줘야 한다.
다음은 Movie Service에서 입력 및 출력 시 Movie 상세조회 XML 데이터와 변환되는 Movie.java
의 일부이다.
Movie
클래스 정의 시 작성한 @XmlRootElement(name = "Movie") Annotation 설정에 유의하도록 한다. JAXB를 이용하여 서비스 결과 XML을
JavaBeans 객체로 변환 시 이 Annotation 정보를 이용한다.
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "Movie")
public class Movie implements Serializable {
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
중략...
다음은 Movie Service에서 입력 및 출력 시 Movie 목록조회 XML 데이터와 변환되는 Movies.java
의 일부이다.
Movies
클래스 정의 시 작성한 @XmlRootElement(name = "Movies") Annotation 설정에 유의하도록 한다. JAXB를 이용하여 서비스 결과 XML을
JavaBeans 객체로 변환 시 이 Annotation 정보를 이용한다. 특히 목록 조회 결과 시 사용되는 리턴 값은 내부 멤버 변수로 Collection 객체를 정의하여
새로운 Movies 객체를 작성함에 유의하도록 한다.
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "Movies")
public class Movies {
private Collection<Movie> movies;
public Collection<Movie> getMovie() {
return movies;
}
public void setMovie(Collection<Movie> movies) {
this.movies = movies;
}
중략...
다음은 JAXB를 사용하기 위해서 필요한 jaxb.index
파일이다.
Movie와 Movies 클래스를 정의해 놓는다.
Test Case
다음은 HttpClient 및 Dispatch 클래스를 사용하여 RESTful Web Services로 노출된 Movie Service에 접근하는 클라이언트 코드를 작성한 ProviderDispatchTest.java
의 일부이다.
testXXX() 메소드 내에서 Get/Post/Put/Delete Method 중 어느 것을 사용할 것인지 정하여 접근하고자 하는 Web Services 주소와 함께 정의하고,
HttpClient 객체를 생성하여 위에서 정의한 Method를 실행시킨다.
이 중 testUpdateMovie() 메소드가 Dispatch API를 이용하여 작성된 클라이언트 코드이다.
RESTful WebService 실행 결과로 받은 XML 데이터를 JavaBeans 객체로 변경하여 메소드 동작이 올바른지 테스트해본다.
public class ProviderDispatchTest extends RemotingTestCase {
// ==============================================================
// ====== TestCase methods ======================================
// ==============================================================
/**
* [Flow #-1] Positive Case : HttpClient를 이용하여
* List 형태의 Movie 전체 목록을 조회한다.
* @throws Exception
* throws exception which is from service
*/
public void testFindMovieListAll() throws Exception {
// 1. find movie
GetMethod get=new GetMethod("http://localhost:9002/movieservice/movies");
HttpClient httpclient = new HttpClient();
String response = "";
try {
assertEquals(200, httpclient.executeMethod(get));
response = get.getResponseBodyAsString();
System.out.println("findMovieListAll : " + response);
assertNotNull(response);
} catch (Exception e) { fail();
} finally { get.releaseConnection(); }
JAXBContext jaxbContext =
JAXBContext.newInstance("anyframe.sample.movie.restful.jaxrs");
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Movies movies = (Movies) unmarshaller.unmarshal(
new InputSource(new StringReader(response)));
// 2. check the movie information
assertEquals(2, movies.getMovie().size());
}
/**
* [Flow #-3] Positive Case : HttpClient를 이용하여 Movie Id가 "001"인 Movie를 조회한다.
* @throws Exception
* throws exception which is from service
*/
public void testFindMovie() throws Exception {
// 1. find movie
GetMethod get =
new GetMethod("http://localhost:9002/movieservice/movies?movieId=001");
HttpClient httpclient = new HttpClient();
String response = "";
try {
assertEquals(200, httpclient.executeMethod(get));
response = get.getResponseBodyAsString();
System.out.println("find: " + response);
assertNotNull(response);
} catch (Exception e) {
fail();
} finally {
get.releaseConnection();
}
JAXBContext jaxbContext =
JAXBContext.newInstance("anyframe.sample.movie.restful.jaxrs");
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Movie movie =(Movie) unmarshaller.unmarshal(
new InputSource(new StringReader(response)));
// 2. check the movie information
assertEquals("The Sound Of Music", movie.getTitle());
assertEquals("Robert Wise", movie.getDirector());
}
/**
* [Flow #-4] Positive Case : Dispatch를 이용하여 Movie Id가 "002"인 기존 Movie 정보를 변경한다.
* @throws Exception
* throws exception which is from service
*/
public void testUpdateMovie() throws Exception {
QName serviceName = new QName("movieservice");
QName portName = new QName("MovieServicePayloadProviderPort");
Service service = Service.create(serviceName);
service.addPort(portName, HTTPBinding.HTTP_BINDING,
"http://localhost:9002/movieservice/movies");
Dispatch<DOMSource> dispatcher=service.createDispatch(portName, DOMSource.class,
Service.Mode.PAYLOAD);
Map<String, Object> requestContext = dispatcher.getRequestContext();
InputStream is = getClass().getClassLoader().getResourceAsStream(
"webservices/restful/dispatch/client/Movie-movieId=002Req.xml");
Document doc = XMLUtils.parse(is);
DOMSource reqMsg = new DOMSource(doc);
requestContext.put(MessageContext.HTTP_REQUEST_METHOD, "POST");
DOMSource result = dispatcher.invoke(reqMsg);
printSource(result);
JAXBContext jaxbContext=JAXBContext.newInstance("anyframe.sample.movie.restful.jaxrs");
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Movie movie = (Movie) unmarshaller.unmarshal(result);
// 3. check the new movie information
assertEquals("Life Is Wonderful", movie.getTitle());
assertEquals("Roberto", movie.getDirector());
}
중략...
Resources
다운로드
샘플 테스트 코드를 포함하고 있는 anyframe-remotingtest-src.zip 파일을 다운받은 후, 테스트 환경 설정
을 참조하여
위에서 제시한 예제 코드를 실행해 볼 수 있다. 이때 해당 프로젝트 내의 src/test/java 소스폴더 하위의 anyframe.core.remoting.webservices.restful.dispatch.ProviderDispatchTest 클래스를
JUnit Test Framework을 이용하여 수행시키도록 한다.
| Name
|
Download
|
| anyframe-remotingtest-src.zip |
Download
|
참고자료