Ir para o conteúdo

Implementação do Conector

Introdução

Embora cada conector construído com o Harmony Connector SDK seja diferente tanto no design quanto na implementação, existem alguns conceitos básicos que todos os conectores incluem, cobertos em Concetores do SDK do conector em Harmony Connector SDK.

Esta página expande esses conceitos principais, cobrindo detalhes de implementação para conectores desenvolvidos usando o SDK do conector.

Se você estiver implementando um conector com a intenção de submetê-lo à certificação por Jitterbit, o código-fonte enviados para certificação devem seguir o estilo de código Java da Jitterbit. Veja os comentários em estilo de código na seção Exemplo Completo abaixo para o arquivo de configuração checkstyle que deve ser usado.

Conector

Nomenclatura

Ao nomear um conector, não inclua caracteres especiais ou barras (/) no nome. Isso pode causar problemas quando o conector é carregado no Agente.

Consulte Adaptador JSON para obter detalhes sobre como especificar os diferentes nomes usados em um conector.

Implementação

Um conector deve estender o BaseJitterbitConnector interface e fornecer uma fábrica que o Harmony pode chame para criar instâncias dele. A implementação base inclui os métodos comuns que um conector deve implemento. (Uma alternativa é implementar o JitterbitConnector interface diretamente.)

Para indicar que a classe que implementa essa interface necessária é aquela a ser considerada como a JitterbitConnector, anote-o com um @Connector anotação e fornecer a classe que implementa sua fábrica.

(Uma alternativa para usar uma anotação é especificar a classe de fábrica no manifesto do arquivo JAR como o valor doJitterbit-Connector-Factory-Classatributo. Ver Registro do conector para detalhes.)

Essas interfaces e seus métodos devem ser implementados:

  • Connection interface e sua open() e close() métodos
  • ConnectionFactory interface e sua createConnection(Map<String,String> properties) método

Aviso

As variáveis estáticas não devem ser usadas em um ambiente multiencadeado, como um conector. Isso pode levar a muitos problemas, incluindo corrupção de dados. Por exemplo, usando e acessando variáveis estáticas públicas em uma classe de utilitário pode causar problemas:

public class MyConnectorUtils {
  public static String accessToken;
  public static String host;
  ...
}
Em vez disso, substitua essas variáveis estáticas públicas por variáveis de instância privadas e use os métodos get e set para acesse-os:

public class MyConnectorUtils {
  private String accessToken;

  public String getAccessToken() {
    return accessToken;
  }

  public String setAccessToken(String accessToken) {
    this.accessToken = accessToken;
  }
  ...
}

Exemplo de Conector

Um exemplo simples de um conector:

/**
 * Example Connector.
 */
@Connector(factory = ExampleConnector.ExampleConnectorFactory.class)
public class ExampleConnector extends BaseJitterbitConnector {

  public static final ExampleConnector INSTANCE = new ExampleConnector();

  static {
    connectionFactory = ExampleConnectionFactory.INSTANCE;
  }

  @Override
  public ConnectionFactory getConnectionFactory() {
    return connectionFactory;
  }

  @Override
  public String getName() {
    return "ExampleConnector";
  }

  private static ConnectionFactory connectionFactory;

  /**
   * ExampleConnectorFactory.
   */
  public static class ExampleConnectorFactory implements
    JitterbitConnector.Factory {
      @Override
      public JitterbitConnector create() {
        return ExampleConnector.INSTANCE;
      }
  }
}

Fábrica de Conexão

Normalmente, os conectores são usados para estabelecer uma conexão com um endpoint. Para criar essa conexão, para facilitar sua configuração e, para testar a conexão, forneça uma implementação de um ConnectionFactory. O a conexão ficará disponível para as atividades do conector através do contexto passado para cada atividade.

Exemplo de Fábrica de Conexão

Um exemplo simples de uma fábrica de conexões:

/**
* Factory that creates an ExampleConnection instance.
*/
public class ExampleConnectionFactory implements ConnectionFactory {

  public static final ExampleConnectionFactory INSTANCE =
    new ExampleConnectionFactory();

  private ExampleConnectionFactory () {}

