달력

4

« 2024/4 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

(역자 주)

본 글은 Springsource 사의 Mark Fisher가 2009년 2월 13일에 영어로 쓴 Spring Integration in 10 minutes을 우리말로 번역한 것입니다.


10분만에 Spring Integration 따라잡기

두 달 전 SpringOne America에서 Spring Integration 1.0 GA release를 발표했고, 그 때부터 얼른 "바로 시작하기" blog post를 쓰려 했습니다. 글쎄요, 새해부터는 늘 바쁜 나머지, 일단 10단계의 따라하기 예제 제공을 목표로 했습니다. 각 단계는 대략 1분 정도가 걸립니다... 생각하느라 멈추지만 않으면요. ^^ 어쨌든, 더 꾸물거리지 말고, 바로 가 보죠!

1단계: Spring Integration 배포본 내려받기

여기에서 가장 최신의 배포판을 얻을 수 있습니다.
http://www.springsource.com/download/community?project=Spring%20Integration

내려받기가 끝나면, zip file을 푸세요. Spring Integration project를 이루는 JAR들을 담은 'dist' directory가 있을 것입니다. 또한 'lib' directory에는 Spring Integration이 필요로 하는 외부 library들이 있습니다.

2단계: project 생성

이 예제에서 Eclipse를 쓰지만, 이 작업은 당연히 다른 IDE에서도 가능합니다. Maven이나 Ivy도 쓸 수 있지만, 이 예제는 매우 간단해서, directory를 만들고 JAR file들을 추가하는 것으로 충분합니다.

새 'Java Project'를 생성하고('Package Explorer' view에서 오른쪽 click 후 'New -> Java Project'), 그 project 안에 'lib' directory를 만듭니다. 그리고나서 다음 JAR file들을 Spring Integration의 'dist'와 'lib' directory에서 이 project의 'lib'으로 옮깁니다.

dist에서는

  • org.springframework.integration-1.0.1.RELEASE.jar
  • org.springframework.integration-file-1.0.1.RELEASE.jar

lib에서는

  • com.springsource.org.aopalliance-1.0.0.jar
  • com.springsource.org.apache.commons.logging-1.1.1.jar
  • org.springframework.aop-2.5.6.A.jar
  • org.springframework.beans-2.5.6.A.jar
  • org.springframework.context-2.5.6.A.jar
  • org.springframework.core-2.5.6.A.jar
  • org.springframework.transaction-2.5.6.A.jar

'lib' directory를 refresh(F5를 치세요)한 다음 build-path에 위 JAR file들을 추가하십시오(JAR file들을 고른 다음, 오른쪽 click 후 "Build Path -> Add to Build Path" 선택). 마지막으로, 'src' directory에 'blog' package를 추가하시고요.

3단계: Spring Integration 시작

Spring IDE나 SpringSource Tool Suite을 쓰신다면, Spring project의 특성을 자연스럽게 쓸 수 있으므로 그냥 새 bean 정의 file을 생성하려면 'blog' package를 오른쪽 click만 하면 됩니다. 그렇지 않더라도 아래 내용을 생성하고 file 이름을 'config.xml'로 하시기만 하면 됩니다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:si="http://www.springframework.org/schema/integration"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/integration
           http://www.springframework.org/schema/integration/spring-integration-1.0.xsd"
>
</beans>

element 한 개를 더 추가하겠습니다. 이는 Java in-memory queue를 근간으로 하는, 한 번에 10개의 message를 담을 수 있는 Message channel을 정의합니다.

<si:channel id="channel">
    <si:queue capacity="10"/>
</si:channel>

4단계: Bootstrap Java class 정의

Spring Integration component들은 Spring ApplicationContext에 들어있습니다. 그래서 설정 file로 ApplicationContext 하나를 만드는 일만 하면 됩니다. Web Application에서 구동하려면 Spring의 ContextLoaderListener으로 기동할 수 있고, 또는 만약 dmServer에서라면, 저절로 알아서 기동할 겁니다. 이 초간단 예제에서는 그냥 ('blog' package에 있는) Bootstrap이라는 class에 main() method를 만드는 것으로 하겠습니다.

