Working on my JavaPOS keyboard driver.
authorgumartinm <gustavo@gumartinm.name>
Mon, 30 Apr 2012 07:24:26 +0000 (09:24 +0200)
committergumartinm <gustavo@gumartinm.name>
Mon, 30 Apr 2012 07:24:26 +0000 (09:24 +0200)
No time for comments...

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/ThreadGate.java [new file with mode: 0644]
JavaPOS/KeyBoardDriver/src/main/java/de/javapos/example/ThreadSafe.java [deleted file]
JavaPOS/KeyBoardDriver/src/main/java/de/javapos/example/annotation/GuardedBy.java [new file with mode: 0644]
JavaPOS/KeyBoardDriver/src/main/java/de/javapos/example/annotation/ThreadSafe.java [new file with mode: 0644]
JavaPOS/KeyBoardDriver/src/main/java/de/javapos/example/hardware/KeyBoardDeviceLinux.java
JavaPOS/KeyBoardDriver/src/main/java/de/javapos/example/queue/JposEventQueueImpl.java

index fd19269..b7e8569 100644 (file)
@@ -1,5 +1,6 @@
 package de.javapos.example;
  
+import de.javapos.example.annotation.ThreadSafe;
 import de.javapos.example.hardware.BaseKeyBoardDriver;
 import de.javapos.example.queue.JposEventListener;
 import de.javapos.example.queue.JposEventQueue;
index d9e6914..d965d96 100644 (file)
@@ -10,14 +10,14 @@ import jpos.services.EventCallbacks;
 import de.javapos.example.queue.JposEventQueue;
 
 public class ThreadFireEvent extends Thread {
+       private boolean isDataEventEnabled = true;
        private final JposEventQueue jposEventQueue;
-       private boolean isDataEventEnabled;
        private final EventCallbacks eventCallbacks;
+       private final ThreadGate freezeEventsGate = new ThreadGate();
        
        public ThreadFireEvent (JposEventQueue jposEventQueue, 
-                               boolean isDataEventEnabled, EventCallbacks  eventCallbacks) {
+                                     boolean isDataEventEnabled, EventCallbacks  eventCallbacks) {
                this.jposEventQueue = jposEventQueue;
-               this.isDataEventEnabled = isDataEventEnabled;
                this.eventCallbacks = eventCallbacks;
        }
        
@@ -26,14 +26,24 @@ public class ThreadFireEvent extends Thread {
                
                while (true) {
                        if (this.jposEventQueue.getNumberOfEvents() != 0) {
+
+                               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;
+                               }
+
                                //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) {
-                                               //restore interrupt status.
-                                               Thread.currentThread().interrupt();
+                                               //We are implementing the interruption policy in this class
+                                               //for this thread. So, this is permitted.
                                                //End of thread.
                                                break;
                                        }
@@ -43,7 +53,6 @@ public class ThreadFireEvent extends Thread {
                                }
                        }
                        
-                       //TODO: Bloquearme hasta que freezeEvent sea false
                        
                        if (jposEvent instanceof DataEvent) {
                                this.eventCallbacks.fireDataEvent((DataEvent)jposEvent);
@@ -64,4 +73,17 @@ public class ThreadFireEvent extends Thread {
                        }
                }
        }
+
+       public void setFreezeEvents (boolean freezeEvents) {
+               if (freezeEvents) {
+                       this.freezeEventsGate.close();
+               }
+               else {
+                       this.freezeEventsGate.open();
+               }
+       }
+
+       public void setDataEventEnabled (boolean dataEventEnabled) {
+               this.isDataEventEnabled = dataEventEnabled;
+       }
 }
