Solaris OS용 Sun Cluster 데이터 서비스 개발 안내서

12장 CRNP

이 장에서는 CRNP(Cluster Reconfiguration Notification Protocol)에 대해 설명합니다. CRNP를 사용하여 페일오버와 확장 가능 응용 프로그램이 “클러스터를 인식”하도록 할 수 있습니다. 특히 CRNP는 응용 프로그램이 Sun Cluster 재구성 이벤트의 후속 비동기 알림을 등록 및 수신할 수 있게 하는 기법을 제공합니다. 클러스터 내에서 실행되는 데이터 서비스와 클러스터 외부에서 실행되는 응용 프로그램은 이벤트 알림을 등록할 수 있습니다. 이벤트는 클러스터의 구성원이 변경될 경우와 자원 그룹이나 자원의 상태가 변경될 경우에 생성됩니다.


주 –

SUNW.Event 자원 유형 구현은 Sun Cluster에 고가용성 CRNP 서비스를 제공합니다. 이 자원 유형 구현에 대한 자세한 내용은 SUNW.Event(5) 설명서 페이지를 참조하십시오.


이 장은 다음 내용으로 구성되어 있습니다.

CRNP 개념

CRNP는 7개 계층의 표준 OSI (Open System Interconnect) 프로토콜 스택에서 응용 프로그램, 표현 및 세션 계층을 정의합니다. 전송 계층은 TCP여야 하며 네트워크 계층은 IP여야 합니다. CRNP는 데이터 링크 및 물리 계층과 무관합니다. CRNP에서 교환되는 모든 응용 프로그램 계층 메시지는 XML 1.0에 기초합니다.

CRNP 작동 방법

CRNP는 클러스터 재구성 이벤트를 생성하고 클러스터를 통해 이벤트를 라우팅하며 원하는 클라이언트에게 보내는 기법과 데몬을 제공합니다.

cl_apid 데몬은 클라이언트와 상호 작용하며Sun Cluster RGM(Resource Group Manager)은 클러스터 재구성 이벤트를 생성합니다. 이 데몬은 syseventd를 사용하여 각 로컬 노드에서 이벤트를 전송합니다. cl_apid 데몬은 TCP/IP를 통한 XML(Extensible Markup Language)을 사용하여 원하는 클라이언트와 통신합니다.

다음 다이어그램은 CRNP 구성 요소 간의 이벤트 흐름을 보여줍니다. 이 다이어그램에서 한 클라이언트는 클러스터 노드 2에서 실행되고 있고 다른 클라이언트는 클러스터의 일부가 아닌 컴퓨터에서 실행되고 있습니다.

그림 12–1 CRNP 구성 요소 간의 이벤트 흐름

CRNP의 작동 방법을 보여주는 흐름도

CRNP 의미

클라이언트는 등록 메시지(SC_CALLBACK_RG)를 서버에 보내 통신을 시작합니다. 이 등록 메시지는 클라이언트가 알림을 받으려는 이벤트 유형과 이벤트를 수신할 포트를 지정합니다. 등록 연결 및 지정한 포트의 소스 IP가 함께 콜백 주소를 구성합니다.

클러스터 내에서 클라이언트에 대한 해당 이벤트가 생성될 때마다 서버는 콜백 주소(IP 및 포트)의 클라이언트에 연결되어 이벤트(SC_EVENT)를 클라이언트에 전달합니다. 서버는 클러스터 자체 내에서 실행되며 높은 수준의 가용성을 가집니다. 서버는 클러스터가 재부트된 후에도 지속되는 저장소에 클라이언트 등록을 저장합니다.

클라이언트는 등록 메시지(REMOVE_CLIENT 메시지를 포함하는 SC_CALLBACK_RG)를 서버에 보내 등록을 취소합니다. 클라이언트는 SC_REPLY 메시지를 서버로부터 받은 후에 연결을 닫습니다.

다음 다이어그램은 클라이언트와 서버 간의 통신 흐름을 보여줍니다.

그림 12–2 클라이언트와 서버 간의 통신 흐름

클라이언트와 서버 간의 통신 흐름을 보여주는 흐름도

CRNP 메시지 유형

CRNP는 다음 표에 설명된 세 가지 유형의 XML 기반 메시지를 사용합니다. 이러한 메시지 유형에 대해서는 나중에 자세히 설명됩니다.

CRNP 메시지 유형 

설명 

SC_CALLBACK_REG

이 메시지는ADD_CLIENT, REMOVE_CLIENT , ADD_EVENTSREMOVE_EVENTS의 네 가지 형식을 가집니다. 이러한 형식은 각각 다음 정보를 포함합니다.

 

  • 프로토콜 버전

  • ASCII 형식의 콜백 포트(이진 형식 아님)

또한 ADD_CLIENT, ADD_EVENTS REMOVE_EVENTS 형식은 각각 다음 정보를 포함하는 바운드되지 않은 이벤트 유형 목록을 포함합니다.

 

  • 이벤트 클래스

  • (옵션) 이벤트 하위 클래스

  • (옵션) 이름 및 값 쌍 목록

이벤트 클래스와 이벤트 하위 클래스가 함께 고유한 “이벤트 유형”을 정의합니다. SC_CALLBACK_REG의 클래스가 생성되는 문서 유형 정의(DTD)는 SC_CALLBACK_REG입니다. 이 DTD에 대한 자세한 내용은 부록 F, CRNP용 문서 유형 정의를 참조하십시오.

SC_REPLY

이 메시지에는 다음 정보가 포함되어 있습니다. 

  • 프로토콜 버전

  • 오류 코드

  • 오류 메시지

SC_REPLY의 클래스가 생성되는 DTD는 SC_REPLY입니다. 이 DTD에 대한 자세한 내용은 부록 F, CRNP용 문서 유형 정의를 참조하십시오.

SC_EVENT

이 메시지에는 다음 정보가 포함되어 있습니다. 

  • 프로토콜 버전

  • 이벤트 클래스

  • 이벤트 하위 클래스

  • 공급업체

  • 게시자

  • 이름 및 값 쌍 목록(0개 이상의 이름 및 값 쌍 데이터 구조)

    • 이름(문자열)

    • 값(문자열 또는 문자열 배열)

SC_EVENT의 값은 입력할 수 없습니다. SC_EVENT의 클래스가 생성되는 DTD는 SC_EVENT입니다. 이 DTD에 대한 자세한 내용은 부록 F, CRNP용 문서 유형 정의를 참조하십시오.

클라이언트가 서버에 등록되는 방법

이 절에서는 클러스터 관리자가 서버를 설정하는 방법, 클라이언트가 식별되는 방법, 응용 프로그램 및 세션 계층을 통해 정보가 전송되는 방법 및 오류 상태에 대해 설명합니다.

