Criando um Nó de Processamento de Mensagem ou de Saída em C

Um nó de processamento de mensagem é usado para processar uma mensagem de alguma maneira e um nó de saída é usado para produzir uma mensagem como um fluxo de bits.

Antes de Começar

Sobre esta tarefa

Ao codificar um nó de processamento de mensagens ou um nó de saída, os nós fornecem essencialmente os mesmos serviços. É possível executar o processamento de mensagens em um nó de saída e enviar uma mensagem para um fluxo de bits usando um nó de processamento de mensagens. Para simplificar, este tópico refere-se principalmente ao nó como um nó de processamento de mensagens, mas também contém informações sobre as funções de dois tipos de nó.

Uma LIL (Biblioteca de Implementação Carregável) é o módulo de implementação para um nó em C. Uma LIL é implementada como uma DLL (Biblioteca de Links Dinâmicos) ou biblioteca compartilhada, mas tem a extensão de arquivo .lil, não .dll.

Para obter mais informações sobre as funções de implementação do nó C gravadas para o nó, consulte Funções de implementação do nó C. É possível chamar as funções do utilitário do nó C, implementadas no nó de integração de tempo de execução, para ajudar com a operação do nó; consulte Funções do utilitário do nó C

IBM® App Connect Enterprise fornece o código-fonte de dois nós definidos pelo usuário de exemplo, chamados SwitchNode e TransformNode. É possível utilizar esses nós em seus estados atuais ou poderá modificá-los.

Declarando e Definindo Seu Nó

Sobre esta tarefa

Para declarar e definir um nó definido pelo usuário para o nó de integração, inclua uma função de inicialização, bipGetMessageflowNodeFactory, na LIL. As etapas a seguir ocorrem no encadeamento de configuração e esboçam como o nó de integração chama sua função de inicialização e como sua função de inicialização declara e define o nó definido pelo usuário:

Procedimento

  1. O nó de integração chama a função de inicialização bipGetMessageflowNodeFactory depois que o sistema operacional carregou e inicializou o LIL. O nó de integração chama essa função para entender o que a LIL pode fazer e como o nó de integração pode chamar a LIL. Por exemplo:
    CciFactory LilFactoryExportPrefix * LilFactoryExportSuffix
    bipGetMessageflowNodeFactory()
    
  2. A função bipGetMessageflowNodeFactory deve chamar a função utilitária cniCreateNodeFactory. Essa função transmite de volta um nome de fábrica (ou nome de grupo) para todos os nós que a LIL suporta. O nome do factory (ou nome do grupo) deve ser exclusivo por todas as LILs em um único nó de integração de tempo de execução.
  3. O LIL deve chamar a função utilitária cniDefineNodeClass para transmitir o nome exclusivo de cada nó e uma tabela de função virtual dos endereços das funções de implementação.
    Por exemplo, o código a seguir declara e define um único nó chamado MessageProcessingxNode:
    {
    	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.iFpEvaluate          = _evaluate;
    
    	cniDefineNodeClass(0, factoryObject, L"MessageProcessingxNode", &vftable);
    
    	}
    
    	/* Return address of this factory object to the integration node */
    	return(factoryObject);
    }

    Um nó definido pelo usuário se identifica como um nó de processamento de mensagem ou de saída implementando a função cniEvaluate. Os nós definidos pelo usuário devem implementar uma função de implementação cniEvaluate ou cniRun, caso contrário, o nó de integração não carrega o nó definido pelo usuário e a função de utilitário cniDefineNodeClass falha, retornando CCI_MISSING_IMPL_FUNCTION.

    Quando um fluxo de mensagens que contém um nó de processamento de mensagens definido pelo usuário é implementado com êxito, a função cniEvaluate do nó é chamada para cada mensagem propagada para o nó.

    Os dados do fluxo de mensagens são recebidos no terminal de entrada do nó, ou seja, a mensagem, Environment, LocalEnvironment e ExceptionList.

    Exemplo:
    void cniEvaluate(                
      CciContext* context,                
      CciMessage* localEnvironment,        
      CciMessage* exceptionList,          
      CciMessage* message                 
    ){                                    
      ...
    }
    Para o código mínimo necessário para compilar um nó definido pelo usuário C, consulte Código de estrutura básica C.

