C로 입력 노드 작성

메시지를 메시지 플로우로 수신하기 위해 C로 사용자 정의 입력 노드를 작성합니다.

시작하기 전에

태스크 정보

로드 가능 구현 라이브러리 또는 LIL은 C 노드의 구현 모듈입니다. LIL은 공유 또는 동적 링크 라이브러리(DLL)로 구현되지만 파일 확장자는 .dll이 아닌 .lil입니다.

노드에 대해 작성하는 구현 함수는 C 노드 구현 함수에 나열되어 있습니다. 런타임 통합 노드에서 구현된 유틸리티 함수를 호출하여 노드 조작을 지원할 수 있습니다. 이러한 함수는 C 노드 유틸리티 함수에 나열되어 있습니다.

IBM® App Connect Enterprise SwitchNode 와 TransformNode 라는 두 가지 사용자 정의 노드의 소스 코드를 제공합니다. 이 노드를 현재 상태로 사용하거나 수정할 수 있습니다.

노드 선언 및 정의

태스크 정보

통합 노드에 사용자 정의 노드를 선언하고 정의하려면 초기화 함수, bipGetMessageflowNodeFactory를 LIL에 포함시키십시오. 다음 단계는 통합 노드가 초기화 함수를 호출하는 방법과 초기화 함수가 사용자 정의 노드를 선언하고 정의하는 방법을 설명합니다.

프로시저

  1. 초기화 함수 bipGetMessageflowNodeFactory는 운영 체제가 LIL을 로드하고 초기화한 후에 통합 노드에 의해 호출됩니다. 통합 노드는 이 함수를 호출하여 LIL이 수행할 수 있는 내용과 통합 노드가 LIL을 호출하는 방법을 이해합니다. 예를 들면 다음과 같습니다.
    CciFactory LilFactoryExportPrefix * LilFactoryExportSuffix
    bipGetMessageflowNodeFactory()
    
  2. bipGetMessageflowNodeFactory 함수는 유틸리티 함수 cniCreateNodeFactory를 호출해야 합니다. 이 함수는 LIL이 지원하는 모든 노드의 고유 팩토리 이름(또는 그룹 이름)을 다시 전달합니다. 다시 전달된 모든 팩토리 이름(또는 그룹 이름)은 단일 런타임 통합 노드의 모든 LIL에서 고유해야 합니다.
  3. LIL은 유틸리티 함수 cniDefineNodeClass 를 호출하여 각 노드의 고유 이름 및 구현 함수 주소의 가상 함수 테이블을 전달해야 합니다.
    예를 들어, 다음 코드는 InputxNode라는 단일 노드를 선언하고 정의합니다.
    {
    	CciFactory* factoryObject;
    	int rc = 0;
    	CciChar factoryName[] = L"MyNodeFactory";
    	CCI_EXCEPTION_ST exception_st;
    
    	/* Create the Node Factory for this node */
    	factoryObject = cniCreateNodeFactory(0, factoryName);
    	if (factoryObject == CCI_NULL_ADDR) {
    		
    		/* Any local error handling can go here */
    	}
    	else {
    		/* Define the nodes supported by this factory */
    		static CNI_VFT vftable = {CNI_VFT_DEFAULT};
    
    	/* Setup function table with pointers to node implementation functions */
    	vftable.iFpCreateNodeContext = _createNodeContext;
    	vftable.iFpDeleteNodeContext = _deleteNodeContext;
    	vftable.iFpGetAttributeName2 = _getAttributeName2;
    	vftable.iFpSetAttribute      = _setAttribute;
    	vftable.iFpGetAttribute2     = _getAttribute2;
    	vftable.iFpRun               = _run;
    
    	cniDefineNodeClass(0, factoryObject, L"InputxNode", &vftable);
    	}
    
    	/* Return address of this factory object to the integration node */
    	return(factoryObject);
    }

    사용자 정의 노드는 cniRun 구현 함수를 구현하여 입력 노드의 기능을 제공하는 것으로 자신을 식별합니다.

    사용자 정의 노드는 cniRun 또는 cniEvaluate 구현 함수를 구현해야 합니다. 구현하지 않을 경우 통합 노드는 사용자 정의 노드를 로드하지 않고 cniDefineNodeClass 유틸리티 함수가 실패하여 CCI_MISSING_IMPL_FUNCTION이 리턴됩니다.

    예를 들어,
    int cniRun(                       
      CciContext* context,                
      CciMessage* localEnvironment,        
      CciMessage* exceptionList,          
      CciMessage* message
    ){          
      ...
      /* Get data from external source */
      return CCI_SUCCESS_CONTINUE;                                    
    }
    리턴값을 주기적으로 사용하여 통합 노드로 제어를 리턴하십시오.

    사용자 정의 입력 노드를 포함한 메시지 플로우가 배치되고 나면 노드가 메시지를 검색해서 메시지 플로우의 나머지 부분에 전파할 수 있도록 노드의 cniRun 함수가 반복해서 호출됩니다.

    C 사용자 정의 노드를 컴파일하는 데 필요한 최소 코드는 C 스켈레톤 코드를 참조하십시오.