public class Bootstrap {
    public static void main(String[] args) {
        new ClassPathXmlApplicationContext("blog/config.xml");
    }
}

(언제든지 Eclipse에서 Ctrl+Shift+O…, Mac에서는 Command+Shift+O를 눌러 "organize imports"를 할 수 있습니다).

5단계: Spring Integration Message 송/수신

이제, context에세 channel을 가져와 message를 보낼 수 있습니다. 아직 구독자(subscriber)가 없기 때문에(다음 단계에서 할 것입니다), 같은 channel에서 message를 받기로 하겠습니다. 이를 통해 설정이 제대로 되었는지 확인할 수 있죠. 아래처럼 main() method를 고치세요.

public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("blog/config.xml");
    PollableChannel channel = (PollableChannel) context.getBean("channel");
    channel.send(new StringMessage("Spring Integration이 끝내줘요"));
    Message<?> reply = channel.receive();
    System.out.println("received: " + reply);
}

이걸 돌리면 message 객체의 toString() method 수행 결과가 console에 찍히는 것을 보실 수 있습니다.

6단계: Service 생성

Spring Integration은 program들이 Spring과 느슨한 결합도를 가지는 것을 추구합니다. 즉, 우린 이 예제를 점진적으로 고쳐서 어떤 code도 framework에 엮여버리지 않도록 할 것입니다. 첫번째 할 일은,  message를 구독할 POJO Service를 추가하는 것입니다. 이 예제에서는 단순히 문자열을 대문자로 바꾼 뒤 느낌표를 덧붙이는 일을 합니다.

public class Shouter {
    public String shout(String s) {
        return s.toUpperCase().concat("!!!");
    }
}

7단계: Spring 설정에 Service 추가

이 Service는 그냥 일반적인 bean으로 추가하면 됩니다.

<bean id="shouter" class="blog.Shouter"/>

그 다음, 우린 Service bean에 입/출력 channel을 붙이는 "Service Activator"를 추가할 것입니다. 현재 있는 "channel"의 이름을 "output"으로 바꾸고, 입력용으로 단순한 non-buffering channel을 더합니다. 전체 설정은 아래와 같습니다.

<si:channel id="input"/>
<si:channel id="output">
    <si:queue capacity="10"/>
</si:channel>
<si:service-activator input-channel="input"
           output-channel="output"
           ref="shouter"
           method="shout"/>
<bean id="shouter" class="blog.Shouter"/>

단계 8: Message를 보내 Service 호출

input channel에 message를 보내고 output channel에서 message를 받도록 main() method를 고칩니다. 의존성을 lookup하는 부분과 channel 객체에 대한 type casting이 바뀌었음을 주목하세요(지금부터 output은 PollableChannel이고 'input' channel은 output과 달리 non-buffering channel입니다).

public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("blog/config.xml");
    MessageChannel input = (MessageChannel) context.getBean("input");
    PollableChannel output = (PollableChannel) context.getBean("output");
    input.send(new StringMessage("Spring Integration이 끝내줘요"));
    Message<?> reply = output.receive();
    System.out.println("received: " + reply);
}

9단계: 출력을 file로 보내기

Channel Adapter를 추가하면 main() method에서 출력을 polling하지 않고 결과를 곧장 file에 보낼 수 있습니다. 이렇게 하면 polling이 필요 없으므로, 먼저 output channel의 하위 element인 queue를 없애겠습니다.

<si:channel id="output"/>

Channel Adapter를 더합니다. 현재 directory를 지정할 수도 있고, Windows라는 drive 문자를 포함할 수도 있습니다(예를 들면 "C:/tmp" 같은).

<file:outbound-channel-adapter channel="output" directory="/tmp"/>