diff --git a/JavaPOS/KeyBoardDriver/src/main/java/de/javapos/example/ThreadGate.java b/JavaPOS/KeyBoardDriver/src/main/java/de/javapos/example/ThreadGate.java
new file mode 100644 (file)
index 0000000..5fb88ea
--- /dev/null
@@ -0,0 +1,34 @@
+package de.javapos.example;
+
+import de.javapos.example.annotation.GuardedBy;
+import de.javapos.example.annotation.ThreadSafe;
+
+/**
+ * See: §Java Concurrency in practice 14.2.6
+ * @author
+ *
+ */
+@ThreadSafe
+public class ThreadGate {
+       //CONDITION-PREDICATE: opened-since(n) (isOpen || generation>n)
+       @GuardedBy("this") private boolean isOpen;
+       @GuardedBy("this") private int generation;
+
+       
+       public synchronized void close() {
+               isOpen = false;
+       }
+       
+       public synchronized void open() {
+               ++generation;
+               isOpen = true;
+               notifyAll();
+       }
+       
+       //BLOCKS-UNTIL: opened-since(generation on entry)
+       public synchronized void await() throws InterruptedException  {
+               int arrivalGeneration = generation;
+               while (!isOpen && arrivalGeneration == generation) 
+                       wait();
+       }
+}
diff --git a/JavaPOS/KeyBoardDriver/src/main/java/de/javapos/example/ThreadSafe.java b/JavaPOS/KeyBoardDriver/src/main/java/de/javapos/example/ThreadSafe.java
deleted file mode 100644 (file)
index dc3a60a..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-package de.javapos.example;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Target;
-
-/**
- * Declare classes as thread-safe. It is assumed that objects of these 
- * classes are thread-safe and can be shared between different threads. *
- */
-@Target(value = ElementType.TYPE)
-public @interface ThreadSafe {
-
-}
diff --git a/JavaPOS/KeyBoardDriver/src/main/java/de/javapos/example/annotation/GuardedBy.java b/JavaPOS/KeyBoardDriver/src/main/java/de/javapos/example/annotation/GuardedBy.java
new file mode 100644 (file)
index 0000000..746bf01
--- /dev/null
@@ -0,0 +1,38 @@
+package de.javapos.example.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Retention;
+
+/*
+ * Copyright (c) 2005 Brian Goetz
+ * Released under the Creative Commons Attribution License
+ *   (http://creativecommons.org/licenses/by/2.5)
+ * Official home: http://www.jcip.net
+ */
+
+/**
+ * GuardedBy
+ * 
+ * The field or method to which this annotation is applied can only be accessed
+ * when holding a particular lock, which may be a built-in (synchronization)
+ * lock, or may be an explicit java.util.concurrent.Lock.
+ * 
+ * The argument determines which lock guards the annotated field or method: this :
+ * The string literal "this" means that this field is guarded by the class in
+ * which it is defined. class-name.this : For inner classes, it may be necessary
+ * to disambiguate 'this'; the class-name.this designation allows you to specify
+ * which 'this' reference is intended itself : For reference fields only; the
+ * object to which the field refers. field-name : The lock object is referenced
+ * by the (instance or static) field specified by field-name.
+ * class-name.field-name : The lock object is reference by the static field
+ * specified by class-name.field-name. method-name() : The lock object is
+ * returned by calling the named nil-ary method. class-name.class : The Class
+ * object for the specified class should be used as the lock object.
+ */
+@Target( { ElementType.FIELD, ElementType.METHOD })
+@Retention(RetentionPolicy.CLASS)
+public @interface GuardedBy {
+    String value();
+}
\ No newline at end of file
diff --git a/JavaPOS/KeyBoardDriver/src/main/java/de/javapos/example/annotation/ThreadSafe.java b/JavaPOS/KeyBoardDriver/src/main/java/de/javapos/example/annotation/ThreadSafe.java
new file mode 100644 (file)
index 0000000..c96c5b2
--- /dev/null
@@ -0,0 +1,22 @@
+package de.javapos.example.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * ThreadSafe
+ * 
+ * The class to which this annotation is applied is thread-safe. This means that
+ * no sequences of accesses (reads and writes to public fields, calls to public
+ * methods) may put the object into an invalid state, regardless of the
+ * interleaving of those actions by the runtime, and without requiring any
+ * additional synchronization or coordination on the part of the caller.
+ */
+@Documented
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.CLASS)
+public @interface ThreadSafe {
+}
\ No newline at end of file
index 5f9828b..0879733 100644 (file)
@@ -34,6 +34,7 @@ public class KeyBoardDeviceLinux implements BaseKeyBoardDriver {
        private FileChannel fileChannelLock;
        private DataInputStream device;
        private boolean isClaimed;
+       private boolean isEnabled;
        //Usando volatile porque cumplo las condiciones de uso. Ver Java Concurrency in Practice PAG????
        private volatile boolean autoDisable;
        private JposEventListener eventListener;
@@ -61,13 +62,14 @@ public class KeyBoardDeviceLinux implements BaseKeyBoardDriver {
        @Override
        public void claim(int time) throws JposException {
                FileLock lock = null;
+               FileChannel fileChannelLock = null;
                
                if (this.isClaimed) {
                        return;
                }
 
                try {
-                       this.fileChannelLock = new FileOutputStream(javaposKeyBoardLock).getChannel();
+                       fileChannelLock = new FileOutputStream(javaposKeyBoardLock).getChannel();
                } catch (FileNotFoundException e) {
                        throw new JposException(JposConst.JPOS_E_NOTCLAIMED, "File not found.",e);
                }
@@ -75,12 +77,24 @@ public class KeyBoardDeviceLinux implements BaseKeyBoardDriver {
                if (time == -1) {
                        try {
                                //This method is not aware interrupt. :(
-                               lock = this.fileChannelLock.lock();
+                               lock = fileChannelLock.lock();
                        //I do not like catching RunTimeExceptions but I have no choice...
                        } catch (OverlappingFileLockException e) {
                                logger.warn("Concurrent access in claim method not supported without synchronization or you have" +
                                                        "more than one instances of the KeyBoardDeviceLinux class", e);
+                               try {
+                                       fileChannelLock.close();
+                               } catch (IOException e1) {
+                                       //The first exception is more important.
+                                       logger.warn("Error while closing the file lock", e1);
+                               }
                        } catch (IOException e) {
+                               try {
+                                       fileChannelLock.close();
+                               } catch (IOException e1) {
+                                       //The first exception is more important.
+                                       logger.warn("Error while closing the file lock", e1);
+                               }
                                throw new JposException(JposConst.JPOS_E_CLAIMED, "Error while trying to claim device.",e);
                        }
                }
@@ -89,34 +103,59 @@ public class KeyBoardDeviceLinux implements BaseKeyBoardDriver {
                        //Espera activa. ¿Alguna idea de cómo hacer esto sin espera activa?  :(
                        while((System.nanoTime() <= lastTime) && (lock == null)) {
                                try {
-                                       if ((lock = this.fileChannelLock.tryLock()) != null) {
+                                       if ((lock = fileChannelLock.tryLock()) != null) {
                                                break;
                                        }
+                                       this.wait(250);
                                //I do not like catching RunTimeExceptions but I have no choice...
                                } catch (OverlappingFileLockException e) {
                                        logger.warn("Concurrent access in claim method not supported without synchronization or you have" +
                                                                "more than one instances of the KeyBoardDeviceLinux class", e);
+                                       try {
+                                               fileChannelLock.close();
+                                       } catch (IOException e1) {
+                                               //The first exception is more important.
+                                               logger.warn("Error while closing the file lock", e1);
+                                       }
                                } catch (IOException e) {
-                                       throw new JposException(JposConst.JPOS_E_CLAIMED, "Error while trying to claim device.",e);
-                               }
-                               try {
-                    this.wait(250);
+                                       try {
+                                               fileChannelLock.close();
+                                       } catch (IOException e1) {
+                                               //The first exception is more important.
+                                               logger.warn("Error while closing the file lock", e1);
+                    }
+                    throw new JposException(JposConst.JPOS_E_CLAIMED, "Error while trying to claim device.",e);
                 } catch(InterruptedException e) {
-                       //restore interrupt status.
+                    try {
+                        fileChannelLock.close();
+                    } catch (IOException e1) {
+                        //The first exception is more important.
+                        logger.warn("Error while closing the file lock", e1);
+                    }
+                    //restore interrupt status.
                        Thread.currentThread().interrupt();
-                }      
+                    throw new JposException(JposConst.JPOSERR, "Interrupt exception detected.", e);
+                }
                        } 
                        
                        if (lock == null) {
-                               throw new JposException(JposConst.JPOS_E_TIMEOUT, "Timeout while trying to claim device.", null);
+                               try {
+                                       fileChannelLock.close();
+                               } catch (IOException e) {
+                                       //The timeout exception is more important.
+                                       logger.warn("Error while closing the file lock", e);
+                               }
+                               throw new JposException(JposConst.JPOS_E_TIMEOUT, "Timeout while trying to claim device.");
                        }
                }
                
                if (lock != null) {
-                       //Just when concurrent access without synchronization (this method is not tread-safe) you could see
-                       //null value. claim does not support concurrent access in itself anyway I take counter measures in case
+                       //Just when concurrent access without synchronization (this method is not tread-safe) or having more
+                       //than one instances of this class you could see null value. 
+                       //claim does not support concurrent access in itself. Anyway I take counter measures in case
                        //of someone forgets it.
                        this.lock = lock;
+                       this.fileChannelLock = fileChannelLock;
                        this.isClaimed = true;
                }
        }
@@ -167,6 +206,9 @@ public class KeyBoardDeviceLinux implements BaseKeyBoardDriver {
         */
        @Override
        public 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"));
@@ -194,10 +236,7 @@ public class KeyBoardDeviceLinux implements BaseKeyBoardDriver {
                //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()) {
-                       throw new JposException(JposConst.JPOSERR, "The device was not disabled.", null);
-               }
-               else {
+               if (!this.thread.isAlive()) {
                        try {
                                this.device = new DataInputStream(Channels.newInputStream(new FileInputStream(this.deviceName).getChannel()));
                        } catch (FileNotFoundException e) {
@@ -216,29 +255,33 @@ public class KeyBoardDeviceLinux implements BaseKeyBoardDriver {
                        this.thread = new Thread (task, "KeyBoardDeviceLinux-Thread");
                        this.thread.setUncaughtExceptionHandler(new DriverHWUncaughtExceptionHandler());
                        this.thread.start();
+                       this.isEnabled = true;
                }
        }
 
        @Override
        public 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.
-                       //QUE PASA SIN LLAMAN A INTERRUPT EN JOIN Y EL HILO NUNCA MURIERA
-                       //TAL Y COMO ESTA HECHO EL HILO NO TARDARA MUCHO EN MORIR (espero) ASI QUE 
-                       //AUNQUE ALGO ME INTERRUMPA NO DEBERIA PASAR GRAN COSA, LO UNICO QUE SI OTRO HILO
-                       //JUSTO AQUI INTENTA HACER ENABLE PUEDE QUE VEA EL HILO TODAVIA VIVO AUNQUE YA SE HABIA
-                       //PASADO POR EL DISABLE, ¿¿¿PERO TAN POCO ES UN GRAN PROBLEMA ESO??? LUEGO PARA MÍ QUE ¿ESTO ESTA OK?
-                       Thread.currentThread().interrupt();     
+                       Thread.currentThread().interrupt();
+                       throw new JposException(JposConst.JPOSERR, "Interrupt exception detected.", e);
                }
+
+               this.isEnabled = false;
        }
 
        @Override
        public boolean isEnabled() {
-               return this.thread.isAlive();
+               return this.isEnabled;
        }
 
        @Override
@@ -349,7 +392,12 @@ public class KeyBoardDeviceLinux implements BaseKeyBoardDriver {
                        value = ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
                        
                        if (type == KeyBoardDeviceLinux.EV_KEY) {
-                    eventListener.inputAvailable(code);
+                    //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);
                     if (autoDisable) {
                         break;
@@ -363,8 +411,9 @@ public class KeyBoardDeviceLinux implements BaseKeyBoardDriver {
                        //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. It is thread safety :) see: Interruptible 
+                       //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.
index 6e90064..0f71378 100644 (file)
@@ -1,7 +1,11 @@
 package de.javapos.example.queue;
 
+import java.util.Iterator;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
+
+import jpos.events.DataEvent;
+import jpos.events.ErrorEvent;
 import jpos.events.JposEvent;
 
 public class JposEventQueueImpl implements JposEventQueue {
@@ -37,7 +41,13 @@ public class JposEventQueueImpl implements JposEventQueue {
 
        @Override
        public void checkEvents() {
-               // TODO Auto-generated method stub
+               //WIP sorry... :/
+               Iterator<JposEvent> iterator = this.linkedBlockingQueue.iterator();
+               while (iterator.hasNext()) {
+                       if (!(iterator.next() instanceof DataEvent) || !(iterator.next() instanceof ErrorEvent)) {
+
+                       }
+               }
        }
 
        @Override
@@ -52,7 +62,7 @@ public class JposEventQueueImpl implements JposEventQueue {
 
        @Override
        public JposEvent peekElement(int paramInt) {
-                return null;
+                return this.linkedBlockingQueue.peek();
        }
 
        @Override