KeyBoardDriver JavaPOS changes.
authorGustavo Martin Morcuende <gu.martinm@gmail.com>
Sun, 5 Aug 2012 21:52:03 +0000 (23:52 +0200)
committerGustavo Martin Morcuende <gu.martinm@gmail.com>
Sun, 5 Aug 2012 21:52:03 +0000 (23:52 +0200)
ThreadFireEvent
KeyBoardDeviceLinux

JavaPOS/KeyBoardDriver/src/main/java/de/javapos/example/MyPOSKeyboard.java
JavaPOS/KeyBoardDriver/src/main/java/de/javapos/example/ThreadFireEvent.java
JavaPOS/KeyBoardDriver/src/main/java/de/javapos/example/hardware/KeyBoardDeviceLinux.java
JavaPOS/KeyBoardDriver/src/main/java/de/javapos/example/queue/JposEventQueue.java
JavaPOS/KeyBoardDriver/src/main/java/de/javapos/example/queue/JposEventQueueImpl.java

index b7e8569..9988d9a 100644 (file)
@@ -19,7 +19,7 @@ import jpos.events.JposEvent;
  
 public class MyPOSKeyboard implements POSKeyboardService112, JposConst, POSKeyboardConst
 {
-       private static final int deviceVersion12  = 1002000;
+       private static final int deviceVersion112  = 1012000;
        private String logicalname;
        private EventCallbacks callbacks;
        private JposEntryRegistry jposEntryRegistry;
@@ -172,7 +172,7 @@ public class MyPOSKeyboard implements POSKeyboardService112, JposConst, POSKeybo
 
        @Override
        public int getDeviceServiceVersion() throws JposException {
-               return MyPOSKeyboard.deviceVersion12;
+               return MyPOSKeyboard.deviceVersion112;
        }
 
        @Override
index d965d96..0619033 100644 (file)
@@ -10,80 +10,96 @@ import jpos.services.EventCallbacks;
 import de.javapos.example.queue.JposEventQueue;
 
 public class ThreadFireEvent extends Thread {
-       private boolean isDataEventEnabled = true;
-       private final JposEventQueue jposEventQueue;
-       private final EventCallbacks eventCallbacks;
-       private final ThreadGate freezeEventsGate = new ThreadGate();
-       
-       public ThreadFireEvent (JposEventQueue jposEventQueue, 
-                                     boolean isDataEventEnabled, EventCallbacks  eventCallbacks) {
-               this.jposEventQueue = jposEventQueue;
-               this.eventCallbacks = eventCallbacks;
-       }
-       
-       public void run () {
-               JposEvent jposEvent = null;
-               
-               while (true) {
-                       if (this.jposEventQueue.getNumberOfEvents() != 0) {
+    private boolean isDataEventEnabled = true;
+    private final JposEventQueue jposEventQueue;
+    private final EventCallbacks eventCallbacks;
+    private final ThreadGate freezeEventsGate = new ThreadGate();
 
-                               try {
-                                       this.freezeEventsGate.await();
-                               } catch (InterruptedException e1) {
-                                       //We are implementing the interruption policy in this class
-                                       //for this thread. So, this is permitted.
-                                       //End of thread.
-                                       break;
-                               }
+    public ThreadFireEvent(JposEventQueue jposEventQueue,
+            boolean isDataEventEnabled, EventCallbacks eventCallbacks) {
+        this.jposEventQueue = jposEventQueue;
+        this.eventCallbacks = eventCallbacks;
+    }
 
-                               //PROBLEMA: si DataEventEnabled es false y lo unico que hay en la cola son eventos de datos
-                               //me meto en un bucle que va a dejar la CPU frita... :/
-                               if (this.isDataEventEnabled) {
-                                       try {
-                                               jposEvent = this.jposEventQueue.getEvent();
-                                       } catch (InterruptedException e) {
-                                               //We are implementing the interruption policy in this class
-                                               //for this thread. So, this is permitted.
-                                               //End of thread.
-                                               break;
-                                       }
-                               }
-                               else {
-                                       //TODO: Buscar eventos que no sean DataEvent o ErrorEvent
-                               }
-                       }
-                       
-                       
-                       if (jposEvent instanceof DataEvent) {
-                               this.eventCallbacks.fireDataEvent((DataEvent)jposEvent);
-                               //TODO: synchronized?
-                               this.isDataEventEnabled = false;
-                       }
-                       if (jposEvent instanceof ErrorEvent) {
-                               this.eventCallbacks.fireErrorEvent((ErrorEvent)jposEvent);
-                       }
-                       if (jposEvent instanceof StatusUpdateEvent) {
-                               this.eventCallbacks.fireStatusUpdateEvent((StatusUpdateEvent)jposEvent);
-                       }
-                       if (jposEvent instanceof DirectIOEvent) {
-                               this.eventCallbacks.fireDirectIOEvent((DirectIOEvent)jposEvent);
-                       }
-                       if (jposEvent instanceof OutputCompleteEvent) {
-                               this.eventCallbacks.fireOutputCompleteEvent((OutputCompleteEvent)jposEvent);
-                       }
-               }
-       }
+    @Override
+    public void run() {
 
-       public void setFreezeEvents (boolean freezeEvents) {
-               if (freezeEvents) {
-                       this.freezeEventsGate.close();
-               }
-               else {
-                       this.freezeEventsGate.open();
-               }
-       }
+        while (!this.isInterrupted()) {
 
-       public void setDataEventEnabled (boolean dataEventEnabled) {
-               this.isDataEventEnabled = dataEventEnabled;
-       }
+            JposEvent event;
+
+            try {
+                event = this.jposEventQueue.peekEvent();
+            } catch (InterruptedException e) {
+                // We are implementing the interruption policy in this class
+                // for this thread. So, this is permitted.
+                // End of thread.
+                break;
+            }
+
+            try {
+                this.freezeEventsGate.await();
+            } catch (InterruptedException e) {
+                // We are implementing the interruption policy in this class
+                // for this thread. So, this is permitted.
+                // End of thread.
+                break;
+            }
+
+            // PROBLEMA: si DataEventEnabled es false y lo unico que hay en la
+            // cola son eventos de datos
+            // me meto en un bucle que va a dejar la CPU frita... :/
+            if (!this.isDataEventEnabled) {
+                if (((event instanceof DataEvent))
+                        || (((event instanceof ErrorEvent)) && (((ErrorEvent) event)
+                                .getErrorLocus() != 1))) {
+                    // TODO: Buscar eventos que no sean DataEvent o ErrorEvent
+                }
+            }
+
+            fireEvent(event);
+        }
+    }
+
+    private void fireEvent(JposEvent jposEvent) {
+        try {
+            jposEvent = this.jposEventQueue.getEvent();
+        } catch (InterruptedException e) {
+            // restore interrupt status.
+            this.interrupt();
+            return;
+        }
+
+        if (jposEvent instanceof DataEvent) {
+            this.eventCallbacks.fireDataEvent((DataEvent) jposEvent);
+            // TODO: synchronized?
+            this.isDataEventEnabled = false;
+        }
+        if (jposEvent instanceof ErrorEvent) {
+            this.eventCallbacks.fireErrorEvent((ErrorEvent) jposEvent);
+        }
+        if (jposEvent instanceof StatusUpdateEvent) {
+            this.eventCallbacks
+                    .fireStatusUpdateEvent((StatusUpdateEvent) jposEvent);
+        }
+        if (jposEvent instanceof DirectIOEvent) {
+            this.eventCallbacks.fireDirectIOEvent((DirectIOEvent) jposEvent);
+        }
+        if (jposEvent instanceof OutputCompleteEvent) {
+            this.eventCallbacks
+                    .fireOutputCompleteEvent((OutputCompleteEvent) jposEvent);
+        }
+    }
+
+    public void setFreezeEvents(boolean freezeEvents) {
+        if (freezeEvents) {
+            this.freezeEventsGate.close();
+        } else {
+            this.freezeEventsGate.open();
+        }
+    }
+
+    public void setDataEventEnabled(boolean dataEventEnabled) {
+        this.isDataEventEnabled = dataEventEnabled;
+    }
 }
index d8e1d4d..2c7edd8 100644 (file)
@@ -8,13 +8,17 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.lang.Thread.UncaughtExceptionHandler;
 import java.nio.channels.Channels;
+import java.nio.channels.ClosedByInterruptException;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
 import java.nio.channels.OverlappingFileLockException;
 import java.util.concurrent.RejectedExecutionException;
+
 import jpos.JposConst;
 import jpos.JposException;
+
 import org.apache.log4j.Logger;
+
 import de.javapos.example.annotation.GuardedBy;
 import de.javapos.example.queue.JposEventListener;
 
@@ -22,427 +26,513 @@ import de.javapos.example.queue.JposEventListener;
  * Not thread-safe (WORKING ON IT)
  * 
  * @author
- *
+ * 
  */
 public class KeyBoardDeviceLinux implements BaseKeyBoardDriver {
-       private static final Logger logger = Logger.getLogger(KeyBoardDeviceLinux.class);
-       //value EV_KEY from include/linux/input.h
-       private static final int EV_KEY = 1;
-       private static final String javaposKeyBoardLock = "/tmp/javaposKeyBoardLock";
-       //No me gusta nada esto, simplemente es para evitar mirar si thread es null la primera vez en el metodo enable...
-       private Thread thread = new Thread ("KeyBoardDeviceLinux-Thread");
-       private String deviceName;
-       @GuardedBy("this") private FileChannel fileChannelLock;
-       @GuardedBy("this") private DataInputStream device;
-       @GuardedBy("this") private boolean isClaimed;
-       @GuardedBy("this") private boolean isEnabled;
-       //Usando volatile porque cumplo las condiciones de uso. Ver Java Concurrency in Practice PAG????
-       private volatile boolean autoDisable;
-       private JposEventListener eventListener;
-       @GuardedBy("this") private FileLock lock;
-       
-       @Override
-       public boolean isOpened() {
-               return false;
-       }
-
-       @Override
-       public synchronized void close() throws JposException {
-               this.disable();
-               this.release();
-       }
-
-       /**
-        * Claim device.
-        *
-        * <p>
-        * <b>Thread-safe</b>
-        * </p>
-        */
-       public synchronized void claim(int time) throws JposException {
-               FileLock lock = null;
-               FileChannel fileChannelLock = null;
-
-
-               if (this.isClaimed) {
-                       return;
-               }
-
-               try {
-                       fileChannelLock = new FileOutputStream(javaposKeyBoardLock).getChannel();
-               } catch (FileNotFoundException e) {
-                       throw new JposException(JposConst.JPOS_E_NOTCLAIMED, "File not found.",e);
-               }
-
-               if (time == -1) {
-                       //Espera activa. ¿Alguna idea de cómo hacer esto sin espera activa?  :(
-                       while(true) {
-                               try {
-                                       if ((lock = fileChannelLock.tryLock()) != null) {
-                                               break;
-                                       }
-                                       //Release monitor
-                                       this.wait(250);
-                               //I do not like catching RunTimeExceptions but I have no choice...
-                               } catch (OverlappingFileLockException e) {
-                                       closeFileLock(fileChannelLock);
-                                       logger.warn("File already locked or more than one instances of the " +
-                                                                       "KeyBoardDeviceLinux JavaPOS driver.",e);
-                                       //Nothing to do. File already locked (claimed device)
-                                       //or you are running more than one instances of this driver at the
-                                       //same time and in the same Java process, which is not supported.
-                                       return;
-                               } catch (IOException e) {
-                                       closeFileLock(fileChannelLock);
-                                       throw new JposException(JposConst.JPOS_E_CLAIMED, "Error while trying to claim device.",e);
-                               } catch(InterruptedException e) {
+    private static final Logger logger = Logger
+            .getLogger(KeyBoardDeviceLinux.class);
+    // value EV_KEY from include/linux/input.h
+    private static final int EV_KEY = 1;
+    private static final String javaposKeyBoardLock = "/tmp/javaposKeyBoardLock";
+    // No me gusta nada esto, simplemente es para evitar mirar si thread es null
+    // la primera vez en el metodo enable...
+    private Thread thread = new Thread("KeyBoardDeviceLinux-Thread");
+    private String deviceName;
+    @GuardedBy("this")
+    private FileChannel fileChannelLock;
+    @GuardedBy("this")
+    private DataInputStream device;
+    @GuardedBy("this")
+    private boolean isClaimed;
+    @GuardedBy("this")
+    private boolean isEnabled;
+    // Usando volatile porque cumplo las condiciones de uso. Ver Java
+    // Concurrency in Practice PAG????
+    private volatile boolean autoDisable;
+    private JposEventListener eventListener;
+    @GuardedBy("this")
+    private FileLock lock;
+
+    @Override
+    public boolean isOpened() {
+        return false;
+    }
+
+    @Override
+    public synchronized void close() throws JposException {
+        this.disable();
+        this.release();
+    }
+
+    /**
+     * Claim device.
+     * 
+     * <p>
+     * <b>Thread-safe</b>
+     * </p>
+     */
+    @Override
+    public synchronized void claim(int time) throws JposException {
+        FileLock lock = null;
+        FileChannel fileChannelLock = null;
+
+        if (this.isClaimed) {
+            return;
+        }
+
+        try {
+            fileChannelLock = new FileOutputStream(javaposKeyBoardLock)
+                    .getChannel();
+        } catch (FileNotFoundException e) {
+            throw new JposException(JposConst.JPOS_E_NOTCLAIMED,
+                    "File not found.", e);
+        }
+
+        if (time == -1) {
+            // Espera activa. ¿Alguna idea de cómo hacer esto sin espera activa?
+            // :(
+            while (true) {
+                try {
+                    if ((lock = fileChannelLock.tryLock()) != null) {
+                        break;
+                    }
+                    // Release monitor
+                    this.wait(250);
+                    // I do not like catching RunTimeExceptions but I have no
+                    // choice...
+                } catch (OverlappingFileLockException e) {
+                    closeFileLock(fileChannelLock);
+                    logger.warn(
+                            "File already locked or more than one instances of the "
+                                    + "KeyBoardDeviceLinux JavaPOS driver.", e);
+                    // Nothing to do. File already locked (claimed device)
+                    // or you are running more than one instances of this driver
+                    // at the
+                    // same time and in the same Java process, which is not
+                    // supported.
+                    return;
+                } catch (IOException e) {
+                    closeFileLock(fileChannelLock);
+                    throw new JposException(JposConst.JPOS_E_CLAIMED,
+                            "Error while trying to claim device.", e);
+                } catch (InterruptedException e) {
                     closeFileLock(fileChannelLock);
-                    //restore interrupt status.
+                    // restore interrupt status.
                     Thread.currentThread().interrupt();
-                    throw new JposException(JposConst.JPOSERR, "Interrupt exception detected.", e);
+                    throw new JposException(JposConst.JPOSERR,
+                            "Interrupt exception detected.", e);
                 }
-                       }
-               }
-               else {
-                       long lastTime = System.nanoTime() + ((time + 250) * 1000000);
-                       //Espera activa. ¿Alguna idea de cómo hacer esto sin espera activa?  :(
-                       do {
-                               try {
-                                       if ((lock = fileChannelLock.tryLock()) != null) {
-                                               break;
-                                       }
-                                       //Release monitor
-                                       this.wait(250);
-                               //I do not like catching RunTimeExceptions but I have no choice...
-                               } catch (OverlappingFileLockException e) {
-                                       closeFileLock(fileChannelLock);
-                                       logger.warn("File already locked or more than one instances of the " +
-                                                                       "KeyBoardDeviceLinux JavaPOS driver.",e);
-                                       //Nothing to do. File already locked (claimed device)
-                                       //or you are running more than one instances of this driver at the
-                                       //same time and in the same Java process, which is not supported.
-                                       return;
-                               } catch (IOException e) {
-                                       closeFileLock(fileChannelLock);
-                                       throw new JposException(JposConst.JPOS_E_CLAIMED, "Error while trying to claim device.",e);    
-                } catch(InterruptedException e) {
+            }
+        } else {
+            long lastTime = System.nanoTime() + ((time + 250) * 1000000);
+            // Espera activa. ¿Alguna idea de cómo hacer esto sin espera activa?
+            // :(
+            do {
+                try {
+                    if ((lock = fileChannelLock.tryLock()) != null) {
+                        break;
+                    }
+                    // Release monitor
+                    this.wait(250);
+                    // I do not like catching RunTimeExceptions but I have no
+                    // choice...
+                } catch (OverlappingFileLockException e) {
+                    closeFileLock(fileChannelLock);
+                    logger.warn(
+                            "File already locked or more than one instances of the "
+                                    + "KeyBoardDeviceLinux JavaPOS driver.", e);
+                    // Nothing to do. File already locked (claimed device)
+                    // or you are running more than one instances of this driver
+                    // at the
+                    // same time and in the same Java process, which is not
+                    // supported.
+                    return;
+                } catch (IOException e) {
+                    closeFileLock(fileChannelLock);
+                    throw new JposException(JposConst.JPOS_E_CLAIMED,
+                            "Error while trying to claim device.", e);
+                } catch (InterruptedException e) {
                     closeFileLock(fileChannelLock);
-                    //restore interrupt status.
+                    // restore interrupt status.
                     Thread.currentThread().interrupt();
-                    throw new JposException(JposConst.JPOSERR, "Interrupt exception detected.", e);
+                    throw new JposException(JposConst.JPOSERR,
+                            "Interrupt exception detected.", e);
                 }
-                       } while(System.nanoTime() <= lastTime);
-                       
-                       if (lock == null) {
-                               //Time out
-                               closeFileLock(fileChannelLock);
-                               throw new JposException(JposConst.JPOS_E_TIMEOUT, "Timeout while trying to claim device.");
-                       }
-               }
-               
-               this.lock = lock;
-               this.fileChannelLock = fileChannelLock;
-               this.isClaimed = true;
-       }
-
-       private void closeFileLock(FileChannel fileChannelLock ) {
-               try {
-                       fileChannelLock.close();
-               } catch (IOException e) {
-                       logger.warn("Error while closing the file lock", e);
-               }
-       }
-
-       /**
-        * Release device.
-        *
-        * <p>
-        * Thread-safe.
-        * </p>
-        */
-       @Override
-       public synchronized void release() throws JposException {
-
-               if (!this.isClaimed) {
-                       return;
-               }
-               
-               try {
-                       this.lock.release();
-                       this.fileChannelLock.close();
-               } catch (IOException e) {
-                       throw new JposException(JposConst.JPOSERR, "Error when closing the keyboard file lock", e);
-               }
-
-               this.isClaimed = false;
-       }
-
-       @Override
-       public synchronized boolean isClaimed() {
-               return this.isClaimed;
-       }
-
-       /**
-        * Not thread-safe.!!!!!
-        * 
-        * NO OLVIDAR try/finally PARA DEJAR EL DISPOSITIVO CORRECTAMENTE
-        * @throws JposException
-        * @throws RejectedExecutionException if this task cannot be
-     * accepted for execution. CUIDADO RUNTIME NO OLVIDAR try/finally PARA DEJAR BIEN EL DISPOSITIVO
-        */
-       @Override
-       public synchronized void enable() throws JposException {
-               if (this.isEnabled) {
-                       return;
-               }
-               if (this.deviceName == null) {
-                       throw new JposException(JposConst.JPOSERR, "There is not an assigned device", 
-                                                                               new NullPointerException("The deviceName field has null value"));
-               }
-
-               //Esto mola porque me ahorro una variable indicando si el dispositivo fue habilitado o no
-               //el problema es que puede darse el caso que se ejecute esto:
-               //open();
-               //claim();<--- El teclado no es un dispostivo compartido (var la especificacion JavaPOS)
-               //enable;
-               //disable();
-               //enable();
-               //Y en segundo enable (aqui) se vea el hilo todavía vivo (temas del planificador del SO) y lanze excepcion de que 
-               //el dispositivo no ha sido deshabilitado cuando realmente sí lo ha sido.  :(
-               //Ese no puede ser el comportamiento esperado... Luego esto estaba gracioso porque me evitaba tener
-               //que usar una variable compartida llamada isEnable pero no es válido. :(
-               //Reahacer esto y usar unicamente una variable compartida llamada isEnable para saber si el
-               //dispostivo fue o no deshabilitado y dejar de fijarme en el estado del hilo.
-               //Espera!!! ¿Y si en disable me espero a que termine el hilo? Entonces esto funcionaría guay.
-               //Por narices el hilo va a terminar tal y como lo he hecho así que no habria problema :)
-               //Esa es la solucion adecuada!!! Aunque el disable ahora tarde un poco mas hace mi aplicacion más robusta
-               //¿Hay algún inconveniente mas a parte de hacer que el disable sea algo más lento? tardara el tiempo
-               //que tarde en morir el hilo.
-               //PROBLEMA OTRA VEZ, ¿y si el hilo murio por alguna cosa inesperada como por ejemplo que el archivo del 
-               //dispositivo del SO se borro o cualquier otra cosa? Aqui veria el hilo muerto pero en realidad el
-               //dispositivo no fue deshabilitado si no que el hilo murio de forma inesperada Y NO SE CERRO this.device!!!... 
-               //Si soluciono este ultimo escollo ¿sí me podre basar en si el hilo está o no muerto?
-               if (!this.thread.isAlive()) {
-                       try {
-                               this.device = new DataInputStream(Channels.newInputStream(new FileInputStream(this.deviceName).getChannel()));
-                       } catch (FileNotFoundException e) {
-                               throw new JposException(JposConst.JPOS_E_NOHARDWARE, "Device not found.",e);
-                       }
-
-                       Runnable task = new Runnable () {
-
-                               @Override
-                               public void run() {
-                                       //Hidden pointer: KeyBoardDeviceLinux.this.runBatchTask();
-                                       runBatchTask();
-                               }
-
-                       };
-                       this.thread = new Thread (task, "KeyBoardDeviceLinux-Thread");
-                       this.thread.setUncaughtExceptionHandler(new DriverHWUncaughtExceptionHandler());
-                       this.thread.start();
-                       this.isEnabled = true;
-               }
-       }
-
-       @Override
-       public synchronized void disable() throws JposException {
-
-               if (!this.isEnabled) {
-                       return;
-               }
-
-               //This method releases the Java NIO channel. It is thread safety. :) see: Interruptible
-               this.thread.interrupt();
-               try {
-                       this.thread.join();
-               } catch (InterruptedException e) {
-                       //restore interrupt status.
-                       Thread.currentThread().interrupt();
-                       throw new JposException(JposConst.JPOSERR, "Interrupt exception detected.", e);
-               }
-
-               this.isEnabled = false;
-       }
-
-       @Override
-       public synchronized boolean isEnabled() {
-               return this.isEnabled;
-       }
-
-       @Override
-       public void autoDisable(boolean autoDisable) {
-               this.autoDisable = autoDisable;
-       }
-
-
-       @Override
-       public void addEventListener(JposEventListener jposEventListener) throws JposException {
-               this.eventListener = jposEventListener;
-       }
-
-       @Override
-       public void removeEventListener(JposEventListener jposEventListener) {
-               this.eventListener = null;
-       }
-
-       @Override
-       public boolean write(byte[] paramArrayOfByte, int paramInt1, int paramInt2,
-                       int paramInt3) throws JposException {
-               return false;
-       }
-
-       @Override
-       public int read(byte[] paramArrayOfByte, int paramInt1, int paramInt2,
-                       int paramInt3) throws JposException {
-               return 0;
-       }
-
-       @Override
-       public int writeRead(byte[] paramArrayOfByte1, int paramInt1,
-                       int paramInt2, byte[] paramArrayOfByte2, int paramInt3,
-                       int paramInt4, int paramInt5) throws JposException {
-               return 0;
-       }
-
-       @Override
-       public String getDescription(int paramInt) {
-               return null;
-       }
-
-       @Override
-       public void flush(int paramInt) throws JposException {
-
-       }
-       
-       
-       /**
-        * 
-        * NO OLVIDAR try/finally PARA DEJAR EL DISPOSITIVO CORRECTAMENTE
-        */
-       @Override
-       public void device(String device) {
-               this.deviceName = device;
-       }
-
-
-       private void runBatchTask() {
-               //OS 64 bits timeval 16 bytes  -> struct input_event 24 bytes
-               //OS 32 bits timeval 8 bytes -> struct input_event 16 bytes
-               byte []buffer = new byte[16];
-               //byte []buffer = new byte[24];
-               short code = 0;
-               short type = 0;
-               int value = 0;
-               int ch1 = 0;
-               int ch2 = 0;
-               int ch3 = 0;
-               int ch4 = 0;
-               
-               try {
-                       while (true) {
-                               //using command: evtest /dev/input/event4
-                               //It is not clear hear but I am using sun.nio.ch.FileChannelImpl.read(ByteBuffer ) method which throws
-                               //exception (java.nio.channels.ClosedByInterruptException) when the thread is interrupted
-                               //see: Thread.interrupt(), sun.nio.ch.FileChannelImpl.read(ByteBuffer) and 
-                               //java.nio.channels.spi.AbstractInterruptibleChannel.begin() methods
-                               //Basically before getting in an I/O operation that might block indefinitely the begin method implements
-                               //the Thread.blocker field. This field is used when running the method Thread.interrupt(). The blocker
-                               //implementation closes the file descriptor, so the I/0 operation throws an IOException (closed file)
-                               //In the finally block of the sun.nio.ch.FileChannelImpl.read(ByteBuffer) method we can find the
-                               //java.nio.channels.spi.AbstractInterruptibleChannel.end(boolean) method which throws the java.nio.channels.ClosedByInterruptException
-                               //We are losing the IOException (because the file was closed by the blocker implementation) but indeed
-                               //the reality is that we are getting out of the I/O blocking operation because we interrupted the running thread. 
-                               this.device.readFully(buffer);
-                               
-                               ch1 = buffer[11];
-                       ch2 = buffer[10];
-                       //ch1 = buffer[19];
-                       //ch2 = buffer[18];
-                       code = (short)((ch1 << 8) + (ch2 << 0));
-                       
-                       ch1 = buffer[9];
-                       ch2 = buffer[8];
-                       //ch1 = buffer[17];
-                       //ch2 = buffer[16];
-                       type = (short)((ch1 << 8) + (ch2 << 0));
-                       
-                       
-                       ch1 = buffer[15];
-                       ch2 = buffer[14];
-                       ch3 = buffer[13];
-                       ch4 = buffer[12];
-                       //ch1 = buffer[23];
-                       //ch2 = buffer[22];
-                       //ch3 = buffer[21];
-                       //ch4 = buffer[20];
-                       value = ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
-                       
-                       if (type == KeyBoardDeviceLinux.EV_KEY) {
-                    //Problema aqui con eventListener y que vea != null y luego justo dentro del if 
-                    //otro hilo me lo quite...
+            } while (System.nanoTime() <= lastTime);
+
+            if (lock == null) {
+                // Time out
+                closeFileLock(fileChannelLock);
+                throw new JposException(JposConst.JPOS_E_TIMEOUT,
+                        "Timeout while trying to claim device.");
+            }
+        }
+
+        this.lock = lock;
+        this.fileChannelLock = fileChannelLock;
+        this.isClaimed = true;
+    }
+
+    private void closeFileLock(FileChannel fileChannelLock)
+            throws JposException {
+        try {
+            fileChannelLock.close();
+        } catch (IOException e) {
+            throw new JposException(JposConst.JPOSERR,
+                    "Error while closing the file lock", e);
+        }
+    }
+
+    /**
+     * Release device.
+     * 
+     * <p>
+     * Thread-safe.
+     * </p>
+     */
+    @Override
+    public synchronized void release() throws JposException {
+
+        if (!this.isClaimed) {
+            return;
+        }
+
+        try {
+            this.lock.release();
+            this.fileChannelLock.close();
+        } catch (IOException e) {
+            throw new JposException(JposConst.JPOSERR,
+                    "Error when closing the keyboard file lock", e);
+        }
+
+        this.isClaimed = false;
+    }
+
+    @Override
+    public synchronized boolean isClaimed() {
+        return this.isClaimed;
+    }
+
+    /**
+     * Not thread-safe.!!!!!
+     * 
+     * NO OLVIDAR try/finally PARA DEJAR EL DISPOSITIVO CORRECTAMENTE
+     * 
+     * @throws JposException
+     * @throws RejectedExecutionException
+     *             if this task cannot be accepted for execution. CUIDADO
+     *             RUNTIME NO OLVIDAR try/finally PARA DEJAR BIEN EL DISPOSITIVO
+     */
+    @Override
+    public synchronized void enable() throws JposException {
+        if (this.isEnabled) {
+            return;
+        }
+        if (this.deviceName == null) {
+            throw new JposException(JposConst.JPOSERR,
+                    "There is not an assigned device",
+                    new NullPointerException(
+                            "The deviceName field has null value"));
+        }
+
+        // Esto mola porque me ahorro una variable indicando si el dispositivo
+        // fue habilitado o no
+        // el problema es que puede darse el caso que se ejecute esto:
+        // open();
+        // claim();<--- El teclado no es un dispostivo compartido (var la
+        // especificacion JavaPOS)
+        // enable;
+        // disable();
+        // enable();
+        // Y en segundo enable (aqui) se vea el hilo todavía vivo (temas del
+        // planificador del SO) y lanze excepcion de que
+        // el dispositivo no ha sido deshabilitado cuando realmente sí lo ha
+        // sido. :(
+        // Ese no puede ser el comportamiento esperado... Luego esto estaba
+        // gracioso porque me evitaba tener
+        // que usar una variable compartida llamada isEnable pero no es válido.
+        // :(
+        // Reahacer esto y usar unicamente una variable compartida llamada
+        // isEnable para saber si el
+        // dispostivo fue o no deshabilitado y dejar de fijarme en el estado del
+        // hilo.
+        // Espera!!! ¿Y si en disable me espero a que termine el hilo? Entonces
+        // esto funcionaría guay.
+        // Por narices el hilo va a terminar tal y como lo he hecho así que no
+        // habria problema :)
+        // Esa es la solucion adecuada!!! Aunque el disable ahora tarde un poco
+        // mas hace mi aplicacion más robusta
+        // ¿Hay algún inconveniente mas a parte de hacer que el disable sea algo
+        // más lento? tardara el tiempo
+        // que tarde en morir el hilo.
+        // PROBLEMA OTRA VEZ, ¿y si el hilo murio por alguna cosa inesperada
+        // como por ejemplo que el archivo del
+        // dispositivo del SO se borro o cualquier otra cosa? Aqui veria el hilo
+        // muerto pero en realidad el
+        // dispositivo no fue deshabilitado si no que el hilo murio de forma
+        // inesperada Y NO SE CERRO this.device!!!...
+        // Si soluciono este ultimo escollo ¿sí me podre basar en si el hilo
+        // está o no muerto?
+        if (!this.thread.isAlive()) {
+            try {
+                this.device = new DataInputStream(
+                        Channels.newInputStream(new FileInputStream(
+                                this.deviceName).getChannel()));
+            } catch (FileNotFoundException e) {
+                throw new JposException(JposConst.JPOS_E_NOHARDWARE,
+                        "Device not found.", e);
+            }
+
+            Runnable task = new Runnable() {
+
+                @Override
+                public void run() {
+                    // Hidden pointer: KeyBoardDeviceLinux.this.runBatchTask();
+                    runBatchTask();
+                }
+
+            };
+            this.thread = new Thread(task, "KeyBoardDeviceLinux-Thread");
+            this.thread
+                    .setUncaughtExceptionHandler(new DriverHWUncaughtExceptionHandler());
+            this.thread.start();
+            this.isEnabled = true;
+        }
+    }
+
+    @Override
+    public synchronized void disable() throws JposException {
+
+        if (!this.isEnabled) {
+            return;
+        }
+
+        // This method releases the Java NIO channel. It is thread safety. :)
+        // see: Interruptible
+        this.thread.interrupt();
+        try {
+            this.thread.join();
+        } catch (InterruptedException e) {
+            // restore interrupt status.
+            Thread.currentThread().interrupt();
+            throw new JposException(JposConst.JPOSERR,
+                    "Interrupt exception detected.", e);
+        }
+
+        this.isEnabled = false;
+    }
+
+    @Override
+    public synchronized boolean isEnabled() {
+        return this.isEnabled;
+    }
+
+    @Override
+    public void autoDisable(boolean autoDisable) {
+        this.autoDisable = autoDisable;
+    }
+
+    @Override
+    public void addEventListener(JposEventListener jposEventListener)
+            throws JposException {
+        this.eventListener = jposEventListener;
+    }
+
+    @Override
+    public void removeEventListener(JposEventListener jposEventListener) {
+        this.eventListener = null;
+    }
+
+    @Override
+    public boolean write(byte[] paramArrayOfByte, int paramInt1, int paramInt2,
+            int paramInt3) throws JposException {
+        return false;
+    }
+
+    @Override
+    public int read(byte[] paramArrayOfByte, int paramInt1, int paramInt2,
+            int paramInt3) throws JposException {
+        return 0;
+    }
+
+    @Override
+    public int writeRead(byte[] paramArrayOfByte1, int paramInt1,
+            int paramInt2, byte[] paramArrayOfByte2, int paramInt3,
+            int paramInt4, int paramInt5) throws JposException {
+        return 0;
+    }
+
+    @Override
+    public String getDescription(int paramInt) {
+        return null;
+    }
+
+    @Override
+    public void flush(int paramInt) throws JposException {
+
+    }
+
+    /**
+     * 
+     * NO OLVIDAR try/finally PARA DEJAR EL DISPOSITIVO CORRECTAMENTE
+     */
+    @Override
+    public void device(String device) {
+        this.deviceName = device;
+    }
+
+    private void runBatchTask() {
+        // OS 64 bits timeval 16 bytes -> struct input_event 24 bytes
+        // OS 32 bits timeval 8 bytes -> struct input_event 16 bytes
+        byte[] buffer = new byte[16];
+        // byte []buffer = new byte[24];
+        short code = 0;
+        short type = 0;
+        int value = 0;
+        int ch1 = 0;
+        int ch2 = 0;
+        int ch3 = 0;
+        int ch4 = 0;
+
+        try {
+            while (true) {
+                // using command: evtest /dev/input/event4
+                // It is not clear hear but I am using
+                // sun.nio.ch.FileChannelImpl.read(ByteBuffer ) method which
+                // throws
+                // exception (java.nio.channels.ClosedByInterruptException) when
+                // the thread is interrupted
+                // see: Thread.interrupt(),
+                // sun.nio.ch.FileChannelImpl.read(ByteBuffer) and
+                // java.nio.channels.spi.AbstractInterruptibleChannel.begin()
+                // methods
+                // Basically before getting in an I/O operation that might block
+                // indefinitely the begin method implements
+                // the Thread.blocker field. This field is used when running the
+                // method Thread.interrupt(). The blocker
+                // implementation closes the file descriptor, so the I/0
+                // operation throws an IOException (closed file)
+                // In the finally block of the
+                // sun.nio.ch.FileChannelImpl.read(ByteBuffer) method we can
+                // find the
+                // java.nio.channels.spi.AbstractInterruptibleChannel.end(boolean)
+                // method which throws the
+                // java.nio.channels.ClosedByInterruptException
+                // We are losing the IOException (because the file was closed by
+                // the blocker implementation) but indeed
+                // the reality is that we are getting out of the I/O blocking
+                // operation because we interrupted the running thread.
+                this.device.readFully(buffer);
+
+                ch1 = buffer[11];
+                ch2 = buffer[10];
+                // ch1 = buffer[19];
+                // ch2 = buffer[18];
+                code = (short) ((ch1 << 8) + (ch2 << 0));
+
+                ch1 = buffer[9];
+                ch2 = buffer[8];
+                // ch1 = buffer[17];
+                // ch2 = buffer[16];
+                type = (short) ((ch1 << 8) + (ch2 << 0));
+
+                ch1 = buffer[15];
+                ch2 = buffer[14];
+                ch3 = buffer[13];
+                ch4 = buffer[12];
+                // ch1 = buffer[23];
+                // ch2 = buffer[22];
+                // ch3 = buffer[21];
+                // ch4 = buffer[20];
+                value = ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
+
+                if (type == KeyBoardDeviceLinux.EV_KEY) {
+                    // Problema aqui con eventListener y que vea != null y luego
+                    // justo dentro del if
+                    // otro hilo me lo quite...
                     if (eventListener != null) {
                         eventListener.inputAvailable(code);
                     }
 
-                               logger.debug("Captured key " + "type: " + type + " code: " + code + " value: " + value);
+                    logger.debug("Captured key " + "type: " + type + " code: "
+                            + code + " value: " + value);
                     if (autoDisable) {
                         break;
-                               }
-                       }
-                       }
-               } catch (EOFException e) {
-                       logger.error("Something went really bad. Impossible end of file while reading keyboard device!!!!", e);
-               } catch (IOException e) {
-                       //We reach this code because the thread was interrupted (disable) or because there was an I/O error
-                       //logger.debug or logger.error if it was an error I am not going to log it... :/
-                       logger.debug("Finished KeyBoardDeviceLinux Thread", e);
-               } finally {
-                       //This method releases the Java NIO channels (this.device). It is thread safety :) see: Interruptible 
-                       this.thread.interrupt();
-                       this.device = null;   //¿Realmente esto es necesario? (no lo creo  :/ )
-                               //SI EL HILO MUERE ¿ESTOY DEJANDO EL ESTADO DEL DISPOSITIVO LOGICO JAVAPOS
-                               //EN UN MODO CONSISTENTE?
-                               //liberar el LOCK SI EL HILO MUERE DE FORMA INESPERADA???? NO, JavaPOS no dice nada.
-                               //¿como moriria este hilo de forma inesperada?? por ejemplo por RunTimeException
-                               //¿Como hago saber a la aplicacion que este hilo murio de forma inesperada por RunTimeException y el teclado
-                               //ha dejado de funcionar... Si el teclado deja de funcionar no sería mejor parar toda la aplicacion y ver
-                               //que pasa...
-
-               }
-       }
-
-       private class DriverHWUncaughtExceptionHandler implements UncaughtExceptionHandler {
-
-               @Override
-               public void uncaughtException(Thread t, Throwable e) {
-                       logger.warn("Exception not expected while running thread " + t.getName() , e);
-               }
-       }
-       
-       public static void main(String[] param) throws InterruptedException
-       {
-               
-               //Because I do not have a POS I am going to use the keyboard of my computer.
-               //see: /dev/input/by-path/
-               //And the Keyborad scancode is for USB devices.
-               String device ="/dev/input/event4";
-               KeyBoardDeviceLinux driver = new KeyBoardDeviceLinux();
-               
-               logger.info("Main test of KeyBoardDeviceLinux class");
-               try {
-                       driver.device(device);
-                       driver.claim(10000);
-                       driver.enable();
-                       Thread.sleep(5000);
-                       driver.disable();
-                       driver.release();
-                       logger.info("End test of KeyBoardDeviceLinux class");
-               } catch (JposException e) {
-                       if (e.getOrigException() != null) {
-                               logger.info("Origen JposException", e.getOrigException());
-                       }
-                       logger.info("JposException", e);
-               }
-       }
+                    }
+                }
+            }
+        } catch (EOFException e) {
+            logger.error(
+                    "Something went really bad. Impossible end of file while "
+                            + "reading keyboard device!!!!", e);
+            eventListener.errorOccurred(code);
+        } catch (ClosedByInterruptException e) {
+            // We reach this code because the thread was interrupted (disable)
+            // see comment above about this exception.
+            logger.debug("Interrupted KeyBoardDeviceLinux Thread", e);
+        } catch (IOException e) {
+            // We reach this code because there was an I/O error
+            logger.error("Finished KeyBoardDeviceLinux Thread", e);
+        } finally {
+            // This method releases the Java NIO channels (this.device). It is
+            // thread safety :) see: Interruptible
+            this.thread.interrupt();
+            this.device = null; // ¿Realmente esto es necesario? (no lo creo :/
+                                // )
+            // SI EL HILO MUERE ¿ESTOY DEJANDO EL ESTADO DEL DISPOSITIVO LOGICO
+            // JAVAPOS
+            // EN UN MODO CONSISTENTE?
+            // liberar el LOCK SI EL HILO MUERE DE FORMA INESPERADA???? NO,
+            // JavaPOS no dice nada.
+            // ¿como moriria este hilo de forma inesperada?? por ejemplo por
+            // RunTimeException
+            // ¿Como hago saber a la aplicacion que este hilo murio de forma
+            // inesperada por RunTimeException y el teclado
+            // ha dejado de funcionar... Si el teclado deja de funcionar no
+            // sería mejor parar toda la aplicacion y ver
+            // que pasa... !!!!Lanzando un evento de error de JavaPOS supongo
+            // cuando hay error pero
+            // no aquí si no en los catch de arriba aqui podriamos haber llegado
+            // simplemente
+            // porque el usuario quiso interrumpir (matar) el hilo con
+            // disable!!!!
+        }
+    }
+
+    private class DriverHWUncaughtExceptionHandler implements
+            UncaughtExceptionHandler {
+
+        @Override
+        public void uncaughtException(Thread t, Throwable e) {
+            logger.warn(
+                    "Exception not expected while running thread "
+                            + t.getName(), e);
+        }
+    }
+
+    public static void main(String[] param) throws InterruptedException {
+
+        // Because I do not have a POS I am going to use the keyboard of my
+        // computer.
+        // see: /dev/input/by-path/
+        // And the Keyborad scancode is for USB devices.
+        String device = "/dev/input/event4";
+        KeyBoardDeviceLinux driver = new KeyBoardDeviceLinux();
+
+        logger.info("Main test of KeyBoardDeviceLinux class");
+        try {
+            driver.device(device);
+            driver.claim(10000);
+            driver.enable();
+            Thread.sleep(5000);
+            driver.disable();
+            driver.release();
+            logger.info("End test of KeyBoardDeviceLinux class");
+        } catch (JposException e) {
+            if (e.getOrigException() != null) {
+                logger.info("Origen JposException", e.getOrigException());
+            }
+            logger.info("JposException", e);
+        }
+    }
 }
index 73ad76e..f448d37 100644 (file)
@@ -5,26 +5,28 @@ import jpos.events.JposEvent;
 //Similar a WNBaseService  ¿mejor una clase o implementarlo en cada servicio :/?
 //¿O mejor un servicio que extienda una clase que implementa este interfaz XD?
 public interface JposEventQueue {
-       
-       public void putEvent(JposEvent paramJposEvent) throws InterruptedException;
 
-       public JposEvent getEvent() throws InterruptedException;
+    public void putEvent(JposEvent paramJposEvent) throws InterruptedException;
 
-       public void clearInputEvents();
+    public JposEvent getEvent() throws InterruptedException;
 
-       public void clearOutputEvents();
+    public void clearInputEvents();
 
-       public int getNumberOfEvents();
+    public void clearOutputEvents();
 
-       public void checkEvents();
+    public int getNumberOfEvents();
 
-       public void removeAllEvents();
+    public void checkEvents();
 
-       public boolean removeEvent(JposEvent paramJposEvent);
+    public void removeAllEvents();
 
-       public JposEvent peekElement(int paramInt);
+    public boolean removeEvent(JposEvent paramJposEvent);
 
-       public boolean isFull();
+    public JposEvent peekElement(int paramInt);
 
-       public int getSize();
+    public boolean isFull();
+
+    public int getSize();
+
+    public JposEvent peekEvent() throws InterruptedException;
 }
index 6e90064..6414c50 100644 (file)
@@ -2,67 +2,83 @@ package de.javapos.example.queue;
 
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
+
 import jpos.events.JposEvent;
 
 public class JposEventQueueImpl implements JposEventQueue {
-       //§JLS Item 16: Favor composition over inheritance
-       //Java Concurrency in Practice 4.4.2
-       //Not sure if this may be called "Composition" LOL
-       private final BlockingQueue<JposEvent> linkedBlockingQueue = new LinkedBlockingQueue<JposEvent>();
-
-       @Override
-       public void putEvent(JposEvent paramJposEvent) throws InterruptedException {
-               this.linkedBlockingQueue.put(paramJposEvent);
-       }
-
-       @Override
-       public JposEvent getEvent() throws InterruptedException {
-               return this.linkedBlockingQueue.take();
-       }
-
-       @Override
-       public void clearInputEvents() {
-               // TODO Auto-generated method stub
-       }
-
-       @Override
-       public void clearOutputEvents() {
-               // TODO Auto-generated method stub
-       }
-
-       @Override
-       public int getNumberOfEvents() {
-               return this.linkedBlockingQueue.size();
-       }
-
-       @Override
-       public void checkEvents() {
-               // TODO Auto-generated method stub
-       }
-
-       @Override
-       public void removeAllEvents() {
-               this.linkedBlockingQueue.clear();
-       }
-
-       @Override
-       public boolean removeEvent(JposEvent paramJposEvent) {
-               return this.linkedBlockingQueue.remove(paramJposEvent);
-       }
-
-       @Override
-       public JposEvent peekElement(int paramInt) {
-                return null;
-       }
-
-       @Override
-       public boolean isFull() {
-               //No seguro de esto :/
-               return false;
-       }
-
-       @Override
-       public int getSize() {
-               return this.linkedBlockingQueue.size();
-       }
+    // §JLS Item 16: Favor composition over inheritance
+    // Java Concurrency in Practice 4.4.2
+    // Not sure if this may be called "Composition" LOL
+    private final BlockingQueue<JposEvent> linkedBlockingQueue = new LinkedBlockingQueue<JposEvent>();
+
+    @Override
+    public synchronized void putEvent(JposEvent paramJposEvent)
+            throws InterruptedException {
+        // Put at the end of this queue
+        this.linkedBlockingQueue.put(paramJposEvent);
+        // If ThreadFireEvent waiting it must be awakened.
+        this.notify();
+    }
+
+    @Override
+    public JposEvent getEvent() throws InterruptedException {
+        return this.linkedBlockingQueue.take();
+    }
+
+    @Override
+    public synchronized JposEvent peekEvent() throws InterruptedException {
+
+        if (this.linkedBlockingQueue.size() == 0) {
+            // Release monitor
+            wait();
+        }
+
+        return this.linkedBlockingQueue.peek();
+    }
+
+    @Override
+    public void clearInputEvents() {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void clearOutputEvents() {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public int getNumberOfEvents() {
+        return this.linkedBlockingQueue.size();
+    }
+
+    @Override
+    public void checkEvents() {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void removeAllEvents() {
+        this.linkedBlockingQueue.clear();
+    }
+
+    @Override
+    public boolean removeEvent(JposEvent paramJposEvent) {
+        return this.linkedBlockingQueue.remove(paramJposEvent);
+    }
+
+    @Override
+    public JposEvent peekElement(int paramInt) {
+        return null;
+    }
+
+    @Override
+    public boolean isFull() {
+        // No seguro de esto :/
+        return false;
+    }
+
+    @Override
+    public int getSize() {
+        return this.linkedBlockingQueue.size();
+    }
 }