그 다음, Bootstrap의 main() method를 송신만 하도록 고칩니다.

public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("blog/config.xml");
    MessageChannel input = (MessageChannel) context.getBean("input");
    input.send(new StringMessage("Spring Integration이 끝내줘요"));
}

XSD 설정에 'file' namespace를 추가하는 일도 해야 합니다. 최상위의 'beans' element가 아래처럼 생길 것입니다.

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:si="http://www.springframework.org/schema/integration"
       xmlns:file="http://www.springframework.org/schema/integration/file"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/integration
            http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
            http://www.springframework.org/schema/integration/file
            http://www.springframework.org/schema/integration/file/spring-integration-file-1.0.xsd">

예제를 돌리면 ".msg" 확장자가 달린 file에 결과를 볼 수 있습니다(file 이름 생성 규칙도 추가할 수 있지만 이 post의 범위를 벗어납니다).

10단계: Gateway interface 생성

마지막 단계의 목표는 Spring과의 의존성을 완벽하게 없애는 것입니다. Message를 Message Channel에 보내기보다는, 단순한 interface의 인자로 문자열를 보내는 것이 훨씬 더 깔끔합니다. 'blog' package에 다음 interface를 추가합니다.

public interface Gateway {
    void send(String s);
}

그리고, 다음 element를 설정에 추가합니다.

<si:gateway id="gateway" service-interface="blog.Gateway" default-request-channel="input"/>


마지막으로, main() method에서 channel 대신 Gateway를 씁니다. 이제 단순히 문자열만 넘기면 됩니다.

public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("blog/config.xml");
    Gateway gateway = (Gateway) context.getBean("gateway");
    gateway.send("Spring Integration이 끝내줘요");
}

마치며

이 글이 Spring Integration에 대한 쓸만한 소개글이었으면 좋겠습니다. 여지껏 다룬 내용의 주안점은 Spring과 낮은 결합도를 가지면서도 통합 계층을 쉽게 만들 수 있다는 점입니다. 만약 독자 제위께서 여러 endpoint를 추가하기 시작한다면 이에 대한 가치를 피부로 느끼실 수 있을 것입니다. 하다 보면 filter, transformer, router를 더 추가해야 할 지도 모르겠습니다.

Spring Integration에는 이 예제에서 나온 것보다 훨씬 더 풍부한 기능이 있습니다. 더 살펴보시려면, 'org.springframework.integration.samples' module(배포본의 'src' directory에 source도 들어있습니다)에 들어있는 다양한 project들을 검토해 보시고 참조 문서를 읽어보십시오. 물론, Spring Integration home page에서도 풍부한 정보를 찾을 수 있습니다.

이 글은 스프링노트에서 작성되었습니다.

:
Posted by 하얀 말

*** Juergen Hoeller가 Spring Framework Team Blog에 올린 글을 번역한 것입니다.

Spring 3.0 milestone을 사용이 가능함을 발표할 수 있어 기쁩니다(내려받는 page).

이번 release에는 다음과 같은 다양한 개선 사항과 신기능이 들어있습니다.

API를 Java 5 형태에 맞도록 수정: 일관되게 generic collection과 map을 썼고, 일관되게 generic을 적용한 FactoryBean을 썼으며, Spring AOP API에서는 bridge method을 일관되게 결정하도록 했습니다. Generic을 적용한 ApplicationListener들은 자동으로 정해진 type에 해당하는 event만 받습니다. TransactionCallback와 HibernateCallbackcallback 따위의 모든 Callback interface는 이제 generic을 적용한 결과값을 돌려주도록 선언했습니다. 전반적으로, Spring core codebase는 이제 산뜻하게 정리되어 Java 5에 잘 들어맞습니다.

