Apache Karaf 트랜잭션 가이드

Red Hat Fuse 7.11

Apache Karaf 컨테이너에 대한 트랜잭션 애플리케이션 작성

Red Hat Fuse Documentation Team

초록

Fuse에 대한 트랜잭션 인식 애플리케이션 개발

머리말

이 가이드에서는 Fuse 트랜잭션 애플리케이션을 구현하기 위한 정보와 지침을 제공합니다. 정보는 다음과 같이 구성됩니다.

보다 포괄적 수용을 위한 오픈 소스 용어 교체

Red Hat은 코드, 문서, 웹 속성에서 문제가 있는 용어를 교체하기 위해 최선을 다하고 있습니다. 먼저 마스터(master), 슬레이브(slave), 블랙리스트(blacklist), 화이트리스트(whitelist) 등 네 가지 용어를 교체하고 있습니다. 이러한 변경 작업은 작업 범위가 크므로 향후 여러 릴리스에 걸쳐 점차 구현할 예정입니다. 자세한 내용은 CTO Chris Wright의 메시지에서 참조하십시오.

1장. 트랜잭션 소개

이 장에서는 일부 기본 트랜잭션 개념과 트랜잭션 관리자에서 중요한 서비스 특성을 논의하여 트랜잭션을 도입합니다. 정보는 다음과 같이 구성됩니다.

1.1. Transaction이란 무엇입니까?

트랜잭션의 프로토타입은 개념적으로 단일 단계(예: 계정 A에서 계정 B로의 양도)로 구성되지만 일련의 단계로 구현되어야 합니다. 이러한 작업은 시스템 실패에 취약합니다. 실패가 완료되지 않은 일부 단계를 떠나 시스템을 일관되지 않은 상태로 둘 수 있기 때문입니다. 예를 들어, 계정 A에서 계정 B로의 양도 작업을 고려하십시오. 계정 A를 인출 한 후 시스템이 실패하지만 계정 B를 계산하기 전에는 시스템이 실패합니다. 그 결과는 일부 비용이 사라집니다.

이와 같은 작업이 안정적인지 확인하려면 이를 트랜잭션 으로 구현하십시오. 트랜잭션은 원자, 일관되고, 격리되고, 세련되기 때문에 안정적인 실행을 보장합니다. 이러한 속성을 트랜잭션의 ACID 속성이라고 합니다.

1.2. 트랜잭션의 ACID 속성

트랜잭션의 ACID 속성은 다음과 같이 정의됩니다.

  • Atomic-a 트랜잭션은 전체 또는 아무 절차입니다. 개별 업데이트가 어셈블되고 트랜잭션이 완료될 때 커밋되거나 취소(롤백)됩니다.
  • 일관된트랜잭션은 하나의 일관된 상태에서 다른 일관된 상태로 시스템을 사용하는 작업 단위입니다.
  • 격리된- 트랜잭션이 실행되는 동안 부분 결과는 다른 엔티티에서 숨겨집니다.
  • Cryostat - 트랜잭션이 커밋된 후 즉시 시스템이 실패하더라도 트랜잭션 결과가 지속됩니다.

1.3. 트랜잭션 클라이언트 정보

트랜잭션 클라이언트는 트랜잭션 을 시작하고 종료할 수 있는 API 또는 오브젝트입니다. 일반적으로 트랜잭션 클라이언트는 트랜잭션을 시작,커밋 또는 롤백 하는 작업을 노출합니다.

표준 Cryostat 애플리케이션에서 javax. Cryostat.UserTransaction 인터페이스는 트랜잭션 클라이언트 API를 노출합니다. Spring Framework, Spring Boot의 컨텍스트에서 org.springframework. Cryostat.PlatformTransactionManager 인터페이스는 트랜잭션 클라이언트 API를 노출합니다.

1.4. 트랜잭션 용어 설명

다음 표에서는 몇 가지 중요한 트랜잭션 용어를 정의합니다.

용어설명

demarcation

트랜잭션 분리는 트랜잭션 시작 및 종료를 나타냅니다. 트랜잭션 종료는 트랜잭션에서 수행한 작업이 커밋되거나 롤백되었음을 의미합니다. 예를 들어 트랜잭션 클라이언트 API를 호출하여 명시적이거나 트랜잭션 끝점에서 메시지를 폴링할 때마다 암시적일 수 있습니다. 자세한 내용은 9장. 트랜잭션을 사용하는 Camel 애플리케이션 작성의 내용을 참조하십시오.

Resources

리소스는 영구적인 또는 영구적으로 변경할 수 있는 컴퓨터 시스템의 모든 구성 요소입니다. 실제로 리소스는 거의 항상 데이터베이스 또는 데이터베이스에 계층화된 서비스(예: 지속성을 갖는 메시지 서비스)입니다. 그러나 다른 종류의 리소스는 실현할 수 있습니다. 예를 들어 ATM(Automated Teller Machine)은 일종의 리소스입니다. 고객이 물리적으로 기계에서 인출을 수락한 후에는 거래를 되돌릴 수 없습니다.

트랜잭션 관리자

트랜잭션 관리자는 하나 이상의 리소스에서 트랜잭션을 조정합니다. 대부분의 경우 트랜잭션 관리자가 리소스에 빌드됩니다. 예를 들어 엔터프라이즈 수준 데이터베이스에는 일반적으로 해당 데이터베이스의 콘텐츠를 변경하는 트랜잭션을 관리할 수 있는 트랜잭션 관리자가 포함되어 있습니다. 두 개 이상의 리소스를 포함하는 트랜잭션에는 일반적으로 외부 트랜잭션 관리자가 필요합니다.

트랜잭션 컨텍스트

트랜잭션 컨텍스트 는 트랜잭션을 추적하는 데 필요한 정보를 캡슐화하는 개체입니다. 트랜잭션 컨텍스트 형식은 전적으로 관련 트랜잭션 관리자 구현에 따라 달라집니다. 최소한 트랜잭션 컨텍스트에는 고유한 트랜잭션 식별자가 포함됩니다.At a minimum, the transaction context contains a unique transaction identifier.

분산 트랜잭션

분산 트랜잭션은 트랜잭션 범위가 여러 네트워크 노드에 걸쳐 있는 분산 시스템의 트랜잭션을 나타냅니다. 분산 트랜잭션을 지원하기 위한 기본 전제 조건은 정식 형식으로 트랜잭션 컨텍스트 전송을 지원하는 네트워크 프로토콜입니다. 분산 트랜잭션은 Apache Camel 트랜잭션 범위를 벗어납니다. 3.2.3절. “분산 트랜잭션 관리자 정보” 도 참조하십시오.

X/Open XA 표준

X/Open XA 표준은 리소스를 트랜잭션 관리자와 통합하기 위한 인터페이스를 설명합니다. 두 개 이상의 리소스가 포함된 트랜잭션을 관리하려면 참여하는 리소스는 XA 표준을 지원해야 합니다. XA 표준을 지원하는 리소스는 트랜잭션 관리자(또는 트랜잭션 처리 모니터)가 리소스의 트랜잭션을 제어할 수 있도록 하는 특수 오브젝트인 XA 스위치 를 노출합니다. XA 표준은 1단계 커밋 프로토콜과 2단계 커밋 프로토콜을 모두 지원합니다.

1.5. 여러 리소스를 수정하는 트랜잭션 관리

단일 리소스와 관련된 트랜잭션의 경우 일반적으로 리소스에 빌드된 트랜잭션 관리자를 사용할 수 있습니다. 여러 리소스가 포함된 트랜잭션의 경우 외부 트랜잭션 관리자 또는 트랜잭션 처리(TP) 모니터를 사용해야 합니다. 이 경우 리소스를 XA 스위치를 등록하여 트랜잭션 관리자와 통합해야 합니다.

단일 리소스 시스템에서 작동하는 트랜잭션을 커밋하는 데 사용되는 프로토콜과 다중 리소스 시스템에서 작동하는 트랜잭션을 커밋하는 데 사용되는 프로토콜에는 중요한 차이점이 있습니다.

  • 1단계 커밋- 단일 리소스 시스템용입니다. 이 프로토콜은 트랜잭션을 단일 단계로 커밋합니다.
  • 2단계 커밋- 다중 리소스 시스템용입니다. 이 프로토콜은 두 단계로 트랜잭션을 커밋합니다.

트랜잭션에 여러 리소스를 포함하면 일부 리소스에서 트랜잭션을 커밋한 후 시스템 오류가 발생할 수 있는 위험이 추가됩니다. 이로 인해 시스템이 일관성 없는 상태로 유지됩니다. 2단계 커밋 프로토콜은 이러한 위험을 제거하도록 설계되었습니다. 시스템을 재시작한 후 항상 일관된 상태로 복원할 수 있습니다.

1.6. 트랜잭션 및 스레드 간 관계

트랜잭션 처리를 이해하려면 트랜잭션과 스레드 간의 기본 관계를 이해하는 것이 중요합니다: 트랜잭션이 스레드에 따라 다릅니다. 즉, 트랜잭션이 시작되면 특정 스레드에 연결됩니다. (기술적으로 트랜잭션 컨텍스트 오브젝트가 생성되고 현재 스레드와 연결됩니다). 트랜잭션이 종료될 때까지 스레드의 모든 활동이 이 트랜잭션 범위 내에서 수행됩니다.From this point until the transaction ends, all of the activity in the thread occurs within this transaction scope. 다른 스레드의 Activity는 이 트랜잭션의 범위 내에 속하지 않습니다. 그러나 다른 스레드의 활성은 다른 트랜잭션의 범위 내에 있을 수 있습니다.

이러한 트랜잭션과 스레드 간의 관계는 다음을 의미합니다.

  • 애플리케이션은 각 트랜잭션이 별도의 스레드에서 생성되는 한 동시에 여러 트랜잭션을 처리할 수 있습니다.
  • 트랜잭션 내에 하위 스레드를 생성해야 합니다. 트랜잭션 중간에 있고 threads() Camel DSL 명령을 호출하여 새 스레드 풀을 생성하는 경우 새 스레드는 원래 트랜잭션의 범위에 있지 않습니다.
  • 이전 시점에서 지정된 것과 동일한 이유로 새 스레드를 암시적으로 생성하는 처리 단계를 주의하십시오.
  • 트랜잭션 범위는 일반적으로 라우팅 세그먼트 간에 확장되지 않습니다. 즉, 하나의 경로 세그먼트가 to(JoinEndpoint) 로 끝나고 다른 경로 세그먼트가 (JoinEndpoint)에서 시작하는 경우 이러한 경로 세그먼트는 일반적으로 동일한 트랜잭션에 속하지 않습니다. 그러나 예외가 있습니다.
참고

일부 고급 트랜잭션 관리자 구현에서는 트랜잭션 컨텍스트를 원하는 대로 스레드에 분리하고 연결할 수 있습니다. 예를 들어 한 스레드에서 다른 스레드로 트랜잭션 컨텍스트를 이동할 수 있습니다.For example, this makes it possible to move a transaction context from one thread to another thread. 경우에 따라 단일 트랜잭션 컨텍스트를 여러 스레드에 연결할 수도 있습니다.

1.7. 거래 서비스 특성 정보

거래 시스템을 구현하는 제품을 선택할 때 다양한 데이터베이스 제품과 거래 관리자, 무료 및 일부 상용이 있습니다. 모두 트랜잭션 처리에 대한 일반 지원이 있지만 이러한 제품에 의해 지원되는 서비스의 특성에는 상당한 차이가 있습니다. 이 섹션에서는 서로 다른 트랜잭션 제품의 안정성과 세분화를 비교할 때 고려해야 할 기능에 대한 간략한 가이드를 제공합니다.

1.7.1. 리소스에서 제공하는 서비스의 특성

다음 기능에 따라 리소스의 서비스 품질이 결정됩니다.

1.7.1.1. 트랜잭션 격리 수준

ANSI SQL은 다음과 같이 네 가지 트랜잭션 격리 수준을 정의합니다.

직렬화 가능
트랜잭션이 서로 완벽하게 격리됩니다. 즉, 트랜잭션이 커밋될 때까지 하나의 트랜잭션이 다른 트랜잭션에 영향을 미칠 수 없습니다. 이 격리 수준은 모든 트랜잭션이 다른 트랜잭션 후 실행되는 것처럼 이므로 직렬화 가능 으로 설명됩니다(실제로 리소스는 일부 트랜잭션이 동시에 진행할 수 있도록 종종 알고리즘을 최적화할 수 있음).
REPEATABLE_READ
트랜잭션이 데이터베이스를 읽거나 업데이트할 때마다 트랜잭션이 종료될 때까지 읽기 또는 쓰기 잠금을 확보하고 유지됩니다. 이는 거의 완벽한 격리를 제공합니다. 그러나 격리가 완전하지 않은 한 가지 사례가 있습니다. a Cryostat 절을 사용하여 행 범위를 읽는 SQL SELECT 문을 고려하십시오. 다른 트랜잭션이 첫 번째 트랜잭션이 실행 중인 동안 이 범위에 행을 추가하는 경우 첫 번째 트랜잭션에서 새 행을 볼 수 있습니다.If another transaction adds a row to this range while the first transaction is running, the first transaction can see this new row, if it repeats the SELECT call (a phantom read).
READ_COMMITTED
쓰기 잠금은 트랜잭션이 종료될 때까지 유지됩니다. 읽기 잠금은 트랜잭션이 종료될 때까지 유지되지 않습니다. 결과적으로 다른 트랜잭션에서 커밋한 업데이트가 진행중인 트랜잭션에 표시되므로 반복된 읽기가 다른 결과를 제공할 수 있습니다.Consequently, repeated reads can give different results because updates committed by other transactions become visible to an ongoing transaction.
READ_UNCOMMITTED
읽기 잠금 또는 쓰기 잠금은 트랜잭션이 종료될 때까지 유지되지 않습니다. 따라서 더티 읽기가 가능합니다. 더티 준비는 다른 트랜잭션에 의해 커밋되지 않은 변경 사항이 진행 중인 트랜잭션에 표시되는 경우입니다.

일반적으로 데이터베이스는 다양한 트랜잭션 격리 수준을 모두 지원하지 않습니다. 예를 들어 일부 무료 데이터베이스는 READ_UNCOMMITTED 만 지원합니다. 또한 일부 데이터베이스는 ANSI 표준과 하위 다른 방식으로 트랜잭션 격리 수준을 구현합니다. 격리는 데이터베이스 성능과의 거래 중단과 관련된 복잡한 문제입니다(예: Wiki 의 격리를 참조하십시오).

1.7.1.2. XA 표준에 대한 지원

리소스가 여러 리소스가 포함된 트랜잭션에 참여하려면 X/Open XA 표준을 지원해야 합니다. 리소스의 XA 표준 구현에 특수 제한 사항이 적용되는지 확인하십시오. 예를 들어 XA 표준의 일부 구현은 단일 데이터베이스 연결로 제한되므로 한 번에 하나의 스레드만 해당 리소스를 포함하는 트랜잭션을 처리할 수 있습니다.

1.7.2. 트랜잭션 관리자가 제공하는 서비스의 특성

다음 기능은 트랜잭션 관리자의 서비스 품질을 결정합니다.

1.7.2.1. 일시 중단/재사용 및 연결/수집 지원

일부 트랜잭션 관리자는 다음과 같이 트랜잭션 컨텍스트와 애플리케이션 스레드 간의 연결을 조작하기 위한 고급 기능을 지원합니다.

  • 애플리케이션이 현재 스레드에서 일부 비-전달 작업을 수행하는 동안 현재 트랜잭션 컨텍스트를 일시적으로 일시 중단할 수 있습니다.Sust/resume current transaction-enables you to suspend temporarily the current transaction context, while the application does some non- Cryostatal work in the current thread.
  • attach/detach 트랜잭션 컨텍스트 - 한 스레드에서 다른 스레드로 트랜잭션 컨텍스트를 이동하거나 여러 스레드를 포함하도록 트랜잭션 범위를 확장할 수 있습니다.Aach/detach transaction context -enables you to move a transaction context from one thread to another or to extend a transaction scope to include multiple threads.

1.7.2.2. 여러 리소스 지원

트랜잭션 관리자의 주요 차이점은 여러 리소스를 지원하는 기능입니다. 이는 일반적으로 XA 표준을 지원하며 트랜잭션 관리자는 XA 스위치를 등록하는 리소스에 대한 방법을 제공합니다.

참고

엄격하게 말하면 XA 표준은 여러 리소스를 지원하는 데 사용할 수있는 유일한 접근 방식은 아니지만 가장 실용적인 방법입니다. 대안은 일반적으로 XA 스위치에서 일반적으로 제공하는 알고리즘을 구현하기 위해 번거 (및 중요한) 사용자 정의 코드를 작성하는 것과 관련이 있습니다.

1.7.2.3. 분산 트랜잭션

일부 트랜잭션 관리자는 범위에 분산 시스템에 여러 노드가 포함된 트랜잭션을 관리하는 기능이 있습니다. 트랜잭션 컨텍스트는 WS-AtomicTransactions 또는 CORBA OTS와 같은 특수 프로토콜을 사용하여 노드에서 노드로 전파됩니다.

1.7.2.4. 트랜잭션 모니터링

고급 트랜잭션 관리자는 일반적으로 보류 중인 트랜잭션의 상태를 모니터링하는 시각적 도구를 제공합니다. 이러한 종류의 도구는 시스템 오류 후 특히 유용합니다. 이 경우 불명확한 상태에 남아 있는 트랜잭션을 식별하고 해결하는 데 도움이 될 수 있습니다(심각적인 예외).

1.7.2.5. 실패에서 복구

시스템 장애 (crash) 발생시 트랜잭션 관리자 사이에는 강력한 차이점이 있습니다. 트랜잭션 관리자가 사용하는 주요 전략은 트랜잭션의 각 단계를 수행하기 전에 영구 로그에 데이터를 쓰는 것입니다. 오류가 발생하는 경우 로그의 데이터를 사용하여 트랜잭션을 복구할 수 있습니다. 일부 거래 관리자는이 전략을 다른 전략보다 더 신중하게 구현합니다. 예를 들어 고급 트랜잭션 관리자는 일반적으로 영구 트랜잭션 로그를 복제하고 각 로그를 별도의 호스트 시스템에 저장할 수 있습니다.

2장. Karaf (OSGi)에서 트랜잭션 시작하기

이 섹션에서는 트랜잭션을 사용하여 Artemis JMS 브로커에 액세스하는 Camel 애플리케이션을 설명합니다. 정보는 다음과 같이 구성됩니다.

2.1. 사전 요구 사항

이 Camel 애플리케이션의 구현에는 다음과 같은 사전 요구 사항이 있습니다.

  • 외부 AMQ 7 JMS 메시지 브로커가 실행 중이어야 합니다.

    다음 샘플 코드는 amq-broker-7.1.0-bin.zip 의 독립 실행형 (Docker이 아닌) 버전을 실행합니다. 실행은 amq7 인스턴스를 생성하고 실행합니다.

    $ pwd
    /data/servers/amq-broker-7.1.0
    
    $ bin/artemis create --user admin --password admin --require-login amq7
    Creating ActiveMQ Artemis instance at: /data/servers/amq-broker-7.1.0/amq7
    
    Auto tuning journal ...
    done! Your system can make 27.78 writes per millisecond, your journal-buffer-timeout will be 36000
    
    You can now start the broker by executing:
    
       "/data/servers/amq-broker-7.1.0/amq7/bin/artemis" run
    
    Or you can run the broker in the background using:
    
       "/data/servers/amq-broker-7.1.0/amq7/bin/artemis-service" start
    
    $ amq7/bin/artemis run
               __  __  ____    ____            _
         /\   |  \/  |/ __ \  |  _ \          | |
        /  \  | \  / | |  | | | |_) |_ __ ___ | | _____ _ __
       / /\ \ | |\/| | |  | | |  _ <| '__/ _ \| |/ / _ \ '__|
      / ____ \| |  | | |__| | | |_) | | | (_) |   <  __/ |
     /_/    \_\_|  |_|\___\_\ |____/|_|  \___/|_|\_\___|_|
    
     Red Hat JBoss AMQ 7.1.0.GA
    
    
    018-05-02 16:37:19,294 INFO  [org.apache.activemq.artemis.integration.bootstrap] AMQ101000: Starting ActiveMQ Artemis Server
    ...
  • 클라이언트 라이브러리가 필요합니다. Artemis 라이브러리는 Maven Central 또는 Red Hat 리포지토리에서 사용할 수 있습니다. 예를 들어 다음을 사용할 수 있습니다.

    • mvn:org.apache.activemq/artemis-core-client/2.4.0.amq-710008-redhat-1
    • mvn:org.apache.activemq/artemis-jms-client/2.4.0.amq-710008-redhat-1

    또는 Artemis/AMQ 7 클라이언트 라이브러리는 Karaf 기능으로 설치할 수 있습니다. 예를 들면 다음과 같습니다.

    • Karaf@root()> feature:install artemis-jms-client artemis-core-client
  • Karaf 쉘 명령 또는 전용 Artemis 지원을 제공하는 일부 지원 기능이 필요합니다.

    karaf@root()> feature:install jms pax-jms-artemis pax-jms-config
  • 필수 Camel 기능은 다음과 같습니다.

    karaf@root()> feature:install camel-jms camel-blueprint

2.2. camel-jms 프로젝트 빌드

Fuse Software Downloads 페이지에서 빠른 시작을 다운로드할 수 있습니다.

zip 파일의 내용을 로컬 폴더(예: quickstarts 라는 새 폴더 )로 추출합니다.

그런 다음 OSGi 번들로 /camel/camel-jms 예제를 빌드하고 설치할 수 있습니다. 이 번들에는 AMQ 7 JMS 큐로 메시지를 전송하는 Camel 경로에 대한 블루프린트 XML 정의가 포함되어 있습니다.

다음 예에서 $FUSE_HOME 은 압축 해제된 Fuse 배포의 위치입니다. 이 프로젝트를 빌드하려면 다음을 수행합니다.

  1. Maven을 호출하여 프로젝트를 빌드합니다.

    $ cd quickstarts
    
    $ mvn clean install -f camel/camel-jms/
  2. javax.jms.ConnectionFactory 서비스가 OSGi 런타임에 게시되도록 JMS 연결 팩토리 구성을 생성합니다. 이렇게 하려면 quickstarts/camel/camel-jms/src/main/resources/etc/org.ops4j.connectionfactory-amq7.cfg$FUSE_HOME/etc 디렉터리에 복사합니다. 이 구성은 작업 연결 팩토리를 생성하기 위해 처리됩니다. 예를 들면 다음과 같습니다.

    $ cp camel/camel-jms/src/main/resources/etc/org.ops4j.connectionfactory-amq7.cfg ../etc/
  3. 게시된 연결 팩토리를 확인합니다.

    karaf@root()> service:list javax.jms.ConnectionFactory
    [javax.jms.ConnectionFactory]
    -----------------------------
     felix.fileinstall.filename = file:$FUSE_HOME/etc/org.ops4j.connectionfactory-amq7.cfg
     name = artemis
     osgi.jndi.service.name = artemis
     password = admin
     pax.jms.managed = true
     service.bundleid = 251
     service.factoryPid = org.ops4j.connectionfactory
     service.id = 436
     service.pid = org.ops4j.connectionfactory.d6207fcc-3fe6-4dc1-a0d8-0e76ba3b89bf
     service.scope = singleton
     type = artemis
     url = tcp://localhost:61616
     user = admin
    Provided by :
     OPS4J Pax JMS Config (251)
    
    karaf@root()> jms:info -u admin -p admin artemis
    Property │ Value
    ─────────┼──────────────────────────
    product  │ ActiveMQ
    version  │ 2.4.0.amq-711002-redhat-1
    
    karaf@root()> jms:queues -u admin -p admin artemis
    JMS Queues
    ────────────────────────────────────
    df2501d1-aa52-4439-b9e4-c0840c568df1
    DLQ
    ExpiryQueue
  4. 번들을 설치합니다.

    karaf@root()> install -s mvn:org.jboss.fuse.quickstarts/camel-jms/7.0.0.redhat-SNAPSHOT
    Bundle ID: 256
  5. 작동하는지 확인합니다.

    karaf@root()> camel:context-list
     Context               Status              Total #       Failed #     Inflight #   Uptime
     -------               ------              -------       --------     ----------   ------
     jms-example-context   Started                   0              0              0   2 minutes
    karaf@root()> camel:route-list
     Context               Route               Status              Total #       Failed #     Inflight #   Uptime
     -------               -----               ------              -------       --------     ----------   ------
     jms-example-context   file-to-jms-route   Started                   0              0              0   2 minutes
     jms-example-context   jms-cbr-route       Started                   0              0              0   2 minutes
  6. Camel 경로가 시작된 즉시 Fuse 설치에 디렉토리, work/jms/input 가 표시됩니다. 이 빠른 시작의 src/main/data 디렉토리에서 찾은 파일을 새로 생성된 work/jms/input 디렉터리에 복사합니다.
  7. 잠시 기다렸다가 국가별로 구성된 동일한 파일이 work/jms/output 디렉토리 아래에 있습니다.

    • work/jms/output/othersorder1.xml,order2.xmlorder4.xml
    • work/jms/output/usorder3.xmlorder5.xml
    • work/jms/output/frorder6.xml
  8. 비즈니스 로깅을 확인하려면 로그를 참조하십시오.

    2018-05-02 17:20:47,952 | INFO  | ile://work/jms/input | file-to-jms-route                | 58 - org.apache.camel.camel-core - 2.21.0.fuse-000077 | Receiving order order1.xml
    2018-05-02 17:20:48,052 | INFO  | umer[incomingOrders] | jms-cbr-route                    | 58 - org.apache.camel.camel-core - 2.21.0.fuse-000077 | Sending order order1.xml to another country
    2018-05-02 17:20:48,053 | INFO  | umer[incomingOrders] | jms-cbr-route                    | 58 - org.apache.camel.camel-core - 2.21.0.fuse-000077 | Done processing order1.xml
  9. 큐가 동적으로 생성되었는지 확인합니다.

    karaf@root()> jms:queues -u admin -p admin artemis
    JMS Queues
    ────────────────────────────────────
    DLQ
    17767323-937f-4bad-a403-07cd63311f4e
    ExpiryQueue
    incomingOrders
  10. Camel 경로 통계를 확인합니다.

    karaf@root()> camel:route-info jms-example-context file-to-jms-route
    Camel Route file-to-jms-route
    	Camel Context: jms-example-context
    	State: Started
    	State: Started
    
    
    Statistics
    	Exchanges Total: 1
    	Exchanges Completed: 1
    	Exchanges Failed: 0
    	Exchanges Inflight: 0
    	Min Processing Time: 67 ms
    	Max Processing Time: 67 ms
    	Mean Processing Time: 67 ms
    	Total Processing Time: 67 ms
    	Last Processing Time: 67 ms
    	Delta Processing Time: 67 ms
    	Start Statistics Date: 2018-05-02 17:14:17
    	Reset Statistics Date: 2018-05-02 17:14:17
    	First Exchange Date: 2018-05-02 17:20:48
    	Last Exchange Date: 2018-05-02 17:20:48

2.3. camel-jms 프로젝트에 대한 설명

Camel 경로는 다음 끝점 URI를 사용합니다.

<route id="file-to-jms-route">
...
    <to uri="jms:queue:incomingOrders?transacted=true" />
</route>

<route id="jms-cbr-route">
    <from uri="jms:queue:incomingOrders?transacted=true" />
...
</route>

jms 구성 요소는 다음 스니펫을 사용하여 구성됩니다.

<bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
    <property name="connectionFactory">
        <reference interface="javax.jms.ConnectionFactory" />
    </property>
    <property name="transactionManager" ref="transactionManager"/>
</bean>

transactionManager 참조는 다음과 같습니다.

<reference id="transactionManager" interface="org.springframework.transaction.PlatformTransactionManager" />

위 화면과 같이, PlatformTransactionManager 의 JMS 연결 팩토리와 Spring 인터페이스 모두 참조일 뿐입니다. 블루프린트 XML에서 정의할 필요가 없습니다. 이러한 서비스는 Fuse 자체에 의해 노출됩니다.

javax.jms.ConnectionFactoryetc/org.ops4j.connectionfactory-amq7.cfg 를 사용하여 생성된 것을 이미 확인했습니다.

트랜잭션 관리자는 다음과 같습니다.

karaf@root()> service:list org.springframework.transaction.PlatformTransactionManager
[org.springframework.transaction.PlatformTransactionManager]
------------------------------------------------------------
 service.bundleid = 21
 service.id = 527
 service.scope = singleton
Provided by :
 Red Hat Fuse :: Fuse Modules :: Transaction (21)
Used by:
 Red Hat Fuse :: Quickstarts :: camel-jms (256)

실제 트랜잭션 관리자가 등록된 다른 인터페이스를 확인합니다.

karaf@root()> headers 21

Red Hat Fuse :: Fuse Modules :: Transaction (21)
------------------------------------------------
...
Bundle-Name = Red Hat Fuse :: Fuse Modules :: Transaction
Bundle-SymbolicName = fuse-pax-transx-tm-narayana
Bundle-Vendor = Red Hat
...

karaf@root()> bundle:services -p 21

Red Hat Fuse :: Fuse Modules :: Transaction (21) provides:
----------------------------------------------------------
objectClass = [org.osgi.service.cm.ManagedService]
service.bundleid = 21
service.id = 519
service.pid = org.ops4j.pax.transx.tm.narayana
service.scope = singleton
 ----
objectClass = [javax.transaction.TransactionManager]
provider = narayana
service.bundleid = 21
service.id = 520
service.scope = singleton
 ----
objectClass = [javax.transaction.TransactionSynchronizationRegistry]
provider = narayana
service.bundleid = 21
service.id = 523
service.scope = singleton
 ----
objectClass = [javax.transaction.UserTransaction]
provider = narayana
service.bundleid = 21
service.id = 524
service.scope = singleton
 ----
objectClass = [org.jboss.narayana.osgi.jta.ObjStoreBrowserService]
provider = narayana
service.bundleid = 21
service.id = 525
service.scope = singleton
 ----
objectClass = [org.ops4j.pax.transx.tm.TransactionManager]
provider = narayana
service.bundleid = 21
service.id = 526
service.scope = singleton
 ----
objectClass = [org.springframework.transaction.PlatformTransactionManager]
service.bundleid = 21
service.id = 527
service.scope = singleton

트랜잭션 관리자는 다음 인터페이스에서 사용할 수 있습니다.

  • javax.transaction.TransactionManager
  • javax.transaction.TransactionSynchronizationRegistry
  • javax.transaction.UserTransaction
  • org.jboss.narayana.osgi.jta.ObjStoreBrowserService
  • org.ops4j.pax.transx.tm.TransactionManager
  • org.springframework.transaction.PlatformTransactionManager

필요한 모든 컨텍스트에서 사용할 수 있습니다. 예를 들어 camel-jms 를 사용하려면 org.apache.camel.component.jms.JmsConfiguration. CryostatManager 필드를 초기화해야 합니다. 이 예제에서는 다음을 사용합니다.

<reference id="transactionManager" interface="org.springframework.transaction.PlatformTransactionManager" />

대신 예를 들면 다음과 같습니다.

<reference id="transactionManager" interface="javax.transaction.TransactionManager" />

3장. 트랜잭션 관리자를 구성하고 참조하는 인터페이스

Cryostat 및 Spring Boot는 각각 Fuse에서 트랜잭션 관리자를 구성하고 배포된 애플리케이션에서 트랜잭션 관리자를 사용하기 위한 트랜잭션 클라이언트 인터페이스를 제공합니다. 관리 작업인 구성과 개발 작업인 구성을 명확하게 구분할 수 있습니다. 애플리케이션 개발자는 이전에 구성된 트랜잭션 관리자를 대상으로 애플리케이션을 가리킵니다.

3.1. 트랜잭션 관리자의 역할

트랜잭션 관리자는 하나 이상의 리소스에서 트랜잭션을 조정하는 데 책임이 있는 애플리케이션의 일부입니다. 거래 관리자의 책임은 다음과 같습니다.

  • 분리 - start, commit, rollback 메서드를 사용하여 트랜잭션을 시작하고 종료합니다.
  • 트랜잭션 컨텍스트 관리 - 트랜잭션 컨텍스트에는 트랜잭션 관리자가 트랜잭션을 추적하는 데 필요한 정보가 포함됩니다.Managing the transaction context - a transaction context contains the information that a transaction manager needs to keep track of a transaction. 트랜잭션 관리자는 트랜잭션 컨텍스트를 생성하고 현재 스레드에 연결합니다.
  • 여러 리소스에 걸쳐 트랜잭션 조정 - 엔터프라이즈 수준 트랜잭션 관리자는 일반적으로 여러 리소스 간에 트랜잭션을 조정하는 기능을 갖습니다. 이 기능을 사용하려면 2단계 커밋 프로토콜이 필요하며 XA 프로토콜을 사용하여 리소스를 등록하고 관리해야 합니다. 1.7.1.2절. “XA 표준에 대한 지원”을 참조하십시오.

    이는 모든 트랜잭션 관리자가 지원하지 않는 고급 기능입니다.

  • 실패에서 복구 - 트랜잭션 관리자는 시스템 오류가 발생하고 애플리케이션이 실패하는 경우 리소스가 일관되지 않은 상태로 유지되지 않도록 할 책임이 있습니다. 경우에 따라 시스템을 일관된 상태로 복원하려면 수동 개입이 필요할 수 있습니다.

3.2. 로컬, 글로벌 및 분산 트랜잭션 관리자 정보

트랜잭션 관리자는 로컬, 글로벌 또는 배포될 수 있습니다.

3.2.1. 로컬 트랜잭션 관리자 정보

로컬 트랜잭션 관리자는 단일 리소스에 대해서만 트랜잭션을 조정할 수 있는 트랜잭션 관리자입니다. 로컬 트랜잭션 관리자의 구현은 일반적으로 리소스 자체에 포함되어 있으며 애플리케이션에서 사용하는 트랜잭션 관리자는 이 기본 제공 트랜잭션 관리자에 대한 씬 래퍼입니다.

예를 들어 Oracle 데이터베이스에는 중복 작업(SQL BEGIN,COMMIT 또는 ROLLBACK 문을 사용하거나 네이티브 Oracle API를 사용하여) 및 다양한 트랜잭션 격리 수준을 지원하는 기본 제공 트랜잭션 관리자가 있습니다. Oracle 트랜잭션 관리자에 대한 제어는 JDBC를 통해 내보낼 수 있으며, 이 JDBC API는 애플리케이션에서 트랜잭션을 위임하는 데 사용됩니다.

이 컨텍스트에서 리소스를 구성하는 사항을 이해하는 것이 중요합니다. 예를 들어 JMS 제품을 사용하는 경우 JMS 리소스는 개별 대기열과 주제가 아닌 JMS 제품의 단일 실행 인스턴스입니다. 또한 동일한 기본 리소스에 다른 방식으로 액세스하면 여러 리소스가 실제로 단일 리소스일 수 있습니다. 예를 들어 애플리케이션은 관계형 데이터베이스에 직접(DBC를 통해) 직접 및 간접적으로( Hibernate와 같은 객체 관계형 매핑 툴을 통해)에 액세스할 수 있습니다. 이 경우 동일한 기본 트랜잭션 관리자가 관련되므로 두 코드 조각을 동일한 트랜잭션에 등록할 수 있어야 합니다.

