[Info]Tags categorized posts and contents patterns..

[AJAX] Ajax Code E xamples.. [Book] About the book.. [CSS] CSS Code E xamples.. [DB] Sql Code E xamples.. [DEV] All development stor...

2017년 4월 26일 수요일

[NEWS] 기술 뉴스 #9 : 14-05-16..


출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

웹개발 관련

그 밖의 프로그래밍 관련

IT 업계 뉴스

볼만한 링크

프로젝트

  • Velocity.js : 자바스크립트 UI 애니메이션의 성능을 가속하는 라이브러리
  • osxc : OS X의 설정을 자동화해주는 도구.(아직 써보지는 않음.)
  • API Changelog : 페이스북, 트위터, Github등의 API 문서에 변경사항이 생겼을 때 알려주고 변경된 히스토리를 볼 수 있는 사이트
  • Spring Cloud : Cloud Foundry나 Heruku등의 클라우드에서 디비나 캐시 등의 서비스와 연결을 쉽게 해주는 스프링 프로젝트

[Book] CODE 코드..


출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

CODE 코드 - 10점
찰스 펫졸드 지음
김현규 옮김
인사이트

아주 오래전에 Nephilim님이 카페에서 읽고 있는 걸 보고 번역서가 나왔을 때부터 읽고 싶어하다가 이제야 보게 되었다. 이 책의 저자인 찰스 펫졸드(Charles Petzold)는 윈도우 프로그래밍 쪽에서 유명하므로 나는 이 사람의 책은 본 적이 없지만, 이 책은 정말 좋은 책이다.

특정 플랫폼이 아니라 컴퓨터와 소프트웨어의 역사에 대해서 다루고 있다. 역사하면 범위가 넓은데 컴퓨터가 만들어지는 과정부터 해서 지금의 프로그래밍 언어가 만들어진 과정과 원리를 모두 다루고 있다. 그래서 프로그래머라면 한 번씩(아니 두세 번씩?)은 읽어보라고 하고 싶은 책이다. 컴퓨터의 모든 원리를 담고 있다고 하면 꽤 어려운 내용일 것 같지만 그렇지는 않고 이해하기 쉽게 정말 잘 풀어놔서 책을 보면서 "이래서 이러한 구성으로 동작하는구나"하고 생각한 적이 한두 번이 아니다. 컴퓨터를 이루는 원리가 간단한 것은 아니므로 뒤로 갈수록 좀 복잡한 내용이 나오기는 하지만 다루는 내용의 깊이에 비해서는 아주 잘 풀어놨다고 생각한다. 한마디로 말하면 지은이의 글에도 나오듯이 "CODE라는 책은 컴퓨터가 어떻게 동작하는지 설명하는 책이다!"

처음에는 친구와 대화를 하는 방법부터 시작한다. 옆집에 사는 친구와 밤에 의사소통하기 위해서 수신호를 만들고 플래시를 이용해서 신호를 만들면서 2진수의 개념을 설명한다. 컴퓨터를 설명하면서 이런 이야기부터 시작하나 싶을 정도로 처음에는 아주 쉬우므로 소설을 읽듯이 가볍게 읽을 수 있다. 여기서 설명하는 이진수부터 시작해서 전신을 통해서 신호를 어떻게 보내고 릴레이를 통해서 신호를 보내는 범위를 어떻게 늘리기 시작했는지로 확장하면서 논리게이트를 만들고 이를 바탕으로 작은 컴퓨터를 만드는 원리를 설명한다.

나처럼 기본이 약한 채로 프로그래밍을 하는 사람한테 기본적인 개념을 잡기에 잘 설명되어 있다. 이진수를 이용해서 간단한 덧셈을 할 수 있는 기기를 만들고 뺄셈도 할 수 있게 확장한 뒤에 값을 저장하기 위한 메모리를 추가하는 등 점점 현재 우리가 보는 컴퓨터의 개념으로 확장하고 있다. 중반을 넘어가면 마이크로프로세서가 등장하고 운영체제와 기계어를 포함한 프래그래밍 언어까지 오면서 책이 끝나게 된다.

500페이지 정도의 얇지 않은 책이지만 이 책만큼 잘 설명한 책은 본 적이 없고 더 잘 설명할 수 있을 것 같지도 않다. 때때로 너무 찬찬히 설명하는 게 아닌가 싶을 정도로 자세히 설명하고 있지만 플래시를 이용한 신호에서 프로그래밍언어를 사용하는 컴퓨터까지 발전하는 설명의 진행이 너무 자연스럽게 이루어져서 이 책을 쓰기 위해서 찰스 펫졸드가 얼마나 많은 고민을 했는지 느껴질 정도고 때때로 관련 없어 보이는 예시들까지 뒤의 개념들에 이어지는 것들을 보면서 책을 보는 내내 감탄을 자아내게 했다. 뒤로 갈수록 좀 복잡해져서 확실히 개념을 다 이해하지는 못했기 때문에 나중에 다시 읽어보고 싶은 책이다. 너무 광범위한 내용이라 요약을 하기는 어렵고 직접 읽어보시라 말하는 수밖에 없다.

2017년 4월 20일 목요일

[NEWS] 기술 뉴스 #8 : 14-05-01..


출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

웹개발 관련

그 밖의 프로그래밍 관련

IT 업계 뉴스

볼만한 링크

  • 구조로 풀어내는 혁신, 홀라크라시(Holacracy) : 자포스의 대담한 실험 : 본사를 도시로 꾸미는 시도를 한 Zappos가 홀라크라시라는 조직구조를 도입한다는 내용을 설명한 글. 조직구조에 대한 글이라 약간 복잡하긴 하지만 자포스의 실험이 어떻게 이어질지는 관심을 가질 만한 일로 보인다.(한글)
  • **Twitter's New Profiles Are Now Available to Everyone : 몇 달 전부터 일부 사용자에게 테스트하던 트위터의 새로운 프로필 디자인이 전체 적용되었다.(영문)
  • Facebook f8 : 4월 30일 페이스북의 f8 콘퍼런스가 3년 만에 열렸고 링크에서 관련 영상을 볼 수 있다. build, grow, monetize라는 키워드를 중심으로 개인정보 유출 없이 앱을 로그인해서 사용해 볼 수 있는 익명 로그인과 모바일 광고 네트워크는 Audience Network를 공개했다.
  • 사이드 프로젝트의 효용성 그리고 중요성 : 업무에서 하는 프로젝트 외에 개인적으로 수행하는 사이트 프로젝트로 어떤 효과가 있을 수 있는가를 설명한 글로 나도 선호하는 방식이기에 크기 다가왔다. 최근 존 레식이 접근한 매일 코드 작성하기에 대한 내용도 설명되어 있어서 사이트프로젝트에 관심이 있다면 볼만하다.(한글)
  • 솜노트 2.0으로 100일간 배운 것들 : 위자드웍스의 표철민님이 솜노트 2.0을 출시하고 100일 동안의 고민과 경험을 정리한 글로 솜노트가 시도한 내용과 그에 대한 인사이트에서 배울 내용이 많이 있다.(한글)
  • 'Growth Hacking(그로스 해킹)'이란? : 그로스 해킹이 무엇인지를 잘 설명한 글(한글)

2017년 4월 19일 수요일

[Spring 레퍼런스] 25장 이메일..


출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

이 문서는 개인적인 목적이나 배포하기 위해서 복사할 수 있다. 출력물이든 디지털 문서든 각 복사본에 어떤 비용도 청구할 수 없고 모든 복사본에는 이 카피라이트 문구가 있어야 한다.

24. 이메일

25.1 소개

