2011년 5월 24일 화요일

Jersey 1.0.2(JAX-RS) + JBoss 4.2.3

Application 소개
Jersey 1.0.2 버전과 JBoss 4.2.3 이용하여 Rest Web Service API 구현하였다.
구현된 어플리케이션은 KPI(Key Performance Indicator) 정보를 저장, 조회 하는 간단한 API 구성되어 있다. API URI 매핑 되면 URI 정보는 아래와 같다.
URI
Example URL
Method
/kpiapi/rest/kpis/listhttp://localhost:8080/kpiapi/rest/kpis/listGET
지금까지 저장된 모든 KPI을 조회한다.
/kpiapi/rest/kpis/inserthttp://localhost:8080/kpiapi/rest/kpis/insertPOST
KPI을 추가한다.
/kpiapi/rest/kpis/updatehttp://localhost:8080/kpiapi/rest/kpis/updatePUT
KPI을 수정한다.
/kpiapi/rest/kpis/{uniqueKey}http://localhost:8080/kpiapi/rest/kpis/{uniqueKey}GET
특정(uniqueKey) KPI을 조회한다.
/kpiapi/rest/kpis/jaxb/{uniqueKey}http://localhost:8080/kpiapi/rest/kpis/jaxb/{uniqueKey}GET
특정 KPI을 조회하여 JAXBElement Type으로 리턴 한다.



Application Project 구조
*SrcAPI Java 소스 위치
TestAPI 테스트 하는 TestCase 소스 위치
LibJaxb, jersey 관련 라이브러리 위치
WEB-INFweb.xml 위치
WEBRootWeb Context Root 위치


Ant Build Script
build.properties
#jboss 설치 위치
jboss.home=C:/Tools/jboss-4.2.3.GA
jboss.server.path=${jboss.home}/server/default
jboss.lib.path=${jboss.home}/lib
jboss.server.lib.path=${jboss.server.path}/lib
jboss.deploy.path=${jboss.server.path}/deploy


build.xml
<?xml version="1.0" encoding="euc-kr"?>
<project name="kpiapi" default="deploy" basedir=".">
<property file="build.properties" />
<property name="project.home" value="." />
<property name="src.path" value="${project.home}/src" />
<property name="build.path" value="${project.home}/build" />
<target name="prepare">
<mkdir dir="${build.path}" />
<mkdir dir="${build.path}/WEB-INF" />
<mkdir dir="${build.path}/WEB-INF/classes" />
<mkdir dir="${build.path}/WEB-INF/lib" />
</target>
<target name="clean">
<delete dir="${build.path}" verbose="${verbose.flag}" />
</target>
<path id="build.classpath">
<fileset dir="${project.home}/lib" includes="*.jar" />
<fileset dir="${jboss.lib.path}" includes="*.jar" />
<fileset dir="${jboss.server.path}/lib" includes="*.jar" />
</path>
<taskdef name="xjc" classname="com.sun.tools.xjc.XJC2Task">
<classpath>
<fileset dir="${project.home}/lib" includes="*.jar" />
</classpath>
</taskdef>
<!-- xsd에서 VO 생성하는 Task -->
<target name="gen.source" depends="prepare">
<xjc destdir="src" package="com.hs.bam">
<schema dir="${project.home}/WEB-INF" includes="*.xsd" />
</xjc>
</target>
<target name="compile" depends="prepare">
<javac srcdir="${src.path}" destdir="${build.path}/WEB-INF/classes">
<compilerarg value="-Xlint:deprecation" />
<include name="**/*.java" />
<classpath refid="build.classpath" />
</javac>
</target>
<target name="build.war" depends="compile">
<copy todir="${build.path}" overwrite="true">
<fileset dir="${project.home}/WebRoot" includes="**/*" />
</copy>
<copy todir="${build.path}/WEB-INF" overwrite="true">
<fileset dir="${project.home}/WEB-INF" includes="**/*" />
</copy>
<copy todir="${build.path}/WEB-INF/lib" overwrite="true">
<fileset dir="${project.home}/lib" includes="**/*" />
</copy>
<jar jarfile="${project.home}/${ant.project.name}.war">
<fileset dir="${build.path}">
</fileset>
</jar>
</target>
<target name="deploy" depends="build.war">
<copy file="${project.home}/${ant.project.name}.war" todir="${jboss.deploy.path}" />
</target>
</project>