참고

이 방법이 모든 경우에 효과적이라는 것을 보장할 수는 없습니다. 기본적으로 가능한 경우도 있지만 Spring Framework 또는 기타 래퍼 계층의 설계에서 일부 세부 사항으로 인해 실제로 작동하지 않을 수 있습니다.

애플리케이션에는 서로 독립적으로 작동하는 다양한 로컬 트랜잭션 관리자가 있을 수 있습니다. 예를 들어 JMS 끝점이 JMS 트랜잭션 관리자를 참조하는 JMS 대기열 및 주제를 조작하는 Camel 경로가 한 개 있을 수 있습니다. 다른 경로는 JDBC를 통해 관계형 데이터베이스에 액세스할 수 있습니다. 그러나 JDBC 및 JMS 액세스를 동일한 경로에 결합하고 둘 다 동일한 트랜잭션에 참여하도록 할 수 없습니다.

3.2.2. 글로벌 트랜잭션 관리자 정보

글로벌 트랜잭션 관리자는 여러 리소스에 대해 트랜잭션을 조정할 수 있는 트랜잭션 관리자입니다. 이는 리소스 자체에 빌드된 트랜잭션 관리자를 사용할 수 없는 경우 필요합니다. 트랜잭션 처리 모니터(TP 모니터)라고도 하는 외부 시스템은 다양한 리소스 간에 트랜잭션을 조정할 수 있습니다.

다음은 여러 리소스에서 작동하는 트랜잭션에 대한 사전 요구 사항입니다.

  • 글로벌 트랜잭션 관리자 또는 TP 모니터 - 여러 XA 리소스를 조정하기 위해 2단계 커밋 프로토콜을 구현하는 외부 트랜잭션 시스템입니다.
  • XA 표준을 지원하는 리소스 - 2단계 커밋에 참여하려면 리소스는 XA 표준을 지원해야 합니다. 1.7.1.2절. “XA 표준에 대한 지원”을 참조하십시오. 실제로 이는 리소스가 XA 스위치 오브젝트를 내보낼 수 있으므로 외부 TP 모니터에 대한 트랜잭션을 완전히 제어할 수 있습니다.
작은 정보

Spring Framework는 글로벌 트랜잭션을 관리하기 위한 TP 모니터를 자체적으로 제공하지 않습니다. 그러나 OSGi 제공 TP 모니터 또는 Cryostat 제공 TP 모니터( JtaTransactionManager 클래스에서 통합이 구현되는 경우)와의 통합을 지원합니다. 따라서 전체 트랜잭션 지원이 포함된 OSGi 컨테이너에 애플리케이션을 배포하는 경우 Spring에서 여러 트랜잭션 리소스를 사용할 수 있습니다.

3.2.3. 분산 트랜잭션 관리자 정보

일반적으로 서버는 트랜잭션과 관련된 리소스에 직접 연결합니다. 그러나 분산 시스템에서는 웹 서비스를 통해 간접적으로 노출되는 리소스에만 연결해야 합니다. 이 경우 분산 트랜잭션을 지원할 수 있는 TP 모니터가 필요합니다. 다양한 분산 프로토콜에 대한 트랜잭션을 지원하는 방법을 설명하는 여러 표준을 사용할 수 있습니다(예: 웹 서비스에 대한 WS-AtomicTransactions 사양).

3.3. Cryostat 트랜잭션 클라이언트 사용

Cryostat를 사용하는 경우 트랜잭션 관리자와 상호 작용하는 가장 많은 지원 대상 및 표준 방법은 Java Transaction API (JTA) 인터페이스 javax. Cryostat.UserTransaction 입니다. 표준 사용은 다음과 같습니다.

InitialContext context = new InitialContext();
UserTransaction ut = (UserTransaction) context.lookup("java:comp/UserTransaction");
ut.begin();

// Access transactional, JTA-aware resources such as database and/or message broker

ut.commit(); // or ut.rollback()

JNDI(Java Naming and Directory Interface)에서 UserTransaction 인스턴스를 얻는 것이 트랜잭션 클라이언트를 가져오는 한 가지 방법입니다. Cryostat 환경에서는 CDI(컨텍스트 및 종속성 주입)와 같이 트랜잭션 클라이언트에 액세스할 수 있습니다.

다음 그림은 typica Cryostat Camel 애플리케이션을 보여줍니다.

Cryostat 트랜잭션 API

이 그림은 Camel 코드와 애플리케이션 코드가 모두 액세스할 수 있음을 보여줍니다.

  • Spring TransactionTemplate 클래스를 사용하여 애플리케이션에서 또는 내부적으로 트랜잭션 인식 Camel 구성 요소를 통해 트랜잭션을 직접 위임하는 javax. Cryostat.UserTransaction 인스턴스입니다.
  • JDBC API를 직접 또는 예를 들어 Spring의 JdbcTemplate 을 사용하거나 camel-jdbc 구성 요소를 사용하여 데이터베이스입니다.
  • Spring의 JmsTemplate 클래스를 사용하거나 camel-jms 구성 요소를 사용하여 JMS API를 직접 통한 메시지 브로커입니다.

javax. Cryostat.UserTransaction 오브젝트를 사용하는 경우 트랜잭션 클라이언트에서만 직접 작업하기 때문에 사용 중인 실제 트랜잭션 관리자를 알 필요가 없습니다. ( 1.3절. “트랜잭션 클라이언트 정보” 참조) 내부적으로 Spring의 트랜잭션 기능을 사용하므로 Spring과 Camel이 다른 접근 방식을 취합니다.

JavaEE Application

일반적인 Cryostat 시나리오에서는 애플리케이션이 Cryostat 애플리케이션 서버(일반적으로 WAR 또는 EAR 아카이브)로 배포됩니다. JNDI 또는 CDI를 통해 애플리케이션은 javax. Cryostat.UserTransaction 서비스의 인스턴스에 액세스할 수 있습니다. 그런 다음, 제거에서는 이 트랜잭션 클라이언트 인스턴스를 사용하여 트랜잭션을 분리합니다. 트랜잭션 내에서 애플리케이션은 JDBC 및/또는 JMS 액세스를 수행합니다.

Camel 구성 요소 및 애플리케이션 코드

이는 JMS/JDBC 작업을 수행하는 코드를 나타냅니다. Camel에는 JMS/JDBC 리소스에 액세스하는 자체 고급 방법이 있습니다. 애플리케이션 코드는 지정된 API를 직접 사용할 수 있습니다.

JMS 연결 Cryostat

javax.jms.ConnectionFactory 인터페이스이며 javax.jms.Connection 인스턴스를 가져온 다음 javax.jms.Session (또는 JMS 2.0의 javax.jms.JmsContext )을 가져오는 데 사용됩니다. 이는 애플리케이션에서 직접 사용하거나 내부적으로 org.springframework.jms.core.JmsTemplate 을 사용할 수 있는 Camel 구성 요소에서 간접적으로 사용할 수 있습니다. 애플리케이션 코드나 Camel 구성 요소에는 이 연결 팩토리에 대한 세부 정보가 필요하지 않습니다. 연결 팩토리는 애플리케이션 서버에서 구성됩니다. 이 구성은 Cryostat 서버에서 확인할 수 있습니다. Fuse와 같은 OSGi 서버는 비슷합니다. 시스템 관리자는 애플리케이션과 독립적으로 연결 팩토리를 구성합니다. 일반적으로 연결 팩토리에서는 풀링 기능을 구현합니다.

JDBC 데이터 소스

java.sql.Connection 의 인스턴스를 가져오는 데 사용되는 javax.sql.DataSource 인터페이스입니다. JMS와 마찬가지로 이 데이터 소스는 직접 또는 간접적으로 사용될 수 있습니다. 예를 들어 camel-sql 구성 요소는 내부적으로 org.springframework.jdbc.core.JdbcTemplate 클래스를 사용합니다. JMS와 마찬가지로 애플리케이션 코드나 Camel에는 이 데이터 소스에 대한 세부 정보가 필요하지 않습니다. 구성은 4장. Narayana 트랜잭션 관리자 구성 에 설명된 방법을 사용하여 애플리케이션 서버 또는 OSGi 서버 내부에서 수행됩니다.

3.4. Spring Boot 트랜잭션 클라이언트 사용

Spring Framework(및 Spring Boot)의 주요 목표 중 하나는 Cryostat API를 더 쉽게 사용할 수 있도록 하는 것입니다. 모든 주요 Cryostat vanilla API는 Spring Framework (Spring Boot)에 포함됩니다. 이는 지정된 API의 대체 또는 대체 가 아니라 더 많은 구성 옵션 또는 더 일관된 사용량을 추가하는 래퍼(예: 예외 처리와 관련하여)입니다.

다음 표는 지정된 Cryostat API와 해당 Spring 관련 인터페이스와 일치합니다.

JavaEE APISpring 유틸리티다음으로 구성됨

JDBC

org.springframework.jdbc.core.JdbcTemplate

javax.sql.DataSource

JMS

org.springframework.jms.core.JmsTemplate

javax.jms.ConnectionFactory

JTA

org.springframework.transaction.support.TransactionTemplate

org.springframework.transaction.PlatformTransactionManager

JdbcTemplateJmsTemplate 은 각각 javax.sql.DataSourcejavax.jms.ConnectionFactory 를 직접 사용합니다. 그러나 TransactionTemplatePlatformTransactionManager 의 Spring 인터페이스를 사용합니다. 여기서 Spring은 단순히 Cryostat를 개선하는 것이 아니라 Cryostat 클라이언트 API를 자체적으로 대체합니다.

Spring은 javax. Cryostat.UserTransaction 을 실제 시나리오에 비해 너무 간단한 인터페이스로 처리합니다. 또한 javax. Cryostat.UserTransaction 은 로컬, 단일 리소스 트랜잭션과 글로벌, 다중 리소스 트랜잭션, org.springframework. Cryostat.PlatformTransactionManager 구현을 통해 개발자에게 더 많은 자유를 제공합니다.

다음은 Spring Boot의 정식 API 사용법입니다.

// Create or get from ApplicationContext or injected with @Inject/@Autowired.
JmsTemplate jms = new JmsTemplate(...);
JdbcTemplate jdbc = new JdbcTemplate(...);
TransactionTemplate tx = new TransactionTemplate(...);

tx.execute((status) -> {
    // Perform JMS operations within transaction.
    jms.execute((SessionCallback<Object>)(session) -> {
        // Perform operations on JMS session
        return ...;
    });
    // Perform JDBC operations within transaction.
    jdbc.execute((ConnectionCallback<Object>)(connection) -> {
        // Perform operations on JDBC connection.
        return ...;
    });
    return ...;
});

위의 예에서 세 가지 유형의 템플릿 은 모두 간단히 인스턴스화되지만 Spring의 ApplicationContext 에서 얻거나 @Autowired 주석을 사용하여 삽입될 수도 있습니다.

3.4.1. Spring PlatformTransactionManager 인터페이스 사용

앞서 언급했듯이 javax. Cryostat.UserTransaction 은 일반적으로 Cryostat 애플리케이션의 JNDI에서 가져옵니다. 그러나 Spring은 많은 시나리오에서 이 인터페이스를 명시적으로 구현합니다. 항상 전체 JTA 시나리오가 필요하지는 않으며 애플리케이션에서 단일 리소스(예: JDBC)에 액세스해야 하는 경우가 있습니다.

일반적으로 org.springframework.PlatformTransactionManager 는 클래식 트랜잭션 클라이언트 작업( 시작,커밋롤백 )을 제공하는 Spring 트랜잭션 클라이언트 API입니다. 즉, 이 인터페이스는 런타임 시 트랜잭션을 제어하는 데 필수 메서드를 제공합니다.

참고

트랜잭션 시스템의 다른 주요 측면은 트랜잭션 리소스를 구현하기 위한 API입니다. 그러나 트랜잭션 리소스는 일반적으로 기본 데이터베이스에 의해 구현되므로 트랜잭션 프로그래밍의 이러한 측면은 애플리케이션 구현에 거의 관련이 없습니다.

3.4.1.1. PlatformTransactionManager 인터페이스 정의

public interface PlatformTransactionManager {

    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

    void commit(TransactionStatus status) throws TransactionException;

    void rollback(TransactionStatus status) throws TransactionException;
}

3.4.1.2. TransactionDefinition 인터페이스 정보

TransactionDefinition 인터페이스를 사용하여 새로 생성된 트랜잭션의 특성을 지정합니다. 격리 수준 및 새 트랜잭션의 전파 정책을 지정할 수 있습니다. 자세한 내용은 9.4절. “트랜잭션 전파 정책”의 내용을 참조하십시오.

3.4.1.3. TransactionStatus 인터페이스 정의

TransactionStatus 인터페이스를 사용하여 현재 트랜잭션, 즉 현재 스레드와 연결된 트랜잭션의 상태를 확인하고 롤백을 위해 현재 트랜잭션을 표시할 수 있습니다. 인터페이스 정의입니다.

public interface TransactionStatus extends SavepointManager, Flushable {

    boolean isNewTransaction();

    boolean hasSavepoint();

    void setRollbackOnly();

    boolean isRollbackOnly();

    void flush();

    boolean isCompleted();
}

3.4.1.4. PlatformTransactionManager 인터페이스에서 정의하는 방법

PlatformTransactionManager 인터페이스는 다음 방법을 정의합니다.

getTransaction()
새 트랜잭션을 만들고 새 트랜잭션의 특성을 정의하는 TransactionDefinition 개체를 전달하여 현재 스레드와 연결합니다.Creates a new transaction and associates it with the current thread by passing in a TransactionDefinition object that defines the characteristics of the new transaction. 이는 다른 많은 트랜잭션 클라이언트 API의 begin() 메서드와 유사합니다.
commit()
등록된 리소스에 대한 보류 중인 모든 변경 사항을 영구적으로 만드는 현재 트랜잭션을 커밋합니다.
rollback()
등록된 리소스에 대한 보류 중인 모든 변경 사항을 취소하는 현재 트랜잭션을 롤백합니다.

3.4.2. 트랜잭션 관리자를 사용하는 단계

일반적으로 PlatformTransactionManager 인터페이스를 직접 사용하지 않습니다. Apache Camel에서는 일반적으로 다음과 같이 트랜잭션 관리자를 사용합니다.

  1. 트랜잭션 관리자의 인스턴스를 만듭니다. Spring에는 다양한 구현이 있습니다. 3.4절. “Spring Boot 트랜잭션 클라이언트 사용”을 참조하십시오.
  2. 트랜잭션 관리자 인스턴스를 Apache Camel 구성 요소 또는 경로의 transacted() DSL 명령에 전달합니다. 그런 다음 트랜잭션 구성 요소 또는 transacted() 명령은 트랜잭션 분리를 담당합니다. 자세한 내용은 9장. 트랜잭션을 사용하는 Camel 애플리케이션 작성에서 참조하십시오.

3.4.3. Spring PlatformTransactionManager 구현 정보

이 섹션에서는 Spring Framework에서 제공하는 트랜잭션 관리자 구현에 대한 간략한 개요를 제공합니다. 이러한 구현은 로컬 트랜잭션 관리자와 글로벌 트랜잭션 관리자의 두 가지 범주로 분류됩니다.

Camel부터 시작:

  • camel-jms 구성 요소에서 사용하는 org.apache.camel.component.jms.JmsConfiguration 오브젝트에는 org.springframework. Cryostat.PlatformTransactionManager 인터페이스의 인스턴스가 필요합니다.
  • org.apache.camel.component.sql.SqlComponentorg.springframework.jdbc.core.JdbcTemplate 클래스를 내부적으로 사용하며 이 JDBC 템플릿은 org.springframework. Cryostat.PlatformTransactionManager 와도 통합됩니다.

위 화면과 같이 이 인터페이스의 일부 구현이 있어야 합니다. 시나리오에 따라 필요한 플랫폼 트랜잭션 관리자를 구성할 수 있습니다.

3.4.3.1. 로컬 PlatformTransactionManager 구현

아래 목록에는 Spring Framework에서 제공하는 로컬 트랜잭션 관리자 구현이 요약되어 있습니다. 이러한 트랜잭션 관리자는 단일 리소스만 지원합니다.

org.springframework.jms.connection.JmsTransactionManager
이 트랜잭션 관리자 구현은 단일 JMS 리소스를 관리할 수 있습니다. 여러 큐 또는 주제에 연결할 수 있지만 동일한 기본 JMS 메시징 제품 인스턴스에 속하는 경우에만 사용할 수 있습니다. 또한 다른 유형의 리소스를 트랜잭션에 등록할 수 없습니다.
org.springframework.jdbc.datasource.DataSourceTransactionManager
이 트랜잭션 관리자 구현은 단일 JDBC 데이터베이스 리소스를 관리할 수 있습니다. 여러 다른 데이터베이스 테이블을 업데이트할 수 있지만 동일한 기본 데이터베이스 인스턴스에 속하는 경우에만 업데이트할 수 있습니다.
org.springframework.orm.jpa.JpaTransactionManager
이 트랜잭션 관리자 구현은 Java Persistence API(JPA) 리소스를 관리할 수 있습니다. 그러나 트랜잭션에서 다른 종류의 리소스를 동시에 등록할 수는 없습니다.
org.springframework.orm.hibernate5.HibernateTransactionManager
이러한 트랜잭션 관리자 구현은 Hibernate 리소스를 관리할 수 있습니다. 그러나 트랜잭션에서 다른 종류의 리소스를 동시에 등록할 수는 없습니다. 또한 JPA API는 기본 Hibernate API보다 우선합니다.

또한 덜 자주 사용되는 다른 PlatformTransactionManager 구현도 있습니다.

3.4.3.2. 글로벌 PlatformTransactionManager 구현

Spring Framework는 OSGi 런타임에서 사용할 수 있도록 하나의 글로벌 트랜잭션 관리자 구현을 제공합니다. org.springframework. Cryostat.jta.JtaTransactionManager 는 트랜잭션의 여러 리소스에 대한 작업을 지원합니다. 이 트랜잭션 관리자는 XA 트랜잭션 API를 지원하며 트랜잭션에 두 개 이상의 리소스를 등록할 수 있습니다. 이 트랜잭션 관리자를 사용하려면 OSGi 컨테이너 또는 Cryostat 서버 내에 애플리케이션을 배포해야 합니다.

PlatformTransactionManager 의 단일 리소스 구현은 실제 구현 이지만JtaTransactionManager 는 표준 javax. Cryostat.TransactionManager 의 실제 구현에 대한 래퍼가 더 많습니다.

따라서 이미 구성된 javax. Cryostat.TransactionManager 및 일반적으로 javax. Cryostat. UserTransaction 의 인스턴스에 액세스 할 수 있는 환경에서 PlatformTransactionManagerJtaTransactionManager 구현을 사용하는 것이 좋습니다. 일반적으로 이러한 두 JTA 인터페이스는 단일 오브젝트/서비스로 구현됩니다.

다음은 JtaTransactionManager 구성/사용의 예입니다.

InitialContext context = new InitialContext();
UserTransaction ut = (UserTransaction) context.lookup("java:comp/UserTransaction");
TransactionManager tm = (TransactionManager) context.lookup("java:/TransactionManager");

JtaTransactionManager jta = new JtaTransactionManager();
jta.setUserTransaction(ut);
jta.setTransactionManager(tm);

TransactionTemplate jtaTx = new TransactionTemplate(jta);

jtaTx.execute((status) -> {
    // Perform resource access in the context of global transaction.
    return ...;
});

위의 예에서 JTA 오브젝트(UserTransactionTransactionManager)의 실제 인스턴스는 JNDI에서 가져옵니다. OSGi에서는 OSGi 서비스 레지스트리에서도 가져올 수 있습니다.

3.5. OSGi는 트랜잭션 클라이언트와 트랜잭션 관리자 간의 인터페이스

Cryostat 트랜잭션 클라이언트 API 및 Spring Boot 트랜잭션 클라이언트 API에 대한 설명 후 Fuse와 같은 OSGi 서버 내의 관계를 확인하는 것이 도움이 됩니다. OSGi의 기능 중 하나는 글로벌 서비스 레지스트리이며 다음과 같이 사용할 수 있습니다.

  • 필터 또는 인터페이스를 통해 서비스를 조회합니다.
  • 지정된 인터페이스 및 속성으로 서비스를 등록합니다.

Cryostat 애플리케이션 서버에 배포된 애플리케이션이 JNDI(서비스 Cryostat 메서드)를 사용하여 javax. Cryostat.UserTransaction 에 대한 참조를 얻거나 OSGi에서 CDI(종속적 또는종속 항목 주입 방법)를 사용하여 삽입하는 것과 동일한 방식으로 다음과 같은 방법으로 동일한 참조(직접 또는 간접적으로)를 얻을 수 있습니다.

  • org.osgi.framework.BundleContext.getServiceReference() 메서드 호출(서비스).
  • 블루프린트 컨테이너에 삽입됩니다.
  • SCR(Service Component Runtime) 주석(종속성 주입)을 사용합니다.

다음 그림은 OSGi 런타임에 배포된 Fuse 애플리케이션을 보여줍니다. 애플리케이션 코드 및/또는 Camel 구성 요소는 해당 API를 사용하여 트랜잭션 관리자, 데이터 소스 및 연결 팩토리에 대한 참조를 가져옵니다.

OSGi 트랜잭션 아키텍처

애플리케이션(번들)은 OSGi 레지스트리에 등록된 서비스와 상호 작용합니다. 액세스는 인터페이스를 통해 수행되며 이는 애플리케이션과 관련이 있어야 합니다.

Fuse에서 (직접 또는 작은 래퍼를 통해) 트랜잭션 클라이언트 인터페이스를 구현하는 기본 오브젝트는 org.jboss.narayana.osgi.jta.internal.OsgiTransactionManager 입니다. 다음 인터페이스를 사용하여 트랜잭션 관리자에 액세스할 수 있습니다.

  • javax.transaction.TransactionManager
  • javax.transaction.UserTransaction
  • org.springframework.transaction.PlatformTransactionManager
  • org.ops4j.pax.transx.tm.TransactionManager

이러한 인터페이스를 직접 사용하거나 Camel과 같은 프레임워크 또는 라이브러리를 선택하여 암시적으로 사용할 수 있습니다.

Fuse에서 org.jboss.narayana.osgi.jta.internal.OsgiTransactionManager 를 구성하는 방법에 대한 자세한 내용은 4장. Narayana 트랜잭션 관리자 구성 을 참조하십시오. 이 가이드의 뒷부분에서는 해당 장의 정보를 살펴보고 JDBC 데이터 소스 및 JMS 연결 팩토리와 같은 다른 서비스를 구성하고 사용하는 방법을 설명합니다.

4장. Narayana 트랜잭션 관리자 구성

Fuse에서 내장된 글로벌 트랜잭션 관리자는 JBoss Narayana Transaction Manager 로, EAP(Enterprise Application Platform) 7에서 사용하는 트랜잭션 관리자와 동일합니다.

Fuse for Karaf와 마찬가지로 OSGi 런타임에서는 PAX TRANSX 프로젝트에서 추가 통합 계층을 제공합니다.

다음 항목에서는 Narayana 구성에 대해 설명합니다.

4.1. Narayana 설치 정보

Narayana 트랜잭션 관리자는 다음 인터페이스의 OSGi 번들과 몇 가지 추가 지원 인터페이스에서 사용하기 위해 노출됩니다.

  • javax.transaction.TransactionManager
  • javax.transaction.UserTransaction
  • org.springframework.transaction.PlatformTransactionManager
  • org.ops4j.pax.transx.tm.TransactionManager

7.11.1.fuse-7_11_1-00013-redhat-00003 배포를 통해 이러한 인터페이스를 처음부터 사용할 수 있습니다.

pax-transx-tm-narayana 기능에는 Narayana를 포함하는 재정의된 번들이 포함되어 있습니다.

karaf@root()> feature:info pax-transx-tm-narayana
Feature pax-transx-tm-narayana 0.3.0
Feature has no configuration
Feature has no configuration files
Feature depends on:
  pax-transx-tm-api 0.0.0
Feature contains followed bundles:
  mvn:org.jboss.fuse.modules/fuse-pax-transx-tm-narayana/7.0.0.fuse-000191-redhat-1 (overriden from mvn:org.ops4j.pax.transx/pax-transx-tm-narayana/0.3.0)
Feature has no conditionals.

fuse-pax-transx-tm-narayana 번들에서 제공하는 서비스는 다음과 같습니다.

karaf@root()> bundle:services fuse-pax-transx-tm-narayana

Red Hat Fuse :: Fuse Modules :: Transaction (21) provides:
----------------------------------------------------------
[org.osgi.service.cm.ManagedService]
[javax.transaction.TransactionManager]
[javax.transaction.TransactionSynchronizationRegistry]
[javax.transaction.UserTransaction]
[org.jboss.narayana.osgi.jta.ObjStoreBrowserService]
[org.ops4j.pax.transx.tm.TransactionManager]
[org.springframework.transaction.PlatformTransactionManager]

이 번들은 org.osgi.service.cm.ManagedService 를 등록하므로 CM 구성의 변경 사항을 추적하고 대응합니다.

karaf@root()> bundle:services -p fuse-pax-transx-tm-narayana

Red Hat Fuse :: Fuse Modules :: Transaction (21) provides:
----------------------------------------------------------
objectClass = [org.osgi.service.cm.ManagedService]
service.bundleid = 21
service.id = 232
service.pid = org.ops4j.pax.transx.tm.narayana
service.scope = singleton
...

기본 org.ops4j.pax.transx.tm.narayana PID는 다음과 같습니다.

karaf@root()> config:list '(service.pid=org.ops4j.pax.transx.tm.narayana)'
----------------------------------------------------------------
Pid:            org.ops4j.pax.transx.tm.narayana
BundleLocation: ?
Properties:
   com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.communicationStore.localOSRoot = communicationStore
   com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.communicationStore.objectStoreDir = /data/servers/7.11.1.fuse-7_11_1-00013-redhat-00003/data/narayana
   com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.communicationStore.objectStoreType = com.arjuna.ats.internal.arjuna.objectstore.ShadowNoFileLockStore
   com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.localOSRoot = defaultStore
   com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.objectStoreDir = /data/servers/7.11.1.fuse-7_11_1-00013-redhat-00003/data/narayana
   com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.objectStoreType = com.arjuna.ats.internal.arjuna.objectstore.ShadowNoFileLockStore
   com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.stateStore.localOSRoot = stateStore
   com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.stateStore.objectStoreDir = /data/servers/7.11.1.fuse-7_11_1-00013-redhat-00003/data/narayana
   com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.stateStore.objectStoreType = com.arjuna.ats.internal.arjuna.objectstore.ShadowNoFileLockStore
   com.arjuna.ats.arjuna.common.RecoveryEnvironmentBean.recoveryBackoffPeriod = 10
   felix.fileinstall.filename = file:/data/servers/7.11.1.fuse-7_11_1-00013-redhat-00003/etc/org.ops4j.pax.transx.tm.narayana.cfg
   service.pid = org.ops4j.pax.transx.tm.narayana

요약:

  • Fuse for Karaf에는 완전한 기능을 갖춘 글로벌, 네라나나 트랜잭션 관리자가 포함되어 있습니다.
  • 트랜잭션 관리자는 다양한 클라이언트 인터페이스(JTA, Spring-tx, PAX JMS)에서 올바르게 노출됩니다.
  • org.ops4j.pax.transx.tm.narayana 에서 사용할 수 있는 표준 OSGi 메서드인 Configuration Admin을 사용하여 Narayana를 구성할 수 있습니다.
  • 기본 구성은 $FUSE_HOME/etc/org.ops4j.pax.transx.tm.narayana.cfg 에 제공됩니다.

4.2. 지원되는 트랜잭션 프로토콜

Narayana 트랜잭션 관리자는 EAP에서 사용되는 JBoss/Red Hat 제품입니다. Narayana는 광범위한 표준 기반 트랜잭션 프로토콜을 사용하여 개발된 애플리케이션을 지원하는 트랜잭션 툴킷입니다.

  • JTA
  • JTS
  • Web-Service 트랜잭션
  • REST 트랜잭션
  • STM
  • XATMI/TX

4.3. narayana 구성 정보

pax-transx-tm-narayana 번들에는 트랜잭션 관리자의 다양한 측면에 대한 기본 구성을 제공하는 jbossts-properties.xml 파일이 포함되어 있습니다. 이러한 속성은 모두 $FUSE_HOME/etc/org.ops4j.pax.transx.tm.narayana.cfg 파일에서 직접 또는 Configuration Admin API를 사용하여 재정의될 수 있습니다.

narayana의 기본 구성은 다양한 EnvironmentBean 오브젝트를 통해 수행됩니다. 이러한 모든 Cryostat는 접두사가 다른 속성을 사용하여 구성할 수 있습니다. 다음 표에서는 사용되는 구성 오브젝트 및 접두사에 대한 요약을 제공합니다.

구성 Cryostat속성 접두사

com.arjuna.ats.arjuna.common.CoordinatorEnvironmentBean

com.arjuna.ats.arjuna.coordinator

com.arjuna.ats.arjuna.common.CoreEnvironmentBean

com.arjuna.ats.arjuna

com.arjuna.ats.internal.arjuna.objectstore.hornetq.HornetqJournalEnvironmentBean

com.arjuna.ats.arjuna.hornetqjournal

com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean

com.arjuna.ats.arjuna.objectstore

com.arjuna.ats.arjuna.common.RecoveryEnvironmentBean

com.arjuna.ats.arjuna.recovery

com.arjuna.ats.jdbc.common.JDBCEnvironmentBean

com.arjuna.ats.jdbc

com.arjuna.ats.jta.common.JTAEnvironmentBean

com.arjuna.ats.jta

com.arjuna.ats.txoj.common.TxojEnvironmentBean

com.arjuna.ats.txoj.lockstore

접두사 는 구성을 단순화할 수 있습니다. 그러나 일반적으로 다음 형식 중 하나를 사용해야 합니다.

NameEnvironmentBean.propertyName (기본 형식) 또는

fully-qualified-class-name.field-name

예를 들어 com.arjuna.ats.arjuna.common.CoordinatorEnvironmentBean.commitOnePhase 필드를 고려하십시오. com.arjuna.ats.arjuna.common.CoordinatorEnvironment.commitOnePhase 속성을 사용하거나 더 간단한 (preferred) 양식 CoordinatorEnvironmentBean.commitOnePhase 속성을 사용하여 구성할 수 있습니다. 속성을 설정하는 방법과 구성할 수 있는 빈에 대한 자세한 내용은 Narayana 제품 문서에서 확인할 수 있습니다.

ObjectStoreEnvironmentBean 과 같은 일부 빈은 다른 용도로 구성을 제공하는 각 명명된 인스턴스에서 여러 번 구성할 수 있습니다. 이 경우 인스턴스 이름은 접두사(위의 항목 모두)와 필드 이름 사이에 사용됩니다. 예를 들어 이름이 communicationStoreObjectStoreEnvironmentBean 인스턴스의 오브젝트 저장소 유형은 이름이 지정된 속성을 사용하여 구성할 수 있습니다.

  • com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.communicationStore.objectStoreType
  • ObjectStoreEnvironmentBean.communicationStore.objectStoreType

4.4. 로그 스토리지 구성

가장 중요한 구성은 오브젝트 로그 스토리지의 유형 및 위치입니다. 일반적으로 com.arjuna.ats.arjuna.objectstore.ObjectStoreAPI 인터페이스의 세 가지 구현이 있습니다.

com.arjuna.ats.internal.arjuna.objectstore.hornetq.HornetqObjectStoreAdaptor
AMQ 7의 org.apache.activemq.artemis.core.journal 스토리지를 내부적으로 사용합니다.
com.arjuna.ats.internal.arjuna.objectstore.jdbc.JDBCStore
JDBC를 사용하여 TX 로그 파일을 유지합니다.
com.arjuna.ats.internal.arjuna.objectstore.FileSystemStore (및 특수 구현)
사용자 정의 파일 기반 로그 스토리지를 사용합니다.

기본적으로 Fuse는 FileSystemStore 의 특수 구현인 com.arjuna.ats.internal.arjuna.objectstore.ShadowNoFileLockStore 를 사용합니다.

Narayana가 트랜잭션/오브젝트 로그를 보관하는 데 사용되는 세 개의 저장소가 있습니다.

  • defaultStore
  • communicationStore
  • stateStore

자세한 내용은 State Management in Narayana 설명서 를 참조하십시오.

이 세 저장소 의 기본 구성은 다음과 같습니다.

# default store
com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.objectStoreType = com.arjuna.ats.internal.arjuna.objectstore.ShadowNoFileLockStore
com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.objectStoreDir = ${karaf.data}/narayana
com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.localOSRoot = defaultStore
# communication store
com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.communicationStore.objectStoreType = com.arjuna.ats.internal.arjuna.objectstore.ShadowNoFileLockStore
com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.communicationStore.objectStoreDir = ${karaf.data}/narayana
com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.communicationStore.localOSRoot = communicationStore
# state store
com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.stateStore.objectStoreType = com.arjuna.ats.internal.arjuna.objectstore.ShadowNoFileLockStore
com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.stateStore.objectStoreDir = ${karaf.data}/narayana
com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.stateStore.localOSRoot = stateStore

CryostatNoFileLockStore 는 기본 디렉터리(objectStoreDir) 및 특정 저장소의 디렉터리(localOSRoot)로 구성됩니다.

많은 설정 옵션은 나레이나 설명서 가이드에 포함되어 있습니다. 그러나 Narayana 설명서는 구성 옵션에 대한 표준 참조가 다양한 EnvironmentBean 클래스의 Javadoc임을 나타냅니다.

5장. Narayana 트랜잭션 관리자 사용

이 섹션에서는 javax. Cryostat. UserTransaction 인터페이스, org.springframework. Cryostat.PlatformTransactionManager 인터페이스 또는 javax. Cryostat.Transaction 인터페이스를 구현하여 Narayana 트랜잭션 관리자를 사용하는 데 대한 세부 정보를 제공합니다. 사용할 인터페이스는 애플리케이션 요구 사항에 따라 다릅니다. 이 장의 끝에는 XA 리소스 등록의 문제 해결에 대한 논의가 있습니다. 정보는 다음과 같이 구성됩니다.

Java 트랜잭션 API 세부 정보는 JTA(Java Transaction API) 1.2 사양 및 Javadoc 를 참조하십시오.

5.1. UserTransaction 오브젝트 사용

트랜잭션 혼동을 위한 javax. Cryostat.UserTransaction 인터페이스를 구현합니다. 즉, 트랜잭션을 시작, 커밋 또는 롤백합니다. 애플리케이션 코드에서 직접 사용할 가능성이 가장 높은 JTA 인터페이스입니다. 그러나 UserTransaction 인터페이스는 트랜잭션을 위임하는 방법 중 하나일 뿐입니다. 트랜잭션을 분류할 수 있는 다양한 방법에 대한 설명은 9장. 트랜잭션을 사용하는 Camel 애플리케이션 작성 에서 참조하십시오.

5.1.1. UserTransaction 인터페이스 정의

JTA UserTransaction 인터페이스는 다음과 같이 정의됩니다.

public interface javax.transaction.UserTransaction {

    public void begin();

    public void commit();

    public void rollback();

    public void setRollbackOnly();

    public int getStatus();