스프링 프레임워크는 유용한 유틸리티 라이브러리를 제공하는데 이 라이브러리는 기반 메일링 시스템의 특화된 부분에서 사용자를 보호하면서 이메일을 발송하고 클라이언트를 대신해서 저수준 리소스 처리를 담당한다.
org.springframework.mail 패키지가 스프링 프레임워크 이메일 지원의 최상위 패키지다. 이메일을 발송하는 중심 인터페이스는 MailSender 인터페이스다. from, to같은 메일 속성들을 은닉화한 간단한 값 객체(value object)는 SimpleMailMessage 클래스이다. 이 패키지는 최상위 예외인 MailException를 던지는 저수준 메일 시스템 예외를 고수준으로 추상화한 체크드 익셉션 계층도 포함되어 있다. 리치 메일 예외 계층에 대한 자세한 내용은 JavaDoc을 참고해라.
org.springframework.mail.javamail.JavaMailSender 인터페이스는 MIME 메시지처럼 특수한 JavaMail 기능을 MailSender 인터페이스(혹은 이 인터페이스를 상속받은)에 추가한다. MailSender도 JavaMail MIME 메시지를 준비하기 위한 콜백 인터페이스 org.springframework.mail.javamail.MimeMessagePreparator를 제공한다.


라이브러리 의존성 스프링 프레임워크의 이메일 라이브러리를 사용하려면 애플리케이션의 클래스패스에 다음의 jar를 추가해야 한다. * JavaMail mail.jar 라이브러리 * JAF activation.jar 라이브러리 이 라이브러리들은 인터넷에서 무료로 구할 수 있다.


25.2 사용방법

OrderManager라는 비즈니스 인터페이스가 있다고 가정해 보자.

1
2
3
4
5
Java

public interface OrderManager {
  void placeOrder(Order order);
}

순서를 가진 이메일 메시지를 생성해야 하고 이 순서에 따라 고객에게 이메일을 발송해야 하는 요구사항이 있다고 해보자.

25.2.1 MailSender와 SimpleMailMessage의 기본 사용방법



 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
31
32
33
34
35
36
37
38
39
40
Java

import org.springframework.mail.MailException;
import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;

public class SimpleOrderManager implements OrderManager {
  private MailSender mailSender;
  private SimpleMailMessage templateMessage;

  public void setMailSender(MailSender mailSender) {
    this.mailSender = mailSender;
  }

  public void setTemplateMessage(SimpleMailMessage templateMessage) {
    this.templateMessage = templateMessage;
  }

  public void placeOrder(Order order) {
    // 비즈니스 연산을 수행...

    // 순서를 유지할 콜라보레이터를 호출...

    // 템플릿 메시지를 스레드 세이프하게 "복사본"을 생성하고 커스터마이징한다
    SimpleMailMessage msg = new SimpleMailMessage(this.templateMessage);
    msg.setTo(order.getCustomer().getEmailAddress());
    msg.setText(
      "Dear " + order.getCustomer().getFirstName()
        + order.getCustomer().getLastName()
        + ", thank you for placing order. Your order number is "
        + order.getOrderNumber());
    try{
      this.mailSender.send(msg);
    }
    catch(MailException ex) {
      // 로깅 등...
      System.err.println(ex.getMessage());
    }
  }
}

위 코드의 빈 정의가 다음에 나와 있다.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
Xml

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
  <property name="host" value="mail.mycompany.com"/>
</bean>

<bean id="templateMessage" class="org.springframework.mail.SimpleMailMessage">
  <property name="from" value="customerservice@mycompany.com"/>
  <property name="subject" value="Your order"/>
</bean>

<bean id="orderManager" class="com.mycompany.businessapp.support.SimpleOrderManager">
  <property name="mailSender" ref="mailSender"/>
  <property name="templateMessage" ref="templateMessage"/>
</bean>

25.2.2 JavaMailSender와 MimeMessagePreparator 사용하기

다음은 MimeMessagePreparator 콜백 인터페이스를 사용하는 OrderManager의 또 다른 구현체다. 이 경우 mailSender 프로퍼티가 JavaMailSender 타입이므로 JavaMail MimeMessage 클래스를 사용할 수 있다는 점을 주의 깊게 봐라.

 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
Java

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import javax.mail.internet.MimeMessage;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessagePreparator;

public class SimpleOrderManager implements OrderManager {
  private JavaMailSender mailSender;

  public void setMailSender(JavaMailSender mailSender) {
    this.mailSender = mailSender;
  }

  public void placeOrder(final Order order) {
    // 비즈니스 연산을 수행...

    // 순서를 유지할 콜라보레이터를 호출...

    MimeMessagePreparator preparator = new MimeMessagePreparator() {

      public void prepare(MimeMessage mimeMessage) throws Exception {
        mimeMessage.setRecipient(Message.RecipientType.TO,
            new InternetAddress(order.getCustomer().getEmailAddress()));
        mimeMessage.setFrom(new InternetAddress("mail@mycompany.com"));
        mimeMessage.setText(
          "Dear " + order.getCustomer().getFirstName() + " "
            + order.getCustomer().getLastName()
            + ", thank you for placing order. Your order number is "
            + order.getOrderNumber());
      }
    };
    try {
      this.mailSender.send(preparator);
    }
    catch (MailException ex) {
      // 로깅 등...
      System.err.println(ex.getMessage());
    }
  }
}



Note
메일 코드는 크로스컷팅 관심(crosscutting concern)이고 custom Spring AOP aspect로 리팩토링할 대상이 될 수 있으며 이는 OrderManager를 대상으로 한 적절한 조인포인트에서 실행될 수 있다.

스프링 프레임워크의 메일 지원은 JavaMail 구현체와 함께 제공된다. 더 자세한 내용은 JavaDoc을 참고해라.

25.3 JavaMail MimeMessageHelper 사용하기

JavaMail 메시지를 상당히 편하게 다룰 수 있게 하는 클래스는 org.springframework.mail.javamail.MimeMessageHelper로 복잡한 JavaMail API를 편하게 사용할 수 있다. MimeMessageHelper를 사용하면 MimeMessage를 아주 쉽게 생성할 수 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Java

// 당연히 실제 프로젝트에서는 DI를 사용할 것이다
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost("mail.host.com");

MimeMessage message = sender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message);
helper.setTo("test@host.com");
helper.setText("Thank you for ordering!");

sender.send(message);


25.3.1 첨부 파일이나 리소스를 내장해서 발송하기

첨부파일과 내장 리소스에 모두 멀티파트 이메일 메시지를 사용할 수 있다. 내장 리소스의 예시로는 메시지에서 사용할 이미지나 스타일 시트 등인데 대신 첨부 파일로 보이기는 원치 않는 리소스들이다.

25.3.1.1 첨부파일

다음 예제는 JPEG 이미지 첨부 파일이 있는 이메일을 보내는 MimeMessageHelper의 사용방법을 보여준다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
Java

JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost("mail.host.com");

MimeMessage message = sender.createMimeMessage();

// 멀티파트 메시지가 필요하다는 의미로 true 플래그를 사용한다
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setTo("test@host.com");

helper.setText("Check out this image!");

// 악명높은 윈도우의 Sample 파일을 첨부하자 (여기서는 c:/ 에서 복사한다)
FileSystemResource file = new FileSystemResource(new File("c:/Sample.jpg"));
helper.addAttachment("CoolImage.jpg", file);

sender.send(message);


25.3.1.2 내장 리소스

다음 예제는 내장 이미지를 가진 이메일을 보내는 MimeMessageHelper의 사용방법을 보여준다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
Java

JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost("mail.host.com");

MimeMessage message = sender.createMimeMessage();

// 멀티파트 메시지가 필요하다는 의미로 true 플래그를 사용한다
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setTo("test@host.com");

// 포함된 텍스트가 HTML이라는 의미로 true 플래그를 사용한다
helper.setText("<html><body><img src='cid:identifier1234'></body></html>", true);