  /**
   * Returns a connection to an Example endpoint,
   * created from the specified properties.
   *
   * @param props properties for configuring and
   *              creating an Example connection
   * @return the configured connection
   * @throws RuntimeException if the name or password
   *                          of the specified properties
   *                          are empty or null
   */
  @Override
  public Connection createConnection(Map<String, String> props) {
    String name = props.get("name");
    String password = props.get("password");
    String locale = !props.containsKey("locale") ?
      Locale.getDefault().toString() : "EN_US";
    if (name == null || name.length() == 0) {
      throw new RuntimeException("Name property cannot be empty. " +
        "Specify the name associated with the Example connection.");
    }
    if (password == null || password.length() == 0) {
      throw new RuntimeException("Password cannot be empty. " +
        "Specify the password associated with the Example connection.");
    }
    return new ExampleConnection(name, password, locale);
  }

  /**
   * Returns the pool size configuration.
   *
   * @return the pool size configuration
   */
  @Override
  public PoolSizeConfiguration getPoolSizeConfiguration() {
    return new PoolSizeConfiguration();
  }
}

Conexão

No exemplo anterior, a classe ExampleConnection (não mostrado) realmente criaria a conexão. Isso é os requisitos são determinados pelo endpoint ou serviço específico ao qual está conectado, quaisquer bibliotecas sendo usadas para essa conexão (como para um banco de dados ou serviço da web) e os detalhes da conexão.

Na IU do Cloud Studio, o open() método da classe que implementa o Connection interface é chamada quando um o usuário clica no botão Test da configuração da conexão. Isso dá ao conector uma oportunidade não apenas de criar a conexão, mas também para verificar se a conexão funciona. Normalmente, isso pode ser feito chamando o endpoint ou serviço e retornando uma pequena payload ou usando o valor retornado para validar a conexão.

Como esse método é chamado toda vez para abrir uma conexão com um endpoint, se não estiver aberto no momento, é uma boa ideia que qualquer teste seja rápido e pequeno para não atrasar nenhum processamento posterior.

Um exemplo disso é mostrado no DropboxConnection.java do Conector do Dropbox:

  /**
   * Opens a Dropbox version 2 connection.
   */
  public void open() throws ConnectionException {
    if (client != null) {
      return;
    }
    try {
      DbxRequestConfig dbxConfig = new DbxRequestConfig(appKey, locale);
      client = new DbxClientV2(dbxConfig, accessToken);
      ListFolderResult results = client.files().listFolder("");
      System.out.println("Dropbox Connection successful -> app-key: "
        + appKey + ", access-token: " + accessToken);
    } catch (Exception x) {
      x.printStackTrace();
      throw new ConnectionException(Messages.DROPBOX_CODE07,
          Messages.getMessage(Messages.DROPBOX_CODE07_MSG,
            new Object[]{x.getLocalizedMessage()}), x);
    }
  }

Se houver uma conexão existente, o método retorna imediatamente. Caso contrário, uma nova conexão de cliente é criada dentro de um bloco try-catch. Depois que o cliente é criado, ele é testado solicitando a lista de objetos na raiz ("") pasta. Os resultados retornados não são realmente verificados, pois uma chamada bem-sucedida é suficiente. Se houver um problema com a chave de acesso que um usuário fornece para criar a conexão, uma exceção será disparada pelo API do Dropbox. Isso será detectado e lançado novamente com uma mensagem de erro apropriada para o usuário.

Variações desse padrão de design podem ser usadas dependendo do endpoint ou serviço em que o conector está funcionando com.

Atividades

As atividades que um conector expõe e implementa são criadas por classes que implementam JitterbitActivity. A A atividade Jitterbit é uma unidade de trabalho com duas funções:

  • Descoberta/configuração: A descoberta de metadados associados a uma atividade e a configuração de sua parâmetros.
  • Execução: Uma unidade de execução que faz parte de uma cadeia de operações.

Embora o processo de descoberta ocorra primeiro na prática, discutiremos primeiro o processo de execução aqui, pois é ele que determina os requisitos do processo de descoberta.

As atividades são declaradas no arquivo de manifesto como Jitterbit-Activity-* atributos, com IDs que são atribuídos com base sobre o registro do conector com o Harmony. (Ver Registro do conector para detalhes.)