    public void setTransactionTimeout(int seconds);
}

5.1.2. UserTransaction 메서드에 대한 설명

UserTransaction 인터페이스는 다음 방법을 정의합니다.

begin()
새 트랜잭션을 시작하고 현재 스레드와 연결합니다.Starts a new transaction and associates it with the current thread. XA 리소스가 이 트랜잭션과 연결된 경우 트랜잭션은 암시적으로 XA 트랜잭션이 됩니다.
commit()

보류 중인 모든 변경 사항이 영구적으로 되도록 현재 트랜잭션을 정상적으로 완료합니다. 커밋 후 현재 스레드와 연결된 트랜잭션이 더 이상 없습니다.

참고

그러나 현재 트랜잭션이 롤백으로만 표시되면 commit() 이 호출될 때 트랜잭션이 실제로 롤백됩니다.

rollback()
보류 중인 모든 변경 사항이 삭제되도록 즉시 트랜잭션을 중지합니다. 롤백 후에는 더 이상 현재 스레드와 연결된 트랜잭션이 없습니다.
setRollbackOnly()
롤백이 유일한 결과이지만 아직 롤백을 수행하지 않도록 현재 트랜잭션의 상태를 수정합니다.
getStatus()

javax. Cryostat.Status 인터페이스에 정의된 대로 다음 정수 값 중 하나일 수 있는 현재 트랜잭션의 상태를 반환합니다.

  • STATUS_ACTIVE
  • STATUS_COMMITTED
  • STATUS_COMMITTING
  • STATUS_MARKED_ROLLBACK
  • STATUS_NO_TRANSACTION
  • STATUS_PREPARED
  • STATUS_PREPARING
  • STATUS_ROLLEDBACK
  • STATUS_ROLLING_BACK
  • STATUS_UNKNOWN
setTransactionTimeout()
초 단위로 지정된 현재 트랜잭션의 시간 초과를 사용자 지정합니다. 지정된 타임아웃 내에서 트랜잭션이 해결되지 않으면 트랜잭션 관리자가 자동으로 롤백합니다.

5.2. TransactionManager 오브젝트 사용

javax. Cryostat.TransactionManager 오브젝트를 사용하는 가장 일반적인 방법은 프레임워크 API(예: Camel JMS 구성 요소)에 전달하는 것입니다. 이를 통해 프레임워크에서 트랜잭션 혼동을 확인할 수 있습니다. 경우에 따라 TransactionManager 개체를 직접 사용하려는 경우가 있습니다. 이 기능은 suspend()resume() 메서드와 같은 고급 트랜잭션 API에 액세스해야 하는 경우에 유용합니다.

5.2.1. TransactionManager 인터페이스 정의

JTA TransactionManager 인터페이스에는 다음과 같은 정의가 있습니다.

interface javax.transaction.TransactionManager {

    // Same as UserTransaction methods

    public void begin();

    public void commit();

    public void rollback();

    public void setRollbackOnly();

    public int getStatus();

    public void setTransactionTimeout(int seconds);

    // Extra TransactionManager methods

    public Transaction getTransaction();

    public Transaction suspend() ;

    public void resume(Transaction tobj);
}

5.2.2. TransactionManager 메서드 설명

TransactionManager 인터페이스는 UserTransaction 인터페이스에 있는 모든 메서드를 지원합니다. TransactionManager 오브젝트를 트랜잭션 분리에 사용할 수 있습니다. 또한 TransactionManager 오브젝트는 다음 메서드를 지원합니다.

getTransaction()
현재 스레드와 연결된 트랜잭션인 현재 트랜잭션에 대한 참조를 가져옵니다.Gets a reference to the current transaction, which is the transaction that is associated with the current thread. 현재 트랜잭션이 없는 경우 이 메서드는 null 을 반환합니다.
suspend()

현재 스레드에서 현재 트랜잭션을 분리하고 트랜잭션에 대한 참조를 반환합니다.Detaches the current transaction from the current thread and returns a reference to the transaction. 이 메서드를 호출한 후 현재 스레드에 더 이상 트랜잭션 컨텍스트가 없습니다.After calling this method, the current thread no longer has a transaction context. 이 시점 이후에 수행하는 작업은 더 이상 트랜잭션 컨텍스트에서 수행되지 않습니다.

참고

모든 트랜잭션 관리자가 트랜잭션 일시 중지를 지원하지는 않습니다. 그러나 이 기능은 나레이나에서 지원합니다.

resume()
일시 중지된 트랜잭션을 현재 스레드 컨텍스트에 다시 연결합니다. 이 메서드를 호출한 후 트랜잭션 컨텍스트가 복원되고 이 시점 이후의 모든 작업이 트랜잭션 컨텍스트에서 수행됩니다.After calling this method, the transaction context is restored and any work that you do after this point is done in the context of a transaction.

5.3. 트랜잭션 오브젝트 사용

트랜잭션을 일시 중지/사용하거나 리소스를 명시적으로 추가해야 하는 경우 javax. Cryostat.Transaction 오브젝트를 직접 사용해야 할 수 있습니다. 5.4절. “XA 등록 문제 해결” 에서 설명한 대로 프레임워크 또는 컨테이너는 일반적으로 리소스 등록에 자동으로 처리됩니다.

5.3.1. 트랜잭션 인터페이스 정의

JTA 트랜잭션 인터페이스에는 다음과 같은 정의가 있습니다.

interface javax.transaction.Transaction {

    public void commit();

    public void rollback();

    public void setRollbackOnly();

    public int getStatus();

    public boolean enlistResource(XAResource xaRes);

    public boolean delistResource(XAResource xaRes, int flag);

    public void registerSynchronization(Synchronization sync);
}

5.3.2. 트랜잭션 메서드 설명

commit(), rollback(), setRollbackOnly()getStatus() 메서드는 UserTransaction 인터페이스에서 해당 메서드와 동일한 동작을 갖습니다. 실제로 UserTransaction 오브젝트는 현재 트랜잭션을 검색한 다음 트랜잭션 오브젝트에서 해당 메서드를 호출하는 편리한 래퍼입니다.

또한 트랜잭션 인터페이스는 UserTransaction 인터페이스에 해당하지 않는 다음 메서드를 정의합니다.

enlistResource()

XA 리소스를 현재 트랜잭션에 연결합니다.

참고

이 방법은 XA 트랜잭션 컨텍스트에서 중요합니다. XA 트랜잭션을 특성화하는 현재 트랜잭션과 함께 여러 XA 리소스를 등록할 수 있는 기능을 정확하게 설명합니다. 반면, 명시적으로 리소스를 등록하는 것은 간과이며 일반적으로 프레임워크 또는 컨테이너가 이를 수행할 것으로 예상합니다. 예를 들면 5.4절. “XA 등록 문제 해결” 을 참조하십시오.

delistResource()

지정된 리소스를 트랜잭션에서 연결 해제합니다. 플래그 인수는 javax. Cryostat.Transaction 인터페이스에 정의된 다음 정수 값 중 하나를 사용할 수 있습니다.

  • TMSUCCESS
  • TMFAIL
  • TMSUSPEND
registerSynchronization()
javax. Cryostat.Synchronization 개체를 현재 트랜잭션에 등록합니다. Synchronization 오브젝트는 커밋 준비 단계 직전에 콜백을 수신하고 트랜잭션이 완료된 직후 콜백을 수신합니다.

5.4. XA 등록 문제 해결

XA 리소스를 등록하기 위한 표준 JTA 접근 방식은 현재 트랜잭션을 나타내는 현재 javax. Cryostat.Transaction 오브젝트에 XA 리소스를 명시적으로 추가하는 것입니다. 즉, 새 트랜잭션이 시작될 때마다 XA 리소스를 명시적으로 추가해야 합니다.

5.4.1. XA 리소스에 가입하는 방법

트랜잭션을 사용하여 XA 리소스를 등록하려면 트랜잭션 인터페이스에서 enlistResource() 메서드를 호출해야 합니다. 예를 들어 TransactionManager 개체와 XAResource 개체가 지정되면 다음과 같이 XAResource 개체를 등록할 수 있습니다.For example, given a TransactionManager object and an XAResource object, you could enlist the XAResource object as follows:

// Java
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import javax.transaction.xa.XAResource;
...
// Given:
// 'tm' of type TransactionManager
// 'xaResource' of type XAResource

// Start the transaction
tm.begin();

Transaction transaction = tm.getTransaction();
transaction.enlistResource(xaResource);

// Do some work...
...

// End the transaction
tm.commit();

리소스 등록의 까다로운 측면은 새 트랜잭션에 리소스를 등록해야 하며 리소스를 사용하기 전에 리소스를 등록해야 한다는 것입니다. 리소스를 명시적으로 추가하는 경우 enlistResource() 호출을 사용하여 리터링된 오류-prone 코드로 끝날 수 있습니다. 또한 적절한 위치에서 enlistResource() 를 호출하기 어려울 수 있습니다. 예를 들어 일부 트랜잭션 세부 정보를 숨기는 프레임워크를 사용하는 경우입니다.

5.4.2. 자동 목록 정보

XA 리소스를 명시적으로 등록하지 않고 XA 리소스의 자동 목록을 지원하는 기능을 사용하는 것이 더 쉽고 안전합니다. 예를 들어 JMS 및 JDBC 리소스 사용 컨텍스트에서 표준 기술은 자동 목록을 지원하는 래퍼 클래스를 사용하는 것입니다.

JDBC 및 JMS 액세스 모두에 대한 일반적인 패턴은 다음과 같습니다.

  1. 애플리케이션 코드는 JDBC 액세스에는 javax.sql.DataSource 와 JMS의 경우 javax.jms.ConnectionFactory 가 JDBC 또는 JMS 연결을 가져와야 합니다.
  2. 애플리케이션/OSGi 서버 내에서 이러한 인터페이스의 데이터베이스 또는 브로커 특정 구현이 등록됩니다.
  3. 애플리케이션/OSGi 서버는 데이터베이스/broker별 팩토리를 일반, 풀링, 인 목록에 포함된 팩토리로 래핑 합니다.

이러한 방식으로 애플리케이션 코드는 여전히 javax.sql.DataSourcejavax.jms.ConnectionFactory 를 사용하지만 내부적으로 액세스하면 일반적으로 다음과 같은 추가 기능이 있습니다.

  • 연결 풀링 - 매번 데이터베이스/메시지 브로커에 대한 새 연결을 생성하는 대신 사전 초기화된 연결 풀이 사용됩니다. 풀링 의 또 다른 측면은 예를 들어 연결의 주기적인 검증일 수 있습니다.
  • JTA 등록 - java.sql.Connection (JDBC) 또는 javax.jms.Connection (JMS)의 인스턴스를 반환하기 전에 실제 연결 개체가 실제 XA 리소스인 경우 등록됩니다. JTA 트랜잭션에서 등록이 가능한 경우 등록이 수행됩니다.

자동 목록을 사용하면 애플리케이션 코드를 변경할 필요가 없습니다.

JDBC 데이터 소스 및 JMS 연결 팩토리에 대한 래퍼 풀링 및 등록에 대한 자세한 내용은 6장. JDBC 데이터 소스 사용7장. JMS 연결 팩토리 사용 를 참조하십시오.

6장. JDBC 데이터 소스 사용

다음 주제에서는 Fuse OSGi 런타임의 JDBC 데이터 소스 사용에 대해 설명합니다.

6.1. 연결 인터페이스 정보

데이터 조작을 수행하는 데 사용되는 가장 중요한 오브젝트java.sql.Connection 인터페이스 구현입니다. Fuse 구성 관점에서 Connection 개체를 가져오는 방법을 알아야 합니다.

관련 오브젝트가 포함된 라이브러리는 다음과 같습니다.

  • PostgreSQL: mvn:org.postgresql/postgresql/42.2.5
  • MySQL: mvn:mysql/mysql-connector-java/5.1.34

기존 구현( 드라이버 JAR에 포함)은다음을 제공합니다.

  • PostgreSQL: org.postgresql.jdbc.PgConnection
  • MySQL: com.mysql.jdbc.JDBC4Connection ( com.mysql.jdbc.Driver의 다양한 connect*() 메서드 참조)

이러한 구현에는 DML, DDL 및 간단한 트랜잭션 관리를 수행하는 데이터베이스별 논리가 포함됩니다.

이론적으로는 이러한 연결 오브젝트를 수동으로 생성할 수 있지만 더 깔끔한 API를 제공하기 위해 세부 정보를 숨기는 두 가지 JDBC 방법이 있습니다.

  • java.sql.Driver.connect() - 이 방법은 오래 전에 독립 실행형 애플리케이션에서 사용되었습니다.
  • javax.sql.DataSource.getConnection() - 팩토리 패턴을 사용하는 데 권장되는 방법입니다. 유사한 방법은 JMS 연결 팩토리에서 JMS 연결을 가져오는 데 사용됩니다.

드라이버 관리자 접근 방식은 여기에서 논의되지 않습니다. 이 메서드는 지정된 연결 개체에 대한 일반 생성자보다 작은 계층 임을 명시하는 것으로 충분합니다.

데이터베이스별 통신 프로토콜을 효과적으로 구현하는 java.sql.Connection 외에도 다른 두 가지 특수 연결 인터페이스가 있습니다.

  • javax.sql.PooledConnection 은 물리적 연결을 나타냅니다. 코드가 이 풀링된 연결과 직접 상호 작용하지 않습니다. 대신 getConnection() 메서드에서 얻은 연결이 사용됩니다. 이 방법을 사용하면 애플리케이션 서버 수준에서 연결 풀을 관리할 수 있습니다. getConnection() 을 사용하여 얻은 연결은 일반적으로 프록시입니다. 이러한 프록시 연결이 닫히면 물리적 연결이 닫히지 않고 관리되는 연결 풀에서 다시 사용할 수 있게 됩니다.
  • javax.sql.XAConnection 을 사용하면 javax. Cryostat. TransactionManager와 함께 사용하기 위해 XA 인식 연결과 연결된 javax. Cryostat.xa. XAResource 개체를 가져올 수 있습니다. javax.sql.XAConnectionjavax.sql.PooledConnection을 확장하므로 일반적인 DML/DQL 메서드를 사용하여 JDBC 연결 오브젝트에 대한 액세스를 제공하는 'getConnection() 메서드도 제공합니다.

6.2. JDBC 데이터 소스 개요

JDBC 1.4 표준에는 java.sql.Connection 개체의 팩토리 역할을 하는 javax.sql.DataSource 인터페이스가 도입되었습니다. 일반적으로 이러한 데이터 소스는 JNDI 레지스트리에 바인딩되어 있고 서블릿 또는 Clevis와 같은 Java EE 구성 요소에 있거나 삽입되었습니다. 주요 측면은 이러한 데이터 소스가 애플리케이션 서버 내에 구성되고 배포된 애플리케이션에서 이름별로 참조 된다는 것입니다.

다음 연결 오브젝트에는 자체 데이터 소스가 있습니다.

데이터 소스연결

javax.sql.DataSource

java.sql.Connection

javax.sql.ConnectionPoolDataSource

javax.sql.PooledConnection

javax.sql.XADataSource

javax.sql.XAConnection

위의 각 데이터 소스 의 가장 중요한 차이점은 다음과 같습니다.

  • javax.sql.DataSourcejava.sql.Connection 인스턴스를 가져오기 위한 것과 같은 팩토리입니다. 대부분의 javax.sql.DataSource 구현이 일반적으로 연결 풀링 을 수행한다는 사실은 이미지를 변경하지 않아야 합니다. 애플리케이션 코드에서 사용해야 하는 유일한 인터페이스입니다. 다음 중 어느 것을 구현했는지는 중요하지 않습니다.

    • 직접 JDBC 액세스
    • JPA 지속성 유닛 구성(< jta-data-source > 또는 < non-jta-data-source> )
    • Apache Camel 또는 Spring Framework와 같은 널리 사용되는 라이브러리
  • javax.sql.ConnectionPoolDataSource 는 가장 중요한 것은 일반(비 데이터베이스별) 연결 풀/데이터 소스와 데이터베이스별 데이터 소스 간의 브릿지 입니다. SPI 인터페이스로 취급될 수 있습니다. 애플리케이션 코드는 일반적으로 JNDI에서 가져와 애플리케이션 서버에서 구현한 일반적인 javax.sql.DataSource 개체를 처리합니다( commons-dbcp2와 같은 라이브러리를 사용). 애플리케이션 코드는 javax.sql.ConnectionPoolDataSource 와 직접 상호 작용하지 않습니다. 애플리케이션 서버와 데이터베이스별 드라이버 간에 사용됩니다. 다음 시퀀스 다이어그램에서는 다음을 보여줍니다.

    diag 7f73f899365cf56b8aa906191213fa81
  • javax.sql.XADataSourcejavax.sql.XAConnectionjavax. Cryostat.xa.XAResource 를 얻는 방법입니다. javax.sql.ConnectionPoolDataSource 와 동일하며 애플리케이션 서버와 데이터베이스별 드라이버 간에 사용됩니다. 다음은 JTA 트랜잭션 관리자를 포함하여 다른 행위자와 함께 약간 수정된 다이어그램입니다.

    diag ebfce784cac352c984238fe0a82ca4fa

위의 두 다이어그램에 표시된 대로 javax.sql.DataSourcejavax. Cryostat.UserTransaction 인스턴스를 구성할 수 있는 일반화된 엔터티인 App Server 와 상호 작용합니다. 이러한 인스턴스는 JNDI를 통해 또는 CDI 또는 다른 종속성 메커니즘을 사용하여 주입하여 액세스할 수 있습니다.

중요

중요한 점은 애플리케이션이 XA 트랜잭션 및/또는 연결 풀링을 사용하는 경우에도 애플리케이션은 다른 두 JDBC 데이터 소스 인터페이스가 아닌 javax.sql.DataSource 와 상호 작용한다는 것입니다.

6.2.1. 데이터베이스 특정 및 일반 데이터 소스

JDBC 데이터 소스 구현은 다음 두 가지 범주로 분류됩니다.

  • 다음과 같은 일반적인 javax.sql.DataSource 구현

  • javax.sql.DataSource,javax.sql.XADataSourcejavax.sql.ConnectionPoolDataSource의 데이터베이스 특정 구현

일반적인 javax.sql.DataSource 구현이 자체적으로 데이터베이스별 연결을 만들 수 없다는 혼동을 줄 수 있습니다. 일반 데이터 소스에서 java.sql.Driver.connect() 또는 java.sql.DriverManager.getConnection() 을 사용할 수 있는 경우에도 일반적으로 데이터베이스별 javax.sql.DataSource 구현을 사용하여 이 일반 데이터 소스를 구성하는 것이 좋습니다.

일반 데이터 소스가 JTA와 상호 작용하려면 javax.sql.XADataSource 의 데이터베이스별 구현으로 구성해야 합니다.

작업을 종료하기 위해 일반적인 데이터 소스에 는 일반적으로 연결 풀링을 수행하기 위해 javax.sql.ConnectionPoolDataSource 의 데이터베이스별 구현이 필요하지 않습니다. 기존 풀은 일반적으로 표준 JDBC 인터페이스(javax.sql.ConnectionPoolDataSourcejavax.sql.PooledConnection) 없이 풀링을 처리하고 대신 자체 사용자 지정 구현을 사용합니다.

6.2.2. 일부 일반 데이터 소스

잘 알려진 일반적인 데이터 소스인 Apache Commons DBCP(2) 샘플을 고려해 보십시오.

javax.sql.XADataSource 구현

DBCP2에는 예상되는 javax.sql.XADataSource 의 구현이 포함되어 있지 않습니다.

javax.sql.ConnectionPoolDataSource implementations

DBCP2 에는 javax.sql.ConnectionPoolDataSource:org.apache.commons.dbcp2.cpdsadapter.DriverAdapterCPDS 의 구현이 포함됩니다. java.sql.DriverManager.getConnection() 을 호출하여 javax.sql.PooledConnection 개체를 생성합니다. 이 풀은 직접 사용해서는 안되며 다음과 같은 드라이버의 어댑터 로 취급해야 합니다.

  • 자체 javax.sql.ConnectionPoolDataSource 구현을 제공하지 마십시오.
  • 연결 풀에 대한 JDBC 권장 사항에 따라 사용하려고 합니다.

위의 시퀀스 다이어그램에 표시된 대로 드라이버는 javax.sql.ConnectionPoolDataSource 를 직접 또는 org.apache.commons.dbcp2.cpds adapter.DriverAdapterCPDS 어댑터의 도움을 받아 직접 또는 DBCP2는 다음과 같은 애플리케이션 서버 계약을 구현합니다.

  • org.apache.commons.dbcp2.datasources.PerUserPoolDataSource
  • org.apache.commons.dbcp2.datasources.SharedPoolDataSource

두 풀 모두 구성 단계에서 javax.sql.ConnectionPoolDataSource 의 인스턴스를 사용합니다.

DBCP2에서 가장 중요하고 흥미로운 부분입니다.

javax.sql.DataSource 구현

연결 풀링 기능을 구현하려면 JDBC 권장 사항을 따라 javax.sql.ConnectionPoolDataSourcejavax.sql.PooledConnection SPI를 사용할 필요가 없습니다.

다음은 DBCP2의 일반 데이터 소스 목록입니다.

  • org.apache.commons.dbcp2.BasicDataSource
  • org.apache.commons.dbcp2.managed.BasicManagedDataSource
  • org.apache.commons.dbcp2.PoolingDataSource
  • org.apache.commons.dbcp2.managed.ManagedDataSource

여기에는 두 개의 축이 있습니다.

기본풀링

풀링 구성 측면을 결정합니다.

두 종류의 데이터 소스 모두 java.sql.Connection 개체의 풀링 을 수행합니다. 유일한 차이점은 다음과 같습니다.

  • 기본 데이터 소스는 org.apache.commons.pool2.impl.GenericObjectPool 의 내부 인스턴스를 구성하는 데 사용되는 maxTotal 또는 minIdle 과 같은 8080 속성을 사용하여 구성됩니다.
  • 풀링 데이터 소스는 외부에서 생성/구성된 org.apache.commons.pool2.ObjectPool 로 구성됩니다.

Managed vs non-managed

연결 생성 측면과 JTA 동작을 결정합니다.

  • 관리되지 않는 기본 데이터 소스는 내부적으로 java.sql.Driver.connect() 를 사용하여 java.sql.Connection 인스턴스를 생성합니다.

    관리되지 않는 풀링 데이터 소스는 전달된 org.apache.commons.pool2.ObjectPool 개체를 사용하여 java.sql.Connection 인스턴스를 생성합니다.

  • 관리되는 풀링 데이터 소스는 org.apache.commons.dbcp2.managed.ManagedConnection 개체 내에서 java.sql.Connection 인스턴스를 래핑하여 JTA 컨텍스트에서 필요한 경우 javax. Cryostat.Transaction.enlistResource() 가 호출되도록 합니다. 그러나 여전히 래핑된 실제 연결은 풀이 구성된 모든 org.apache.commons.pool2.ObjectPool 개체에서 가져옵니다.

    관리형 기본 데이터 소스를 사용하면 전용 org.apache.commons.pool2.ObjectPool 을 구성할 수 없습니다. 대신 기존의 실제 데이터베이스별 javax.sql.XADataSource 개체를 구성하는 것으로 충분합니다. Cryostat 속성은 org.apache.commons.pool2.impl.GenericObjectPool 의 내부 인스턴스를 생성하는 데 사용되며, 이 인스턴스는 관리되는 풀링 데이터 소스(org.apache.commons.dbcp2.managed.ManagedDataSource)의 내부 인스턴스에 전달됩니다.

참고

DBCP2에서 할 수 없는 유일한 것은 XA 트랜잭션 복구 입니다. DBCP2는 활성 JTA 트랜잭션에 XAResources를 올바르게 등록하지만 복구를 수행하지 않습니다. 이 작업은 별도로 수행해야 하며 구성은 일반적으로 선택한 트랜잭션 관리자 구현(예: 네레이나 )에 따라 다릅니다.

6.2.3. 사용할 패턴

권장 패턴은 다음과 같습니다.

  • 연결/XA 연결을 생성할 수 있는 데이터베이스별 javax.sql.DataSource 또는 javax.sql.XADataSource 인스턴스(URL, 인증 정보 등)를 생성하거나 가져옵니다.
  • 데이터베이스별 구성(연결 풀링, 트랜잭션 관리자 등)을 사용하여 데이터베이스 별 javax.sql.DataSource 인스턴스(위의 데이터베이스별 데이터 소스로 내부 구성)를 생성하거나 가져옵니다.
  • javax.sql.DataSource 를 사용하여 java.sql.Connection 의 인스턴스를 가져오고 JDBC 작업을 수행합니다.

다음은 표준 예입니다.

// Database-specific, non-pooling, non-enlisting javax.sql.XADataSource
PGXADataSource postgresql = new org.postgresql.xa.PGXADataSource();
// Database-specific configuration
postgresql.setUrl("jdbc:postgresql://localhost:5432/reportdb");
postgresql.setUser("fuse");
postgresql.setPassword("fuse");
postgresql.setCurrentSchema("report");
postgresql.setConnectTimeout(5);
// ...

// Non database-specific, pooling, enlisting javax.sql.DataSource
BasicManagedDataSource pool = new org.apache.commons.dbcp2.managed.BasicManagedDataSource();
// Delegate to database-specific XADatasource
pool.setXaDataSourceInstance(postgresql);
// Delegate to JTA transaction manager
pool.setTransactionManager(transactionManager);
// Non database-specific configuration
pool.setMinIdle(3);
pool.setMaxTotal(10);
pool.setValidationQuery("select schema_name, schema_owner from information_schema.schemata");
// ...

// JDBC code:
javax.sql.DataSource applicationDataSource = pool;

