java-8event-listenerjssc

How to retrieve the value from the event listener from another class in java?


I have a program to get the values from a Bar code scanner(using jssc library) and it returns value as expected using event listener but I need to access that value from another class.

I tried to instantiate BarcodeScanner class to main method (ProcessTicket class) and called scannerHandler method and also called the getter method of model class to retrieve value but the getter method runs before the scan is completed. Could you please help me to achieve this?

    public class BarcodeScanner {
            public static Object SerialPortReader;
            static SerialPort serialPort;
         public void scannerHandler() {
                serialPort = new SerialPort("COM4");      
    try{
       serialPort.openPort();//Open serial port
      //Set params. Also set params by this string: 
       serialPort.setParams(9600, 8, 1, 0);
       serialPort.setParams(9600, 8, 1, 0);
       serialPort.writeString(new String(new byte[]{0x02})); //triggers barcode scanner
       serialPort.addEventListener(new SerialPortReader());//Add SerialPortEventListenerS
    } catch (SerialPortException ex) {
                    System.out.println(ex);
                }
            }

        public static class SerialPortReader implements SerialPortEventListener {
                String str;
                String value;

                public void serialEvent(SerialPortEvent event) {
                    if (event.isRXCHAR() && event.getEventValue() > 0) {//If data is available
                        //Check bytes count in the input buffer
                        try {
                            byte[] bytesCont = serialPort.readBytes(14);
                            str = new String(bytesCont);
                            ModelClass modelClass = new ModelClass();
                            modelClass.setBarcodeValue(str);
                        } catch (SerialPortException e) {
                            e.printStackTrace();
                        }
                    }
        }

My ProcessTicket.java Class

public class ProcessTicket {
    public static void main(String[] args) throws SQLException, SerialPortException {
 BarcodeScanner bSC = new BarcodeScanner();
        bSC.scannerHandler();
       BarcodeScanner.SerialPortReader portReader = new BarcodeScanner.SerialPortReader();
       ModelClass modelClass = new ModelClass();
       String value = modelClass.getBarcodeValue();
       System.out.println(value);
    }
}

Solution

  • The main problem here is that you're treating an inherently asynchronous operation (reading from an external sensor in the real world) as if it's synchronous.

    I simulated that external sensor stuff to make a standalone app that tests your business logic:

    HowToRetrieveTheValueFromTheEventListenerFromAnotherClassInJava.java

    package com.stackoverflow;
    
    /**
     * https://stackoverflow.com/questions/57452205/how-to-retrieve-the-value-from-the-event-listener-from-another-class-in-java
     */
    public class HowToRetrieveTheValueFromTheEventListenerFromAnotherClassInJava {
    
        public static void main(String[] args) {
    
            BarcodeScanner barcodeScanner = new BarcodeScanner((String barcode) -> {
    
                System.out.println("Barcode scanned: " + barcode);
            });
    
            barcodeScanner.startScan();
    
            MockUser.startScanningStuffLol();
        }
    }
    

    That call to MockUser.startScanningStuffLol() is only necessary because I'm testing this just in code, without using a real barcode scanner. Please don't focus on it. I'll post its implementation if you ask, but otherwise I'm assuming that your OS/Java/hardware are working the way they were designed to work, and you can just test this with those tools instead of my MockUser software simulation.

    Here are the rest of the classes that you need to implement this:

    BarcodeScannedCallback.java

    package com.stackoverflow;
    
    public interface BarcodeScannedCallback {
    
        void callback(String barcode);
    }
    

    Since we're dealing with an asynchronous operation, we can't just start it and then check for a return value, like we would with a synchronous operation. Instead, we need to pass in a function that will be called once the operation is complete, and just wait for it to finish. BarcodeScannedCallback is the signature of that function (in other words, a description of how that function needs to be structured). It takes one string parameter, and returns nothing.

    The implementation of BarcodeScannedCallback is this function that I've already mentioned above, which I'm passing into the BarcodeScanner constructor:

    (String barcode) -> {
        System.out.println("Barcode scanned: " + barcode);
    }
    

    As you can see, this function takes one string parameter, and returns nothing. So, it's an implementation of the BarcodeScannedCallback interface.

    Now for the last class: the one that bridges our main method and the serial port, using the above interface.

    BarcodeScanner.java

    package com.stackoverflow;
    
    public class BarcodeScanner implements SerialPortEventListener {
    
        private SerialPort serialPort;
    
        private final BarcodeScannedCallback callback;
    
        public void startScan() {
    
            try {
    
                serialPort = new SerialPort("COM4");
                serialPort.openPort();
    
                serialPort.addEventListener(this);
    
                // Also you can set params by this string: serialPort.setParams(9600, 8, 1, 0);
                serialPort.setParams(9600, 8, 1, 0);
    
                // Triggers barcode scanner.
                serialPort.writeString(new String(new byte[]{0x02}));
    
            } catch (SerialPortException ex) {
    
                System.out.println(ex);
            }
        }
    
        @Override
        public void serialEvent(SerialPortEvent event) {
    
            boolean isDataAvailable = event.isRXCHAR() && event.getEventValue() > 0;
    
            if (isDataAvailable) {
    
                try {
    
                    byte[] bytesCont = serialPort.readBytes(14);
                    String barcode = new String(bytesCont);
    
                    callback.callback(barcode);
    
                } catch (SerialPortException ex) {
    
                    System.out.println(ex);
                }
            }
        }
    
        public BarcodeScanner(BarcodeScannedCallback callback) {
    
            this.callback = callback;
        }
    }
    

    So here's the full lifecycle of these events:

    When I run this (with my MockUser class setting up a background thread that "scans" a barcode every 3 seconds), I get this output:

    Barcode scanned: 420L0L
    Barcode scanned: 007
    Barcode scanned: 12345
    

    In your case, you should be able to scan 3 barcodes with your real-world barcode scanner, and get the same results.

    Note that you may need to do something to keep the main method's thread from ending prematurely, depending on the context that you're running this in.

    If you're running it in an Android app or a web server, those frameworks keep their main thread running indefinitely, until you kill the app/server.

    But if you're running it as a custom command-line app (which it seems like you're doing, based on the existence of a main method), you will need to do something to keep it alive until you decide to kill it. The simplest way is to put an infinite loop like while (true); on the last line of your main method.