동시성 지원 확대: 그동안 우리는 Spring의 TaskExecutor를 Java 5의 java.util.concurrent 기능과 밀접하게 통합시켜 왔습니다. 이제 ExecutorService adapter, ThreadFactory 통합 등과 아울러 Callable과 Future를 최우선적으로 지원합니다. 이들은 그간 최대한 JSR-236(Java EE 6용 Concurrency Utility)과 발맞추어 왔습니다. 더불어, @Async annotation(이나 EJB 3.1의 @Asynchronous annotation)을 써서 비동기적으로 method를 호출할 수 있습니다. 그리고 Spring 3.0 M3에는 scheduling을 편하게 설정할 수 있는, cron 형태의 timer 지원을 포함하는, scheduling namespace를 추가할 예정입니다.

OXM을 core에 포함: 객체(Object)/XML mapping module을 Spring Web Service project에서 Spring core project로 옮겼습니다. OXM을 Java 5에 걸맞게 수정해 왔으며, JAXB2, JiBX, Castor, XMLBeans, XStream을 쓰는 marshalling 및 unmarshalling을 지원합니다. 또한 Spring JMS(MarshallingMessageConverter)와 Spring MVC(MarshallingView)용 OXM 지원 기능도 있습니다.

RestTemplate: 새로운 client측 REST 지원 기능이 생겼습니다. 바로 오랫동안 기다려온, Spring solution으로부터 기대할 수 있는 유연하고 확장성이 풍부한 HTTP 처리 하부 구조를 지닌 RestTemplate입니다. 또한 Spring MVC에서의 REST 지원과 관련하여 몇몇 개선이 이루어졌습니다. Arjen이 올릴 최신 REST 지원 기능에 대한 blog post를 기대하세요!

Portlet 2.0 상의 MVC: 이제 Spring Portlet MVC는 Portlet 2.0 API(JSR-286)을 기반으로 합니다. (Portlet 2.0에서 정의하는) action 이름, window 상태, resource id, event 이름의 특성을 지원하는 기능을 포함한 Portlet MVC Handler method용 @ActionMapping, @RenderMapping, @ResourceMapping and @EventMapping annotations를 제공합니다.

신속한 JPA 2.0 지원: 마지막으로, 우리는 곧 나올 JPA 2.0 preview를 지원하는 JPA provider와 아울러 JPA 2.0 명세도 능동적으로 쫒아가고 있습니다. Spring 3.0 M2는 이미, Spring이 관장하는 transaction 하에서의 query timeout 및 Spring이 관리하는 EntityManager proxy에서의 QueryBuilder 접근 따위 같이, JPA 2.0 API를 지원합니다. JPA 2.0이 안정화되는대로 이 기능을 Spring 3.0 RC1에서 매듭짓겠습니다.

지금이야말로 Spring 3.0을 일찍 시도해 보기 좋은 때입니다! 여러분께서 어떻게 이를 쓰고 있는지 알려주세요... M2에는 아직 참조 문서가 없지만 더 자세해진 javadoc과 test suite이 있습니다. 앞으로 blog에도 예제들을 올릴 것이고요.

우린 이미 마지막 mileston 작업을 하는 중입니다. M3에서는 annotation 기반 factory method, (JSR-303 "bean validation"을 기초로 하는) 선언적 validation, 새 XML 설정 namespace(orm, scheduling)를 선보일 예정입니다. Spring MVC는 conversation 관리 관점에서 정밀 검토를 할 것이고요. 우리는 또한 필요해지는대로 JSP 2.0이 Spring과 매끄럽게 연동되도록 준비 중입니다.

참고 문헌

:
Posted by 하얀 말
2009. 1. 23. 00:23

Spring JDBC 삽질기 - 1 Computing에 관한 독백2009. 1. 23. 00:23

아아, 이 화면을 보기 위해 얼마나 삽질을 했던가....


그렇습니다, 바로 JUnit Test Case 수행하여 모든 JUnit Test를 통과한 것입니다. 덜렁 Class 한 개에 method 7개에 대한 test지만 이거 한다고 정말 갖은 삽질을.... 저번 주말부터 시작했는데 과문한 터라 오늘사 이 Test를 모두 통과했습니다. 흐~.