노드의 인스턴스 작성

태스크 정보

노드를 인스턴스화하려면 다음을 수행하십시오.

프로시저

  1. 통합 노드가 함수 포인터 테이블을 수신하면 사용자 정의 노드의 각 인스턴스화에 대해 cniCreateNodeContext 함수를 호출합니다. 예를 들어, 3개의 메시지 플로우에 사용자 정의 노드가 사용되고 있으면 각 플로우마다 cniCreateNodeContext 함수가 호출됩니다. 이 함수는 구성된 속성의 값을 보유하기 위해 사용자 정의 노드의 인스턴스 생성을 위한 메모리를 할당해야 합니다. 예를 들면 다음과 같습니다.
    1. cniCreateNodeContext 함수를 호출하십시오.
      CciContext* _createNodeContext(
        CciFactory* factoryObject,
        CciChar*    nodeName,
        CciNode*    nodeObject
      ){
        static char* functionName = (char *)"_createNodeContext()";
        NODE_CONTEXT_ST* p;
        CciChar          buffer[256];
      
      
    2. 로컬 컨텍스트에 대한 포인터를 할당하고 컨텍스트 영역을 지우십시오.
        p = (NODE_CONTEXT_ST *)malloc(sizeof(NODE_CONTEXT_ST));
      
        if (p) {
           memset(p, 0, sizeof(NODE_CONTEXT_ST));
      
    3. 노드 오브젝트 포인터를 컨텍스트에 저장하십시오.
         p->nodeObject = nodeObject;
      
    4. 노드 이름을 저장하십시오.
       CciCharNCpy((CciChar*)&p->nodeName, nodeName, MAX_NODE_NAME_LEN);
      
    5. 노드 컨텍스트를 리턴하십시오.
      return (CciContext*) p;
      
  2. 한 입력 노드에는 연관된 여러 출력 터미널이 있지만 일반적으로 입력 터미널은 없습니다. 노드가 인스턴스화될 때 입력 노드에 출력 터미널을 추가하려면 유틸리티 함수 cniCreateOutputTerminal을 사용하십시오. 이러한 함수는 cniCreateNodeContext 구현 함수 내에서 호출되어야 합니다. 예를 들어, 3개의 출력 터미널이 있는 입력 노드를 정의하려는 경우 다음과 같습니다.
       {
          const CciChar* ucsOut = CciString("out", BIP_DEF_COMP_CCSID) ;
          insOutputTerminalListEntry(p, (CciChar*)ucsOut);
          free((void *)ucsOut) ;
        }
        {
          const CciChar* ucsFailure = CciString("failure", BIP_DEF_COMP_CCSID) ;
          insOutputTerminalListEntry(p, (CciChar*)ucsFailure);
          free((void *)ucsFailure) ;
        }    
        {
          const CciChar* ucsCatch = CciString("catch", BIP_DEF_COMP_CCSID) ;
          insOutputTerminalListEntry(p, (CciChar*)ucsCatch);
          free((void *)ucsCatch) ;    }
    C 사용자 정의 노드를 컴파일하는 데 필요한 최소 코드는 C 스켈레톤 코드를 참조하십시오.

속성 설정

태스크 정보

통합 노드를 시작할 때마다 또는 메시지 플로우를 새 값으로 재배치할 때마다 속성이 설정됩니다.

출력 터미널이 작성되고 나면 통합 노드는 사용자 정의 노드의 구성된 속성의 값을 전달하기 위해 cniSetAttribute 함수를 호출합니다. 예를 들어,
    {
      const CciChar* ucsAttr = CciString("nodeTraceSetting", BIP_DEF_COMP_CCSID) ;
      insAttrTblEntry(p, (CciChar*)ucsAttr, CNI_TYPE_INTEGER);
      _setAttribute(p, (CciChar*)ucsAttr, (CciChar*)constZero);
      free((void *)ucsAttr) ;
    }
    {
      const CciChar* ucsAttr = CciString("nodeTraceOutfile", BIP_DEF_COMP_CCSID) ;
      insAttrTblEntry(p, (CciChar*)ucsAttr, CNI_TYPE_STRING);
      _setAttribute(p, (CciChar*)ucsAttr, (CciChar*)constSwitchTraceLocation);
      free((void *)ucsAttr) ;
    }
노드가 보유할 수 있는 구성 속성의 수는 제한이 없습니다. 하지만 사용자 정의 노드는 이미 기본 구성 속성으로 구현된 속성을 구현해선 안됩니다. 기본 속성은 다음과 같습니다.
  • 레이블
  • userTraceLevel
  • traceLevel
  • userTraceFilter
  • traceFilter

노드 기능 구현

태스크 정보

통합 노드는 입력 노드가 있다는 사실을 알게 되면 일정한 간격으로 이 노드의 cniRun 함수를 호출합니다. 그러면 cniRun 함수는 수행해야 할 조치를 결정해야 합니다. 데이터가 처리 가능한 경우 cniRun 함수는 메시지를 전파할 수 있습니다. 처리 가능한 데이터가 없으면 통합 노드가 구성 변경을 계속할 수 있도록 cniRun 함수가 CCI_TIMEOUT을 리턴해야 합니다.

예를 들어, cniDispatchThread를 호출하고 메시지를 처리하거나 CCI_TIMEOUT과 함께 리턴되도록 노드를 구성하려는 경우 다음과 같습니다.
If ( anything to do )
	CniDispatchThread;

   /* do the work */

	If ( work done O.K.)
		Return CCI_SUCCESS_CONTINUE;
	Else
		Return CCI_FAILURE_CONTINUE;
Else
  Return CCI_TIMEOUT;

기본 메시지 구문 분석기 속성 대체(선택사항)

태스크 정보

입력 노드 구현은 일반적으로 초기에 입력 메시지를 구문 분석하는 메시지 구문 분석기를 판별합니다. 예를 들어, MQInput 노드는 MQMD 구문 분석기가 MQMD 헤더를 구문 분석해야 함을 나타냅니다. 사용자 정의 입력 노드는 포함된 다음 속성을 사용하거나 기본값으로 대체해서, 적절한 헤더 또는 메시지 구문 분석기 및 구문 분석을 제어하는 모드를 선택할 수 있습니다.

rootParserClassName
사용자 정의 입력 노드에서 지원되는 메시지 형식을 구문 분석하는 루트 구문 분석기의 이름을 정의합니다. 기본값은 통합 노드가 구문 분석기를 할당하고 함께 연결하도록 하는 제공된 루트 구문 분석기, GenericRoot입니다. 노드가 이 속성 값을 수정할 필요는 없습니다.
firstParserClassName
비트스트림을 구문 분석해야 하는 구문 분석기 체인일 수 있는 첫 번째 구문 분석기의 이름을 정의합니다. 기본값은 XML입니다.
messageDomainProperty
입력 메시지를 구문 분석하는 데 필요한 메시지 구문 분석기의 이름을 정의하는 선택적 속성입니다. 지원되는 값은 MQInput 노드에서 지원되는 값과 동일합니다. (자세한 정보는 MQInput 노드 를 참조하십시오.)
messageSetProperty
messageDomainProperty 속성에 MRM 구문 분석기가 지정된 경우에만 Message Set 필드에 메시지 세트 ID 또는 메시지 세트 이름을 정의하는 선택적 속성입니다.
messageTypeProperty
messageDomainProperty 속성에 MRM 구문 분석기가 지정된 경우에만 MessageType 필드에 메시지의 ID를 정의하는 선택적 속성입니다.
messageFormatProperty
messageDomainProperty 속성에 MRM 구문 분석기가 지정된 경우에만 Message Format 필드에 메시지 형식을 정의하는 선택적 속성입니다.
항상 알려진 구조의 데이터로 시작하는 사용자 정의 입력 노드를 쓴 경우 특정 구문 분석기가 데이터의 시작을 반드시 핸들링하도록 할 수 있습니다. 예를 들어, MQInput 노드는 IBM MQ 큐에서만 데이터를 읽기 때문에, 이 데이터의 앞부분에는 항상 MQMD가 포함되어 있으며, MQInput 노드는 firstParserClassName 를 MQHMD로 설정합니다. 사용자 정의 노드가 항상 특정 구문 분석기(예를 들어, "MYPARSER")로 구문 분석할 수 있는 구조로 시작하는 데이터를 핸들링하면, 다음과 같이 firstParserClassName을 MYPARSER로 설정하십시오.
  1. 구현 함수를 선언하십시오.
    CciFactory LilFactoryExportPrefix * LilFactoryExportSuffix bipGetMessageflowNodeFactory()
    {
      ....
      CciFactory*      factoryObject;
      ....
      factoryObject = cniCreateNodeFactory(0, (unsigned short *)constPluginNodeFactory);
      ...
      vftable.iFpCreateNodeContext = _createNodeContext;
      vftable.iFpSetAttribute      = _setAttribute;
      vftable.iFpGetAttribute      = _getAttribute;
      ...  
      cniDefineNodeClass(&rc, factoryObject, (CciChar*)constSwitchNode, &vftable);
      ...
      return(factoryObject);
    }
  2. cniCreateNodeContext 구현 함수에 속성을 설정하십시오.
    CciContext* _createNodeContext(
      CciFactory* factoryObject,
      CciChar*    nodeName,
      CciNode*    nodeObject
    ){
      NODE_CONTEXT_ST* p;
      ...
    
        /* Allocate a pointer to the local context */
        p = (NODE_CONTEXT_ST *)malloc(sizeof(NODE_CONTEXT_ST));
        /* Create attributes and set default values */
        {
          const CciChar* ucsAttrName  = CciString("firstParserClassName", BIP_DEF_COMP_CCSID);
          const CciChar* ucsAttrValue = CciString("MYPARSER", BIP_DEF_COMP_CCSID) ;
          insAttrTblEntry(p, (CciChar*)ucsAttrName, CNI_TYPE_INTEGER);
          /*see sample BipSampPluginNode.c for implementation of insAttrTblEntry*/
    
          _setAttribute(p, (CciChar*)ucsAttrName, (CciChar*)ucsAttrValue);
          free((void *)ucsAttrName) ;
          free((void *)ucsAttrValue) ;
        }
    위의 코드 예에서는 insAttrTblEntry 메소드가 호출됩니다. 이 메소드는 SwitchNode 및 TransformNode 샘플 사용자 정의 노드에 선언됩니다.

노드의 인스턴스 삭제

태스크 정보

노드는 메시지 플로우가 재배치될 때 또는 통합 서버 프로세스가 중지될 때 ( mqsistop 명령 사용) 영구 삭제됩니다. 노드가 영구 삭제되면 cniDeleteNodeContext 함수를 호출하여 사용된 모든 메모리를 비우고 보유한 모든 자원을 해제해야 합니다. 예를 들어,

void _deleteNodeContext(
  CciContext* context
){
  static char* functionName = (char *)"_deleteNodeContext()";

  return;
}