try (Connection c = applicationDataSource.getConnection()) {
    try (Statement st = c.createStatement()) {
        try (ResultSet rs = st.executeQuery("select ...")) {
            // ....

Fuse 환경에는 많은 구성 옵션이 있으며 DBCP2를 사용할 필요가 없습니다.

6.3. JDBC 데이터 소스 구성

OSGi 트랜잭션 아키텍처에서 설명한 대로 일부 서비스는 OSGi 서비스 레지스트리에 등록되어야 합니다. 예를 들어 javax. Cryostat. UserTransaction 인터페이스를 사용하여 트랜잭션 관리자 인스턴스를 찾을 수 있는 것과 마찬가지로 javax. sql.DataSource 인터페이스를 사용하여 JDBC 데이터 소스에서 동일한 작업을 수행할 수 있습니다. 요구 사항은 다음과 같습니다.

  • 대상 데이터베이스와 통신할 수 있는 데이터베이스별 데이터 소스
  • 풀링 및 가능한 트랜잭션 관리(XA)를 구성할 수 있는 일반 데이터 소스

Fuse와 같은 OSGi 환경에서는 OSGi 서비스에 등록된 경우 애플리케이션에서 데이터 소스에 액세스할 수 있습니다. 기본적으로 다음과 같이 수행됩니다.

org.osgi.framework.BundleContext.registerService(javax.sql.DataSource.class,
                                                 dataSourceObject,
                                                 properties);
org.osgi.framework.BundleContext.registerService(javax.sql.XADataSource.class,
                                                 xaDataSourceObject,
                                                 properties);

이러한 서비스를 등록하는 방법은 다음 두 가지가 있습니다.

  • jdbc:ds-create Karaf console 명령을 사용하여 데이터 소스 게시. 구성 방법입니다.
  • 블루프린트, OSGi 선언 서비스(SCR) 또는 BundleContext.registerService() API 호출과 같은 메서드를 사용하여 데이터 소스 게시. 이 메서드에는 코드 및/또는 메타데이터가 포함된 전용 OSGi 번들이 필요합니다. 이 방법은 the_deployment method_입니다.

6.4. OSGi JDBC 서비스 사용

OSGi Enterprise R6 사양의 125장에서는 org.osgi.service.jdbc 패키지에서 단일 인터페이스를 정의합니다. 다음은 OSGi에서 데이터 소스를 처리하는 방법입니다.

public interface DataSourceFactory {

    java.sql.Driver createDriver(Properties props);

    javax.sql.DataSource createDataSource(Properties props);

    javax.sql.ConnectionPoolDataSource createConnectionPoolDataSource(Properties props);

    javax.sql.XADataSource createXADataSource(Properties props);
}

앞서 언급했듯이 일반 java.sql.Connection 연결은 java.sql.Driver 에서 직접 가져올 수 있습니다.

Generic org.osgi.service.jdbc.DataSourceFactory

org.osgi.service.jdbc.DataSourceFactory 의 가장 간단한 구현은 org.ops4j.pax.jdbc.impl.DriverDataSourceFactory 입니다. mvn:org.ops4j.pax.jdbc/pax-jdbc/pax-jdbc/1.3.0 번들이 제공하는 org.ops4j.impl.DriverDataSourceFactory입니다. 이 모든 작업은 표준 Java™ ServiceLoader 유틸리티에 대한 /META-INF/services/java.sql.Driver 설명자를 포함할 수 있는 번들을 추적하는 것입니다. 표준 JDBC 드라이버를 설치하는 경우 pax-jdbc 번들은 java.sql.Driver.connect() 호출을 통해 연결을 가져오기 위해 (직접은 아님) DataSourceFactory 를 등록합니다.

karaf@root()> install -s mvn:org.osgi/org.osgi.service.jdbc/1.0.0
Bundle ID: 223
karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc/1.3.0
Bundle ID: 224
karaf@root()> install -s mvn:org.postgresql/postgresql/42.2.5
Bundle ID: 225
karaf@root()> install -s mvn:mysql/mysql-connector-java/5.1.34
Bundle ID: 226

karaf@root()> bundle:services -p org.postgresql.jdbc42

PostgreSQL JDBC Driver JDBC42 (225) provides:
---------------------------------------------
objectClass = [org.osgi.service.jdbc.DataSourceFactory]
osgi.jdbc.driver.class = org.postgresql.Driver
osgi.jdbc.driver.name = PostgreSQL JDBC Driver
osgi.jdbc.driver.version = 42.2.5
service.bundleid = 225
service.id = 242
service.scope = singleton

karaf@root()> bundle:services -p com.mysql.jdbc

Oracle Corporation's JDBC Driver for MySQL (226) provides:
----------------------------------------------------------
objectClass = [org.osgi.service.jdbc.DataSourceFactory]
osgi.jdbc.driver.class = com.mysql.jdbc.Driver
osgi.jdbc.driver.name = com.mysql.jdbc
osgi.jdbc.driver.version = 5.1.34
service.bundleid = 226
service.id = 243
service.scope = singleton
-----
objectClass = [org.osgi.service.jdbc.DataSourceFactory]
osgi.jdbc.driver.class = com.mysql.fabric.jdbc.FabricMySQLDriver
osgi.jdbc.driver.name = com.mysql.jdbc
osgi.jdbc.driver.version = 5.1.34
service.bundleid = 226
service.id = 244
service.scope = singleton

karaf@root()> service:list org.osgi.service.jdbc.DataSourceFactory
[org.osgi.service.jdbc.DataSourceFactory]
-----------------------------------------
 osgi.jdbc.driver.class = org.postgresql.Driver
 osgi.jdbc.driver.name = PostgreSQL JDBC Driver
 osgi.jdbc.driver.version = 42.2.5
 service.bundleid = 225
 service.id = 242
 service.scope = singleton
Provided by :
 PostgreSQL JDBC Driver JDBC42 (225)

[org.osgi.service.jdbc.DataSourceFactory]
-----------------------------------------
 osgi.jdbc.driver.class = com.mysql.jdbc.Driver
 osgi.jdbc.driver.name = com.mysql.jdbc
 osgi.jdbc.driver.version = 5.1.34
 service.bundleid = 226
 service.id = 243
 service.scope = singleton
Provided by :
 Oracle Corporation's JDBC Driver for MySQL (226)

[org.osgi.service.jdbc.DataSourceFactory]
-----------------------------------------
 osgi.jdbc.driver.class = com.mysql.fabric.jdbc.FabricMySQLDriver
 osgi.jdbc.driver.name = com.mysql.jdbc
 osgi.jdbc.driver.version = 5.1.34
 service.bundleid = 226
 service.id = 244
 service.scope = singleton
Provided by :
 Oracle Corporation's JDBC Driver for MySQL (226)

위의 명령을 사용하면 javax.sql.DataSource 서비스가 아직 등록되지 않았지만 한 발짝 더 가깝습니다. 위의 중간 org.osgi.service.jdbc.DataSourceFactory 서비스를 사용하여 다음을 얻을 수 있습니다.

  • java.sql.Driver
  • 속성을 전달하여 javax.sql.DataSource: url,userpasswordcreateDataSource() 메서드에 전달합니다.

일반 org .osgi.service.jdbc.DataFactory에서 javax.sql.XADataSource 또는 javax. sql.XADataSource를 가져올 수 없습니다.

참고

mvn:org.postgresql/postgresql/42.2.5 번들은 OSGi JDBC 사양을 올바르게 구현하고 org.osgi.service.jdbc.DataSourceFactory 인스턴스를 XA 및 ConnectionPool 데이터 소스를 생성하는 것을 포함하여 구현되는 모든 메서드에 등록합니다.

전용 데이터베이스별 org.osgi.service.jdbc.DataSourceFactory 구현

다음과 같은 추가 번들이 있습니다.

  • mvn:org.ops4j.pax.jdbc/pax-jdbc-mysql/1.3.0
  • mvn:org.ops4j.pax.jdbc/pax-jdbc-db2/1.3.0
  • …​

이러한 번들은 javax.sql .ConnectionPoolDataSourcejavax.sql.XADataSource 를 포함하여 모든 종류의 팩토리 를 반환할 수 있는 데이터베이스별 org.osgi.service.jdbc.DataSourcey 서비스를 등록합니다. 예를 들면 다음과 같습니다.

karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc-mysql/1.3.0
Bundle ID: 227

karaf@root()> bundle:services -p org.ops4j.pax.jdbc.mysql

OPS4J Pax JDBC MySQL Driver Adapter (227) provides:
---------------------------------------------------
objectClass = [org.osgi.service.jdbc.DataSourceFactory]
osgi.jdbc.driver.class = com.mysql.jdbc.Driver
osgi.jdbc.driver.name = mysql
service.bundleid = 227
service.id = 245
service.scope = singleton

karaf@root()> service:list org.osgi.service.jdbc.DataSourceFactory
...
[org.osgi.service.jdbc.DataSourceFactory]
-----------------------------------------
 osgi.jdbc.driver.class = com.mysql.jdbc.Driver
 osgi.jdbc.driver.name = mysql
 service.bundleid = 227
 service.id = 245
 service.scope = singleton
Provided by :
 OPS4J Pax JDBC MySQL Driver Adapter (227)

6.4.1. PAX-JDBC 구성 서비스

pax-jdbc (또는 pax-jdbc-mysql,pax-jdbc-oracle, …​) 번들을 사용하면 org.osgi.service.jdbc.DataSourceFactory 서비스가 등록되어 지정된 데이터베이스의 데이터 소스를 가져올 수 있습니다( 6.2.1절. “데이터베이스 특정 및 일반 데이터 소스”참조). 그러나 실제 데이터 소스는 아직 없습니다.

mvn:org.ops4j.pax.jdbc/pax-jdbc-config/1.3.0 번들은 다음 두 가지 작업을 수행하는 관리형 서비스 팩토리를 제공합니다.

  • 메서드를 호출하기 위해 org.osgi.service.jdbc.DataSourceFactory OSGi 서비스를 추적합니다.

    public DataSource createDataSource(Properties props);
    public XADataSource createXADataSource(Properties props);
    public ConnectionPoolDataSource createConnectionPoolDataSource(Properties props);
  • 위의 방법에 필요한 속성을 수집하기 위해 org.ops4j.datasource 팩토리 PID 를 추적합니다. 예를 들어 ${karaf.etc}/org.ops4j.datasource-mysql.cfg 파일을 생성하여 구성 관리 서비스에 사용할 수 있는 방법을 사용하여 팩토리 구성을 생성하는 경우 최종 단계를 수행하여 실제 데이터베이스별 데이터 소스를 노출할 수 있습니다.

다음은 새로운 Fuse 설치부터 시작하기위한 상세한 표준 단계별 가이드입니다.

참고

필요한 번들을 정확하게 표시하기 위해 기능 대신 번들을 명시적으로 설치합니다. 편의를 위해 PAX JDBC 프로젝트는 여러 데이터베이스 제품 및 구성 접근 방식에 대한 기능을 제공합니다.

  1. Install a JDBC driver with /META-INF/services/java.sql.Driver:

    karaf@root()> install -s mvn:mysql/mysql-connector-java/5.1.34
    Bundle ID: 223
  2. 중간org.osgi.service.jdbc.DataSourceFactory 를 등록하는 OSGi JDBC 서비스 번들 및 pax-jdbc-mysql 번들을 설치합니다.

    karaf@root()> install -s mvn:org.osgi/org.osgi.service.jdbc/1.0.0
    Bundle ID: 224
    karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc-mysql/1.3.0
    Bundle ID: 225
    
    karaf@root()> service:list org.osgi.service.jdbc.DataSourceFactory
    [org.osgi.service.jdbc.DataSourceFactory]
    -----------------------------------------
     osgi.jdbc.driver.class = com.mysql.jdbc.Driver
     osgi.jdbc.driver.name = mysql
     service.bundleid = 225
     service.id = 242
     service.scope = singleton
    Provided by :
     OPS4J Pax JDBC MySQL Driver Adapter (225)
  3. org.osgi.service.jdbc.DataSourceFactory 서비스 및org.ops4j.datasource factory PID 를 추적하는 pax-jdbc -config 번 들과 pax-jdbc-config 번들을 설치합니다.

    karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc/1.3.0
    Bundle ID: 226
    karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc-pool-common/1.3.0
    Bundle ID: 227
    karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc-config/1.3.0
    Bundle ID: 228
    
    karaf@root()> bundle:services -p org.ops4j.pax.jdbc.config
    
    OPS4J Pax JDBC Config (228) provides:
    -------------------------------------
    objectClass = [org.osgi.service.cm.ManagedServiceFactory]
    service.bundleid = 228
    service.id = 245
    service.pid = org.ops4j.datasource
    service.scope = singleton
  4. 팩토리 구성 을 생성합니다(MySQL 서버가 실행 중이라고 가정).

    karaf@root()> config:edit --factory --alias mysql org.ops4j.datasource
    karaf@root()> config:property-set osgi.jdbc.driver.name mysql
    karaf@root()> config:property-set dataSourceName mysqlds
    karaf@root()> config:property-set url jdbc:mysql://localhost:3306/reportdb
    karaf@root()> config:property-set user fuse
    karaf@root()> config:property-set password fuse
    karaf@root()> config:update
    
    karaf@root()> config:list '(service.factoryPid=org.ops4j.datasource)'
    ----------------------------------------------------------------
    Pid:            org.ops4j.datasource.a7941498-9b62-4ed7-94f3-8c7ac9365313
    FactoryPid:     org.ops4j.datasource
    BundleLocation: ?
    Properties:
       dataSourceName = mysqlds
       felix.fileinstall.filename = file:${karaf.etc}/org.ops4j.datasource-mysql.cfg
       osgi.jdbc.driver.name = mysql
       password = fuse
       service.factoryPid = org.ops4j.datasource
       service.pid = org.ops4j.datasource.a7941498-9b62-4ed7-94f3-8c7ac9365313
       url = jdbc:mysql://localhost:3306/reportdb
       user = fuse
  5. pax-jdbc-configjavax.sql.DataSource 서비스로 구성을 처리했는지 확인합니다.

    karaf@root()> service:list javax.sql.DataSource
    [javax.sql.DataSource]
    ----------------------
     dataSourceName = mysqlds
     felix.fileinstall.filename = file:${karaf.etc}/org.ops4j.datasource-mysql.cfg
     osgi.jdbc.driver.name = mysql
     osgi.jndi.service.name = mysqlds
     password = fuse
     pax.jdbc.managed = true
     service.bundleid = 228
     service.factoryPid = org.ops4j.datasource
     service.id = 246
     service.pid = org.ops4j.datasource.a7941498-9b62-4ed7-94f3-8c7ac9365313
     service.scope = singleton
     url = jdbc:mysql://localhost:3306/reportdb
     user = fuse
    Provided by :
     OPS4J Pax JDBC Config (228)

이제 실제 데이터베이스별(아직 풀링 없음) 데이터 소스가 있습니다. 필요한 곳에 이미 삽입할 수 있습니다. 예를 들어 Karaf 명령을 사용하여 데이터베이스를 쿼리할 수 있습니다.

karaf@root()> feature:install -v jdbc
Adding features: jdbc/[4.2.0.fuse-000237-redhat-1,4.2.0.fuse-000237-redhat-1]
...
karaf@root()> jdbc:ds-list
Mon May 14 08:46:22 CEST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Name    │ Product │ Version │ URL                                  │ Status
────────┼─────────┼─────────┼──────────────────────────────────────┼───────
mysqlds │ MySQL   │ 5.7.21  │ jdbc:mysql://localhost:3306/reportdb │ OK

karaf@root()> jdbc:query mysqlds 'select * from incident'
Mon May 14 08:46:46 CEST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
date                  │ summary    │ name   │ details                       │ id │ email
──────────────────────┼────────────┼────────┼───────────────────────────────┼────┼─────────────────
2018-02-20 08:00:00.0 │ Incident 1 │ User 1 │ This is a report incident 001 │ 1  │ user1@redhat.com
2018-02-20 08:10:00.0 │ Incident 2 │ User 2 │ This is a report incident 002 │ 2  │ user2@redhat.com
2018-02-20 08:20:00.0 │ Incident 3 │ User 3 │ This is a report incident 003 │ 3  │ user3@redhat.com
2018-02-20 08:30:00.0 │ Incident 4 │ User 4 │ This is a report incident 004 │ 4  │ user4@redhat.com

위의 예제에서는 MySQL 경고를 볼 수 있습니다. 이것은 문제가 아닙니다. 모든 속성(OSGi JDBC 특정 속성뿐만 아니라)이 제공될 수 있습니다.

karaf@root()> config:property-set --pid org.ops4j.datasource.a7941498-9b62-4ed7-94f3-8c7ac9365313 useSSL false

karaf@root()> jdbc:ds-list
Name    │ Product │ Version │ URL                                  │ Status
────────┼─────────┼─────────┼──────────────────────────────────────┼───────
mysqlds │ MySQL   │ 5.7.21  │ jdbc:mysql://localhost:3306/reportdb │ OK

6.4.2. 처리된 속성 요약

admin 팩토리 PID 구성의 속성은 관련 org.osgi.service.jdbc.DataSourceFactory 구현으로 전달됩니다.

일반

org.ops4j.pax.jdbc.impl.DriverDataSourceFactory properties:

  • url
  • user
  • 암호

DB2

org.ops4j.pax.jdbc.db2.impl.DB2DataSourceFactory 속성에는 다음과 같은 구현 클래스의 모든 empty 속성이 포함됩니다.

  • com.ibm.db2.jcc.DB2SimpleDataSource
  • com.ibm.db2.jcc.DB2ConnectionPoolDataSource
  • com.ibm.db2.jcc.DB2XADataSource

PostgreSQL

Nnative org.postgresql.osgi.PGDataSourceFactory 속성에는 org.postgresql.PGProperty 에 지정된 모든 속성이 포함됩니다.

HSQLDB

org.ops4j.pax.jdbc.hsqldb.impl.HsqldbDataSourceFactory properties:

  • url
  • user
  • 암호
  • databaseName
  • 모든 Cryostat 속성

    • org.hsqldb.jdbc.JDBCDataSource
    • org.hsqldb.jdbc.pool.JDBCPooledDataSource
    • org.hsqldb.jdbc.pool.JDBCXADataSource

SQL Server 및 Sybase

org.ops4j.pax.jdbc.jtds.jtds.jtds.JTDSDataFactory 속성에는 net.sourceforge.jtds.jdbcx.JtdsDataSource.

SQL Server

org.ops4j.pax.jdbc.mssql.impl.MSSQLDataSourceFactory properties:

  • url
  • user
  • 암호
  • databaseName
  • serverName
  • portNumber
  • 모든 Cryostat 속성

    • com.microsoft.sqlserver.jdbc.SQLServerDataSource
    • com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource
    • com.microsoft.sqlserver.jdbc.SQLServerXADataSource

MySQL

org.ops4j.pax.jdbc.mysql.impl.MysqlDataSourceFactory properties:

  • url
  • user
  • 암호
  • databaseName
  • serverName
  • portNumber
  • 모든 Cryostat 속성

    • com.mysql.jdbc.jdbc2.optional.MysqlDataSource
    • com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource
    • com.mysql.jdbc.jdbc2.optional.MysqlXADataSource

Oracle

org.ops4j.pax.jdbc.oracle.impl.OracleDataSourceFactory properties:

  • url
  • databaseName
  • serverName
  • user
  • 암호
  • 모든 Cryostat 속성

    • oracle.jdbc.pool.OracleDataSource
    • oracle.jdbc.pool.OracleConnectionPoolDataSource
    • oracle.jdbc.xa.client.OracleXADataSource

SQLite

org.ops4j.pax.jdbc.sqlite.impl.SqliteDataSourceFactory properties:

  • url
  • databaseName
  • org.sqlite.SQLiteDataSource의 모든 Cryostat 속성

6.4.3. pax-jdb-config 번들이 속성을 처리하는 방법

pax-jdbc-config 번들은 jdbc 로 접두사가 지정된 속성을 처리합니다. 이러한 모든 속성은 이 접두사가 제거되고 나머지 이름은 전달됩니다.

다음은 Fuse 새로 설치부터 시작하는 예제입니다.

karaf@root()> install -s mvn:mysql/mysql-connector-java/5.1.34
Bundle ID: 223
karaf@root()> install -s mvn:org.osgi/org.osgi.service.jdbc/1.0.0
Bundle ID: 224
karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc-mysql/1.3.0
Bundle ID: 225
karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc/1.3.0
Bundle ID: 226
karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc-pool-common/1.3.0
Bundle ID: 227
karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc-config/1.3.0
Bundle ID: 228

karaf@root()> config:edit --factory --alias mysql org.ops4j.datasource
karaf@root()> config:property-set osgi.jdbc.driver.name mysql
karaf@root()> config:property-set dataSourceName mysqlds
karaf@root()> config:property-set dataSourceType DataSource
karaf@root()> config:property-set jdbc.url jdbc:mysql://localhost:3306/reportdb
karaf@root()> config:property-set jdbc.user fuse
karaf@root()> config:property-set jdbc.password fuse
karaf@root()> config:property-set jdbc.useSSL false
karaf@root()> config:update

karaf@root()> config:list '(service.factoryPid=org.ops4j.datasource)'
----------------------------------------------------------------
Pid:            org.ops4j.datasource.7c3ee718-7309-46a0-ae3a-64b38b17a0a3
FactoryPid:     org.ops4j.datasource
BundleLocation: ?
Properties:
   dataSourceName = mysqlds
   dataSourceType = DataSource
   felix.fileinstall.filename = file:/data/servers/7.11.1.fuse-7_11_1-00013-redhat-00003/etc/org.ops4j.datasource-mysql.cfg
   jdbc.password = fuse
   jdbc.url = jdbc:mysql://localhost:3306/reportdb
   jdbc.useSSL = false
   jdbc.user = fuse
   osgi.jdbc.driver.name = mysql
   service.factoryPid = org.ops4j.datasource
   service.pid = org.ops4j.datasource.7c3ee718-7309-46a0-ae3a-64b38b17a0a3

karaf@root()> service:list javax.sql.DataSource
[javax.sql.DataSource]
----------------------
 dataSourceName = mysqlds
 dataSourceType = DataSource
 felix.fileinstall.filename = file:${karaf.etc}/org.ops4j.datasource-mysql.cfg
 jdbc.password = fuse
 jdbc.url = jdbc:mysql://localhost:3306/reportdb
 jdbc.user = fuse
 jdbc.useSSL = false
 osgi.jdbc.driver.name = mysql
 osgi.jndi.service.name = mysqlds
 pax.jdbc.managed = true
 service.bundleid = 228
 service.factoryPid = org.ops4j.datasource
 service.id = 246
 service.pid = org.ops4j.datasource.7c3ee718-7309-46a0-ae3a-64b38b17a0a3
 service.scope = singleton
Provided by :
 OPS4J Pax JDBC Config (228)

pax-jdbc-config 번들에는 다음과 같은 속성이 필요합니다.

  • osgi.jdbc.driver.name
  • dataSourceName
  • dataSourceType

관련 org.osgi.service.jdbc.DataSourceFactory 메서드를 찾아서 호출하려면 다음을 수행합니다. jdbc. 접두사가 있는 속성은 ( 접두사를 제거한 후) org.osgi.service.jdbc.DataSourceFactory.createDataSourceSource(properties) 로 전달됩니다. 그러나 이러한 속성은 접두사가 제거되지 않고 (예: javax.sql.DataSource OSGi 서비스) 속성으로 추가됩니다.

6.5. JDBC 콘솔 명령 사용

Fuse는 jdbc:* 범위에 쉘 명령을 포함하는 jdbc 기능을 제공합니다. 이전 예제에서는 jdbc:query 를 사용하는 방법을 보여줍니다. 구성 관리 구성을 생성할 필요성을 숨기는 명령도 있습니다.

Fuse의 새로운 인스턴스부터 다음과 같이 일반 DataSourceFactory 서비스에 데이터베이스별 데이터 소스를 등록할 수 있습니다.

karaf@root()> feature:install jdbc

karaf@root()> jdbc:ds-factories
Name │ Class │ Version
─────┼───────┼────────

karaf@root()> install -s mvn:mysql/mysql-connector-java/5.1.34
Bundle ID: 228

karaf@root()> jdbc:ds-factories
Name           │ Class                                   │ Version
───────────────┼─────────────────────────────────────────┼────────
com.mysql.jdbc │ com.mysql.jdbc.Driver                   │ 5.1.34
com.mysql.jdbc │ com.mysql.fabric.jdbc.FabricMySQLDriver │ 5.1.34

다음은 MySQL 관련 DataSourceFactory 서비스를 등록하는 예입니다.

karaf@root()> feature:repo-add mvn:org.ops4j.pax.jdbc/pax-jdbc-features/1.3.0/xml/features-gpl
Adding feature url mvn:org.ops4j.pax.jdbc/pax-jdbc-features/1.3.0/xml/features-gpl

karaf@root()> feature:install pax-jdbc-mysql

karaf@root()> la -l|grep mysql

232 │ Active   │  80 │ 5.1.34                   │ mvn:mysql/mysql-connector-java/5.1.34

233 │ Active   │  80 │ 1.3.0                    │ mvn:org.ops4j.pax.jdbc/pax-jdbc-mysql/1.3.0

karaf@root()> jdbc:ds-factories
Name           │ Class                                   │ Version
───────────────┼─────────────────────────────────────────┼────────
com.mysql.jdbc │ com.mysql.jdbc.Driver                   │ 5.1.34
mysql          │ com.mysql.jdbc.Driver                   │
com.mysql.jdbc │ com.mysql.fabric.jdbc.FabricMySQLDriver │ 5.1.34

위의 표는 혼란스러울 수 있지만 위에서 언급한 것처럼 pax-jdbc-데이터베이스 번들 중 하나만 org.osgi.service.jdbc.jdbc.DataSourceFactory 인스턴스를 등록할 수 있으며 java.sql.Driver.connect() 에만 위임 되지 않는 표준/XA/connection 풀 데이터 소스를 생성할 수 있습니다.

다음 예제에서는 MySQL 데이터 소스를 생성하고 확인합니다.

karaf@root()> jdbc:ds-create -dt DataSource -dn mysql -url 'jdbc:mysql://localhost:3306/reportdb?useSSL=false' -u fuse -p fuse mysqlds

karaf@root()> jdbc:ds-list
Name    │ Product │ Version │ URL                                               │ Status
────────┼─────────┼─────────┼───────────────────────────────────────────────────┼───────
mysqlds │ MySQL   │ 5.7.21  │ jdbc:mysql://localhost:3306/reportdb?useSSL=false │ OK

karaf@root()> jdbc:query mysqlds 'select * from incident'
date                  │ summary    │ name   │ details                       │ id │ email
──────────────────────┼────────────┼────────┼───────────────────────────────┼────┼─────────────────
2018-02-20 08:00:00.0 │ Incident 1 │ User 1 │ This is a report incident 001 │ 1  │ user1@redhat.com
2018-02-20 08:10:00.0 │ Incident 2 │ User 2 │ This is a report incident 002 │ 2  │ user2@redhat.com
2018-02-20 08:20:00.0 │ Incident 3 │ User 3 │ This is a report incident 003 │ 3  │ user3@redhat.com
2018-02-20 08:30:00.0 │ Incident 4 │ User 4 │ This is a report incident 004 │ 4  │ user4@redhat.com

karaf@root()> config:list '(service.factoryPid=org.ops4j.datasource)'
----------------------------------------------------------------
Pid:            org.ops4j.datasource.55b18993-de4e-4e0b-abb2-a4c13da7f78b
FactoryPid:     org.ops4j.datasource
BundleLocation: mvn:org.ops4j.pax.jdbc/pax-jdbc-config/1.3.0
Properties:
   dataSourceName = mysqlds
   dataSourceType = DataSource
   osgi.jdbc.driver.name = mysql
   password = fuse
   service.factoryPid = org.ops4j.datasource
   service.pid = org.ops4j.datasource.55b18993-de4e-4e0b-abb2-a4c13da7f78b
   url = jdbc:mysql://localhost:3306/reportdb?useSSL=false
   user = fuse

위 화면과 같이 org.ops4j.datasource 팩토리 PID가 생성됩니다. 그러나 config:update 를 사용하면 ${karaf.etc} 에 자동으로 저장되지 않습니다.

6.6. 암호화된 구성 값 사용

pax-jdbc-config 기능은 값이 암호화된 구성 관리 구성을 처리할 수 있습니다. 널리 사용되는 솔루션은 블루프린트에서도 사용하는 Jasypt 암호화 서비스를 사용하는 것입니다.

별칭 서비스 속성을 사용하여 OSGi에 등록된 org.jasypt.encryption.StringEncryptor 서비스가 있는 경우 데이터 소스 팩토리 PID 에서 거부하고 암호화된 암호를 사용할 수 있습니다. 예를 들면 다음과 같습니다.

felix.fileinstall.filename = */etc/org.ops4j.datasource-mysql.cfg
dataSourceName = mysqlds
dataSourceType = DataSource
decryptor = my-jasypt-decryptor
osgi.jdbc.driver.name = mysql
url = jdbc:mysql://localhost:3306/reportdb?useSSL=false
user = fuse
password = ENC(<encrypted-password>)

암호 해독기 서비스를 찾는 데 사용되는 서비스 필터는 (&(objectClass=org.jasypt.encryption.StringEncryptor)(alias=<alias>) 입니다. 여기서 < alias >는 데이터 소스 구성 팩토리 PID암호 해독 속성 값입니다.

6.7. JDBC 연결 풀 사용

이 섹션에서는 JDBC 연결 풀을 사용한 다음 이러한 연결 풀 모듈을 사용하는 방법을 보여줍니다.

중요

이 장에서는 데이터 소스 관리의 내부에 대한 완전한 정보를 제공합니다. DBCP2 연결 풀 기능에 대한 정보가 제공되지만 이 연결 풀은 적절한 JTA 등록 기능을 제공하지만 XA 복구 는 제공하지 않습니다.

XA 복구 가 적용되도록 하려면 pax-jdbc-pool-transx 또는 pax-jdbc-pool-narayana 연결 풀 모듈을 사용합니다.

6.7.1. JDBC 연결 풀 사용 소개

이전 예제에서는 데이터베이스별 데이터 소스 팩토리 를 등록하는 방법을 보여줍니다. 데이터 소스 자체는 연결에 대한 팩토리이기 때문에 org.osgi.service.jdbc.DataSourceFactory 는 세 가지 종류의 데이터 소스를 생성할 수 있어야 하는 메타 팩토리 로 취급될 수 있으며, 보너스로 java.sql.Driver:

  • javax.sql.DataSource
  • javax.sql.ConnectionPoolDataSource
  • javax.sql.XADataSource

예를 들어 pax-jdbc-mysqlorg.ops4j.pax.jdbc.mysql.impl.MysqlDataSourceFactory 를 생성합니다.

  • javax.sql.DataSourcecom.mysql.jdbc.jdbc2.optional.MysqlDataSource
  • javax.sql.ConnectionPoolDataSourcecom.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource
  • javax.sql.XADataSourcecom.mysql.jdbc.jdbc2.optional.MysqlXADataSource
  • java.sql.Drivercom.mysql.jdbc.Driver

PostgreSQL 드라이버 자체는 OSGi JDBC 서비스를 구현하고 다음을 생성합니다.

  • javax.sql.DataSourceorg.postgresql.jdbc2.optional.optional.PoolingDataSource (풀 관련 속성이 지정된 경우) 또는 org.postgresql.jdbc2.optional.SimpleDataSource
  • javax.sql.ConnectionPoolDataSourceorg.postgresql.jdbc2.optional.ConnectionPool
  • javax.sql.XADataSourceorg.postgresql.xa.PGXADataSource
  • java.sql.Driverorg.postgresql.Driver

표준 데이터 소스 예 에 표시된 것처럼 모든 풀링 (일반 데이터 소스)이 JTA 환경에서 작동하려면 실제로 (XA) 연결을 얻으려면 데이터베이스별 데이터 소스가 필요합니다.

우리는 이미 후자를 보유하고 있으며 실제적이고 일반적인 신뢰할 수 있는 연결 풀이 필요합니다.

표준 데이터 소스 예 는 데이터베이스별 데이터 소스를 사용하여 일반 풀을 구성하는 방법을 보여줍니다. pax-jdbc-pool-* 번들은 위의 org.osgi.service.jdbc.DataSourceFactory 서비스와 원활하게 작동합니다.

OSGI Enterprise R6 JDBC 사양에서 org.osgi.service.jdbc.DataSourceFactory 표준 인터페이스를 제공하는 것처럼 pax-jdbc-pool-common독점 org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory 인터페이스를 제공합니다.

public interface PooledDataSourceFactory {

    javax.sql.DataSource create(org.osgi.service.jdbc.DataSourceFactory dsf, Properties config)

}

이 인터페이스는 이전에 제시된 중요한 참고 사항에 완벽하게 부합하며 반복할 가치가 있습니다.

중요

애플리케이션에서 XA 트랜잭션 및/또는 연결 풀링을 사용하는 경우에도 애플리케이션은 다른 두 JDBC 데이터 소스 인터페이스가 아닌 javax.sql.DataSource 와 상호 작용합니다.

이 인터페이스는 데이터베이스별 비풀링 데이터에서 간단히 풀링 데이터 소스를 생성합니다. 또는 보다 정확하게는 데이터베이스별 데이터 소스의 팩토리 를 데이터 소스 풀링 팩토리로 변환하는 데이터 소스 팩토리(meta factory)입니다.

참고

javax.sql.DataSource 개체에 대한 풀링을 이미 반환하는 org.osgi.service.jdbc.DataSourceFactory 서비스를 사용하여 애플리케이션에서 javax.sql.DataSource Source 개체에 대한 풀링을 구성하지 못하도록 하는 것은 없습니다.

다음 표에서는 풀링된 데이터 소스 팩토리를 등록하는 번들을 보여줍니다. 표에서 o.o.p.j.porg.ops4j.pax.jdbc.pool 을 나타냅니다.

번들PooledDataSourceFactory풀 키

pax-jdbc-pool-narayana

o.o.p.j.p.narayana.impl.Dbcp(XA)PooledDataSourceFactory

narayana

pax-jdbc-pool-dbcp2

o.o.p.j.p.dbcp2.impl.Dbcp(XA)PooledDataSourceFactory

dbcp2

pax-jdbc-pool-transx

o.o.p.j.p.transx.impl.Transx(Xa)PooledDataSourceFactory

transx

위의 번들은 데이터 소스 자체가 아닌 데이터 소스 팩토리만 설치합니다. 애플리케이션에는 javax.sql.DataSource create(org.osgi.service.jdbc.DataSourceFactory dsf, Properties config) 메서드를 호출하는 항목이 필요합니다.

6.7.2. dbcp2 연결 풀 모듈 사용

일반 데이터 소스에 대한 섹션에서는 Apache Commons DBCP 모듈을 사용하고 구성하는 방법에 대한 예를 제공합니다. 이 섹션에서는 Fuse OSGi 환경에서 이 작업을 수행하는 방법을 설명합니다.

6.4.1절. “PAX-JDBC 구성 서비스” 번들을 고려하십시오. 다음을 추적하는 것 외에도 다음을 수행하십시오.

  • org.osgi.service.jdbc.DataSourceFactory services
  • org.ops4j.datasource 팩토리 PID

또한 번들은 pax-jc -pool-* 번들 중 하나에서 등록한 org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory 의 인스턴스도 추적합니다.

팩토리 구성에 pool 속성이 포함된 경우 pax-jdbc-config 번들에 의해 등록된 궁극적인 데이터 소스는 데이터베이스별 데이터이며 데이터베이스별 데이터입니다. 그러나 pool=dbcp2인 경우 다음 중 하나로 래핑됩니다.

  • org.apache.commons.dbcp2.PoolingDataSource
  • org.apache.commons.dbcp2.managed.ManagedDataSource

이는 일반 데이터 소스 예와 일치합니다. pool 속성 및 비xa 또는 xa 데이터 소스를 선택하는 부울 xa 속성 외에도 org.ops4j.datasource 팩토리 PID 에는 접두사 가 지정된 속성이 포함될 수 있습니다.

  • pool.*
  • factory.*

여기서 각 속성이 사용되는 위치는 pax-jdbc-pool-* 번들이 사용되는지에 따라 다릅니다. DBCP2의 경우 다음과 같습니다.

  • pool.*: org.apache.commons.pool2.impl.GenericObjectPoolConfig 의 빈 속성(xa 및 비xa 시나리오 모두)
  • factory.*: org.apache.commons.dbcp2.managed.PoolableManagedConnectionFactory (xa) 또는 org.apache.commons.dbcp2.PoolableConnectionFactory (non-xa)의 8080 속성

6.7.2.1. BasicDataSource의 구성 속성

다음 표에는 BasicDataSource의 일반 구성 속성이 나열되어 있습니다.

매개변수Default설명

사용자 이름

 

연결을 설정하기 위해 JDBC 드라이버에 전달할 연결 사용자 이름입니다.

암호

 

연결을 설정하기 위해 JDBC 드라이버에 전달할 연결 암호입니다.

url

 

연결을 설정하기 위해 JDBC 드라이버에 전달할 연결 URL입니다.

driverClassName

 

사용할 JDBC 드라이버의 정규화된 Java 클래스 이름입니다.

initialSize

0

풀을 시작할 때 생성되는 초기 연결 수입니다.

maxTotal

8

이 풀에서 동시에 할당할 수 있는 최대 활성 연결 수 또는 제한 없음에 대해 음수입니다.

maxIdle

8

추가 해제 없이 풀에서 유휴 상태로 유지할 수 있는 최대 연결 수 또는 제한 없이 음수입니다.

minIdle

0

추가 생성 없이 풀에서 유휴 상태로 유지할 수 있는 최소 연결 수 또는 0을 생성하여 none을 생성합니다.

maxWaitMillis

무기

풀에서 사용할 수 있는 최대 시간(사용 가능한 연결이 없는 경우)이 예외를 throw하거나 -1이 무기한 대기하기 전에 반환될 때까지 대기하는 최대 시간(밀리초)입니다.

validationQuery

 

호출자로 반환하기 전에 이 풀의 연결을 검증하는 데 사용할 SQL 쿼리입니다. 지정된 경우 이 쿼리는 하나 이상의 행을 반환하는 SQL SELECT 문이어야 합니다. 지정하지 않으면 isValid() 메서드를 호출하여 연결을 검증합니다.

validationQueryTimeout

시간 초과 없음

연결 유효성 검사 쿼리가 실패하기 전 시간(초)입니다. 양수 값으로 설정하면 이 값은 검증 쿼리를 실행하는 데 사용되는 Statement의 setQueryTimeout 메서드를 통해 드라이버에 전달됩니다.

testOnCreate

false

생성 후 오브젝트의 유효성을 검사합니다. 오브젝트의 유효성을 검사하지 못하면 오브젝트 생성을 트리거한 차용 시도에 실패합니다.

testOnBorrow

true

개체가 풀에서 빌리기 전에 유효성을 검사합니다. 개체의 유효성 검사가 실패하면 풀에서 삭제되고 다른 개체를 빌리려고 합니다.

testOnReturn

false

개체가 풀로 반환되기 전에 유효성을 검사할지 여부를 나타내는 값입니다.

testWhileIdle

false

유휴 오브젝트 evictor(있는 경우)에서 오브젝트의 유효성을 검사합니다. 오브젝트의 유효성 검사가 실패하면 풀에서 삭제됩니다.

timeBetweenEvictionRunsMillis

-1

유휴 개체 evictor 스레드의 실행 사이에 유휴 상태의 시간(밀리초)입니다. 양수가 아닌 경우 유휴 오브젝트 제거 스레드가 실행되지 않습니다.

numTestsPerEvictionRun

3

유휴 개체의 각 실행 중에 검사할 오브젝트 수(있는 경우)입니다.

minEvictableIdleTimeMillis

1000 * 60 * 30

유휴 오브젝트 제거 대상(있는 경우)을 제거하기 전에 오브젝트가 풀에 유휴 상태로 남아 있을 수 있는 최소 시간입니다.

6.7.2.2. DBCP2 풀을 구성하는 방법의 예

다음은 jdbc와 함께 편리한 구문을 사용하는 DBCP2 풀(org.ops4j.datasource-mysql PID) 구성의 비현실 예제( useSSL=false제외)입니다.

# Configuration for pax-jdbc-config to choose and configure specific org.osgi.service.jdbc.DataSourceFactory
dataSourceName = mysqlds
dataSourceType = DataSource
osgi.jdbc.driver.name = mysql
jdbc.url = jdbc:mysql://localhost:3306/reportdb
jdbc.user = fuse
jdbc.password = fuse
jdbc.useSSL = false

# Hints for pax-jdbc-config to use org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory
pool = dbcp2
xa = false

# dbcp2 specific configuration of org.apache.commons.pool2.impl.GenericObjectPoolConfig
pool.minIdle = 10
pool.maxTotal = 100
pool.initialSize = 8
pool.blockWhenExhausted = true
pool.maxWaitMillis = 2000
pool.testOnBorrow = true
pool.testWhileIdle = false
pool.timeBetweenEvictionRunsMillis = 120000
pool.evictionPolicyClassName = org.apache.commons.pool2.impl.DefaultEvictionPolicy

# dbcp2 specific configuration of org.apache.commons.dbcp2.PoolableConnectionFactory
factory.maxConnLifetimeMillis = 30000
factory.validationQuery  = select schema_name from information_schema.schemata
factory.validationQueryTimeout = 2

위의 구성에서 poolxa 키는 등록된 org.ops4j.pax.j.jdbc.pool.common.PooledDataSourceFactory 서비스 중 하나를 선택하는 힌트 (서비스 필터 속성)입니다. DBCP2의 경우 다음과 같습니다.

karaf@root()> feature:install pax-jdbc-pool-dbcp2

karaf@root()> bundle:services -p org.ops4j.pax.jdbc.pool.dbcp2

OPS4J Pax JDBC Pooling DBCP2 (230) provides:
--------------------------------------------
objectClass = [org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory]
pool = dbcp2
service.bundleid = 230
service.id = 337
service.scope = singleton
xa = false
-----
objectClass = [org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory]
pool = dbcp2
service.bundleid = 230
service.id = 338
service.scope = singleton
xa = true

완전성을 위해 이전 예제에 연결 풀 구성이 추가된 전체 예제 는 다음과 같습니다. 다시 말해 새로운 Fuse 설치를 시작한다고 가정합니다.

  1. JDBC 드라이버를 설치합니다.

    karaf@root()> install -s mvn:mysql/mysql-connector-java/5.1.34
    Bundle ID: 223
  2. jdbc,pax-jdbc-mysqlpax-jdbc-pool-dbcp2 기능을 설치합니다.

    karaf@root()> feature:repo-add mvn:org.ops4j.pax.jdbc/pax-jdbc-features/1.3.0/xml/features-gpl
    Adding feature url mvn:org.ops4j.pax.jdbc/pax-jdbc-features/1.3.0/xml/features-gpl
    
    karaf@root()> feature:install jdbc pax-jdbc-mysql pax-jdbc-pool-dbcp2
    
    karaf@root()> service:list org.osgi.service.jdbc.DataSourceFactory
    ...
    [org.osgi.service.jdbc.DataSourceFactory]
    -----------------------------------------
     osgi.jdbc.driver.class = com.mysql.jdbc.Driver
     osgi.jdbc.driver.name = mysql
     service.bundleid = 232
     service.id = 328
     service.scope = singleton
    Provided by :
     OPS4J Pax JDBC MySQL Driver Adapter (232)
    
    karaf@root()> service:list org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory
    [org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory]
    --------------------------------------------------------
     pool = dbcp2
     service.bundleid = 233
     service.id = 324
     service.scope = singleton
     xa = false
    Provided by :
     OPS4J Pax JDBC Pooling DBCP2 (233)
    
    [org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory]
    --------------------------------------------------------
     pool = dbcp2
     service.bundleid = 233
     service.id = 332
     service.scope = singleton
     xa = true
    Provided by :
     OPS4J Pax JDBC Pooling DBCP2 (233)
  3. 팩토리 구성 을 생성합니다.

    karaf@root()> config:edit --factory --alias mysql org.ops4j.datasource
    karaf@root()> config:property-set osgi.jdbc.driver.name mysql
    karaf@root()> config:property-set dataSourceName mysqlds
    karaf@root()> config:property-set dataSourceType DataSource
    karaf@root()> config:property-set jdbc.url jdbc:mysql://localhost:3306/reportdb
    karaf@root()> config:property-set jdbc.user fuse
    karaf@root()> config:property-set jdbc.password fuse
    karaf@root()> config:property-set jdbc.useSSL false
    karaf@root()> config:property-set pool dbcp2
    karaf@root()> config:property-set xa false
    karaf@root()> config:property-set pool.minIdle 2
    karaf@root()> config:property-set pool.maxTotal 10
    karaf@root()> config:property-set pool.blockWhenExhausted true
    karaf@root()> config:property-set pool.maxWaitMillis 2000
    karaf@root()> config:property-set pool.testOnBorrow true
    karaf@root()> config:property-set pool.testWhileIdle alse
    karaf@root()> config:property-set pool.timeBetweenEvictionRunsMillis 120000
    karaf@root()> config:property-set factory.validationQuery 'select schema_name from information_schema.schemata'
    karaf@root()> config:property-set factory.validationQueryTimeout 2
    karaf@root()> config:update
  4. pax-jdbc-configjavax.sql.DataSource 서비스로 구성을 처리했는지 확인합니다.

    karaf@root()> service:list javax.sql.DataSource
    [javax.sql.DataSource]
    ----------------------
     dataSourceName = mysqlds
     dataSourceType = DataSource
     factory.validationQuery = select schema_name from information_schema.schemata
     factory.validationQueryTimeout = 2
     felix.fileinstall.filename = file:${karaf.etc}/org.ops4j.datasource-mysql.cfg
     jdbc.password = fuse
     jdbc.url = jdbc:mysql://localhost:3306/reportdb
     jdbc.user = fuse
     jdbc.useSSL = false
     osgi.jdbc.driver.name = mysql
     osgi.jndi.service.name = mysqlds
     pax.jdbc.managed = true
     pool.blockWhenExhausted = true
     pool.maxTotal = 10
     pool.maxWaitMillis = 2000
     pool.minIdle = 2
     pool.testOnBorrow = true
     pool.testWhileIdle = alse
     pool.timeBetweenEvictionRunsMillis = 120000
     service.bundleid = 225
     service.factoryPid = org.ops4j.datasource
     service.id = 338
     service.pid = org.ops4j.datasource.fd7aa3a1-695b-4342-b0d6-23d018a46fbb
     service.scope = singleton
    Provided by :
     OPS4J Pax JDBC Config (225)
  5. 데이터 소스를 사용합니다.

    karaf@root()> jdbc:query mysqlds 'select * from incident'
    date                  │ summary    │ name   │ details                       │ id │ email
    ──────────────────────┼────────────┼────────┼───────────────────────────────┼────┼─────────────────
    2018-02-20 08:00:00.0 │ Incident 1 │ User 1 │ This is a report incident 001 │ 1  │ user1@redhat.com
    2018-02-20 08:10:00.0 │ Incident 2 │ User 2 │ This is a report incident 002 │ 2  │ user2@redhat.com
    2018-02-20 08:20:00.0 │ Incident 3 │ User 3 │ This is a report incident 003 │ 3  │ user3@redhat.com
    2018-02-20 08:30:00.0 │ Incident 4 │ User 4 │ This is a report incident 004 │ 4  │ user4@redhat.com

6.7.3. narayana 연결 풀 모듈 사용

pax-jdbc-pool-narayna 모듈은 pax-jdbc-pool-dbcp2 로 거의 모든 작업을 수행합니다. XA 및 비 XA 시나리오에 대해 DBCP2-specific org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory 를 설치합니다. 유일한 차이점은 XA 시나리오에서 추가 통합 포인트가 있다는 것입니다. org.jboss.tm.XAResourceRecovery OSGi 서비스는 Narayana 트랜잭션 관리자의 일부인 com.arjuna.ats.arjuna.recovery.RecoveryManager 에서 선택하도록 등록됩니다.

6.7.4. transx 연결 풀 모듈 사용

pax-jdbc-pool-transx 번들에서는 pax- transx-jc-jdbc bundle에서 org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory 서비스를 기반으로 합니다. pax-transx-jdbc 번들에서는 org.ops4j.pax.transx.jdbc.ManagedDataSourceBuilder 기능을 사용하여 javax.sql.DataSource Sourcec 풀을 생성합니다. JCA(Java™ Connector Architecture) 솔루션이며 나중에 설명합니다.

6.8. 데이터 소스를 아티팩트로 배포

이 장에서는 OSGi JDBC 서비스를 도입하여 데이터베이스별 및 일반 데이터 소스를 등록하는 데 pax-jdbc 번들이 어떤 도움을 주는지와 OSGi 서비스 및 구성 관리 구성의 관점에서 어떻게 보이는지 보여줍니다. 두 가지 범주의 데이터 소스 의 구성은 Configuration Admin factory PIDs( pax-jdbc-config 번들의 도움말 포함)를 사용하여 수행할 수 있지만 일반적으로 배포 방법을 사용하는 것이 좋습니다.

배포 방법 에서는javax.sql.DataSource 서비스는 일반적으로 블루프린트 컨테이너 내에서 애플리케이션 코드로 직접 등록됩니다. 블루프린트 XML은 일반 OSGi 번들의 일부일 수 있으며 mvn: URI를 사용하여 설치하고 Maven 리포지토리(로컬 또는 원격)에 저장할 수 있습니다. 이러한 번들을 구성 관리 구성과 비교하여 버전 제어가 훨씬 쉽습니다.

pax-jdbc-config 번들 버전 1.3.0은 데이터 소스 구성에 대한 배포 방법을 추가합니다. 애플리케이션 개발자는 javax.sql.(XA)DataSource 서비스(일반적으로 Bluerpint XML를 사용하여)를 등록하고 서비스 속성을 지정합니다. pax-jdbc-config 번들은 이러한 등록된 데이터베이스별 데이터 소스를 감지하고(서비스 속성 사용) 일반 비 데이터베이스별 연결 풀 내에서 서비스를 래핑합니다.

완전성을 위해 블루프린트 XML을 사용하는 세 가지 배포 방법은 다음과 같습니다. Fuse는 Fuse 의 다양한 측면의 다양한 예와 함께 빠른 시작 다운로드를 제공합니다. Fuse Software Downloads 페이지에서 빠른 시작 zip 파일을 다운로드할 수 있습니다.

빠른 시작 zip 파일의 내용을 로컬 폴더에 추출합니다.

다음 예제에서 quickstarts/persistence 디렉터리를 $PQ_HOME 이라고 합니다.

6.8.1. 데이터 소스 수동 배포

데이터 소스의 수동 배포 예에서는 docker 기반 PostgreSQL 설치를 사용합니다. 이 방법에서는 pax-jdbc-config 가 필요하지 않습니다. 애플리케이션 코드는 데이터베이스별 및 일반 데이터 소스 모두를 등록합니다.

이 세 가지 번들이 필요합니다.

  • mvn:org.postgresql/postgresql/42.2.5
  • mvn:org.apache.commons/commons-pool2/2.5.0
  • mvn:org.apache.commons/commons-dbcp2/2.1.1
<!--
    Database-specific, non-pooling, non-enlisting javax.sql.XADataSource
-->
<bean id="postgresql" class="org.postgresql.xa.PGXADataSource">
    <property name="url" value="jdbc:postgresql://localhost:5432/reportdb" />
    <property name="user" value="fuse" />
    <property name="password" value="fuse" />
    <property name="currentSchema" value="report" />
    <property name="connectTimeout" value="5" />
</bean>

<!--
    Fuse/Karaf exports this service from fuse-pax-transx-tm-narayana bundle
-->
<reference id="tm" interface="javax.transaction.TransactionManager" />

<!--
    Non database-specific, generic, pooling, enlisting javax.sql.DataSource
-->
<bean id="pool" class="org.apache.commons.dbcp2.managed.BasicManagedDataSource">
    <property name="xaDataSourceInstance" ref="postgresql" />
    <property name="transactionManager" ref="tm" />
    <property name="minIdle" value="3" />
    <property name="maxTotal" value="10" />
    <property name="validationQuery" value="select schema_name, schema_owner from information_schema.schemata" />
</bean>

<!--
    Expose datasource to use by application code (like Camel, Spring, ...)
-->
<service interface="javax.sql.DataSource" ref="pool">
    <service-properties>
        <entry key="osgi.jndi.service.name" value="jdbc/postgresql" />
    </service-properties>
</service>

위의 블루프린트 XML 조각은 표준 데이터 소스 예 과 일치합니다. 다음은 사용 방법을 보여주는 쉘 명령은 다음과 같습니다.

karaf@root()> install -s mvn:org.postgresql/postgresql/42.2.5
Bundle ID: 233
karaf@root()> install -s mvn:org.apache.commons/commons-pool2/2.5.0
Bundle ID: 224
karaf@root()> install -s mvn:org.apache.commons/commons-dbcp2/2.1.1
Bundle ID: 225
karaf@root()> install -s blueprint:file://$PQ_HOME/databases/blueprints/postgresql-manual.xml
Bundle ID: 226

karaf@root()> bundle:services -p 226

Bundle 226 provides:
--------------------
objectClass = [javax.sql.DataSource]
osgi.jndi.service.name = jdbc/postgresql
osgi.service.blueprint.compname = pool
service.bundleid = 226
service.id = 242
service.scope = bundle
-----
objectClass = [org.osgi.service.blueprint.container.BlueprintContainer]
osgi.blueprint.container.symbolicname = postgresql-manual.xml
osgi.blueprint.container.version = 0.0.0
service.bundleid = 226
service.id = 243
service.scope = singleton

karaf@root()> feature:install jdbc

karaf@root()> jdbc:ds-list
Name            │ Product    │ Version                       │ URL                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           │ Status
────────────────┼────────────┼───────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────
jdbc/postgresql │ PostgreSQL │ 10.3 (Debian 10.3-1.pgdg90+1) │ jdbc:postgresql://localhost:5432/reportdb?prepareThreshold=5&preparedStatementCacheQueries=256&preparedStatementCacheSizeMiB=5&databaseMetadataCacheFields=65536&databaseMetadataCacheFieldsMiB=5&defaultRowFetchSize=0&binaryTransfer=true&readOnly=false&binaryTransferEnable=&binaryTransferDisable=&unknownLength=2147483647&logUnclosedConnections=false&disableColumnSanitiser=false&tcpKeepAlive=false&loginTimeout=0&connectTimeout=5&socketTimeout=0&cancelSignalTimeout=10&receiveBufferSize=-1&sendBufferSize=-1&ApplicationName=PostgreSQL JDBC Driver&jaasLogin=true&useSpnego=false&gsslib=auto&sspiServiceClass=POSTGRES&allowEncodingChanges=false&currentSchema=report&targetServerType=any&loadBalanceHosts=false&hostRecheckSeconds=10&preferQueryMode=extended&autosave=never&reWriteBatchedInserts=false │ OK

karaf@root()> jdbc:query jdbc/postgresql 'select * from incident';
date                │ summary    │ name   │ details                       │ id │ email
────────────────────┼────────────┼────────┼───────────────────────────────┼────┼─────────────────
2018-02-20 08:00:00 │ Incident 1 │ User 1 │ This is a report incident 001 │ 1  │ user1@redhat.com
2018-02-20 08:10:00 │ Incident 2 │ User 2 │ This is a report incident 002 │ 2  │ user2@redhat.com
2018-02-20 08:20:00 │ Incident 3 │ User 3 │ This is a report incident 003 │ 3  │ user3@redhat.com
2018-02-20 08:30:00 │ Incident 4 │ User 4 │ This is a report incident 004 │ 4  │ user4@redhat.com

위 목록에 표시된 것처럼 블루프린트 번은 일반적인 데이터베이스 별 연결 풀인 javax.sql.DataSource 서비스를 내보냅니다. 블루프린트 XML에는 명시적인 <service ref="postgresql" > 선언이 없기 때문에 데이터베이스별 javax.sql.XADataSource 번들은 OSGi 서비스로 등록되지 않습니다.

6.8.2. 데이터 소스 팩토리 배포

데이터 소스의 팩토리 배포는 표준 방식으로 pax-jdbc-config 번들을 사용합니다. Fuse 6.x에서 권장되는 방법과 약간 다릅니다. 이 방법은 풀링 구성을 서비스 속성으로 지정해야 합니다.

블루프린트 XML 예제는 다음과 같습니다.

<!--
    A database-specific org.osgi.service.jdbc.DataSourceFactory that can create DataSource/XADataSource/
    /ConnectionPoolDataSource/Driver using properties. It is registered by pax-jdbc-* or for example
    mvn:org.postgresql/postgresql/42.2.5 bundle natively.
-->
<reference id="dataSourceFactory"
        interface="org.osgi.service.jdbc.DataSourceFactory"
        filter="(osgi.jdbc.driver.class=org.postgresql.Driver)" />

<!--
    Non database-specific org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory that can create
    pooled data sources using some org.osgi.service.jdbc.DataSourceFactory. dbcp2 pool is registered
    by pax-jdbc-pool-dbcp2 bundle.
-->
<reference id="pooledDataSourceFactory"
        interface="org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory"
        filter="(&amp;(pool=dbcp2)(xa=true))" />

<!--
    Finally, use both factories to expose pooled, xa-aware data source.
-->
<bean id="pool" factory-ref="pooledDataSourceFactory" factory-method="create">
    <argument ref="dataSourceFactory" />
    <argument>
        <props>
            <!--
                Properties needed by postgresql-specific org.osgi.service.jdbc.DataSourceFactory.
                Cannot prepend them with 'jdbc.' prefix as the DataSourceFactory is implemented directly
                by PostgreSQL driver, not by pax-jdbc-* bundle.
            -->
            <prop key="url" value="jdbc:postgresql://localhost:5432/reportdb" />
            <prop key="user" value="fuse" />
            <prop key="password" value="fuse" />
            <prop key="currentSchema" value="report" />
            <prop key="connectTimeout" value="5" />
            <!-- Properties needed by dbcp2-specific org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory -->
            <prop key="pool.minIdle" value="2" />
            <prop key="pool.maxTotal" value="10" />
            <prop key="pool.blockWhenExhausted" value="true" />
            <prop key="pool.maxWaitMillis" value="2000" />
            <prop key="pool.testOnBorrow" value="true" />
            <prop key="pool.testWhileIdle" value="false" />
            <prop key="factory.validationQuery" value="select schema_name from information_schema.schemata" />
            <prop key="factory.validationQueryTimeout" value="2" />
        </props>
    </argument>
</bean>

<!--
    Expose data source for use by application code (such as  Camel, Spring, ...).
-->
<service interface="javax.sql.DataSource" ref="pool">
    <service-properties>
        <entry key="osgi.jndi.service.name" value="jdbc/postgresql" />
    </service-properties>
</service>

이 예에서는 데이터 소스 팩토리 를 사용하여 데이터 소스를 생성하는 팩토리 빈을 사용합니다. XA 인식 PooledDataSourceFactory 에서 내부적으로 추적하므로 javax. Cryostat.TransactionManager 서비스를 명시적으로 참조할 필요가 없습니다.

다음은 동일한 예이지만 Fuse/Karaf 쉘에서는 다음과 같습니다.

참고

네이티브 org.osgi.service.jdbc.DataSourcFactory 번들을 등록하려면 mvn:org.osgi/org.osgi.service.jdbc/1.0.0 을 설치한 다음 PostgreSQL 드라이버를 설치합니다.

karaf@root()> feature:install jdbc pax-jdbc-config pax-jdbc-pool-dbcp2
karaf@root()> install -s mvn:org.postgresql/postgresql/42.2.5
Bundle ID: 232
karaf@root()> install -s blueprint:file://$PQ_HOME/databases/blueprints/postgresql-pax-jdbc-factory-dbcp2.xml
Bundle ID: 233
karaf@root()> bundle:services -p 233

Bundle 233 provides:
--------------------
objectClass = [javax.sql.DataSource]
osgi.jndi.service.name = jdbc/postgresql
osgi.service.blueprint.compname = pool
service.bundleid = 233
service.id = 336
service.scope = bundle
-----
objectClass = [org.osgi.service.blueprint.container.BlueprintContainer]
osgi.blueprint.container.symbolicname = postgresql-pax-jdbc-factory-dbcp2.xml
osgi.blueprint.container.version = 0.0.0
service.bundleid = 233
service.id = 337
service.scope = singleton

karaf@root()> jdbc:ds-list
Name            │ Product    │ Version                       │ URL                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           │ Status
────────────────┼────────────┼───────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────
jdbc/postgresql │ PostgreSQL │ 10.3 (Debian 10.3-1.pgdg90+1) │ jdbc:postgresql://localhost:5432/reportdb?prepareThreshold=5&preparedStatementCacheQueries=256&preparedStatementCacheSizeMiB=5&databaseMetadataCacheFields=65536&databaseMetadataCacheFieldsMiB=5&defaultRowFetchSize=0&binaryTransfer=true&readOnly=false&binaryTransferEnable=&binaryTransferDisable=&unknownLength=2147483647&logUnclosedConnections=false&disableColumnSanitiser=false&tcpKeepAlive=false&loginTimeout=0&connectTimeout=5&socketTimeout=0&cancelSignalTimeout=10&receiveBufferSize=-1&sendBufferSize=-1&ApplicationName=PostgreSQL JDBC Driver&jaasLogin=true&useSpnego=false&gsslib=auto&sspiServiceClass=POSTGRES&allowEncodingChanges=false&currentSchema=report&targetServerType=any&loadBalanceHosts=false&hostRecheckSeconds=10&preferQueryMode=extended&autosave=never&reWriteBatchedInserts=false │ OK

karaf@root()> jdbc:query jdbc/postgresql 'select * from incident';
date                │ summary    │ name   │ details                       │ id │ email
────────────────────┼────────────┼────────┼───────────────────────────────┼────┼─────────────────
2018-02-20 08:00:00 │ Incident 1 │ User 1 │ This is a report incident 001 │ 1  │ user1@redhat.com
2018-02-20 08:10:00 │ Incident 2 │ User 2 │ This is a report incident 002 │ 2  │ user2@redhat.com
2018-02-20 08:20:00 │ Incident 3 │ User 3 │ This is a report incident 003 │ 3  │ user3@redhat.com
2018-02-20 08:30:00 │ Incident 4 │ User 4 │ This is a report incident 004 │ 4  │ user4@redhat.com

위 목록에 표시된 것처럼 블루프린트 번은 일반적인 데이터베이스 별 연결 풀인 javax.sql.DataSource 서비스를 내보냅니다. 블루프린트 XML에는 명시적인 <service ref="postgresql" > 선언이 없기 때문에 데이터베이스별 javax.sql.XADataSource 는 OSGi 서비스로 등록되지 않습니다.

6.8.3. 데이터 소스 혼합 배포

데이터 소스 혼합 배포에서 pax-jdbc-config 1.3.0 번들은 서비스 속성을 사용하여 데이터베이스별 데이터 소스를 풀링 데이터 소스 내에 래핑 하는 또 다른 방법을 추가합니다. 이 방법은 Fuse 6.x에서 작동하는 방식과 일치합니다.

블루프린트 XML 예제는 다음과 같습니다.

<!--
    Database-specific, non-pooling, non-enlisting javax.sql.XADataSource
-->
<bean id="postgresql" class="org.postgresql.xa.PGXADataSource">
    <property name="url" value="jdbc:postgresql://localhost:5432/reportdb" />
    <property name="user" value="fuse" />
    <property name="password" value="fuse" />
    <property name="currentSchema" value="report" />
    <property name="connectTimeout" value="5" />
</bean>

<!--
    Expose database-specific data source with service properties.
    No need to expose pooling, enlisting, non database-specific javax.sql.DataSource. It is registered
    automatically by pax-jdbc-config with the same properties as this <service>, but with higher service.ranking.
-->
<service id="pool" ref="postgresql" interface="javax.sql.XADataSource">
    <service-properties>
        <!-- "pool" key is needed for pax-jdbc-config to wrap database-specific data source inside connection pool -->
        <entry key="pool" value="dbcp2" />
        <entry key="osgi.jndi.service.name" value="jdbc/postgresql" />
        <!-- Other properties that configure given connection pool, as indicated by pool=dbcp2 -->
        <entry key="pool.minIdle" value="2" />
        <entry key="pool.maxTotal" value="10" />
        <entry key="pool.blockWhenExhausted" value="true" />
        <entry key="pool.maxWaitMillis" value="2000" />
        <entry key="pool.testOnBorrow" value="true" />
        <entry key="pool.testWhileIdle" value="false" />
        <entry key="factory.validationQuery" value="select schema_name from information_schema.schemata" />
        <entry key="factory.validationQueryTimeout" value="2" />
    </service-properties>
</service>

위의 예에서는 데이터베이스별 데이터 소스만 수동으로 등록됩니다. pool=dbcp2 service 속성은 pax-jdbc-config 번들에서 관리하는 데이터 소스 추적기에 대한 힌트입니다. 이 서비스 속성이 있는 데이터 소스 서비스는 풀링 데이터 소스(이 예에서는 pax-jdbc-pool-dbcp2 ) 내에서 래핑됩니다.

Fuse/Karaf 쉘의 예는 다음과 같습니다.

karaf@root()> feature:install jdbc pax-jdbc-config pax-jdbc-pool-dbcp2
karaf@root()> install -s mvn:org.postgresql/postgresql/42.2.5
Bundle ID: 232
karaf@root()> install -s blueprint:file://$PQ_HOME/databases/blueprints/postgresql-pax-jdbc-discovery.xml
Bundle ID: 233
karaf@root()> bundle:services -p 233

Bundle 233 provides:
--------------------
factory.validationQuery = select schema_name from information_schema.schemata
factory.validationQueryTimeout = 2
objectClass = [javax.sql.XADataSource]
osgi.jndi.service.name = jdbc/postgresql
osgi.service.blueprint.compname = postgresql
pool = dbcp2
pool.blockWhenExhausted = true
pool.maxTotal = 10
pool.maxWaitMillis = 2000
pool.minIdle = 2
pool.testOnBorrow = true
pool.testWhileIdle = false
service.bundleid = 233
service.id = 336
service.scope = bundle
-----
objectClass = [org.osgi.service.blueprint.container.BlueprintContainer]
osgi.blueprint.container.symbolicname = postgresql-pax-jdbc-discovery.xml
osgi.blueprint.container.version = 0.0.0
service.bundleid = 233
service.id = 338
service.scope = singleton

karaf@root()> service:list javax.sql.XADataSource
[javax.sql.XADataSource]
------------------------
 factory.validationQuery = select schema_name from information_schema.schemata
 factory.validationQueryTimeout = 2
 osgi.jndi.service.name = jdbc/postgresql
 osgi.service.blueprint.compname = postgresql
 pool = dbcp2
 pool.blockWhenExhausted = true
 pool.maxTotal = 10
 pool.maxWaitMillis = 2000
 pool.minIdle = 2
 pool.testOnBorrow = true
 pool.testWhileIdle = false
 service.bundleid = 233
 service.id = 336
 service.scope = bundle
Provided by :
 Bundle 233
Used by:
 OPS4J Pax JDBC Config (224)

karaf@root()> service:list javax.sql.DataSource
[javax.sql.DataSource]
----------------------
 factory.validationQuery = select schema_name from information_schema.schemata
 factory.validationQueryTimeout = 2
 osgi.jndi.service.name = jdbc/postgresql
 osgi.service.blueprint.compname = postgresql
 pax.jdbc.managed = true
 pax.jdbc.service.id.ref = 336
 pool.blockWhenExhausted = true
 pool.maxTotal = 10
 pool.maxWaitMillis = 2000
 pool.minIdle = 2
 pool.testOnBorrow = true
 pool.testWhileIdle = false
 service.bundleid = 224
 service.id = 337
 service.ranking = 1000
 service.scope = singleton
Provided by :
 OPS4J Pax JDBC Config (224)

karaf@root()> jdbc:ds-list
Name            │ Product    │ Version                       │ URL                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           │ Status
────────────────┼────────────┼───────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────
jdbc/postgresql │ PostgreSQL │ 10.3 (Debian 10.3-1.pgdg90+1) │ jdbc:postgresql://localhost:5432/reportdb?prepareThreshold=5&preparedStatementCacheQueries=256&preparedStatementCacheSizeMiB=5&databaseMetadataCacheFields=65536&databaseMetadataCacheFieldsMiB=5&defaultRowFetchSize=0&binaryTransfer=true&readOnly=false&binaryTransferEnable=&binaryTransferDisable=&unknownLength=2147483647&logUnclosedConnections=false&disableColumnSanitiser=false&tcpKeepAlive=false&loginTimeout=0&connectTimeout=5&socketTimeout=0&cancelSignalTimeout=10&receiveBufferSize=-1&sendBufferSize=-1&ApplicationName=PostgreSQL JDBC Driver&jaasLogin=true&useSpnego=false&gsslib=auto&sspiServiceClass=POSTGRES&allowEncodingChanges=false&currentSchema=report&targetServerType=any&loadBalanceHosts=false&hostRecheckSeconds=10&preferQueryMode=extended&autosave=never&reWriteBatchedInserts=false │ OK
jdbc/postgresql │ PostgreSQL │ 10.3 (Debian 10.3-1.pgdg90+1) │ jdbc:postgresql://localhost:5432/reportdb?prepareThreshold=5&preparedStatementCacheQueries=256&preparedStatementCacheSizeMiB=5&databaseMetadataCacheFields=65536&databaseMetadataCacheFieldsMiB=5&defaultRowFetchSize=0&binaryTransfer=true&readOnly=false&binaryTransferEnable=&binaryTransferDisable=&unknownLength=2147483647&logUnclosedConnections=false&disableColumnSanitiser=false&tcpKeepAlive=false&loginTimeout=0&connectTimeout=5&socketTimeout=0&cancelSignalTimeout=10&receiveBufferSize=-1&sendBufferSize=-1&ApplicationName=PostgreSQL JDBC Driver&jaasLogin=true&useSpnego=false&gsslib=auto&sspiServiceClass=POSTGRES&allowEncodingChanges=false&currentSchema=report&targetServerType=any&loadBalanceHosts=false&hostRecheckSeconds=10&preferQueryMode=extended&autosave=never&reWriteBatchedInserts=false │ OK

karaf@root()> jdbc:query jdbc/postgresql 'select * from incident'
date                │ summary    │ name   │ details                       │ id │ email
────────────────────┼────────────┼────────┼───────────────────────────────┼────┼─────────────────
2018-02-20 08:00:00 │ Incident 1 │ User 1 │ This is a report incident 001 │ 1  │ user1@redhat.com
2018-02-20 08:10:00 │ Incident 2 │ User 2 │ This is a report incident 002 │ 2  │ user2@redhat.com
2018-02-20 08:20:00 │ Incident 3 │ User 3 │ This is a report incident 003 │ 3  │ user3@redhat.com
2018-02-20 08:30:00 │ Incident 4 │ User 4 │ This is a report incident 004 │ 4  │ user4@redhat.com

이 목록에는 jdbc:ds-list 출력에서 볼 수 있듯이 개의 데이터 소스, 원래 데이터 소스 및 래퍼 데이터 소스가 있습니다.

javax.sql.XADataSource 는 블루프린트 번들에서 등록되며 pool = dbcp2 속성이 선언되어 있습니다.

javax.sql.DataSourcepax-jdbc-config 번들에서 등록되어 있습니다.

  • pool = dbcp2 속성이 없습니다( 래퍼 데이터 소스를 등록할 때 제거됨).
  • service.ranking = 1000 속성이 있으므로 예를 들어 이름으로 데이터 소스를 찾을 때 기본 설정 버전입니다.
  • pax.jdbc.managed = true 속성이 있으므로 다시 래핑하려고 시도하지 않습니다.
  • 연결 풀 내에서 래핑된 원래 데이터 소스 서비스를 표시하기 위해 pax.jdbc.service.id.ref = 336 속성이 있습니다.

6.9. Java™ 지속성 API로 데이터 소스 사용

트랜잭션 관리 관점에서 JPA(Java™ Persistence API)와 함께 데이터 소스를 사용하는 방법을 이해하는 것이 중요합니다. 이 섹션에서는 JPA 사양 자체의 세부 정보 또는 가장 알려진 JPA 구현인 Hibernate에 대한 세부 정보를 설명하지 않습니다. 대신 이 섹션에서는 JPA 영구 단위를 데이터 소스에 가리키는 방법을 보여줍니다.

6.9.1. 데이터 소스 참조 정보

META-INF/persistence.xml 설명자( JPA 2.1 사양 참조, 8.2.1.5 jta-data-source, non-jta-data-source)는 다음 두 가지 종류의 데이터 소스 참조를 정의합니다.

  • <JTA-data-source > - JTA 트랜잭션과 함께 사용할 JTA 사용 데이터 소스에 대한 JNDI 참조입니다.
  • <non-jta-data-source > - JTA 트랜잭션 외부에서 사용할 JTA 사용 데이터 소스에 대한 JNDI 참조입니다. 이 데이터 소스는 일반적으로 초기화 단계에서도 사용됩니다. 예를 들어, Hibernate를 자동 생성 데이터베이스 스키마로 구성하는 hibernate.hbm2ddl.auto 속성도 사용됩니다.

이 두 데이터 소스는 javax.sql.DataSource 또는 javax.sql.XADataSource 와 관련이 없습니다! 이는 JPA 애플리케이션을 개발할 때 일반적으로 잘못된 오류입니다. 두 JNDI 이름은 모두 JNDI 바인딩된 javax.sql.DataSource 서비스를 참조해야 합니다.

6.9.2. JNDI 이름 참조

osgi.jndi.service.name 속성에 OSGi 서비스를 등록하면 OSGi JNDI 서비스에 바인딩 됩니다. OSGi 런타임(예: Fuse/Karaf)에서 JNDI는 이름 → 값 쌍으로 구성된 간단한 사전이 아닙니다. OSGi의 JNDI 이름을 사용하여 오브젝트를 참조하려면 서비스 조회 및 서비스 후크와 같은 기타 더 복잡한 OSGi 메커니즘이 포함됩니다.

다음 목록에서는 새로운 Fuse 설치에서 JNDI에 데이터 소스를 등록하는 방법을 보여줍니다.

karaf@root()> install -s mvn:mysql/mysql-connector-java/5.1.34
Bundle ID: 223
karaf@root()> install -s mvn:org.osgi/org.osgi.service.jdbc/1.0.0
Bundle ID: 224
karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc-mysql/1.3.0
Bundle ID: 225
karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc/1.3.0
Bundle ID: 226
karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc-pool-common/1.3.0
Bundle ID: 227
karaf@root()> install -s mvn:org.ops4j.pax.jdbc/pax-jdbc-config/1.3.0
Bundle ID: 228

karaf@root()> config:edit --factory --alias mysql org.ops4j.datasource
karaf@root()> config:property-set osgi.jdbc.driver.name mysql
karaf@root()> config:property-set dataSourceName mysqlds
karaf@root()> config:property-set osgi.jndi.service.name jdbc/mysqlds
karaf@root()> config:property-set dataSourceType DataSource
karaf@root()> config:property-set jdbc.url jdbc:mysql://localhost:3306/reportdb
karaf@root()> config:property-set jdbc.user fuse
karaf@root()> config:property-set jdbc.password fuse
karaf@root()> config:property-set jdbc.useSSL false
karaf@root()> config:update

karaf@root()> feature:install jndi

karaf@root()> jndi:names
JNDI Name                 │ Class Name
──────────────────────────┼───────────────────────────────────────────────
osgi:service/jndi         │ org.apache.karaf.jndi.internal.JndiServiceImpl
osgi:service/jdbc/mysqlds │ com.mysql.jdbc.jdbc2.optional.MysqlDataSource

표시된 대로 데이터 소스는 osgi:service/jdbc/mysqlds JNDI 이름에서 사용할 수 있습니다.

그러나 OSGi에서 JPA의 경우 전체 JNDI 이름을 사용해야 합니다. 다음은 데이터 소스 참조를 지정하는 샘플 META-INF/persistence.xml 조각입니다.

<jta-data-source>
    osgi:service/javax.sql.DataSource/(osgi.jndi.service.name=jdbc/mysqlds)
</jta-data-source>
<non-jta-data-source>
    osgi:service/javax.sql.DataSource/(osgi.jndi.service.name=jdbc/mysqlds)
</non-jta-data-source>

위의 구성이 없으면 다음과 같은 오류가 발생할 수 있습니다.

Persistence unit "pu-name" refers to a non OSGi service DataSource

7장. JMS 연결 팩토리 사용

이 장에서는 OSGi에서 JMS 연결 팩토리를 사용하는 방법을 설명합니다. 기본적으로 다음을 사용하여 수행합니다.

org.osgi.framework.BundleContext.registerService(javax.jms.ConnectionFactory.class,
                                                 connectionFactoryObject,
                                                 properties);
org.osgi.framework.BundleContext.registerService(javax.jms.XAConnectionFactory.class,
                                                 xaConnectionFactoryObject,
                                                 properties);

이러한 서비스를 등록하는 방법은 다음 두 가지가 있습니다.

  • jms:create Karaf console 명령을 사용하여 연결 팩토리 게시. 구성 방법입니다.
  • 블루프린트, OSGi 선언 서비스(SCR) 또는 BundleContext.registerService() API 호출과 같은 방법을 사용하여 연결 팩토리 게시. 이 메서드에는 코드 및/또는 메타데이터가 포함된 전용 OSGi 번들이 필요합니다. 배포 방법입니다.

자세한 내용은 다음 항목에 있습니다.

7.1. OSGi JMS 서비스 정보

JDBC 데이터 소스를 처리하는 OSGi 방법은 다음 두 인터페이스와 관련이 있습니다.

  • standard org.osgi.service.jdbc.DataSourceFactory
  • proprietary org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory

JMS의 경우 다음 유추를 고려하십시오.

  • 표준 OSGi JDBC org. osgi.service.jdbc.DataSourcefactory와 동일한 목적을 가진 전용 org.ops4j.pax.jms.service.ConnectionFactoryfactory
  • 독점 org.ops4j.pax.jms.service.PooledConnectionFactory factory 전용 pax-jdbc org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory와 동일한 용도

전용 브로커별 org.ops4j.pax.jms.service.ConnectionFactoryFactory 구현의 경우 다음과 같은 번들이 있습니다.

  • mvn:org.ops4j.pax.jms/pax-jms-artemis/1.0.0
  • mvn:org.ops4j.pax.jms/pax-jms-ibmmq/1.0.0
  • mvn:org.ops4j.pax.jms/pax-jms-activemq/1.0.0

이러한 번들은 javax. jms.ConnectionFactory Factory 및 javax.jms.XAConnectionFactory 와 같은 JMS 팩토리 를 반환할 수 있는 브로커별 org.ops4j.pax.jms.service.ConnectionFactoryfactory 서비스를 등록합니다. 예를 들면 다음과 같습니다.

karaf@root()> feature:install pax-jms-artemis

karaf@root()> bundle:services -p org.ops4j.pax.jms.pax-jms-config

OPS4J Pax JMS Config (248) provides:
------------------------------------
objectClass = [org.osgi.service.cm.ManagedServiceFactory]
service.bundleid = 248
service.id = 328
service.pid = org.ops4j.connectionfactory
service.scope = singleton

karaf@root()> bundle:services -p org.ops4j.pax.jms.pax-jms-artemis

OPS4J Pax JMS Artemis Support (247) provides:
---------------------------------------------
objectClass = [org.ops4j.pax.jms.service.ConnectionFactoryFactory]
service.bundleid = 247
service.id = 327
service.scope = singleton
type = artemis

7.2. PAX-JMS 구성 서비스 정보

mvn:org.ops4j.pax.jms/pax-jms-config/1.0.0 번들에서는 다음 세 가지 작업을 수행하는 관리형 서비스 Cryostat를 제공합니다.

  • org.ops4j.pax.jms.service.ConnectionFactoryFactory OSGi 서비스를 추적하여 해당 메서드를 호출합니다.

    public ConnectionFactory createConnectionFactory(Map<String, Object> properties);
    
    public XAConnectionFactory createXAConnectionFactory(Map<String, Object> properties);
  • 위의 방법에 필요한 속성을 수집하기 위해 org.ops4j.connection factory 팩토리 PID 를 추적합니다. 예를 들어 ${karaf.etc}/org.ops4j.connectionfactory-artemis.cfg 파일을 생성하여 구성 관리 서비스에 사용할 수 있는 방법을 사용하여 팩토리 구성을 생성하는 경우 최종 단계를 수행하여 브로커별 연결 팩토리를 노출할 수 있습니다.
  • javax.jms.ConnectionFactoryjavax.jms.XAConnectionFactory 서비스를 추적하여 JMS 연결 팩토리 풀링 내에서 래핑합니다.

자세한 내용은 다음 항목에 있습니다.

7.2.1. AMQ 7.1용 연결 팩토리 생성

다음은 Artemis 브로커에 대한 연결 요소를 만들기 위한 세부적이고 표준 적인 단계별 가이드입니다.

  1. pax-jms-artemis 기능 및 pax-jms-config 기능을 사용하여 Artemis 드라이버를 설치합니다.

    karaf@root()> feature:install pax-jms-artemis
    
    karaf@root()> bundle:services -p org.ops4j.pax.jms.pax-jms-config
    
    OPS4J Pax JMS Config (248) provides:
    ------------------------------------
    objectClass = [org.osgi.service.cm.ManagedServiceFactory]
    service.bundleid = 248
    service.id = 328
    service.pid = org.ops4j.connectionfactory
    service.scope = singleton
    
    karaf@root()> bundle:services -p org.ops4j.pax.jms.pax-jms-artemis
    
    OPS4J Pax JMS Artemis Support (247) provides:
    ---------------------------------------------
    objectClass = [org.ops4j.pax.jms.service.ConnectionFactoryFactory]
    service.bundleid = 247
    service.id = 327
    service.scope = singleton
    type = artemis
  2. 팩토리 구성 을 생성합니다.

    karaf@root()> config:edit --factory --alias artemis org.ops4j.connectionfactory
    karaf@root()> config:property-set type artemis
    karaf@root()> config:property-set osgi.jndi.service.name jms/artemis # "name" property may be used too
    karaf@root()> config:property-set connectionFactoryType ConnectionFactory # or XAConnectionFactory
    karaf@root()> config:property-set jms.url tcp://localhost:61616
    karaf@root()> config:property-set jms.user admin
    karaf@root()> config:property-set jms.password admin
    karaf@root()> config:property-set jms.consumerMaxRate 1234
    karaf@root()> config:update
    
    karaf@root()> config:list '(service.factoryPid=org.ops4j.connectionfactory)'
    ----------------------------------------------------------------
    Pid:            org.ops4j.connectionfactory.965d4eac-f5a7-4f65-ba1a-15caa4c72703
    FactoryPid:     org.ops4j.connectionfactory
    BundleLocation: ?
    Properties:
       connectionFactoryType = ConnectionFactory
       felix.fileinstall.filename = file:${karar.etc}/org.ops4j.connectionfactory-artemis.cfg
       jms.consumerMaxRate = 1234
       jms.password = admin
       jms.url = tcp://localhost:61616
       jms.user = admin
       osgi.jndi.service.name = jms/artemis
       service.factoryPid = org.ops4j.connectionfactory
       service.pid = org.ops4j.connectionfactory.965d4eac-f5a7-4f65-ba1a-15caa4c72703
       type = artemis
  3. pax-jms-configjavax.jms.ConnectionFactory 서비스로 구성을 처리했는지 확인합니다.

    karaf@root()> service:list javax.jms.ConnectionFactory
    [javax.jms.ConnectionFactory]
    -----------------------------
     connectionFactoryType = ConnectionFactory
     felix.fileinstall.filename = file:${karaf.etc}/org.ops4j.connectionfactory-artemis.cfg
     jms.consumerMaxRate = 1234
     jms.password = admin
     jms.url = tcp://localhost:61616
     jms.user = admin
     osgi.jndi.service.name = jms/artemis
     pax.jms.managed = true
     service.bundleid = 248
     service.factoryPid = org.ops4j.connectionfactory
     service.id = 342
     service.pid = org.ops4j.connectionfactory.965d4eac-f5a7-4f65-ba1a-15caa4c72703
     service.scope = singleton
     type = artemis
    Provided by :
     OPS4J Pax JMS Config (248)
    참고

    추가 Artemis 구성(특히 protocol=amqp )을 지정하는 경우 Artemis JMS 클라이언트 대신 QPID JMS 라이브러리가 사용됩니다. 그런 다음 amqp:// 프로토콜을 jms.url 속성에 사용해야 합니다.

  4. 연결을 테스트합니다.

이제 필요한 경우 삽입할 수 있는 브로커별(아직 풀링 없음) 연결 팩토리가 있습니다. 예를 들어 jms 기능의 Karaf 명령을 사용할 수 있습니다.

karaf@root()> feature:install -v jms
Adding features: jms/[4.2.0.fuse-000237-redhat-1,4.2.0.fuse-000237-redhat-1]
...
karaf@root()> jms:connectionfactories
JMS Connection Factory
──────────────────────
jms/artemis

karaf@root()> jms:info -u admin -p admin jms/artemis
Property │ Value
─────────┼──────────────────────────
product  │ ActiveMQ
version  │ 2.4.0.amq-711002-redhat-1

karaf@root()> jms:send -u admin -p admin jms/artemis DEV.QUEUE.1 "Hello Artemis"

karaf@root()> jms:browse -u admin -p admin jms/artemis DEV.QUEUE.1
Message ID                              │ Content       │ Charset │ Type │ Correlation ID │ Delivery Mode │ Destination                │ Expiration │ Priority │ Redelivered │ ReplyTo │ Timestamp
────────────────────────────────────────┼───────────────┼─────────┼──────┼────────────────┼───────────────┼────────────────────────────┼────────────┼──────────┼─────────────┼─────────┼──────────────────────────────
ID:2b6ea56d-574d-11e8-971a-7ee9ecc029d4 │ Hello Artemis │ UTF-8   │      │                │ Persistent    │ ActiveMQQueue[DEV.QUEUE.1] │ Never      │ 4        │ false       │         │ Mon May 14 10:02:38 CEST 2018

다음 목록은 프로토콜을 전환할 때 발생하는 상황을 보여줍니다.

karaf@root()> config:list '(service.factoryPid=org.ops4j.connectionfactory)'
----------------------------------------------------------------
Pid:            org.ops4j.connectionfactory.965d4eac-f5a7-4f65-ba1a-15caa4c72703
FactoryPid:     org.ops4j.connectionfactory
BundleLocation: ?
Properties:
   connectionFactoryType = ConnectionFactory
   felix.fileinstall.filename = file:${karaf.etc}/org.ops4j.connectionfactory-artemis.cfg
   jms.consumerMaxRate = 1234
   jms.password = fuse
   jms.url = tcp://localhost:61616
   jms.user = fuse
   osgi.jndi.service.name = jms/artemis
   service.factoryPid = org.ops4j.connectionfactory
   service.pid = org.ops4j.connectionfactory.965d4eac-f5a7-4f65-ba1a-15caa4c72703
   type = artemis

karaf@root()> config:edit org.ops4j.connectionfactory.312eb09a-d686-4229-b7e1-2ea38a77bb0f
karaf@root()> config:property-set protocol amqp
karaf@root()> config:property-delete user
karaf@root()> config:property-set username admin # mind the difference between artemis-jms-client and qpid-jms-client
karaf@root()> config:property-set jms.url amqp://localhost:61616
karaf@root()> config:update

karaf@root()> jms:info -u admin -p admin jms/artemis
Property │ Value
─────────┼────────────────
product  │ QpidJMS
version  │ 0.30.0.redhat-1

karaf@root()> jms:browse -u admin -p admin jms/artemis DEV.QUEUE.1
Message ID │ Content       │ Charset │ Type │ Correlation ID │ Delivery Mode │ Destination │ Expiration │ Priority │ Redelivered │ ReplyTo │ Timestamp
───────────┼───────────────┼─────────┼──────┼────────────────┼───────────────┼─────────────┼────────────┼──────────┼─────────────┼─────────┼──────────────────────────────
           │ Hello Artemis │ UTF-8   │      │                │ Persistent    │ DEV.QUEUE.1 │ Never      │ 4        │ false       │         │ Mon May 14 10:02:38 CEST 2018

7.2.2. IBM MQ 8 또는 IBM MQ 9용 연결 팩토리 생성

이 섹션에서는 IBM MQ 8 및 IBM MQ 9에 연결하는 방법을 설명합니다. pax-jms-ibmmq 가 관련 pax-jms 번들을 설치하더라도 라이센스로 인해 IBM MQ 드라이버가 설치되지 않습니다.

  1. https://developer.ibm.com/messaging/mq-downloads/으로 이동합니다.
  2. 로그인합니다.
  3. 설치할 버전을 클릭합니다(예: IBM MQ 8.0 Client 또는 IBM MQ 9.0 Client ).
  4. 표시되는 페이지의 다운로드 버전 표에서 원하는 버전을 클릭합니다.
  5. 다음 페이지에서 접미사 IBM-MQ-Install-Java-All 이 있는 최신 버전을 선택합니다. 예를 들어 8.0.0.10-WS-MQ-Install-Java-All 또는 9.0.0.4-IBM-Install-Java-All를 다운로드합니다.
  6. 다운로드한 JAR 파일의 내용을 추출합니다.
  7. bundle:install 명령을 실행합니다. 예를 들어 /home/Downloads 디렉터리에 콘텐츠를 추출한 경우 다음과 같은 명령을 입력합니다.

    `bundle:install -s wrap:file:////home/Downloads/9.0.0.4-IBM-MQ-Install-Java-All/ibmmq9/wmq/JavaSE/com.ibm.mq.allclient.jar`.
  8. 다음과 같이 연결 팩토리를 생성합니다.

    1. pax-jms-ibmmq 를 설치합니다.

      karaf@root()> feature:install pax-jms-ibmmq
      
      karaf@root()> bundle:services -p org.ops4j.pax.jms.pax-jms-ibmmq
      
      OPS4J Pax JMS IBM MQ Support (239) provides:
      --------------------------------------------
      objectClass = [org.ops4j.pax.jms.service.ConnectionFactoryFactory]
      service.bundleid = 239
      service.id = 346
      service.scope = singleton
      type = ibmmq
    2. 팩토리 구성 생성:

      karaf@root()> config:edit --factory --alias ibmmq org.ops4j.connectionfactory
      karaf@root()> config:property-set type ibmmq
      karaf@root()> config:property-set osgi.jndi.service.name jms/mq9 # "name" property may be used too
      karaf@root()> config:property-set connectionFactoryType ConnectionFactory # or XAConnectionFactory
      karaf@root()> config:property-set jms.queueManager FUSEQM
      karaf@root()> config:property-set jms.hostName localhost
      karaf@root()> config:property-set jms.port 1414
      karaf@root()> config:property-set jms.transportType 1 # com.ibm.msg.client.wmq.WMQConstants.WMQ_CM_CLIENT
      karaf@root()> config:property-set jms.channel DEV.APP.SVRCONN
      karaf@root()> config:property-set jms.CCSID 1208 # com.ibm.msg.client.jms.JmsConstants.CCSID_UTF8
      karaf@root()> config:update
      
      karaf@root()> config:list '(service.factoryPid=org.ops4j.connectionfactory)'
      ----------------------------------------------------------------
      Pid:            org.ops4j.connectionfactory.eee4a757-a95d-46b8-b8b6-19aa3977d863
      FactoryPid:     org.ops4j.connectionfactory
      BundleLocation: ?
      Properties:
         connectionFactoryType = ConnectionFactory
         felix.fileinstall.filename = file:${karaf.etc}/org.ops4j.connectionfactory-ibmmq.cfg
         jms.CCSID = 1208
         jms.channel = DEV.APP.SVRCONN
         jms.hostName = localhost
         jms.port = 1414
         jms.queueManager = FUSEQM
         jms.transportType = 1
         osgi.jndi.service.name = jms/mq9
         service.factoryPid = org.ops4j.connectionfactory
         service.pid = org.ops4j.connectionfactory.eee4a757-a95d-46b8-b8b6-19aa3977d863
         type = ibmmq
    3. pax-jms-configjavax.jms.ConnectionFactory 서비스로 구성을 처리했는지 확인합니다.

      karaf@root()> service:list javax.jms.ConnectionFactory
      [javax.jms.ConnectionFactory]
      -----------------------------
       connectionFactoryType = ConnectionFactory
       felix.fileinstall.filename = file:/data/servers/7.11.1.fuse-7_11_1-00013-redhat-00003/etc/org.ops4j.connectionfactory-ibmmq.cfg
       jms.CCSID = 1208
       jms.channel = DEV.APP.SVRCONN
       jms.hostName = localhost
       jms.port = 1414
       jms.queueManager = FUSEQM
       jms.transportType = 1
       osgi.jndi.service.name = jms/mq9
       pax.jms.managed = true
       service.bundleid = 237
       service.factoryPid = org.ops4j.connectionfactory
       service.id = 347
       service.pid = org.ops4j.connectionfactory.eee4a757-a95d-46b8-b8b6-19aa3977d863
       service.scope = singleton
       type = ibmmq
      Provided by :
       OPS4J Pax JMS Config (237)
    4. 연결을 테스트합니다.

      karaf@root()> feature:install -v jms
      Adding features: jms/[4.2.0.fuse-000237-redhat-1,4.2.0.fuse-000237-redhat-1]
      ...
      karaf@root()> jms:connectionfactories
      JMS Connection Factory
      ──────────────────────
      jms/mq9
      
      karaf@root()> jms:info -u app -p fuse jms/mq9
      Property │ Value
      ─────────┼────────────────────
      product  │ IBM MQ JMS Provider
      version  │ 8.0.0.0
      
      karaf@root()> jms:send -u app -p fuse jms/mq9 DEV.QUEUE.1 "Hello IBM MQ 9"
      
      karaf@root()> jms:browse -u app -p fuse jms/mq9 DEV.QUEUE.1
      Message ID                                          │ Content                     │ Charset │ Type │ Correlation ID │ Delivery Mode │ Destination          │ Expiration │ Priority │ Redelivered │ ReplyTo │ Timestamp
      ────────────────────────────────────────────────────┼─────────────────────────────┼─────────┼──────┼────────────────┼───────────────┼──────────────────────┼────────────┼──────────┼─────────────┼─────────┼──────────────────────────────
      ID:414d512046555345514d202020202020c940f95a038b3220 │ Hello IBM MQ 9              │ UTF-8   │      │                │ Persistent    │ queue:///DEV.QUEUE.1 │ Never      │ 4        │ false       │         │ Mon May 14 10:17:01 CEST 2018

IBM MQ Explorer 또는 웹 콘솔에서 메시지가 전송되었는지 확인할 수도 있습니다.

7.2.3. Apache Karaf에서 Fuse A-MQ 6.3 Client 사용

Fuse 소프트웨어 다운로드 페이지에서 Fuse 빠른 시작을 다운로드할 수 있습니다.

빠른 시작 zip 파일의 내용을 로컬 폴더(예: quickstarts 라는 폴더 )로 추출합니다.

OSGi 번들로 퀵스타트/camel/camel-jms 예제를 빌드하고 설치할 수 있습니다. 이 번들에는 JBoss A-MQ 6.3 JMS 큐로 메시지를 전송하는 Camel 경로에 대한 블루프린트 XML 정의가 포함되어 있습니다. JBoss A-MQ 6.3 브로커를 위한 연결 팩토리를 생성하는 절차는 다음과 같습니다.

7.2.3.1. 사전 요구 사항

  • Maven 3.3.1 이상을 설치했습니다.
  • 시스템에 Red Hat Fuse가 설치되어 있어야 합니다.
  • 시스템에 JBoss A-MQ Broker 6.3이 설치되어 있어야 합니다.
  • 고객 포털에서 Karaf 빠른 시작 zip 파일을 다운로드하여 추출했습니다.

7.2.3.2. 절차

  1. quickstarts/camel/camel-jms/src/main/resources/OSGI-INF/blueprint/ 디렉터리로 이동합니다.
  2. camel-context.xml 파일의 id="jms"로 다음 8080을 바꿉니다.

        <bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
            <property name="connectionFactory">
                <reference interface="javax.jms.ConnectionFactory" />
            </property>
            <property name="transactionManager" ref="transactionManager"/>
        </bean>

    다음 섹션에서는 JBoss A-MQ 6.3 연결 팩토리를 인스턴스화합니다.

    	<bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
    	        <property name="connectionFactory" ref="activemqConnectionFactory"/>
    	        <property name="transactionManager" ref="transactionManager"/>
    	</bean>
    	<bean id="activemqConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
    		<property name="brokerURL" value="tcp://localhost:61616"/>
    		<property name="userName" value="admin"/>
    		<property name="password" value="admin"/>
    	</bean>

    JBoss A-MQ 6.3 연결 팩토리는 tcp://localhost:61616 에서 수신 대기하는 브로커에 연결하도록 구성되어 있습니다. 기본적으로 JBoss A-MQ는 IP 포트 값 61616 을 사용합니다. 연결 팩토리도 userName/password 인증 정보 admin/admin을 사용하도록 구성되어 있습니다. 이 사용자가 브로커 구도에서 활성화되어 있는지 확인하십시오 (또는 브로커 구성과 일치하도록 여기에서 이러한 설정을 사용자 지정할 수 있습니다).

  3. camel-context.xml 파일을 저장합니다.
  4. camel-jms 빠른 시작을 빌드합니다.

    $ cd quickstarts/camel/camel-jms
    $ mvn install
  5. 빠른 시작이 성공적으로 설치되면 $FUSE_HOME/ 디렉터리로 이동하여 다음 명령을 실행하여 Apache Karaf 서버에서 Fuse를 시작합니다.

    $ ./bin/fuse
  6. Fuse on Apache Karaf 인스턴스에서 activemq-client 기능 및 camel-jms 기능을 설치합니다.

    karaf@root()> feature:install activemq-client
    karaf@root()> feature:install camel-jms
  7. camel-jms 빠른 시작 번들을 설치합니다.

    karaf@root()> install -s mvn:org.jboss.fuse.quickstarts/camel-jms/{$fuseversion}

    여기서 {$fuseversion} 을 방금 빌드한 Maven 아티팩트의 실제 버전으로 바꿉니다( camel-jms quickstart README 파일을 구성).

  8. JBoss A-MQ 6.3 브로커를 시작합니다(이 작업을 위해 JBoss A-MQ 6.3 설치 필요). 다른 터미널 창을 열고 JBOSS_AMQ_63_INSTALLDIR 디렉터리로 이동합니다.

    $ cd JBOSS_AMQ_63_INSTALLDIR
    $ ./bin/amq
  9. Camel 경로가 시작된 즉시 Fuse 설치 시 work/jms/input 디렉터리가 표시됩니다. 이 빠른 시작의 src/main/data 디렉토리에서 찾은 파일을 새로 생성된 work/jms/input 디렉터리에 복사합니다.
  10. 잠시 기다렸다가 국가별로 구성된 동일한 파일이 work/jms/output 디렉토리 아래에 있습니다.

        order1.xml, order2.xml and order4.xml in work/jms/output/others
        order3.xml and order5.xml in work/jms/output/us
        order6.xml in work/jms/output/fr
  11. log:display 를 사용하여 비즈니스 로깅을 확인합니다.

        Receiving order order1.xml
    
        Sending order order1.xml to another country
    
        Done processing order1.xml

7.2.4. 처리된 속성 요약

구성 관리 팩토리 PID 의 속성은 관련 org.ops4j.pax.jms.service.ConnectionFactoryFactory 구현으로 전달됩니다.

  • ActiveMQ

    org.ops4j.pax.jms.activemq.ActiveMQConnectionFactoryFactory (JMS 1.1만 해당)

    org.apache.activemq.ActiveMQConnectionFactory.buildFromMap() 메서드에 전달되는 속성

  • Artemis

    org.ops4j.pax.jms.artemis.ArtemisConnectionFactoryFactory

    protocol=amqp 인 경우 속성은 org.apache.qpid.jms.jms.jms.jms. jms.JmsConnectionFactory 인스턴스를 구성하기 위해 org.apache.qpid.util.PropertyUtil. setProperties() 메서드로 전달됩니다.

    그렇지 않으면 org.apache.activemq.artemis.utils.uri.BeanSupport.setData()org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory 인스턴스에 대해 호출됩니다.

  • IBM MQ

    org.ops4j.pax.jms.ibmmq.MQConnectionFactoryFactory

    com.ibm.mq.jms.MQConnectionFactory 또는 com.ibm.mq.jms.MQXAConnectionFactory 의 Cryostat 속성이 처리됩니다.

7.3. JMS 콘솔 명령 사용

Apache Karaf는 jms:* 범위에 쉘 명령을 포함하는 jms 기능을 제공합니다. 수동으로 구성된 연결 팩토리를 확인하기 위해 이러한 명령을 사용하는 몇 가지 예를 이미 확인했습니다. 구성 관리 구성을 생성할 필요성을 숨기는 명령도 있습니다.

새로운 Fuse 인스턴스부터 브로커별 연결 팩토리를 등록할 수 있습니다. 다음 목록에서는 Karaf에서 jms 기능 설치 및 pax-jms -artemis 의 설치를 보여줍니다.

karaf@root()> feature:install jms pax-jms-artemis

karaf@root()> jms:connectionfactories
JMS Connection Factory
──────────────────────
karaf@root()> service:list javax.jms.ConnectionFactory # should be empty

karaf@root()> service:list org.ops4j.pax.jms.service.ConnectionFactoryFactory
[org.ops4j.pax.jms.service.ConnectionFactoryFactory]
----------------------------------------------------
 service.bundleid = 250
 service.id = 326
 service.scope = singleton
 type = artemis
Provided by :
 OPS4J Pax JMS Artemis Support (250)

다음 목록은 Artemis 연결 팩토리를 생성하고 확인하는 방법을 보여줍니다.

karaf@root()> jms:create -t artemis -u admin -p admin --url tcp://localhost:61616 artemis

karaf@root()> jms:connectionfactories
JMS Connection Factory
──────────────────────
jms/artemis

karaf@root()> jms:info -u admin -p admin jms/artemis
Property │ Value
─────────┼──────────────────────────
product  │ ActiveMQ
version  │ 2.4.0.amq-711002-redhat-1

karaf@root()> jms:send -u admin -p admin jms/artemis DEV.QUEUE.1 "Hello Artemis"

karaf@root()> jms:browse -u admin -p admin jms/artemis DEV.QUEUE.1
Message ID                              │ Content       │ Charset │ Type │ Correlation ID │ Delivery Mode │ Destination                │ Expiration │ Priority │ Redelivered │ ReplyTo │ Timestamp
────────────────────────────────────────┼───────────────┼─────────┼──────┼────────────────┼───────────────┼────────────────────────────┼────────────┼──────────┼─────────────┼─────────┼──────────────────────────────
ID:7a944470-574f-11e8-918e-7ee9ecc029d4 │ Hello Artemis │ UTF-8   │      │                │ Persistent    │ ActiveMQQueue[DEV.QUEUE.1] │ Never      │ 4        │ false       │         │ Mon May 14 10:19:10 CEST 2018

karaf@root()> config:list '(service.factoryPid=org.ops4j.connectionfactory)'
----------------------------------------------------------------
Pid:            org.ops4j.connectionfactory.9184db6f-cb5f-4fd7-b5d7-a217090473ad
FactoryPid:     org.ops4j.connectionfactory
BundleLocation: mvn:org.ops4j.pax.jms/pax-jms-config/1.0.0
Properties:
   name = artemis
   osgi.jndi.service.name = jms/artemis
   password = admin
   service.factoryPid = org.ops4j.connectionfactory
   service.pid = org.ops4j.connectionfactory.9184db6f-cb5f-4fd7-b5d7-a217090473ad
   type = artemis
   url = tcp://localhost:61616
   user = admin

위 화면과 같이 org.ops4j.connectionfactory 팩토리 PID가 생성됩니다. 그러나 config:update 를 사용하면 ${karaf.etc} 에 자동으로 저장되지 않습니다. 다른 속성을 지정할 수는 없지만 나중에 추가할 수 있습니다.

7.4. 암호화된 구성 값 사용

pax-jdbc-config 번들과 마찬가지로 Jasypt를 사용하여 속성을 암호화할 수 있습니다.

별칭 서비스 속성으로 OSGi에 등록된 org.jasypt.encryption.StringEncryptor 서비스가 있는 경우 연결 팩토리 PID 에서 참조하고 암호화된 암호를 사용할 수 있습니다. 다음은 예제입니다.

felix.fileinstall.filename = */etc/org.ops4j.connectionfactory-artemis.cfg
name = artemis
type = artemis
decryptor = my-jasypt-decryptor
url = tcp://localhost:61616
user = fuse
password = ENC(<encrypted-password>)

암호 해독기 서비스를 찾는 데 사용되는 서비스 필터는 (&(objectClass=org.jasypt.encryption.StringEncryptor)(alias=<alias>) 입니다. 여기서 < alias >는 연결 팩토리 구성 팩토리 PID암호 해독 속성 값입니다.

7.5. JMS 연결 풀 사용

이 섹션에서는 JMS 연결/세션 풀링 옵션에 대해 설명합니다. JDBC에 대한 선택 사항보다 적은 선택 사항이 있습니다. 정보는 다음 주제로 구성됩니다.

중요

XA 복구를 사용하려면 pax-jms-pool-transx 또는 pax-jms-pool-narayana 연결 풀 모듈을 사용해야 합니다.

7.5.1. JMS 연결 풀 사용 소개

지금까지 브로커별 연결 팩토리 를 등록했습니다. 연결 팩토리 자체는 연결 팩토리 의 팩토리이기 때문에 org.ops4j.pax.jms.service.ConnectionFactoryFactory 서비스는 메타 팩토리 로 취급될 수 있습니다. 두 종류의 연결 팩토리를 생성할 수 있어야 합니다.

  • javax.jms.ConnectionFactory
  • javax.jms.XAConnectionFactory

pax-jms-pool-* 번들은 org.ops4j.pax.jms.service.ConnectionFactoryFactory 서비스와 원활하게 작동합니다. 이러한 번들은 속성 세트와 원래 org.ops4j.pax.service.ConnectionFactoryfactory를 사용하여 풀링된 연결 팩토리를 생성하는 데 사용할 수 있는 org.ops4j.pax.pax.jms.service.service.service.service.service.Pooled ConnectionFactory factory의 구현을 제공합니다. 예를 들면 다음과 같습니다.

public interface PooledConnectionFactoryFactory {

    ConnectionFactory create(ConnectionFactoryFactory cff, Map<String, Object> props);

}

다음 표에서는 풀링된 연결 팩토리 팩토리 팩토리를 등록하는 번들을 보여줍니다. 표에서 o.o.p.j.porg.ops4j.pax.jms.pool 을 나타냅니다.

번들PooledConnectionFactoryFactory풀 키

pax-jms-pool-pooledjms

o.o.p.j.p.pooledjms.PooledJms(XA)PooledConnectionFactoryFactory

pooledjms

pax-jms-pool-narayana

o.o.p.j.p.narayana.PooledJms(XA)PooledConnectionFactoryFactory

narayana

pax-jms-pool-transx

o.o.p.j.p.transx.Transx(XA)PooledConnectionFactoryFactory

transx

참고

pax-jms-pool-narayana 팩토리를 pooled-jms 라이브러리를 기반으로 하므로 PooledJms(XA)PooledConnectionFactoryFactory 라고 합니다. XA 복구를 위한 Narayana 트랜잭션 관리자와의 통합을 추가합니다.

위의 번들은 연결 팩토리만 설치합니다. 연결 팩토리를 직접 설치하지 않는 번들입니다. 결과적으로 javax.jms.ConnectionFactory org.ops4j.pax.jms.service.PooledConnectionFactoryFactory.create() 메서드를 호출하는 데 필요한 것이 있습니다.

7.5.2. pax-jms-pool-pooledjms 연결 풀 모듈 사용

pax-jms-pooledjms 번들을 사용하는 방법을 이해하면 pax-jms-pool-pooledjms 번들뿐만 아니라 pax-jms-pool-pool - narayna 번들도 사용할 수 있습니다.

pax-jms-config 번들은 다음을 추적합니다.

  • org.ops4j.pax.jms.service.ConnectionFactoryFactory services
  • org.ops4j.connection factory팩토리 PID
  • pax-jms-pool-* 번들 중 하나로 등록된 org.ops4j.pax.jms.service.PooledConnectionFactory factory의 인스턴스입니다.

팩토리 구성에 pool 속성이 포함된 경우 pax-jms-config 번들에 의해 등록된 궁극적인 연결 팩토리는 브로커별 연결 팩토리입니다. pool=pooledjms 인 경우 연결 팩토리는 다음 중 하나로 래핑됩니다.

  • org.messaginghub.pooled.jms.JmsPoolConnectionFactory (xa=false)
  • org.messaginghub.pooled.jms.JmsPoolXAConnectionFactory (xa=true)

pool 속성(및 비xa/xa 연결 팩토리 중 하나를 선택하는 부울 xa 속성 외에도 org.ops4j.connectionfactory factory PID 에는 풀 접두사가 있는 속성이 포함될 수 있습니다. .

pooled-jms 라이브러리의 경우 다음과 같은 접두사가 지정된 속성이 ( 접두사를 제거한 후) 사용하여 인스턴스를 구성합니다.

  • org.messaginghub.pooled.jms.JmsPoolConnectionFactory, or
  • org.messaginghub.pooled.jms.JmsPoolXAConnectionFactory

다음 목록은 jms와 함께 편리한 구문을 사용하는 pooled-jms 풀(org.ops4j.connectionfactory-artemis factory PID)의 비현실적 구성입니다.-prefixed properties:

# configuration for pax-jms-config to choose and configure specific org.ops4j.pax.jms.service.ConnectionFactoryFactory
name = jms/artemis
connectionFactoryType = ConnectionFactory
jms.url = tcp://localhost:61616
jms.user = fuse
jms.password = fuse
# org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory specific coniguration
jms.callTimeout = 12000
# ...

# hints for pax-jms-config to use selected org.ops4j.pax.jms.service.PooledConnectionFactoryFactory
pool = pooledjms
xa = false

# pooled-jms specific configuration of org.messaginghub.pooled.jms.JmsPoolConnectionFactory
pool.idleTimeout = 10
pool.maxConnections = 100
pool.blockIfSessionPoolIsFull = true
# ...

위의 구성에서 poolxa 키는 등록된 org.ops4j.pax.jms.service.PooledConnectionFactoryfactory 서비스 중 하나를 선택하는 힌트 (서비스 필터 속성)입니다. pooled-jms 라이브러리의 경우 다음과 같습니다.

karaf@root()> feature:install pax-jms-pool-pooledjms

karaf@root()> bundle:services -p org.ops4j.pax.jms.pax-jms-pool-pooledjms

OPS4J Pax JMS MessagingHub JMS Pool implementation (252) provides:
------------------------------------------------------------------
objectClass = [org.ops4j.pax.jms.service.PooledConnectionFactoryFactory]
pool = pooledjms
service.bundleid = 252
service.id = 331
service.scope = singleton
xa = false
-----
objectClass = [org.ops4j.pax.jms.service.PooledConnectionFactoryFactory]
pool = pooledjms
service.bundleid = 252
service.id = 335
service.scope = singleton
xa = true

다음은 연결 풀을 생성하고 구성하는 단계의 전체 예입니다.

  1. 필요한 기능을 설치합니다.

    karaf@root()> feature:install -v pax-jms-pool-pooledjms pax-jms-artemis
    Adding features: pax-jms-pool-pooledjms/[1.0.0,1.0.0]
    ...
  2. jms 기능을 설치합니다.

    karaf@root()> feature:install jms
    
    karaf@root()> service:list org.ops4j.pax.jms.service.ConnectionFactoryFactory
    [org.ops4j.pax.jms.service.ConnectionFactoryFactory]
    ----------------------------------------------------
     service.bundleid = 249
     service.id = 327
     service.scope = singleton
     type = artemis
    Provided by :
     OPS4J Pax JMS Artemis Support (249)
    
    karaf@root()> service:list org.ops4j.pax.jms.service.PooledConnectionFactoryFactory
    [org.ops4j.pax.jms.service.PooledConnectionFactoryFactory]
    ----------------------------------------------------------
     pool = pooledjms
     service.bundleid = 251
     service.id = 328
     service.scope = singleton
     xa = false
    Provided by :
     OPS4J Pax JMS MessagingHub JMS Pool implementation (251)
    
    [org.ops4j.pax.jms.service.PooledConnectionFactoryFactory]
    ----------------------------------------------------------
     pool = pooledjms
     service.bundleid = 251
     service.id = 333
     service.scope = singleton
     xa = true
    Provided by :
     OPS4J Pax JMS MessagingHub JMS Pool implementation (251)
  3. 팩토리 구성 을 생성합니다.

    karaf@root()> config:edit --factory --alias artemis org.ops4j.connectionfactory
    karaf@root()> config:property-set connectionFactoryType ConnectionFactory
    karaf@root()> config:property-set osgi.jndi.service.name jms/artemis
    karaf@root()> config:property-set type artemis
    karaf@root()> config:property-set protocol amqp # so we switch to org.apache.qpid.jms.JmsConnectionFactory
    karaf@root()> config:property-set jms.url amqp://localhost:61616
    karaf@root()> config:property-set jms.username admin
    karaf@root()> config:property-set jms.password admin
    karaf@root()> config:property-set pool pooledjms
    karaf@root()> config:property-set xa false
    karaf@root()> config:property-set pool.idleTimeout 10
    karaf@root()> config:property-set pool.maxConnections 123
    karaf@root()> config:property-set pool.blockIfSessionPoolIsFull true
    karaf@root()> config:update
  4. pax-jms-configjavax.jms.ConnectionFactory 서비스로 구성을 처리했는지 확인합니다.

    karaf@root()> service:list javax.jms.ConnectionFactory
    [javax.jms.ConnectionFactory]
    -----------------------------
     connectionFactoryType = ConnectionFactory
     felix.fileinstall.filename = file:${karaf.etc}/org.ops4j.connectionfactory-artemis.cfg
     jms.password = admin
     jms.url = amqp://localhost:61616
     jms.username = admin
     osgi.jndi.service.name = jms/artemis
     pax.jms.managed = true
     pool.blockIfSessionPoolIsFull = true
     pool.idleTimeout = 10
     pool.maxConnections = 123
     protocol = amqp
     service.bundleid = 250
     service.factoryPid = org.ops4j.connectionfactory
     service.id = 347
     service.pid = org.ops4j.connectionfactory.fc1b9e85-91b4-421b-aa16-1151b0f836f9
     service.scope = singleton
     type = artemis
    Provided by :
     OPS4J Pax JMS Config (250)
  5. 연결 팩토리를 사용합니다.

    karaf@root()> jms:connectionfactories
    JMS Connection Factory
    ──────────────────────
    jms/artemis
    
    karaf@root()> jms:info -u admin -p admin jms/artemis
    Property │ Value
    ─────────┼────────────────
    product  │ QpidJMS
    version  │ 0.30.0.redhat-1
    
    karaf@root()> jms:send -u admin -p admin jms/artemis DEV.QUEUE.1 "Hello Artemis"
    
    karaf@root()> jms:browse -u admin -p admin jms/artemis DEV.QUEUE.1
    Message ID                                      │ Content       │ Charset │ Type │ Correlation ID │ Delivery Mode │ Destination │ Expiration │ Priority │ Redelivered │ ReplyTo │ Timestamp
    ────────────────────────────────────────────────┼───────────────┼─────────┼──────┼────────────────┼───────────────┼─────────────┼────────────┼──────────┼─────────────┼─────────┼──────────────────────────────
    ID:64842f99-5cb2-4850-9e88-f50506d49d20:1:1:1-1 │ Hello Artemis │ UTF-8   │      │                │ Persistent    │ DEV.QUEUE.1 │ Never      │ 4        │ false       │         │ Mon May 14 12:47:13 CEST 2018

7.5.3. pax-jms-pool-narayana 연결 풀 모듈 사용

pax-jms-pool-narayna 모듈은 pax-jms-pool-pooledjms 로 거의 모든 작업을 수행합니다. XA 및 비 XA 시나리오에 대해 pooled-jms-specific org.ops4j.pax.jms.service.PooledConnectionFactoryFactory 를 설치합니다. 유일한 차이점은 XA 시나리오에서는 추가 통합 포인트가 있다는 것입니다. org.jboss.tm.XAResourceRecovery OSGi 서비스는 com.arjuna.ats.arjuna.recovery.Recovery.RecoveryManager 에서 선택할 수 있도록 등록되었습니다.

7.5.4. pax-jms-pool-transx 연결 풀 모듈 사용

pax-jms-pool-transx 모듈은 pax-transx-jms 번들을 기반으로 하는 org.ops4j.pax.service.PooledConnectionFactoryFactory 서비스의 구현을 제공합니다. pax-transx-jms 번들은 org.ops4j.pax.transx.jms.ManagedConnectionFactoryBuilder 기능을 사용하여 javax.jms.ConnectionFactory 풀을 생성합니다. 이는 8.3절. “pax-transx 프로젝트 정보” 에서 설명하는 JCA(Java™ Connector Architecture) 솔루션입니다.

7.6. 연결 팩토리를 아티팩트로 배포

이 주제에서는 실제 권장 사항에 대해 설명합니다.

배포 방법 에서는javax.jms.ConnectionFactory 서비스는 애플리케이션 코드로 직접 등록합니다. 일반적으로 이 코드는 블루프린트 컨테이너 내에 있습니다. 블루프린트 XML은 일반 OSGi 번들의 일부일 수 있으며 mvn: URI를 사용하여 설치하고 Maven 리포지토리(로컬 또는 원격)에 저장할 수 있습니다. 구성 관리 구성에 비해 번들과 같은 버전 제어를 더 쉽게 수행할 수 있습니다.

pax-jms-config 버전 1.0.0 번들은 연결 팩토리 구성을 위한 배포 방법을 추가합니다. 애플리케이션 개발자는 javax.jms.(XA)ConnectionFactory 서비스(일반적으로 Bluerpint XML를 사용하여)를 등록하고 서비스 속성을 지정합니다. 그런 다음 pax-jms-config 는 등록된 브로커별 연결 팩토리를 감지하고 (서비스 속성 사용) 일반 비 브로커별 연결 풀 내에서 서비스를 래핑합니다.

블루프린트 XML을 사용하는 세 가지 배포 방법은 다음과 같습니다.

7.6.1. 연결 팩토리의 수동 배포

이 방법에서는 pax-jms-config 번들이 필요하지 않습니다. 애플리케이션 코드는 브로커별 및 일반 연결 풀을 모두 등록합니다.

<!--
    Broker-specific, non-pooling, non-enlisting javax.jms.XAConnectionFactory
-->
<bean id="artemis" class="org.apache.activemq.artemis.jms.client.ActiveMQXAConnectionFactory">
    <argument value="tcp://localhost:61616" />
    <property name="callTimeout" value="2000" />
    <property name="initialConnectAttempts" value="3" />
</bean>

<!--
    Fuse exports this service from fuse-pax-transx-tm-narayana bundle.
-->
<reference id="tm" interface="javax.transaction.TransactionManager" />

<!--
    Non broker-specific, generic, pooling, enlisting javax.jms.ConnectionFactory
-->
<bean id="pool" class="org.messaginghub.pooled.jms.JmsPoolXAConnectionFactory">
    <property name="connectionFactory" ref="artemis" />
    <property name="transactionManager" ref="tm" />
    <property name="maxConnections" value="10" />
    <property name="idleTimeout" value="10000" />
</bean>

<!--
    Expose connection factory for use by application code (such as Camel, Spring, ...)
-->
<service interface="javax.jms.ConnectionFactory" ref="pool">
    <service-properties>
        <!-- Giving connection factory a name using one of these properties makes identification easier in jms:connectionfactories: -->
        <entry key="osgi.jndi.service.name" value="jms/artemis" />
        <!--<entry key="name" value="jms/artemis" />-->
        <!-- Without any of the above, name will fall back to "service.id" -->
    </service-properties>
</service>

다음은 사용 방법을 보여주는 쉘 명령은 다음과 같습니다.

karaf@root()> feature:install artemis-core-client artemis-jms-client
karaf@root()> install -s mvn:org.apache.commons/commons-pool2/2.5.0
Bundle ID: 244
karaf@root()> install -s mvn:org.messaginghub/pooled-jms/0.3.0
Bundle ID: 245
karaf@root()> install -s blueprint:file://$PQ_HOME/message-brokers/blueprints/artemis-manual.xml
Bundle ID: 246

karaf@root()> bundle:services -p 246

Bundle 246 provides:
--------------------
objectClass = [javax.jms.ConnectionFactory]
osgi.jndi.service.name = jms/artemis
osgi.service.blueprint.compname = pool
service.bundleid = 246
service.id = 340
service.scope = bundle
-----
objectClass = [org.osgi.service.blueprint.container.BlueprintContainer]
osgi.blueprint.container.symbolicname = artemis-manual.xml
osgi.blueprint.container.version = 0.0.0
service.bundleid = 246
service.id = 341
service.scope = singleton

karaf@root()> feature:install jms

karaf@root()> jms:connectionfactories
JMS Connection Factory
──────────────────────
jms/artemis

karaf@root()> jms:info -u admin -p admin jms/artemis
Property │ Value
─────────┼──────────────────────────
product  │ ActiveMQ
version  │ 2.4.0.amq-711002-redhat-1

위 목록에 표시된 것처럼 블루프린트 번은 일반적인 브로커별 연결 풀인 javax.jms.ConnectionFactory 서비스를 내보냅니다. 블루프린트 XML에는 명시적인 <service ref="artemis" > 선언이 없기 때문에 브로커별 javax.jms.XAConnectionFactory 는 OSGi 서비스로 등록되지 않습니다.

7.6.2. 연결 팩토리 배포

이 방법은 pax-jms-config표준 방식으로 사용하는 방법을 보여줍니다. 이는 Fuse 6.x에 권장되는 방법과 약간 다릅니다. 여기서 풀링 구성을 서비스 속성으로 지정해야 했습니다.

블루프린트 XML 예제는 다음과 같습니다.

<!--
    A broker-specific org.ops4j.pax.jms.service.ConnectionFactoryFactory that can create (XA)ConnectionFactory
    using properties. It is registered by pax-jms-* bundles
-->
<reference id="connectionFactoryFactory"
        interface="org.ops4j.pax.jms.service.ConnectionFactoryFactory"
        filter="(type=artemis)" />

<!--
    Non broker-specific org.ops4j.pax.jms.service.PooledConnectionFactoryFactory that can create
    pooled connection factories with the help of org.ops4j.pax.jms.service.ConnectionFactoryFactory

    For example, pax-jms-pool-pooledjms bundle registers org.ops4j.pax.jms.service.PooledConnectionFactoryFactory
    with these properties:
     - pool = pooledjms
     - xa = true|false (both are registered)
-->
<reference id="pooledConnectionFactoryFactory"
        interface="org.ops4j.pax.jms.service.PooledConnectionFactoryFactory"
        filter="(&amp;(pool=pooledjms)(xa=true))" />

<!--
    When using XA connection factories, javax.transaction.TransactionManager service is not needed here.
    It is used internally by xa-aware pooledConnectionFactoryFactory.
-->
<!--<reference id="tm" interface="javax.transaction.TransactionManager" />-->

<!--
    Finally, use both factories to expose the pooled, xa-aware, connection factory.
-->
<bean id="pool" factory-ref="pooledConnectionFactoryFactory" factory-method="create">
    <argument ref="connectionFactoryFactory" />
    <argument>
        <props>
            <!--
                Properties needed by artemis-specific org.ops4j.pax.jms.service.ConnectionFactoryFactory
            -->
            <prop key="jms.url" value="tcp://localhost:61616" />
            <prop key="jms.callTimeout" value="2000" />
            <prop key="jms.initialConnectAttempts" value="3" />
            <!-- Properties needed by pooled-jms-specific org.ops4j.pax.jms.service.PooledConnectionFactoryFactory -->
            <prop key="pool.maxConnections" value="10" />
            <prop key="pool.idleTimeout" value="10000" />
        </props>
    </argument>
</bean>

<!--
    Expose connection factory for use by application code (such as Camel, Spring, ...)
-->
<service interface="javax.jms.ConnectionFactory" ref="pool">
    <service-properties>
        <!-- Giving connection factory a name using one of these properties makes identification easier in jms:connectionfactories: -->
        <entry key="osgi.jndi.service.name" value="jms/artemis" />
        <!--<entry key="name" value="jms/artemis" />-->
        <!-- Without any of the above, name will fall back to "service.id" -->
    </service-properties>
</service>

이전 예제에서는 연결 팩토리 (…​)를 사용하여 연결 팩토리를 생성하는 팩토리 빈을 사용합니다. XA 인식 PooledConnectionFactoryFactory 에서 내부적으로 추적하므로 javax. Cryostat.TransactionManager 서비스에 대한 명시적 참조가 필요하지 않습니다.

Fuse/Karaf 쉘의 예는 다음과 같습니다.

karaf@root()> feature:install jms pax-jms-artemis pax-jms-pool-pooledjms

karaf@root()> install -s blueprint:file://$PQ_HOME/message-brokers/blueprints/artemis-pax-jms-factory-pooledjms.xml
Bundle ID: 253
karaf@root()> bundle:services -p 253

Bundle 253 provides:
--------------------
objectClass = [javax.jms.ConnectionFactory]
osgi.jndi.service.name = jms/artemis
osgi.service.blueprint.compname = pool
service.bundleid = 253
service.id = 347
service.scope = bundle
-----
objectClass = [org.osgi.service.blueprint.container.BlueprintContainer]
osgi.blueprint.container.symbolicname = artemis-pax-jms-factory-pooledjms.xml
osgi.blueprint.container.version = 0.0.0
service.bundleid = 253
service.id = 348
service.scope = singleton

karaf@root()> jms:connectionfactories
JMS Connection Factory
──────────────────────
jms/artemis

karaf@root()> jms:info -u admin -p admin jms/artemis
Property │ Value
─────────┼──────────────────────────
product  │ ActiveMQ
version  │ 2.4.0.amq-711002-redhat-1

위 목록에 표시된 것처럼 블루프린트 번은 일반적인 브로커별 연결 풀인 javax.jms.ConnectionFactory 서비스를 내보냅니다. 블루프린트 XML에는 명시적인 <service ref="artemis" > 선언이 없기 때문에 브로커별 javax.jms.XAConnectionFactory 는 OSGi 서비스로 등록되지 않습니다.

7.6.3. 연결 팩토리의 혼합 배포

pax-jms-config 1.0.0 번에서는 서비스 속성을 사용하여 풀링 연결 팩토리 내의 브로커별 연결 팩토리를 래핑 하는 다른 방법을 추가합니다. 이 방법은 Fuse 6.x에서 작업하는 방식과 일치합니다.

블루프린트 XML 예제는 다음과 같습니다.

<!--
    Broker-specific, non-pooling, non-enlisting javax.jms.XAConnectionFactory
-->
<bean id="artemis" class="org.apache.activemq.artemis.jms.client.ActiveMQXAConnectionFactory">
    <argument value="tcp://localhost:61616" />
    <property name="callTimeout" value="2000" />
    <property name="initialConnectAttempts" value="3" />
</bean>

<!--
    Expose broker-specific connection factory with service properties.
    No need to expose pooling, enlisting, non broker-specific javax.jms.XAConnectionFactory. It will be registered
    automatically by pax-jms-config with the same properties as this <service>, but with a higher service.ranking
-->
<service id="pool" ref="artemis" interface="javax.jms.XAConnectionFactory">
    <service-properties>
        <!-- "pool" key is needed for pax-jms-config to wrap broker-specific connection factory inside connection pool -->
        <entry key="pool" value="pooledjms" />
        <!-- <service>/@id attribute does not propagate, but name of the connection factory is required using one of: -->
        <entry key="osgi.jndi.service.name" value="jms/artemis" />
        <!-- or: -->
        <!--<entry key="name" value="jms/artemis" />-->
        <!-- Other properties, that normally by e.g., pax-jms-pool-pooledjms -->
        <entry key="pool.maxConnections" value="10" />
        <entry key="pool.idleTimeout" value="10000" />
    </service-properties>
</service>

위의 예에서는 브로커별 연결 팩토리의 수동 레지스터만 볼 수 있습니다. pool=pooledjms service 속성은 pax-jms-config 번들에서 관리하는 연결 팩토리 추적기에 대한 힌트입니다. 이 서비스 속성을 사용하는 연결 팩토리 서비스는 풀링 연결 팩토리(이 예에서는 pax-jms-pool-pooledjms ) 내에서 래핑됩니다.

Fuse/Karaf 쉘의 예는 다음과 같습니다.

karaf@root()> feature:install jms pax-jms-config pax-jms-artemis pax-jms-pool-pooledjms

karaf@root()> install -s blueprint:file://$PQ_HOME/message-brokers/blueprints/artemis-pax-jms-discovery.xml
Bundle ID: 254

karaf@root()> bundle:services -p 254

Bundle 254 provides:
--------------------
objectClass = [javax.jms.XAConnectionFactory]
osgi.jndi.service.name = jms/artemis
osgi.service.blueprint.compname = artemis
pool = pooledjms
pool.idleTimeout = 10000
pool.maxConnections = 10
service.bundleid = 254
service.id = 349
service.scope = bundle
-----
objectClass = [org.osgi.service.blueprint.container.BlueprintContainer]
osgi.blueprint.container.symbolicname = artemis-pax-jms-discovery.xml
osgi.blueprint.container.version = 0.0.0
service.bundleid = 254
service.id = 351
service.scope = singleton

karaf@root()> service:list javax.jms.XAConnectionFactory
[javax.jms.XAConnectionFactory]
-------------------------------
 osgi.jndi.service.name = jms/artemis
 osgi.service.blueprint.compname = artemis
 pool = pooledjms
 pool.idleTimeout = 10000
 pool.maxConnections = 10
 service.bundleid = 254
 service.id = 349
 service.scope = bundle
Provided by :
 Bundle 254
Used by:
 OPS4J Pax JMS Config (251)

karaf@root()> service:list javax.jms.ConnectionFactory
[javax.jms.ConnectionFactory]
-----------------------------
 osgi.jndi.service.name = jms/artemis
 osgi.service.blueprint.compname = artemis
 pax.jms.managed = true
 pax.jms.service.id.ref = 349
 pool.idleTimeout = 10000
 pool.maxConnections = 10
 service.bundleid = 251
 service.id = 350
 service.ranking = 1000
 service.scope = singleton
Provided by :
 OPS4J Pax JMS Config (251)

karaf@root()> jms:connectionfactories
JMS Connection Factory
──────────────────────
jms/artemis

karaf@root()> jms:info -u admin -p admin jms/artemis
Property │ Value
─────────┼──────────────────────────
product  │ ActiveMQ
version  │ 2.4.0.amq-711002-redhat-1

이전 예에서 jms:connectionfactories 는 하나의 서비스만 표시합니다. 이 명령은 중복 이름을 제거하기 때문입니다. 데이터 소스의 혼합 배포에 두 가지 서비스가 jdbc:ds-list 에서 제공되었습니다.

javax.jms.XAConnectionFactory 는 블루프린트 번들에서 등록되며, 선언된 pool = pooledjms 속성이 있습니다.

javax.jms.ConnectionFactorypax-jms-config 번들 및에서 등록됩니다.

  • pool = pooledjms 속성이 없습니다. 래퍼 연결 팩토리를 등록할 때 제거되었습니다.
  • service.ranking = 1000 속성이 있으므로 예를 들어 이름으로 연결 팩토리를 찾을 때 기본 설정 버전입니다.
  • pax.jms.managed = true 속성이 있으므로 다시 래핑하려고 시도하지 않습니다.
  • 연결 풀 내에서 래핑된 원래 연결 팩토리 서비스를 나타내는 pax.jms.service.id.ref = 349 속성이 있습니다.

8장. Java 커넥터 아키텍처 정보

JCA 사양은 다음과 같은 세 가지 참가자가 있는 시나리오를 일반화 하기 위해 생성되었습니다.

  • 데이터베이스 또는 일반적으로 EIS 시스템과 같은 외부 시스템
  • Cryostat 애플리케이션 서버
  • 배포된 애플리케이션

8.1. 간단한 JDBC 유추

애플리케이션 및 데이터베이스만 있는 가장 간단한 시나리오에서는 다음과 같습니다.

diag c0119ca1c6730be208f7cc89d769d54b

javax.sql.DataSource 를 노출하는 애플리케이션 서버를 추가하면 XA와 같은 데이터 소스의 다양한 측면을 호출하지 않고 다음과 같습니다.

diag 436a38fd23a0dbd93854268606a797ff

8.2. JCA 사용 개요

JCA는 드라이버와 애플리케이션 서버 간에 양방향 통신을 추가하여 데이터베이스 드라이버 의 개념을 일반화합니다. 드라이버는 javax. resource.spi.ResourceAdapter 로 표시되는 리소스 어댑터 가 됩니다.

두 가지 중요한 인터페이스가 있습니다.

  • 리소스 어댑터에서 구현하는 javax.resource.spi.ManagedConnectionFactory
  • 애플리케이션 서버에서 구현하는 javax.resource.spi.ConnectionManager.

ManagedConnectionFactory 인터페이스는 다음 두 가지 용도로 사용됩니다.

  • Object createConnectionFactory(ConnectionManager cxManager) 메서드는 애플리케이션 코드에서 사용할 수 있는 지정된 EIS(또는 데이터베이스 또는 메시지 브로커)에 대한 연결 팩토리 를 생성하는 데 사용할 수 있습니다. 반환된 오브젝트 는 다음과 같습니다.

    • 일반적인 javax.resource.cci.ConnectionFactory (자세한 내용은 JCA 1.6, Chapter 17: Common Client Interface)를 참조하십시오.
    • 잘 알려진 javax.sql.DataSource 또는 javax.jms.ConnectionFactory 와 같은 EIS 특정 연결 팩토리 이는 pax-transx-jdbcpax-transx-jms 번들에서 사용하는 연결 팩토리 유형입니다.
  • 애플리케이션 서버에서 사용하는 javax.resource.spi.ManagedConnection ManagedConnectionFactory.createManagedConnection() 메서드는 EIS/database/broker에 대한 실제 물리적 연결을 생성합니다.

ConnectionManager애플리케이션 서버에서 구현하고 리소스 어댑터 에서 사용합니다. 먼저 QoS 작업(풀링, 보안, 트랜잭션 관리)을 수행하고 마지막으로 ManagedConnection 인스턴스를 생성하기 위해 리소스 어댑터ManagedConnectionFactory 에 위임하는 애플리케이션 서버 입니다. 흐름은 다음과 같습니다.

  1. 애플리케이션 코드는 ManagedConnectionFactory.createConnectionFactory() 에서 반환된 오브젝트를 사용하여 애플리케이션 서버에서 생성 및 노출된 연결 팩토리 를 사용합니다. 일반 CCI 인터페이스 또는 javax.sql.DataSource 일 수 있습니다.
  2. 연결 팩토리 에서는 자체적으로 연결을 생성하지 않고 ConnectionManager.allocateConnection() 에 위임하는 대신 리소스 어댑터(특정 ManagedConnectionFactory)를 전달합니다.
  3. 애플리케이션 서버에서 구현된 ConnectionManager지원 개체 를 생성하고, 트랜잭션, 풀링 등을 관리하며 결국 전달된 ManagedConnectionFactory 에서 물리적(관리) 연결을 가져옵니다.
  4. 애플리케이션 코드는 결국 리소스 어댑터 의 특정 물리적 연결에 위임하는 애플리케이션 서버에서 생성한 래퍼/proxy인 연결을 가져옵니다.

다음은 애플리케이션 서버 가 EIS별로 연결된 비CCI 연결 팩토리 를 생성한 다이어그램입니다. 간단히 - EIS(here: database)에 대한 액세스는 javax.sql.DataSource 인터페이스를 사용하여 수행되며 드라이버의 작업은 물리적 연결을 제공하는 것입니다. 애플리케이션 서버는 풀링/거부/보안을 수행하는 프록시 내부(일반적으로) 내부로 래핑됩니다.

diag d19ebefefdbf2bac343521fa48cab709

8.3. pax-transx 프로젝트 정보

pax-transx 프로젝트는 OSGi의 JTA/JTS 트랜잭션 관리와 JDBC 및 JMS에 대한 리소스 풀링을 지원합니다. pax-jdbcpax-jms 간의 격차를 닫습니다.

  • pax-jdbc 는 구성 옵션 및 javax.sql.(XA)ConnectionFactory 서비스에 대한 검색 기능을 추가하고 일부 JDBC 풀링 구현을 제공합니다.
  • pax-jmsjavax.jms.(XA)ConnectionFactory 서비스에 대해 동일한 작업을 수행하고 일부 JMS 풀링 구현을 제공합니다.
  • pax-transxjavax. Cryostat.TransactionManager 구현에 대한 구성 옵션 및 검색을 추가하며 (finally) 풀링 및 tranasction 지원을 통해 JCA 기반 JDBC/JMS 연결 관리를 제공합니다.

JDBC 연결 풀 및 JMS 연결 풀에 대한 섹션은 여전히 유효합니다. JCA 기반 풀을 사용하는 데 필요한 유일한 변경 사항은 JDBC 데이터 소스 및 JMS 연결 팩토리를 등록할 때 pool=transx 속성을 사용하는 것입니다.

  • pax-jdbc-pool-transxorg.ops4j.pax.transx.jdbc.ManagedDataSourceBuilder 에서 pax-transx-jdbc를 사용합니다.
  • pax-jms-pool-transxorg.ops4j.pax.transx.jms.ManagedConnectionFactoryBuilder 에서 pax-transx-jms를 사용합니다.

풀링된 데이터 소스/연결 팩토리는 빌더 스타일 (Java™ans 속성 없음)에서 생성되는 반면, 이러한 속성은 JDBC에서 지원됩니다.

  • name
  • userName
  • 암호
  • commitBeforeAutocommit
  • preparedStatementCacheSize
  • transactionIsolationLevel
  • minIdle
  • maxPoolSize
  • aliveBypassWindow
  • houseKeepingPeriod
  • connectionTimeout
  • idleTimeout
  • maxLifetime

이러한 속성은 JMS에서 지원됩니다.

  • name
  • userName
  • 암호
  • clientID
  • minIdle
  • maxPoolSize
  • aliveBypassWindow
  • houseKeepingPeriod
  • connectionTimeout
  • idleTimeout
  • maxLifetime

XA 복구를 위해서는 사용자 이름과 암호 속성이 필요합니다( Fuse 6.x의 aries.xa.usernamearies.xa.password 속성과 마찬가지로).

블루프린트에서 이 JDBC 구성을 사용하여 (mind pool=transx)

<!--
    Database-specific, non-pooling, non-enlisting javax.sql.XADataSource
-->
<bean id="postgresql" class="org.postgresql.xa.PGXADataSource">
    <property name="url" value="jdbc:postgresql://localhost:5432/reportdb" />
    <property name="user" value="fuse" />
    <property name="password" value="fuse" />
    <property name="currentSchema" value="report" />
    <property name="connectTimeout" value="5" />
</bean>

<!--
    Expose database-specific data source with service properties
    No need to expose pooling, enlisting, non database-specific javax.sql.DataSource - it'll be registered
    automatically by pax-jdbc-config with the same properties as this <service>, but with higher service.ranking
-->
<service id="pool" ref="postgresql" interface="javax.sql.XADataSource">
    <service-properties>
        <!-- "pool" key is needed for pax-jdbc-config to wrap database-specific data source inside connection pool -->
        <entry key="pool" value="transx" />
        <!-- <service>/@id attribute doesn't propagate, but name of the datasource is required using one of: -->
        <entry key="osgi.jndi.service.name" value="jdbc/postgresql" />
        <!-- or: -->
        <!--<entry key="dataSourceName" value="jdbc/postgresql" />-->
        <!-- Other properties, that normally are needed by e.g., pax-jdbc-pool-transx -->
        <entry key="pool.maxPoolSize" value="13" />
        <entry key="pool.userName" value="fuse" />
        <entry key="pool.password" value="fuse" />
    </service-properties>
</service>

블루프린트의 이 JMS 구성으로 (mind pool=transx)

<!--
    Broker-specific, non-pooling, non-enlisting javax.jms.XAConnectionFactory
-->
<bean id="artemis" class="org.apache.activemq.artemis.jms.client.ActiveMQXAConnectionFactory">
    <argument index="0" value="tcp://localhost:61616" />
    <!-- credentials needed for JCA-based XA-recovery -->
    <argument index="1" value="admin" />
    <argument index="2" value="admin" />
    <property name="callTimeout" value="2000" />
    <property name="initialConnectAttempts" value="3" />
</bean>

<!--
    Expose broker-specific connection factory with service properties
    No need to expose pooling, enlisting, non broker-specific javax.jms.XAConnectionFactory - it'll be registered
    automatically by pax-jms-config with the same properties as this <service>, but with higher service.ranking
-->
<service id="pool" ref="artemis" interface="javax.jms.XAConnectionFactory">
    <service-properties>
        <!-- "pool" key is needed for pax-jms-config to wrap broker-specific connection factory inside connection pool -->
        <entry key="pool" value="transx" />
        <!-- <service>/@id attribute doesn't propagate, but name of the connection factory is required using one of: -->
        <entry key="osgi.jndi.service.name" value="jms/artemis" />
        <!-- or: -->
        <!--<entry key="name" value="jms/artemis" />-->
        <!-- Other properties, that normally are needed e.g., pax-jms-pool-transx -->
        <entry key="pool.maxPoolSize" value="13" />
        <entry key="pool.userName" value="admin" />
        <entry key="pool.password" value="admin" />
    </service-properties>
</service>

JCA 기반 리소스 관리를 활용하는 JDBC 데이터 소스 및 JMS 연결 팩토리가 있습니다. Transx 기반 풀은 XA 복구와 관련하여 pax-transx-tm-narayana 와 적절하게 통합됩니다.

필요한 기능은 다음과 같습니다.

  • pax-jdbc-pool-tranx
  • pax-jms-pool-tranx
  • pax-transx-jdbc
  • pax-transx-jms
  • pax-jms-artemis (A-MQ 7 사용 시)

9장. 트랜잭션을 사용하는 Camel 애플리케이션 작성

available-to-be-referenced 서비스 유형을 구성한 후 애플리케이션을 작성할 준비가 된 것입니다. 세 가지 유형의 서비스는 다음과 같습니다.

  • 다음 인터페이스 중 하나를 구현하는 트랜잭션 관리자 한 개:

    • javax.transaction.UserTransaction
    • javax.transaction.TransactionManager
    • org.springframework.transaction.PlatformTransactionManager
  • javax.sql.DataSource. 인터페이스를 구현하는 하나 이상의 JDBC 데이터 소스입니다. 종종 둘 이상의 데이터 소스가 있습니다.
  • javax.jms.ConnectionFactory 인터페이스를 구현하는 하나 이상의 JMS 연결 팩토리입니다. 종종 하나 이상의 것이 있습니다.

이 섹션에서는 트랜잭션, 데이터 소스 및 연결 팩토리 관리와 관련된 Camel 관련 구성에 대해 설명합니다.

참고

이 섹션에서는 SpringTransactionPolicy 와 같은 여러 가지 Spring 관련 개념에 대해 설명합니다. Camel 컨텍스트를 정의하는 XML 언어인 Spring XML DSL 과 블루프린트 XML DSL 사이에는 명확한 차이점이 있습니다. Spring XML DSL은 Fuse에서 더 이상 사용되지 않습니다. 그러나 Camel 트랜잭션 메커니즘은 내부적으로 Spring 라이브러리를 계속 사용합니다.

여기에 있는 대부분의 정보는 사용되는 PlatformTransactionManager 유형에 의존하지 않습니다. PlatformTransactionManager 가 Narayana 트랜잭션 관리자인 경우 전체 JTA 트랜잭션이 사용됩니다. PlatformTransactionManager 가 로컬 블루프린트 < bean >로 정의된 경우 (예: org.springframework.jms.connection.JmsTransactionManager ) 로컬 트랜잭션이 사용됩니다.

트랜잭션 분리는 트랜잭션을 시작, 커밋, 롤백하는 절차를 나타냅니다. 이 섹션에서는 프로그래밍 및 구성에 의해 트랜잭션 분리를 제어하는 데 사용할 수 있는 메커니즘에 대해 설명합니다.

9.1. 경로를 표시하여 트랜잭션 분리

Apache Camel은 경로에서 트랜잭션을 시작하는 간단한 메커니즘을 제공합니다. transacted() 명령을 Java DSL에 삽입하거나 XML DSL에 < transacted/ > 태그를 삽입합니다.

그림 9.1. 경로를 표시하여 분리

TXN 분리 01

트랜잭션된 프로세서는 다음과 같이 트랜잭션을 분류합니다.

  1. 교환이 트랜잭션된 프로세서에 들어가면 트랜잭션된 프로세서는 기본 트랜잭션 관리자를 호출하여 트랜잭션을 시작하고 현재 스레드에 트랜잭션을 연결합니다.
  2. 교환이 나머지 경로의 끝에 도달하면, 트랜잭션된 프로세서는 트랜잭션 관리자를 호출하여 현재 트랜잭션을 커밋합니다.

9.1.1. JDBC 리소스가 포함된 샘플 경로

그림 9.1. “경로를 표시하여 분리” 경로에 transacted() DSL 명령을 추가하여 트랜잭션되는 경로의 예를 보여줍니다. transacted() 노드를 따르는 모든 경로 노드는 트랜잭션 범위에 포함됩니다. 이 예에서 다음 두 노드는 JDBC 리소스에 액세스합니다.

9.1.2. Java DSL의 경로 정의

다음 Java DSL 예제에서는 transacted() DSL 명령으로 경로를 표시하여 트랜잭션 경로를 정의하는 방법을 보여줍니다.

import org.apache.camel.builder.RouteBuilder;

class MyRouteBuilder extends RouteBuilder {
    public void configure() {
        from("file:src/data?noop=true")
                .transacted()
                .bean("accountService","credit")
                .bean("accountService","debit");
    }
}

이 예에서 파일 끝점은 한 계정에서 다른 계정으로 수신자 전송을 설명하는 일부 XML 형식 파일을 읽습니다. 첫 번째 Cryostat () 호출 크레딧은 수혜자의 계정에 지정된 합계를 제공한 다음 두 번째 blank () 호출에서 지정된 양의 비용을 보낸 사람의 계정에서 뺀 값입니다. 둘 다 useful() 호출을 수행하면 데이터베이스 리소스에 대한 업데이트가 수행됩니다. 데이터베이스 리소스가 트랜잭션 관리자를 통해 트랜잭션에 바인딩되는 것으로 가정합니다(예: 6장. JDBC 데이터 소스 사용 ).

9.1.3. 블루프린트 XML의 경로 정의

이전 경로는 블루프린트 XML로 표시될 수도 있습니다. & lt;transacted / > 태그는 다음 XML과 같이 경로를 트랜잭션으로 표시합니다.

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ...>

    <camelContext xmlns="http://camel.apache.org/schema/blueprint">
        <route>
            <from uri="file:src/data?noop=true" />
            <transacted />
            <bean ref="accountService" method="credit" />
            <bean ref="accountService" method="debit" />
        </route>
    </camelContext>

</blueprint>

9.1.4. 기본 트랜잭션 관리자 및 트랜잭션 정책

트랜잭션을 분리하려면 트랜잭션된 프로세서가 특정 트랜잭션 관리자 인스턴스와 연결되어야 합니다. Transact ed() 를 호출할 때마다 트랜잭션 관리자를 지정해야 합니다. 변환된 프로세서는 합리적인 기본값을 자동으로 선택합니다. 예를 들어 구성에 트랜잭션 관리자 인스턴스가 하나만 있는 경우 거래된 프로세서는 암시적으로 이 트랜잭션 관리자를 선택하여 트랜잭션을 위임합니다.

트랜잭션된 프로세서는 전파 정책 및 트랜잭션 관리자를 캡슐화하는 TransactedPolicy 유형의 트랜잭션 정책으로 구성할 수도 있습니다(자세한 내용은 9.4절. “트랜잭션 전파 정책” 참조). 다음 규칙은 기본 트랜잭션 관리자 또는 트랜잭션 정책을 선택하는 데 사용됩니다.

  1. org.apache.camel.spi.#159edPolicy 유형에 대해 하나의 8080만 있는 경우 이 8080을 사용합니다.

    참고

    CryostatedPolicy 유형은 9.4절. “트랜잭션 전파 정책” 에 설명된 SpringTransactionPolicy 유형의 기본 유형입니다. 따라서 여기에 언급된 빈은 SpringTransactionPolicy 8080이 될 수 있습니다.

  2. 유형의 빈이 있는 경우 org.apache.camel.spi.#159edPolicy ID 가 ,PROPAGATION_REQUIRED 인 경우 이 Cryostat를 사용합니다.
  3. org.springframework. Cryostat.PlatformTransactionManager 유형의 Cryostat가 하나뿐인 경우 이 8080을 사용하십시오.

또한 빈 ID를 transacted() 의 인수로 제공하여 8080을 명시적으로 지정하는 옵션도 있습니다. 9.4.4절. “Java DSL의 PROPAGATION_NEVER 정책이 포함된 샘플 경로”을 참조하십시오.

9.1.5. 트랜잭션 범위

트랜잭션 프로세서를 경로에 삽입하면 트랜잭션 관리자는 교환이 이 노드를 통과할 때마다 새 트랜잭션을 생성합니다. 트랜잭션 범위는 다음과 같이 정의됩니다.

  • 트랜잭션은 현재 스레드와만 연결됩니다.
  • 트랜잭션 범위는 트랜잭션된 프로세서를 따르는 모든 경로 노드를 포함합니다.

트랜잭션된 프로세서 앞에 있는 경로 노드는 트랜잭션에 없습니다. 그러나 경로가 트랜잭션 끝점으로 시작되면 경로의 모든 노드가 트랜잭션에 있습니다. 9.2.5절. “경로 시작 시 트랜잭션 끝점”을 참조하십시오.

다음 경로를 고려하십시오. transacted() DSL 명령이 데이터베이스 리소스에 액세스하는 첫 번째 8080 () 호출 뒤에 실수로 표시되기 때문에 올바르지 않습니다.

// Java
import org.apache.camel.builder.RouteBuilder;

public class MyRouteBuilder extends RouteBuilder {
    ...
    public void configure() {
        from("file:src/data?noop=true")
                .bean("accountService", "credit")
                .transacted()  // <-- WARNING: Transaction started in the wrong place!
                .bean("accountService", "debit");
    }
}

9.1.6. 트랜잭션 경로에 스레드 풀 없음

지정된 트랜잭션이 현재 스레드와만 연결되어 있음을 이해하는 것이 중요합니다. 새 스레드의 처리는 현재 트랜잭션 트랜잭션에 참여하지 않으므로 트랜잭션 경로 중간에 스레드 풀을 만들지 않아야 합니다. 예를 들어 다음 경로는 문제를 유발하도록 바인딩됩니다.

// Java
import org.apache.camel.builder.RouteBuilder;

public class MyRouteBuilder extends RouteBuilder {
    ...
    public void configure() {
        from("file:src/data?noop=true")
                .transacted()
                .threads(3)  // WARNING: Subthreads are not in transaction scope!
                .bean("accountService", "credit")
                .bean("accountService", "debit");
    }
}

이전과 같은 경로는 threads() DSL 명령이 트랜잭션된 경로와 호환되지 않기 때문에 데이터베이스가 손상될 수 있습니다. threads() 호출이 transacted() 호출보다 우선하더라도 경로가 예상대로 작동하지 않습니다.

9.1.7. 경로를 조각으로 분할

경로를 조각으로 분할하고 각 경로 조각이 현재 트랜잭션에 참여하도록 하려면 직접 엔드포인트를 사용할 수 있습니다. 예를 들어, 전송 양이 큰지(100보다) 작는지(100 미만) 작는지 여부에 따라 라우터 조각을 분리하기 위해 다음과 같이 choice() DSL 명령 및 직접 끝점을 사용할 수 있습니다.

// Java
import org.apache.camel.builder.RouteBuilder;

public class MyRouteBuilder extends RouteBuilder {
    ...
    public void configure() {
        from("file:src/data?noop=true")
                .transacted()
                .bean("accountService", "credit")
                .choice().when(xpath("/transaction/transfer[amount > 100]"))
                .to("direct:txbig")
                .otherwise()
                .to("direct:txsmall");

        from("direct:txbig")
                .bean("accountService", "debit")
                .bean("accountService", "dumpTable")
                .to("file:target/messages/big");

        from("direct:txsmall")
                .bean("accountService", "debit")
                .bean("accountService", "dumpTable")
                .to("file:target/messages/small");
    }
}

직접 끝점이 동기이므로 direct:txbigdirect:tx windows로 시작하는 조각과 현재 트랜잭션에 모두 참여합니다. 즉, 조각은 첫 번째 경로 조각과 동일한 스레드에서 실행되므로 동일한 트랜잭션 범위에 포함됩니다.

참고

seda 끝점을 사용하여 경로 조각에 참여해서는 안 됩니다. Seda 소비자 끝점은 경로 조각(동기화 처리)을 실행하기 위한 새 스레드(또는 스레드)를 생성합니다. 따라서 조각은 원래 트랜잭션에 참여하지 않습니다.

9.1.8. 리소스 끝점

다음 Apache Camel 구성 요소는 경로의 대상으로 표시될 때 리소스 끝점 역할을 합니다(예: to() DSL 명령에 표시되는 경우). 즉, 이러한 끝점은 데이터베이스 또는 영구 큐와 같은 트랜잭션 리소스에 액세스할 수 있습니다. 리소스 끝점은 현재 트랜잭션을 시작한 트랜잭션 프로세서와 동일한 트랜잭션 관리자와 연결된 경우 현재 트랜잭션에 참여할 수 있습니다.

  • ActiveMQ
  • AMQP
  • Hibernate
  • iBATIS
  • JavaSpace
  • JBI
  • JCR
  • JDBC
  • JMS
  • JPA
  • LDAP

9.1.9. 리소스 끝점이 있는 샘플 경로

다음 예제에서는 리소스 끝점이 있는 경로를 보여줍니다. 이렇게 하면 두 개의 다른 JMS 큐로 금전 전송 순서를 보냅니다. 크레딧 대기열은 주문을 처리하여 수신자의 계정을 계산합니다. 직불 큐는 발신자의 계정을 끌기 위해 주문을 처리합니다. 해당 직불이 있는 경우에만 크레딧이 있어야 합니다. 결과적으로 큐에 있는 작업을 단일 트랜잭션으로 묶어야 합니다. 트랜잭션이 성공하면 신용 주문과 직불 순서가 모두 큐에 추가됩니다. 오류가 발생하면 주문이 큐에 추가되지 않습니다.

from("file:src/data?noop=true")
        .transacted()
        .to("jmstx:queue:credits")
        .to("jmstx:queue:debits");

9.2. 트랜잭션 끝점의 분리

경로 시작 시 소비자 끝점이 리소스에 액세스하면 교환을 폴링한 후 트랜잭션을 시작하기 때문에 transacted() 명령은 사용되지 않습니다. 즉, 트랜잭션은 너무 늦기 때문에 트랜잭션 범위 내에 소비자 끝점을 포함합니다. 이 경우 올바른 접근 방식은 끝점 자체를 트랜잭션 시작을 담당하도록 하는 것입니다. 트랜잭션을 관리할 수 있는 끝점을 트랜잭션 끝점이라고 합니다.

Camel 경로 tx 엔드 포인트

다음과 같이 트랜잭션 끝점의 두 가지 혼동 모델이 있습니다.

  • 일반적인 경우(일반적으로 트랜잭션 끝점에서 트랜잭션은 다음과 같이 트랜잭션을 위임합니다.General case - normally, a transactional endpoint demarcates transactions as follows:

    1. 교환이 엔드포인트에 도착하거나 끝점이 교환을 성공적으로 폴링하면 끝점은 연결된 트랜잭션 관리자를 호출하여 트랜잭션을 시작합니다.
    2. 끝점은 새 트랜잭션을 현재 스레드에 연결합니다.
    3. 교환이 경로 끝에 도달하면 트랜잭션 끝점은 트랜잭션 관리자를 호출하여 현재 트랜잭션을 커밋합니다.
  • InOut 교환이 있는 JMS 끝점 - JMS 소비자 끝점이 InOut 교환을 수신하고 이 교환은 다른 JMS 엔드포인트로 라우팅되는 경우 특수 케이스로 처리되어야 합니다. 문제는 경로가 전체 요청/반복을 단일 트랜잭션으로 묶는 경우 교착 상태가 될 수 있다는 것입니다.

9.2.1. JMS 엔드 포인트가 있는 샘플 경로

9.2절. “트랜잭션 끝점의 분리” 경로 시작 시 트랜잭션 엔드포인트가 있는 경로의 예( from() 명령)를 보여줍니다. 모든 경로 노드는 트랜잭션 범위에 포함됩니다. 이 예에서 경로의 모든 엔드포인트는 JMS 리소스에 액세스합니다.

9.2.2. Java DSL의 경로 정의

다음 Java DSL 예제에서는 트랜잭션 끝점으로 경로를 시작하여 트랜잭션 경로를 정의하는 방법을 보여줍니다.

from("jmstx:queue:giro")
        .to("jmstx:queue:credits")
        .to("jmstx:queue:debits");

이전 예에서 트랜잭션 범위는 엔드포인트, jmstx:queue:giro,jmstx:queue:credits, jmstx:queue:debits 를 포함합니다. 트랜잭션이 성공하면 기프트 큐에서 교환이 영구적으로 제거되고 신용 큐 및 직불 로 푸시됩니다. 트랜잭션이 실패하면 교환이 신용 및 직불 큐에 배치되지 않고 교환이 기가 큐로 다시 푸시 됩니다. 기본적으로 JMS는 메시지를 자동으로 다시 전달하려고 시도합니다. jmstx 인 JMS 구성 요소 빈은 다음과 같이 트랜잭션을 사용하도록 명시적으로 구성해야 합니다.

<blueprint ...>
    <bean id="jmstx" class="org.apache.camel.component.jms.JmsComponent">
        <property name="configuration" ref="jmsConfig" />
    </bean>

    <bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
        <property name="connectionFactory" ref="jmsConnectionFactory" />
        <property name="transactionManager" ref="jmsTransactionManager" />
        <property name="transacted" value="true" />
    </bean>
    ...
</blueprint>

이전 예에서 트랜잭션 관리자 인스턴스 jmsTransactionManager 는 JMS 구성 요소와 연결되고 트랜잭션 속성이 true 로 설정되어 InOnly 교환에 대한 트랜잭션 요약을 활성화합니다.

9.2.3. 블루프린트 XML의 경로 정의

앞의 경로는 다음과 같이 블루프린트 XML로 나타낼 수 있습니다.

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">

    <camelContext xmlns="http://camel.apache.org/schema/blueprint">
        <route>
            <from uri="jmstx:queue:giro" />
            <to uri="jmstx:queue:credits" />
            <to uri="jmstx:queue:debits" />
        </route>
    </camelContext>

</blueprint>

9.2.4. DSL transacted() 명령이 필요하지 않음

Transacted() DSL 명령은 트랜잭션 끝점으로 시작하는 경로에 필요하지 않습니다. 그러나 기본 트랜잭션 정책은 PROPAGATION_REQUIRED ( 9.4절. “트랜잭션 전파 정책”참조)라고 가정하면 일반적으로 다음 예제와 같이 Transact ed() 명령을 포함하는 것은 무해합니다.

from("jmstx:queue:giro")
        .transacted()
        .to("jmstx:queue:credits")
        .to("jmstx:queue:debits");

그러나 이 경로는 예기치 않은 방식으로 작동할 수 있습니다(예: 블루프린트 XML에서 기본이 아닌 전파 정책이 있는 single Cryostated Policy VLAN). 9.1.4절. “기본 트랜잭션 관리자 및 트랜잭션 정책”을 참조하십시오. 따라서 일반적으로 트랜잭션 끝점으로 시작하는 경로에 transacted() DSL 명령을 포함하지 않는 것이 좋습니다.

9.2.5. 경로 시작 시 트랜잭션 끝점

다음 Apache Camel 구성 요소는 경로 시작 시 표시되는 경우 트랜잭션 끝점 역할을 합니다(예: from() DSL 명령에 표시되는 경우). 즉, 이러한 끝점은 트랜잭션 클라이언트로 작동하도록 구성할 수 있으며 트랜잭션 리소스에도 액세스할 수 있습니다.

  • ActiveMQ
  • AMQP
  • JavaSpace
  • JMS
  • JPA

9.3. 선언적 트랜잭션의 분리

블루프린트 XML을 사용할 때 블루프린트 XML 파일에 트랜잭션 정책을 선언하여 트랜잭션을 위임할 수도 있습니다. Required 정책과 같이 빈 또는 8080 메서드에 적절한 트랜잭션 정책을 적용하면 특정 Quarkus 또는 Cryostat 메서드가 호출될 때마다 트랜잭션이 시작되도록 할 수 있습니다. Quarkus 메서드 종료 시 트랜잭션이 커밋됩니다. 이 접근 방식은 Enterprise Java Cryostat에서 트랜잭션이 적용되는 방식과 유사합니다.

OSGi 선언적 트랜잭션을 사용하면 블루프린트 파일의 다음 범위에서 트랜잭션 정책을 정의할 수 있습니다.

9.3.3절. “tx에 대한 설명: Cryostat 속성” 도 참조하십시오.

9.3.1. Cryostat 수준 선언

8080 수준에서 트랜잭션 정책을 선언하려면 다음과 같이 tx: Cryostat 요소를^2 요소의 자식으로 삽입합니다.

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
        xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.1.0">

    <bean id="accountFoo" class="org.jboss.fuse.example.Account">
        <tx:transaction method="*" value="Required" />
        <property name="accountName" value="Foo" />
    </bean>

    <bean id="accountBar" class="org.jboss.fuse.example.Account">
        <tx:transaction method="*" value="Required" />
        <property name="accountName" value="Bar" />
    </bean>

</blueprint>

이전 예에서 필수 트랜잭션 정책은 accountFoo 콩 및 accountBar 8080의 모든 메서드에 적용됩니다. 여기서 메서드 속성은 모든 Cryostat 메서드를 일치시키기 위해 와일드카드 * 를 지정합니다.

9.3.2. 최상위 수준 선언

최상위 수준에서 트랜잭션 정책을 선언하려면 다음과 같이 tx: Cryostat 요소를 블루프린트 요소의 자식으로 삽입합니다.

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
        xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.1.0">

    <tx:transaction bean="account*" value="Required" />

    <bean id="accountFoo" class="org.jboss.fuse.example.Account">
        <property name="accountName" value="Foo" />
    </bean>

    <bean id="accountBar" class="org.jboss.fuse.example.Account">
        <property name="accountName" value="Bar" />
    </bean>

</blueprint>

이전 예에서 Required 트랜잭션 정책은 ID 가 패턴인 account* 와 일치하는 모든 빈의 모든 메서드에 적용됩니다.

9.3.3. tx에 대한 설명: Cryostat 속성

tx: Cryostat 요소는 다음 특성을 지원합니다.

Cryostat

(상위 수준만 해당) 트랜잭션 정책이 적용되는 빈 ID(콤마 또는 공백으로 구분된) 목록을 지정합니다. 예를 들면 다음과 같습니다.

<blueprint ...>
    <tx:transaction bean="accountFoo,accountBar" value="..." />
</blueprint>

와일드카드 문자 * 를 사용할 수도 있습니다. 이 문자는 각 목록 항목에서 최대 한 번 표시될 수 있습니다. 예를 들면 다음과 같습니다.

<blueprint ...>
    <tx:transaction bean="account*,jms*" value="..." />
</blueprint>

8080 속성을 생략하면 기본값은 * 입니다( 블루프린트 파일의 모든 비-synthetic 빈과 일치).

method

(최상 및 빈 수준) 트랜잭션 정책이 적용되는 메서드 이름(콤마 또는 공백으로 구분된) 목록을 지정합니다. 예를 들면 다음과 같습니다.

<bean id="accountFoo" class="org.jboss.fuse.example.Account">
    <tx:transaction method="debit,credit,transfer" value="Required" />
    <property name="accountName" value="Foo" />
</bean>

와일드카드 문자 * 를 사용할 수도 있습니다. 이 문자는 각 목록 항목에서 최대 한 번 표시될 수 있습니다.

메서드 속성을 생략하면 기본값은 * (해당 빈의 모든 메서드와 일치)입니다.

value

(상위 수준 및 8080 수준) 트랜잭션 정책을 지정합니다. 정책 값은 다음과 같이 EventListener 3.0 사양에 정의된 정책과 동일한 의미가 있습니다.

  • 필수 - 현재 트랜잭션을 지원하고, 존재하지 않는 경우 새 트랜잭션을 만듭니다.
  • mandatory - 현재 트랜잭션을 지원합니다. 현재 트랜잭션이 없는 경우 예외가 발생합니다.
  • RequiresNew - 새 트랜잭션을 생성하여 현재 트랜잭션이 있는 경우 일시 중지합니다.
  • 지원 - 현재 트랜잭션을 지원하며, 존재하지 않는 경우 비- applications를 실행합니다.
  • NotSupported - 현재 트랜잭션을 지원하지 않습니다. 대신 항상 비 applications를 실행합니다.
  • never - 현재 트랜잭션이 있는 경우 예외를 throw합니다.

9.4. 트랜잭션 전파 정책

트랜잭션 클라이언트가 새 트랜잭션을 생성하는 방식에 영향을 미치려면 JmsTransactionManager 를 사용하고 트랜잭션 정책을 지정할 수 있습니다. 특히 Spring 트랜잭션 정책을 사용하면 트랜잭션에 대한 전파 동작을 지정할 수 있습니다. 예를 들어 트랜잭션 클라이언트가 새 트랜잭션을 만들고 트랜잭션이 현재 스레드와 이미 연결되어 있음을 감지하는 경우 새 트랜잭션을 만들고 새 트랜잭션을 생성해야 합니까? 아니면 기존 트랜잭션이 적용되도록 해야 합니까? 이러한 종류의 동작은 트랜잭션 정책에 전파 동작을 지정하여 규제됩니다.

트랜잭션 정책은 블루프린트 XML에서 빈으로 인스턴스화됩니다. 그런 다음 tradeact ed() DSL 명령에 인수로ans ID 를 제공하여 트랜잭션 정책을 참조할 수 있습니다. 예를 들어 동작에 따라 트랜잭션을 시작하려면 PROPAGATION_REQUIRES_NEW 에서는 다음 경로를 사용할 수 있습니다.

from("file:src/data?noop=true")
        .transacted("PROPAGATION_REQUIRES_NEW")
        .bean("accountService","credit")
        .bean("accountService","debit")
        .to("file:target/messages");

여기서 PROPAGATION_REQUIRES_NEW 인수는 PROPAGATION_REQUIRES_NEW 동작으로 구성된 트랜잭션 정책의 빈 ID 를 지정합니다. 9.4.3절. “블루프린트 XML에서 정책 빈 정의”을 참조하십시오.

9.4.1. Spring 트랜잭션 정책 정보

Apache Camel을 사용하면 org.apache.camel.spring.spi.SpringTransactionPolicy 클래스를 사용하여 Spring 트랜잭션 정책을 정의할 수 있습니다. 이 정책은 기본적으로 네이티브 Spring 클래스와 관련된 래퍼입니다. SpringTransactionPolicy 클래스는 다음 두 가지 데이터를 캡슐화합니다.

  • PlatformTransactionManager 유형의 트랜잭션 관리자에 대한 참조
  • 전파 동작

예를 들어 다음과 같이 PROPAGATION_MANDATORY 동작을 사용하여 Spring 트랜잭션 정책 metrics을 인스턴스화할 수 있습니다.

<blueprint ...>
  <bean id="PROPAGATION_MANDATORY "class="org.apache.camel.spring.spi.SpringTransactionPolicy">
    <property name="transactionManager" ref="txManager" />
    <property name="propagationBehaviorName" value="PROPAGATION_MANDATORY" />
  </bean>
  ...
</blueprint>

9.4.2. 전파 동작 설명

다음 전파 동작은 Spring에서 지원합니다. 이러한 값은 원래 JavaeEE에서 지원하는 전파 동작에 따라 모델링되었습니다.

PROPAGATION_MANDATORY
현재 트랜잭션을 지원합니다. 현재 트랜잭션이 없는 경우 예외를 throw합니다.
PROPAGATION_NESTED

현재 트랜잭션이 있는 경우 중첩된 트랜잭션 내에서 실행되며 PROPAGATION_REQUIRED.

참고

중첩된 트랜잭션은 모든 트랜잭션 관리자가 지원하지 않습니다.

PROPAGATION_NEVER
현재 트랜잭션을 지원하지 않습니다. 현재 트랜잭션이 있는 경우 예외를 throw합니다.
PROPAGATION_NOT_SUPPORTED

현재 트랜잭션을 지원하지 않습니다. 항상 비-SQL을 실행합니다.

참고

이 정책을 사용하려면 모든 트랜잭션 관리자가 지원하지 않는 기능인 현재 트랜잭션을 일시 중단해야 합니다.

PROPAGATION_REQUIRED
(기본값) 현재 트랜잭션을 지원합니다. 존재하지 않는 경우 새 항목을 만듭니다.
PROPAGATION_REQUIRES_NEW

새 트랜잭션을 생성하여 현재 트랜잭션이 있는 경우 일시 중지합니다.

참고

트랜잭션 일시 중단은 모든 트랜잭션 관리자가 지원하지 않습니다.

PROPAGATION_SUPPORTS
현재 트랜잭션을 지원합니다. 존재하지 않는 경우 기본적으로 실행합니다.

9.4.3. 블루프린트 XML에서 정책 빈 정의

다음 예제에서는 지원되는 모든 전파 동작에 대해 트랜잭션 정책 빈을 정의하는 방법을 보여줍니다. 편의를 위해 각 빈 ID는 전파 동작 값의 지정된 값과 일치하지만 실제로는 빈 ID에 원하는 값을 사용할 수 있습니다.

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <bean id="PROPAGATION_MANDATORY " class="org.apache.camel.spring.spi.SpringTransactionPolicy">
        <property name="transactionManager" ref="txManager" />
        <property name="propagationBehaviorName" value="PROPAGATION_MANDATORY" />
    </bean>

    <bean id="PROPAGATION_NESTED" class="org.apache.camel.spring.spi.SpringTransactionPolicy">
        <property name="transactionManager" ref="txManager" />
        <property name="propagationBehaviorName" value="PROPAGATION_NESTED" />
    </bean>

    <bean id="PROPAGATION_NEVER" class="org.apache.camel.spring.spi.SpringTransactionPolicy">
        <property name="transactionManager" ref="txManager" />
        <property name="propagationBehaviorName" value="PROPAGATION_NEVER" />
    </bean>

    <bean id="PROPAGATION_NOT_SUPPORTED" class="org.apache.camel.spring.spi.SpringTransactionPolicy">
        <property name="transactionManager" ref="txManager" />
        <property name="propagationBehaviorName" value="PROPAGATION_NOT_SUPPORTED" />
    </bean>

    <!-- This is the default behavior. -->
    <bean id="PROPAGATION_REQUIRED" class="org.apache.camel.spring.spi.SpringTransactionPolicy">
        <property name="transactionManager" ref="txManager" />
    </bean>

    <bean id="PROPAGATION_REQUIRES_NEW" class="org.apache.camel.spring.spi.SpringTransactionPolicy">
        <property name="transactionManager" ref="txManager" />
        <property name="propagationBehaviorName" value="PROPAGATION_REQUIRES_NEW" />
    </bean>

    <bean id="PROPAGATION_SUPPORTS" class="org.apache.camel.spring.spi.SpringTransactionPolicy">
        <property name="transactionManager" ref="txManager" />
        <property name="propagationBehaviorName" value="PROPAGATION_SUPPORTS" />
    </bean>

</blueprint>
참고

이러한 Quarkus 정의를 자체 블루프린트 XML 구성에 붙여넣려면 트랜잭션 관리자에 대한 참조를 사용자 지정해야 합니다. 즉, txManager 에 대한 참조를 트랜잭션 관리자 metrics의 실제 ID 로 바꿉니다.

9.4.4. Java DSL의 PROPAGATION_NEVER 정책이 포함된 샘플 경로

트랜잭션 정책이 트랜잭션에 영향을 미치는 간단한 방법은 다음 경로에 표시된 것처럼 기존 트랜잭션 중간에 PROPAGATION_NEVER 정책을 삽입하는 것입니다.

from("file:src/data?noop=true")
        .transacted()
        .bean("accountService","credit")
        .transacted("PROPAGATION_NEVER")
        .bean("accountService","debit");

이러한 방식으로 사용되는 PROPAGATION_NEVER 정책은 모든 트랜잭션을 필연적으로 중단하여 트랜잭션 롤백을 생성합니다. 애플리케이션에 미치는 영향을 쉽게 확인할 수 있습니다.

참고

Transact ed() 에 전달된 문자열 값은 전파 동작 이름이 아니라 빈 ID 입니다. 이 예에서 빈 ID 는 전파 동작 이름과 동일하게 선택되지만 항상 그런 것은 아닙니다. 예를 들어 애플리케이션이 두 개 이상의 트랜잭션 관리자를 사용하는 경우 특정 전파 동작이 있는 정책VLAN을 두 개 이상 사용할 수 있습니다. 이 경우 전파 동작 후 빈의 이름을 지정할 수 없었습니다.

9.4.5. 블루프린트 XML에서 PROPAGATION_NEVER 정책이 포함된 샘플 경로

이전 경로는 블루프린트 XML에서 다음과 같이 정의할 수 있습니다.

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <camelContext xmlns="http://camel.apache.org/schema/blueprint">
        <route>
            <from uri="file:src/data?noop=true" />
            <transacted />
            <bean ref="accountService" method="credit" />
            <transacted ref="PROPAGATION_NEVER" />
            <bean ref="accountService" method="debit" />
        </route>
    </camelContext>

</blueprint>

9.5. 오류 처리 및 롤백

트랜잭션 경로에서 표준 Apache Camel 오류 처리 기술을 사용할 수 있지만 예외와 트랜잭션 요약 간의 상호 작용을 이해하는 것이 중요합니다. 특히 throw된 예외로 인해 일반적으로 트랜잭션 롤백이 발생하는 것을 고려해야 합니다. 다음 항목을 참조하십시오.

9.5.1. 트랜잭션을 롤백하는 방법

다음 방법 중 하나를 사용하여 트랜잭션을 롤백할 수 있습니다.

9.5.1.1. 런타임 예외를 사용하여 롤백 트리거

Spring 트랜잭션을 롤백하는 가장 일반적인 방법은 런타임(확인되지 않음) 예외를 throw하는 것입니다. 즉, 예외는 java.lang.RuntimeException 의 인스턴스 또는 하위 클래스입니다. java.lang.Error 유형의 Java 오류도 트랜잭션 롤백을 트리거합니다. 반면 검사된 예외는 롤백을 트리거하지 않습니다.

다음 그림은 Java 오류 및 예외가 트랜잭션에 미치는 영향을 요약한 것으로, 롤백을 트리거하는 클래스는 회색으로 표시됩니다.

Camel 예외
참고

Spring 프레임워크는 롤백을 트리거하거나 트리거하지 않아야 하는 예외를 지정할 수 있는 XML 주석 시스템도 제공합니다. 자세한 내용은 Spring Reference Guide의 "Rolling back"를 참조하십시오.

주의

트랜잭션 내에서 런타임 예외가 처리되는 경우, 즉 예외가 트랜잭션 분리를 수행하는 코드로 대체될 가능성이 있기 전에 트랜잭션이 롤백되지 않습니다. 자세한 내용은 9.5.2절. “dead letter queue를 정의하는 방법”의 내용을 참조하십시오.

9.5.1.2. rollback() DSL 명령 사용

트랜잭션된 경로의 중간에서 롤백을 트리거하려면 org.apache.camel.RollbackExchangeException 예외를 throw하는 rollback() DSL 명령을 호출하여 이 작업을 수행할 수 있습니다. 즉, rollback() 명령은 런타임 예외를 발생시켜 롤백을 트리거하는 표준 접근 방식을 사용합니다.

예를 들어 계정 서비스 애플리케이션에서 수익 이전 크기에 대한 절대 제한이 있어야 한다고 가정합니다. 다음 예제의 코드를 사용하여 양이 100을 초과하면 롤백을 트리거할 수 있습니다.

from("file:src/data?noop=true")
    .transacted()
    .bean("accountService","credit")
    .choice().when(xpath("/transaction/transfer[amount > 100]"))
        .rollback()
    .otherwise()
        .to("direct:txsmall");

from("direct:txsmall")
    .bean("accountService","debit")
    .bean("accountService","dumpTable")
    .to("file:target/messages/small");
참고

이전 경로에서 롤백을 트리거하면 무한 루프에 갇히게 됩니다. 그 이유는 rollback() 에서 throw한 RollbackExchangeException 예외가 경로 시작 시 파일 끝점으로 다시 전파되기 때문입니다. File 구성 요소에는 예외가 throw된 모든 교환을 다시 보낼 수 있는 기본 제공 안정성 기능이 있습니다. 물론 다시 전송하면 교환이 다른 롤백을 트리거하여 무한 루프가 발생합니다. 다음 예제에서는 무한 루프를 방지하는 방법을 보여줍니다.

9.5.1.3. markRollbackOnly() DSL 명령 사용

markRollbackOnly() DSL 명령을 사용하면 예외를 발생하지 않고 현재 트랜잭션이 롤백되도록 할 수 있습니다. 이 기능은 예외에 9.5.1.2절. “rollback() DSL 명령 사용” 의 예와 같이 원하지 않는 부작용을 발생시킬 때 유용할 수 있습니다.

다음 예제에서는 rollback() 명령을 markRollbackOnly() 명령으로 교체하여 9.5.1.2절. “rollback() DSL 명령 사용” 의 예제를 수정하는 방법을 보여줍니다. 이 버전의 경로는 무한 루프의 문제를 해결합니다. 이 경우 수익 이전 양이 100을 초과하면 현재 트랜잭션이 롤백되지만 예외는 발생하지 않습니다. 파일 끝점에서 예외를 수신하지 않으므로 교환을 재시도하지 않고 실패한 트랜잭션이 자동으로 삭제됩니다.

다음 코드는 markRollbackOnly() 명령을 사용하여 예외를 롤백합니다.

from("file:src/data?noop=true")
    .transacted()
    .bean("accountService","credit")
    .choice().when(xpath("/transaction/transfer[amount > 100]"))
        .markRollbackOnly()
    .otherwise()
        .to("direct:txsmall");
...

그러나 앞의 경로 구현에는 적합하지 않습니다. 경로가 트랜잭션을 깔끔하게 롤백하고 일관된 상태의 데이터베이스를 그대로 롤백하고 무한 루프 처리 문제를 방지하지만 실패한 트랜잭션에 대한 레코드를 유지하지 않습니다. 실제 애플리케이션에서는 일반적으로 실패한 트랜잭션을 추적하려고 합니다. 예를 들어 트랜잭션이 성공하지 못한 이유를 설명하기 위해 관련 고객에게 문자를 작성하고자 할 수 있습니다. 실패한 트랜잭션을 추적하는 편리한 방법은 경로에 dead-letter queue를 추가하는 것입니다.

9.5.2. dead letter queue를 정의하는 방법

실패한 트랜잭션을 추적하기 위해 관련 교환 개체를 dead-letter queue로 전환할 수 있는 onException() 절을 정의할 수 있습니다. 그러나 트랜잭션 컨텍스트에서 사용하는 경우 예외 처리와 트랜잭션 처리 간의 잠재적인 상호 작용으로 인해 onException() 절을 정의하는 방법에 대해 주의해야 합니다. 다음 예제에서는 rethrown 예외를 억제해야 한다고 가정하면 onException() 절을 정의하는 올바른 방법을 보여줍니다.

// Java
import org.apache.camel.builder.RouteBuilder;

public class MyRouteBuilder extends RouteBuilder {
    ...
    public void configure() {
        onException(IllegalArgumentException.class)
            .maximumRedeliveries(1)
            .handled(true)
            .to("file:target/messages?fileName=deadLetters.xml&fileExist=Append")
            .markRollbackOnly();  // NB: Must come *after* the dead letter endpoint.

        from("file:src/data?noop=true")
            .transacted()
            .bean("accountService","credit")
            .bean("accountService","debit")
            .bean("accountService","dumpTable")
            .to("file:target/messages");
    }
}

이전 예에서 onException()IllegalArgumentException 예외를 catch하고 잘못된 교환을 dead letter file dead letter file, deadLet Cryostat.xml 로 전송하도록 구성되어 있습니다. 물론 이 정의를 변경하여 애플리케이션에서 발생하는 모든 종류의 예외를 파악할 수 있습니다. 예외 다시 화살표 동작 및 트랜잭션 롤백 동작은 onException() 절의 다음과 같은 특수 설정으로 제어됩니다.

  • handled(true) - rethrown 예외를 비활성화합니다. 이 특정 예에서 rethrown 예외는 파일 엔드포인트로 다시 전파될 때 무한 루프를 트리거하기 때문에 바람직하지 않습니다. 9.5.1.3절. “markRollbackOnly() DSL 명령 사용”을 참조하십시오. 그러나 경로 시작 시 끝점이 재시도 기능을 구현하지 않는 경우 예외를 다시 시작하는 것이 허용되는 경우도 있습니다.
  • markRollbackOnly() - 예외를 throw하지 않고 롤백의 현재 트랜잭션을 표시합니다. 교환을 dead letter queue로 라우팅하는 to() 명령 뒤에 이 DSL 명령을 삽입하는 것이 중요합니다. 그렇지 않으면 markRollbackOnly() 가 처리 체인을 중단하기 때문에 교환은 dead letter 큐에 도달하지 않습니다.

9.5.3. 트랜잭션 관련 예외 처리

onException() 을 사용하는 대신 트랜잭션 경로에서 예외를 처리하는 간단한 방법은 경로 주위에 doTry()doCatch() 절을 사용하는 것입니다. 예를 들어 다음 코드는 무한 루프에 갇혀 있을 위험 없이 트랜잭션 경로에서 IllegalArgumentException 을 catch하고 처리하는 방법을 보여줍니다.

// Java
import org.apache.camel.builder.RouteBuilder;

public class MyRouteBuilder extends RouteBuilder {
    ...
    public void configure() {
        from("file:src/data?noop=true")
            .doTry()
                .to("direct:split")
            .doCatch(IllegalArgumentException.class)
                .to("file:target/messages?fileName=deadLetters.xml&fileExist=Append")
            .end();

        from("direct:split")
            .transacted()
            .bean("accountService","credit")
            .bean("accountService","debit")
            .bean("accountService","dumpTable")
            .to("file:target/messages");
    }
}

이 예에서 경로는 두 세그먼트로 나뉩니다. 첫 번째 세그먼트( 파일:src/data 끝점)는 들어오는 교환을 수신하고 doTry()doCatch() 를 사용하여 예외 처리를 수행합니다. 두 번째 세그먼트( 직접:split 끝점)는 모든 트랜잭션 작업을 수행합니다. 이 트랜잭션 세그먼트 내에서 예외가 발생하면 먼저 transacted() 명령으로 전파되므로 현재 트랜잭션이 롤백되고 첫 번째 경로 세그먼트의 doCatch() 절에 의해 catch됩니다. doCatch() 절은 예외를 다시 가져오지 않으므로 파일 끝점에서 재시도를 수행하지 않고 무한 루프를 방지할 수 있습니다.

법적 공지

Copyright © 2023 Red Hat, Inc.
The text of and illustrations in this document are licensed by Red Hat under a Creative Commons Attribution–Share Alike 3.0 Unported license ("CC-BY-SA"). An explanation of CC-BY-SA is available at http://creativecommons.org/licenses/by-sa/3.0/. In accordance with CC-BY-SA, if you distribute this document or an adaptation of it, you must provide the URL for the original version.
Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.
Red Hat, Red Hat Enterprise Linux, the Shadowman logo, the Red Hat logo, JBoss, OpenShift, Fedora, the Infinity logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries.
Linux® is the registered trademark of Linus Torvalds in the United States and other countries.
Java® is a registered trademark of Oracle and/or its affiliates.
XFS® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United States and/or other countries.
MySQL® is a registered trademark of MySQL AB in the United States, the European Union and other countries.
Node.js® is an official trademark of Joyent. Red Hat is not formally related to or endorsed by the official Joyent Node.js open source or commercial project.
The OpenStack® Word Mark and OpenStack logo are either registered trademarks/service marks or trademarks/service marks of the OpenStack Foundation, in the United States and other countries and are used with the OpenStack Foundation's permission. We are not affiliated with, endorsed or sponsored by the OpenStack Foundation, or the OpenStack community.
All other trademarks are the property of their respective owners.