// 악명높은 윈도우의 Sample 파일을 첨부하자 (여기서는 c:/ 에서 복사한다)
FileSystemResource res = new FileSystemResource(new File("c:/Sample.jpg"));
helper.addInline("identifier1234", res);

sender.send(message);



Warning
특정 Content-ID로(이 예제에서는 identifier1234) mime 메시지에 내장 리소스를 추가한다. 추가하는 텍스트와 리소스의 순서는 아주 중요하다. 텍스트를 먼저 추가하고 리소스를 나중에 추가해라. 반대로 한다면 동작하지 않는다!


25.3.2 템플릿 라이브러리로 이메일 콘텐츠를 생성하기

앞의 예제에서는 이메일 메시지의 내용을 message.setText(..)같은 메서드를 호출해서 명시적으로 생성했다. 앞의 예제처럼 간단한 이메일이라면 괜찮다.(앞의 예제는 기본적인 API를 보여주기 위함이었다.)
하지만 전형적인 엔터프라이즈 애플리케이션이라면 여러 가지 이유로 위의 방법으로 이메일의 내용을 만들지 않을 것이다.
  • Java 코드로 HTML 기반의 이메일 내용을 생성하는 것은 지루하고 오류가 발생할 가능성도 크다
  • 표현 로직과 비즈니스 로직 간의 구분이 쉽지 않다
  • 이메일 내용의 표현 구조를 변경하려면 자바 코드를 수정하고 다시 컴파일한 다음에 배포해야 한다
이 문제를 해결하는 일반적인 방법은 이메일 콘텐츠의 표현 구조를 정의할 때 FreeMarker나 Velocity 같은 템플릿 라이브러리를 사용하는 것이다. 템플릿 라이브러리를 사용하면 코드는 데이터를 생성하는 작업만 하고 이 데이터를 이메일 템플릿으로 렌더링해서 이메일을 발송한다. 이메일의 내용이 꽤 복잡하더라도 템플릿을 사용하는 것이 아주 좋은 방법이고 스프링 프레임워크의 FreeMarker와 Velocity 지원 클래스로 아주 쉽게 사용할 수 있다. Velocity 템플릿 라이브러리로 이메일 내용을 생성하는 예제가 다음에 나와 있다.

25.3.2.1 Velocity 기반 예제

이메일 템플릿을 만들 때 Velocity를 사용하려면 Velocity 라이브러리를 클래스패스에 두고 어플리케이션에서 사용할 이메일 컨텐츠의 Velocity 템플릿을 하나이상 만들어야 한다. 이 예제에서 사용할 Velocity 템플릿이 다음에 나와 있다. 예제에서 보듯이 템플릿은 HTML 기반이고 일반적인 텍스트이므로 자신이 선호하는 HTML 에디터나 텍스트 에디터로 템플릿을 작성할 수 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Html

# com/foo/package에 있다
<html>
<body>

Hi ${user.userName}, welcome to the Chipping Sodbury On-the-Hill message boards!

  Your email address is ${user.emailAddress}.

</body>

</html>

위의 Velocity 템플릿을 사용해서 이메일을 작성하고 보내는 간단한 코드와 스프링 XML 설정이 다음에 나와 있다.

 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
Java

package com.foo;

import org.apache.velocity.app.VelocityEngine;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.mail.javamail.MimeMessagePreparator;
import org.springframework.ui.velocity.VelocityEngineUtils;

import javax.mail.internet.MimeMessage;
import java.util.HashMap;
import java.util.Map;

public class SimpleRegistrationService implements RegistrationService {
  private JavaMailSender mailSender;
  private VelocityEngine velocityEngine;

  public void setMailSender(JavaMailSender mailSender) {
   this.mailSender = mailSender;
  }

  public void setVelocityEngine(VelocityEngine velocityEngine) {
   this.velocityEngine = velocityEngine;
  }

  public void register(User user) {

    // 등록과 관련된 로직...

    sendConfirmationEmail(user);
  }

  private void sendConfirmationEmail(final User user) {
    MimeMessagePreparator preparator = new MimeMessagePreparator() {
      public void prepare(MimeMessage mimeMessage) throws Exception {
        MimeMessageHelper message = new MimeMessageHelper(mimeMessage);
        message.setTo(user.getEmailAddress());
        message.setFrom("webmaster@csonth.gov.uk"); // 파라미터화할 수 있다...
        Map model = new HashMap();
        model.put("user", user);
        String text = VelocityEngineUtils.mergeTemplateIntoString(
           velocityEngine, "com/dns/registration-confirmation.vm", model);
        message.setText(text, true);
      }
    };
    this.mailSender.send(preparator);
  }
}


Xml

<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-3.0.xsd">
  <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
    <property name="host" value="mail.csonth.gov.uk"/>
  </bean>

  <bean id="registrationService" class="com.foo.SimpleRegistrationService">
    <property name="mailSender" ref="mailSender"/>
    <property name="velocityEngine" ref="velocityEngine"/>
  </bean>

  <bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
    <property name="velocityProperties">
       <value>
        resource.loader=class
        class.resource.loader.class=org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
       </value>
    </property>
  </bean>
</beans>

[Spring 레퍼런스] 24장 JCA CCI..


출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

이 문서는 개인적인 목적이나 배포하기 위해서 복사할 수 있다. 출력물이든 디지털 문서든 각 복사본에 어떤 비용도 청구할 수 없고 모든 복사본에는 이 카피라이트 문구가 있어야 한다.

24. JCA CCI

24.1 소개

Java EE는 엔터프라이즈 인포메이션 시스템(EIS, Enterprise Information Systems)에 대한 표준화된 접근의 명세 JCA(J2EE Connector Architecture)를 제공한다. 이 명세는 여러 부분으로 나누어져 있다.
  • 커넥터 프로바이더인 SPI (Service provider interfaces)는 반드시 구현해야 한다. 이러한 인터페이스는 Java EE 애플리케이션 서버에 배포할 수 있는 리소스 어댑터다. 이런 시나리오에서 서버는 커넥션 풀, 트랜잭션, 보안(관리 모드)를 관리한다. 애플리케이션 서버는 클라이언트 애플리케이션 외부에 있는 구성도 관리한다. 애플리케이션 서버 없이도 커넥터를 사용할 수 있는데 이 경우에는 애플리케이션이 커넥터를 직접 구성해야 한다.(비관리 모드)
  • 애플리케이션이 사용할 수 있는 CCI (Common Client Interface) 는 커넥터와 상호작용하므로 EIS로 통신한다. 로컬 트랜잭션 경계에 대한 API도 제공된다.
스프링 CCI 지원은 스프링 프레임워크의 일반적인 리소스 관리와 트랜잭션 관리 기능을 사용하면서 일반적인 스프링 방식으로 CCI 커넥터에 접근하는 클래스를 제공한다.


Note
커넥터의 클라이언트 측이 항상 CCI를 사용하는 것은 아니다. 어떤 커넥터는 자신만의 API를 가지고 Java EE 컨테이너의 시스템 규약(커넥션 풀링, 전역 트랜잭션, 보안)을 사용하는 JCA 리소스 어댑터만 제공하기도 한다. 이렇게 커넥터에 특화된 API를 스프링이 따로 지원하진 않는다.


24.2 CCI 구성

24.2.1 커넥터 구성

