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: + *

    + *
  1. {@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);
  2. + *
  3. {@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).
  4. + *
+ *

+ * 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: + *
    + *
  1. 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;
  2. + *
  3. 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: + *
      + *
    1. If the System property {@value PID_SYSTEM_PROPERTY} is set then the value to use as a PID + * will be read from that property;
    2. + *
    3. Otherwise, if a native call to {@link Native#processId() getpid()} is possible, then the PID + * will be read from that call;
    4. + *
    5. 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;
    6. + *
    7. If all of the above fails, a random integer will be generated and used as a surrogate PID.
    8. + *
    + *
  4. + *
+ * + * @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