Label Reader Tutorial
This section covers a basic Java Application tutorial. The tutorial will demonstrate how to create a basic label reader application using Java and the Datalogic JavaPOS API.
Introduction
For this tutorial, we will be creating a simple console application in Java that opens, claims and enables a scanner device and provides a mechanism to see what labels were scanned with that device. This tutorial only covers a very basic aspect of development in JavaPOS, but should suffice as a starting point for any application that looks to read labels from a Datalogic scanning device.
Prerequisites
In order to develop an application for Datalogic Scanning devices, you will need to first have the supporting JAR libraries and configuration files needed for Datalogic JavaPOS present.
Support Libraries
The easiest way to integrate JavaPOS into your application is to copy the entire SupportJars folder from the installed JavaPOS location to your project. This will add a SupportJars folder to your project and will contain all of the support libraries needed for JavaPOS. You will also need to copy the JavaPOS.jar file to your project. This file can be copied to the SupportJars folder if you wish, or can be copied to your application's directory.
Configuration Files
In order for JavaPOS to operate correctly, you will need the configuration files that are installed with JavaPOS copied into your application's directory.
Application Development
The Tutorial Class
In order to respond to JPOS events, your class will need to implement the jpos.events.DataListener interface in your class. This interface is part of the OMG JPOS 1.14 library (jpos114.jar). In order to implement the listener, you will also need to import the jpos.events.DataEvent class. Add the following imports to your class:
import jpos.events.DataEvent;
import jpos.events.DataListener;
Now create your Tutorial class which implements the DataListener interface as follows:
public class Tutorial implements DataListener {
public void dataOccurred(DataEvent de) {
}
}
Note that each time data is received from the Scanner device, the dataOccurred method will be called. This method is where we will put our label processing.
The Scanner implementation
In order to program for a UPOS Scanner device, we will need to import the jpos.Scanner class. This class is the UPOS Scanner device implementation.
Add the following import to the Tutorial class:
import jpos.Scanner;
Next, we will add a member variable to the Tutorial class to hold the Scanner instance.
public class Tutorial implements DataListener {
private Scanner scanner;
Next, add a constructor for the Tutorial class that instantiates the Scanner instance as follows:
public class Tutorial implements DataListener {
private Scanner scanner;
public Tutorial() {
this.scanner = new Scanner();
}
Now the Tutorial class has a member variable named scanner that holds a generic UPOS Scanner instance.
Open -> Claim -> Enable
In order for an application to read labels using a UPOS Scanning device, the application must first open the UPOS device. The application must then claim the device. Finally, the application sends an enable command to the claimed device allowing the device to begin reading labels. Each step of this process is represented by a method within the UPOS Scanner class. Each of these methods potentially throws a JposException when they are called, so it is a good time to import jpos.JposException by adding the following to your Tutorial class imports:
import jpos.JposException;
jpos.xml Profile
The JPOS Scanner.open method takes a single argument denoting the name of the jpos.xml Profile to use. This profile must match the logicalName attribute of a JposEntry element in the jpos.xml file. For this example, I will be using the default jpos.xml that is shipped with Datalogic JavaPOS, and I will be using the DL-Gryphon-GD4430-USB-OEM profile from jpos.xml. You can find the beginning of this profile by looking in the jpos.xml file, but it is included here as well.
<JposEntry logicalName="DL-Gryphon-GD4430-USB-OEM">
<creation factoryClass="com.dls.jpos.service.DLSScannerInstanceFactory" serviceClass="com.dls.jpos.service.DLSScannerService"/>
<vendor name="DLA" url="http://www.adc.datalogic.com"/>
<jpos category="Scanner" version="1.13"/>
<product description="ScannerService" name="ScannerService" url="http://www.adc.datalogic.com"/>
Connecting to the device
Now that I have my profile selected, it is time to add a method for connecting to the Scanner device. Add a method to your class to encapsulate connecting to a Scanner device. This method should take a single parameter that contains the logicalName of the profile to use. This method should return a boolean indicating whether it succeeded in connecting to the device.
public boolean connectScanner(String profile) {
}
Next, we will add the call to Scanner.open. Because this call can throw an exception, we will need to surround it in a try .. catch clause. If the device fails to open, we need to notify the user and return false. Note that the call to open merely loads the jpos.xml profile and does not attempt to connect to the device. The only reason that this call should fail is if a JposEntry element could not be found with the profile name.
public boolean connectScanner(String profile) {
try {
this.scanner.open(profile);
} catch (JposException je) {
System.err.println("ERROR: Failed to open profile: " + profile + ", " + je);
return false;
}
}
After the device profile has been loaded through the call to Scanner.open, it is time to claim the device using Scanner.claim. Like Scanner.open, Scanner.claim can throw an exception and will need to be surrounded in a try .. catch clause. There are a variety of reasons that a call to claim would throw an exception, so it is important to examine the exception thrown and close the device if an exception is thrown during the call to Scanner.claim. The call to Scanner.claim takes a single integer argument indicating the number of milliseconds to wait before timeout when claiming a device. For the purpose of this tutorial, we will be using a fixed timeout of 1000 milliseconds or one second, but it is recommended that this argument be configurable externally so that the code does not have to be rebuilt to change the value.
public boolean connectScanner(String profile) {
try {
this.scanner.open(profile);
} catch (JposException openx) {
System.err.println("ERROR: Failed to open profile: " + profile + ", " + openx);
return false;
}
try {
this.scanner.claim(1000);
} catch (JposException claimx) {
System.err.println("ERROR: Failed to claim scanner: " + claimx);
try {
this.scanner.close();
} catch (JposException closex) {
System.err.println("ERROR: Failed to close device: " + closex);
}
return false;
}
}
At this point, it is time to enable the device. The Scanner device is enabled by a call to Scanner.setDeviceEnabled with the argument of true. The Scanner.setDeviceEnabled method can throw a JposException, so it is necessary to wrap it in a try .. catch clause. If the call fails, it will be necessary to close the scanner before returning false.
try {
this.scanner.setDeviceEnabled(true);
} catch (JposException enablex) {
System.err.println("ERROR: Failed to enable device: " + enablex);
try {
this.scanner.close();
} catch (JposException closex) {
System.err.println("ERROR: Failed to close device: " + closex);
}
return false;
}
The device should now be opened, claimed and enabled. However, there will be no data delivered from the device until the Data Event Enabled is set to true. This step is performed by a call to Scanner.setDataEventEnabled. Because Scanner.setDataEventEnabled can throw an exception, it is necessary to surround the statement in a try .. catch clause. Add the following code after the call to Scanner.setDeviceEnabled.
try {
this.scanner.setDataEventEnabled(true);
} catch (JposException datax) {
System.err.println("ERROR: Failed to enable Data Events: " + datax);
try {
this.scanner.close();
} catch (JposException closex) {
System.err.println("ERROR: Failed to close device: " + closex);
}
return false;
}
Now that the scanner is opened, claimed, enabled and has Data Events enabled, the device can now start to deliver events to our application. In order to process those events, we will need to add our application as a data listener for the device. This will result in our dataOccurred method being called each time a data event is delivered from the device. Add the following to the end of your connectScanner method:
this.scanner.addDataListener(this);
return true;
Here is the completed Tutorial.connectScanner method:
public boolean connectScanner(String profile) {
try {
this.scanner.open(profile);
} catch (JposException openx) {
System.err.println("ERROR: Failed to open profile: " + profile + ", " + openx);
return false;
}
try {
this.scanner.claim(1000);
} catch (JposException claimx) {
System.err.println("ERROR: Failed to claim scanner: " + claimx);
try {
this.scanner.close();
} catch (JposException closex) {
System.err.println("ERROR: Failed to close device: " + closex);
}
return false;
}
try {
this.scanner.setDeviceEnabled(true);
} catch (JposException enablex) {
System.err.println("ERROR: Failed to enable device: " + enablex);
try {
this.scanner.close();
} catch (JposException closex) {
System.err.println("ERROR: Failed to close device: " + closex);
}
return false;
}
try {
this.scanner.setDataEventEnabled(true);
} catch (JposException datax) {
System.err.println("ERROR: Failed to enable Data Events: " + datax);
try {
this.scanner.close();
} catch (JposException closex) {
System.err.println("ERROR: Failed to close device: " + closex);
}
return false;
}
this.scanner.addDataListener(this);
return true;
}
Disable -> Release -> Close
Similar to the Open -> Claim -> Enable process, the Disable -> Release -> Close process is necessary to properly disconnect from the Scanner device. The UPOS Scanner class has methods for each of the steps to properly disconnect and the entire process should be encapsulated within a method similar to connectScanner. In this section, we will create a disconnectScanner method that facilitates this process. For the purpose of this tutorial, we will not be implementing any error handling outside of displaying the error that occurred, so there will be no need to return a value from this method.
public void disconnectScanner() {
}
The first thing that we need to do is remove our application as a Data listener. This will stop any calls to the dataOccured method in our application.
public void disconnectScanner() {
this.scanner.removeDataListener(this);
}
At this point, we will assume that the device is in the Device Enabled state. This means that the first thing that we will need to do is disable the device by calling the Scanner.setDeviceEnabled with false for the argument. This method can throw an exception, so we will need to surround it in a try .. catch clause. If an exception is thrown here, we will not return and instead carry on to the next step of the process.
try {
this.scanner.setDeviceEnabled(false);
} catch (JposException enablex) {
System.err.println("ERROR: Failed to disable device: " + enablex);
}
The device has now been returned to the Claimed state. The next step in the process is to release the device from our application. This is accomplished by calling the UPOS Scanner.release method. The release method can throw an exception, so we will need to surround it in a try .. catch clause. If an exception is thrown here, we will not return and instead carry on to the final step of the process.
try {
this.scanner.release();
} catch (JposException releasex) {
System.err.println("ERROR: Failed to release device: " + releasex);
}
The device has now been returned to the Open state. The final step in the process is to close the device from our application. This is accomplished by calling the UPOS Scanner.close method. The close method can throw an exception, so we will need to surround it in a try .. catch clause. If an exception is thrown here, we will not return as there is no further action to be taken.
try {
this.scanner.close();
} catch (JposException closex) {
System.err.println("ERROR: Failed to close device: " + closex);
}
Now we have a method to properly disconnect from the device. The error handling is very simple, but suffices for the purpose of this tutorial. Here is the entire disconnectScanner method:
public void disconnectScanner() {
this.scanner.removeDataListener(this);
try {
this.scanner.setDeviceEnabled(false);
} catch (JposException enablex) {
System.err.println("ERROR: Failed to disable device: " + enablex);
}
try {
this.scanner.release();
} catch (JposException releasex) {
System.err.println("ERROR: Failed to release device: " + releasex);
}
try {
this.scanner.close();
} catch (JposException closex) {
System.err.println("ERROR: Failed to close device: " + closex);
}
}
Data Events
A Data Event is produced each time data is received from the Scanner device. For the purpose of this tutorial, we are going to concentrate on gathering Label events and ignore all other types of Data events. We have already implemented the dataOccurred event as part of implementing the DataListener interface. Each time a Label is scanned using a Scanner device, a Data Event occurs, and our dataOccurred method is called.
With Label data, we are concerned with three pieces of data: the raw Label data, the decoded Label data and the Label type. The UPOS Scanner class provides three methods to obtain this data. Scanner.getScanData returns a byte array containing the raw Label data. Scanner.getScanDataLabel returns a byte array containing the decoded Label data. Scanner.getScanDataType returns an integer denoting the symbology used to encode the Label data.
We will now expand our dataOccurred method by adding local variables to hold the potential label data during each Data Event. Add the following to your dataOccurred method:
public void dataOccurred(DataEvent de) {
byte[] scanData = new byte[]{};
byte[] scanDataLabel = new byte[]{};
int scanDataType = -1;
}
The three methods used to populate our local variables are each methods that can throw an exception. It is usually not best practice to group statements into a single try .. catch clause, in this particular case, we will be grouping the three calls into a single try .. catch for brevity and clarity.
try {
scanData = this.scanner.getScanData();
scanDataLabel = this.scanner.getScanDataLabel();
scanDataType = this.scanner.getScanDataType();
} catch (JposException scannerx) {
System.err.println("ERROR: JPOS Exception during data event: " + scannerx);
}
At this point, any sort of Data event may have been triggered. Calling each of these three methods will return a value regardless of whether a label event occurred. We are only interested in Scanned Label Events for the purpose of this tutorial, so we will test the length of the local scanData byte array to determine if we have any scan data, and display the information that was returned from the Scanner to the user. Add the following to your dataOccurred method:
if (scanData.length > 0) {
String sData = new String(scanData);
String sLabel = new String(scanDataLabel);
System.out.println("Raw Data: " + sData + ", Label Data: " + sLabel +
", Type: " + scanDataType);
}
Now when a label is scanned, our event handler will output the data from the label. By default, the Data Event Enable is automatically set to false each time a Data Event is delivered. In order to get another label to read, we will need to set the Data Event Enabled to true again. Each time a data event is delivered, we need to repeat this process. As before, the Scanner.setDataEventEnabled can throw an exception, so we will surround it in a try .. catch clause.
try {
this.scanner.setDataEventEnabled(true);
} catch (JposException datax) {
System.err.println("ERROR: Failed to enable Data Events. " + datax);
}
Now our event handler displays label events for the user and enables Data Events for another label read. The complete dataOccurred method is here for reference:
public void dataOccurred(DataEvent de) {
byte[] scanData = new byte[]{};
byte[] scanDataLabel = new byte[]{};
int scanDataType = -1;
try {
scanData = this.scanner.getScanData();
scanDataLabel = this.scanner.getScanDataLabel();
scanDataType = this.scanner.getScanDataType();
} catch (JposException scannerx) {
System.err.println("ERROR: JPOS Exception during data event: " + scannerx);
}
if (scanData.length > 0) {
String sData = new String(scanData);
String sLabel = new String(scanDataLabel);
System.out.println("Raw Data: " + sData + ", Label Data: " + sLabel +
", Type: " + scanDataType);
}
try {
this.scanner.setDataEventEnabled(true);
} catch (JposException datax) {
System.err.println("ERROR: Failed to enable Data Events. " + datax);
}
}
The main method
Now it is time to tie everything together into a working console application. We will now add a main method to our class to allow this tutorial to be launched from the command line. For the purpose of this tutorial, we will be creating a simple loop waiting for the user to press the Enter key. Until the user has pressed this key, the application will continue to read and display label events. Add a main method to your Tutorial class as follows:
public static void main(String[] args) {
Tutorial tutorial = new Tutorial();
}
As mentioned previously, we will be using the DL-Gryphon-GD4430-USB-OEM profile from the default jpos.xml file that is distributed with JavaPOS. If you have a different device that you are writing for, change this to the name of the profile that you are using in your jpos.xml.
public static void main(String[] args) {
Tutorial tutorial = new Tutorial();
String profileName = "DL-Gryphon-GD4430-USB-OEM";
}
The next step is to connect to the scanner. We will be testing the boolean returned by the Tutorial.connectScanner method to determine whether the device is connected before proceeding. If the device does not connect, there is no reason to continue executing, so we will simply exit if Tutorial.connectScanner fails.
if (!tutorial.connectScanner(profileName)) {
System.exit(1);
}
Using a BufferedReader
For our main loop, we will be using a BufferedReader instance to read from an InputStreamReader instance that is attached to System.in. This will allow us to read input from the console and determine when to stop executing. In order to use these classes, the following imports will need to be added to the Tutorial class:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
Now we will add our loop to the main method after our call to Tutorial.connect.
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (true) {
System.out.println("Press Enter to exit.");
try {
reader.readLine();
} catch (IOException iox) {
System.err.println("ERROR: I/O Exception reading from System.in: " + iox);
}
}
Once the user has pressed Enter and is exiting the program, we will need to go ahead and disconnect from the scanner. Add the following to your main method:
tutorial.disconnectScanner();
System.out.println("Disconnected from device.");
System.exit(0);
Here is the completed Tutorial.main method:
public static void main(String[] args) {
Tutorial tutorial = new Tutorial();
String profileName = "DL-Gryphon-GD4430-USB-OEM";
if (!tutorial.connectScanner(profileName)) {
System.exit(1);
}
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (true) {
System.out.println("Press Enter to exit.");
try {
reader.readLine();
} catch (IOException iox) {
System.err.println("ERROR: I/O Exception reading from System.in: " + iox);
}
}
tutorial.disconnectScanner();
System.out.println("Disconnected from device.");
System.exit(0);
}
Summary
We have now created a simple application that will allow a user to connect to a Scanner device, display Label events until the user presses Enter and then disconnect from the Scanner device. This is, of course, a very minimal example and does not cover the full capabilities of the Datalogic JavaPOS API, however, it serves as an excellent starting point for applications that use the Datalogic JavaPOS API.