Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,25 @@ JUG supports 3 original official UUID generation methods as well as later additi
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- For testing, JUnit is needed -->
<!-- For testing, JUnit 5 is needed -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${version.junit}</version>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${version.junit5}</version>
<scope>test</scope>
</dependency>
<!-- For JUnit 4 parameterized tests migration -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${version.junit5}</version>
<scope>test</scope>
</dependency>
<!-- For Hamcrest matchers used in tests -->
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down Expand Up @@ -184,6 +198,12 @@ https://stackoverflow.com/questions/37958104/maven-javadoc-no-source-files-for-p
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>perf/**</exclude>
<exclude>test/**</exclude>
</excludes>
</configuration>
<executions>
<execution>
<goals>
Expand Down
1 change: 1 addition & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Releases

#124: TimeBasedEpochGenerator should prevent overflow
(Chad P)
#139: Upgrade tests from JUnit 4 to JUnit 5
- Update to `oss-parent` v69

5.1.1 (26-Sep-2025)
Expand Down
76 changes: 60 additions & 16 deletions src/main/java/com/fasterxml/uuid/ext/LockedFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@
*/
class LockedFile
{
private static final Logger LOGGER = LoggerFactory.getLogger(LockedFile.class);

private static final Logger logger = LoggerFactory.getLogger(LockedFile.class);
// Wrapper just to allow test(s) to disable/re-route
static LoggerWrapper logger = new LoggerWrapper(LOGGER);

/**
* Expected file length comes from hex-timestamp (16 digits),
Expand Down Expand Up @@ -115,6 +117,11 @@ class LockedFile
mLock = lock;
}

// @since 5.2 for testing (for `LockedFileTest`)
static void logging(boolean enabled) {
logger = new LoggerWrapper(enabled ? LOGGER : null);
}

public void deactivate()
{
RandomAccessFile raf = mRAFile;
Expand All @@ -131,7 +138,7 @@ public long readStamp()
try {
size = (int) mChannel.size();
} catch (IOException ioe) {
logger.error("Failed to read file size", ioe);
logger.error(ioe, "Failed to read file size");
return READ_ERROR;
}

Expand All @@ -151,7 +158,7 @@ public long readStamp()
try {
mRAFile.readFully(data);
} catch (IOException ie) {
logger.error("(file '{}') Failed to read {} bytes", mFile, size, ie);
logger.error(ie, "(file '"+mFile+"') Failed to read "+size+" bytes");
return READ_ERROR;
}

Expand All @@ -168,25 +175,25 @@ public long readStamp()
dataStr = dataStr.trim();

long result = -1;
String err = null;
String errMsg = null;

if (!dataStr.startsWith("[0")
|| dataStr.length() < 3
|| Character.toLowerCase(dataStr.charAt(2)) != 'x') {
err = "does not start with '[0x' prefix";
errMsg = "does not start with '[0x' prefix";
} else {
int ix = dataStr.indexOf(']', 3);
if (ix <= 0) {
err = "does not end with ']' marker";
errMsg = "does not end with ']' marker";
} else {
String hex = dataStr.substring(3, ix);
if (hex.length() > 16) {
err = "length of the (hex) timestamp too long; expected 16, had "+hex.length()+" ('"+hex+"')";
errMsg = "length of the (hex) timestamp too long; expected 16, had "+hex.length()+" ('"+hex+"')";
} else {
try {
result = Long.parseLong(hex, 16);
} catch (NumberFormatException nex) {
err = "does not contain a valid hex timestamp; got '"
errMsg = "does not contain a valid hex timestamp; got '"
+hex+"' (parse error: "+nex+")";
}
}
Expand All @@ -195,7 +202,7 @@ public long readStamp()

// Unsuccesful?
if (result < 0L) {
logger.error("(file '{}') Malformed timestamp file contents: {}", mFile, err);
logger.error("(file '{}') Malformed timestamp file contents: {}", mFile, errMsg);
return READ_ERROR;
}

Expand All @@ -210,12 +217,11 @@ public void writeStamp(long stamp)
{
// Let's do sanity check first:
if (stamp <= mLastTimestamp) {
/* same stamp is not dangerous, but pointless... so warning,
* not an error:
*/
// same stamp is not dangerous, but pointless... so warning,
// not an error:
if (stamp == mLastTimestamp) {
logger.warn("(file '{}') Trying to re-write existing timestamp ({})", mFile, stamp);
return;
return;
}
throw new IOException(""+mFile+" trying to overwrite existing value ("+mLastTimestamp+") with an earlier timestamp ("+stamp+")");
}
Expand Down Expand Up @@ -260,20 +266,58 @@ public void writeStamp(long stamp)
*/

protected static void doDeactivate(File f, RandomAccessFile raf,
FileLock lock)
FileLock lock)
{
if (lock != null) {
try {
lock.release();
} catch (Throwable t) {
logger.error("Failed to release lock (for file '{}')", f, t);
logger.error(t, "Failed to release lock (for file '"+f+"')");
}
}
if (raf != null) {
try {
raf.close();
} catch (Throwable t) {
logger.error("Failed to close file '{}'", f, t);
logger.error(t, "Failed to close file '{"+f+"}'");
}
}
}

/*
//////////////////////////////////////////////////////////////
// Helper class(es)
//////////////////////////////////////////////////////////////
*/

