diff --git a/changelog/README.md b/changelog/README.md
index 42f7ac59eb4..7bef4322be4 100644
--- a/changelog/README.md
+++ b/changelog/README.md
@@ -35,6 +35,7 @@
- [new feature] JAVA-982: Introduce new method ConsistencyLevel.isSerial().
- [bug] JAVA-764: Retry with the normal consistency level (not the serial one) when a write times out on the Paxos phase.
- [bug] JAVA-727: Allow monotonic timestamp generators to drift in the future + use microsecond precision when possible.
+- [improvement] JAVA-444: Add Java process information to UUIDs.makeNode() hash.
Merged from 2.0 branch:
diff --git a/driver-core/pom.xml b/driver-core/pom.xml
index 66a5eb0fd31..2a6b4ac33d3 100644
--- a/driver-core/pom.xml
+++ b/driver-core/pom.xml
@@ -57,6 +57,12 @@
${jnr-ffi.version}
+
+ com.github.jnr
+ jnr-posix
+ ${jnr-posix.version}
+
+
@@ -368,6 +374,7 @@
false
**/SSL*Test.java
+ **/UUIDsPID*.java
diff --git a/driver-core/src/main/java/com/datastax/driver/core/Clock.java b/driver-core/src/main/java/com/datastax/driver/core/Clock.java
index 4ff922654ea..a5736f37a2e 100644
--- a/driver-core/src/main/java/com/datastax/driver/core/Clock.java
+++ b/driver-core/src/main/java/com/datastax/driver/core/Clock.java
@@ -48,7 +48,7 @@ class ClockFactory {
private static final String USE_NATIVE_CLOCK_SYSTEM_PROPERTY = "com.datastax.driver.USE_NATIVE_CLOCK";
static Clock newInstance() {
- if (Native.isLibCLoaded() && SystemProperties.getBoolean(USE_NATIVE_CLOCK_SYSTEM_PROPERTY, true)) {
+ if (Native.isGettimeofdayAvailable() && SystemProperties.getBoolean(USE_NATIVE_CLOCK_SYSTEM_PROPERTY, true)) {
LOGGER.info("Using native clock to generate timestamps.");
return new NativeClock();
} else {
diff --git a/driver-core/src/main/java/com/datastax/driver/core/Native.java b/driver-core/src/main/java/com/datastax/driver/core/Native.java
index f638082e384..96101304328 100644
--- a/driver-core/src/main/java/com/datastax/driver/core/Native.java
+++ b/driver-core/src/main/java/com/datastax/driver/core/Native.java
@@ -24,25 +24,49 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.lang.reflect.Method;
+
/**
- * Helper class to deal with native system call through JNR.
+ * Helper class to deal with native system calls through the
+ * JNR library.
+ *
+ * The driver can benefit from native system calls to improve its performance and accuracy
+ * in some situations.
+ *
+ * Currently, the following features may be used by the driver when available:
+ *
+ * - {@link #currentTimeMicros()}: thanks to a system call to {@code gettimeofday()},
+ * the driver is able to generate timestamps with true microsecond precision
+ * (see {@link AtomicMonotonicTimestampGenerator} or {@link ThreadLocalMonotonicTimestampGenerator} for
+ * more information);
+ * - {@link #processId()}: thanks to a system call to {@code getpid()},
+ * the driver has access to the JVM's process ID it is running under – which
+ * makes time-based UUID generation easier and more reliable (see {@link com.datastax.driver.core.utils.UUIDs UUIDs}
+ * for more information).
+ *
+ *
+ * The availability of the aforementioned system calls depends on the underlying operation system's
+ * capabilities. For instance, {@code gettimeofday()} is not available under Windows systems.
+ * You can check if any of the system calls exposed through this class is available
+ * by calling {@link #isGettimeofdayAvailable()} or {@link #isGetpidAvailable()}.
+ *
+ * Note: This class is public because it needs to be accessible from other packages of the Java driver,
+ * but it is not meant to be used directly by client code.
+ *
+ * @see JNR library on Github
*/
-class Native {
+public final class Native {
private static final Logger LOGGER = LoggerFactory.getLogger(Native.class);
- /**
- * Interface for LIBC calls through JNR.
- * Note that this interface must be declared public.
- */
- public interface LibC {
+ private static class LibCLoader {
/**
* Timeval struct.
*
* @see GETTIMEOFDAY(2)
*/
- class Timeval extends Struct {
+ static class Timeval extends Struct {
public final time_t tv_sec = new time_t();
@@ -54,56 +78,167 @@ public Timeval(Runtime runtime) {
}
/**
- * JNR call to {@code gettimeofday}.
- *
- * @param tv Timeval struct
- * @param unused Timezone struct (unused)
- * @return 0 for success, or -1 for failure
- * @see GETTIMEOFDAY(2)
+ * Interface for LIBC calls through JNR.
+ * Note that this interface must be declared public.
*/
- int gettimeofday(@Out @Transient Timeval tv, Pointer unused);
+ public interface LibC {
+
+ /**
+ * JNR call to {@code gettimeofday}.
+ *
+ * @param tv Timeval struct
+ * @param unused Timezone struct (unused)
+ * @return 0 for success, or -1 for failure
+ * @see GETTIMEOFDAY(2)
+ */
+ int gettimeofday(@Out @Transient Timeval tv, Pointer unused);
+
+ }
+
+ private static final LibC LIB_C;
+
+ private static final Runtime LIB_C_RUNTIME;
+
+ private static final boolean GETTIMEOFDAY_AVAILABLE;
+
+ static {
+ LibC libc;
+ Runtime runtime = null;
+ try {
+ libc = LibraryLoader.create(LibC.class).load("c");
+ runtime = Runtime.getRuntime(libc);
+ } catch (Throwable t) {
+ libc = null; // dereference proxy to library if runtime could not be loaded
+ if (LOGGER.isDebugEnabled())
+ LOGGER.debug("Could not load JNR C Library, native system calls through this library will not be available", t);
+ else
+ LOGGER.info("Could not load JNR C Library, native system calls through this library will not be available " +
+ "(set this logger level to DEBUG to see the full stack trace).");
+
+ }
+ LIB_C = libc;
+ LIB_C_RUNTIME = runtime;
+ boolean gettimeofday = false;
+ if (LIB_C_RUNTIME != null) {
+ try {
+ gettimeofday = LIB_C.gettimeofday(new Timeval(LIB_C_RUNTIME), null) == 0;
+ } catch (Throwable t) {
+ if (LOGGER.isDebugEnabled())
+ LOGGER.debug("Native calls to gettimeofday() not available on this system.", t);
+ else
+ LOGGER.info("Native calls to gettimeofday() not available on this system " +
+ "(set this logger level to DEBUG to see the full stack trace).");
+ }
+ }
+ GETTIMEOFDAY_AVAILABLE = gettimeofday;
+ }
+
}
- private static final LibC LIB_C;
+ private static class PosixLoader {
- private static final Runtime LIB_C_RUNTIME;
+ public static final jnr.posix.POSIX POSIX;
- static {
- LibC libc = null;
- Runtime runtime = null;
+ private static final boolean GETPID_AVAILABLE;
+
+ static {
+ jnr.posix.POSIX posix;
+ try {
+ // use reflection below to get the classloader a chance to load this class
+ Class> posixHandler = Class.forName("jnr.posix.POSIXHandler");
+ Class> defaultPosixHandler = Class.forName("jnr.posix.util.DefaultPOSIXHandler");
+ Class> posixFactory = Class.forName("jnr.posix.POSIXFactory");
+ Method getPOSIX = posixFactory.getMethod("getPOSIX", posixHandler, Boolean.TYPE);
+ posix = (jnr.posix.POSIX) getPOSIX.invoke(null, defaultPosixHandler.newInstance(), true);
+ } catch (Throwable t) {
+ posix = null;
+ if (LOGGER.isDebugEnabled())
+ LOGGER.debug("Could not load JNR POSIX Library, native system calls through this library will not be available.", t);
+ else
+ LOGGER.info("Could not load JNR POSIX Library, native system calls through this library will not be available " +
+ "(set this logger level to DEBUG to see the full stack trace).");
+ }
+ POSIX = posix;
+ boolean getpid = false;
+ if (POSIX != null) {
+ try {
+ POSIX.getpid();
+ getpid = true;
+ } catch (Throwable t) {
+ if (LOGGER.isDebugEnabled())
+ LOGGER.debug("Native calls to getpid() not available on this system.", t);
+ else
+ LOGGER.info("Native calls to getpid() not available on this system " +
+ "(set this logger level to DEBUG to see the full stack trace).");
+ }
+ }
+ GETPID_AVAILABLE = getpid;
+ }
+
+ }
+
+ /**
+ * Returns {@code true} if JNR C library is loaded and
+ * a call to {@code gettimeofday} is possible through this library
+ * on this system, and {@code false} otherwise.
+ *
+ * @return {@code true} if JNR C library is loaded and
+ * a call to {@code gettimeofday} is possible.
+ */
+ public static boolean isGettimeofdayAvailable() {
try {
- libc = LibraryLoader.create(LibC.class).load("c");
- runtime = Runtime.getRuntime(libc);
- } catch (Throwable t) {
- if (LOGGER.isDebugEnabled())
- LOGGER.debug("Could not load JNR LibC Library, native calls will not be available", t);
- else
- LOGGER.info("Could not load JNR LibC Library, native calls will not be available (set this logger level to DEBUG to see the full stack trace)");
+ return LibCLoader.GETTIMEOFDAY_AVAILABLE;
+ } catch (NoClassDefFoundError e) {
+ return false;
}
- LIB_C = libc;
- LIB_C_RUNTIME = runtime;
}
/**
- * Returns true if LibC could be loaded with JNR.
+ * Returns {@code true} if JNR POSIX library is loaded and
+ * a call to {@code getpid} is possible through this library
+ * on this system, and {@code false} otherwise.
*
- * @return true if LibC could be loaded with JNR, false otherwise.
+ * @return {@code true} if JNR POSIX library is loaded and
+ * a call to {@code getpid} is possible.
*/
- static boolean isLibCLoaded() {
- return LIB_C_RUNTIME != null;
+ public static boolean isGetpidAvailable() {
+ try {
+ return PosixLoader.GETPID_AVAILABLE;
+ } catch (NoClassDefFoundError e) {
+ return false;
+ }
+
}
/**
* Returns the current timestamp with microsecond precision
- * via a system call to {@code gettimeofday}.
+ * via a system call to {@code gettimeofday}, through JNR C library.
*
* @return the current timestamp with microsecond precision.
+ * @throws UnsupportedOperationException if JNR C library is not loaded or {@code gettimeofday} is not available.
+ * @throws IllegalStateException if the call to {@code gettimeofday} did not complete with return code 0.
*/
- static long currentTimeMicros() {
- LibC.Timeval tv = new LibC.Timeval(LIB_C_RUNTIME);
- if (LIB_C.gettimeofday(tv, null) != 0)
- LOGGER.error("gettimeofday failed");
+ public static long currentTimeMicros() {
+ if (!isGettimeofdayAvailable())
+ throw new UnsupportedOperationException("JNR C library not loaded or gettimeofday not available");
+ LibCLoader.Timeval tv = new LibCLoader.Timeval(LibCLoader.LIB_C_RUNTIME);
+ int res = LibCLoader.LIB_C.gettimeofday(tv, null);
+ if (res != 0)
+ throw new IllegalStateException("Call to gettimeofday failed with result " + res);
return tv.tv_sec.get() * 1000000 + tv.tv_usec.get();
}
+ /**
+ * Returns the JVM's process identifier (PID)
+ * via a system call to {@code getpid}.
+ *
+ * @return the JVM's process identifier (PID).
+ * @throws UnsupportedOperationException if JNR POSIX library is not loaded or {@code getpid} is not available.
+ */
+ public static int processId() {
+ if (!isGetpidAvailable())
+ throw new UnsupportedOperationException("JNR POSIX library not loaded or getpid not available");
+ return PosixLoader.POSIX.getpid();
+ }
+
}
diff --git a/driver-core/src/main/java/com/datastax/driver/core/utils/UUIDs.java b/driver-core/src/main/java/com/datastax/driver/core/utils/UUIDs.java
index 1a648356fe8..58383af1a5e 100644
--- a/driver-core/src/main/java/com/datastax/driver/core/utils/UUIDs.java
+++ b/driver-core/src/main/java/com/datastax/driver/core/utils/UUIDs.java
@@ -15,8 +15,12 @@
*/
package com.datastax.driver.core.utils;
+import com.datastax.driver.core.Native;
import com.google.common.base.Charsets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
@@ -27,17 +31,43 @@
import java.util.concurrent.atomic.AtomicLong;
/**
- * Utility methods to work with UUID and most specifically with time-based ones
- * (version 1).
+ * Utility methods to help working with UUIDs, and more specifically, with time-based UUIDs
+ * (also known as Version 1 UUIDs).
+ * Notes on the algorithm used to generate time-based UUIDs
+ * The algorithm follows roughly the description in RFC-4122, but with the following adaptations:
+ *
+ * - Since Java does not provide direct access to the host's MAC address, that information
+ * is replaced with a digest of all IP addresses available on the host;
+ * - The process ID (PID) isn't easily available to Java either, so it is determined by one of the
+ * following methods, in the order they are listed below:
+ *
+ * - If the System property
{@value PID_SYSTEM_PROPERTY} is set then the value to use as a PID
+ * will be read from that property;
+ * - Otherwise, if a native call to {@link Native#processId() getpid()} is possible, then the PID
+ * will be read from that call;
+ * - Otherwise, an attempt will be made to read the PID from JMX's
+ * {@link ManagementFactory#getRuntimeMXBean() RuntimeMXBean}, which is a well-known,
+ * yet undocumented "hack", since most JVMs tend to use the JVM's PID as part of that MXBean name;
+ * - If all of the above fails, a random integer will be generated and used as a surrogate PID.
+ *
+ *
+ *
+ *
+ * @jira_ticket JAVA-444
+ * @see A Universally Unique IDentifier (UUID) URN Namespace (RFC 4122)
*/
public final class UUIDs {
+ /**
+ * The System property to use to force the value of the process ID (PID).
+ */
+ public static final String PID_SYSTEM_PROPERTY = "com.datastax.driver.PID";
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(UUIDs.class);
+
private UUIDs() {
}
- ;
-
- // http://www.ietf.org/rfc/rfc4122.txt
private static final long START_EPOCH = makeEpoch();
private static final long CLOCK_SEQ_AND_NODE = makeClockSeqAndNode();
@@ -94,6 +124,7 @@ private static long makeNode() {
update(digest, props.getProperty("os.arch"));
update(digest, props.getProperty("os.name"));
update(digest, props.getProperty("os.version"));
+ update(digest, getProcessPiece());
byte[] hash = digest.digest();
@@ -108,6 +139,43 @@ private static long makeNode() {
}
}
+ private static String getProcessPiece() {
+ Integer pid = null;
+ String pidProperty = System.getProperty(PID_SYSTEM_PROPERTY);
+ if (pidProperty != null) {
+ try {
+ pid = Integer.parseInt(pidProperty);
+ LOGGER.info("PID obtained from System property {}: {}", PID_SYSTEM_PROPERTY, pid);
+ } catch (NumberFormatException e) {
+ LOGGER.warn("Incorrect integer specified for PID in System property {}: {}", PID_SYSTEM_PROPERTY, pidProperty);
+ }
+ }
+ if (pid == null && Native.isGetpidAvailable()) {
+ try {
+ pid = Native.processId();
+ LOGGER.info("PID obtained through native call to getpid(): {}", pid);
+ } catch (Exception e) {
+ LOGGER.warn("Native call to getpid() failed", e);
+ }
+ }
+ if (pid == null) {
+ try {
+ String pidJmx = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
+ pid = Integer.parseInt(pidJmx);
+ LOGGER.info("PID obtained through JMX: {}", pid);
+ } catch (Exception e) {
+ LOGGER.warn("Failed to obtain PID from JMX", e);
+ }
+ }
+ if (pid == null) {
+ pid = new java.util.Random().nextInt();
+ LOGGER.warn("Could not determine PID, falling back to a random integer: {}", pid);
+ }
+ ClassLoader loader = UUIDs.class.getClassLoader();
+ int loaderId = loader != null ? System.identityHashCode(loader) : 0;
+ return Integer.toHexString(pid) + Integer.toHexString(loaderId);
+ }
+
private static void update(MessageDigest digest, String value) {
if (value != null)
digest.update(value.getBytes(Charsets.UTF_8));
@@ -138,7 +206,7 @@ public static UUID random() {
/**
* Creates a new time-based (version 1) UUID.
*
- * UUID generated by this method are suitable for use with the
+ * UUIDs generated by this method are suitable for use with the
* {@code timeuuid} Cassandra type. In particular the generated UUID
* includes the timestamp of its generation.
*
@@ -162,29 +230,30 @@ public static UUID timeBased() {
* Creates a "fake" time-based UUID that sorts as the smallest possible
* version 1 UUID generated at the provided timestamp.
*
- * Such created UUID are useful in queries to select a time range of a
+ * Such created UUIDs are useful in queries to select a time range of a
* {@code timeuuid} column.
*
- * The UUID created by this method are not unique and as such are
+ * The UUIDs created by this method are not unique and as such are
* not suitable for anything else than querying a specific time
- * range. In particular, you should not insert such UUID. "True" UUID from
+ * range. In particular, you should not insert such UUIDs. "True" UUIDs from
* user-provided timestamps are not supported (see {@link #timeBased()}
* for more explanations).
*
- * Also, the timestamp to provide as parameter must be a unix timestamp (as
- * returned by {@link System#currentTimeMillis} or {@link java.util.Date#getTime}),
- * not a UUID 100-nanoseconds interval since 15 October 1582. In other
- * words, given a UUID {@code uuid}, you should never do
+ * Also, the timestamp to provide as a parameter must be a Unix timestamp (as
+ * returned by {@link System#currentTimeMillis} or {@link java.util.Date#getTime}), and
+ * not a count of 100-nanosecond intervals since 00:00:00.00, 15 October 1582 (as required by RFC-4122).
+ *
+ * In other words, given a UUID {@code uuid}, you should never call
* {@code startOf(uuid.timestamp())} but rather
* {@code startOf(unixTimestamp(uuid))}.
*
- * Lastly, please note that Cassandra's timeuuid sorting is not compatible
- * with {@link UUID#compareTo} and hence the UUID created by this method
+ * Lastly, please note that Cassandra's {@code timeuuid} sorting is not compatible
+ * with {@link UUID#compareTo} and hence the UUIDs created by this method
* are not necessarily lower bound for that latter method.
*
- * @param timestamp the unix timestamp for which the created UUID must be a
+ * @param timestamp the Unix timestamp for which the created UUID must be a
* lower bound.
- * @return the smallest (for Cassandra timeuuid sorting) UUID of {@code timestamp}.
+ * @return the smallest (for Cassandra {@code timeuuid} sorting) UUID of {@code timestamp}.
*/
public static UUID startOf(long timestamp) {
return new UUID(makeMSB(fromUnixTimestamp(timestamp)), MIN_CLOCK_SEQ_AND_NODE);
@@ -196,9 +265,9 @@ public static UUID startOf(long timestamp) {
*
* See {@link #startOf(long)} for explanations about the intended usage of such UUID.
*
- * @param timestamp the unix timestamp for which the created UUID must be an
+ * @param timestamp the Unix timestamp for which the created UUID must be an
* upper bound.
- * @return the biggest (for Cassandra timeuuid sorting) UUID of {@code timestamp}.
+ * @return the biggest (for Cassandra {@code timeuuid} sorting) UUID of {@code timestamp}.
*/
public static UUID endOf(long timestamp) {
long uuidTstamp = fromUnixTimestamp(timestamp + 1) - 1;
@@ -206,20 +275,20 @@ public static UUID endOf(long timestamp) {
}
/**
- * Return the unix timestamp contained by the provided time-based UUID.
+ * Return the Unix timestamp contained by the provided time-based UUID.
*
- * This method is not equivalent to {@code uuid.timestamp()}. More
+ * This method is not equivalent to {@link UUID#timestamp()}. More
* precisely, a version 1 UUID stores a timestamp that represents the
* number of 100-nanoseconds intervals since midnight, 15 October 1582 and
- * that is what {@code uuid.timestamp()} returns. This method however
- * converts that timestamp to the equivalent unix timestamp in
+ * that is what {@link UUID#timestamp()} returns. This method however
+ * converts that timestamp to the equivalent Unix timestamp in
* milliseconds, i.e. a timestamp representing a number of milliseconds
- * since midnight, January 1, 1970 UTC. In particular the timestamps
- * returned by this method are comparable to the timestamp returned by
+ * since midnight, January 1, 1970 UTC. In particular, the timestamps
+ * returned by this method are comparable to the timestamps returned by
* {@link System#currentTimeMillis}, {@link java.util.Date#getTime}, etc.
*
* @param uuid the UUID to return the timestamp of.
- * @return the unix timestamp of {@code uuid}.
+ * @return the Unix timestamp of {@code uuid}.
* @throws IllegalArgumentException if {@code uuid} is not a version 1 UUID.
*/
public static long unixTimestamp(UUID uuid) {
@@ -231,7 +300,7 @@ public static long unixTimestamp(UUID uuid) {
}
/*
- * Note that currently we use System.currentTimeMillis() for a base time in
+ * Note that currently we use {@link System#currentTimeMillis} for a base time in
* milliseconds, and then if we are in the same milliseconds that the
* previous generation, we increment the number of nanoseconds.
* However, since the precision is 100-nanoseconds intervals, we can only
diff --git a/driver-core/src/test/java/com/datastax/driver/core/ClockFactoryTest.java b/driver-core/src/test/java/com/datastax/driver/core/ClockFactoryTest.java
new file mode 100644
index 00000000000..58623e6332c
--- /dev/null
+++ b/driver-core/src/test/java/com/datastax/driver/core/ClockFactoryTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012-2015 DataStax Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.datastax.driver.core;
+
+import org.testng.SkipException;
+import org.testng.annotations.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ClockFactoryTest {
+
+ final String osName = System.getProperty("os.name");
+
+ @Test(groups = "unit")
+ public void should_use_native_clock_on_unix_platforms() {
+ // Cowardly assume any non-windows platform will support gettimeofday. If we find one that doesn't,
+ // we will have learned something.
+ if (osName.startsWith("Windows")) {
+ throw new SkipException("Skipping test for Windows platforms.");
+ }
+ Clock clock = ClockFactory.newInstance();
+ assertThat(clock).isInstanceOf(NativeClock.class);
+ assertThat(clock.currentTimeMicros()).isGreaterThan(0);
+ }
+
+ @Test(groups = "unit")
+ public void should_fallback_on_system_clock_on_windows_platforms() {
+ if (!osName.startsWith("Windows")) {
+ throw new SkipException("Skipping test for non-Windows platforms.");
+ }
+ Clock clock = ClockFactory.newInstance();
+ assertThat(clock).isInstanceOf(SystemClock.class);
+ assertThat(clock.currentTimeMicros()).isGreaterThan(0);
+ }
+}
diff --git a/driver-core/src/test/java/com/datastax/driver/core/utils/UUIDsPIDFromPropertyTest.java b/driver-core/src/test/java/com/datastax/driver/core/utils/UUIDsPIDFromPropertyTest.java
new file mode 100644
index 00000000000..c24ee805cfb
--- /dev/null
+++ b/driver-core/src/test/java/com/datastax/driver/core/utils/UUIDsPIDFromPropertyTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012-2015 DataStax Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.datastax.driver.core.utils;
+
+import com.datastax.driver.core.MemoryAppender;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.testng.annotations.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class UUIDsPIDFromPropertyTest {
+
+ private static final Logger logger = Logger.getLogger(UUIDs.class);
+
+ @Test(groups = "isolated")
+ public void should_obtain_pid_from_system_property() {
+ // If the com.datastax.driver.PID property is set, it should be used and this should be logged.
+ MemoryAppender appender = new MemoryAppender();
+ Level originalLevel = logger.getLevel();
+ try {
+ logger.setLevel(Level.INFO);
+ logger.addAppender(appender);
+ int pid = 8675;
+ System.setProperty(UUIDs.PID_SYSTEM_PROPERTY, "" + pid);
+ UUIDs.timeBased();
+ assertThat(appender.get())
+ .containsOnlyOnce(String.format("PID obtained from System property %s: %d",
+ UUIDs.PID_SYSTEM_PROPERTY, pid));
+ } finally {
+ logger.removeAppender(appender);
+ logger.setLevel(originalLevel);
+ }
+ }
+}
diff --git a/driver-core/src/test/java/com/datastax/driver/core/utils/UUIDsPIDNativeTest.java b/driver-core/src/test/java/com/datastax/driver/core/utils/UUIDsPIDNativeTest.java
new file mode 100644
index 00000000000..013fa8eb572
--- /dev/null
+++ b/driver-core/src/test/java/com/datastax/driver/core/utils/UUIDsPIDNativeTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2012-2015 DataStax Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.datastax.driver.core.utils;
+
+import com.datastax.driver.core.MemoryAppender;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.testng.annotations.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class UUIDsPIDNativeTest {
+
+ private static final Logger logger = Logger.getLogger(UUIDs.class);
+
+ @Test(groups = "isolated")
+ public void should_obtain_pid_through_native_call() {
+ // In the general case the JNR call should *just* work as most systems should support POSIX getpid.
+ MemoryAppender appender = new MemoryAppender();
+ Level originalLevel = logger.getLevel();
+ try {
+ logger.setLevel(Level.INFO);
+ logger.addAppender(appender);
+ UUIDs.timeBased();
+
+ assertThat(appender.get()).containsOnlyOnce("PID obtained through native call to getpid()");
+ } finally {
+ logger.removeAppender(appender);
+ logger.setLevel(originalLevel);
+ }
+ }
+}
diff --git a/driver-core/src/test/java/com/datastax/driver/core/utils/UUIDsPIDPropertyInvalidTest.java b/driver-core/src/test/java/com/datastax/driver/core/utils/UUIDsPIDPropertyInvalidTest.java
new file mode 100644
index 00000000000..1007d0c14b0
--- /dev/null
+++ b/driver-core/src/test/java/com/datastax/driver/core/utils/UUIDsPIDPropertyInvalidTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2012-2015 DataStax Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.datastax.driver.core.utils;
+
+import com.datastax.driver.core.MemoryAppender;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.testng.annotations.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class UUIDsPIDPropertyInvalidTest {
+
+ private static final Logger logger = Logger.getLogger(UUIDs.class);
+
+ @Test(groups = "isolated")
+ public void should_fallback_on_native_call_if_system_property_invalid() {
+ // If the com.datastax.driver.PID property is set, but is invalid, it should fallback onto native getpid().
+ MemoryAppender appender = new MemoryAppender();
+ Level originalLevel = logger.getLevel();
+ try {
+ logger.setLevel(Level.INFO);
+ logger.addAppender(appender);
+ String pid = "NOT_A_PID";
+ System.setProperty(UUIDs.PID_SYSTEM_PROPERTY, pid);
+ UUIDs.timeBased();
+ assertThat(appender.get())
+ .containsOnlyOnce(String.format("Incorrect integer specified for PID in System property %s: %s",
+ UUIDs.PID_SYSTEM_PROPERTY, pid))
+ .containsOnlyOnce("PID obtained through native call to getpid()");
+ } finally {
+ logger.removeAppender(appender);
+ logger.setLevel(originalLevel);
+ }
+ }
+}
diff --git a/pom.xml b/pom.xml
index f97d8d4b5f2..249c21f8ccb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -57,6 +57,7 @@
1.2.0
2.1.4
2.0.7
+ 3.0.27
6.8.8
1.7.0