JCA CCI를 사용하는 기반은 ConnectionFactory 인터페이스다. 사용하는 커넥터는 반드시 이 인터페이스의 구현체를 제공해야 한다.
커넥터를 사용하려면 애플리케이션 서버에 커넥터를 배포하고 서버의 JNDI 환경에서 ConnectionFactory를 가져올 수 있어야 한다.(관리 모드) 커넥터를 RAR 파일(resource adapter archive)로 패키징해야 하고 배포와 관련된 정보를 담고 있는 ra.xml이 포함되어 있어야 한다. 리소스의 실제 이름은 배포할 때 지정한다. 스프링 내에서 커넥터에 접근하려면 JNDI 이름으로 팩토리를 가져오는 스프링의 JndiObjectFactoryBean / 를 사용하면 된다.
어플리케이션서버를 사용해서 배포하고 구성하는 대신 애플리케이션에 커넥터를 내장시키는 것도 커넥터를 사용하는 또 하나의 방법이다.(비관리 모드) 스프링에서는 제공된 FactoryBean으로 커넥터를 빈으로 구성할 수 있다. 이 방법을 사용할 때는 클래스패스에 커넥터 라이브러리만 넣으면 된다.(RAR 파일이나 ra.xml 디스크립터가 필요 없다.) 필요하다만 커넥터의 RAR 파일에서 라이브러리만 추출해야 한다.
일단 ConnectionFactory 인스턴스에 접근하고 나면 컴포넌트에 주입할 수 있다. 이러한 컴포넌트는 평범한 CCI API를 기반으로 작성하거나 스프링의 CCI 접근 지원 클래스(CciTemplate 같은)를 기반으로 작성할 수 있다.


Note
비관리 모드에서 커넥터를 사용할 때는 현재 스레드의 현재 전역 트랜잭션에 리소스가 절대 추가되거나 빠지지 않으므로 전역 트랜잭션을 사용할 수 없다. 리소스는 어떤 전역 Java EE 트랜잭션도 인지하지 못한다.


24.2.2 스프링에서 ConnectionFactory의 구성

EIS에 연결하려고 할 때 관리 모드인 경우에는 애플리케이션 서버에서 ConnectionFactory를 얻어야 하고 비관리 모드인 경우에는 스프링에서 직접 ConnectionFactory를 가져와야 한다.
관리 모드에서는 JNDI에서 ConnectionFactory에 접근한다. ConnectionFactory의 프로퍼티는 애플리케이션 서버에서 설정할 것이다.

1
2
3
Xml

<jee:jndi-lookup id="eciConnectionFactory" jndi-name="eis/cicseci"/>

비관리 모드에서는 스프링 설정에서 사용할 ConnectionFactory를 JavaBean으로 설정해야 한다. LocalConnectionFactoryBean 클래스가 커넥터의 ManagedConnectionFactory 구현체를 전달하고 애플리케이션 수준의 CCI ConnectionFactory를 노출시켜서 이러한 설정 방법을 제공한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Xml

<bean id="eciManagedConnectionFactory" class="com.ibm.connector2.cics.ECIManagedConnectionFactory">
  <property name="serverName" value="TXSERIES"/>
  <property name="connectionURL" value="tcp://localhost/"/>
  <property name="portNumber" value="2006"/>
</bean>

<bean id="eciConnectionFactory" class="org.springframework.jca.support.LocalConnectionFactoryBean">
  <property name="managedConnectionFactory" ref="eciManagedConnectionFactory"/>
</bean>



Note
특정 ConnectionFactory를 직접 인스턴스화 할 수는 없다. 커넥터에 대한 ManagedConnectionFactory 인터페이스의 대응되는 구현체를 통해야 한다. 이 인터페이스는 JCA SPI 명세에 포함된 내용이다.


24.2.3 CCI 연결 구성

개발자는 JCA CCI를 사용해서 커넥터의 ConnectionSpec 구현체를 사용해서 EIS에 대한 연결을 구성할 수 있다. ConnectionSpec의 프로퍼티를 설정하려면 전용 어댑터 ConnectionSpecConnectionFactoryAdapter로 대상 커넥션 팩토리를 감싸야 한다. 그래서 전용 ConnectionSpec를 connectionSpec 프로퍼티(내부 빈으로)로 설정할 수 있다.
CCI ConnectionFactory 인터페이스는 CCI 연결을 얻는 두 가지 방법을 정의하고 있으므로 이 프로퍼티는 의무사항은 아니다. ConnectionSpec 프로퍼티 중 일부는 애플리케이션 서버에서 설정하거나(관리모드에서) 대응되는 로컬 ManagedConnectionFactory 구현체에서 설정할 수 있다.

1
2
3
4
5
6
7
8
Java

public interface ConnectionFactory implements Serializable, Referenceable {
  ...
  Connection getConnection() throws ResourceException;
  Connection getConnection(ConnectionSpec connectionSpec) throws ResourceException;
  ...
}

스프링은 해당 팩토리의 모든 동작을 사용할 수 있도록 ConnectionSpec 인스턴스를 지정할 수 있는 ConnectionSpecConnectionFactoryAdapter를 제공한다. 이 어댑터의 connectionSpec 프로퍼티를 지정했다면 어댑터는 인자 없이 getConnection 계열을 사용하고 지정하지 않았다면 ConnectionSpec 인자와 함께 사용한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Xml

<bean id="managedConnectionFactory"
    class="com.sun.connector.cciblackbox.CciLocalTxManagedConnectionFactory">
  <property name="connectionURL" value="jdbc:hsqldb:hsql://localhost:9001"/>
  <property name="driverName" value="org.hsqldb.jdbcDriver"/>
</bean>

<bean id="targetConnectionFactory"
    class="org.springframework.jca.support.LocalConnectionFactoryBean">
  <property name="managedConnectionFactory" ref="managedConnectionFactory"/>
</bean>

<bean id="connectionFactory"
    class="org.springframework.jca.cci.connection.ConnectionSpecConnectionFactoryAdapter">
  <property name="targetConnectionFactory" ref="targetConnectionFactory"/>
  <property name="connectionSpec">
    <bean class="com.sun.connector.cciblackbox.CciConnectionSpec">
      <property name="user" value="sa"/>
      <property name="password" value=""/>
    </bean>
  </property>
</bean>


24.2.4 단일 CCI 연결의 사용

단일 CCI 연결을 사용하고 싶은 경우를 위해서 스프링은 ConnectionFactory 어댑터로 이를 관리할 수 있게 한다. SingleConnectionFactory 어댑터 클래스가 단일 연결을 지연해서 열고 애플리케이션 종료 시 해당 빈이 파괴될 때 연결을 닫는다. 이 클래스는 물리적 연결에 기반을 둬서 같은 연결을 공유하면서 상황에 따라 적절하게 동작하는 전용 Connection 프록시를 노출할 것이다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
Xml

<bean id="eciManagedConnectionFactory"
    class="com.ibm.connector2.cics.ECIManagedConnectionFactory">
  <property name="serverName" value="TEST"/>
  <property name="connectionURL" value="tcp://localhost/"/>
  <property name="portNumber" value="2006"/>
</bean>

<bean id="targetEciConnectionFactory"
    class="org.springframework.jca.support.LocalConnectionFactoryBean">
  <property name="managedConnectionFactory" ref="eciManagedConnectionFactory"/>
</bean>

<bean id="eciConnectionFactory"
    class="org.springframework.jca.cci.connection.SingleConnectionFactory">
  <property name="targetConnectionFactory" ref="targetEciConnectionFactory"/>
</bean>



Note
이 ConnectionFactory 어댑터는 ConnectionSpec로 직접 설정할 수 없다. 특정 ConnectionSpec에 대한 단일 커넥션이 필요한 경우에는 SingleConnectionFactory와 통신하는 ConnectionSpecConnectionFactoryAdapter로 중개해서 사용해라.


24.3 스프링의 CCI 접근에 대한 지원 사용하기

24.3.1 레코드(Record) 변환