kpis.xsd
<?xml version="1.0" encoding="utf-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:annotation>
<xsd:documentation xml:lang="en">
KPI Integration API XML Schema
</xsd:documentation>
</xsd:annotation>
<xsd:element name="Kpis">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Kpi" minOccurs="0" maxOccurs="unbounded" type="Kpi" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="Kpi" type="Kpi" />
<xsd:complexType name="Kpi">
<xsd:sequence>
<xsd:element name="uniqueKey" type="xsd:string"></xsd:element>
<xsd:element name="name" type="xsd:string"></xsd:element>
<xsd:element name="description" type="xsd:string"></xsd:element>
<xsd:element name="period" type="xsd:string"></xsd:element>
<xsd:element name="unit" type="xsd:string"></xsd:element>
<xsd:element name="fromDate" type="xsd:date"></xsd:element>
<xsd:element name="toDate" type="xsd:date"></xsd:element>
<xsd:element name="targetValue" type="xsd:double"></xsd:element>
<xsd:element name="ownerId" type="xsd:string"></xsd:element>
<xsd:element name="ownerName" type="xsd:string"></xsd:element>
<xsd:element name="kpiType" type="xsd:string"></xsd:element>
<xsd:element name="fullPath" type="xsd:string"></xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>


VO 생성을 위해 정의된 XML 스키마
VO 생성을 위한 get.source Task 실행
D:\Repository\SelfStudy\jax-rs\RestKPIApi>ant gen.source
Buildfile: build.xml
prepare:
gen.source:
[xjc] Consider using <depends>/<produces> so that XJC won't do unnecessary
compilation
[xjc] Compiling file:/D:/Repository/SelfStudy/jax-rs/RestKPIApi/WEB-INF/kp
is.xsd
[xjc] Writing output to D:\Repository\SelfStudy\jax-rs\RestKPIApi\src
BUILD SUCCESSFUL
Total time: 1 second


Rest API 작성
package com.hs.bam.kpi.rest;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.xml.bind.JAXBElement;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import com.hs.bam.Kpi;
import com.hs.bam.Kpis;
import com.hs.bam.ObjectFactory;
@Path("/kpis")
public class KpiResource {
private static Map<String, Kpi> kpiDb = new HashMap<String, Kpi>();
public KpiResource() {
try {
XMLGregorianCalendar cal =
DatatypeFactory.newInstance().newXMLGregorianCalendar((GregorianCalendar)GregorianCalendar.getInstance());
kpiDb.put("1", new Kpi("1", "매출1", "테스트 KPI1", "주단위", "",cal, cal, 20, "2005045", "이윤창", "상향식", "루투/테스트1"));
kpiDb.put("2", new Kpi("2", "매출2", "테스트 KPI2", "주단위", "",cal, cal, 20, "2005045", "이윤창", "상향식", "루투/테스트2"));
kpiDb.put("3", new Kpi("3", "매출3", "테스트 KPI3", "주단위", "",cal, cal, 20, "2005045", "이윤창", "상향식", "루투/테스트3"));
} catch (DatatypeConfigurationException e) {
e.printStackTrace();
}
}
@GET
@Path("/list")
@Produces({"application/xml","application/json"})
public Kpis getAllKpis() {
Kpis kpis = new Kpis();
kpis.getKpi().addAll(kpiDb.values());
return kpis;
}
@POST
@Path("/insert")
@Consumes({"application/xml","application/json"})
public void createKpi(Kpis kpis) {
List<Kpi> list = kpis.getKpi();
for(Kpi kpi : list)
kpiDb.put(kpi.getUniqueKey(), kpi);
}
@PUT
@Path("/update")
@Consumes({"application/xml","application/json"})
@Produces({"application/xml","application/json"})
public JAXBElement<Kpi> updateKpi(JAXBElement<Kpi> kpi) {
kpiDb.put(kpi.getValue().getUniqueKey(), kpi.getValue() );
return kpi;
}
@GET
@Path("/{uniqueKey}")
@Produces({"application/xml","application/json"})
public Kpis getKpi(@PathParam("uniqueKey") String uniqueKey) {
Kpis kpis = new Kpis();
kpis.getKpi().add(kpiDb.get(uniqueKey));
return kpis;
}
@GET
@Path("/jaxb/{uniqueKey}")
@Produces({"application/xml","application/json"})
public JAXBElement<Kpi> getJAXBKpi(@PathParam("uniqueKey") String uniqueKey) {
Kpi kpi = kpiDb.get(uniqueKey);
return new ObjectFactory().createKpi(kpi);
}
}