삽질의 시작

석사도 넘쳐나는 요사이에 비록 학부로 끝났지만 그래도 컴퓨터 과학을 전공하고 졸업 후 취직해서 Java 전문가 행세 및 Java EE 기반 Web System  구축 관련 S/W Architect라고 구라도 좀 치고, IBM MQ로 EAI도 하고 지금은 급기야 AnyLink 5라는 Solution으로 금융권 외부 기관 연계 일을 하며 별별 System(Mainframe은 물론 Tandem이란 것도 구경해 봄), 별별 Network(X.25라는 거 아세요?), 별별 인간들과 부딪히며 살아온 온 지도 어언 만 9년 지나 10년째로 접어들었습니다. 여하튼 별 걸 다 봤고, Python이나 Ruby 같은 떠오르는 태양도 탐내 하지만 아무래도 Java를 제일 편해 하고 회사 업무도 맨 Java 판이다 보니, 아무래도 Java쪽 기술이 눈에 잘 들어옵니다.

그간 Java 바닥의 super star인 Spring Framework(이하 Spring)란 탐스러운 S/W Framework에 대한 경험을 갈구하던 나머지, '스프링 프레임워크 워크북'이란 책도 사 보고, 내성적인 성격임에도 불구하고 이차저차 KSUG(Korea Spring User Group)에서 Spring Reference 3장도 번역하고 그랬지만(아직 손 많이 봐야하지만), 업무에서는 Spring 쓸 일이 전혀 없고 앞으로도 쓸 것 같지도 않아, 이래서는 실제로 아는 것 하나도 없고 탐스러워 하기만 하다 끝나겠다 싶어, Spring을 써서 간단한 Web Site라도 하나 만들어 보자고 뽐뿌 받아 시작하게 되었습니다(Sacred라는 game을 다 깨고, 이제 나이도 한국식으로는 36살인데 game 고만 하고 여가 시간에 생산적 일을 좀 하자는 취지도 있었습니다... 그런데.... 해야 할 game이 지금도 잔뜩 밀린 것도 사실이라 설치할까 말까 이러고 있습니다 ^---------------^).

Apache Derby = Sun JavaDB

Web site를 만들더래도 DB나 file system의 사용은 거의 필수입니다. DB를 쓰려면 생짜 JDBC로만 할 수도 있을 것이고, Apache Commons DbUtils를 써도 되고, 거창하게 iBatisHibernate, JPA를 쓸 수도 있을 겁니다(생짜 JDBC 빼고는 다 깨작거린 정도지 실 업무에서 쓴 건 하나도 없군요). File을 쓰는 것은 걍 생짜로 java.io package의 class 쓰는 거 이상은 생각이 안나네요. SQL만 던지면 결과 얻는 DB에 비해서도 엄청 불편하고요.

'File System의 간편함과 DB의 편리함을 동시에 누릴 수는 없을까?'하는 찰나 퍼뜩 생각난 것이 'Simple한 JavaDB'라는 글입니다. Java SE 6 SDK 깔면서 같이 깔려 하는 바로 그 놈이죠. 우리가 일반적으로 쓰는 DB 형태인 DB Server 형태 동작도 가능하고, Java Application의 일부로 작동도 가능한(Embedded DB라고 합니다) 가벼운 DB입니다. '이게 딱이네' 싶어 이걸 쓰기로 했습니다. Sun에서 JDK의 일부로 배포하긴 하지만 Java DB의 원조는 Apache Derby입니다(사실 둘 다 똑같습니다). 걍 Apache에서 내려받았습니다.

IBM이 Cloudscape란 경량 DBMS를 Apache에 기증하면서 생긴 DB다 보니 이거 참 DB2와 비슷한 구석이 많더군요. 이 DB에 관해서는 별도로 글을 올리겠습니다. 아무래도 익숙치 않은 DB다 보니 삽질 좀 했거든요.