Criando uma Instância do Nó

Sobre esta tarefa

Para instanciar seu nó:

Procedimento

  1. Quando o nó de integração recebeu a tabela de ponteiros de função, ele chama a função cniCreateNodeContext para cada instanciação do nó definido pelo usuário. Por exemplo, se houver três fluxos de mensagens que estejam utilizando o nó definido pelo usuário, a função cniCreateNodeContext será chamada para cada um deles. Esta função aloca memória para essa instanciação do nó definido pelo usuário para reter os valores para os atributos configurados. Por exemplo:
    1. A função de usuário cniCreateNodeContext é chamada:
      CciContext* _Switch_createNodeContext(
        CciFactory* factoryObject,
        CciChar*    nodeName,
        CciNode*    nodeObject
      ){
        static char* functionName = (char *)"_Switch_createNodeContext()";
        NODE_CONTEXT_ST* p;
        CciChar          buffer[256];
      
      
      
    2. Alocar um ponteiro para o contexto local e limpar a área do contexto:
        p = (NODE_CONTEXT_ST *)malloc(sizeof(NODE_CONTEXT_ST));
      
        if (p) {
           memset(p, 0, sizeof(NODE_CONTEXT_ST));
      
    3. Salvar o ponteiro do objeto do nó no contexto:
         p->nodeObject = nodeObject;
      
    4. Salvar o nome do nó:
       CciCharNCpy((CciChar*)&p->nodeName, nodeName, MAX_NODE_NAME_LEN);
      
    5. Retorne o contexto do nó:
      return (CciContext*) p;
      
  2. O nó de integração chama as funções de utilitário apropriadas para descobrir sobre os terminais de entrada e os terminais de saída do nó. Um nó tem um número de terminais de entrada e de terminais de saída associados a ele. Com a função do usuário cniCreateNodeContext, as chamadas devem ser feitas para cniCreateInputTerminal e cniCreateOutputTerminal para definir os terminais de nó do usuário. Essas funções devem ser iniciadas dentro da função de implementação cniCreateNodeContext .
    Por exemplo, para definir um nó com um terminal de entrada e dois terminais de saída:
        {
          const CciChar* ucsIn = CciString("in", BIP_DEF_COMP_CCSID) ;
          insInputTerminalListEntry(p, (CciChar*)ucsIn);
          free((void *)ucsIn) ;
        }
        {
          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) ;
        }

    O código anterior inicia as funções insInputTerminalListEntry e insOutputTerminalListEntry. É possível localizar essas funções no código de amostra Common.c; consulte Arquivos do nó de amostra Essas funções definem os terminais para o nó de integração e armazenam identificadores para os terminais. Os identificadores são armazenados na estrutura mencionada pelo valor retornado em CciContext*. Em seguida, o nó pode acessar os identificadores de terminal de dentro das outras funções de implementação (por exemplo, CciEvaluate), porque CciContext é transmitida para essas funções de implementação.

    O código a seguir mostra a definição de insInputTerminalListEntry:

    TERMINAL_LIST_ENTRY *insInputTerminalListEntry( 
      NODE_CONTEXT_ST* context, 
      CciChar*         terminalName 
    ){ 
      static char* functionName = (char *)"insInputTerminalListEntry()"; 
      TERMINAL_LIST_ENTRY* entry; 
      int                  rc; 
     
      entry = (TERMINAL_LIST_ENTRY *)malloc(sizeof(TERMINAL_LIST_ENTRY)); 
      if (entry) { 
     
        /* This entry is the current end of the list */ 
        entry->next = 0; 
     
        /* Store the terminal name */ 
        CciCharCpy(entry->name, terminalName); 
     
        /* Create terminal and save its handle */ 
        entry->handle = cniCreateInputTerminal(&rc, context->nodeObject, (CciChar*)terminalName); 
     
        /* Link an existing previous element to this one */ 
        if (context->inputTerminalListPrevious) context->inputTerminalListPrevious->next = entry; 
        else if ((context->inputTerminalListHead) == 0) context->inputTerminalListHead = entry; 
     
        /* Save the pointer to the previous element */ 
        context->inputTerminalListPrevious = entry; 
      } 
      else { 
        /* Error: Unable to allocate memory */ 
      } 
     
      return(entry); 
    } 
    

    O exemplo a seguir mostra o código para insOutputTerminalListEntry:

    TERMINAL_LIST_ENTRY *insOutputTerminalListEntry( 
      NODE_CONTEXT_ST* context, 
      CciChar*         terminalName 
    ){ 
      static char* functionName = (char *)"insOutputTerminalListEntry()"; 
      TERMINAL_LIST_ENTRY* entry; 
      int                  rc; 
     
      entry = (TERMINAL_LIST_ENTRY *)malloc(sizeof(TERMINAL_LIST_ENTRY)); 
      if (entry) { 
     
        /* This entry is the current end of the list */ 
        entry->next = 0; 
     
        /* Store the terminal name */ 
        CciCharCpy(entry->name, terminalName); 
     
        /* Create terminal and save its handle */ 
        entry->handle = cniCreateOutputTerminal(&rc, context->nodeObject, (CciChar*)terminalName); 
     
        /* Link an existing previous element to this one */ 
        if (context->outputTerminalListPrevious) context->outputTerminalListPrevious->next = entry; 
        else if ((context->outputTerminalListHead) == 0) context->outputTerminalListHead = entry; 
     
        /* Save the pointer to the previous element */ 
        context->outputTerminalListPrevious = entry; 
      } 
      else { 
        /* Error: Unable to allocate memory */ 
      } 
     
      return(entry); 
    } 
    Para o código mínimo necessário para compilar um nó definido pelo usuário C, consulte Código de estrutura básica C.

Definindo Atributos

Sobre esta tarefa

Atributos são configurados sempre que se inicia o nó de integração ou quando se reimplementa um fluxo de mensagens com novos valores. Atributos são configurados pelo nó de integração chamando o código do usuário no encadeamento de configuração. Seu código precisa armazenar esses atributos em sua área de contexto do nó, para utilização no processamento de mensagens posterior.

Após a criação de terminais de entrada e de saída, o nó de integração chama a função cniSetAttribute para passar os valores para os atributos configurados para essa instanciação do nó definido pelo usuário. Exemplo:
    {
      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) ;
    }
O número de atributos de configuração que um nó pode ter é ilimitado. No entanto, um nó não deve implementar um atributo que já esteja implementado como um atributo de configuração base. A lista a seguir mostra os atributos base:
  • label
  • userTraceLevel
  • traceLevel
  • userTraceFilter
  • traceFilter

Implementando a Funcionalidade do Nó

Sobre esta tarefa

Quando o nó de integração recupera uma mensagem da fila e essa mensagem chega no terminal de entrada do nó de es de integração ou os ou de saída de mensagem definido pelo usuário, o nó de integração chama a função de implementação cniEvaluate. Esta função é chamada no encadeamento de processamento de mensagens e deve decidir o que fazer com a mensagem. Essa função pode ser chamada em diversos encadeamentos, principalmente, se instâncias adicionais forem utilizadas.

Excluindo uma Instância do Nó

Sobre esta tarefa

Se um nó for excluído, o nó de integração chama a função cniDeleteNodeContext. Esta função é iniciada no mesmo encadeamento que cniCreateNodeContext. Utilize essa função para liberar os recursos utilizados pelo nó definido pelo usuário. Exemplo:

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