XMLGregorianCalendar cal = DatatypeFactory.newInstance().newXMLGregorianCalendar((GregorianCalendar)GregorianCalendar.getInstance());
XML 내부에서 사용되는 Date Type은 XMLGregorianCalendar Type을 사용하여야 한다. 생성 방법은 위와 같은 식으로 DatatypeFactory을 이용하여 생성하면 된다.
@Produces({"application/xml","application/json"})
“@Produces” Annotation은 리턴값으로 사용될 Context-type을 의미한다. 위 예는 “application/xml”, “application/json”의 두가지 타입을 지원한다.
@Consumes({"application/xml","application/json"})
“@Consumes” Annotation은 파라미터로 전달되는 인자의 Context-type을 의미한다. 위 예도 역시 “application/xml”, “application/json” 두가지 타입을 지원한다.
@Path("/{uniqueKey}")
“@Path” Annotation은 URL Path중 특정부분을 파라미터로 전달 받을 수 있게 한다. 이 Annotation은 아래와 같이 Method 인자 부분에 “@PathParam” Annotation과 항상 같이 쓰이게 된다.
public Kpis getKpi(@PathParam("uniqueKey") String uniqueKey) {…
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<servlet>
<servlet-name>jersey</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer
</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.hs.bam.kpi.rest</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>jersey</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</web-app>


Rest API의 Package 위치가 다르다면 “<param-value>com.hs.bam.kpi.rest</param-value>” 부분을 알맞게 바꿔준다.
Rest API Test Case
package com.hs.bam.kpi.rest.test;
import java.net.URI;
import java.util.GregorianCalendar;
import java.util.List;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import org.junit.Before;
import org.junit.Test;
import com.hs.bam.Kpi;
import com.hs.bam.Kpis;
import com.hs.bam.ObjectFactory;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
public class TestKpiResource {
private WebResource r;
private static int PORT = 8088;
private static String CONTEXT_NAME = "kpiapi";
private static String SERVLET_URL = "rest";
private static String BASIC_URL = "http://localhost";
private static URI webServiceURL() {
return UriBuilder.fromUri(BASIC_URL).port(PORT).path(CONTEXT_NAME)
.path(SERVLET_URL).build();
}
@Before
public void setUp() throws Exception {
Client c = Client.create();
r = c.resource(webServiceURL());
}
@Test
public void createKpi() throws Exception {
XMLGregorianCalendar cal = DatatypeFactory.newInstance()
.newXMLGregorianCalendar(
(GregorianCalendar) GregorianCalendar.getInstance());
Kpi kpi = new Kpi("4", "매출4", "테스트 KPI4", "주단위", "", cal, cal, 20,
"2005045", "이윤창", "상향식", "루투/테스트4");
Kpis kpis = new Kpis();
kpis.getKpi().add(kpi);
ClientResponse msg = r.path("kpis").path("insert").type(
MediaType.APPLICATION_XML).post(ClientResponse.class, kpis);
System.out.println(msg.getStatus());
}
@Test
public void getAllKpis() throws Exception {
Kpis msg = r.path("kpis").path("list").type(MediaType.APPLICATION_XML)
.get(Kpis.class);
List<Kpi> kpis = msg.getKpi();
for (Kpi kpi : kpis) {
System.out.println(kpi.getName() + "(" + kpi.getUniqueKey() + ")");
}
}
@Test
public void getKpi() throws Exception {
Kpis msg = r.path("kpis").path("2").type(MediaType.APPLICATION_XML)
.get(Kpis.class);
List<Kpi> kpis = msg.getKpi();
for (Kpi kpi : kpis) {
System.out.println(kpi.getName() + "(" + kpi.getUniqueKey() + ")");
}
}
@Test
public void getJAXBKpi() throws Exception {
Kpi kpi = r.path("kpis").path("jaxb").path("3").type(
MediaType.APPLICATION_XML).get(Kpi.class);
System.out.println("==> " + kpi.getName() + "(" + kpi.getUniqueKey()
+ ")");
}
@Test
public void updateKpi() throws Exception {
Kpi kpi = r.path("kpis").path("jaxb").path("1").type(
MediaType.APPLICATION_XML).get(Kpi.class);
kpi.setName("수정된 매출");
Kpi rKpi = r.path("kpis").path("update")
.type(MediaType.APPLICATION_XML).put(Kpi.class,
new ObjectFactory().createKpi(kpi));
System.out.println("==> update " + rKpi.getName() + "("
+ rKpi.getUniqueKey() + ")");
}
}


Client에서 Rest API 이용하는 방법은 WebResource 객체를 생성한 path API URL 확장하고 Context-Type 지정하고, 해당 Method 방식(get, post, put, delete) 적합한 메서드를 호출 하면 된다.
ClientResponse msg = r.path("kpis").path("insert").type(
MediaType.APPLICATION_XML).post(ClientResponse.class, kpis);”

댓글 없음:

댓글 쓰기

ETL 솔루션 환경

ETL 솔루션 환경 하둡은 대용량 데이터를 값싸고 빠르게 분석할 수 있는 길을 만들어줬다. 통계분석 엔진인 “R”역시 하둡 못지 않게 관심을 받고 있다. 빅데이터 역시 데이터라는 점을 볼때 분산처리와 분석 그 이전에 데이터 품질 등 데이...