Cada classe de atividade recebe um @Activity anotação para registrá-lo como parte do conector e deve implementar um execute() método que é passado um JitterbitActivity.ExecutionContext.

Durante o tempo de execução, o processo de operação invocará a atividade chamando o método da atividade

execute(ExecutionContext) método.

O contexto de execução contém informações sobre a solicitação (se presente) e o payload. A atividade é responsável por definir a payload de resposta (através da implementação do JitterbitActivity.ExecutionContext.getResponsePayload() método), que será então entregue à próxima atividade dentro da cadeia de operações pelo mecanismo de operação do processo.

A partir do contexto passado, a atividade tem sua conexão com o Harmony. Ele pode obter:

  • Os parâmetros, se houver, com os quais a atividade foi configurada pelo usuário final. Por exemplo, chamando o context.getFunctionParameters().get("folder") método.
  • Eventuais conexões estabelecidas na configuração inicial da conexão, caso o desenvolvedor as disponibilize. Por exemplo, chamando o context.getConnection() método.
  • Parâmetros do próprio conector, caso o desenvolvedor disponibilize.
  • O payload de solicitação ou resposta, gravado ou obtido da conexão.

Parâmetros configuráveis são definidos pelo usuário final na IU do Cloud Studio e são feitos na configuração do conector e suas atividades. Eles são declarados no adapter.json incluído no arquivo JAR do conector.

As cargas úteis de solicitação ou resposta de uma atividade são os dados gravados ou obtidos da conexão; eles são determinados pelos arquivos de esquema XML que definem essas cargas úteis, conforme descrito no próxima seção.

Solicitação e Resposta de Atividade

A solicitação e a resposta das atividades de um conector são tratadas usando a API Java para XML Binding (JAXB, versão 2+) para gerar classes Java a partir de esquemas XML. Um arquivo de esquema XML separado (.xsd) é usado para cada solicitação ou resposta. O mapeamento entre os arquivos gerados e essas fontes é dado no sun-jaxb.episode arquivo de saída.

As classes Java geradas podem então ser importadas pelas classes que implementam a atividade execute() método.

Por exemplo, no conector do Dropbox FetchFileActivity, os dados estão se movendo do Dropbox para o Harmony. Que execute() método usa um DropboxConnection e a API do Dropbox para recuperar dados e metadados; então define esses valores em um objeto de resposta (uma instância de FetchFileResponse) e, em seguida, organiza a resposta para o fluxo de saída de payload de resposta.

Por outro lado, no conector do Dropbox PutFileActivity, os dados estão se movendo do Harmony para o Dropbox. O

execute() método nessa classe funciona na direção oposta. Ele descompacta um fluxo de entrada, usa o Dropbox API para carregar no Dropbox e, em seguida, cria um objeto de resposta (neste caso, uma instância de PutFileResponse) preenchido com valores obtidos a partir da resposta do Dropbox.

Cada atividade é responsável pela implementação do getActivityRequestResponseMetadata() método e retorno um ActivityRequestResponseMetaData. Os arquivos de esquema XML são usados para criar o ActivityRequestResponseMetaData.

Para auxiliar na criação desse objeto, um utilitário auxiliar (como mostrado no conector do Dropbox DropboxUtils.setRequestResponseSchemas de DropboxUtils.java) está disponível para carregar os arquivos de esquema XML e definir como solicitação ou resposta.

Eles aparecerão na IU do Cloud Studio no esquema de dados exibido durante a etapa final de uma atividade configuração. Se uma resposta ou solicitação não for desejada ou necessária, ela pode ser desconsiderada e nenhuma árvore de estrutura de dados será criado na IU do Cloud Studio para esse componente. A atividade Buscar arquivo do conector do Dropbox é um exemplo disto; ele tem apenas uma resposta e nenhuma estrutura de dados de solicitação.

Se uma solicitação for necessária, ela poderá ser especificada no arquivo JSON que define a IU do Cloud Studio para o conector. Ao declarar inputRequired para uma atividade no adapter.json para o conector forçará a IU do Cloud Studio lançar um erro de validação para a operação pai se não houver uma transformação de origem antes do uso do atividade. Por exemplo, este fragmento de um arquivo JSON mostra a definição da atividade SAP BAPI como exigindo entrada:

"activities": {
    "bapi": {
        "displayName": "BAPI",
        "inputRequired": true,
        "properties": [
            " . . . "
        ]
    }
}

Consulte Componentes de IU do SDK do conector para obter detalhes sobre como definir o arquivo JSON que especifica o Cloud IU do estúdio.

Descoberta e Metadados

Como parte do ciclo de vida da configuração de uma atividade de conector, você pode usar um processo de descoberta para obter informações necessárias para concluir a configuração.

Um exemplo disso é obter um nome de tabela e, a partir dessa seleção, obter os nomes dos campos. Para facilitar isso, uma interface no Connector SDK (org.jitterbit.connector.sdk.Discoverable) está disponível para implementação.

Quando a configuração de uma atividade é chamada na IU do Cloud Studio, a interface getObjectList() método é chamado, permitindo que o conector para criar e retornar uma lista de objetos descobertos que podem ser exibidos na interface do usuário. Depois que uma seleção é feita pelo usuário, essa seleção fica disponível no menu da interface getActivityRequestResponseMetadata() método através do activityFunctionParams parâmetro que é passado.

Veja o ProcessFileActivity do conector do Dropbox para obter um exemplo de como a descoberta pode ser usada na criação de metadados.

Outros exemplos estão incluídos nas descrições dos componentes de IU do Cloud Studio que utilizam metadados, conforme descrito na próxima seção.

IU do Cloud Studio

O conector e suas atividades são configurados por meio da IU do Cloud Studio. A interface do usuário desses configurações são especificadas no adapter.json arquivo. O nome real do arquivo JSON pode ser alterado de esse padrão; é especificado no manifesto do arquivo JAR.

O arquivo especifica a interface do usuário para o conector e todas as suas atividades. Os detalhes do arquivo JSON são cobertos em Componentes de IU do SDK do conector.

Os componentes são categorizados como componentes básicos ou complexos.

  • Componentes básicos não interagem com o conector. Eles são usados para simplesmente receber um valor do usuário e devolvê-lo ao conector.
  • Componentes complexos são mais sofisticados e envolvem vários métodos e código adicional no conector para implementar seu processo de descoberta e usar na execução. Eles são destinados a resolver desafios de interface do usuário de conector mais difíceis.

Observe que o name usado no arquivo JSON deve ser o mesmo nome sob o qual o conector está registrado e definido no código Java. Consulte Registro do conector para detalhes.

Manifesto

Esses vários componentes (informações de registro para o conector e cada atividade, classes, externo caminho de classe, nome do arquivo de interface do usuário do conector) estão vinculados no MANIFEST.MF que está incluído no arquivo JAR que arquiva o conector. Os detalhes do registro e do manifesto são abordados em Registro do conector.

Construindo o Conector

Como de costume para um projeto Java dessa complexidade, um Maven pom.xml arquivo é essencial para vincular corretamente todos dependências importadas e todos os componentes juntos. Ao executar a compilação, você precisará primeiro compilar o Arquivos de esquema XML antes da compilação e empacotamento do Java. O comando apropriado do Maven é:

$ mvn jaxb2:xjc compile install

Instalando

Ao contrário dos plug-ins do Harmony (que são instalados ao carregá-los no Harmony e permitir que o plataforma para instalá-los associando os plug-ins a um Grupo de Agentes), conectores Harmony construídos com o SDK é instalado colocando manualmente seus arquivos JAR no diretório apropriado de um Agente Privado. Se o conector for usado em um Grupo de Agentes com mais de um agente, os arquivos JAR precisam ser copiados para cada Private Agente. Se o conector depende de bibliotecas específicas que não estão incluídas em seus arquivos JAR, elas precisam ser instalados no classpath de cada Agente Privado para que possam ser acessados no momento em que o conector for carregado pelo agente.

Ao desenvolver um conector, se estiver executando em Linux ou macOS, recomendamos o uso de um Docker Agente Privado, pois pode ser configurado para montar como um volume local para o agente no diretório de construção do conector. Para Windows, um Windows Agente Privado pode ser usado.

O diretório do conector é verificado automaticamente pelo agente quanto a quaisquer alterações e quaisquer conectores modificados são recarregado automaticamente, sem exigir que o agente seja reiniciado ou solicitado. Isso agiliza e simplifica o processo de desenvolvimento. Observe que você não deve compilar diretamente neste diretório, pois os produtos de compilação intermediários pode confundir o agente.