JCA CCI 지원 중 하나는 CCI 레코드를 편리하게 조작할 수 있도록 하는 것이다. 개발자는 스프링의 CciTemplate를 사용해서 레코드를 생성하고 레코드에서 데이터를 추출하는 전략을 지정할 수 있다. 다음 인터페이스들은 애플리케이션에서 레코드를 직접 다루길 원하지 않는 경우 입력과 출력 레코드를 사용하는 전략을 설정할 것이다.
입력 Record를 생성하기 위해 개발자가 RecordCreator 인터페이스의 전용 구현체를 사용할 수 있다.

1
2
3
4
5
Java

public interface RecordCreator {
  Record createRecord(RecordFactory recordFactory) throws ResourceException, DataAccessException;
}

여기서 보듯이 createRecord(..) 메서드는 파라미터로 RecordFactory를 받는다. 이 파라미터는 사용한 ConnectionFactory의 RecordFactory를 가리키는데 IndexedRecord나 MappedRecord를 생성하는 데 사용한다. 다음 예제는 RecordCreator 인터페이스와 색인 되거나(indexed) 매핑된(mapped) 레코드를 사용하는 방법을 보여준다.

1
2
3
4
5
6
7
8
9
Java

public class MyRecordCreator implements RecordCreator {
  public Record createRecord(RecordFactory recordFactory) throws ResourceException {
    IndexedRecord input = recordFactory.createIndexedRecord("input");
    input.add(new Integer(id));
    return input;
  }
}

EIS에서 다시 데이터를 받을 때 출력 Record를 사용할 수도 있다. 그러므로 출력 Record의 데이터를 추출하기 위해 RecordExtractor 인터페이스의 특정 구현체를 스프링 CciTemplate에 전달할 수 있다.

1
2
3
4
5
Java

public interface RecordExtractor {
  Object extractData(Record record) throws ResourceException, SQLException, DataAccessException;
}

다음 예제는 RecordExtractor 인터페이스를 사용하는 방법을 보여준다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Java

public class MyRecordExtractor implements RecordExtractor {
  public Object extractData(Record record) throws ResourceException {
    CommAreaRecord commAreaRecord = (CommAreaRecord) record;
    String str = new String(commAreaRecord.toByteArray());
    String field1 = string.substring(0,6);
    String field2 = string.substring(6,1);
    return new OutputObject(Long.parseLong(field1), field2);
  }
}


24.3.2 CciTemplate

CciTemplate은 핵심 CCI 지원 패키지 (org.springframework.jca.cci.core)의 중심이 되는 클래스다. CciTemplate는 리소스의 생성과 해제를 관리해서 CCI를 쉽게 사용할 수 있도록 한다. 그리고 연결을 닫는 것을 잊어버리는 등의 일반적인 오류를 피할 수 있게 해준다. CciTemplate는 연결과 상호작용(interaction) 객체의 생명주기를 관리해서 애플리케이션 코드는 애플리케이션에서 입력 레코드를 생성하고 출력 레코드에서 애플리케이션 데이터를 추출하는 데 집중하도록 한다.
JCA CCI 명세는 EIS에서 작업을 호출하는 별개의 두 메서드를 정의하고 있다. CCI Interaction 인터페이스는 두 가지 execute 메서드 시그니처를 제공한다.

1
2
3
4
5
6
7
8
9
Java

public interface javax.resource.cci.Interaction {
  ...
  boolean execute(InteractionSpec spec, Record input, Record output) throws ResourceException;

  Record execute(InteractionSpec spec, Record input) throws ResourceException;
  ...
}

호출된 템플릿 메서드에 따라 CciTemplate이 상호작용에 어떤 execute 메서드를 호출할지 알 수 있다. 어떤 경우에든 제대로 초기화된 InteractionSpec 인스턴스가 필요하다.
CciTemplate.execute(..)는 두 가지로 사용할 수 있다.
  • 직접 Record 인자로 사용하는 방법. 이 경우 CCI 입력 레코드를 전달해야 하고 반환된 객체는 CCI 출력 레코드에 대응된다.
  • 레코드 매핑을 사용해서 애플리케이션 객체를 사용하는 방법. 이 경우에는 대응되는 RecordCreator와 RecordExtractor 인스턴스를 제공해야 한다.
첫번째 방법을 사용하면 템플릿의 다음 메서드를 사용할 것이다. 이 메서드들은 Interaction 인터페이스의 메서드들과 직접 대응된다.

1
2
3
4
5
6
7
8
9
Java

public class CciTemplate implements CciOperations {
  public Record execute(InteractionSpec spec, Record inputRecord)
      throws DataAccessException { ... }

  public void execute(InteractionSpec spec, Record inputRecord, Record outputRecord)
      throws DataAccessException { ... }
}

두번째 방법을 사용하면 인자로 레코드 생성 전략과 레코드 추출 전략을 지정해야 한다. 레코드 변환에 사용하는 인터페이스는 이전 장에서 설명했다. 대응되는 CciTemplate 메서드는 다음과 같다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Java

public class CciTemplate implements CciOperations {
  public Record execute(InteractionSpec spec, RecordCreator inputCreator)
      throws DataAccessException { ... }

  public Object execute(InteractionSpec spec, Record inputRecord, RecordExtractor outputExtractor)
      throws DataAccessException { ... }

  public Object execute(InteractionSpec spec, RecordCreator creator, RecordExtractor extractor)
      throws DataAccessException { ... }
}

템플릿에 outputRecordCreator 프로퍼티를 설정하지 않으면(다음 장 참고) 모든 메서드가 적절한 CCI Interaction의 execute 메서드를 두 개의 파라미터 InteractionSpec와 입력 Record로 호출할 것이고 이 메서드는 출력 Record를 반환한다. CciTemplate은 RecordCreator 구현체 외부에서 IndexRecord와 MappedRecord를 생성하는 createIndexRecord(..)와 createMappedRecord(..) 메서드도 제공한다. 해당 CciTemplate.execute(..) 메서드에 전달할 Record 인스턴스를 생성하기 위해서 DAO 구현체 내에서 이 메서드를 사용할 수 있다.

1
2
3
4
5
6
7
Java

public class CciTemplate implements CciOperations {
  public IndexedRecord createIndexedRecord(String name) throws DataAccessException { ... }

  public MappedRecord createMappedRecord(String name) throws DataAccessException { ... }
}


24.3.3 DAO 지원

스프링의 CCI 지원은 DAO에 대한 추상 클래스를 제공하고 ConnectionFactory나 CciTemplate 인스턴스의 주입을 지원한다. 클래스 이름은 CciDaoSupport이고 setConnectionFactory와 setCciTemplate 메서드를 제공한다. 내부적으로 이 클래스는 하위클래스에서 데이터 접근 구현체를 구현할 수 있게 CciTemplate 인스턴스를 노출하는 해당 ConnectionFactory를 위해서 CciTemplate 인스턴스를 생성할 것이다.

1
2
3
4
5
6
7
8
9
Java

public abstract class CciDaoSupport {
  public void setConnectionFactory(ConnectionFactory connectionFactory) { ... }
  public ConnectionFactory getConnectionFactory() { ... }

  public void setCciTemplate(CciTemplate cciTemplate) { ... }
  public CciTemplate getCciTemplate() { ... }
}


24.3.4 출력 레코드 자동 생성

사용한 커넥터가 입력 레코드와 출력 레코드를 파라미터로 받는 Interaction.execute(..) 메서드(즉, 이 메서드는 적절한 출력 레코드를 반환하지 않고 원하는 출력 레코드를 전달해야 한다.)만 지원한다면 응답을 받을 때 JCA 커넥터가 작성하는 출력 레코드를 자동으로 생성할 수 있게 CciTemplate의 outputRecordCreator 프로퍼티를 설정할 수 있다. 그러면 템플릿의 호출자에게 이 레코드를 반환할 것이다.
위 목적에 맞게 이 프로퍼티에는 RecordCreator 인터페이스의 구현체가 담겨 있다. RecordCreator 인터페이스는 Section 24.3.1, “레코드(Record) 변환”에서 이미 설명했다. outputRecordCreator 프로퍼티는 CciTemplate에서 직접 지정해야 한다. 애플리케이션 코드에서는 다음과 같이 작성한다.