SimpleJdbcTemplate?

Internet 검색을 해 보니 맨 처음 걸린 것이 'SimpleJdbcTemplate'라는 글이었습니다. 살펴보니 JdbcTemplate 구현체가 몇 개 되더군요. 그런데 이 글에서 'Java 5의 새로운 기능들을 추가한 JdbcTemplate의 wrapper 클래스이다. varargs나 autoboxing등을 활용하여 기존의 방식들에 비해서 cast하는 일들이 줄어들게 된다'는 문구를 보고, JDK 6으로 진행중이겠다, '그래, 이 놈이야!'하고 일단 SimpleJdbcTemplate로 하기로 합니다(참 단순하죠? 최신 기술이라고 무턱대도 도입하려는 고객 욕할 수 없다니까요 ㅋㅋㅋ). 그럼 이제부터SimpleJdbcTemplate를 보겠습니다.

SimpleJdbcTemplate 객체 생성부터 난관

일단 SimpleJdbcTemplate 객체를 생성해야 하겠지요? SimpleJdbcTemplate Javadoc을 보면 생성자(constructor)가 세 개 있는데 두 개는 다른 Spring JDBC 객체를 감싸는 역할이고 실제로 DB에 붙이는 것은 DataSource를 인자로 받는 생성자입니다.

DriverManager.getConnection() method로 Connection 객체를 얻거나 WAS 등에서 관리하는 Connection Pool을 가진 DataSource를 JNDI로 집어왔는데... 이거 초장부터 막힙니다. 이 Web application의 web.xml에 DataSource를 등록하고 JNDI로 lookup할까요? 나쁘진 않군요.

제가 선택한 방법은 그냥 JDBC Driver에 있는 DataSource 구현체를 이용하는 것이었습니다. 다행히 Derby는 Derby를 구성하는 class들에 대한 Javadoc을 제공하고 있어서 그걸 볼 수 있었습니다(옆 link를 눌러보시면 jdbc3, jdbc4라는 directory가 나오는데 이들은 뭘까요? Derby는 JDBC 4.0 명세도 준수하는데, 바로 jdbc4란 directory 안의 Javadoc들은 이 JDBC 4.0에 대응하는 Derby class들에 대한 Javadoc입니다. jdbc3은 설명 따로 안드려도 감 잡겠죠?). org.apache.derby.jdbc package에 있는 class 중 이름이 *DataSource, 또는 *DataSource40으로 끝나는 것들이 바로 Derby의 DataSource 구현체입니다. 이 중 하나를 골라 SimpleJdbcTemplate 생성자의 인자로 쓰면 됩니다.

그래서 일단은 모든 DAO의 부모 역할을 하는 추상 class를 하나 선언하고 그 class의 member 변수로 이 SimpleJdbcTemplate 객체를 하나 잡기로 했습니다.

...
public abstract class AbstractDao {
    private SimpleJdbcTemplate jdbcTemplate = null;
    ...
}

이 SimpleJdbcTemplate가 필요로 하는 DataSource는 따로 member 변수로는 안 잡았습니다. 필요하면 jdbcTemplate의 getDataSource()로 얻음 되니까요. 그럼 SimpleJdbcTemplate 객체 생성에 필요한 DataSource는 어떻게 얻을까요?

...
public abstract class AbstractDao {
    private SimpleJdbcTemplate jdbcTemplate = null;
    ...
    public final void setDataSource(DataSource ds) {
        this.jdbcTemplate = new SimpleJdbcTemplate(ds);
        ...
    }
}