관리자의 서버 설정 방법에 대한 가정

클러스터 관리자는 고가용성 IP 주소(즉, 클러스터의 특정 시스템에 연결되지 않은 IP 주소)와 포트 번호를 사용하여 서버를 구성해야 합니다. 또한 이 네트워크 주소를 예정된 클라이언트에 게시해야 합니다. CRNP는 이 서버 이름을 클라이언트가 사용할 수 있게 하는 방법을 정의하지 않습니다. 클러스터 관리자는 클라이언트가 서버의 네트워크 주소를 동적으로 찾을 수 있도록 하는 이름 지정 서비스를 사용하거나 클라이언트가 읽을 수 있도록 구성 파일에 네트워크 이름을 추가합니다. 서버는 클러스터 내에서 페일오버 자원 유형으로 실행됩니다.

서버가 클라이언트를 식별하는 방법

각 클라이언트는 콜백 주소, 즉 해당 IP 주소와 포트 번호에 의해 고유하게 식별됩니다. 포트는 SC_CALLBACK_REG 메시지에 지정되며 IP 주소는 TCP 등록 연결로부터 가져옵니다. CRNP는 동일한 콜백 주소를 가진 후속 SC_CALLBACK_REG 메시지가 동일한 클라이언트로부터 온다고 가정하며 이는 메시지를 보낸 원본 포트가 다른 경우에도 마찬가지입니다.

클라이언트와 서버 간에 SC_CALLBACK_REG 메시지가 전달되는 방법

클라이언트는 서버의 IP 주소와 포트 번호에 대한 TCP 연결을 열어 등록을 시작합니다. TCP 연결이 설정되고 쓰기가 가능해지면 클라이언트는 해당 등록 메시지를 보내야 합니다. 등록 메시지는 메시지 앞뒤에 추가 바이트를 포함하지 않아야 하며 서식이 올바로 지정된 단일 SC_CALLBACK_REG 메시지여야 합니다.

모든 바이트가 스트림에 기록되고 나면 클라이언트는 서버로부터 응답을 받기 위해 연결을 열어 두어야 합니다. 클라이언트가 메시지의 서식을 올바로 지정하지 않을 경우 서버는 클라이언트를 등록하지 않으며 오류 응답을 클라이언트에게 보냅니다. 그러나 서버가 응답을 보내기 전에 클라이언트가 소켓 연결을 닫을 경우 서버는 정상적으로 클라이언트를 등록합니다.

클라이언트는 언제든지 서버에 연결할 수 있습니다. 클라이언트는 서버에 연결할 때마다 SC_CALLBACK_REG 메시지를 보내야 합니다. 잘못되었거나, 문제가 있거나, 유효하지 않은 메시지가 전달되면 서버는 클라이언트에게 오류 응답을 보냅니다.

클라이언트는 ADD_CLIENT 메시지를 보내기 전까지 ADD_EVENTS, REMOVE_EVENTS 또는 REMOVE_CLIENT 메시지를 보낼 수 없습니다. 클라이언트는 ADD_CLIENT 메시지를 보내기 전까지 REMOVE_CLIENT 메시지를 보낼 수 없습니다.

클라이언트가 이미 등록되어 있는 상태에서 ADD_CLIENT 메시지가 전달될 경우 서버는 이 메시지를 허용할 수 있습니다. 이 경우 서버는 이전 클라이언트 등록을 두 번째 ADD_CLIENT 메시지에 지정된 새 클라이언트 등록으로 자동으로 대체합니다.

대부분의 경우 클라이언트는 시작될 때 ADD_CLIENT 메시지를 보냄으로써 서버에 한 차례 등록합니다. 또한 클라이언트는 REMOVE_CLIENT 메시지를 서버로 보내 등록을 한 번 취소합니다. 그러나 CRNP는 자신의 이벤트 유형 목록을 동적으로 수정해야 하는 클라이언트를 위한 더 많은 유연성을 제공합니다.

SC_CALLBACK_REG 메시지의 내용

ADD_CLIENT, REMOVE_CLIENT, ADD_EVENTSREMOVE_EVENTS 메시지는 이벤트 목록을 포함합니다. 다음 표에는 CRNP에서 허용하는 이벤트 유형과 함께 필수 이름 및 값 쌍이 설명되어 있습니다.

클라이언트가 다음 작업 중 하나를 수행하면 서버는 이러한 메시지를 자동으로 무시합니다.

클래스 및 하위 클래스 

이름 및 값 쌍 

설명 

EC_Cluster

ESC_cluster_membership

필수: 없음 

선택 사항: 없음 

모든 클러스터 구성원 변경 이벤트(노드 상실 또는 결합)를 등록합니다. 

EC_Cluster

ESC_cluster_rg_state

다음과 같은 필수 항목 하나: 

rg_name

값 유형: 문자열 

선택 사항: 없음 

자원 그룹 name에 대한 모든 상태 변경 이벤트를 등록합니다.

EC_Cluster

ESC_cluster_r_state

다음과 같은 필수 항목 하나: 

r_name

값 유형: 문자열 

선택 사항: 없음 

자원 이름에 대한 모든 상태 변경 이벤트를 등록합니다.

EC_Cluster

없음 

필수: 없음 

선택 사항: 없음 

모든 Sun Cluster 이벤트를 등록합니다. 

서버에서 클라이언트에 응답하는 방법

등록을 처리한 후 등록 요청을 받은 서버는 클라이언트에서 열린 TCP 연결을 통해 SC_REPLY 메시지를 보낸 다음 연결을 닫습니다. 클라이언트는 서버로부터 SC_REPLY 메시지를 받을 때까지 TCP 연결을 열어 두어야 합니다.

예를 들어, 클라이언트는 다음 작업을 수행합니다.

  1. 서버에 대한 TCP 연결을 엽니다.

  2. 연결이 “쓸 수 있는 상태”가 될 때까지 기다립니다.

  3. ADD_CLIENT 메시지를 포함하는 SC_CALLBACK_REG 메시지를 보냅니다.

  4. 서버로부터 SC_REPLY 메시지를 기다립니다.

  5. 서버로부터 SC_REPLY 메시지를 수신합니다.

  6. 서버가 연결을 닫았다는 표시를 수신합니다(소켓에서 0바이트를 읽음).

  7. 연결을 닫습니다.

그런 다음 클라이언트는 나중에 다음 작업을 수행합니다.

  1. 서버에 대한 TCP 연결을 엽니다.

  2. 연결이 “쓸 수 있는 상태”가 될 때까지 기다립니다.

  3. REMOVE_CLIENT 메시지를 포함하는 SC_CALLBACK_REG 메시지를 보냅니다.

  4. 서버로부터 SC_REPLY 메시지를 기다립니다.

  5. 서버로부터 SC_REPLY 메시지를 수신합니다.

  6. 서버가 연결을 닫았다는 표시를 수신합니다(소켓에서 0바이트를 읽음).

  7. 연결을 닫습니다.