1
2
3
Java

cciTemplate.setOutputRecordCreator(new EciOutputRecordCreator());

아니면 스프링 설정에서 전용 빈 인스턴스로 CciTemplate을 설정한다.(추천하는 방법)

1
2
3
4
5
6
7
8
Xml

<bean id="eciOutputRecordCreator" class="eci.EciOutputRecordCreator"/>

<bean id="cciTemplate" class="org.springframework.jca.cci.core.CciTemplate">
  <property name="connectionFactory" ref="eciConnectionFactory"/>
  <property name="outputRecordCreator" ref="eciOutputRecordCreator"/>
</bean>



Note
CciTemplate 클래스가 스레드 세이프 하므로 공유하는 인스턴스로 설정하는 게 일반적이다.


24.3.5 요약

다음 표는 CciTemplate 클래스의 메커니즘과 CCI Interaction 인터페이스에서 호출되는 해당 메서드를 요약해서 보여주고 있다.

Table 24.1. Interaction execute 메서드의 사용방법
CciTemplate 메서드 시그니처CciTemplate outputRecordCreator 프로퍼티CCI Interaction에서 호출되는 execute 메서드
Record execute(InteractionSpec, Record)설정안함Record execute(InteractionSpec, Record)
Record execute(InteractionSpec, Record)설정함boolean execute(InteractionSpec, Record, Record)
void execute(InteractionSpec, Record, Record)설정안함void execute(InteractionSpec, Record, Record)
void execute(InteractionSpec, Record, Record)설정함void execute(InteractionSpec, Record, Record)
Record execute(InteractionSpec, RecordCreator)설정안함Record execute(InteractionSpec, Record)
Record execute(InteractionSpec, RecordCreator)설정함void execute(InteractionSpec, Record, Record)
Record execute(InteractionSpec, Record, RecordExtractor)설정안함Record execute(InteractionSpec, Record)
Record execute(InteractionSpec, Record, RecordExtractor)설정함void execute(InteractionSpec, Record, Record)
Record execute(InteractionSpec, RecordCreator, RecordExtractor)설정안함Record execute(InteractionSpec, Record)
Record execute(InteractionSpec, RecordCreator, RecordExtractor)설정함void execute(InteractionSpec, Record, Record)




















24.3.6 CCI Connection와 Interaction을 직접 사용하기

CciTemplate도 JdbcTemplate나 JmsTemplate와 같은 방식으로 직접 CCI 연결과 상호작용을 할 수 있게 지원한다. 이는 CCI 연결이나 상호작용에서 여러 작업을 수행하고자 할 때 유용하다.
ConnectionCallback 인터페이스는 CCI Connection에서 임의의 작업을 할 수 있도록 인자로 CCI Connection을 제공한다. 아니면 Connection를 생성한 CCI ConnectionFactory을 제공할 수도 있다. 후자의 경우는 관련 RecordFactory 인스턴스를 가져와서 색인 된/매핑된 레코드를 생성할 때 유용하게 사용할 수 있다.

1
2
3
4
5
6
Java

public interface ConnectionCallback {
  Object doInConnection(Connection connection, ConnectionFactory connectionFactory)
      throws ResourceException, SQLException, DataAccessException;
}

InteractionCallback 인터페이스는 CCI Interaction에서 임의의 작업을 할 수 있도록 CCI Interaction를 제공하고 대응되는 CCI ConnectionFactory를 제공할 수도 있다.

1
2
3
4
5
6
Java

public interface InteractionCallback {
  Object doInInteraction(Interaction interaction, ConnectionFactory connectionFactory)
      throws ResourceException, SQLException, DataAccessException;
}



Note
InteractionSpec 객체는 여러 템플릿 호출 간에 공유할 수도 있고 콜백 메서드마다 내부에서 새로 생성할 수도 있다. 어떻게 되느냐는 오로지 DAO 구현체에 달려있다.


24.3.7 CciTemplate 사용 예제

이번 장의 CciTemplate 사용방법은 IBM CICS ECI 커넥터를 사용해서 ECI 모드로 CICS에 접근하는 방법을 보여준다.
우선 접근할 CICS 프로그램을 지정하고 CICS 프로그램과 상호작용하는 방법을 지정할 수 있도록 CCI InteractionSpec의 초기화가 이뤄져야 한다.

1
2
3
4
5
Java

ECIInteractionSpec interactionSpec = new ECIInteractionSpec();
interactionSpec.setFunctionName("MYPROG");
interactionSpec.setInteractionVerb(ECIInteractionSpec.SYNC_SEND_RECEIVE);

이 프로그램은 스프링의 템플릿을 사용해서 CCI를 사용할 수 있고 커스텀 객체와 CCI Records간의 매핑을 지정할 수 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Java

public class MyDaoImpl extends CciDaoSupport implements MyDao {
  public OutputObject getData(InputObject input) {
    ECIInteractionSpec interactionSpec = ...;
    OutputObject output = (ObjectOutput) getCciTemplate().execute(interactionSpec,
        new RecordCreator() {
          public Record createRecord(RecordFactory recordFactory) throws ResourceException {
            return new CommAreaRecord(input.toString().getBytes());
          }
        },
        new RecordExtractor() {
          public Object extractData(Record record) throws ResourceException {
            CommAreaRecord commAreaRecord = (CommAreaRecord)record;
            String str = new String(commAreaRecord.toByteArray());
            String field1 = string.substring(0,6);
            String field2 = string.substring(6,1);
            return new OutputObject(Long.parseLong(field1), field2);
          }
        });

    return output;
  }
}

앞에서 얘기했듯이 CCI 연결과 상호작용에서 직접 동작하도록 콜백을 사용할 수 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
Java

public class MyDaoImpl extends CciDaoSupport implements MyDao {
  public OutputObject getData(InputObject input) {
    ObjectOutput output = (ObjectOutput) getCciTemplate().execute(
        new ConnectionCallback() {
          public Object doInConnection(Connection connection, ConnectionFactory factory)
              throws ResourceException {

            // 여기서 작업을 수행한다...
          }
        });
    }
    return output;
  }
}



Note
ConnectionCallback에서 사용한 Connection를 관리할 것이고 CciTemplate가 Connection를 닫을 것이지만 연결에서 생성한 모든 상호작용을 콜백 구현체에서 관리해야 한다.

더 상세한 콜백이 필요하다면 InteractionCallback를 구현할 수 있다. 이 경우 전달한 Interaction를 관리할 것이고 CciTemplate가 Interaction를 닫을 것이다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
Java

public class MyDaoImpl extends CciDaoSupport implements MyDao {
  public String getData(String input) {
    ECIInteractionSpec interactionSpec = ...;
    String output = (String) getCciTemplate().execute(interactionSpec,
        new InteractionCallback() {
          public Object doInInteraction(Interaction interaction, ConnectionFactory factory)
              throws ResourceException {
            Record input = new CommAreaRecord(inputString.getBytes());
            Record output = new CommAreaRecord();
            interaction.execute(holder.getInteractionSpec(), input, output);
            return new String(output.toByteArray());
          }
        });

    return output;
  }
}

위 예제에 포함된 스프링 빈의 해당 설정은 비관리 모드에서는 다음과 같을 것이다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
Xml

<bean id="managedConnectionFactory" class="com.ibm.connector2.cics.ECIManagedConnectionFactory">
  <property name="serverName" value="TXSERIES"/>
  <property name="connectionURL" value="local:"/>
  <property name="userName" value="CICSUSER"/>
  <property name="password" value="CICS"/>