네, 바로 setDataSource()에서 인자로 받는 DataSource를 가지고 바로 SimpleJdbcTemplate로 만들었습니다. 그럼 이 DataSource는 어떻게 만들지? 네... 바로 Spring의 XML 설정에서 이 부분을 정의하였습니다. 아래와 같이요.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
   
    <!-- DataSource Setup for Derby Network DB -->
    <bean id="derbyNetworkDS"
        class="org.apache.derby.jdbc.ClientConnectionPoolDataSource40">
              <!-- DB IP나 hostname -->
        <property name="serverName" value="127.0.0.1" />
        <!-- DB port -->
        <property name="portNumber" value="1527" />
              <!-- DB 이름 -->
        <property name="databaseName" value="scientia"/>
        <!-- DB id -->
        <property name="user" value="scientia" />
              <!-- DB password -->
        <property name="password" value="scientia" />
        <property name="description" value="Apache Derby Network DataSource" />
        <property name="dataSourceName" value="derbyNetworkDS"/>
    </bean>
...
</beans>

Apache Derby에 있는 derby.jar나 derbyclient.jar를 보시면 실제로 org.apache.derby.jdbc.ClientConnectionPoolDataSource40이라는 class가 있습니다. 이 class가 바로 Derby에 대한 DataSource의 실 구현체이고, Javadoc 보시면 아시겠지만 DataSource 구현체는 이것 말고도 몇 개 더 있으니 입맛이나 요건에 맞게 용도별로 골라 쓰시면 됩니다. 어쨌든 이들 객체에 대한 property를 설정하면 DB에 접속하여 필요한 연산을 할 수 있습니다. 그 class에 대한 Javadoc 중 이름이 set*으로 시작하는 method들이 바로 이 XML의 property로 설정할 후보들이죠.

(다음에 계속)
:
Posted by 하얀 말

이 글은 하얀말님의 2009년 1월 7일의 미투데이 내용입니다.

:
Posted by 하얀 말

이 글은 하얀말님의 2008년 12월 22일의 미투데이 내용입니다.

:
Posted by 하얀 말

이 글은 하얀말님의 2008년 12월 10일의 미투데이 내용입니다.

:
Posted by 하얀 말
스프링과 아파치 CXF를 이용해 POJO 웹 서비스를 디자인하고 구현하기, Part 2: RESTful 웹 서비스 만들기의 예제(예제도 위 site에서 받을 수 있다) 따라 하다 된통 걸렸다... -.-

HTTP POST method로 request를 날려 Order(주문)를 하나 추가하려고 했는데 다음과 같은 알도살도 못하는 Exception이 발생하였다.

2008. 12. 10 오후 1:55:57 org.apache.cxf.binding.http.interceptor.DispatchInterceptor handleMessage
정보: Invoking POST on /orders
2008. 12. 10 오후 1:55:57 org.apache.cxf.binding.http.interceptor.URIParameterInInterceptor handleMessage
정보: URIParameterInterceptor handle message on path [/orders] with content-type [application/x-www-form-urlencoded]
2008. 12. 10 오후 1:55:57 org.apache.cxf.phase.PhaseInterceptorChain doIntercept
정보: Interceptor has thrown exception, unwinding now
org.apache.cxf.interceptor.Fault: Unexpected EOF in prolog
 at [row,col {unknown-source}]: [1,0]
    at org.apache.cxf.binding.http.interceptor.URIParameterInInterceptor.mergeParams(URIParameterInInterceptor.java:120)
    at org.apache.cxf.binding.http.interceptor.URIParameterInInterceptor.handleMessage(URIParameterInInterceptor.java:103)
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:220)
    at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:78)
    at org.apache.cxf.transport.servlet.ServletDestination.invoke(ServletDestination.java:92)
    at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:285)
    at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:131)
    at org.apache.cxf.transport.servlet.AbstractCXFServlet.invoke(AbstractCXFServlet.java:175)
    at org.apache.cxf.transport.servlet.AbstractCXFServlet.doPost(AbstractCXFServlet.java:153)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:286)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:845)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
    at java.lang.Thread.run(Thread.java:619)