Locais do Conector

Agente Caminho do diretório do conector (padrão)
Agente Privado do Windows C:\Program Files (x86)\Jitterbit Agent\Connectors\
Linux Agente Privado /opt/jitterbit/Connectors/
Docker Agente Privado /opt/jitterbit/Connectors/
Este diretório geralmente é mapeado para um diretório externo no comando que inicia a imagem do Docker

Sincronizando

Conectores públicos (conectores publicados pela Jitterbit) sincronizam automaticamente com um agente Harmony conforme necessário. Prevenir a sincronização de conectores públicos, uma variável de ambiente (SKIP_SYNC_CONNECTORS) está disponível que controla sincronizando. Defina essa variável de ambiente no shell que está executando o agente e reinicie o agente.

Contexto SKIP_SYNC_CONNECTORS para um asterisco interromperá a sincronização de todos os conectores públicos:

SKIP_SYNC_CONNECTORS=*

Contexto SKIP_SYNC_CONNECTORS a uma lista separada por vírgulas de conectores interromperá a sincronização de todos os conectores, exceto os listados:

SKIP_SYNC_CONNECTORS=Box,Magento

Este último exemplo interromperá a sincronização de todos os conectores públicos, exceto os conectores Box e Magento, que será sincronizado.

Exemplo Completo

O conector do Dropbox é um exemplo de trabalho completo desses conceitos. Consulte-o para obter detalhes adicionais.

Se você deseja personalizar o conector do Dropbox em seu próprio conector usando seu próprio pacote e domínio, você deve precisa atualizar—além dos nomes dos pacotes, caminhos, conteúdo do código Java e o registro do seu conector—estes itens:

  • pom.xml: Substitua o uso do Jitterbit pelo seu próprio domínio, conforme apropriado; atualize o nome do artefato e versão
  • MANIFEST.MF: Substitua o uso de Jitterbit pelo seu próprio nome, conforme apropriado; atualizar as chaves e IDs
  • DropboxConstants.java: Atualize o namespace em conjunto com os arquivos XML Schema XSD; atualizar o nome do conector
  • Arquivos XSD de esquema XML: Atualize o namespace de destino, em conjunto com o DropboxConstants.java
  • adapter.json e BaseJitterbitConnector: O campo nome do adapter.json e o nome anotando o extensão de classe BaseJitterbitConnector são usados para nomear o conector. Se especificado no adapter.json, o o framework usará esse nome; caso contrário, o nome fornecido na anotação será usado. Ver DropboxConstants.java e DropboxConnector.java para exemplos de como isso acontece.

Estilo de Código

O conector do Dropbox foi formatado de acordo com o Jitterbit Estilo de código Java. Esse estilo deve ser seguido para qualquer código enviado ao Jitterbit como parte do certificação de um conector.

Para implementar esse estilo em seu código-fonte:

  • inclua em seu código fonte o Jitterbit checkstyle.xml arquivo; e
  • inclua no seu pom.xml arquivar uma referência ao maven-checkstyle-plugin e essa checkstyle.xml arquivo. Adicionar para o <plugins> do <build> seção do pom.xml:
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-checkstyle-plugin</artifactId>
      <version>2.17</version>
      <executions>
        <execution>
          <id>validate</id>
          <phase>process-test-classes</phase>
          <configuration>
            <configLocation>checkstyle.xml</configLocation>
            <suppressionsLocation>suppressions.xml</suppressionsLocation>
            <encoding>UTF-8</encoding>
            <consoleOutput>true</consoleOutput>
            <failsOnError>true</failsOnError>
            <includeTestSourceDirectory>true</includeTestSourceDirectory>
          </configuration>
          <goals>
            <goal>check</goal>
          </goals>
        </execution>
      </executions>
      <dependencies>
        <dependency>
          <groupId>com.puppycrawl.tools</groupId>
          <artifactId>checkstyle</artifactId>
          <version>6.19</version>
        </dependency>
      </dependencies>
    </plugin>
    

Consulte o Conector do Dropbox pom.xml para obter um exemplo de uso desse estilo de verificação em um projeto.