</bean>

<bean id="connectionFactory" class="org.springframework.jca.support.LocalConnectionFactoryBean">
  <property name="managedConnectionFactory" ref="managedConnectionFactory"/>
</bean>

<bean id="component" class="mypackage.MyDaoImpl">
  <property name="connectionFactory" ref="connectionFactory"/>
</bean>

관리모드(즉, Java EE 환경)에서는 설정이 다음과 같을 것이다.

1
2
3
4
5
6
7
Xml

<jee:jndi-lookup id="connectionFactory" jndi-name="eis/cicseci"/>

<bean id="component" class="MyDaoImpl">
  <property name="connectionFactory" ref="connectionFactory"/>
</bean>


24.4 operation 객체로 CCI 접근 모델링하기

org.springframework.jca.cci.object 패키지에서는 다른 방법으로 EIS에 접근할 수 있는 지원 클래스가 있다. 이는 스프링의 JDBC operation 객체(JDBC 장을 봐라)와 유사한 재사용 가능한 operation 객체를 통해서 이뤄지고 보통 CCI API를 은닉화할 것이다. 이는 애플리케이션 수준의 입력 객체를 operation 객체에 전달할 것이므로 입력 레코드를 구성하고 받은 레코드 데이터를 애플리케이션 수준의 출력 객체로 변환해서 반환할 수 있다.
Note: 이 접근방법은 내부적으로 CciTemplate 클래스와 RecordCreator / RecordExtractor 인터페이스에 기반을 두고 있고 스프링의 핵심 CCI 지원 기능을 재사용한다.

24.4.1 MappingRecordOperation

MappingRecordOperation는 본래 CciTemplate와 같은 작업을 하지만 미리 구성한 특정 작업을 객체로 나타낸다. MappingRecordOperation는 입력 객체를 입력 레코드로 변환하는(레코드 매핑) 방법을 지정하는 두 가지 템플릿 메서드를 제공한다.
  • 입력 객체를 입력 Record로 변환하는 방법을 지정하는 createInputRecord(..)
  • 출력 Record에서 출력 객체를 추출하는 방법을 지정하는 extractOutputData(..)
이 메서드들의 시그니처는 다음에 나와 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Java

public abstract class MappingRecordOperation extends EisOperation {
  ...
  protected abstract Record createInputRecord(RecordFactory recordFactory, Object inputObject)
      throws ResourceException, DataAccessException { ... }

  protected abstract Object extractOutputData(Record outputRecord)
      throws ResourceException, SQLException, DataAccessException { ... }
  ...
}

EIS 작업을 실행하려면 단일 execute 메서드를 사용해야 하고 애플리케이션 수준의 입력 객체를 전달해서 그 결과로 애플리케이션 수준의 출력 객체를 받는다.

1
2
3
4
5
6
7
Java

public abstract class MappingRecordOperation extends EisOperation {
  ...
  public Object execute(Object inputObject) throws DataAccessException {
  ...
}

여기서 보듯이 CciTemplate 클래스와는 반대로 execute(..) 메서드는 인자로 InteractionSpec를 갖지 않는다. 대신에 InteractionSpec는 작업(operation)에 대해서 전역적이다. 작업(operation) 객체를 특정 InteractionSpec로 인스턴스화 하는데 다음의 생성자를 반드시 사용해야 한다.

1
2
3
4
5
Java

InteractionSpec spec = ...;
MyMappingRecordOperation eisOperation = new MyMappingRecordOperation(getConnectionFactory(), spec);
...


24.4.2 MappingCommAreaOperation

일부 커넥터는 EIS에 전송할 파라미터와 EIS에서 반환받은 데이터를 담고 있는 바이트 배열을 나타내는 COMMAREA에 기반을 둬서 레코드를 사용한다. 레코드 대신 COMMAREA와 직접 동작하는 특수한 오퍼레이션 클래스를 스프링이 제공하고 있다. MappingCommAreaOperation 클래스는 COMMAREA 등을 지원하기 위해 MappingRecordOperation 클래스를 상속받는다. MappingCommAreaOperation는 암묵적으로 입력과 출력 레코드로 CommAreaRecord 클래스를 사용하고 입력 객체를 입력 COMMAREA로 변환하고 출력 COMMAREA를 출력 객체로 변환하는 새로운 두 메서드를 제공한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Java

public abstract class MappingCommAreaOperation extends MappingRecordOperation {
  ...
  protected abstract byte[] objectToBytes(Object inObject)
      throws IOException, DataAccessException;

  protected abstract Object bytesToObject(byte[] bytes)
      throws IOException, DataAccessException;
  ...
}


24.4.3 출력 레코드 자동 생성

모든 MappingRecordOperation 하위클래스가 내부적으로 CciTemplate에 기반을 두고 있으므로 CciTemplate와 같을 방법으로 출력 레코드를 자동 생성할 수 있다. 각 작업 객체는 이에 대응되는 setOutputRecordCreator(..) 메서드를 제공한다. 더 자세한 내용은 Section 24.3.4, “출력 레코드 자동 생성”를 참고해라.

24.4.4 요약

작업(operation) 객체 접근방법은 CciTemplate 클래스와 같은 방법으로 레코드를 사용한다.

Table 24.2. Interaction execute 메서드의 사용방법
MappingRecordOperation 메서드 시그니처MappingRecordOperation outputRecordCreator 프로퍼티CCI Interaction에서 호출된 execute 메서드
Object execute(Object)설정 안함Record execute(InteractionSpec, Record)
Object execute(Object)설정함boolean execute(InteractionSpec, Record, Record)





24.4.5 MappingRecordOperation 사용방법의 예시

이번 장에서는 블랙박스 CCI 커넥터로 데이터베이스에 접근하는 MappingRecordOperation의 사용방법을 볼 것이다.


Note
이 커넥터의 원래 버전은 (Sun의) Java EE SDK (버전 1.3)에서 제공한 것이다.

우선, 어떤 SQL 요청을 처리할 것인지 정하기 위해 CCI InteractionSpec의 일부 초기화 작업이 완료되어야 한다. 이 예제에서는 요청의 파라미터들을 CCI 레코드로 변환하는 방법과 CCI 결과 레코드를 Person 클래스의 인스턴스로 변환하는 방법을 직접 정의한다.

 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
31
32
Java

public class PersonMappingOperation extends MappingRecordOperation {

  public PersonMappingOperation(ConnectionFactory connectionFactory) {
    setConnectionFactory(connectionFactory);
    CciInteractionSpec interactionSpec = new CciConnectionSpec();
    interactionSpec.setSql("select * from person where person_id=?");
    setInteractionSpec(interactionSpec);
  }

  protected Record createInputRecord(RecordFactory recordFactory, Object inputObject)
      throws ResourceException {
    Integer id = (Integer) inputObject;
    IndexedRecord input = recordFactory.createIndexedRecord("input");
    input.add(new Integer(id));
    return input;
  }