서버는 클라이언트로부터 SC_CALLBACK_REG 메시지를 받을 때마다 열려 있는 동일한 연결을 통해 SC_REPLY 메시지를 보냅니다. 이 메시지는 작업의 성공 또는 실패 여부를 지정합니다. SC_REPLY XML DTD에는 SC_REPLY 메시지의 XML 문서 유형 정의와 이 메시지가 포함할 수 있는 가능한 오류 메시지가 포함되어 있습니다.

SC_REPLY 메시지의 내용

SC_REPLY 메시지는 작업에 성공 또는 실패했는지를 지정합니다. 이 메시지는 CRNP 메시지의 버전, 상태 코드 및 상태 코드를 자세하게 설명하는 상태 메시지를 포함합니다. 다음 표에는 가능한 상태 코드 값이 설명되어 있습니다.

상태 코드 

설명 

OK

메시지가 성공적으로 처리되었습니다. 

RETRY

일시적인 오류로 인하여 서버에서 클라이언트 등록을 거부했습니다. 클라이언트가 다른 인자를 사용하여 다시 등록을 시도해야 합니다. 

LOW_RESOURCE

클러스터 자원이 부족하므로 클라이언트가 나중에 다시 시도해야 합니다. 클러스터의 클러스터 관리자가 클러스터의 자원을 늘릴 수도있습니다.  

SYSTEM_ERROR

심각한 문제가 발생했습니다. 클러스터 관리자에게 클러스터에 대해 문의하십시오.  

FAIL

인증에 실패했거나 다른 문제로 인해 등록에 실패했습니다. 

MALFORMED

XML 요청이 잘못되었거나 구문 분석하지 못했습니다. 

INVALID

XML 요청이 유효하지 않습니다(즉, XML 사양을 충족하지 않음).  

VERSION_TOO_HIGH

메시지 버전이 너무 높아 메시지를 성공적으로 처리하지 못했습니다.  

VERSION_TOO_LOW

메시지의 버전이 너무 낮아 메시지를 성공적으로 처리하지 못했습니다.  

클라이언트의 오류 상태 처리 방법

정상적인 상태에서 SC_CALLBACK_REG 메시지를 보내는 클라이언트는 등록이 성공 또는 실패했는지를 나타내는 응답을 받습니다.

그러나 클라이언트를 등록하는 동안 서버에 오류 상태가 발생하여 서버에서 SC_REPLY 메시지를 클라이언트로 보내지 못할 수 있습니다. 이러한 경우 등록은 오류 상태가 발생하기 전에 성공했거나, 실패했거나, 아직 처리되지 않았을 수 있습니다.

서버는 클러스터에서 페일오버 또는 고가용성 서버로 작동해야 하므로 이 오류 상태로 인해 서비스가 종료되는 것은 아닙니다. 실제로 서버는 새로 등록된 클라이언트에 대한 이벤트 전송을 곧바로 시작할 수 있습니다.

이러한 문제를 해결하려면 클라이언트가 다음 작업을 수행해야 합니다.

서버에서 클라이언트에 이벤트를 전달하는 방법

클러스터 내에서 이벤트가 생성되면 CRNP 서버는 해당 유형의 이벤트를 요청한 모든 클라이언트에게 이벤트를 전달합니다. 이러한 전달은 SC_EVENT 메시지를 클라이언트의 콜백 주소로 보내는 것으로 이루어집니다. 각 이벤트의 전달은 새 TCP 연결에서 수행됩니다.

ADD_CLIENT 메시지나 ADD_EVENT 메시지를 포함하는 SC_CALLBACK_REG 메시지를 통해 클라이언트가 이벤트 유형을 등록한 직후에 서버는 해당 유형의 최신 이벤트를 클라이언트에게 보냅니다. 이렇게 하면 클라이언트는 후속 이벤트를 보내는 시스템의 현재 상태를 확인할 수 있습니다.

서버는 클라이언트에 대한 TCP 연결을 시작할 때 정확하게 하나의 SC_EVENT 메시지를 연결을 통해 보냅니다. 그런 다음 서버는 전이중 닫기를 실행합니다.

예를 들어, 클라이언트는 다음 작업을 수행합니다.

  1. 서버가 TCP 연결을 시작하기를 기다립니다.

  2. 서버로부터 들어오는 연결을 수락합니다.

  3. 서버로부터 SC_EVENT 메시지를 기다립니다.

  4. 서버로부터 SC_EVENT 메시지를 읽습니다.

  5. 서버가 연결을 닫았다는 표시를 수신합니다(소켓에서 0바이트를 읽음).

  6. 연결을 닫습니다.

등록된 모든 클라이언트는 해당 콜백 주소(IP 주소 및 포트 번호)에서 들어오는 이벤트 전달 연결을 계속 수신해야 합니다.

서버가 이벤트 전달을 위해 클라이언트에 연결하는 데 실패할 경우 지정된 간격으로 여러 번에 걸쳐 이벤트 전달을 다시 시도합니다. 모든 시도에 실패하면 서버의 클라이언트 목록에서 해당 클라이언트가 제거됩니다. 또한 클라이언트는 ADD_CLIENT 메시지를 포함하는 다른 SC_CALLBACK_REG 메시지를 보내 다시 등록해야만 추가 이벤트를 받을 수 있습니다.

이벤트 전달을 보장하는 방법

클러스터에서의 전체 이벤트 생성 순서가 각 클라이언트에 대한 이벤트 전달 순서가 됩니다. 다시 말해서 클러스터 내에서 이벤트 A가 이벤트 B보다 먼저 생성된 경우 클라이언트 X는 이벤트 B를 받기 전에 이벤트를 A를 받습니다. 그러나 모든 클라이언트에 대해 전체 이벤트 전달 순서가 유지되는 것은 아닙니다. 즉, 클라이언트 Y는 클라이언트 X가 이벤트 A를 받기 전에 이벤트 A와 B를 모두 받을 수 있습니다. 이는 속도가 느린 클라이언트가 모든 클라이언트에 대한 이벤트 전달을 방해하지 않는다는 것을 의미합니다.

클러스터가 생성한 이벤트를 누락시키는 오류가 서버에서 발생하는 경우를 제외하고 서버가 전달하는 모든 이벤트(하위 클래스에 대한 첫 번째 이벤트와 서버 오류 이후의 이벤트는 제외)는 클러스터가 생성하는 실제 이벤트에 응답하여 발생합니다. 이 경우 서버는 해당 유형의 시스템의 현재 상태를 나타내는 각 이벤트 유형에 대해 이벤트를 생성합니다. 각 이벤트는 해당 이벤트 유형을 원하는 등록된 클라이언트에게 보내집니다.

