This tutorial explains how to call a Java library from Genero in a SOA environment,
using Genero and Java Web services.
This can easily be done using the Java JAX-WS framework on a server, and a Genero application for the client part.
Notice that there is no strong linkage between Genero and a java JVM.
For this tutorial we will use a Java barcode creation library to build a picture from a code.
Note: Accessing a .NET library could be done in the same manner.
The usage of Genero Web Services to call a Java service is recommended in a SOA environment. It enables several Genero applications to connect
to a centralized Java service without the need to start a new JVM for each running Genero application. It also provides more flexibility because there is no strong linkage between Genero
and the Java virtual machine. You can for instance upgrade the Java service without changing anything in your Genero code.
However due to the XML serialization process and the HTTP transport protocol in Web Services, there can be some performance issues. So if your main concern is performance,
it is recommended to use the Genero Java bridge.
The barcode library is composed of two libraries -- one for building a barcode image from a numeric code, and one for reading a barcode image to return the numeric code. This section depends on the library you want to use in Genero.
In our tutorial, we create two functions called buildImage and readImage; the Java implementation is below:
buildImage( type : String, code : String) : byte[ ]
try { Barcode builder=new Barcode();
builder.setType(GetBarcodeBuilderType(type));
builder.setData(data);
builder.setAddCheckSum(true);
ByteArrayOutputStream out=new ByteArrayOutputStream();
if (builder.createBarcodeImage(out)) {
byte[] ret = out.toByteArray();
return ret;
} else { return null; } } catch(Exception e) { return null;
}
readImage( type : String, img : byte[ ]) : String
try { File f=new File("tmp.jpg"); FileOutputStream stream=new FileOutputStream(f); stream.write(img); stream.close(); String[] datas = BarcodeReader.read(f, GetBarcodeReaderType(type)); if (datas==null) { return null; } else { String ret = datas[0]; return ret; } } catch (Exception e) { return null; }The following two functions convert the type of a code bar to the type expected by the library:
private int GetBarcodeBuilderType(String str) {
if (str.equals("CODABAR")) {
return Barcode.CODABAR;
} else if (str.equals("CODE11")) {
return Barcode.CODE11;
} else if (str.equals("CODE128")) {
return Barcode.CODE128;
} else if (str.equals("CODE128A")) {
return Barcode.CODE128A;
} else if (str.equals("CODE128B")) {
return Barcode.CODE128B;
} else if (str.equals("CODE128C")) {
return Barcode.CODE128C;
} else if (str.equals("CODE2OF5")) {
return Barcode.CODE2OF5;
} else if (str.equals("CODE39")) {
return Barcode.CODE39;
} else if (str.equals("CODE39EX")) {
return Barcode.CODE39EX;
} else if (str.equals("CODE93")) {
return Barcode.CODE93;
} else if (str.equals("CODE93EX")) {
return Barcode.CODE93EX;
} else if (str.equals("EAN13")) {
return Barcode.EAN13;
} else if (str.equals("EAN13_2")) {
return Barcode.EAN13_2;
} else if (str.equals("EAN13_5")) {
return Barcode.EAN13_5;
} else if (str.equals("EAN8")) {
return Barcode.EAN8;
} else if (str.equals("EAN8_2")) {
return Barcode.EAN8_2;
} else if (str.equals("EAN8_5")) {
return Barcode.EAN8_5;
} else if (str.equals("INTERLEAVED25")) {
return Barcode.INTERLEAVED25;
} else if (str.equals("ITF14")) {
return Barcode.ITF14;
} else if (str.equals("ONECODE")) {
return Barcode.ONECODE;
} else if (str.equals("PLANET")) {
return Barcode.PLANET;
} else if (str.equals("POSTNET")) {
return Barcode.POSTNET;
} else if (str.equals("RM4SCC")) {
return Barcode.RM4SCC; } else if (str.equals("UPCA")) {
return Barcode.UPCA;
} else if (str.equals("UPCE")) {
return Barcode.UPCE; } else {
return return -1; } }
private int GetBarcodeReaderType(String str) {
if (str.equals("CODABAR")) {
return BarcodeReader.CODABAR;
} else if (str.equals("CODE11")) {
return BarcodeReader.CODE11;
} else if (str.equals("CODE128")) {
return BarcodeReader.CODE128;
} else if (str.equals("CODE39")) {
return BarcodeReader.CODE39;
} else if (str.equals("CODE39EX")) {
return BarcodeReader.CODE39EX;
} else if (str.equals("CODE93")) {
return BarcodeReader.CODE93;
} else if (str.equals("DATAMATRIX")) {
return BarcodeReader.DATAMATRIX;
} else if (str.equals("EAN13")) {
return BarcodeReader.EAN13;
} else if (str.equals("EAN8")) {
return BarcodeReader.EAN8;
} else if (str.equals("INTERLEAVED25")) {
return BarcodeReader.INTERLEAVED25;
} else if (str.equals("ITF14")) {
return BarcodeReader.ITF14;
} else if (str.equals("ONECODE")) {
return BarcodeReader.ONECODE;
} else if (str.equals("PLANET")) {
return BarcodeReader.PLANET;
} else if (str.equals("POSTNET")) {
return BarcodeReader.POSTNET;
} else if (str.equals("QRCODE")) {
return BarcodeReader.QRCODE;
} else if (str.equals("RM4SCC")) {
return BarcodeReader.RM4SCC;
} else if (str.equals("RSS14")) {
return BarcodeReader.RSS14;
} else if (str.equals("RSSLIMITED")) {
return BarcodeReader.RSSLIMITED;
} else if (str.equals("UPCA")) {
return BarcodeReader.UPCA;
} else if (str.equals("UPCE")) {
return BarcodeReader.UPCE;
} else {r> return -1; } }
The integration of one or several Java libraries with multiple methods in a Genero application can be performed as described below:
Instead of writing the functions in 4GL, you simply need to write them in a Java class with the methods you want to use in 4GL as described below. In our example, the two functions are buildImage and readImage. And of course, don't forget to import the necessary Java import instructions.
import com.barcodelib.barcodereader.BarcodeReader; import com.barcodelib.barcode.Barcode; import java.io.*; import javax.jws.*; import javax.jws.soap.SOAPBinding; import javax.xml.ws.Endpoint; public class BarcodeService { public byte[] buildImage(String type,String data) { /*BUILDIMAGE IMPLEMENTATION CODE DESCRIBED ABOVE*/ } public String readImage(String type,byte[] img) { { { { /*READIMAGE IMPLEMENTATION CODE DESCRIBED ABOVE*/ } } }
Notice that if you want the service to run standalone, you must also add following the main method to tell the system the port number on which the service will run:
public static void main(String[] args) { String endpointUri = "http://localhost:9090/"; Endpoint.publish(endpointUri, new BarcodeService ()); System.out.println("BarcodeService started at " + endpointUri); }
To transform the previous java class in a Web Service, simply add a WebService annotation:
@WebService(targetNamespace = "http://www.mycompany.com/barcode", name="Barcode", serviceName="BarcodeService") public class BarcodeService { ... }
This defines all public and non static methods of the class as operations of the BarcodeService Web Service.
Compile the previously created java class, and run it.
Commands to compile and execute the service in standalone mode:
$ javac BarcodeService.java
$ java BarcodeService
Once the service is started, it is ready to accept requests and you can also retrieve its WSDL at following URL:
http://localhost/9090/BarcodeService?WSDLNote: If you want the service to be started on a web server, you must deploy it first using Eclipse or the Web Server deployment tools.
Use the fglwsdl tool to generate the client stub to access the BarcodeService:
$ fglwsdl http://localhost:9090/BarcodeService?WSDL
This will create two 4GL files that must be compiled and linked into your 4GL application in order to call the Java barcode library functions. These files contain the 4GL interface to access the Java library where you will find the two functions, readImage and buildImage, defined in 4GL.
The last step is to modify the existing application where you want to use the Java library, by calling the 4GL functions generated in the stub. Then compile your application and the previously generated stubs, and link everything together.
Your application is now ready to use the different features of your Java library.
This program calls the buildImage function of the Barcode Java library.
GLOBALS "BarcodeService_BarcodePort.inc"
MAIN
DEFINE wsstatus INTEGER
IF num_args() != 3 THEN
CALL ExitHelp() END IF
LET ns1buildImage.arg0 = arg_val(1) LET ns1buildImage.arg1 = arg_val(2) LOCATE ns1buildImageResponse.return IN MEMORY LET wsstatus = buildImage_g()
IF wsstatus <> 0 THEN DISPLAY "Error ("||wsError.code||") : ",wsError.description
ELSE IF IF ns1buildImageResponse.return IS NULL THEN DISPLAY "Encoding Failed" ELSE CALL ns1buildImageResponse.return.writeFile(arg_val(3)) END IF
END IF
FREE ns1buildImageResponse.return
END MAIN
FUNCTION ExitHelp()
DISPLAY arg_val(0)||" <type> <data> <filename>"
DISPLAY "type : barcode type such as EAN8 or CODE128"
DISPLAY "data : data to be encoded with a barcode [0-9A-D]"
DISPLAY "filename : resulting image filename"
DISPLAY "example : createImage EAN8 12358723A mybarcode.jpg"
EXIT PROGRAM (-1)
END FUNCTION
In a SOA environment, you can call any Java library from Genero using Web Services, and without a strong dependency to a JVM. This follows SOA principles - it allows you to reuse the Java library in another 4GL application without any new development, you can update the Java part without recompiling any 4GL sources, and integrate any function available from a SOA platform.