  protected Object extractOutputData(Record outputRecord)
      throws ResourceException, SQLException {
    ResultSet rs = (ResultSet) outputRecord;
    Person person = null;
    if (rs.next()) {
      Person person = new Person();
      person.setId(rs.getInt("person_id"));
      person.setLastName(rs.getString("person_last_name"));
      person.setFirstName(rs.getString("person_first_name"));
    }
    return person;
  }
}

그러면 애플리케이션이 인자로 person 식별자를 사용해서 작업 객체를 실행할 수 있다. 이 작업객체는 스레드 세이프 하므로 공유 인스턴스로 설정할 수도 있다.

1
2
3
4
5
6
7
8
9
Java

public class MyDaoImpl extends CciDaoSupport implements MyDao {
  public Person getPerson(int id) {
    PersonMappingOperation query = new PersonMappingOperation(getConnectionFactory());
    Person person = (Person) query.execute(new Integer(id));
    return person;
  }
}

비관리 모드에서 이와 같은 스프링 빈 설정은 다음과 같을 것이다.

 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
Xml

<bean id="managedConnectionFactory"
    class="com.sun.connector.cciblackbox.CciLocalTxManagedConnectionFactory">
  <property name="connectionURL" value="jdbc:hsqldb:hsql://localhost:9001"/>
  <property name="driverName" value="org.hsqldb.jdbcDriver"/>
</bean>

<bean id="targetConnectionFactory"
    class="org.springframework.jca.support.LocalConnectionFactoryBean">
  <property name="managedConnectionFactory" ref="managedConnectionFactory"/>
</bean>

<bean id="connectionFactory"
    class="org.springframework.jca.cci.connection.ConnectionSpecConnectionFactoryAdapter">
  <property name="targetConnectionFactory" ref="targetConnectionFactory"/>
  <property name="connectionSpec">
    <bean class="com.sun.connector.cciblackbox.CciConnectionSpec">
      <property name="user" value="sa"/>
      <property name="password" value=""/>
    </bean>
  </property>
</bean>

<bean id="component" class="MyDaoImpl">
  <property name="connectionFactory" ref="connectionFactory"/>
</bean>

관리 모드에서는(즉, Java EE 환경) 설정이 다음과 같을 것이다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
Xml

<jee:jndi-lookup id="targetConnectionFactory" jndi-name="eis/blackbox"/>

<bean id="connectionFactory"
    class="org.springframework.jca.cci.connection.ConnectionSpecConnectionFactoryAdapter">
  <property name="targetConnectionFactory" ref="targetConnectionFactory"/>
  <property name="connectionSpec">
    <bean class="com.sun.connector.cciblackbox.CciConnectionSpec">
      <property name="user" value="sa"/>
      <property name="password" value=""/>
    </bean>
  </property>
</bean>

<bean id="component" class="MyDaoImpl">
  <property name="connectionFactory" ref="connectionFactory"/>
</bean>


24.4.6 MappingCommAreaOperation 사용방법의 예시

이번 장에서는 MappingCommAreaOperation을 사용해서 IBM CICS ECI 커넥터로 ECI 모드에서 CICS에 접근하는 방법을 볼 것이다. 먼저 어떤 CICS 프로그램에 접근하고 어떻게 상호작용할 것인지 지정하기 위해 CCI InteractionSpec을 초기화 해야 한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
Java

public abstract class EciMappingOperation extends MappingCommAreaOperation {
  public EciMappingOperation(ConnectionFactory connectionFactory, String programName) {
    setConnectionFactory(connectionFactory);
    ECIInteractionSpec interactionSpec = new ECIInteractionSpec(),
    interactionSpec.setFunctionName(programName);
    interactionSpec.setInteractionVerb(ECIInteractionSpec.SYNC_SEND_RECEIVE);
    interactionSpec.setCommareaLength(30);
    setInteractionSpec(interactionSpec);
    setOutputRecordCreator(new EciOutputRecordCreator());
  }

  private static class EciOutputRecordCreator implements RecordCreator {
    public Record createRecord(RecordFactory recordFactory) throws ResourceException {
      return new CommAreaRecord();
    }
  }
}

초기화하고 나면 커스텀 객체와 Records의 매핑을 위해 추상 EciMappingOperation 클래스가 하위 클래스가 될 수 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
Java

public class MyDaoImpl extends CciDaoSupport implements MyDao {
  public OutputObject getData(Integer id) {
    EciMappingOperation query = new EciMappingOperation(getConnectionFactory(), "MYPROG") {
      protected abstract byte[] objectToBytes(Object inObject) throws IOException {
        Integer id = (Integer) inObject;
        return String.valueOf(id);
      }
      protected abstract Object bytesToObject(byte[] bytes) throws IOException;
        String str = new String(bytes);
        String field1 = str.substring(0,6);
        String field2 = str.substring(6,1);
        String field3 = str.substring(7,1);
        return new OutputObject(field1, field2, field3);
      }
    });

    return (OutputObject) query.execute(new Integer(id));
  }
}

비관리 모드에서 스프링 빈을 사용한 설정은 다음과 같을 것이다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
Xml

<bean id="managedConnectionFactory" class="com.ibm.connector2.cics.ECIManagedConnectionFactory">
  <property name="serverName" value="TXSERIES"/>
  <property name="connectionURL" value="local:"/>
  <property name="userName" value="CICSUSER"/>
  <property name="password" value="CICS"/>
</bean>

<bean id="connectionFactory" class="org.springframework.jca.support.LocalConnectionFactoryBean">
  <property name="managedConnectionFactory" ref="managedConnectionFactory"/>
</bean>

<bean id="component" class="MyDaoImpl">
  <property name="connectionFactory" ref="connectionFactory"/>
</bean>

관리모드에서는(즉, Java EE 환경) 설정이 다음과 같을 것이다.

1
2
3
4
5
6
7
Xml

<jee:jndi-lookup id="connectionFactory" jndi-name="eis/cicseci"/>

<bean id="component" class="MyDaoImpl">
  <property name="connectionFactory" ref="connectionFactory"/>
</bean>


24.5 트랜잭션

JCA는 리소스 어댑터의 트랜잭션을 여러 단계로 지원한다. 리소스 어댑터가 지원하는 트랜잭션 종류는 리소스 어댑터의 ra.xml 파일에서 지정한다. 사실상 세 가지 옵션이 있는데 none(예시: CICS EPI 커넥터), 지역(local) 트랜잭션(예시: CICS ECI 커넥터), 전역(global) 트랜잭션(예시: IMS 커넥터)이다.

1
2
3
4
5
6
7
8
9
Xml

<connector>
  <resourceadapter>
    <!-- <transaction-support>NoTransaction</transaction-support> -->
    <!-- <transaction-support>LocalTransaction</transaction-support> -->
    <transaction-support>XATransaction</transaction-support>
  <resourceadapter>
<connector>

전역 트랜잭션에서는 트랜잭션의 경계를 정하기 위해 JtaTransactionManager를 백엔드로 사용해서(Java EE 서버의 분산 트랜잭션 코디네이터에 위임해서) 스프링의 제너릭 트랜잭션을 사용할 수 있다.
단일 CCI ConnectionFactory상의 지역 트랜잭션에서는 JDBC의 DataSourceTransactionManager와 유사하게 스프링이 CCI에 대한 구체적인 트랜잭션 관리 전략을 제공한다. CCI API는 지역 트랜잭션 객체와 해당 지역 트랜잭션 경계 메서드를 정의한다. 스프링의 CciLocalTransactionManager는 이러한 지역 CCI 트랜잭션을 실행하고 이는 스프링의 제너릭 PlatformTransactionManager 추상화와 완전히 호환된다.

1
2
3
4
5
6
7
Xml

<jee:jndi-lookup id="eciConnectionFactory" jndi-name="eis/cicseci"/>
<bean id="eciTransactionManager"
    class="org.springframework.jca.cci.connection.CciLocalTransactionManager">
  <property name="connectionFactory" ref="eciConnectionFactory"/>
</bean>

두 트랜잭션 전략 모두 스프링의 어떤 트랜잭션 경계 기능(선언적이거나 프로그래밍적인)과도 사용할 수 있다. 이는 실제 실행 전략과 트랜잭션 경계를 디커플링한 스프링의 제너릭 PlatformTransactionManager 추상화 덕이다. 그래서 트랜잭션 경계는 그대로 둔 채 JtaTransactionManager와 CciLocalTransactionManager를 필요한대로 바꿔 쓸 수 있다.
스프링의 트랜잭션 기능에 대한 자세한 내용은 Chapter 11, 트랜잭션 관리장을 참고해라.