이벤트 전달에는 “최소한 한 번 이상”이라는 표현이 적용됩니다. 즉, 서버에서 동일한 이벤트를 클라이언트에 두 번 이상 보낼 수 있습니다. 이 기능은 서버가 일시적으로 다운되었다가 다시 원래 상태로 돌아왔을 때 클라이언트가 최신 정보를 받았는지 확인할 수 없는 경우에 필요합니다.

SC_EVENT 메시지의 내용

SC_EVENT 메시지는 클러스터 내에서 생성되어 SC_EVENT XML 메시지 형식에 맞게 변환된 실제 메시지를 포함합니다. 다음 표에는 이름 및 값 쌍, 게시자 및 공급업체를 비롯하여 CRNP가 제공하는 이벤트 유형이 설명되어 있습니다.


주 –

state_list에 대한 배열 요소의 위치는 node_list에 대한 배열 요소의 위치와 동기화됩니다. 즉, node_list 배열에 첫 번째로 나열된 노드의 상태는 state_list 배열에서도 첫 번째로 나열됩니다.

ev_로 시작하는 추가 이름과 관련 값이 존재할 수 있지만 이러한 값은 클라이언트가 사용하도록 되어 있지 않습니다.


클래스 및 하위 클래스 

게시자 및 공급업체 

이름 및 값 쌍 

EC_Cluster

ESC_cluster_membership

게시자: rgm 

공급업체: SUNW 

이름: node_list

값 유형: 문자열 배열 

이름: state_list

state_list는 ASCII로 표시되는 숫자만 포함합니다. 각 숫자는 클러스터의 해당 노드에 대한 현재 구현 번호를 나타냅니다. 이 번호가 이전 메시지에서 받은 번호와 같을 경우 노드의 클러스터에 대한 관계(이탈, 결합 또는 재결합)가 변경되지 않은 것입니다. 구현 번호가 –1일 경우 노드는 클러스터의 구성원이 아닙니다. 구현 번호가 음수가 아닌 숫자일 경우 노드는 클러스터의 구성원입니다.

값 유형: 문자열 배열 

EC_Cluster

ESC_cluster_rg_state

게시자: rgm 

공급업체: SUNW 

이름: rg_name

값 유형: 문자열 

이름: node_list

값 유형: 문자열 배열 

이름: state_list

state_list는 자원 그룹의 상태에 대한 문자열 표시를 포함합니다. 유효한 값은 scha_cmds(1HA) 명령으로 검색할 수 있는 값입니다.

값 유형: 문자열 배열 

EC_Cluster

ESC_cluster_r_state

게시자: rgm 

공급업체: SUNW 

이름: r_name

값 유형: 문자열 

이름: node_list

값 유형: 문자열 배열 

이름: state_list

state_list는 자원의 상태에 대한 문자열 표시를 포함합니다. 유효한 값은 scha_cmds(1HA) 명령으로 검색할 수 있는 값입니다.

값 유형: 문자열 배열 

CRNP의 클라이언트 및 서버 인증 방법

서버는 TCP 래퍼 형태를 사용하여 클라이언트를 인증합니다. 이벤트가 전달되는 콜백 IP 주소로도 사용되는 등록 메시지의 소스 IP 주소는 서버에서 허용하는 클라이언트 목록 에 있어야 합니다. 소스 IP 주소 및 등록 메시지가 거부된 클라이언트 목록에 있으면 안 됩니다. 소스 IP 주소 및 등록이 목록에 없을 경우 서버는 요청을 거부하고 클라이언트에게 오류 응답을 보냅니다.

서버가 SC_CALLBACK_REG ADD_CLIENT 메시지를 받으면 해당 클라이언트에 대한 후속 SC_CALLBACK_REG 메시지는 첫 번째 메시지의 소스 IP 주소와 동일한 소스 IP 주소를 포함해야 합니다. CRNP 서버는 이 요구 사항을 충족하지 않는 SC_CALLBACK_REG를 받을 경우 다음 작업 중 하나를 수행합니다.

이 보안 기법은 누군가가 정당한 클라이언트의 등록을 취소하려고 시도하는 서비스 거부 공격을 방지하는 데 도움이 됩니다.

또한 클라이언트도 마찬가지로 서버를 인증해야 합니다. 클라이언트는 소스 IP 주소와 포트 번호가 자신이 사용했던 등록 IP 주소 및 포트 번호와 같은 서버의 이벤트 전달만 수락하면 됩니다.

CRNP 서비스의 클라이언트가 클러스터를 보호하는 방화벽 안쪽에 있다고 가정하기 때문에 CRNP에는 추가 보안 기법이 포함되지 않습니다.

CRNP를 사용하는 Java 응용 프로그램 생성 예

다음 예에서는 CRNP를 사용하는 CrnpClient라는 간단한 Java 응용 프로그램을 개발하는 방법을 보여줍니다. 이 응용 프로그램은 클러스터의 CRNP 서버에 이벤트 콜백을 등록하고 이러한 이벤트 콜백을 수신하며 해당 내용을 인쇄하여 이벤트를 처리합니다. 종료하기 전에 이 응용 프로그램은 이벤트 콜백에 대한 요청을 등록 취소합니다.

다음 사항에 주의하면서 이 예를 검토합니다.

Procedure환경 설정 방법

단계
  1. JAXP와 올바른 버전의 Java 컴파일러 및 가상 머신을 다운로드하여 설치합니다.

    http://java.sun.com/xml/jaxp/index.html에서 지침을 확인할 수 있습니다.


    주 –

    이 예를 사용하려면 Java 1.3.1 이상이 필요합니다.


  2. 소스 파일이 있는 디렉토리에서 다음을 입력합니다.


    % javac -classpath jaxp-root/dom.jar:jaxp-rootjaxp-api. \
    jar:jaxp-rootsax.jar:jaxp-rootxalan.jar:jaxp-root/xercesImpl \
    .jar:jaxp-root/xsltc.jar -sourcepath . source-filename.java
    

    여기서 jaxp-root는 JAXP jar 파일이 있는 디렉토리의 절대 또는 상대 경로 이며 source-filename은 Java 소스 파일의 이름입니다.

    컴파일러가 JAXP 클래스를 찾을 수 있도록 컴파일 명령줄에 classpath 를 지정해야 합니다.

  3. 응용 프로그램을 실행할 때 응용 프로그램이 적절한 JAXP 클래스 파일을 로드할 수 있도록 다음과 같이 classpath를 지정합니다. classpath의 첫 번째 경로는 현재 디렉토리입니다.


    % java -cp .:jaxp-root/dom.jar:jaxp-rootjaxp-api. \
    jar:jaxp-rootsax.jar:jaxp-rootxalan.jar:jaxp-root/xercesImpl \
    .jar:jaxp-root/xsltc.jar source-filename arguments
    

    이제 환경이 구성되었으므로 응용 프로그램을 개발할 수 있습니다.