private static class LoggerWrapper {
private final Logger logger;

public LoggerWrapper(Logger l) {
logger = l;
}

public void error(Throwable t, String msg) {
if (logger != null) {
logger.error(msg, t);
}
}

public void error(String msg, Object arg1, Object arg2) {
if (logger != null) {
logger.error(msg, arg1, arg2);
}
}

public void warn(String msg) {
if (logger != null) {
logger.warn(msg);
}
}

public void warn(String msg, Object arg1, Object arg2) {
if (logger != null) {
logger.warn(msg, arg1, arg2);
}
}
}
Expand Down
38 changes: 23 additions & 15 deletions src/test/java/com/fasterxml/uuid/EgressInterfaceFinderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,58 +2,61 @@

import com.fasterxml.uuid.EgressInterfaceFinder.EgressResolutionException;
import com.fasterxml.uuid.EgressInterfaceFinder.Finder;
import junit.framework.TestCase;
import org.junit.jupiter.api.Test;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;

import static com.fasterxml.uuid.EgressInterfaceFinder.DEFAULT_TIMEOUT_MILLIS;
import static org.junit.jupiter.api.Assertions.*;

public class EgressInterfaceFinderTest extends TestCase {
public class EgressInterfaceFinderTest {

private final EgressInterfaceFinder finder = new EgressInterfaceFinder();

@Test
public void testUnspecifiedIPv4LocalAddress() throws UnknownHostException {
EgressResolutionException ex = null;
try {
finder.fromLocalAddress(InetAddress.getByName("0.0.0.0"));
} catch (EgressResolutionException e) {
ex = e;
}
assertNotNull("EgressResolutionException was not thrown", ex);
assertNotNull(ex, "EgressResolutionException was not thrown");
String message = ex.getMessage();
assertTrue(String.format(
assertTrue(message.startsWith("local address"), String.format(
"message [%s] does not begin with \"local address\"",
message),
message.startsWith("local address"));
message));
assertEquals(1, ex.getMessages().size());
}

@Test
public void testUnspecifiedIPv6LocalAddress() throws Exception {
EgressResolutionException ex = null;
try {
finder.fromLocalAddress(InetAddress.getByName("::"));
} catch (EgressResolutionException e) {
ex = e;
}
assertNotNull("EgressResolutionException was not thrown", ex);
assertNotNull(ex, "EgressResolutionException was not thrown");
String message = ex.getMessage();
assertTrue(String.format(
assertTrue(message.startsWith("local address"), String.format(
"message [%s] does not begin with \"local address\"",
message),
message.startsWith("local address"));
message));
assertEquals(1, ex.getMessages().size());
}

@Test
public void testFromLocalAddress() throws Exception {
NetworkInterface anInterface =
NetworkInterface.getNetworkInterfaces().nextElement();
InetAddress anAddress = anInterface.getInetAddresses().nextElement();
assertEquals(anInterface, finder.fromLocalAddress(anAddress));
}

@Test
public void testFromIncorrectLocalAddress() throws Exception {
EgressResolutionException ex = null;
try {
Expand All @@ -62,15 +65,15 @@ public void testFromIncorrectLocalAddress() throws Exception {
} catch (EgressResolutionException e) {
ex = e;
}
assertNotNull("EgressResolutionException was not thrown", ex);
assertNotNull(ex, "EgressResolutionException was not thrown");
String message = ex.getMessage();
assertTrue(String.format(
assertTrue(message.startsWith("no interface found"), String.format(
"message [%s] does not begin with \"no interface found\"",
message),
message.startsWith("no interface found"));
message));
assertEquals(1, ex.getMessages().size());
}

@Test
public void testFromRemoteDatagramSocketConnection() throws Exception {
if (!System.getProperty("os.name").startsWith("Mac")) {
String name = EgressInterfaceFinder.randomRootServerName();
Expand All @@ -79,22 +82,26 @@ public void testFromRemoteDatagramSocketConnection() throws Exception {
}
}

@Test
public void testFromRemoteSocketConnection() throws Exception {
String name = EgressInterfaceFinder.randomRootServerName();
InetSocketAddress address = new InetSocketAddress(name, 53);
finder.fromRemoteSocketConnection(DEFAULT_TIMEOUT_MILLIS, address);
}

@Test
public void testFromRemoteConnection() throws Exception {
String name = EgressInterfaceFinder.randomRootServerName();
InetSocketAddress address = new InetSocketAddress(name, 53);
finder.fromRemoteConnection(DEFAULT_TIMEOUT_MILLIS, address);
}

@Test
public void testFromRootNameServerConnection() throws Exception {
finder.fromRootNameserverConnection(DEFAULT_TIMEOUT_MILLIS);
}

@Test
public void testAggregateExceptions() {
EgressResolutionException ex = null;
final int[] counter = {0};
Expand All @@ -112,10 +119,11 @@ public NetworkInterface egressInterface()
} catch (EgressResolutionException e) {
ex = e;
}
assertNotNull("EgressResolutionException was not thrown", ex);
assertNotNull(ex, "EgressResolutionException was not thrown");
assertEquals(9, ex.getMessages().size());
}

@Test
public void testDefaultMechanisms() throws Exception {
try {
finder.egressInterface();
Expand Down
Loading