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