Implementación del conector utilizando el SDK de Conectores de Jitterbit
Introducción
Aunque cada conector construido con el SDK de Conectores de Jitterbit será diferente en diseño e implementación, hay algunos conceptos básicos que todos los conectores incluyen, cubiertos en Conceptos del SDK de Conectores en SDK de Conectores de Jitterbit.
Esta página amplía esos conceptos básicos, cubriendo detalles de implementación para conectores desarrollados utilizando el SDK de Conectores.
Si se está implementando un conector con la intención de enviarlo para certificación por Jitterbit, el código fuente enviado para certificación debe seguir el estilo de código Java de Jitterbit. Consulte los comentarios sobre estilo de código en la sección Ejemplo completo a continuación para el archivo de configuración de checkstyle que debe utilizarse.
Conector
Nomenclatura
Al nombrar un conector, no incluya caracteres especiales ni barras diagonales (/
) en el nombre. Estos pueden causar problemas cuando el conector se carga en el agente.
Consulte JSON del adaptador para obtener detalles sobre cómo especificar los diferentes nombres utilizados en un conector.
Implementación
Un conector debe extender la interfaz BaseJitterbitConnector
y proporcionar una fábrica que Harmony pueda
llamar para crear instancias de él. La implementación base incluye los métodos comunes que un conector debe
implementar. (Una alternativa es implementar la interfaz JitterbitConnector
directamente.)
Para indicar que la clase que implementa esta interfaz requerida es la que debe considerarse como el
JitterbitConnector
, anótela con una anotación @Connector
y proporcione la clase que implementa su fábrica.
(Una alternativa a usar una anotación es especificar la clase de fábrica en el manifiesto del archivo JAR como el valor
del atributo Jitterbit-Connector-Factory-Class
. Consulte
Registro de conectores para obtener detalles.)
Estas interfaces y sus métodos deben ser implementados:
- Interfaz
Connection
y sus métodosopen()
yclose()
- Interfaz
ConnectionFactory
y su métodocreateConnection(Map<String,String> properties)
Advertencia
Las variables estáticas no deben ser utilizadas en un entorno multi-hilo como un conector. Esto puede llevar a muchos problemas, incluyendo la corrupción de datos. Por ejemplo, usar y acceder a variables estáticas públicas en una clase de utilidad puede causar problemas:
public class MyConnectorUtils {
public static String accessToken;
public static String host;
...
}
public class MyConnectorUtils {
private String accessToken;
public String getAccessToken() {
return accessToken;
}
public String setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
...
}
Example connector
Un ejemplo simple de un 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;
}
}
}
Connection factory
Los conectores se utilizan típicamente para establecer una conexión con un punto final. Para crear esa conexión, facilitar
su configuración y probar la conexión, proporcione una implementación de ConnectionFactory
. La
conexión estará disponible para las actividades del conector a través del contexto pasado a cada actividad.
Example connection factory
Un ejemplo simple de una fábrica de conexiones:
/**
* 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();
}
}
Connection
En el ejemplo anterior, la clase ExampleConnection
(no mostrada) realmente crearía la conexión. Sus
requisitos son determinados por el punto final o servicio particular al que se está conectando, cualquier biblioteca que se esté utilizando para
esa conexión (como a una base de datos o servicio web), y los detalles de la conexión.
En la interfaz de usuario de Integration Studio, el método open()
de la clase que implementa la interfaz Connection
se llama cuando un
usuario hace clic en el botón Test de la configuración de la conexión. Esto le da al conector la oportunidad de no solo
crear la conexión, sino también de verificar que la conexión funcione. Típicamente, esto se puede hacer llamando al
punto final o servicio y devolviendo un pequeño payload o utilizando el valor devuelto para validar la conexión.
Como este método se llama cada vez que se abre una conexión a un endpoint si no está actualmente abierta, es una buena idea que cualquier prueba sea rápida y pequeña para no retrasar ningún procesamiento adicional.
Un ejemplo de esto se muestra en el DropboxConnection.java
del
conector de 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);
}
}
Si hay una conexión existente, el método devuelve inmediatamente. De lo contrario, se crea una nueva conexión de cliente dentro de un bloque try-catch. Una vez que se crea el cliente, se prueba solicitando la lista de objetos en la carpeta raíz (""
). Los resultados devueltos no se verifican realmente, ya que una llamada exitosa es suficiente. Si hay un problema con la clave de acceso que un usuario proporciona para crear la conexión, la API de Dropbox generará una excepción. Esta será capturada y luego lanzada nuevamente con un mensaje de error apropiado para el usuario.
Se pueden utilizar variaciones de este patrón de diseño dependiendo del endpoint o servicio con el que esté trabajando el conector.
Actividades
Las actividades que un conector expone e implementa son creadas por clases que implementan JitterbitActivity
. Una actividad de Jitterbit es una unidad de trabajo con dos roles:
- Descubrimiento/configuración: El descubrimiento de metadatos asociados con una actividad y la configuración de sus parámetros.
- Ejecución: Una unidad de ejecución que es parte de una cadena de operaciones.
Aunque el proceso de descubrimiento ocurre primero en la práctica, discutiremos el proceso de ejecución primero aquí, ya que es lo que determina los requisitos del proceso de descubrimiento.
Las actividades se declaran en el archivo de manifiesto como atributos Jitterbit-Activity-*
, con IDs que se asignan en función del registro del conector con Harmony. (Consulte
Registro de conectores para más detalles.)
Cada clase de actividad recibe una anotación @Activity
para registrarla como parte del conector y debe implementar un método execute()
que recibe un JitterbitActivity.ExecutionContext
.
Durante el tiempo de ejecución, el proceso de operación invocará la actividad llamando al método execute(ExecutionContext)
de la actividad.
El contexto de ejecución contiene información sobre la solicitud (si está presente) y la carga útil. La actividad es responsable de establecer la carga útil de respuesta (implementando el método JitterbitActivity.ExecutionContext.getResponsePayload()
), que luego será entregada a la siguiente actividad dentro de la cadena de operaciones por el motor de operaciones del proceso.
Desde el contexto pasado, la actividad tiene su conexión a Harmony. Puede obtener:
- Los parámetros, si los hay, con los que la actividad fue configurada por el usuario final. Por ejemplo, llamando al método
context.getFunctionParameters().get("folder")
. - Cualquier conexión establecida en la configuración inicial de la conexión, si el desarrollador las hace disponibles. Por ejemplo, llamando al método
context.getConnection()
. - Parámetros del conector en sí, si el desarrollador los hace disponibles.
- La carga útil de solicitud o respuesta, escrita o obtenida de la conexión.
Los parámetros configurables son establecidos por el usuario final en la interfaz de usuario de Integration Studio y se realizan en la configuración del conector y sus actividades. Se declaran en el archivo adapter.json
incluido en el archivo JAR del conector.
Las cargas útiles de solicitud o respuesta de una actividad son los datos escritos o obtenidos de la conexión; se determinan por los archivos de esquema XML que definen esas cargas útiles, como se describe en la siguiente sección.
Solicitud y respuesta de actividad
La solicitud y respuesta de las actividades de un conector se manejan utilizando la API de Java para el enlace XML (JAXB, versión 2+) para generar clases de Java a partir de esquemas XML. Se utiliza un archivo de esquema XML separado (.xsd
) para cada solicitud o respuesta. La correspondencia entre los archivos generados y estas fuentes se da en el archivo de salida sun-jaxb.episode
.
Las clases de Java generadas pueden ser importadas por las clases que implementan el método execute()
de la actividad.
Por ejemplo, en la FetchFileActivity
del conector de Dropbox, los datos se mueven de Dropbox a Harmony. Ese método execute()
utiliza una DropboxConnection
y la API de Dropbox para recuperar datos y metadatos; luego establece esos valores en un objeto de respuesta (una instancia de FetchFileResponse
), y luego envía la respuesta al flujo de salida de carga útil de respuesta.
Por el contrario, en la PutFileActivity
del conector de Dropbox, los datos se mueven de Harmony a Dropbox. El método execute()
en esa clase trabaja en la dirección opuesta. Deserializa un flujo de entrada, utiliza la API de Dropbox para cargar en Dropbox, y luego crea un objeto de respuesta (en este caso, una instancia de PutFileResponse
) completado con valores obtenidos de la respuesta de Dropbox.
Cada actividad es responsable de implementar el método getActivityRequestResponseMetadata()
y devolver un ActivityRequestResponseMetaData
. Los archivos de esquema XML se utilizan para crear el ActivityRequestResponseMetaData
.
Para ayudar en la creación de ese objeto, hay una utilidad auxiliar (como se muestra en DropboxUtils.setRequestResponseSchemas
de DropboxUtils.java
) disponible para cargar los archivos de esquema XML y establecerlos como la solicitud o respuesta.
Luego aparecerán en la interfaz de usuario de Integration Studio en el esquema de datos mostrado durante el paso final de la configuración de una actividad. Si no se desea o se requiere una respuesta o solicitud, se puede ignorar y no se construirá un árbol de estructura de datos en la interfaz de usuario de Integration Studio para ese componente. La actividad Fetch File del conector de Dropbox es un ejemplo de esto; solo tiene una respuesta y no una estructura de datos de solicitud.
Si se requiere una solicitud, esto se puede especificar en el archivo JSON que define la interfaz de usuario de Integration Studio para el conector. Al declarar inputRequired
para una actividad en el adapter.json
del conector, se forzará a la interfaz de usuario de Integration Studio a lanzar un error de validación para la operación principal si no hay una transformación de origen antes del uso de la actividad. Por ejemplo, este fragmento de un archivo JSON muestra la definición de la actividad SAP BAPI como que requiere entrada:
"activities": {
"bapi": {
"displayName": "BAPI",
"inputRequired": true,
"properties": [
" . . . "
]
}
}
Consulte Componentes de la interfaz de usuario del SDK de Conectores para obtener detalles sobre cómo definir el archivo JSON que especifica la interfaz de usuario de Cloud Studio.
Descubrimiento y metadatos
Como parte del ciclo de vida de la configuración de una actividad de conector, se puede utilizar un proceso de descubrimiento para obtener información que se requiere para completar la configuración.
Un ejemplo de esto es obtener un nombre de tabla y, a partir de esta selección, obtener nombres de campos. Para facilitar esto, hay una interfaz en el SDK de Conectores (org.jitterbit.connector.sdk.Discoverable
) disponible para implementación.
Cuando se llama a la configuración de una actividad en la interfaz de usuario de Integration Studio, se llama al método getObjectList()
de la interfaz, lo que permite al
conector crear y devolver una lista de objetos descubiertos que se pueden mostrar en la interfaz de usuario. Después de que el usuario realiza una selección,
esa selección está disponible en el método getActivityRequestResponseMetadata()
de la interfaz a través del
parámetro activityFunctionParams
que se pasa.
Consulte la ProcessFileActivity
del conector de Dropbox para un ejemplo de cómo se puede utilizar el descubrimiento en la creación de metadatos.
Se incluyen más ejemplos en las descripciones de los componentes de la interfaz de usuario de Integration Studio que utilizan metadatos, como se describe en la siguiente sección.
Interfaz de usuario de Integration Studio
El conector y sus actividades se configuran a través de la interfaz de usuario de Integration Studio. La interfaz de usuario de esas
configuraciones se especifica en el archivo adapter.json
. El nombre real del archivo JSON se puede cambiar de
ese valor predeterminado; se especifica en el manifiesto del archivo JAR.
El archivo especifica la interfaz de usuario para el conector y todas sus actividades. Los detalles del archivo JSON se cubren en Componentes de la interfaz de usuario del SDK de Conectores.
Los componentes se clasifican como componentes básicos o complejos.
- Componentes básicos no interactúan con el conector. Se utilizan simplemente para recibir un valor del usuario y devolverlo al conector.
- Componentes complejos son más sofisticados e involucran múltiples métodos y código adicional en el conector para implementar su proceso de descubrimiento y para usarse en la ejecución. Están destinados a resolver desafíos más difíciles en la interfaz de usuario del conector.
Tenga en cuenta que el name
utilizado en el archivo JSON debe ser el mismo nombre bajo el cual está registrado el conector y que está definido en el código Java. Consulte Registro de conectores para más detalles.
Manifiesto
Estos diversos componentes (información de registro para el conector y cada actividad, clases, classpath de terceros, nombre de archivo de la interfaz de usuario del conector) están vinculados en el MANIFEST.MF
que se incluye en el archivo JAR que archiva el conector. Los detalles del registro y el manifiesto se cubren en Registro de conectores.
Construir el conector
Como es habitual en un proyecto de Java de esta complejidad, un archivo Maven pom.xml
es esencial para vincular correctamente todas las dependencias importadas y todos los componentes juntos. Al ejecutar la construcción, primero deberá compilar los archivos XML Schema antes de la compilación y empaquetado de Java. El comando Maven apropiado es:
$ mvn jaxb2:xjc compile install
Instalar
A diferencia de los complementos de Jitterbit (que se instalan subiéndolos a Harmony y permitiendo que la plataforma los instale asociándolos con un grupo de agentes), los conectores de Harmony construidos con el SDK se instalan colocando manualmente sus archivos JAR en el directorio apropiado de un agente privado. Si el conector se va a utilizar en un grupo de agentes con más de un agente, los archivos JAR deben copiarse a cada agente privado. Si el conector depende de bibliotecas específicas que no están incluidas en sus archivos JAR, deben instalarse en el classpath de cada agente privado para que puedan ser accedidas en el momento en que el conector sea cargado por el agente.
Cuando se desarrolla un conector, si se está ejecutando en Linux o macOS, se recomienda utilizar un agente privado de Docker, ya que se puede configurar para montar como un volumen local al agente el directorio de construcción del conector. Para Windows, se puede utilizar un agente privado de Windows.
El directorio del conector es escaneado automáticamente por el agente en busca de cambios, y cualquier conector modificado se recarga automáticamente, sin necesidad de reiniciar o solicitar al agente. Esto acelera y simplifica el proceso de desarrollo. Tenga en cuenta que no debe construir directamente en este directorio, ya que los productos de construcción intermedios pueden confundir al agente.
Ubicaciones de conectores
Agente | Ruta del directorio del conector (predeterminado) |
---|---|
Agente privado de Windows | C:\Program Files (x86)\Jitterbit Agent\Connectors\ |
Agente privado de Linux | /opt/jitterbit/Connectors/ |
Agente privado de Docker | /opt/jitterbit/Connectors/ Este directorio generalmente está mapeado a un directorio externo en el comando que inicia la imagen de Docker |
Sincronización
Los conectores públicos (conectores publicados por Jitterbit) se sincronizan automáticamente con un agente de Jitterbit según sea necesario. Para evitar la sincronización de conectores públicos, hay una variable de entorno (SKIP_SYNC_CONNECTORS
) disponible que controla la sincronización. Establezca esta variable de entorno en el shell que está ejecutando el agente y reinicie el agente.
Establecer SKIP_SYNC_CONNECTORS
a un asterisco detendrá la sincronización de todos los conectores públicos:
SKIP_SYNC_CONNECTORS=*
Establecer SKIP_SYNC_CONNECTORS
a una lista de conectores separada por comas detendrá la sincronización de todos los conectores públicos, excepto los que están listados:
SKIP_SYNC_CONNECTORS=Box,Magento
Este último ejemplo detendrá la sincronización de todos los conectores públicos, excepto para los conectores de Box y Magento, que serán sincronizados.
Ejemplo completo
El conector de Dropbox es un ejemplo completo de trabajo de estos conceptos. Consúltalo para obtener detalles adicionales.
Si deseas personalizar el conector de Dropbox en tu propio conector utilizando tu propio paquete y dominio, necesitarías actualizar, además de los nombres de los paquetes, las rutas, el contenido del código Java y el registro de tu conector, estos elementos:
pom.xml
: Reemplaza el uso de Jitterbit con tu propio dominio según corresponda; actualiza el nombre del artefacto y la versión.MANIFEST.MF
: Reemplaza el uso de Jitterbit con tu propio nombre según corresponda; actualiza las claves y los IDs.DropboxConstants.java
: Actualiza el espacio de nombres en conjunto con los archivos XML Schema XSD; actualiza el nombre del conector.- Archivos XML Schema XSD: Actualiza el espacio de nombres objetivo, en conjunto con el
DropboxConstants.java
. adapter.json
yBaseJitterbitConnector
: El campo de nombre deladapter.json
y el nombre que anota la clase que extiendeBaseJitterbitConnector
se utilizan para nombrar el conector. Si se especifica en eladapter.json
, el marco utilizará ese nombre; de lo contrario, se utilizará el nombre proporcionado en la anotación. ConsultaDropboxConstants.java
yDropboxConnector.java
para ejemplos de cómo sucede esto.
Estilo de código
El conector de Dropbox ha sido formateado siguiendo el estilo de código Java de Jitterbit. Este estilo debe ser seguido para cualquier código que se envíe a Jitterbit como parte de la certificación de un conector.
Para implementar este estilo en tu código fuente:
-
Incluye en tu código fuente el archivo Jitterbit
checkstyle.xml
; y -
Incluye en tu archivo
pom.xml
una referencia almaven-checkstyle-plugin
y ese archivocheckstyle.xml
. Agrega a los<plugins>
de la sección<build>
delpom.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>
Refiérase al
archivo pom.xml
del conector de Dropbox
para un ejemplo de cómo usar este checkstyle en un proyecto.