From aba5f79ed62128b74962c1efa15a3f921d66b3e0 Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Sat, 11 Jun 2011 02:54:32 +0200
Subject: SingletonInstance Enhancements / Minor Lock/LockExt API Change
 (isLocked moved up)

We learned that FileChannel.lock() is not reliable on at least GNU/Linux + Sun's JVM implementation,
hence we need a ServerSocket implementation.

Since this code may be useful to others, it has been promoted to GlueGen.

- Abstract SingletonInstance
- Implement Lock interface
- SingletonInstance Spezialisation: FileLock and ServerSocket

Minor API Change: LockExt.isLocked() -> Lock.isLocked()
---
 .../util/locks/SingletonInstanceFileLock.java      | 129 ++++++++++++
 .../util/locks/SingletonInstanceServerSocket.java  | 222 +++++++++++++++++++++
 2 files changed, 351 insertions(+)
 create mode 100644 src/java/jogamp/common/util/locks/SingletonInstanceFileLock.java
 create mode 100644 src/java/jogamp/common/util/locks/SingletonInstanceServerSocket.java

(limited to 'src/java/jogamp/common/util')

diff --git a/src/java/jogamp/common/util/locks/SingletonInstanceFileLock.java b/src/java/jogamp/common/util/locks/SingletonInstanceFileLock.java
new file mode 100644
index 0000000..f0bed66
--- /dev/null
+++ b/src/java/jogamp/common/util/locks/SingletonInstanceFileLock.java
@@ -0,0 +1,129 @@
+/**
+ * Copyright 2010 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ * 
+ *    1. Redistributions of source code must retain the above copyright notice, this list of
+ *       conditions and the following disclaimer.
+ * 
+ *    2. Redistributions in binary form must reproduce the above copyright notice, this list
+ *       of conditions and the following disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+ 
+package jogamp.common.util.locks;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.channels.FileLock;
+import com.jogamp.common.util.locks.SingletonInstance;
+
+public class SingletonInstanceFileLock extends SingletonInstance {
+
+    static final String temp_file_path;
+
+    static {
+        String s = null;
+        try {
+            File tmpFile = File.createTempFile("TEST", "tst");
+            String absTmpFile = tmpFile.getCanonicalPath();
+            tmpFile.delete();
+            s = absTmpFile.substring(0, absTmpFile.lastIndexOf(File.separator));
+        } catch (IOException ex) {
+            ex.printStackTrace();
+        }
+        temp_file_path = s;
+    }
+
+    public static String getCanonicalTempPath() {
+        return temp_file_path;
+    }
+
+    public static String getCanonicalTempLockFilePath(String basename) {
+        return getCanonicalTempPath() + File.separator + basename;
+    }
+
+    public SingletonInstanceFileLock(long poll_ms, String lockFileBasename) {
+        super(poll_ms);
+        file = new File ( getCanonicalTempLockFilePath ( lockFileBasename ) );
+        setupFileCleanup();
+    }
+
+    public SingletonInstanceFileLock(long poll_ms, File lockFile) {
+        super(poll_ms);
+        file = lockFile ;
+        setupFileCleanup();
+    }
+
+    public final String getName() { return file.getPath(); }
+    
+    private void setupFileCleanup() {
+        file.deleteOnExit();
+        Runtime.getRuntime().addShutdownHook(new Thread() {
+            public void run() {
+                unlock();
+            }
+        });        
+    }
+
+    @Override
+    protected boolean tryLockImpl() {
+        try {
+            randomAccessFile = new RandomAccessFile(file, "rw");
+            fileLock = randomAccessFile.getChannel().tryLock();
+
+            if (fileLock != null) {
+                return true;
+            }
+        } catch (Exception e) {
+            System.err.println("SLOCK "+System.currentTimeMillis()+" EEE "+getName()+" - Unable to create and/or lock file");
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+    @Override
+    protected boolean unlockImpl() {
+        try {
+            if(null != fileLock) {
+                fileLock.release();
+                fileLock = null;
+            }
+            if(null != randomAccessFile) {
+                randomAccessFile.close();
+                randomAccessFile = null;
+            }
+            if(null != file) {
+                file.delete();
+            }
+            return true;
+        } catch (Exception e) {
+            System.err.println("SLOCK "+System.currentTimeMillis()+" EEE "+getName()+" - Unable to remove lock file");
+            e.printStackTrace();
+        } finally {
+            fileLock = null;
+            randomAccessFile = null;
+        }
+        return false;
+    }
+
+    private final File file;
+    private RandomAccessFile randomAccessFile = null;
+    private FileLock fileLock = null;
+}
diff --git a/src/java/jogamp/common/util/locks/SingletonInstanceServerSocket.java b/src/java/jogamp/common/util/locks/SingletonInstanceServerSocket.java
new file mode 100644
index 0000000..e5d0ae1
--- /dev/null
+++ b/src/java/jogamp/common/util/locks/SingletonInstanceServerSocket.java
@@ -0,0 +1,222 @@
+/**
+ * Copyright 2011 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ * 
+ *    1. Redistributions of source code must retain the above copyright notice, this list of
+ *       conditions and the following disclaimer.
+ * 
+ *    2. Redistributions in binary form must reproduce the above copyright notice, this list
+ *       of conditions and the following disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+ 
+package jogamp.common.util.locks;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import com.jogamp.common.util.locks.SingletonInstance;
+
+public class SingletonInstanceServerSocket extends SingletonInstance {
+
+    private final Server singletonServer;
+    private final String fullName;
+    
+    public SingletonInstanceServerSocket(long poll_ms, int portNumber) {
+        super(poll_ms);
+        
+        // Gather the local InetAddress, loopback is prioritized 
+        InetAddress ilh = null;
+        try {
+            ilh = InetAddress.getByName(null); // loopback
+        } catch (UnknownHostException e1) { }
+        if(null == ilh) {
+            try {
+                ilh = InetAddress.getByName("localhost");
+                if(null!=ilh && !ilh.isLoopbackAddress()) { ilh = null; }
+            } catch (UnknownHostException e1) { }
+        }
+        if(null == ilh) {
+            try {
+                ilh = InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 } );            
+                if(null!=ilh && !ilh.isLoopbackAddress()) { ilh = null; }
+            } catch (UnknownHostException e) { }
+        }        
+        if(null == ilh) {
+            try {
+                ilh = InetAddress.getLocalHost();
+            } catch (UnknownHostException e) { }
+        }
+        if(null == ilh) {
+            throw new RuntimeException("Could not determine local InetAddress");
+        }
+        
+        fullName = ilh.toString()+":"+portNumber;
+        singletonServer = new Server(ilh, portNumber);        
+    }
+
+    public final InetAddress getLocalInetAddress() {
+        return singletonServer.getLocalInetAddress();
+    }
+
+    public final int getPortNumber() {
+        return singletonServer.getPortNumber();
+    }
+
+    public final String getName() { return fullName; }
+    
+    @Override
+    protected boolean tryLockImpl() {
+        if( singletonServer.isRunning() ) {
+            return false; // same JVM .. server socket already installed !
+        }
+        
+        // check if other JVM's locked the server socket ..
+        Socket clientSocket = singletonServer.connect();
+        if(null != clientSocket) {
+            try {
+                clientSocket.close();
+            } catch (IOException e) { }
+            return false;
+        }
+        
+        if( !singletonServer.start() ) {
+            return false;
+        }
+        
+        return true;
+    }
+
+    @Override
+    protected boolean unlockImpl() {
+        return singletonServer.shutdown();
+    }
+
+    public class Server implements Runnable {
+       private final InetAddress localInetAddress;
+       private final int portNumber;   
+       
+       private volatile boolean shallQuit = false; 
+       private volatile boolean alive = false;
+       
+       private Object syncOnStartStop = new Object();
+       private ServerSocket serverSocket = null;
+       
+       public Server(InetAddress localInetAddress, int portNumber) {
+           this.localInetAddress = localInetAddress;
+           this.portNumber = portNumber;
+       }
+       
+       public final InetAddress getLocalInetAddress() { return localInetAddress; }
+       public final int getPortNumber() { return portNumber; }
+       
+       public boolean start() {
+           if(alive) return true;
+           
+           Thread t = new Thread(this);
+           t.setDaemon(false);  // don't keep the JVM running ..
+           
+           synchronized (syncOnStartStop) {
+               t.start();
+               try {
+                   syncOnStartStop.wait();
+               } catch (InterruptedException ie) {
+                   ie.printStackTrace();
+               }            
+           }
+           boolean ok = isBound();
+           if(!ok) {
+               shutdown();
+           }
+           return ok;
+       }
+       
+       public final boolean shutdown() {
+           if(!alive) return true;
+           
+           synchronized (syncOnStartStop) {
+               shallQuit = true;
+               connect();
+               try {
+                   syncOnStartStop.wait();
+               } catch (InterruptedException ie) {
+                   ie.printStackTrace();
+               }            
+           }
+           if(alive) {
+               System.err.println("SLOCK "+System.currentTimeMillis()+" EEE "+getName()+" - Unable to remove lock: ServerThread still alive ?");
+           }
+           return !alive;
+       }
+       
+       public final boolean isRunning() { return alive; }
+       
+       public final boolean isBound() {
+           return alive && null != serverSocket && serverSocket.isBound() ;
+       }
+
+       public final Socket connect() {
+           try {
+               return new Socket(localInetAddress, portNumber);
+           } catch (Exception e) { }
+           return null;
+       }
+       
+       public void run() {
+           alive = false;
+           synchronized (syncOnStartStop) {
+               try {
+                   serverSocket = new ServerSocket(portNumber, 1, localInetAddress);
+                   alive = true;
+               } catch (IOException e) {
+                    System.err.println("SLOCK "+System.currentTimeMillis()+" EEE "+getName()+" - Unable to install ServerSocket: "+e.getMessage());
+                    shallQuit = true;
+               } finally {
+                    syncOnStartStop.notifyAll();                   
+               }
+           }
+           
+           while (!shallQuit) {
+               try {
+                   final Socket clientSocket = serverSocket.accept();
+                   clientSocket.close();
+               } catch (IOException ioe) {
+                   System.err.println("SLOCK "+System.currentTimeMillis()+" EEE "+getName()+" - Exception during accept: " + ioe.getMessage());
+               }
+           }
+           
+           synchronized (syncOnStartStop) {
+               try {
+                   if(null != serverSocket) {
+                       serverSocket.close();
+                   }
+               } catch (IOException e) {
+                   System.err.println("SLOCK "+System.currentTimeMillis()+" EEE "+getName()+" - Exception during close: " + e.getMessage());
+               } finally {
+                   serverSocket = null;
+                   alive = false;
+                   shallQuit = false;
+                   syncOnStartStop.notifyAll();
+               }
+           }
+       }
+    }
+}
-- 
cgit v1.2.3