Procedure응용 프로그램 개발 시작 방법

이 부분의 예에서는 명령줄 인자를 구문 분석하고 CrnpClient 객체를 생성하는 주 메소드를 가진 CrnpClient라는 기본 클래스를 만듭니다. 이 객체는 클래스에 명령줄 인자를 전달하고 사용자가 응용 프로그램을 종료하기를 기다렸다가 CrnpClient에서 shutdown을 호출한 다음 종료합니다.

CrnpClient 클래스의 구성자는 다음 작업을 실행해야 합니다.

단계

    앞의 논리를 구현하는 Java 코드를 만듭니다.

    다음 예에서는 CrnpClient 클래스의 스켈레톤 코드를 보여줍니다. 구성자 및 종료 메소드에서 참조되는 4개의 도우미 메소드에 대한 구현은 나중에 표시됩니다. 필요한 모든 패키지를 가져오는 코드가 표시됩니다.

    import javax.xml.parsers.*;
    import javax.xml.transform.*;
    import javax.xml.transform.dom.*;
    import javax.xml.transform.stream.*;
    import org.xml.sax.*;
    import org.xml.sax.helpers.*;
    import org.w3c.dom.*;
    import java.net.*;
    import java.io.*;
    import java.util.*;
    
    class CrnpClient
    {
            public static void main(String []args)
            {
                    InetAddress regIp = null;
                    int regPort = 0, localPort = 0;
                    try {
                            regIp = InetAddress.getByName(args[0]);
                            regPort = (new Integer(args[1])).intValue();
                            localPort = (new Integer(args[2])).intValue();
                    } catch (UnknownHostException e) {
                            System.out.println(e);
                            System.exit(1);
                    }
                    CrnpClient client = new CrnpClient(regIp, regPort, 
                        localPort, args);
                    System.out.println("Hit return to terminate demo...");
                    try {
                            System.in.read();
                    } catch (IOException e) {
                            System.out.println(e.toString());
                    }
                    client.shutdown();
                    System.exit(0);
            }
    
            public CrnpClient(InetAddress regIpIn, int regPortIn, 
                int localPortIn, String []clArgs)
            {
                    try {
                            regIp = regIpIn;
                            regPort = regPortIn;
                            localPort = localPortIn;
                            regs = clArgs;
                            setupXmlProcessing();
                            createEvtRecepThr();
                            registerCallbacks();
                    } catch (Exception e) {
                            System.out.println(e.toString());
                            System.exit(1);
                    }
            }
    
            public void shutdown()
            {
                    try {
                            unregister();
                    } catch (Exception e) {
                            System.out.println(e);
                            System.exit(1);
                    }
            }
    
            private InetAddress regIp;
            private int regPort;
            private EventReceptionThread evtThr;
            private String regs[];
            public int localPort;
            public DocumentBuilderFactory dbf;
    }

    구성원 변수에 대해서는 나중에 자세히 설명됩니다.

Procedure명령줄 인자 구문 분석 방법

단계

    명령줄 인자를 구문 분석하려면 부록 G, CrnpClient.java 응용 프로그램의 코드를 참조하십시오.

Procedure이벤트 수신 스레드 정의 방법

이벤트 스레드가 차단되어 이벤트 콜백을 대기하는 동안 응용 프로그램에서 다른 작업을 계속할 수 있으려면 코드에서 이벤트 수신이 별도의 스레드에서 수행되도록 지정해야 합니다.


주 –

XML 설정에 대해서는 나중에 자세히 설명됩니다.


단계
  1. ServerSocket을 만들고 이 소켓에서 이벤트가 도착하기를 기다리는 EventReceptionThread라는 Thread 하위 클래스를 코드에 정의합니다.

    이 코드 예 부분에서는 이벤트를 읽거나 처리하지 않습니다. 이벤트 읽기 및 처리에 대해서는 나중에 자세히 설명됩니다. EventReceptionThread는 와일드카드 인터네트워킹 프로토콜 주소에서 ServerSocket을 만듭니다. EventReceptionThreadCrnpClient 객체에 대한 참조를 유지하므로 EventReceptionThread에서 처리할 CrnpClient 객체에 이벤트를 보낼 수 있습니다.

    class EventReceptionThread extends Thread
    {
            public EventReceptionThread(CrnpClient clientIn) throws IOException
            {
                    client = clientIn;
                    listeningSock = new ServerSocket(client.localPort, 50,
                        InetAddress.getLocalHost());
            }
    
            public void run()
            {
                    try {
                            DocumentBuilder db = client.dbf.newDocumentBuilder();
                            db.setErrorHandler(new DefaultHandler());
    
                            while(true) {
                                    Socket sock = listeningSock.accept();
                                    // Construct event from the sock stream and process it
                                    sock.close();
                            }
                            // UNREACHABLE
    
                    } catch (Exception e) {
                            System.out.println(e);
                            System.exit(1);
                    }
            }
    
            /* private member variables */
            private ServerSocket listeningSock;
            private CrnpClient client;
    }
  2. createEvtRecepThr 객체를 생성합니다.

    private void createEvtRecepThr() throws Exception
    {
            evtThr = new EventReceptionThread(this);
            evtThr.start();
    }

Procedure콜백 등록 및 등록 취소 방법

등록 작업은 다음과 같이 구성됩니다.

