Se adjunta información tècnica técnica de interes interés que se estuvo generando durante el desarrollo del proyecto por parte de Eldar.
View file | ||||
---|---|---|---|---|
|
El presente informe detalla el desarrollo y configuración de una aplicación de procesamiento de transacciones basada en el framework jPOS. Este sistema proporciona una solución robusta y escalable para el procesamiento de transacciones financieras, haciendo uso del estándar ISO 8583.
Arquitectura del flujo del escenario 1
- el primer escenario que consta de compras de Paraguayos en Paraguay.
Arquitectura del flujo del escenario 2 y 3
- El segundo escenario consta de compras de Paraguayos en Argentina/Brasil/Uruguay (no pasan por discover internacional).
- El tercer escenario consta de compras de Paraguayos internacional (pasan por discover internacional)
Estructura de Carpetas de la app
Archivos de cgf
En la ruta src/dist/cfg, vamos a encontrar el archivo de configuracion `iso93ascii.xml` y `iso87ascii.xml`
iso92ascii.xml & iso87ascii.xml:
Los archivos `iso93ascii.xml` y `iso87ascii.xml` son una configuración de jPOS para el empaquetador genérico (GenericPackager).
El empaquetador genérico es una parte de jPOS que se utiliza para convertir mensajes ISO 8583 entre una representación binaria (o de cadena) y una representación de objeto (ISOMsg). Los archivos iso93ascii.xml y iso87ascii.xml definen la estructura de un mensaje ISO 8583 de acuerdo con la versión del estándar y utilizando codificación ASCII. Cada campo en un mensaje ISO 8583 tiene un número (del 0 al 128), y este archivo define para cada número de campo:
- id: El número del campo.
- length: La longitud máxima del campo.
- name: Un nombre descriptivo para el campo.
- class: La clase de jPOS que se utiliza para empaquetar y desempaquetar el campo.
El archivo `iso93ascii.xml` Se utiliza para el flujo 1, mientras que el archivo de configuracion `iso87ascii.xml` se utiliza para los flujos 2 y 3
Archivos de Configuraciones
En la ruta src/dist/deploy/ vamos a encontrar todos los archivos de configuraciones XML de jpos
00_logger.xml
**Este archivo configura el registro de logs para el sistema (imprime los mensajes de log en la consola, almacena en un buffer y escribe en archivos que se rotan y comprimen diariamente).**
**Función**: Define un logger llamado "Q2" que redirige la salida estándar y de error al stdout y stderr respectivamente. Además, especifica tres listeners de logs: **`SimpleLogListener`**, **`BufferedLogListener`** y **`DailyLogListener`**, cada uno con sus propias configuraciones.
1. `SimpleLogListener`: Este listener imprime los mensajes de log en la consola. El nivel de log se establece en "DEBUG", lo que significa que se imprimirán todos los mensajes de log, incluyendo los de nivel DEBUG, INFO, WARN y ERROR.
2. `BufferedLogListener`: Este listener almacena los mensajes de log en un buffer. El tamaño máximo del buffer se establece en "100", lo que significa que se almacenarán hasta 100 mensajes de log. El nombre del buffer es "logger.Q2.buffered".
3. `DailyLogListener`: Este listener escribe los mensajes de log en un archivo que se rota diariamente. Los archivos de log se guardan en la carpeta "log/q2" y tienen la extensión ".log". Los archivos se comprimen en formato gzip después de que se roten.
- Archivo
```xml
<?xml version="1.0" encoding="UTF-8"?>
<logger name="Q2" class="org.jpos.q2.qbean.LoggerAdaptor">
<property name="redirect" value="stdout, stderr" />
<log-listener class="org.jpos.util.SimpleLogListener" />
<log-listener class="org.jpos.util.BufferedLogListener">
<property name="max-size" value="100" />
<property name="name" value="logger.Q2.buffered" />
</log-listener>
<log-listener class="org.jpos.util.DailyLogListener">
<property name="window" value="86400" />
<property name="prefix" value="log/q2" />
<property name="suffix" value=".log"/>
<property name="date-format" value="-yyyy-MM-dd-HH"/>
<property name="compression-format" value="gzip"/>
</log-listener>
</logger>
```
10_channel_cabal_ar.xml
**Configura un adaptador de canal para la comunicación ISO 8583 con un servidor específico, utilizando el formato ASCII para los mensajes y una configuración de empaquetador específica..**
**Función**: Define un adaptador de canal llamado "cabal-ar-channel" que utiliza un canal ASCII para la comunicación. Especifica la dirección del host y el puerto del servidor al que se conectará el canal. También configura los nombres de los queues de entrada y salida, así como el retraso de reconexión en caso de desconexión
Desglose de las configuraciones:
- `<channel-adaptor>`: Este es el elemento raíz que define un adaptador de canal. Un adaptador de canal en jPOS es una entidad que maneja la comunicación de red para un canal específico.
- `name='cabal-ar-channel'`: Este es el nombre del adaptador de canal. Se utiliza para identificar el adaptador de canal en la configuración de jPOS.
- `class="org.jpos.q2.iso.ChannelAdaptor"`: Esta es la clase Java que implementa el adaptador de canal. En este caso, se utiliza la clase `ChannelAdaptor` proporcionada por jPOS.
- `logger="Q2"`: Este es el logger que se utilizará para registrar los eventos relacionados con este adaptador de canal.
- `<channel>`: Este elemento define el canal que se utilizará para la comunicación de red.
- `class="org.jpos.iso.channel.ASCIIChannel"`: Esta es la clase Java que implementa el canal. En este caso, se utiliza la clase `ASCIIChannel` proporcionada por jPOS, que es un canal que utiliza el formato ASCII para las transacciones ISO 8583.
- `packager="org.jpos.iso.packager.GenericPackager"`: Define la clase que se utilizará para empaquetar y desempaquetar los mensajes ISO 8583.
- `<property name="packager-config" value="cfg/iso93ascii.xml"/>`: Esta propiedad define la configuración del empaquetador (packager). En este caso, se utiliza la configuración definida en el archivo `iso93ascii.xml`.
- `<property name="host" value="@cabal.ar.host@"/>` y `<property name="port" value="@cabal.ar.port@"/>`: Estas propiedades definen el host y el puerto del servidor al que se conectará este canal. En este caso, se setearán dinámicamente por el tag del target del `gradle.properties` (por defecto está en develop, con las IP y puerto de Cabal Argentina).
- `<filter class="com.app.gateway.filters.IncomingFilterCabalAr" direction="incoming"/>`: Filtro para mensajes entrantes.
- `<filter class="com.app.gateway.filters.OutgoingFilterCabalAr" direction="outgoing"/>`: Filtro para mensajes salientes.
- `<filter class="org.jpos.iso.filter.MacroFilter" direction="outgoing">`: Filtro macro para mensajes salientes.
- `<property name="valid" value="0 2 11 12 22 23 41 55"/>`: Define los campos válidos para el filtro macro.
- `<in>` y `<out>`: Estos elementos definen las colas de mensajes entrantes y salientes para este adaptador de canal. En este caso, los mensajes que se envían a través de este canal se colocarán en la cola `cabal-ar-send` y los mensajes que se reciben a través de este canal se colocarán en la cola `cabal-ar-receive`.
- `<reconnect-delay>`: Este elemento define el tiempo de espera (en milisegundos) antes de intentar reconectar después de una desconexión. En este caso, el adaptador de canal intentará reconectar después de 10 segundos (10000 milisegundos) si se desconecta.
- Archivo
```xml
<?xml version="1.0" ?>
<channel-adaptor name='cabal-ar-channel' class="org.jpos.q2.iso.ChannelAdaptor" logger="Q2">
<channel class="org.jpos.iso.channel.ASCIIChannel"
packager="org.jpos.iso.packager.GenericPackager"
type="client"
connect="no" logger="Q2" realm="cabal-ar-channel">
<property name="packager-config" value="cfg/iso93ascii.xml"/>
<property name="host" value="@cabal.ar.host@" />
<property name="port" value="@cabal.ar.port@" />
<filter class="com.app.gateway.filters.IncomingFilterCabalAr" direction="incoming"/>
<filter class="com.app.gateway.filters.OutgoingFilterCabalAr" direction="outgoing"/>
<filter class="org.jpos.iso.filter.MacroFilter" direction="outgoing">
<property name="valid" value="0 2 11 12 22 23 41 55 " />
</filter>
</channel>
<in>cabal-ar-send</in>
<out>cabal-ar-receive</out>
<reconnect-delay>10000</reconnect-delay>
</channel-adaptor>
```
20_mux_cabal_ar.xml
Este archivo XML define un multiplexor (`mux`) para jPOS, un marco de trabajo de Java para transacciones financieras ISO 8583.
Desglose de las configuraciones:
- `<mux>`: Este es el elemento raíz que define un multiplexor. Un multiplexor en jPOS es una entidad que maneja el enrutamiento de los mensajes ISO 8583 entre varios canales.
- `class="org.jpos.q2.iso.QMUX"`: Esta es la clase Java que implementa el multiplexor. En este caso, se utiliza la clase `QMUX` proporcionada por jPOS.
- `logger="Q2"`: Este es el logger que se utilizará para registrar los eventos relacionados con este multiplexor.
- `name="mux-cabal-ar"`: Este es el nombre del multiplexor. Se utiliza para identificar el multiplexor en la configuración de jPOS.
- `<in>`: Este elemento define la cola de mensajes entrantes para este multiplexor. En este caso, los mensajes que se reciben a través de este multiplexor se colocarán en la cola `cabal-ar-receive`.
- `<out>`: Este elemento define la cola de mensajes salientes para este multiplexor. En este caso, los mensajes que se envían a través de este multiplexor se colocarán en la cola `cabal-ar-send`.
- `<ready>`: Este elemento define la cola de mensajes que indica que el canal asociado está listo para recibir mensajes. En este caso, la cola es `cabal-ar-channel.ready`.
**En resumen, este archivo XML configura un multiplexor para manejar el enrutamiento de los mensajes ISO 8583 entre varios canales. Los mensajes entrantes se colocarán en una cola específica, los mensajes salientes se tomarán de otra cola y habrá una cola separada para indicar cuando el canal asociado está listo para recibir mensajes.**
- Archivo
```xml
<?xml version="1.0" ?>
<mux class="org.jpos.q2.iso.QMUX" logger="Q2" name="mux-cabal-ar">
<in>cabal-ar-receive</in>
<out>cabal-ar-send</out>
<ready>cabal-ar-channel.ready</ready>
</mux>
```
30_txnmgr.xml
El archivo `30_txnmgr.xml` es una configuración de jPOS que define un administrador de transacciones (TransactionManager).
Este TransactionManager se utiliza para el flujo 1
Desglose de las configuraciones:
- `<txnmgr>`: Este es el elemento raíz que define un gestor de transacciones (Transaction Manager) en jPOS.
- `class="org.jpos.transaction.TransactionManager"`: Esta es la clase Java que implementa el gestor de transacciones.
- `logger="Q2"`: Este es el logger que se utilizará para registrar los eventos relacionados con este gestor de transacciones.
- `name="txn-nac"`: Este es el nombre del gestor de transacciones.
- `realm="txn-nac"`: Este es el dominio del gestor de transacciones.
- `<property>`: Define propiedades específicas para el gestor de transacciones.
- `name="queue" value="TXNMGR"`: Define la cola de mensajes que utilizará el gestor de transacciones.
- `name="sessions" value="2"`: Define el número de sesiones concurrentes.
- `name="max-sessions" value="128"`: Define el número máximo de sesiones.
- `name="debug" value="true"`: Habilita el modo de depuración.
- `<participant>`: Define los participantes en la transacción.
- `class="com.app.gateway.participants.EntryModeFilter"`: Participante que filtra el modo de entrada.
- `class="com.app.gateway.participants.EntryModeSelector"`: Participante que selecciona el modo de entrada.
- `class="com.app.gateway.participants.PrepareForCeibo"`: Participante que prepara la transacción para Ceibo.
- `class="com.app.gateway.participants.CeiboAuthorization"`: Participante que maneja la autorización de Ceibo.
- `class="org.jpos.transaction.participant.SendResponse"`: Participante que envía la respuesta.
- `<group>`: Define un grupo de participantes que procesan transacciones específicas.
- `name="group-contact-contactless"`: Nombre del grupo encargado de procesar transacciones contact o contactless.
- `<participant>`: Participante dentro del grupo.
- `class="com.app.gateway.participants.QueryHostDGW"`: Participante que consulta el host DGW.
- `realm="query-host-dgw"`: Dominio del participante.
- `<property name="response" value="RESPONSE_FROM_CABAL_AR"/>`: Propiedad específica del participante que define la respuesta esperada.
- `<participant>`: Participante dentro del grupo.
- `class="com.app.gateway.participants.CheckResponseCabalAr"`: Participante que verifica la respuesta de Cabal AR.
- `realm="check-response-cabal-ar"`: Dominio del participante.
- Archivo
```xml
<?xml version='1.0'?>
<txnmgr class="org.jpos.transaction.TransactionManager" logger="Q2" name="txn-nac" realm="txn-nac">
<property name="queue" value="TXNMGR"/>
<property name="sessions" value="2"/>
<property name="max-sessions" value="128"/>
<property name="debug" value="true"/>
<participant class="com.app.gateway.participants.EntryModeFilter" logger="Q2" realm="entry-mode-filter"/>
<participant class="com.app.gateway.participants.EntryModeSelector" logger="Q2" realm="entry-mode-selector"/>
<participant class="com.app.gateway.participants.PrepareForCeibo" logger="Q2" realm="prepare-for-ceibo"/>
<participant class="com.app.gateway.participants.CeiboAuthorization" logger="Q2" realm="ceibo-authorization"/>
<participant class="org.jpos.transaction.participant.SendResponse" logger="Q2" realm="send-response"/>
<!-- Grupo encargado de procesar transacciones contact o contactless -->
<group name="group-contact-contactless">
<participant class="com.app.gateway.participants.QueryHostDGW" logger="Q2" realm="query-host-dgw">
<property name="response" value="RESPONSE_FROM_CABAL_AR"/>
</participant>
<participant class="com.app.gateway.participants.CheckResponseCabalAr" logger="Q2" realm="check-response-cabal-ar"/>
</group>
</txnmgr>
```
El archivo `30_txnmgr.xml` define un administrador de transacciones que maneja una cola de transacciones llamada "TXNMGR", con un número inicial de 2 sesiones y un máximo de 128 sesiones. Las transacciones son procesadas por varios participantes y un grupo de participantes.
<aside>
📌 Flujo del Transaction Manager:
1. **Recibir la transacción**: El `TransactionManager` recibe una transacción de la cola de transacciones "TXNMGR".
2. **Iniciar la transacción**: El `TransactionManager` inicia la transacción creando un nuevo contexto de transacción.
3. **Procesar la transacción**: El `TransactionManager` invoca a cada uno de los participantes en el orden en que aparecen en el archivo de configuración.
- **EntryModeFilter**: Filtra el modo de entrada.
- **EntryModeSelector**: Selecciona el modo de entrada.
- **PrepareForCeibo**: Prepara la transacción para Ceibo.
- **CeiboAuthorization**: Maneja la autorización de Ceibo.
- **SendResponse**: Envía la respuesta al cliente.
- **Grupo `group-contact-contactless`**: Procesa transacciones contact o contactless.
- **QueryHostDGW**: Consulta el host DGW.
- **CheckResponseCabalAr**: Verifica la respuesta de Cabal AR.
4. **Preparar la transacción**: Verifica si alguno de los participantes ha marcado la transacción para ser deshecha.
5. **Confirmar o deshacer la transacción**: Si la transacción ha sido marcada para ser deshecha, se invoca el método `abort` de cada participante en orden inverso. Si no, se invoca el método `commit`.
6. **Finalizar la transacción**: Limpia cualquier recurso utilizado por la transacción.
</aside>
30_txnmgr_dci.xml
El archivo `30_txnmgr_dci.xml` es una configuración de jPOS que define un administrador de transacciones (TransactionManager).
Este TransactionManager se utiliza para los flujos 2 y 3
Desglose de las configuraciones:
- `<txnmgr>`: Este es el elemento raíz que define un gestor de transacciones (Transaction Manager) en jPOS.
- `class="org.jpos.transaction.TransactionManager"`: Esta es la clase Java que implementa el gestor de transacciones.
- `logger="Q2"`: Este es el logger que se utilizará para registrar los eventos relacionados con este gestor de transacciones.
- `name="txn-dci"`: Este es el nombre del gestor de transacciones.
- `realm="txn-dci"`: Este es el dominio del gestor de transacciones.
- `<property>`: Define propiedades específicas para el gestor de transacciones.
- `name="queue" value="TXNMGR-DCI"`: Define la cola de mensajes que utilizará el gestor de transacciones.
- `name="sessions" value="2"`: Define el número de sesiones concurrentes.
- `name="max-sessions" value="128"`: Define el número máximo de sesiones.
- `name="debug" value="true"`: Habilita el modo de depuración.
- `<participant>`: Define los participantes en la transacción.
- `class="com.app.gateway.participants.CeiboAuthorizationDCI"`: Participante que maneja la autorización de Ceibo DCI.
- `<property name="target" value="@target.name@"/>`: Propiedad específica del participante que define el objetivo.
- `class="org.jpos.transaction.participant.SendResponse"`: Participante que envía la respuesta.
- Archivo
```xml
<?xml version='1.0'?>
<txnmgr class="org.jpos.transaction.TransactionManager" logger="Q2" name="txn-dci" realm="txn-dci">
<property name="queue" value="TXNMGR-DCI"/>
<property name="sessions" value="2"/>
<property name="max-sessions" value="128"/>
<property name="debug" value="true"/>
<participant class="com.app.gateway.participants.CeiboAuthorizationDCI" logger="Q2" realm="ceibo-authorization-dci">
<property name="target" value="@target.name@"/>
</participant>
<participant class="org.jpos.transaction.participant.SendResponse" logger="Q2" realm="send-response"/>
</txnmgr>
```
El archivo `30_txnmgr_dci.xml` define un administrador de transacciones que maneja una cola de transacciones llamada "TXNMGR-DCI", con un número inicial de 2 sesiones y un máximo de 128 sesiones. Las transacciones son procesadas por dos participantes.
<aside>
📌 Flujo del Transaction Manager:
1. **Recibir la transacción**: El `TransactionManager` recibe una transacción de la cola de transacciones "TXNMGR-DCI".
2. **Iniciar la transacción**: El `TransactionManager` inicia la transacción creando un nuevo contexto de transacción.
3. **Procesar la transacción**: El `TransactionManager` invoca a cada uno de los participantes en el orden en que aparecen en el archivo de configuración.
- **CeiboAuthorizationDCI**: Maneja la autorización de Ceibo DCI.
- **SendResponse**: Envía la respuesta al cliente.
4. **Preparar la transacción**: Verifica si alguno de los participantes ha marcado la transacción para ser deshecha.
5. **Confirmar o deshacer la transacción**: Si la transacción ha sido marcada para ser deshecha, se invoca el método `abort` de cada participante en orden inverso. Si no, se invoca el método `commit`.
6. **Finalizar la transacción**: Limpia cualquier recurso utilizado por la transacción.
</aside>
50_srv_asc_93.xml
**El archivo `50_srv_asc_93.xml` es una configuración de jPOS que define un servidor ISO 8583 (en formato ascii) .**
Este server es utilizado para el flujo 1.
Desglose de las configuraciones:
- `<server class="org.jpos.q2.iso.QServer" logger="Q2" name="ascii-93-server" realm="ascii-93-server">`: Esta línea define un servidor jPOS con el nombre "ascii-93-server". La clase org.jpos.q2.iso.QServer es la clase de jPOS que implementa la funcionalidad del servidor.
- `<attr name="port" type="java.lang.Integer">7002</attr>`: Esta línea define el puerto en el que el servidor escucha las conexiones entrantes. En este caso, el puerto es 7002.
- `<channel class="org.jpos.iso.channel.ASCIIChannel" packager="org.jpos.iso.packager.GenericPackager" type="server" logger="Q2">`: Esta línea define un canal para el servidor. Un canal es una conexión entre el servidor y un cliente. En este caso, el canal utiliza la clase ASCIIChannel de jPOS, que es un canal que utiliza la codificación ASCII para los mensajes ISO 8583.
- `<property name="packager-config" value="cfg/iso93ascii.xml"/>`: Esta línea define la configuración del empaquetador para el canal. El empaquetador es responsable de convertir los mensajes ISO 8583 entre una representación binaria (o de cadena) y una representación de objeto (ISOMsg). En este caso, la configuración del empaquetador se encuentra en el archivo iso93ascii.xml.
- `<request-listener class="org.jpos.iso.IncomingListener" logger="Q2" realm="incoming-request-listener">`: Esta línea define un oyente de solicitudes para el servidor. Un oyente de solicitudes es un componente que se encarga de manejar las solicitudes entrantes al servidor. En este caso, el oyente de solicitudes utiliza la clase IncomingListener de jPOS.
- `<property name="queue" value="TXNMGR" />`: Esta línea define la cola a la que el oyente de solicitudes envía las solicitudes entrantes. En este caso, la cola es "TXNMGR".
- `<property name="ctx.DESTINATION" value="mux-cabal-ar" />`: Esta línea define el destino de las solicitudes entrantes. En este caso, el destino es "mux-cabal-ar".
**En resumen, el archivo 50_srv_asc_nac_93.xml define un servidor jPOS que escucha en el puerto 7002, utiliza la codificación ASCII para los mensajes ISO 8583, y envía las solicitudes entrantes a la cola "TXNMGR" para su procesamiento.**
- Archivo
```xml
<server class="org.jpos.q2.iso.QServer" logger="Q2" name="ascii-93-server" realm="ascii-93-server">
<attr name="port" type="java.lang.Integer">7002</attr>
<channel class="org.jpos.iso.channel.ASCIIChannel"
packager="org.jpos.iso.packager.GenericPackager"
type="server"
logger="Q2">
<property name="packager-config" value="cfg/iso93ascii.xml"/>
<property name="timeout" value="180000"/>
<property name="debug" value="true" />
</channel>
<request-listener class="org.jpos.iso.IncomingListener" logger="Q2" realm="incoming-request-listener">
<property name="queue" value="TXNMGR" />
<property name="ctx.DESTINATION" value="mux-cabal-ar" />
</request-listener>
</server>
```
50_srv_asc_dci_87.xml
**El archivo `50_srv_asc_dci_87.xml` es una configuración de jPOS que define un servidor ISO 8583 (en formato ASCII).**
Este server es utilizado para los flujo 1.
Desglose de las configuraciones:
- `<server class="org.jpos.q2.iso.QServer" logger="Q2" name="ascii-87-server-dci" realm="ascii-87-server-dci">`: Esta línea define un servidor jPOS con el nombre "ascii-87-server-dci". La clase `org.jpos.q2.iso.QServer` es la clase de jPOS que implementa la funcionalidad del servidor.
- `<attr name="port" type="java.lang.Integer">7004</attr>`: Esta línea define el puerto en el que el servidor escucha las conexiones entrantes. En este caso, el puerto es 7004.
- `<channel class="org.jpos.iso.channel.ASCIIChannel" packager="org.jpos.iso.packager.GenericPackager" type="server" logger="Q2">`: Esta línea define un canal para el servidor. Un canal es una conexión entre el servidor y un cliente. En este caso, el canal utiliza la clase `ASCIIChannel` de jPOS, que es un canal que utiliza la codificación ASCII para los mensajes ISO 8583.
- `<property name="packager-config" value="cfg/iso87ascii.xml"/>`: Esta línea define la configuración del empaquetador para el canal. El empaquetador es responsable de convertir los mensajes ISO 8583 entre una representación binaria (o de cadena) y una representación de objeto (`ISOMsg`). En este caso, la configuración del empaquetador se encuentra en el archivo `iso87ascii.xml`.
- `<property name="timeout" value="180000"/>`: Esta línea define el tiempo de espera (en milisegundos) para las operaciones de red. En este caso, el tiempo de espera es de 180000 milisegundos (3 minutos).
- `<property name="debug" value="true"/>`: Esta línea habilita el modo de depuración.
- `<request-listener class="org.jpos.iso.IncomingListener" logger="Q2" realm="incoming-request-listener">`: Esta línea define un oyente de solicitudes para el servidor. Un oyente de solicitudes es un componente que se encarga de manejar las solicitudes entrantes al servidor. En este caso, el oyente de solicitudes utiliza la clase `IncomingListener` de jPOS.
- `<property name="queue" value="TXNMGR-DCI"/>`: Esta línea define la cola a la que el oyente de solicitudes envía las solicitudes entrantes. En este caso, la cola es `TXNMGR-DCI`.
**En resumen, el archivo `50_srv_asc_dci_87.xml` define un servidor jPOS que escucha en el puerto 7004, utiliza la codificación ASCII para los mensajes ISO 8583, y envía las solicitudes entrantes a la cola `TXNMGR-DCI` para su procesamiento.**
- Archivo
```xml
<server class="org.jpos.q2.iso.QServer" logger="Q2" name="ascii-87-server-dci" realm="ascii-87-server-dci">
<attr name="port" type="java.lang.Integer">7004</attr>
<channel class="org.jpos.iso.channel.ASCIIChannel"
packager="org.jpos.iso.packager.GenericPackager"
type="server"
logger="Q2">
<property name="packager-config" value="cfg/iso87ascii.xml"/>
<property name="timeout" value="180000"/>
<property name="debug" value="true" />
</channel>
<request-listener class="org.jpos.iso.IncomingListener" logger="Q2" realm="incoming-request-listener">
<property name="queue" value="TXNMGR-DCI" />
</request-listener>
</server>
```
99_sysmon.xml
**El archivo `99_sysmon.xml` es una configuración del monitor del sistema (SysMon) para el framework jPOS. SysMon es un servicio que proporciona información sobre el estado del sistema.**
Aquí está el desglose de las configuraciones:
1. `<sysmon logger="Q2">`:
Esta línea define un monitor del sistema. El logger "Q2" se utiliza para manejar la salida de log.
2. `<attr name="sleepTime" type="java.lang.Long">3600000</attr>`:
Esta línea establece el tiempo de sueño del monitor del sistema. En este caso, el tiempo de sueño es de 3600000 milisegundos, o una hora. Esto significa que el monitor del sistema se despertará y comprobará el estado del sistema cada hora.
3. `<attr name="detailRequired" type="java.lang.Boolean">true</attr>`:
Esta línea indica si se requiere un detalle en los logs. En este caso, el detalle está habilitado.
4. `<property name="metrics-dir" value="log" />`:
Esta línea establece el directorio donde se almacenarán las métricas del sistema. En este caso, las métricas se almacenarán en el directorio "log".
**En resumen, este archivo configura un monitor del sistema que se despierta cada hora y proporciona detalles en los logs. Las métricas del sistema se almacenan en el directorio "log".**
- Archivo
```xml
<sysmon logger="Q2">
<!-- Environment: '@target@' -->
<attr name="sleepTime" type="java.lang.Long">3600000</attr>
<attr name="detailRequired" type="java.lang.Boolean">true</attr>
<property name="metrics-dir" value="log" />
</sysmon>
```
Archivos java (FILTERS)
En la ruta src/main/java/com/app/gateway/filters vamos a encontrar los filtros
IncomingFilterCabalAr :
**La clase `IncomingFilterCabalAr` es un filtro ISO 8583 en jPOS. Esta clase es responsable de procesar mensajes entrantes de Cabal AR.**
Detalle de este filtro:
- `setConfiguration(Configuration cfg)`: Este método se utiliza para configurar el filtro. En este caso, no se realiza ninguna configuración específica.
- `filter(ISOChannel channel, ISOMsg message, LogEvent evt)`: Este es el método principal que se ejecuta durante el filtrado del mensaje ISO 8583. En este método, se realiza lo siguiente:
- Se obtiene una instancia de `Space`.
- Se agrega un mensaje de log indicando que se ha recibido un mensaje entrante.
- Se obtienen y formatean los campos 11 y 41 del mensaje.
- Se crea una clave basada en los valores de los campos 11 y 41.
- Se busca un mensaje en `Space` utilizando la clave generada.
- Se establece el MTI de respuesta en el mensaje encontrado.
- Si el mensaje original tiene el campo 39 con el valor "081" y el campo 55, se copia el campo 55 al mensaje de respuesta.
- Se copia el campo 39 del mensaje original al mensaje de respuesta.
- `get41(ISOMsg message)`: Este método privado se utiliza para obtener y formatear el campo 41 del mensaje.
**En resumen, la clase `IncomingFilterCabalAr` es un filtro ISO 8583 que procesa mensajes entrantes de Cabal AR. Busca un mensaje en `Space` basado en ciertos campos del mensaje entrante, establece el MTI de respuesta y ajusta algunos campos antes de devolver el mensaje filtrado.**
- Archivo
```java
package com.app.gateway.filters;
import org.jpos.core.Configurable;
import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.*;
import org.jpos.space.Space;
import org.jpos.space.SpaceFactory;
import org.jpos.tlv.TLVList;
import org.jpos.transaction.Context;
import org.jpos.util.LogEvent;
public class IncomingFilterCabalAr implements ISOFilter, Configurable {
@Override
public void setConfiguration(Configuration cfg) throws ConfigurationException {
}
@Override
public ISOMsg filter(ISOChannel channel, ISOMsg message, LogEvent evt) throws VetoException {
final Space sp = SpaceFactory.getSpace();
evt.addMessage("-------------- py_incoming_from_cabal_ar, incoming message --------------");
evt.addMessage((Object)message);
ISOMsg r=null;
try{
final String firstValue = message.hasField(11) ? ISOUtil.zeropad(message.getString(11).trim(), 12) : "";
final String secondValue = message.hasField(41) ? this.get41(message) : "";
final String key = "msg_py" + firstValue + "-" + secondValue;
r = (ISOMsg)sp.inp((Object)key);
r.setResponseMTI();
if(message.hasField(39)){
if(message.getString(39).equals("081")){
if (message.hasField(55)) {
r.set(55,message.getString(55));
}
}
r.set(39,message.getString(39));
}
} catch (ISOException e) {
throw new RuntimeException(e);
}
return r;
}
private String get41(final ISOMsg message) throws ISOException {
String resultado = ISOUtil.strpad(message.getString(41).trim(), 8);
if (resultado.length() > 8) {
resultado = resultado.substring(resultado.length() - 8, resultado.length());
}
return ISOUtil.padright(resultado, 8, ' ');
}
}
```
IncomingFilterPy:
La clase `IncomingFilterPy` es un filtro ISO 8583 en jPOS. Esta clase es responsable de procesar mensajes entrantes de Py.
Detalle de este filtro:
- `setConfiguration(Configuration cfg)`: Este método se utiliza para configurar el filtro. En este caso, no se realiza ninguna configuración específica.
- `filter(ISOChannel channel, ISOMsg message, LogEvent evt)`: Este es el método principal que se ejecuta durante el filtrado del mensaje ISO 8583. En este método, se realiza lo siguiente:
- Se clona el mensaje entrante.
- Se verifica si el MTI del mensaje es igual a `ECHO_MTI`. Si es así, se devuelve el mensaje sin modificaciones.
- Si el mensaje tiene el campo 55, se convierte el contenido del campo 55 a una cadena hexadecimal.
- Se agrega un mensaje de log indicando que se ha recibido un mensaje entrante.
- Se agrega el mensaje clonado al log.
**En resumen, la clase `IncomingFilterPy` es un filtro ISO 8583 que procesa mensajes entrantes de Py. Clona el mensaje entrante, verifica y convierte el campo 55 a una cadena hexadecimal si está presente, y registra el mensaje antes de devolverlo.**
- Archivo
```java
package com.app.gateway.filters;
import org.jpos.core.Configurable;
import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.*;
import org.jpos.space.Space;
import org.jpos.space.SpaceFactory;
import org.jpos.util.LogEvent;
import static com.app.gateway.Constants.ECHO_MTI;
import static com.app.gateway.Constants.MESSAGE_INCOMING_BANCARD;
public class IncomingFilterPy implements ISOFilter, Configurable {
@Override
public void setConfiguration(Configuration cfg) throws ConfigurationException {
}
@Override
public ISOMsg filter(ISOChannel channel, ISOMsg message, LogEvent evt) throws VetoException {
ISOMsg m = (ISOMsg) message.clone();
try {
if(m.getMTI().equals(ECHO_MTI)){
return m;
}
if (m.hasField(55)) {
byte[] f55 = m.getBytes(55);
m.set(55, ISOUtil.hexString(f55));
}
} catch (ISOException e) {
throw new RuntimeException(e);
}
evt.addMessage("-------------- py_incoming_filter, incoming message --------------");
evt.addMessage(m);
return m;
}
}
```
OutgoingFilterCabalAr:
**La clase `OutgoingFilterCabalAr` es un filtro ISO 8583 en jPOS. Esta clase es responsable de procesar mensajes salientes hacia Cabal AR.**
Detalle de este filtro:
- `setConfiguration(Configuration cfg)`: Este método se utiliza para configurar el filtro. En este caso, no se realiza ninguna configuración específica.
- `filter(ISOChannel channel, ISOMsg message, LogEvent evt)`: Este es el método principal que se ejecuta durante el filtrado del mensaje ISO 8583. En este método, se realiza lo siguiente:
- Se clona el mensaje entrante.
- Se agrega un mensaje de log indicando que se ha recibido un mensaje sin filtrar.
- Se obtienen y formatean los campos 11 y 41 del mensaje.
- Se crea una clave basada en los valores de los campos 11 y 41 y se almacena el mensaje en `Space` utilizando esta clave.
- Se agrega un mensaje de log indicando la clave del mensaje almacenado.
- Se ajusta el campo 22 del mensaje.
- Se agrega un mensaje de log indicando que se ha recibido un mensaje saliente.
- Se devuelve el mensaje modificado.
- `get41(ISOMsg message)`: Este método privado se utiliza para obtener y formatear el campo 41 del mensaje.
**En resumen, la clase `OutgoingFilterCabalAr` es un filtro ISO 8583 que procesa mensajes salientes hacia Cabal AR. Clona el mensaje entrante, ajusta ciertos campos, almacena el mensaje en `Space` y registra el mensaje antes de devolverlo.**
- Archivo
```java
package com.app.gateway.filters;
import org.jpos.core.Configurable;
import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.*;
import org.jpos.space.Space;
import org.jpos.space.SpaceFactory;
import org.jpos.util.LogEvent;
public class OutgoingFilterCabalAr implements ISOFilter, Configurable {
@Override
public void setConfiguration(Configuration cfg) throws ConfigurationException {
}
@Override
public ISOMsg filter(ISOChannel channel, ISOMsg message, LogEvent evt) throws VetoException {
final Space sp = SpaceFactory.getSpace();
ISOMsg m = (ISOMsg) message.clone();
evt.addMessage((Object)"--- py_outgoing_to_cabal_ar, unfiltered message ---");
evt.addMessage((Object)message);
try {
final String firstValue = message.hasField(11) ? ISOUtil.zeropad(message.getString(11).trim(), 12) : "";
final String secondValue = message.hasField(41) ? this.get41(message) : "";
final String key = "msg_py" + firstValue + "-" + secondValue;
sp.out((Object) key, (Object) m, 300000L);
evt.addMessage((Object) ("message key out: " + key));
String entryMode= String.valueOf(message.getString(22).charAt(6));
m.set(22,ISOUtil.zeropad(entryMode,12));
} catch (ISOException e) {
throw new RuntimeException(e);
}
evt.addMessage((Object)"--- py_outgoing_to_cabal_ar, outgoing message ---");
return m;
}
private String get41(final ISOMsg message) throws ISOException {
String resultado = ISOUtil.strpad(message.getString(41).trim(), 8);
if (resultado.length() > 8) {
resultado = resultado.substring(resultado.length() - 8, resultado.length());
}
return ISOUtil.padright(resultado, 8, ' ');
}
}
```
Archivos java (PARTICIPANTS)
En la ruta src/main/java/com/app/gateway/participants vamos a encontrar los participants
CeiboAuthorization:
**La clase `CeiboAuthorization` es un participante en una transacción de jPOS. Esta clase es responsable de autorizar la transacción a través de la llamada a un procedimiento almacenado `PR_CABAL_DISCOVER`. Este participant es el validador del flujo 1**
**Detalle de este participante:**
- `setConfiguration(Configuration cfg)`: Este método se utiliza para configurar el participante. En este caso, se configura una propiedad llamada "target".
- `getDB(Context ctx)`: Este método obtiene una instancia de la base de datos desde el contexto. Si no existe, crea una nueva instancia de `DB` utilizando un `ExecutorService` para manejar la conexión de manera asíncrona.
- `doPrepare(long id, Context ctx)`: Este es el método principal que se ejecuta durante la fase de preparación de la transacción. En este método, se realiza lo siguiente:
- Se clona el mensaje ISO de la transacción.
- Se crean los parámetros para el procedimiento almacenado `PR_CABAL_DISCOVER`.
- Se abre una conexión a la base de datos y se ejecuta el procedimiento almacenado con los parámetros creados.
- Dependiendo del resultado del procedimiento almacenado, se decide si la transacción debe ser rechazada o no. Si el procedimiento almacenado devuelve ciertos valores, se ajustan los campos del mensaje de respuesta y se determina el flujo de la transacción.
- `prepareParamsToCeiboSP(ISOMsg[] isoMsgsArrayToSPCeibo, CallableStatement sp)`: Este método prepara los parámetros para el procedimiento almacenado `PR_CABAL_DISCOVER`.
- `createParamsToCeibo(Context ctx, ISOMsg messageIncomingBancardToCeibo)`: Este método crea los parámetros de entrada para el procedimiento almacenado `PR_CABAL_DISCOVER` basándose en el contexto y el mensaje entrante.
- `handleException(Context ctx, ISOMsg messageIncomingBancard)`: Este método maneja las excepciones que ocurren durante la preparación de la transacción, ajustando los mensajes de respuesta en el contexto.
**En resumen, la clase `CeiboAuthorization` es un participante en una transacción jPOS que interactúa con una base de datos. Durante la fase de preparación de la transacción, envía un mensaje ISO8583 a un procedimiento almacenado, obtiene una respuesta y determina el flujo de la transacción basado en la respuesta.**
- Archivo
```java
package com.app.gateway.participants;
import org.hibernate.Session;
import org.hibernate.internal.SessionImpl;
import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.ISOMsg;
import org.jpos.transaction.AbortParticipant;
import org.jpos.transaction.Context;
import org.jpos.transaction.TxnSupport;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.Types;
import java.util.concurrent.*;
import org.jpos.ee.DB;
import static com.app.gateway.Constants.*;
import static com.app.gateway.utils.Utilities.*;
/**
* This participant is responsible for authorizing the transaction through the so-called
* to a stored procedure called PR_CABAL_DISCOVER.
*/
public class CeiboAuthorization extends TxnSupport implements AbortParticipant {
private String target;
@Override
public void setConfiguration(Configuration cfg) throws ConfigurationException {
super.setConfiguration(cfg);
this.target = cfg.get("target");
}
@Override
public synchronized DB getDB(Context ctx) {
DB db = (DB) ctx.get(DB.class);
if (db == null) {
ExecutorService executor = Executors.newSingleThreadExecutor();
Callable<DB> task = DB::new;
Future<DB> future = executor.submit(task);
try {
db = future.get(20, TimeUnit.SECONDS);
ctx.put(DB.class, db);
} catch (TimeoutException e) {
future.cancel(true);
error("Timeout: No se pudo conectar a la base de datos en 20 segundos");
throw new RuntimeException();
} catch (InterruptedException | ExecutionException e) {
error("Error al crear la instancia de DB");
throw new RuntimeException();
} finally {
executor.shutdown();
}
}
return db;
}
@Override
protected int doPrepare(long id, Context ctx) throws Exception {
ISOMsg messageIncomingBancard = ctx.get("REQUEST");
ISOMsg messageIncomingBancardToCeibo= (ISOMsg) messageIncomingBancard.clone();
ISOMsg[] isoMsgsArrayToSPCeibo = createParamsToCeibo(ctx,messageIncomingBancardToCeibo);
try {
DB db = getDB(ctx);
try (Session session = db.open(); Connection con = ((SessionImpl) session).connection();
CallableStatement sp = con.prepareCall("{call PR_CABAL_DISCOVER(?, ?, ?,?,?)}")) {
sp.setQueryTimeout(30);
prepareParamsToCeiboSP(isoMsgsArrayToSPCeibo,sp);
sp.registerOutParameter(4, Types.VARCHAR);
sp.registerOutParameter(5, Types.NUMERIC);
sp.executeUpdate();
String respuestaISO = sp.getString(4);
ISOMsg isoMsgResponse = convertStringToISOMsg(respuestaISO, "ISO93ASCII");
if(!ctx.hasKey(TRANSACTION_TYPE)){
info("Respuesta desde sp ceibo - Transaccion no determinada\n"+respuestaISO);
ctx.put("RESPONSE", isoMsgResponse);
return ABORTED;
}
else {
String typeTransaction=ctx.get(TRANSACTION_TYPE);
if (typeTransaction.equals(BANDA)) {
info("Respuesta desde sp ceibo - Transaccion banda\n" + respuestaISO);
ctx.put("RESPONSE", isoMsgResponse);
if (!ctx.hasKey(RESPONSE_INTERNAL_DISCOVER_ERROR)) {
String responseCodeRespuestaSP = isoMsgResponse.getString(39);
if (responseCodeRespuestaSP.equals("081")) {
return PREPARED;
} else {
ctx.put(REJECTION_REASON, NOT_AUTHORIZED_BY_CEIBO);
return ABORTED;
}
}
} else {
info("Respuesta desde sp ceibo - Transaccion chip\n" + respuestaISO);
if (ctx.hasKey(RESPONSE_FROM_CABAL_AR)) {
ISOMsg responseFromCabalArgentina = ctx.get(RESPONSE_FROM_CABAL_AR);
boolean isResponseFromCabalArgentina081 = responseFromCabalArgentina.getString(39).equals("081");
if (isResponseFromCabalArgentina081 && !ctx.hasKey(RESPONSE_INTERNAL_DISCOVER_ERROR)) {
String responseCodeRespuestaSP = isoMsgResponse.getString(39);
if (responseCodeRespuestaSP.equals("081")) {
isoMsgResponse.set(55, responseFromCabalArgentina.getString(55));
ctx.put("RESPONSE", isoMsgResponse);
return PREPARED;
} else {
isoMsgResponse.set(55, messageIncomingBancard.getString(55));
ctx.put("RESPONSE", isoMsgResponse);
ctx.put(REJECTION_REASON, NOT_AUTHORIZED_BY_CEIBO);
return ABORTED;
}
} else if (!isResponseFromCabalArgentina081 || ctx.hasKey(RESPONSE_INTERNAL_DISCOVER_ERROR)) {
isoMsgResponse.set(55, messageIncomingBancard.getString(55));
ctx.put("RESPONSE", isoMsgResponse);
return ABORTED;
}
} else {
isoMsgResponse.set(55, messageIncomingBancard.getString(55));
ctx.put("RESPONSE", isoMsgResponse);
return ABORTED;
}
}
}
} catch (Exception e) {
error("Error during stored procedure call");
handleException(ctx, messageIncomingBancard);
return ABORTED;
}
} catch (Exception e) {
error("Error during connection to db");
handleException(ctx, messageIncomingBancard);
return ABORTED;
}
return PREPARED;
}
private void prepareParamsToCeiboSP(ISOMsg[] isoMsgsArrayToSPCeibo, CallableStatement sp) throws Exception {
for (int i = 0; i < isoMsgsArrayToSPCeibo.length; i++) {
if (isoMsgsArrayToSPCeibo[i] != null) {
String isoMsgString = convertISOMsgToString(isoMsgsArrayToSPCeibo[i]);
if (!target.equals("production")) {
if (i == 0) {
info("The ISO message received by DiscoverGW: " + isoMsgString);
} else if (i == 1) {
info("The ISO message response by Cabal AR: " + isoMsgString);
} else {
info("The ISO message response to terminal by Discover GW error: " + isoMsgString);
}
}
sp.setString(i + 1, isoMsgString);
} else {
sp.setString(i + 1, null);
}
}
}
private ISOMsg[] createParamsToCeibo(Context ctx, ISOMsg messageIncomingBancardToCeibo) throws Exception {
ISOMsg[] isoMsgsArrayToSPCeibo = new ISOMsg[3];
if(!ctx.hasKey(TRANSACTION_TYPE)) {
info("Transaccion no determinada - creando parametros de entrada para ceibo sp");
if(messageIncomingBancardToCeibo.hasField(55)){
messageIncomingBancardToCeibo.unset(55);
}
isoMsgsArrayToSPCeibo[0] = messageIncomingBancardToCeibo;
if (ctx.hasKey(RESPONSE_INTERNAL_DISCOVER_ERROR)) {
ISOMsg responseToTerminalDiscoverError = ctx.get(RESPONSE_INTERNAL_DISCOVER_ERROR);
ISOMsg responseToTerminalDiscoverErrorToCeibo = (ISOMsg) responseToTerminalDiscoverError.clone();
responseToTerminalDiscoverErrorToCeibo.unset(55);
isoMsgsArrayToSPCeibo[2] = responseToTerminalDiscoverErrorToCeibo;
}
}else {
String typeTransaction=ctx.get(TRANSACTION_TYPE);
if (typeTransaction.equals(BANDA)) {
info("Transaccion banda - creando parametros de entrada para ceibo sp");
if(messageIncomingBancardToCeibo.hasField(55)){
messageIncomingBancardToCeibo.unset(55);
}
isoMsgsArrayToSPCeibo[0] = messageIncomingBancardToCeibo;
if (ctx.hasKey(RESPONSE_INTERNAL_DISCOVER_ERROR)) {
ISOMsg responseToTerminalDiscoverError = ctx.get(RESPONSE_INTERNAL_DISCOVER_ERROR);
ISOMsg responseToTerminalDiscoverErrorToCeibo = (ISOMsg) responseToTerminalDiscoverError.clone();
responseToTerminalDiscoverErrorToCeibo.unset(55);
isoMsgsArrayToSPCeibo[2] = responseToTerminalDiscoverErrorToCeibo;
}
} else {
info("Transaccion chip - creando parametros de entrada para ceibo sp");
messageIncomingBancardToCeibo.unset(55);
isoMsgsArrayToSPCeibo[0] = messageIncomingBancardToCeibo;
if (ctx.hasKey(RESPONSE_FROM_CABAL_AR)) {
ISOMsg responseFromCabalArgentina = ctx.get(RESPONSE_FROM_CABAL_AR);
ISOMsg responseFromCabalArgentinaToCeibo = (ISOMsg) responseFromCabalArgentina.clone();
responseFromCabalArgentinaToCeibo.unset(55);
isoMsgsArrayToSPCeibo[1] = responseFromCabalArgentinaToCeibo;
}
if (ctx.hasKey(RESPONSE_INTERNAL_DISCOVER_ERROR)) {
ISOMsg responseToTerminalDiscoverError = ctx.get(RESPONSE_INTERNAL_DISCOVER_ERROR);
ISOMsg responseToTerminalDiscoverErrorToCeibo = (ISOMsg) responseToTerminalDiscoverError.clone();
responseToTerminalDiscoverErrorToCeibo.unset(55);
isoMsgsArrayToSPCeibo[2] = responseToTerminalDiscoverErrorToCeibo;
}
}
}
return isoMsgsArrayToSPCeibo;
}
private void handleException(Context ctx, ISOMsg messageIncomingBancard){
try {
error("...... Devolviendo respuesta con codigos de respuesta internos ......");
if(!ctx.hasKey(TRANSACTION_TYPE)){
ISOMsg errorDiscoverGw= ctx.get(RESPONSE_INTERNAL_DISCOVER_ERROR);
ctx.put("RESPONSE",errorDiscoverGw);
}
else {
String typeTransaction=ctx.get(TRANSACTION_TYPE);
if (typeTransaction.equals(BANDA)) {
if (!ctx.hasKey(RESPONSE_INTERNAL_DISCOVER_ERROR)) {
ISOMsg responseToTerminalExceptionInSP = (ISOMsg) messageIncomingBancard.clone();
responseToTerminalExceptionInSP.setResponseMTI();
ctx.put(REJECTION_REASON, ERROR_CONNECTION_TO_CEIBO);
String responseCode = mapRejectionReasonToResponseCodeError(ERROR_CONNECTION_TO_CEIBO);
responseToTerminalExceptionInSP.set(39, responseCode);
ctx.put("RESPONSE", responseToTerminalExceptionInSP);
} else {
ISOMsg errorDiscoverGw = ctx.get(RESPONSE_INTERNAL_DISCOVER_ERROR);
ctx.put("RESPONSE", errorDiscoverGw);
}
} else {
if (ctx.hasKey(RESPONSE_FROM_CABAL_AR)) {
ISOMsg responseFromCabalArgentina = ctx.get(RESPONSE_FROM_CABAL_AR);
boolean isResponseFromCabalArgentina081 = responseFromCabalArgentina.getString(39).equals("081");
if (isResponseFromCabalArgentina081) {
ISOMsg responseToTerminalExceptionInSP = (ISOMsg) responseFromCabalArgentina.clone();
ctx.put(REJECTION_REASON, ERROR_CONNECTION_TO_CEIBO);
String responseCode = mapRejectionReasonToResponseCodeError(ERROR_CONNECTION_TO_CEIBO);
responseToTerminalExceptionInSP.set(39, responseCode);
ctx.put("RESPONSE", responseToTerminalExceptionInSP);
} else {
ctx.put("RESPONSE", responseFromCabalArgentina);
}
} else {
ISOMsg errorDiscoverGw = ctx.get(RESPONSE_INTERNAL_DISCOVER_ERROR);
ctx.put("RESPONSE", errorDiscoverGw);
}
}
}
}catch (Exception e){
error(e);
}
}
@Override
protected int doPrepareForAbort(long id, Context ctx) throws Exception {
return doPrepare(id, ctx);
}
}
```
CeiboAuthorizationDCI:
**La clase `CeiboAuthorizationDCI` es un participante en una transacción de jPOS. Esta clase es responsable de autorizar la transacción a través de la llamada a un procedimiento almacenado `PR_DISCOVER_INTERNACIONAL`. Este participant es el validador de los flujos 2 y 3.**
**Detalle de este participante:**
- `setConfiguration(Configuration cfg)`: Este método se utiliza para configurar el participante. En este caso, se configura una propiedad llamada "target".
- `getDB(Context ctx)`: Este método obtiene una instancia de la base de datos desde el contexto. Si no existe, crea una nueva instancia de `DB` utilizando un `ExecutorService` para manejar la conexión de manera asíncrona.
- `prepare(long id, Serializable context)`: Este es el método principal que se ejecuta durante la fase de preparación de la transacción. En este método, se realiza lo siguiente:
- Se clona el mensaje ISO de la transacción y se elimina el campo 55.
- Se abre una conexión a la base de datos y se ejecuta el procedimiento almacenado `PR_DISCOVER_INTERNACIONAL` con los parámetros creados.
- Se obtiene la respuesta del procedimiento almacenado y se convierte a un objeto `ISOMsg`.
- Se ajusta el campo 55 del mensaje de respuesta.
- Se registra el mensaje de respuesta en el contexto y se determina el flujo de la transacción.
**En resumen, la clase `CeiboAuthorizationDCI` es un participante en una transacción jPOS que interactúa con una base de datos. Durante la fase de preparación de la transacción, envía un mensaje ISO8583 a un procedimiento almacenado, obtiene una respuesta y determina el flujo de la transacción basado en la respuesta.**
- Archivo
```java
package com.app.gateway.participants;
import org.hibernate.Session;
import org.hibernate.internal.SessionImpl;
import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.ee.DB;
import org.jpos.iso.ISOMsg;
import org.jpos.transaction.Context;
import org.jpos.transaction.TxnSupport;
import java.io.Serializable;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.Types;
import java.util.concurrent.*;
import static com.app.gateway.utils.Utilities.convertISOMsgToString;
import static com.app.gateway.utils.Utilities.convertStringToISOMsg;
public class CeiboAuthorizationDCI extends TxnSupport implements Serializable {
private String target;
@Override
public void setConfiguration(Configuration cfg) throws ConfigurationException {
super.setConfiguration(cfg);
this.target=cfg.get("target");
}
@Override
public synchronized DB getDB(Context ctx) {
DB db = (DB) ctx.get(DB.class);
if (db == null) {
ExecutorService executor = Executors.newSingleThreadExecutor();
Callable<DB> task = DB::new;
Future<DB> future = executor.submit(task);
try {
db = future.get(20, TimeUnit.SECONDS);
ctx.put(DB.class, db);
} catch (TimeoutException e) {
future.cancel(true);
error("Timeout: No se pudo conectar a la base de datos en 20 segundos");
throw new RuntimeException();
} catch (InterruptedException | ExecutionException e) {
error("Error al crear la instancia de DB");
throw new RuntimeException();
} finally {
executor.shutdown();
}
}
return db;
}
@Override
public int prepare(long id, Serializable context) {
Context ctx = (Context) context;
ISOMsg isoMsg = ctx.get("REQUEST");
// Es un clon del msj iso que viene del contexto. Pero sin el campo 55 para enviarlo al stored procedure
ISOMsg isoMsgSP = (ISOMsg) isoMsg.clone();
isoMsgSP.unset(55);
try {
DB db = getDB(ctx);
try (Session session = db.open(); Connection con = ((SessionImpl) session).connection();
CallableStatement sp = con.prepareCall("{call PR_DISCOVER_INTERNACIONAL(?, ?, ?)}")) {
String isoMsgRequest=convertISOMsgToString(isoMsgSP);
sp.setString(1,isoMsgRequest);
sp.registerOutParameter(2, Types.VARCHAR);
sp.registerOutParameter(3, Types.INTEGER);
info("Excute the Ceibo stored procedure PR_DISCOVER_INTERNACIONAL");
sp.executeUpdate();
int puntoDestino = sp.getInt(3);
String respuestaISO = sp.getString(2);
ISOMsg isoMsgResponse = convertStringToISOMsg(respuestaISO, "ISO87Ascii");
if(!target.equals("production")){
info("The ISO message RECIVED by the Ceibo stored procedure: " + isoMsgRequest);
info("----------------------------------");
info("The ISO message RESPONSE sent by the Ceibo stored procedure: ", respuestaISO);
}
isoMsgResponse.set(55,isoMsg.getString(55));
info("Sending ISO message request to Cabal AR");
ctx.put("RESPONSE",isoMsgResponse);
return PREPARED;
} catch (Exception e) {
error("Error during stored procedure call ");
return ABORTED;
}
} catch (Exception e) {
error("Error during connection to db " );
return ABORTED;
}
}
}
```
CheckResponseCabalAr:
**La clase `CheckResponseCabalAr` es un participante en una transacción de jPOS. Esta clase es responsable de verificar la respuesta de Cabal AR.**
**Detalle de este participante:**
- `setConfiguration(Configuration cfg)`: Este método se utiliza para configurar el participante. En este caso, no se realiza ninguna configuración específica.
- `doPrepare(long id, Context ctx)`: Este es el método principal que se ejecuta durante la fase de preparación de la transacción. En este método, se realiza lo siguiente:
- Se obtiene el mensaje ISO de la respuesta de Cabal AR desde el contexto.
- Se verifica si hay una razón de rechazo en el contexto. Si la razón de rechazo es `CONNECTION_TIMEOUT_TO_CABAL_AR` o `CABAL_AR_NOT_CONNECTED`, la transacción se aborta.
- Se verifica si el mensaje ISO tiene el campo 39 y si su valor no es "081". Si el valor del campo 39 no es "081", se establece la razón de rechazo como `INVALID_CRYPTOGRAM` y se aborta la transacción.
- Si todas las verificaciones pasan, la transacción se prepara.
**En resumen, la clase `CheckResponseCabalAr` es un participante en una transacción jPOS que verifica la respuesta de Cabal AR. Si la respuesta no es válida, la transacción se aborta.**
**Asociación de flujo:**
La clase `CheckResponseCabalAr` está asociada al flujo 1, ya que verifica la respuesta de Cabal AR.
- Archivo
```java
package com.app.gateway.participants;
import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.ISOMsg;
import org.jpos.transaction.Context;
import org.jpos.transaction.TxnSupport;
import java.io.Serializable;
import static com.app.gateway.Constants.*;
import static org.jpos.transaction.TransactionConstants.PREPARED;
public class CheckResponseCabalAr extends TxnSupport {
@Override
public void setConfiguration(Configuration cfg) throws ConfigurationException{
super.setConfiguration(cfg);
}
@Override
protected int doPrepare(long id, Context ctx) {
ISOMsg isoMsg = ctx.get(RESPONSE_FROM_CABAL_AR);
if(ctx.hasKey(REJECTION_REASON)){
String rejectionReason = ctx.get(REJECTION_REASON);
if (CONNECTION_TIMEOUT_TO_CABAL_AR.equals(rejectionReason) || CABAL_AR_NOT_CONNECTED.equals(rejectionReason)) {
return ABORTED;
}
}
// Verificar si el mensaje ISO tiene el campo 39 y si su valor no es "081"
if (isoMsg.hasField(39)) {
String isoField39 = isoMsg.getString(39);
if (isoField39 != null && !"081".equals(isoField39)) {
ctx.put(REJECTION_REASON, INVALID_CRYPTOGRAM);
ctx.put("RESPONSE",isoMsg);
return ABORTED;
}
}
return PREPARED;
}
}
```
EntryModeFilter:
**La clase `EntryModeFilter` es un participante en una transacción de jPOS. Esta clase es responsable de preparar el mensaje para su procesamiento basado en reglas específicas según el modo de entrada.** **Determina el tipo de transacción (CHIP o BANDA)**
**Detalle de este participante:**
- `setConfiguration(Configuration cfg)`: Este método se utiliza para configurar el participante. En este caso, no se realiza ninguna configuración específica.
- `doPrepare(long id, Context ctx)`: Este es el método principal que se ejecuta durante la fase de preparación de la transacción. En este método, se realiza lo siguiente:
- Se obtiene el mensaje ISO desde el contexto.
- Se verifica si el mensaje ISO tiene el campo 22. Si no lo tiene, se establece la razón de rechazo como `INVALID_FORMAT` y se aborta la transacción.
- Se obtiene el sexto carácter del campo 22 para determinar el modo de entrada.
- Se verifica si el modo de entrada es válido (2, 5 o 7). Si no es válido, se establece la razón de rechazo como `INVALID_ENTRY_MODE` y se aborta la transacción.
- Si el modo de entrada es 5 o 7 (tarjeta chip), se verifica que el mensaje ISO tenga los campos obligatorios para tarjetas chip. Si faltan campos, se establece la razón de rechazo como `INVALID_FORMAT` y se aborta la transacción.
- Si el modo de entrada es 2 (tarjeta banda), se establece el tipo de transacción como `BANDA`.
**En resumen, la clase `EntryModeFilter` es un participante en una transacción jPOS que verifica y prepara el mensaje ISO basado en el modo de entrada. Si el modo de entrada o los campos obligatorios no son válidos, la transacción se aborta.**
**Asociación de flujo:**
La clase `EntryModeFilter` está asociada al flujo 1, ya que maneja el modo de entrada de las transacciones.
- Archivo
```java
package com.app.gateway.participants;
import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.ISOMsg;
import org.jpos.space.Space;
import org.jpos.space.SpaceFactory;
import org.jpos.transaction.Context;
import org.jpos.transaction.TxnSupport;
import static com.app.gateway.Constants.*;
public class EntryModeFilter extends TxnSupport {
@Override
public void setConfiguration(Configuration cfg) throws ConfigurationException {
super.setConfiguration(cfg);
}
/**
* Prepara el mensaje para procesamiento basado en reglas específicas según el modo de entrada.
*/
@Override
protected int doPrepare(long id, Context ctx) {
ISOMsg isoMsg = ctx.get("REQUEST");
if (!isoMsg.hasField(22)) {
error("El campo 22 no esta presente en el mensaje");
ctx.put(REJECTION_REASON, INVALID_FORMAT);
return ABORTED;
}
String f22 = isoMsg.getString(22);
char entryMode;
try {
entryMode = f22.charAt(6);
} catch (IndexOutOfBoundsException e) {
error("El campo 22 no presenta el 6to caracter que indica el entry mode");
ctx.put(REJECTION_REASON, INVALID_FORMAT);
return ABORTED;
}
if (entryMode != '2' && entryMode != '5' && entryMode != '7') {
error("Modo de entrada no permitido: " + entryMode);
ctx.put(REJECTION_REASON, INVALID_ENTRY_MODE);
return ABORTED;
}
if (entryMode == '5' || entryMode == '7') {
info("Tarjeta chip");
ctx.put(TRANSACTION_TYPE,CHIP);
// Campos obligatorios para tarjetas chip
int[] mandatoryFields = {2, 11, 12, 22, 23, 41, 55};
if (!isoMsg.hasFields(mandatoryFields)) {
error("Campos obligatorios para tarjetas chip faltantes");
ctx.put(REJECTION_REASON, INVALID_FORMAT);
return ABORTED;
}
} else if (entryMode == '2') {
info("Tarjeta banda");
ctx.put(TRANSACTION_TYPE,BANDA);
// TODO: Agregar campos mandatorios para tarjetas banda
}
return PREPARED;
}
}
```
EntryModeSelector:
**La clase `EntryModeSelector` es un participante en una transacción de jPOS que implementa la interfaz `GroupSelector`. Esta clase es responsable de seleccionar un grupo de transacciones basado en el modo de entrada del mensaje ISO.**
**Detalle de este participante:**
- `setConfiguration(Configuration cfg)`: Este método se utiliza para configurar el participante. En este caso, no se realiza ninguna configuración específica.
- `select(long l, Serializable serializable)`: Este método selecciona un grupo de transacciones basado en el modo de entrada del mensaje ISO. En este método, se realiza lo siguiente:
- Se obtiene el mensaje ISO desde el contexto.
- Se verifica si el contexto no tiene una razón de rechazo.
- Se obtiene el sexto carácter del campo 22 para determinar el modo de entrada.
- Si el modo de entrada es '5' o '7', se selecciona el grupo "group-contact-contactless".
- `prepare(long l, Serializable serializable)`: Este método prepara la transacción y siempre devuelve `PREPARED`.
**En resumen, la clase `EntryModeSelector` es un participante en una transacción jPOS que selecciona un grupo de transacciones basado en el modo de entrada del mensaje ISO. Si el modo de entrada es '5' o '7', se selecciona el grupo "group-contact-contactless".**
**Asociación de flujo:**
La clase `EntryModeSelector` está asociada al flujo 1, ya que su función es seleccionar un grupo de transacciones basado en el modo de entrada.
- Archivo
```java
package com.app.gateway.participants;
import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.ISOMsg;
import org.jpos.space.Space;
import org.jpos.space.SpaceFactory;
import org.jpos.transaction.Context;
import org.jpos.transaction.GroupSelector;
import org.jpos.transaction.TxnSupport;
import java.io.Serializable;
import static com.app.gateway.Constants.*;
public class EntryModeSelector extends TxnSupport implements GroupSelector {
@Override
public void setConfiguration(Configuration cfg) throws ConfigurationException {
super.setConfiguration(cfg);
}
@Override
public String select(long l, Serializable serializable) {
Context context = (Context) serializable;
ISOMsg isoMsg=context.get("REQUEST");
String groupSelector="";
if(!context.hasKey(REJECTION_REASON)){
String f22 = isoMsg.getString(22);
char entryMode = f22.charAt(6);
if(entryMode=='5' || entryMode=='7') {
groupSelector="group-contact-contactless";
}
}
return groupSelector;
}
@Override
public int prepare(long l, Serializable serializable) {
return PREPARED;
}
}
```
PrepareForCeibo:
**La clase `PrepareForCeibo` es un participante en una transacción de jPOS que implementa la interfaz `AbortParticipant`. Esta clase es responsable de preparar el contexto para Ceibo y manejar los abortos de transacción.**
**Detalle de este participante:**
- `setConfiguration(Configuration cfg)`: Este método se utiliza para configurar el participante. En este caso, no se realiza ninguna configuración específica.
- `doPrepare(long id, Context ctx)`: Este método se ejecuta durante la fase de preparación de la transacción. Actualmente, no realiza ninguna acción específica y simplemente devuelve `PREPARED | NO_JOIN`.
- `doPrepareForAbort(long id, Context ctx)`: Este método se ejecuta durante la fase de preparación para el aborto de la transacción. En este método, se realiza lo siguiente:
- Se obtiene la razón de rechazo desde el contexto.
- Si la razón de rechazo no es `INVALID_CRYPTOGRAM`, se clona el mensaje ISO de la solicitud y se ajusta el campo 39 con el código de respuesta correspondiente a la razón de rechazo.
- Se guarda el mensaje de respuesta ajustado en el contexto bajo la clave `RESPONSE_INTERNAL_DISCOVER_ERROR`.
**En resumen, la clase `PrepareForCeibo` es un participante en una transacción jPOS que prepara el contexto para Ceibo y maneja los abortos de transacción ajustando el mensaje de respuesta basado en la razón de rechazo.**
**Asociación de flujo:**
La clase `PrepareForCeibo` está asociada al flujo 1, ya que maneja los abortos de transacción y prepara el contexto para Ceibo, lo cual es relevante para `CeiboAuthorization`.
- Archivo
```java
package com.app.gateway.participants;
import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.ISOMsg;
import org.jpos.transaction.AbortParticipant;
import org.jpos.transaction.Context;
import org.jpos.transaction.TxnSupport;
import static com.app.gateway.Constants.*;
import static com.app.gateway.utils.Utilities.mapRejectionReasonToResponseCodeError;
public class PrepareForCeibo extends TxnSupport implements AbortParticipant {
@Override
public void setConfiguration(Configuration cfg) throws ConfigurationException {
super.setConfiguration(cfg);
}
@Override
protected int doPrepare(long id, Context ctx) throws Exception {
//
return PREPARED | NO_JOIN;
}
@Override
protected int doPrepareForAbort(long id, Context ctx) throws Exception {
String rejectionReason=ctx.get(REJECTION_REASON);
if(!rejectionReason.equals(INVALID_CRYPTOGRAM)){
ISOMsg isoMsg=ctx.get("REQUEST");
String responseCode=mapRejectionReasonToResponseCodeError(rejectionReason);
ISOMsg isoMsgResponseToTerminalDiscoverError= (ISOMsg) isoMsg.clone();
isoMsgResponseToTerminalDiscoverError.setResponseMTI();
isoMsgResponseToTerminalDiscoverError.set(39,responseCode);
ctx.put(RESPONSE_INTERNAL_DISCOVER_ERROR,isoMsgResponseToTerminalDiscoverError);
}
return PREPARED | NO_JOIN;
}
}
```
QueryHostDGW:
**La clase `QueryHostDGW` es un participante en una transacción de jPOS que implementa las interfaces `TransactionParticipant`, `ISOResponseListener` y `Configurable`. Esta clase es responsable de enviar una solicitud ISO a un host y manejar la respuesta.**
**Detalle de este participante:**
- `setConfiguration(Configuration cfg)`: Este método se utiliza para configurar el participante. Configura varios parámetros como `timeout`, `waitTimeout`, `requestName`, `responseName`, `destination`, entre otros.
- `prepare(long id, Serializable ser)`: Este es el método principal que se ejecuta durante la fase de preparación de la transacción. En este método, se realiza lo siguiente:
- Se obtiene el destino del contexto.
- Se obtiene el MUX correspondiente al destino.
- Se obtiene el mensaje ISO de la solicitud desde el contexto.
- Se verifica si el MUX está conectado y envía la solicitud al host.
- Maneja la respuesta del host y la guarda en el contexto.
- Si el host no responde o no está conectado, maneja los errores correspondientes.
- `responseReceived(ISOMsg resp, Object handBack)`: Este método maneja la respuesta recibida del host y la guarda en el contexto.
- `expired(Object handBack)`: Este método maneja el caso en que la solicitud al host expira y fuerza el aborto de la transacción.
- `resolveTimeout(Context ctx)`: Este método resuelve el tiempo de espera configurado.
- `isConnected(MUX mux)`: Este método verifica si el MUX está conectado.
**En resumen, la clase `QueryHostDGW` es un participante en una transacción jPOS que envía una solicitud ISO a un host, maneja la respuesta y maneja los errores en caso de que el host no responda o no esté conectado.**
**Asociación de flujo:**
La clase `QueryHostDGW` está asociada al flujo 1, ya que maneja la comunicación con el host, lo cual es relevante para `CeiboAuthorization`.
- Archivo
```java
package com.app.gateway.participants;
import org.jpos.core.Configurable;
import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.*;
import org.jpos.rc.CMF;
import org.jpos.rc.Result;
import org.jpos.transaction.Context;
import org.jpos.transaction.ContextConstants;
import org.jpos.transaction.TransactionParticipant;
import org.jpos.transaction.participant.QueryHost;
import org.jpos.util.Caller;
import org.jpos.util.Chronometer;
import org.jpos.util.NameRegistrar;
import java.io.Serializable;
import static com.app.gateway.Constants.*;
public class QueryHostDGW implements TransactionParticipant, ISOResponseListener, Configurable {
public static final String TIMEOUT_NAME = "QUERYHOST_TIMEOUT";
private static final long DEFAULT_TIMEOUT = 30000L;
private static final long DEFAULT_WAIT_TIMEOUT = 1000L;
private long timeout;
private long waitTimeout;
private String timeoutName = "QUERYHOST_TIMEOUT";
private String requestName;
private String responseName;
private String destination;
private boolean continuations;
private Configuration cfg;
private boolean ignoreUnreachable;
private boolean checkConnected = true;
public QueryHostDGW() {
}
public int prepare(long id, Serializable ser) {
Context ctx = (Context)ser;
Result result = ctx.getResult();
String ds = ctx.getString(this.destination);
if (ds == null) {
return result.fail(CMF.MISCONFIGURED_ENDPOINT, Caller.info(), "'%s' not present in Context", new Object[]{this.destination}).FAIL();
} else {
String muxName = this.cfg.get("mux." + ds, "mux." + ds);
MUX mux = (MUX)NameRegistrar.getIfExists(muxName);
if (mux == null) {
return result.fail(CMF.MISCONFIGURED_ENDPOINT, Caller.info(), "MUX '%s' not found", new Object[]{muxName}).FAIL();
} else {
ISOMsg m = (ISOMsg)ctx.get(this.requestName);
if (m == null) {
return result.fail(CMF.INVALID_REQUEST, Caller.info(), "'%s' is null", new Object[]{this.requestName}).FAIL();
} else {
Chronometer chronometer = new Chronometer();
if (this.isConnected(mux)) {
long t = Math.max(this.resolveTimeout(ctx) - chronometer.elapsed(), 1000L);
try {
if (this.continuations) {
mux.request(m, t, this, ctx);
return 197;
}
ISOMsg resp = mux.request(m, t);
if (resp != null) {
ctx.put(this.responseName, resp);
return 193;
}
if (!this.ignoreUnreachable) {
return result.fail(CMF.HOST_UNREACHABLE, Caller.info(), "'%s' does not respond", new Object[]{muxName}).FAIL();
}
ctx.log(String.format("MUX '%s' no response", muxName));
} catch (ISOException var14) {
ISOException e = var14;
return result.fail(CMF.SYSTEM_ERROR, Caller.info(), e.getMessage(), new Object[0]).FAIL();
}
} else {
ctx.put(REJECTION_REASON,CABAL_AR_NOT_CONNECTED);
if (!this.ignoreUnreachable) {
return result.fail(CMF.HOST_UNREACHABLE, Caller.info(), "'%s' is not connected", new Object[]{muxName}).FAIL();
}
ctx.log(String.format("MUX '%s' not connected", muxName));
}
return 193;
}
}
}
}
public void responseReceived(ISOMsg resp, Object handBack) {
Context ctx = (Context)handBack;
ctx.put(this.responseName, resp);
ctx.resume();
}
public void expired(Object handBack) {
Context ctx = (Context)handBack;
String ds = ctx.getString(this.destination);
String muxName = this.cfg.get("mux." + ds, "mux." + ds);
ctx.getResult().fail(CMF.HOST_UNREACHABLE, Caller.info(), "'%s' does not respond", new Object[]{muxName}).FAIL();
ctx.getPausedTransaction().forceAbort();
ctx.put(REJECTION_REASON, CONNECTION_TIMEOUT_TO_CABAL_AR);
ctx.resume();
}
public void setConfiguration(Configuration cfg) throws ConfigurationException {
this.cfg = cfg;
this.timeout = cfg.getLong("timeout", 18000L);
this.waitTimeout = cfg.getLong("wait-timeout", 1000L);
this.timeoutName = cfg.get("timeout-name", this.timeoutName);
this.requestName = cfg.get("request", ContextConstants.REQUEST.toString());
this.responseName = cfg.get("response", ContextConstants.RESPONSE.toString());
this.destination = cfg.get("destination", ContextConstants.DESTINATION.toString());
this.continuations = cfg.getBoolean("continuations", true);
this.ignoreUnreachable = cfg.getBoolean("ignore-host-unreachable", false);
this.checkConnected = cfg.getBoolean("check-connected", this.checkConnected);
}
protected long resolveTimeout(Context ctx) {
Object o = ctx.get(this.timeoutName);
if (o == null) {
return this.timeout;
} else {
return o instanceof Number ? ((Number)o).longValue() : Long.parseLong(o.toString());
}
}
protected boolean isConnected(MUX mux) {
if (this.checkConnected && !mux.isConnected()) {
long timeout = System.currentTimeMillis() + this.waitTimeout;
while(System.currentTimeMillis() < timeout) {
if (mux.isConnected()) {
return true;
}
ISOUtil.sleep(500L);
}
return false;
} else {
return true;
}
}
}
```
Archivos java (UTILS)
En la ruta src/main/java/com/app/gateway/utils/Utilities.java vamos a encontrar el utilities
Utilities:
La clase `Utilities` proporciona métodos utilitarios para manejar mensajes ISO y mapear razones de rechazo a códigos de respuesta. A continuación se detallan los métodos y su uso:
**Detalle de la Clase `Utilities`:**
1. **`convertISOMsgToString(ISOMsg isoMsg)`**:
- **Descripción**: Convierte un objeto `ISOMsg` a su representación en cadena.
- **Uso**: Se utiliza para serializar un mensaje ISO a una cadena ASCII.
2. **`convertStringToISOMsg(String isoMsgString, String packagerType)`**:
- **Descripción**: Convierte una cadena a un objeto `ISOMsg` utilizando un tipo de empaquetador específico.
- **Uso**: Se utiliza para deserializar una cadena ASCII a un mensaje ISO.
3. **`mapRejectionReasonToResponseCodeError(String rejectionReason)`**:
- **Descripción**: Mapea una razón de rechazo a un código de respuesta de error.
- **Uso**: Se utiliza para obtener el código de respuesta correspondiente a una razón de rechazo específica.
- Archivo
```java
package com.app.gateway.utils;
import org.jpos.iso.ISOMsg;
import org.jpos.iso.packager.GenericPackager;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import static com.app.gateway.Constants.*;
public class Utilities {
/**
* Convert the ISOMsg to a string
*/
public static String convertISOMsgToString(ISOMsg isoMsg) throws Exception {
if (isoMsg == null) {
throw new Exception("IsoMsg cannot be null");
}
try {
byte[] isoMsgBytes;
isoMsgBytes = isoMsg.pack();
return new String(isoMsgBytes, StandardCharsets.US_ASCII);
} catch (Exception e) {
throw new Exception("Error converting from ISOMsg to String");
}
}
/**
* Convert string to an ISOMsg
*/
public static ISOMsg convertStringToISOMsg(String isoMsgString, String packagerType) throws Exception {
if (isoMsgString == null) {
throw new Exception("IsoMsg string cannot be null");
}
if (packagerType == null) {
throw new Exception("Packager type cannot be null");
}
try {
byte[] isoMsgBytes = isoMsgString.getBytes(StandardCharsets.US_ASCII);
ISOMsg isoMsg = new ISOMsg();
switch (packagerType) {
case "ISO93ASCII":
isoMsg.setPackager(new GenericPackager("cfg/iso93ascii.xml"));
break;
case "ISO87Ascii":
isoMsg.setPackager(new GenericPackager("cfg/iso87ascii.xml"));
break;
default:
throw new Exception("Unsupported packager type: " + packagerType);
}
isoMsg.unpack(isoMsgBytes);
return isoMsg;
} catch (Exception e) {
throw new Exception("Error converting from string to IsoMsg", e);
}
}
public static String mapRejectionReasonToResponseCodeError(String rejectionReason) {
Map<String,String> map = new HashMap<>();
map.put(CABAL_AR_NOT_CONNECTED,"182");
map.put(INVALID_ENTRY_MODE,"100");
map.put(INVALID_FORMAT,"904");
map.put(CONNECTION_TIMEOUT_TO_CABAL_AR,"183");
map.put(ERROR_CONNECTION_TO_CEIBO,"194");
map.put(ERROR_CONNECTION_TO_DB,"204");
return map.get(rejectionReason);
}
}
```
Archivos java (CONSTANTS & LISTEINER)
En la ruta src/main/java/com/app/gateway/Constants.java vamos a encontrar las constantes y el listeiner
Constants:
**Detalle de las Constantes:**
1. **`ECHO_MTI`**:
- **Descripción**: Código de tipo de mensaje ISO para mensajes de eco.
- **Uso**: Utilizado en `IncomingListener` para identificar y responder a mensajes de eco.
2. **`REJECTION_REASON`**:
- **Descripción**: Clave para almacenar la razón de rechazo en el contexto.
- **Uso**: Utilizado en varios participantes para manejar razones de rechazo.
3. **`INVALID_CRYPTOGRAM`**:
- **Descripción**: Razón de rechazo para criptograma inválido.
- **Uso**: Utilizado en `PrepareForCeibo` para verificar razones de rechazo.
4. **`NOT_AUTHORIZED_BY_CEIBO`**:
- **Descripción**: Razón de rechazo para no autorizado por Ceibo.
- **Uso**: Utilizado en participantes relacionados con la autorización de Ceibo.
5. **`CONNECTION_TIMEOUT_TO_CABAL_AR`**:
- **Descripción**: Razón de rechazo para tiempo de espera de conexión a Cabal AR.
- **Uso**: Utilizado en `QueryHostDGW` y `Utilities` para manejar tiempos de espera.
6. **`ERROR_CONNECTION_TO_CEIBO`**:
- **Descripción**: Razón de rechazo para error de conexión a Ceibo.
- **Uso**: Utilizado en `Utilities` para mapear razones de rechazo.
7. **`ERROR_CONNECTION_TO_DB`**:
- **Descripción**: Razón de rechazo para error de conexión a la base de datos.
- **Uso**: Utilizado en `Utilities` para mapear razones de rechazo.
8. **`CABAL_AR_NOT_CONNECTED`**:
- **Descripción**: Razón de rechazo para Cabal AR no conectado.
- **Uso**: Utilizado en `QueryHostDGW` y `Utilities` para manejar conexiones.
9. **`INVALID_ENTRY_MODE`**:
- **Descripción**: Razón de rechazo para modo de entrada inválido.
- **Uso**: Utilizado en `Utilities` para mapear razones de rechazo.
10. **`INVALID_FORMAT`**:
- **Descripción**: Razón de rechazo para formato inválido.
- **Uso**: Utilizado en `Utilities` para mapear razones de rechazo.
11. **`RESPONSE_INTERNAL_DISCOVER_ERROR`**:
- **Descripción**: Clave para almacenar la respuesta de error interno de Discover en el contexto.
- **Uso**: Utilizado en `PrepareForCeibo` para manejar respuestas de error.
12. **`RESPONSE_FROM_CABAL_AR`**:
- **Descripción**: Clave para almacenar la respuesta de Cabal AR en el contexto.
- **Uso**: Utilizado en `QueryHostDGW` para manejar respuestas.
13. **`MESSAGE_INCOMING_BANCARD`**:
- **Descripción**: Clave para almacenar mensajes entrantes de Bancard en el contexto.
- **Uso**: Utilizado en participantes relacionados con Bancard.
14. **`TRANSACTION_TYPE`**:
- **Descripción**: Clave para almacenar el tipo de transacción en el contexto.
- **Uso**: Utilizado en varios participantes para manejar tipos de transacción.
15. **`CHIP`**:
- **Descripción**: Valor para identificar transacciones con chip.
- **Uso**: Utilizado en `EntryModeFilter` y `EntryModeSelector`.
16. **`BANDA`**:
- **Descripción**: Valor para identificar transacciones con banda magnética.
- **Uso**: Utilizado en `EntryModeFilter` y `EntryModeSelector`.
- Archivo
```java
package com.app.gateway;
public interface Constants {
public static final String ECHO_MTI="1804";
public static final String REJECTION_REASON="REJECTION_REASON";
public static final String INVALID_CRYPTOGRAM="INVALID_CRYPTOGRAM";
public static final String NOT_AUTHORIZED_BY_CEIBO="NOT_AUTHORIZED_BY_CEIBO";
public static final String CONNECTION_TIMEOUT_TO_CABAL_AR="CONNECTION_TIMEOUT_TO_CABAL_AR";
public static final String ERROR_CONNECTION_TO_CEIBO="ERROR_CONNECTION_TO_CEIBO";
public static final String ERROR_CONNECTION_TO_DB="ERROR_CONNECTION_TO_DB";
public static final String CABAL_AR_NOT_CONNECTED ="CABAL_AR_NOT_CONNECTED";
public static final String INVALID_ENTRY_MODE="INVALID_ENTRY_MODE";
public static final String INVALID_FORMAT="INVALID_FORMAT";
public static final String RESPONSE_INTERNAL_DISCOVER_ERROR="RESPONSE_INTERNAL_DISCOVER_ERROR";
public static final String RESPONSE_FROM_CABAL_AR="RESPONSE_FROM_CABAL_AR";
public static final String MESSAGE_INCOMING_BANCARD="MESSAGE_INCOMING_BANCARD";
public static final String TRANSACTION_TYPE="TRANSACTION_TYPE";
public static final String CHIP="CHIP";
public static final String BANDA="BANDA";
}
```
IncomingListener:
La clase `IncomingListener` extiende la clase `org.jpos.iso.IncomingListener` y se utiliza para procesar mensajes ISO entrantes. A continuación se detalla su funcionamiento:
**Detalle de la Clase `IncomingListener` :**
Atributos Importantes
- **`ECHO_MTI`**: Constante que representa el tipo de mensaje ISO para mensajes de eco.
Métodos
1. **`process(ISOSource src, ISOMsg m)`**:
- **Descripción**: Este método se encarga de procesar los mensajes ISO entrantes.
- **Parámetros**:
- `ISOSource src`: Fuente del mensaje ISO.
- `ISOMsg m`: Mensaje ISO a procesar.
- **Funcionamiento**:
- Verifica si el tipo de mensaje (`MTI`) del mensaje entrante es igual a `ECHO_MTI`.
- Si es un mensaje de eco (`ECHO_MTI`):
- Establece el tipo de mensaje de respuesta (`setResponseMTI`).
- Envía el mensaje de respuesta de vuelta a la fuente (`src.send(m)`).
- Retorna `true` indicando que el mensaje fue procesado.
- Si ocurre una excepción (`ISOException` o `IOException`), lanza una `RuntimeException`.
- Si no es un mensaje de eco, delega el procesamiento al método `process` de la clase base (`super.process(src, m)`).
**Impacto en los Flujos**
- **Flujos Impactados**: Flujos 1, 2 y 3.
- **Uso**: Este listener es utilizado para manejar mensajes de eco en todos los flujos, asegurando que los mensajes de eco sean respondidos adecuadamente.
- Archivo
```java
package com.app.gateway;
import org.jpos.iso.ISOException;
import org.jpos.iso.ISOMsg;
import org.jpos.iso.ISOSource;
import org.jpos.space.LocalSpace;
import org.jpos.space.SpaceSource;
import org.jpos.transaction.Context;
import java.io.IOException;
import java.util.Date;
import static com.app.gateway.Constants.ECHO_MTI;
public class IncomingListener extends org.jpos.iso.IncomingListener {
@Override
public boolean process(ISOSource src, ISOMsg m) {
try {
if (m.getMTI().equals(ECHO_MTI)) {
m.setResponseMTI();
src.send(m);
return true;
}
} catch (ISOException | IOException e) {
throw new RuntimeException(e);
}
return super.process(src, m);
}
}
```
Archivos resources
**En esta carpeta ubicada en src/main/resource y contiene ehcache.xml y hibernate.cfg.xml, son archivos de configuración relacionados con la base de datos y cómo la aplicación interactúa con ella (configuración para las bibliotecas Ehcache y Hibernate).**
Ehcache.xml :
**El archivo ehcache.xml es un archivo de configuración para Ehcache, que es una biblioteca de caché en memoria para Java. Ehcache puede mejorar el rendimiento al reducir la carga en la base de datos y la red al almacenar los datos en la memoria del servidor.**
Detalle de la implementacion:
- `<ehcache>`: Raíz del documento de configuración de EhCache.
- `<defaultCache>`: Define la configuración predeterminada para las cachés que no tienen una configuración específica.
- `maxElementsInMemory="0"`: Número máximo de elementos en memoria. Un valor de 0 significa sin límite.
- `eternal="false"`: Si es verdadero, los elementos en caché nunca expiran. Falso significa que pueden expirar.
- `timeToIdleSeconds="1200"`: Tiempo máximo, en segundos, que un elemento puede estar inactivo en caché antes de ser considerado expirable.
- `timeToLiveSeconds="1200"`: Tiempo máximo, en segundos, que un elemento puede vivir en caché, independientemente del tiempo de inactividad.
- Archivo | ehcache.xml
```xml
<ehcache>
<defaultCache
maxElementsInMemory="0"
eternal="false"
timeToIdleSeconds="1200"
timeToLiveSeconds="1200"
/>
</ehcache>
```
hibernate.cfg.xml :
El archivo hibernate.cfg.xml es un archivo de configuración para Hibernate, que es un marco de trabajo de mapeo objeto-relacional (ORM) para Java. Hibernate permite mapear las clases Java a las tablas de la base de datos y viceversa.
Detalle de la implementacion:
- `<hibernate-configuration>`: Raíz del documento de configuración de Hibernate.
- `<session-factory>`: Define una fábrica de sesiones para crear sesiones de Hibernate, que son interfaces para interactuar con la base de datos.
- `show_sql`, `format_sql`, `use_sql_comments`: Controlan la visualización y formato del SQL generado por Hibernate.
- `generate_statistics`: Habilita o deshabilita la generación de estadísticas.
- `max_fetch_depth`, `default_batch_fetch_size`, `use_streams_for_binary`: Optimizan la recuperación de datos.
- `connection.isolation`: Define el nivel de aislamiento de las transacciones.
- Configuraciones de c3p0 (`hibernate.c3p0.*`): Definen propiedades para la gestión del pool de conexiones, como tamaño mínimo y máximo, tiempo de espera, etc.
- `hibernate.cache.provider_class`: Especifica el proveedor de caché, en este caso, EhCache.
- `net.sf.ehcache.configurationResourceName`: Indica el archivo de configuración de EhCache.
- `hibernate.cache.use_structured_entries`: Habilita o deshabilita el uso de entradas estructuradas en la caché.
- `hbm2ddl.auto`: Controla la generación automática de esquemas de base de datos.
- `unreturnedConnectionTimeout`, `debugUnreturnedConnectionStackTraces`: Configuraciones para manejar conexiones no devueltas.
- Configuraciones de conexión (`connection.*`): Definen cómo Hibernate se conecta a la base de datos, incluyendo el dialecto, controlador, URL, credenciales y esquema.
- `hibernate.jdbc.time_zone`: Establece la zona horaria para las operaciones JDBC.
- Archivo | hibernate.cfg.xml
```xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- _hibernate.cfg.head -->
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<property name="use_sql_comments">true</property>
<property name="generate_statistics">false</property>
<property name="max_fetch_depth">2</property>
<property name="default_batch_fetch_size">16</property>
<property name="use_streams_for_binary">true</property>
<property name="connection.isolation">2</property>
<!--Pool de conexiones db -->
<property name="hibernate.c3p0.min_size">40</property>
<property name="hibernate.c3p0.max_size">400</property>
<property name="hibernate.c3p0.acquire_increment">30</property>
<property name="hibernate.c3p0.timeout">120</property>
<property name="hibernate.c3p0.max_statements">600</property>
<property name="hibernate.c3p0.idle_test_period">300</property>
<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<property name="net.sf.ehcache.configurationResourceName">ehcache.xml</property>
<property name="hibernate.cache.use_structured_entries">true</property>
<property name="hbm2ddl.auto">update</property>
<property name="unreturnedConnectionTimeout">30000</property>
<property name="debugUnreturnedConnectionStackTraces">false</property>
<!-- _hibernate.cfg.properties,v -->
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="connection.url">jdbc:oracle:thin:@@dbhost@:@dbport@@SID@</property>
<property name="connection.username">@dbusername@</property>
<property name="connection.password">@dbpassword@</property>
<property name="connection.schema">@dbname@</property>
<!-- Adding time zone -->
<property name="hibernate.jdbc.time_zone">UTC</property>
</session-factory>
</hibernate-configuration>
```
Cada una de estas configuraciones juega un papel crucial en el rendimiento, seguridad y correcto funcionamiento de la aplicación, permitiendo una gestión eficiente de la persistencia de datos y el caché.
---
Archivos Gradle
gradle.properties :
**Se realizaron modificaciones en el archivo `build.gradle` para gestionar dependencias, plugins y tareas de Gradle. Se agregaron dependencias necesarias para el funcionamiento del sistema, incluyendo jPOS, bibliotecas para procesamiento JSON y soporte de base de datos, entre otras.**
Aquí está el desglose de su contenido:
1. **Plugins**: Los plugins proporcionan funcionalidades adicionales a Gradle. En este caso, se están utilizando los plugins 'java', 'maven-publish', 'idea' y 'eclipse'. El plugin 'java' añade funcionalidades para compilar y probar proyectos Java. 'maven-publish' se utiliza para publicar artefactos en un repositorio Maven. 'idea' y 'eclipse' se utilizan para generar archivos de proyecto para los IDEs IntelliJ IDEA y Eclipse, respectivamente.
2. **Propiedades del proyecto**: Las propiedades `group`, `version`, `sourceCompatibility` y `targetCompatibility` definen la identidad y la compatibilidad del proyecto. `group` y `version` se utilizan para identificar el proyecto en un repositorio Maven. `sourceCompatibility` y `targetCompatibility` definen la versión mínima de Java requerida para construir y ejecutar el proyecto, respectivamente. En este caso, ambas están establecidas en Java 11.
3. **Repositorios**: En este caso, se están utilizando Maven Central, un repositorio personalizado en '[https://jpos.org/maven](https://jpos.org/maven)' y el repositorio local de Maven.
4. **Dependencias**: Las dependencias utilizadas son:
- `org.jpos:jpos:2.1.8-SNAPSHOT`: jPOS
- `org.jpos.ee:jposee-server-simulator:2.2.10-SNAPSHOT`: simulador de servidor para jPOS.
- `com.fasterxml.jackson.core:jackson-databind:2.17.0-rc1`: biblioteca para procesar datos JSON en Java.
- `org.jpos.ee:jposee-txn:2.2.8` y `org.jpos.ee:jposee-core:2.2.8`: bibliotecas de jPOS para el manejo de transacciones y funcionalidades básicas, respectivamente.
- `org.hibernate:hibernate-ehcache:5.5.4.Final`: Hibernate, Ehcache es una biblioteca de caché para Hibernate.
- `org.jpos.ee:jposee-dbsupport:2.2.8`: biblioteca de jPOS para el soporte de base de datos.
- `com.oracle.database.jdbc:ojdbc10:19.22.0.0`: controlador JDBC de Oracle para conectar con bases de datos Oracle.
5. **Aplicación de otro archivo Gradle**: La línea `apply from: 'jpos-app.gradle'` aplica la configuración de otro archivo Gradle llamado `jpos-app.gradle`. Esto permite separar la configuración de Gradle en varios archivos para una mejor organización.
- Archivo | gradle.properties
```xml
apply plugin: 'java'
apply plugin: 'maven-publish'
apply plugin: 'idea'
apply plugin: 'eclipse'
group = 'org.jpos.template'
version = '2.1.8-SNAPSHOT'
sourceCompatibility = 11
targetCompatibility = 11
repositories {
mavenCentral()
maven { url 'https://jpos.org/maven' }
mavenLocal()
}
dependencies {
implementation 'org.jpos:jpos:2.1.8-SNAPSHOT'
implementation 'org.jpos.ee:jposee-server-simulator:2.2.10-SNAPSHOT'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.0-rc1'
implementation 'org.jpos.ee:jposee-txn:2.2.8'
implementation 'org.jpos.ee:jposee-core:2.2.8'
implementation 'org.hibernate:hibernate-ehcache:5.5.4.Final'
implementation 'org.jpos.ee:jposee-dbsupport:2.2.8'
implementation 'com.oracle.database.jdbc:ojdbc10:19.22.0.0'
}
apply from: 'jpos-app.gradle'
```
---
jpos-app.gradle :
**Se creo la tarea `debug` en el archivo `jpos-app.gradle` es una tarea de Gradle que se utiliza para iniciar la aplicación en modo de depuración.**
Aquí está el desglose de lo que hace cada línea:
1. `task debug( dependsOn: 'installApp', type: Exec )`:
Esta línea define una nueva tarea llamada `debug` que depende de la tarea `installApp`. Esto significa que antes de que se ejecute la tarea `debug`, se ejecutará la tarea `installApp`. El tipo de tarea es `Exec`, lo que significa que esta tarea ejecutará un comando del sistema.
2. `workingDir installDir`:
Esta línea establece el directorio de trabajo para el comando que se va a ejecutar. `installDir` es una variable que se define en otro lugar del archivo `jpos-app.gradle` y representa el directorio donde se instala la aplicación.
3. `commandLine 'java','-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005','-jar', archiveJarName`:
Esta línea define el comando que se va a ejecutar. En este caso, el comando es `java` con varios argumentos. Los argumentos son:
- `agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005`: Este argumento inicia la Máquina Virtual de Java (JVM) con el agente de depuración habilitado. El agente de depuración permite a los depuradores remotos (como un IDE) conectarse a la JVM para depurar la aplicación. El agente de depuración se configura para escuchar en el puerto 5005 y suspender la ejecución de la aplicación hasta que un depurador se conecte.
- `jar archiveJarName`: Este argumento le dice a la JVM que ejecute la aplicación a partir de un archivo JAR. `archiveJarName` es una variable que se define en otro lugar del archivo `jpos-app.gradle` y representa el nombre del archivo JAR de la aplicación.
En resumen, la tarea `debug` instala la aplicación (si aún no está instalada), luego inicia la aplicación en modo de depuración y espera a que un depurador se conecte en el puerto 5005.
- Codigo
```xml
task debug( dependsOn: 'installApp', type: Exec ) {
workingDir installDir
commandLine 'java','-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005','-jar', archiveJarName
}
```
<aside>
📌 NOTA:
El archivo `jpos-app.gradle` es un archivo de configuración de Gradle que se utiliza para definir tareas y configuraciones específicas para la aplicación jPOS de este proyecto.
1. **Definición de variables y propiedades**: Al principio del archivo, se definen varias variables y propiedades que se utilizan en todo el archivo. Estas incluyen el nombre del archivo JAR de la aplicación, el directorio de instalación y la configuración específica del objetivo.
2. **Especificación de copia**: Se define una especificación de copia para copiar archivos de varias ubicaciones al directorio de instalación de la aplicación. Esto incluye archivos de configuración, archivos JAR y archivos de recursos.
3. **Tareas de Gradle**: Se definen varias tareas de Gradle para compilar, instalar y ejecutar la aplicación. Estas incluyen:
- `version`: Muestra la versión de jPOS.
- `dist` y `distnc`: Crean una distribución de la aplicación en formato tar.gz, con y sin archivos de configuración, respectivamente.
- `zip`: Crea una distribución de la aplicación en formato zip.
- `installApp`: Instala la aplicación en el directorio de instalación.
- `run`: Ejecuta la aplicación.
- `debug`: Ejecuta la aplicación en modo de depuración, permitiendo a los depuradores remotos conectarse a la aplicación.
4. **Tareas de generación de propiedades**: Se definen tareas para generar archivos de propiedades que contienen información sobre la compilación, como la marca de tiempo de la compilación y la revisión de Git.
En resumen, el archivo `jpos-app.gradle` se utiliza para definir la configuración y las tareas específicas de la aplicación jPOS, incluyendo la compilación, la instalación, la ejecución y la depuración de la aplicación.
</aside>
---
Archivos .properties
local.properties , develop.properties y production.properties :
Los archivos develop.properties, production.properties y local.properties son archivos de configuración que se utilizan para definir las propiedades específicas del entorno para los entornos de desarrollo, producción y local, respectivamente. Estos archivos tienen información como la ip y puerto de un servidor, las credenciales de la base de datos, etc., que son específicas para cada entorno.
- Archivo | local.properties
```xml
# Cabal AR
cabal.ar.host=localhost
cabal.ar.port=7942
# DB ORACLE
dbhost=10.5.2.36
dbport=1521
dbusername=creditopy
dbpassword=desarrollo
SID=/olpy
dbname=CREDITOPY
# Env
target.name=local
```
- Archivo | develop.properties
```xml
# Cabal AR
cabal.ar.host=172.27.6.55
cabal.ar.port=9015
# DB ORACLE
dbhost=10.5.2.36
dbport=1521
dbusername=creditopy
dbpassword=desarrollo
SID=/olpy
dbname=CREDITOPY
# Env
target.name=develop
```
- Archivo | production.properties
```xml
# Env
target.name=production
```
gradle.properties :
El archivo gradle.properties es un archivo de configuración de Gradle que se utiliza para configurar las propiedades del proyecto de Gradle. Puede contener propiedades que se aplican a todo el proyecto de Gradle. En el contexto proporcionado, este archivo se utiliza para definir el entorno objetivo (target) para el cual se está construyendo el proyecto.
En este caso, el valor de target puede ser local, develop o production, y se puede utilizar para determinar qué archivo de propiedades del entorno (local.properties, develop.properties o production.properties) se debe utilizar para la construcción actual.
- Archivo | gradle.properties
```xml
# ENVIRONMENT CONFIGURATION (local, develop, production)
target=develop
```
---
Docker
Dockerfile :
**En este `Dockerfile` definimos una imagen de Docker que se construye a partir de la imagen base `openjdk:11-jdk-slim`, que es una imagen de Docker que contiene el JDK 11 en una versión reducida de Debian.**
El detalle del mismo:
- `WORKDIR /app`: Establece el directorio de trabajo en el contenedor a `/app`. Todos los comandos que se ejecuten después de esta línea se ejecutarán en este directorio.
- `RUN apt-get update && apt-get install -y locales && echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && locale-gen`: Actualiza la lista de paquetes disponibles, instala el paquete `locales`, genera la localización `en_US.UTF-8` y la configura como la localización predeterminada.
- `ENV LANG=en_US.UTF-8`, `ENV LANGUAGE=en_US:en`, `ENV LC_ALL=en_US.UTF-8`, `ENV TZ='America/Asuncion'`: Establece las variables de entorno para la localización y la zona horaria de Paraguay.
- `RUN apt-get update && apt-get install -y curl tzdata`: Actualiza la lista de paquetes disponibles e instala los paquetes `curl` y `tzdata`.
- `RUN apt-get install -y net-tools && apt-get install -y iputils-ping && apt-get install -y mc`: Instala los paquetes `net-tools`, `iputils-ping` y `mc`.
- `COPY . .`: Copia todos los archivos y directorios del directorio actual en el host al directorio de trabajo en el contenedor.
- `RUN ./gradlew installApp`: Ejecuta el comando `./gradlew installApp` para instalar la aplicación.
- `EXPOSE 7002 9015`: Expone los puertos 7002 y 9015 del contenedor.
- `CMD ["./gradlew", "run"]`: Define el comando que se ejecutará cuando se inicie el contenedor.
- Archivo | Dockerfile
```xml
FROM openjdk:11-jdk-slim
WORKDIR /app
# Instalar locales y generar la localización en_US.UTF-8
RUN apt-get update && apt-get install -y locales && \
echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && locale-gen
# Configurar el fuso horario y la localización
ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US:en
ENV LC_ALL=en_US.UTF-8
ENV TZ='America/Asuncion'
RUN apt-get update && apt-get install -y curl tzdata
RUN apt-get install -y net-tools && apt-get install -y iputils-ping && apt-get install -y mc
COPY . .
RUN ./gradlew installApp
EXPOSE 7002 9015
CMD ["./gradlew", "run"]
```
En el entorno de desarrollo (VM de Cabal Paraguay), se utiliza la imagen creada `discovergw-image` y el contenedor `jpos-discovergw`. Esto significa que después de construir la imagen con este `Dockerfile`, se etiqueta como `discovergw-image` y se utiliza para crear un contenedor llamado `jpos-discovergw`.
**COMANDO BASICOS DE GUIA (DESARROLLO)→**
ENTRAR AL CONTENEDOR :
`docker exec -it jpos-discovergw bash`
VER LOGS :
`docker logs --tail 100 -f jpos-discovergw`
EJECUTAR CONTENEDOR:
`docker run -d --name jpos-discovergw -p 7002:7002 -p 7004:7004 -p 9015:9015 discovergw-image`