Caused by: com.ctc.wstx.exc.WstxEOFException: Unexpected EOF in prolog
 at [row,col {unknown-source}]: [1,0]
    at com.ctc.wstx.sr.StreamScanner.throwUnexpectedEOF(StreamScanner.java:686)
    at com.ctc.wstx.sr.BasicStreamReader.handleEOF(BasicStreamReader.java:2134)
    at com.ctc.wstx.sr.BasicStreamReader.nextFromProlog(BasicStreamReader.java:2040)
    at com.ctc.wstx.sr.BasicStreamReader.next(BasicStreamReader.java:1069)
    at org.apache.cxf.staxutils.StaxUtils.readDocElements(StaxUtils.java:746)
    at org.apache.cxf.staxutils.StaxUtils.read(StaxUtils.java:605)
    at org.apache.cxf.binding.http.interceptor.URIParameterInInterceptor.mergeParams(URIParameterInInterceptor.java:118)
    ... 22 more

이 뭥미? 해결책을 찾을 수 있길 바라며, 일단 오류를 남긴다. 관련 오류가 Apache CXF Issue Tracker에도 있어서 좀 불길하긴 하다만...(https://issues.apache.org/jira/browse/CXF-1863)

  • 사용 JDK: Sun JDK 1.6.0_11 for Windows
  • 사용 library: Apache CXF 2.1.3
  • 사용 Servlet/JSP Engine: Apache Tomcat 6.0.18

----------- 해결 -----------

문제는 client였다. 기본 예제의 client인 Client.java는 아래와 같이 단순히 java.net.URL을 기반으로 작성되었다.

package demo.order.client;

import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.IOException;
import java.net.URL;
import java.net.HttpURLConnection;

public final class Client {

    public static void main(String[] args) throws Exception {
        String xml = null;
        try {
            // Make a HTTP connection to the server
            URL u = new URL("http://localhost:8080/orderapp_rest/orders");
            HttpURLConnection httpCon = (HttpURLConnection) u.openConnection();
            // Set the request method as POST
            httpCon.setRequestMethod("POST");
            httpCon.setDoOutput(true);

            OutputStream os = httpCon.getOutputStream();
            // XML are encoded in UTF-8 format
            OutputStreamWriter wout = new OutputStreamWriter(os, "UTF-8");
            wout.write("<?xml version=\"1.0\"?>\r\n"); 
            // Add customer name as XML fragment
            wout.write("<order xmlns=\"http://demo.order\"><name>Rajeev</name></order>r\n");
            wout.flush();

            // Make implicit connection to the server
            httpCon.getContentLength();
            httpCon.disconnect();
  
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

혹시나 하는 마음에 화끈하게 Apache HttpClient library를 써서 아래와 같이 다시 만들어 보았다.

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;

public class Client {
  public static void main(String... args) throws Exception {
    String strURL = "http://localhost:8080/orderapp_rest/orders";
    String reqData = "<?xml version=\"1.0\"?>\r\n<order xmlns=\"http://demo.order\"><name>Rajeev</name></order>\r\n";

    PostMethod post = new PostMethod(strURL);
    post.setRequestEntity(new StringRequestEntity(reqData));
    HttpClient httpclient = new HttpClient();

    try {
      int result = httpclient.executeMethod(post);

      if (200 == result)
        System.out.println(post.getResponseBodyAsString());
    } finally {
      post.releaseConnection();
    }
  }
}

이랬더니 정상적으로 동작하였다. 하기사 java.net.URL이 잘 동작했으면 Apache HttpClient가 있을 필요가 있겠어?

설마허니, 예제 code 돌려보지도 않은 것은 아니겠지? 혹시 Web site에서 배포하는 예제에서 동일한 문제를 겪으시는 분들은 위 Client.java로 다시 해 보시길...

그런데 둘 차이는 뭘까? 한 번 파보고 싶은데... 싶은데싶은데싶은데싶은데싶은데... 젠장.
:
Posted by 하얀 말

이 글은 하얀말님의 2008년 9월 8일의 미투데이 내용입니다.

:
Posted by 하얀 말