단계
  1. 앞의 논리를 구현하는 Java 코드를 만듭니다.

    다음 예제 코드에서는 CrnpClient 구성자에 의해 호출되는 CrnpClient 클래스의 registerCallbacks 메소드에 대한 구현을 보여줍니다. createRegistrationString()readRegistrationReply() 호출에 대해서는 나중에 자세히 설명됩니다.

    regIpregPort는 구성자에 의해 설정되는 객체 구성원입니다.

    private void registerCallbacks() throws Exception
    { 
            Socket sock = new Socket(regIp, regPort);
            String xmlStr = createRegistrationString();
            PrintStream ps = new 
                    PrintStream(sock.getOutputStream());
            ps.print(xmlStr);
            readRegistrationReply(sock.getInputStream();
            sock.close();
    }
  2. unregister 메소드를 구현합니다.

    이 메소드는 CrnpClientshutdown 메소드에 의해 호출됩니다. createUnregistrationString 구현에 대해서는 나중에 자세히 설명됩니다.

    private void unregister() throws Exception
    {
            Socket sock = new Socket(regIp, regPort);
            String xmlStr = createUnregistrationString();
            PrintStream ps = new PrintStream(sock.getOutputStream());
            ps.print(xmlStr);
            readRegistrationReply(sock.getInputStream());
            sock.close();
    }

ProcedureXML 생성 방법

이제 응용 프로그램의 구조를 설정하고 모든 네트워킹 코드를 작성했으므로 XML을 생성 및 구문 분석하는 코드를 작성합니다. 먼저 SC_CALLBACK_REG XML 등록 메시지를 생성하는 코드를 작성합니다.

SC_CALLBACK_REG 메시지는 등록 유형(ADD_CLIENT, REMOVE_CLIENT, ADD_EVENTS 또는 REMOVE_EVENTS), 콜백 포트 및 원하는 이벤트 목록으로 구성됩니다. 각 이벤트는 클래스 및 하위 클래스로 구성되며 이름 및 값 쌍 목록이 그 뒤에 옵니다.

이 예 부분에서는 등록 유형, 콜백 포트 및 등록 이벤트 목록을 저장하는 CallbackReg 클래스를 작성합니다. 이 클래스는 또한 SC_CALLBACK_REG XML 메시지에 대해 자신을 일련화할 수 있습니다.

이 클래스에서 흥미로운 메소드는 클래스 구성원에서 SC_CALLBACK_REG XML 메시지 문자열을 만드는 convertToXml 메소드입니다. http://java.sun.com/xml/jaxp/index.html의 JAXP 설명서에는 이 메소드의 코드가 자세히 설명되어 있습니다.

다음 예제 코드에는 Event 클래스의 구현이 나와 있습니다. CallbackReg 클래스는 하나의 이벤트를 저장하고 이 이벤트를 XML Element로 변환할 수 있는 Event 클래스를 사용합니다.

단계
  1. 앞의 논리를 구현하는 Java 코드를 만듭니다.

    class CallbackReg
    {
            public static final int ADD_CLIENT = 0;
            public static final int ADD_EVENTS = 1;
            public static final int REMOVE_EVENTS = 2;
            public static final int REMOVE_CLIENT = 3;
    
            public CallbackReg()
            {
                    port = null;
                    regType = null;
                    regEvents = new Vector();
            }
    
            public void setPort(String portIn)
            {
                    port = portIn;
            }
    
            public void setRegType(int regTypeIn)
            {
                    switch (regTypeIn) {
                    case ADD_CLIENT:
                            regType = "ADD_CLIENT";
                            break;
                    case ADD_EVENTS:
                            regType = "ADD_EVENTS";
                            break;
                    case REMOVE_CLIENT:
                            regType = "REMOVE_CLIENT";
                            break;
                    case REMOVE_EVENTS:
                            regType = "REMOVE_EVENTS";
                            break;
                    default:
                            System.out.println("Error, invalid regType " +
                                regTypeIn);
                            regType = "ADD_CLIENT";
                            break;
                    }
            }
    
            public void addRegEvent(Event regEvent)
            {
                    regEvents.add(regEvent);
            } 
    
            public String convertToXml()
            {
                    Document document = null;
                    DocumentBuilderFactory factory =
                        DocumentBuilderFactory.newInstance();
                    try {
                            DocumentBuilder builder = factory.newDocumentBuilder();
                            document = builder.newDocument(); 
                    } catch (ParserConfigurationException pce) {
                            // Parser with specified options can't be built
                            pce.printStackTrace();
                            System.exit(1);
                    }
    
                    // Create the root element
                    Element root = (Element) document.createElement("SC_CALLBACK_REG");
    
                    // Add the attributes
                    root.setAttribute("VERSION", "1.0");
                    root.setAttribute("PORT", port);
                    root.setAttribute("regType", regType);
    
                    // Add the events
                    for (int i = 0; i < regEvents.size(); i++) {
                            Event tempEvent = (Event)
                                (regEvents.elementAt(i));
                            root.appendChild(tempEvent.createXmlElement(document));
                    }
                    document.appendChild(root);
    
                    // Convert the whole thing to a string
                    DOMSource domSource = new DOMSource(document);
                    StringWriter strWrite = new StringWriter();
                    StreamResult streamResult = new StreamResult(strWrite);
                    TransformerFactory tf = TransformerFactory.newInstance();
                    try {
                            Transformer transformer = tf.newTransformer();
                            transformer.transform(domSource, streamResult);
                    } catch (TransformerException e) {
                            System.out.println(e.toString());
                            return ("");
                    }
                    return (strWrite.toString());
            }
    
            private String port;
            private String regType;
            private Vector regEvents;
    }
  2. EventNVPair 클래스를 구현합니다.

    CallbackReg 클래스는 NVPair 클래스를 직접 사용하는 Event 클래스를 사용합니다.

    class Event
    {
    
            public Event()
            {
                    regClass = regSubclass = null;
                    nvpairs = new Vector();
            }
    
            public void setClass(String classIn)
            {
                    regClass = classIn;
            }
    
            public void setSubclass(String subclassIn)
            {
                    regSubclass = subclassIn;
            }
    
            public void addNvpair(NVPair nvpair)
            {
                    nvpairs.add(nvpair);
            }
    
            public Element createXmlElement(Document doc)
            {
                    Element event = (Element)
                        doc.createElement("SC_EVENT_REG");
                    event.setAttribute("CLASS", regClass);
                    if (regSubclass != null) {
                            event.setAttribute("SUBCLASS", regSubclass);
                    }
                    for (int i = 0; i < nvpairs.size(); i++) {
                         NVPair tempNv = (NVPair)
                             (nvpairs.elementAt(i));
                         event.appendChild(tempNv.createXmlElement(doc));
                    }
                    return (event);
            }
    
            private String regClass, regSubclass;
            private Vector nvpairs;
    }
    
    class NVPair
    {
            public NVPair()
            {
                    name = value = null;
            }
    
            public void setName(String nameIn)
            {
                    name = nameIn;
            }
    
            public void setValue(String valueIn)
            {
                    value = valueIn;
            }
    
            public Element createXmlElement(Document doc)
            {
                    Element nvpair = (Element)
                        doc.createElement("NVPAIR");
                    Element eName = doc.createElement("NAME");
                    Node nameData = doc.createCDATASection(name);
                    eName.appendChild(nameData);
                    nvpair.appendChild(eName);
                    Element eValue = doc.createElement("VALUE");
                    Node valueData = doc.createCDATASection(value);
                    eValue.appendChild(valueData);
                    nvpair.appendChild(eValue);
    
                    return (nvpair);
            }
    
            private String name, value;
    }

Procedure등록 및 등록 취소 메시지 작성 방법

이제 XML 메시지를 생성하는 도우미 클래스를 만들었으므로 createRegistrationString 메소드의 구현을 작성할 수 있습니다. 이 메소드는 콜백 등록 및 등록 취소 방법에 설명된 registerCallbacks 메소드에 의해 호출됩니다.

createRegistrationStringCallbackReg 객체를 생성하고 해당 등록 유형 및 포트를 설정합니다. 그런 다음 createRegistrationStringcreateAllEvent, createMembershipEvent, createRgEventcreateREvent 도우미 메소드를 사용하여 다양한 이벤트를 생성합니다. CallbackReg 객체가 만들어진 후에 각 이벤트는 이 객체에 추가됩니다. 마지막으로 createRegistrationStringCallbackReg 객체에서 convertToXml 메소드를 호출하여 String 형태로 XML 메시지를 검색합니다.

regs 구성원 변수는 사용자가 응용 프로그램에 제공하는 명령줄 인자를 저장합니다. 다섯 번째 및 그 이후의 인자는 응용 프로그램이 등록해야 하는 이벤트를 지정합니다. 네 번째 인자는 등록 유형을 지정하지만 이 예에서는 무시됩니다. 부록 G, CrnpClient.java 응용 프로그램의 전체 코드에는 이 네 번째 인자를 사용하는 방법이 나와 있습니다.

단계
  1. 앞의 논리를 구현하는 Java 코드를 만듭니다.

    private String createRegistrationString() throws Exception
    {
            CallbackReg cbReg = new CallbackReg();
            cbReg.setPort("" + localPort);
    
            cbReg.setRegType(CallbackReg.ADD_CLIENT);
    
            // add the events
            for (int i = 4; i < regs.length; i++) {
                    if (regs[i].equals("M")) {
                            cbReg.addRegEvent(createMembershipEvent());
                    } else if (regs[i].equals("A")) {
                            cbReg.addRegEvent(createAllEvent());
                    } else if (regs[i].substring(0,2).equals("RG")) {
                            cbReg.addRegEvent(createRgEvent(regs[i].substring(3)));
                    } else if (regs[i].substring(0,1).equals("R")) {
                            cbReg.addRegEvent(createREvent(regs[i].substring(2))); 
                    }
            }
    
            String xmlStr = cbReg.convertToXml();
            return (xmlStr);
    }
    
    private Event createAllEvent()
    {
            Event allEvent = new Event();
            allEvent.setClass("EC_Cluster");
            return (allEvent);
    }
    
    private Event createMembershipEvent()
    {
            Event membershipEvent = new Event();
            membershipEvent.setClass("EC_Cluster");
            membershipEvent.setSubclass("ESC_cluster_membership");
            return (membershipEvent);
    }
    
    private Event createRgEvent(String rgname)
    {
            Event rgStateEvent = new Event();
            rgStateEvent.setClass("EC_Cluster");
            rgStateEvent.setSubclass("ESC_cluster_rg_state");
    
            NVPair rgNvpair = new NVPair();
            rgNvpair.setName("rg_name");
            rgNvpair.setValue(rgname);
            rgStateEvent.addNvpair(rgNvpair);
    
            return (rgStateEvent);
    }
    
    private Event createREvent(String rname)
    {
            Event rStateEvent = new Event();
            rStateEvent.setClass("EC_Cluster");
            rStateEvent.setSubclass("ESC_cluster_r_state");
    
            NVPair rNvpair = new NVPair();
            rNvpair.setName("r_name");
            rNvpair.setValue(rname);
            rStateEvent.addNvpair(rNvpair);
    
            return (rStateEvent);
    }
  2. 등록 취소 문자열을 만듭니다.

    등록 취소 문자열을 만드는 것은 이벤트를 수용할 필요가 없기 때문에 등록 문자열을 만드는 것보다 쉽습니다.

    private String createUnregistrationString() throws Exception
    {
            CallbackReg cbReg = new CallbackReg();
            cbReg.setPort("" + localPort);
            cbReg.setRegType(CallbackReg.REMOVE_CLIENT);
            String xmlStr = cbReg.convertToXml();
            return (xmlStr);
    }

ProcedureXML 구문 분석기 설정 방법

이제 응용 프로그램의 네트워킹 및 XML 생성 코드를 만들었습니다. CrnpClient 구성자는 setupXmlProcessing 메소드를 호출합니다. 이 메소드는 DocumentBuilderFactory 객체를 만들고 이 객체에 대한 다양한 구분 분석 등록 정보를 설정합니다. JAXP 설명서에 이 메소드가 자세히 설명되어 있습니다. http://java.sun.com/xml/jaxp/index.html을 참조하십시오.

단계

    앞의 논리를 구현하는 Java 코드를 만듭니다.

    private void setupXmlProcessing() throws Exception
    {
            dbf = DocumentBuilderFactory.newInstance();
    
            // We don't need to bother validating
            dbf.setValidating(false);
            dbf.setExpandEntityReferences(false);
    
            // We want to ignore comments and whitespace
            dbf.setIgnoringComments(true);
            dbf.setIgnoringElementContentWhitespace(true);
    
            // Coalesce CDATA sections into TEXT nodes.
            dbf.setCoalescing(true);
    }

Procedure등록 응답 구문 분석 방법

등록 또는 등록 취소 메시지에 응답하여 CRNP 서버가 보낸 SC_REPLY XML 메시지를 구분 분석하려면 RegReply 도우미 클래스가 필요합니다. 이 클래스는 XML 문서에서 생성할 수 있습니다. 이 클래스는 상태 코드와 상태 메시지에 대한 액세서를 제공합니다. 서버의 XML 스트림을 구문 분석하려면 새 XML 문서를 만들고 이 문서의 구문 분석 메소드를 사용해야 합니다. http://java.sun.com/xml/jaxp/index.html의 JAXP 설명서에 이 메소드가 자세히 설명되어 있습니다.

단계
  1. 앞의 논리를 구현하는 Java 코드를 만듭니다.

    readRegistrationReply 메소드는 새 RegReply 클래스를 사용한다는 점에 주의합니다.

    private void readRegistrationReply(InputStream stream) throws Exception
    {
            // Create the document builder
            DocumentBuilder db = dbf.newDocumentBuilder();
            db.setErrorHandler(new DefaultHandler());
    
            //parse the input file
            Document doc = db.parse(stream);
    
            RegReply reply = new RegReply(doc);
            reply.print(System.out);
    }
  2. RegReply 클래스를 구현합니다.

    retrieveValues 메소드가 XML 문서의 DOM 트리를 통과하면서 상태 코드와 상태 메시지를 추출한다는 것에 주의합니다. http://java.sun.com/xml/jaxp/index.html의 JAXP 설명서에 자세한 내용 이 나와 있습니다.

    class RegReply
    {
            public RegReply(Document doc)
            {
                    retrieveValues(doc);
            }
    
            public String getStatusCode()
            {
                    return (statusCode);
            }
    
            public String getStatusMsg()
            {
                    return (statusMsg);
            }
            public void print(PrintStream out)
            {
                    out.println(statusCode + ": " +
                        (statusMsg != null ? statusMsg : ""));
            }
    
            private void retrieveValues(Document doc)
            {
                    Node n;
                    NodeList nl;
                    String nodeName;
    
                    // Find the SC_REPLY element.
                    nl = doc.getElementsByTagName("SC_REPLY");
                    if (nl.getLength() != 1) {
                            System.out.println("Error in parsing: can't find "
                                + "SC_REPLY node.");
                            return;
                    }
    
                    n = nl.item(0);
    
                    // Retrieve the value of the statusCode attribute
                    statusCode = ((Element)n).getAttribute("STATUS_CODE");
    
                    // Find the SC_STATUS_MSG element
                    nl = ((Element)n).getElementsByTagName("SC_STATUS_MSG");
                    if (nl.getLength() != 1) {
                            System.out.println("Error in parsing: can't find "
                                + "SC_STATUS_MSG node.");
                            return;
                    }
                    // Get the TEXT section, if there is one.
                    n = nl.item(0).getFirstChild();
                    if (n == null || n.getNodeType() != Node.TEXT_NODE) {
                    // Not an error if there isn't one, so we just silently return.
                            return;
                    }
    
                    // Retrieve the value
                    statusMsg = n.getNodeValue();
            }
    
            private String statusCode;
            private String statusMsg;
    }

Procedure콜백 이벤트 구문 분석 방법

마지막 단계는 실제 콜백 이벤트를 구분 분석 및 처리하는 것입니다. 이 작업을 지원하기 위해 XML 생성 방법에서 만든 Event 클래스를 수정하여 이 클래스가 XML 문서에서 Event를 생성하고 XML Element를 만들 수 있게 합니다. 이러한 변경 작업에는 XML 문서를 사용하는 추가 구성자, retrieveValues 메소드, 두 개의 추가 구성원 변수(vendorpublisher), 모든 필드에 대한 액세서 메소드 및 마지막으로 인쇄 메소드가 필요합니다.

단계
  1. 앞의 논리를 구현하는 Java 코드를 만듭니다.

    이 코드는 등록 응답 구문 분석 방법에 설명된 RegReply 클래스의 코드와 비슷합니다.

    public Event(Document doc)
            {
                    nvpairs = new Vector();
                    retrieveValues(doc);
            }
            public void print(PrintStream out)
            {
                    out.println("\tCLASS=" + regClass);
                    out.println("\tSUBCLASS=" + regSubclass);
                    out.println("\tVENDOR=" + vendor);
                    out.println("\tPUBLISHER=" + publisher);
                    for (int i = 0; i < nvpairs.size(); i++) {
                            NVPair tempNv = (NVPair)
                                (nvpairs.elementAt(i));
                            out.print("\t\t");
                            tempNv.print(out);
                    }
            }
    
            private void retrieveValues(Document doc)
            {
                    Node n;
                    NodeList nl;
                    String nodeName;
    
                    // Find the SC_EVENT element.
                    nl = doc.getElementsByTagName("SC_EVENT");
                    if (nl.getLength() != 1) {
                       System.out.println("Error in parsing: can't find "
                           + "SC_EVENT node.");
                       return;
                    }
    
                    n = nl.item(0);
    
                    //
                    // Retrieve the values of the CLASS, SUBCLASS,
                    // VENDOR and PUBLISHER attributes.
                    //
                    regClass = ((Element)n).getAttribute("CLASS");
                    regSubclass = ((Element)n).getAttribute("SUBCLASS");
                    publisher = ((Element)n).getAttribute("PUBLISHER");
                    vendor = ((Element)n).getAttribute("VENDOR");
    
                    // Retrieve all the nv pairs
                    for (Node child = n.getFirstChild(); child != null;
                        child = child.getNextSibling())
                    {
                           nvpairs.add(new NVPair((Element)child));
                    }
            }
    
            public String getRegClass()
            {
                    return (regClass);
            }
    
            public String getSubclass()
            {
                    return (regSubclass);
            }
    
            public String getVendor()
            {
                    return (vendor);
            }
    
            public String getPublisher()
            {
                    return (publisher);
            }
    
            public Vector getNvpairs()
            {
                    return (nvpairs);
            }
    
            private String vendor, publisher;
  2. XML 구문 분석을 지원하는 NVPair 클래스에 대한 추가 구성자와 메소드를 구현합니다.

    단계 1에 표시된 것처럼 Event 클래스를 변경하려면 NVPair 클래스를 비슷하게 변경해야 합니다.

    public NVPair(Element elem)
            {
                    retrieveValues(elem);
            }
            public void print(PrintStream out)
            {
                    out.println("NAME=" + name + " VALUE=" + value);
            }
            private void retrieveValues(Element elem)
            {
                    Node n;
                    NodeList nl;
                    String nodeName;
    
                    // Find the NAME element
                    nl = elem.getElementsByTagName("NAME");
                    if (nl.getLength() != 1) {
                       System.out.println("Error in parsing: can't find "
                           + "NAME node.");
                       return;
                    }
                    // Get the TEXT section
                    n = nl.item(0).getFirstChild();
                    if (n == null || n.getNodeType() != Node.TEXT_NODE) {
                       System.out.println("Error in parsing: can't find "
                           + "TEXT section.");
                       return;
                    }
    
                    // Retrieve the value
                    name = n.getNodeValue();
    
                    // Now get the value element
                    nl = elem.getElementsByTagName("VALUE");
                    if (nl.getLength() != 1) {
                       System.out.println("Error in parsing: can't find "
                           + "VALUE node.");
                       return;
                    }
                    // Get the TEXT section
                    n = nl.item(0).getFirstChild();
                    if (n == null || n.getNodeType() != Node.TEXT_NODE) {
                    System.out.println("Error in parsing: can't find "
                                + "TEXT section.");
                            return;
                    }
    
                    // Retrieve the value
                    value = n.getNodeValue();
                    }
    
            public String getName()
            {
                    return (name);
            }
    
            public String getValue()
            {
                    return (value);
            }
    }
  3. 이벤트 콜백을 대기하는 EventReceptionThread에서 while 루프를 구현합니다.

    EventReceptionThread 이벤트 수신 스레드 정의 방법에 설명되어 있습니다.

    while(true) {
                    Socket sock = listeningSock.accept();
                    Document doc = db.parse(sock.getInputStream());
                    Event event = new Event(doc);
                    client.processEvent(event);
                    sock.close();
            }

Procedure응용 프로그램 실행 방법

단계
  1. 수퍼유저가 되거나 동등한 역할을 맡습니다.

  2. 응용 프로그램을 실행합니다.


    # java CrnpClient crnpHost crnpPort localPort ...
    

    CrnpClient 응용 프로그램의 전체 코드는 부록 G, CrnpClient.java 응용 프로그램에 나와 있습니다.