You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by an...@apache.org on 2014/11/28 16:18:27 UTC

tomee git commit: #TOMEE-1461 - Improve get next port

Repository: tomee
Updated Branches:
  refs/heads/tomee-1.7.x b72144c03 -> b15645db7


#TOMEE-1461 - Improve get next port

Adding env and sys prop 'TOMEE_LOCK_FILE'


Project: http://git-wip-us.apache.org/repos/asf/tomee/repo
Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/b15645db
Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/b15645db
Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/b15645db

Branch: refs/heads/tomee-1.7.x
Commit: b15645db7ceed895a54de8b58594a2168bbff6a7
Parents: b72144c
Author: andygumbrecht <an...@apache.org>
Authored: Fri Nov 28 16:17:35 2014 +0100
Committer: andygumbrecht <an...@apache.org>
Committed: Fri Nov 28 16:17:58 2014 +0100

----------------------------------------------------------------------
 .../org/apache/openejb/util/NetworkUtil.java    | 242 +++++++++++++++++--
 .../apache/openejb/util/NetworkUtilTest.java    |  61 ++++-
 2 files changed, 285 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tomee/blob/b15645db/container/openejb-core/src/main/java/org/apache/openejb/util/NetworkUtil.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/util/NetworkUtil.java b/container/openejb-core/src/main/java/org/apache/openejb/util/NetworkUtil.java
index d2f2fe5..e65f69e 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/util/NetworkUtil.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/util/NetworkUtil.java
@@ -17,29 +17,53 @@
 
 package org.apache.openejb.util;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.io.IOException;
+import java.io.RandomAccessFile;
 import java.net.InetAddress;
 import java.net.NetworkInterface;
 import java.net.ServerSocket;
 import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
 import java.util.Collection;
