Aquí aprenderá como reutilizar un programa COBOL, exponiéndolo como una API REST moderna. Para ello, le enseñaremos a:
- Instalar un compilador COBOL open (en caso de que no disponga de uno).
- Escribir una sencilla “Hello, world” subrutina COBOL.
- Compilar la subrutina COBOL y probarla.
- Construir una API REST con el código anterior.
Pre-requisitos
- Experiencia de programación básica. Aunque el programa “Hello, world” es muy sencillo, es necesario tener los conocimientos necesarios para compilarlo estáticamente.
- Entender como funciona el paquete cgo de Go.
- Una herramienta IDE para editar el código. Como hemos comentado anteriormente, el código es muy sencillo, por lo que cualquier herramienta debería servirnos. VSCode (free) dispone de distintas extensiones, incluido COBOL para facilitarnos este tipo de tareas.
- Una aplicación para ejecutar comandos. Linux o Mac terminal.
Instalar GNUCobol
Necesitaremos un compilador de COBOL. Lo ideal, es disponer de un compilador de 64-bit, utilizaremos GNUCobol para compilar y ejecutar el siguiente ejemplo.
Es posible utilizar compiladores COBOL de terceros, con o sin licencia, siempre que ofrezcan la posibilidad de llamar al código COBOL desde programas C.
Step1. Si está utilizando MacOS, instale previamente Homebrew
Step2. Instale GNUCobol, abra un terminal y ejecute el siguiente comando (MacOS):
brew install gnu-cobol
Para instalar GNUCobol en Linux ejecute el comando equivalente según el tipo de distribución utilizada.
Step3. Compruebe si la instalación se realizó de manera correcta ejecutando el siguiente comando:
cobc -v
Comenzar a escribir código
- Abrir la aplicación terminal e ir a nuestro home directory.
cd
- Crear un directorio “hello” para nuestro programa COBOL.
mkdir hello
cd hello
-
En la herramienta de IDE (o editor de texto), crear un fichero con el nombre “hello.cbl”.
-
Copiar el siguiente código en el fichero “hello.cbl” y guardarlo.
IDENTIFICATION DIVISION.
PROGRAM-ID. hello.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
DATA DIVISION.
WORKING-STORAGE SECTION.
* In COBOL, you declare variables in the WORKING-STORAGE section
LINKAGE SECTION.
* Data to share with COBOL subroutines
01 INPUT-NAME PIC X(10).
01 OUTPUT-PARM.
05 PARM1 PIC X(07).
05 PARM2 PIC X(10).
PROCEDURE DIVISION USING INPUT-NAME, OUTPUT-PARM.
MOVE "Hello," TO PARM1.
IF INPUT-NAME IS EQUAL TO (SPACES OR LOW-VALUES)
MOVE "World" TO PARM2
MOVE 2 TO RETURN-CODE
ELSE
MOVE INPUT-NAME TO PARM2
MOVE 0 TO RETURN-CODE
END-IF.
GOBACK.
El programa COBOL “hello” (subrutina COBOL) recibe un &nombre (INPUT-NAME) y devuelve “Hello, &nombre” (OUTPUT-PARM). En caso de que &nombre no sea informado, el programa devuelve “Hello, World”.
- Compilar el programa COBOL “hello”.
cobc -c -O -fstatic-call hello.cbl
Este comando creará un objeto hello.o en el directorio hello
- Ahora necesitaremos un programa principal para poder probar nuestra subrutina COBOL. Cree un fichero “launch.cbl” en el mismo directorio y copie el siguiente código:
IDENTIFICATION DIVISION.
PROGRAM-ID. launch.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
DATA DIVISION.
WORKING-STORAGE SECTION.
* Declare program variables
01 INPUT-NAME PIC X(10).
01 OUTPUT-PARM PIC X(17).
PROCEDURE DIVISION.
* code goes here!
DISPLAY "Your name: " WITH NO ADVANCING.
ACCEPT INPUT-NAME.
CALL 'hello' USING INPUT-NAME, OUTPUT-PARM.
DISPLAY OUTPUT-PARM.
DISPLAY "Return Code: " RETURN-CODE.
STOP RUN.
- Compile el programa principal y haga un linkado estático con la subrutina.
cobc -c -x launch.cbl
cobc -x launch.o hello.o
- Para ejecutar el código ejecute el siguiente comando.
./launch
Construya una API REST
Usaremos el lenguaje Go para construir nuestra API REST. El código Go de nuestra API llamará de manera estática a la subrutina COBOL, de manera equivalente a como lo hacía el programa principal “launch.cbl” usando cgo.
- Para ello, necesitaremos instalar Go
Las instrucciones para la instalación de Go sobre las plataformas MacOS y Linux puede encontrarse aquí. Siga las instrucciones e instale la última versión estable de Go.
- Verifique que Go se ha instalado de manera correcta. Para ello, abra un terminal y ejecute el siguiente comando:
go version
- Vamos a crear la siguiente estructura de directorios para almacenar los componentes del proyecto.
├── greetings
│ └── include
│ └── libs
│ go.mod
│ go.sum
│ main.go
greetings -> Go programs
greetings/include -> .h files
greetings/libs -> hello.o (COBOL routine)
Vaya al directorio go/src y teclee los siguientes comandos:
mkdir greetings
mkdir greetings/include
mkdir greetings/libs
- Cree un fichero “go.mod” para manejar las dependencias del código.
cd greetings
go mod init example/greetings
go: creating new go.mod: module example/greetings
go: to add module requirements and sums:
go mod tidy
- Cree un fichero “main.go” utilizando la herramienta IDE o el editor de texto y copie el siguiente código
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.GET("/hello", getName)
router.GET("/hello/:name", getName)
router.Run("localhost:8080")
}
func getName(c *gin.Context) {
d := c.Param("name")
c.IndentedJSON(http.StatusOK, gin.H{"output-parm": d})
}
- Vamos a ejecutar nuestra primera API Go.
go mod tidy
go run .
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /hello --> main.getName (3 handlers)
[GIN-debug] GET /hello/:name --> main.getName (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on localhost:8080
Ya tenemos nuestra API funcionando, abra una sesión nueva de terminal y utilice curl para probarla.
curl http://localhost:8080/hello
curl http://localhost:8080/hello/Hooper
Linkado estático de la API REST con el módulo COBOL
- Ahora modificaremos el programa “main.go” escrito anteriormente.
func main() {
C.cob_init(C.int(0), nil)
router := gin.Default()
router.GET("/hello", getName)
router.GET("/hello/:name", getName)
router.Run("localhost:8080")
}
func getName(c *gin.Context) {
d := c.Param("name")
o := callhello(d)
c.IndentedJSON(http.StatusOK, gin.H{"output-parm": o})
}
Desde la función “main” del programa Go tendremos que inicializar el “runtime” COBOL.
C.cob_init(C.int(0), nil)
No vamos a pasar ningún argumento, por lo que será más sencillo pasar un puntero con valor null
Ahora necesitamos crear una nueva función para efectuar la llamada al módulo COBOL “hello.o”
o := callhello(d)
- Copie este código, justo encima de la función “main”.
func callhello(d string) string {
inputName := C.CString(d)
defer C.free(unsafe.Pointer(inputName))
outputParm := C.CString("")
defer C.free(unsafe.Pointer(outputParm))
returnCode := C.hello(inputName, outputParm)
if returnCode == 0 || returnCode == 2 {
return C.GoString(outputParm)
} else {
return "ERROR FROM COBOL"
}
}
- Para utilizar cgo, necesitamos importar el paquete “C”. Copie el siguiente código justo después de la sentencia “package”.
/*
#cgo CFLAGS: -I${SRCDIR}/include
#cgo LDFLAGS: ${SRCDIR}/libs/hello.o -L/opt/homebrew/Cellar/gnucobol/3.2/lib -lcob
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "hello.h"
extern void cob_init(int argc,char** argv);
*/
import "C"
import (
"net/http"
"unsafe"
"github.com/gin-gonic/gin"
)
El paquete “C” usará las instrucciones definidas como comentarios justo antes de la línea “import C”. No agrupe la sentencia “import C” con la importación de otros paquetes y asegúrese de que se encuentra a justo a continuación de las líneas de comentarios
#cgo CFLAGS: -I${SRCDIR}/include
Defina el directorio donde se encuentran los ficheros “.h”.
#cgo LDFLAGS: ${SRCDIR}/libs/hello.o -L/opt/homebrew/Cellar/gnu-cobol/3.2/lib -lcob
Defina el directorio donde se encuentra el módulo COBOL (hello.o) y el runtime del lenguaje (GNUCobol).
Por favor, revise de acuerdo al gestor de paquetes utilizado, donde está instalado GNUCobol y específicamente la librería “libcob”. Por ejemplo en MacOs con arquitectura arm (chip M1/M2) homebrew puede instalar GNUCobol en el directorio /opt/homebrew/Cellar/gnucobol/3.2/lib
- A continuación el código completo del programa “main.go”.
package main
/*
#cgo CFLAGS: -I${SRCDIR}/include
#cgo LDFLAGS: ${SRCDIR}/libs/hello.o -L/opt/homebrew/Cellar/gnucobol/3.2/lib -lcob
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "hello.h"
extern void cob_init(int argc,char** argv);
*/
import "C"
import (
"net/http"
"unsafe"
"github.com/gin-gonic/gin"
)
func callhello(d string) string {
inputName := C.CString(d)
defer C.free(unsafe.Pointer(inputName))
outputParm := C.CString("")
defer C.free(unsafe.Pointer(outputParm))
returnCode := C.hello(inputName, outputParm)
if returnCode == 0 || returnCode == 2 {
return C.GoString(outputParm)
} else {
return "ERROR FROM COBOL"
}
}
func main() {
C.cob_init(C.int(0), nil)
router := gin.Default()
router.GET("/hello", getName)
router.GET("/hello/:name", getName)
router.Run("localhost:8080")
}
func getName(c *gin.Context) {
d := c.Param("name")
o := callhello(d)
c.IndentedJSON(http.StatusOK, gin.H{"output-parm": o})
}
-
Copie el módulo COBOL “hello.o” en el directorio /greetings/libs.
-
Cree un fichero “hello.h” en el directorio /greetings/include.
cd include
Copie el siguiente código en el fichero “hello.h”
extern int hello(char* inputName, char* outputParm);
¡Hagamos una prueba!
-
Si el programa “main.go” sigue ejecutandose, detengalo.
-
Sitúese en el directorio que contiene el programa “main.go” y ejecute el comando:
go run .
- Probemos nuestra API usando curl
curl http://localhost:8080/hello
curl http://localhost:8080/hello/Hooper
Aprenda a escribir APIs gRPC
En este sencillo ejemplo hemos aprendido como reutilizar nuestro código COBOL mediante la funcionalidad que nos ofrece Go. Si quiere seguir aprendiendo, puede revisar otros ejemplos en la siguiente sección: Ejemplos.