-import java.util.concurrent.atomic.AtomicReference;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
 import java.util.concurrent.locks.ReentrantLock;
 
 public final class NetworkUtil {
 
+    /**
+     * Lock file property name
+     */
+    public static final String TOMEE_LOCK_FILE = "TOMEE_LOCK_FILE";
+
     private static final ReentrantLock lock = new ReentrantLock();
-    private static final AtomicReference<LastPort> lastPort = new AtomicReference<LastPort>();
+    private static final Set<LastPort> lastPort = new HashSet<LastPort>();
+    private static final ByteBuffer buf = ByteBuffer.allocate(512);
+    public static final int PORT_MIN = 1024;
+    public static final int PORT_MAX = 65535;
+    private static File lockFile = null;
 
     private NetworkUtil() {
         // no-op
     }
 
-    public static int getNextAvailablePort() {
+    public static synchronized void clearLockFile() {
+        System.clearProperty(NetworkUtil.TOMEE_LOCK_FILE);
+        lockFile = null;
+    }
+
+    public static synchronized int getNextAvailablePort() {
         final ReentrantLock l = lock;
         l.lock();
         try {
-            return getNextAvailablePort(new int[]{0});
+            return getNextAvailablePort(PORT_MIN, PORT_MAX, null);
         } finally {
             l.unlock();
         }
@@ -51,6 +75,7 @@ public final class NetworkUtil {
         l.lock();
 
         try {
+            purgeLast();
             int port;
             ServerSocket s = null;
             try {
@@ -68,7 +93,6 @@ public final class NetworkUtil {
                 }
             }
 
-            lastPort.set(new LastPort(port, System.currentTimeMillis()));
             return port;
         } finally {
             l.unlock();
@@ -81,16 +105,20 @@ public final class NetworkUtil {
         l.lock();
 
         try {
+            purgeLast();
             int port = -1;
             ServerSocket s = null;
             for (int i = min; i <= max; i++) {
+
+                if (excluded != null && excluded.contains(i) || i > PORT_MAX || i < PORT_MIN) {
+                    continue;
+                }
+
                 try {
                     s = create(new int[]{i});
                     port = s.getLocalPort();
+                    break;
 
-                    if (excluded == null || !excluded.contains(port)) {
-                        break;
-                    }
                 } catch (final IOException ioe) {
                     port = -1;
                 } finally {
@@ -104,34 +132,202 @@ public final class NetworkUtil {
                 }
             }
 
-            lastPort.set(new LastPort(port, System.currentTimeMillis()));
             return port;
         } finally {
             l.unlock();
         }
     }
 
+    private static void purgeLast() {
+        final Iterator<LastPort> it = lastPort.iterator();
+        while (it.hasNext()) {
+            final LastPort last = it.next();
+            if ((System.currentTimeMillis() - last.time) >= 10000) {
+                it.remove();
+            }
+        }
+    }
+
     private static ServerSocket create(final int[] ports) throws IOException {
+
         for (final int port : ports) {
             try {
 
-                final LastPort last = lastPort.get();
-                if (null != last && port == last.port) {
-                    if ((System.currentTimeMillis() - last.time) < 10000) {
-                        continue;
+                final LastPort lp = new LastPort(port, System.currentTimeMillis());
+                if (lastPort.contains(lp)) {
+                    continue;
+                }
+
+                final ServerSocket ss = new ServerSocket(port);
+
+                if (!checkLockFile(port)) {
+                    try {
+                        ss.close();
+                    } catch (final Exception ignored) {
                     }
+                    continue;
                 }
 
-                return new ServerSocket(port);
+                lastPort.add(lp);
+
+                return ss;
+
             } catch (final IOException ex) {
                 // try next port
             }
         }
 
-        // if the program gets here, no port in the range was found
+        // If the program gets here, no port in the range was found
         throw new IOException("No free port found");
     }
 
+    private static File getLockFile() {
+
+        if (null == lockFile) {
+            String lf = System.getenv("TOMEE_LOCK_FILE");
+            lf = (null != lf ? lf : System.getProperty("TOMEE_LOCK_FILE"));
+
+            if (null != lf) {
+                final File f = new File(lf);
+                try {
+                    lockFile = (!f.exists() && !f.createNewFile() ? null : (f.isFile() ? f : null));
+                } catch (final IOException ignored) {
+                }
+            }
+        }
+
+        return lockFile;
+    }
+
+    /**
+     * If a lockfile exists then see if we can really reserve this port
+     *
+     * @param port int
+     * @return true if we can reserve else false
+     */
+    private static boolean checkLockFile(final int port) {
+
+        boolean result = true;
+
+        final File lf = getLockFile();
+        if (null != lf) {
+
+            final Properties p = new Properties();
+            RandomAccessFile raf = null;
+            ByteArrayOutputStream baos = null;
+            ByteArrayInputStream bais = null;
+            FileLock lock = null;
+
+            try {
+
+                raf = new RandomAccessFile(lf, "rw");
+                final FileChannel fileChannel = raf.getChannel();
+
+                int i = 0;
+                while ((lock = fileChannel.tryLock()) == null) {
+                    Thread.sleep(10);
+                    i++;
+
+                    if (i > 200) {
+                        return false;
+                    }
+                }
+
+                baos = new ByteArrayOutputStream();
+
+                while (fileChannel.read(buf) > 0) {
+                    baos.write((byte[]) buf.flip().array());
+                    buf.clear();
+                }
+
+                bais = new ByteArrayInputStream(baos.toByteArray());
+                p.load(bais);
+
+                final boolean purged = purgeOld(p);
+
+                if (null != p.getProperty(String.valueOf(port))) {
+                    result = false;
+                    //System.out.println("Locked " + port);
+                } else {
+                    p.setProperty(String.valueOf(port), String.valueOf(System.currentTimeMillis()));
+                    //System.out.println("Reserved " + port);
+                }
+
+                if (result || purged) {
+                    baos.reset();
+                    p.store(baos, "TomEE port locks");
+                    fileChannel.truncate(0);
+                    fileChannel.write(ByteBuffer.wrap(baos.toByteArray()));
+                }
+
+            } catch (final Exception ignored) {
+                result = false;
+            } finally {
+                if (null != lock) {
+                    try {
+                        lock.release();
+                    } catch (final Exception ignored) {
+
+                    }
+                }
+                if (null != baos) {
+                    try {
+                        baos.close();
+                    } catch (final Exception ignored) {
+
+                    }
+                }
+                if (null != bais) {
+                    try {
+                        bais.close();
+                    } catch (final Exception ignored) {
+
+                    }
+                }
+                if (null != raf) {
+                    try {
+                        raf.close();
+                    } catch (final Exception ignored) {
+
+                    }
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Purge keys (ports) older than 30 seconds
+     *
+     * @param p Properties
+     */
+    private static boolean purgeOld(final Properties p) {
+
+        boolean purged = false;
+        final long now = System.currentTimeMillis();
+        final Set<String> names = p.stringPropertyNames();
+
+        for (final String key : names) {
+            final String value = p.getProperty(key);
+
+            if (isOld(now, value)) {
+                purged = true;
+                p.remove(key);
+            }
+        }
+
+        return purged;
+    }
+
+    private static boolean isOld(final long now, final String value) {
+        try {
+            return now - Long.parseLong(value) > 30000;
+        } catch (final Exception e) {
+            return true;
+        }
+    }
+
     public static String getLocalAddress(final String start, final String end) {
         return start + "localhost:" + getNextAvailablePort() + end;
     }
@@ -166,5 +362,21 @@ public final class NetworkUtil {
             this.port = port;
             this.time = time;
         }
+
+        @Override
+        public boolean equals(final Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            final LastPort lastPort = (LastPort) o;
+
+            return port == lastPort.port;
+
+        }
+
+        @Override
+        public int hashCode() {
+            return port;
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/tomee/blob/b15645db/container/openejb-core/src/test/java/org/apache/openejb/util/NetworkUtilTest.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/test/java/org/apache/openejb/util/NetworkUtilTest.java b/container/openejb-core/src/test/java/org/apache/openejb/util/NetworkUtilTest.java
index 261fd8d..ac7b228 100644
--- a/container/openejb-core/src/test/java/org/apache/openejb/util/NetworkUtilTest.java
+++ b/container/openejb-core/src/test/java/org/apache/openejb/util/NetworkUtilTest.java
@@ -17,9 +17,12 @@
  */
 package org.apache.openejb.util;
 
+import org.junit.AfterClass;
 import org.junit.Assert;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
+import java.io.File;
 import java.net.InetAddress;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CountDownLatch;
@@ -27,8 +30,60 @@ import java.util.concurrent.TimeUnit;
 
 public class NetworkUtilTest {
 
+    private static File f = null;
+
+    @BeforeClass
+    public static void beforeClass() throws Exception {
+        f = File.createTempFile("tomee", "lock");
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        if (null != f && !f.delete()) {
+            f.deleteOnExit();
+        }
+    }
+
+
     @Test
-    public void test() throws Exception {
+    public void testNext() throws Exception {
+
+        NetworkUtil.clearLockFile();
+
+        final int count = 20;
+        final CountDownLatch latch = new CountDownLatch(count);
+        final long start = System.currentTimeMillis();
+        final CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<Integer>();
+
+        for (int i = 0; i < count; i++) {
+            final Thread thread = new Thread(new Runnable() {
+                public void run() {
+                    final int nextAvailablePort = NetworkUtil.getNextAvailablePort();
+                    if (list.contains(nextAvailablePort)) {
+                        if ((System.currentTimeMillis() - start) < 10000) {
+                            Assert.fail("Got a duplicate port with ten seconds");
+                        }
+                    } else {
+                        list.add(nextAvailablePort);
+                    }
+
+                    latch.countDown();
+                }
+            }, "test-thread-" + count);
+            thread.setDaemon(false);
+            thread.start();
+        }
+
+        final boolean success = latch.await(15, TimeUnit.SECONDS);
+        Assert.assertTrue(success);
+
+        System.out.println("VM safe port list = " + list);
+    }
+
+    @Test
+    public void testNextLock() throws Exception {
+
+        System.setProperty(NetworkUtil.TOMEE_LOCK_FILE, f.getAbsolutePath());
 
         final int count = 20;
         final CountDownLatch latch = new CountDownLatch(count);
@@ -40,7 +95,7 @@ public class NetworkUtilTest {
                 public void run() {
                     final int nextAvailablePort = NetworkUtil.getNextAvailablePort();
                     if (list.contains(nextAvailablePort)) {
-                        if ((System.currentTimeMillis() - start) > 10000) {
+                        if ((System.currentTimeMillis() - start) < 10000) {
                             Assert.fail("Got a duplicate port with ten seconds");
                         }
                     } else {
@@ -57,7 +112,7 @@ public class NetworkUtilTest {
         final boolean success = latch.await(15, TimeUnit.SECONDS);
         Assert.assertTrue(success);
 
-        System.out.println("Thread safe port list = " + list);
+        System.out.println("Machine safe port list = " + list);
     }
 
     @Test