diff --git a/src/main/java/io/ebean/ExtendedServer.java b/src/main/java/io/ebean/ExtendedServer.java
index 1d3d6264f..0b159f877 100644
--- a/src/main/java/io/ebean/ExtendedServer.java
+++ b/src/main/java/io/ebean/ExtendedServer.java
@@ -3,6 +3,7 @@ package io.ebean;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.persistence.NonUniqueResultException;
+import java.time.Clock;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -28,6 +29,19 @@ import java.util.function.Predicate;
*/
public interface ExtendedServer {
+ /**
+ * Return the NOW time from the Clock.
+ */
+ long clockNow();
+
+ /**
+ * Set the Clock to use for @WhenCreated and @WhenModified.
+ *
+ * Note that we only expect to change the Clock for testing purposes.
+ *
+ */
+ void setClock(Clock clock);
+
/**
* Return the number of 'top level' or 'root' entities this query should return.
*
@@ -370,7 +384,7 @@ public interface ExtendedServer {
/**
* Execute the update query returning the number of rows updated.
*
- * The update query must be created using {@link #update(Class)}.
+ * The update query must be created using {@link EbeanServer#update(Class)}.
*
*
* @param query the update query to execute
diff --git a/src/main/java/io/ebean/config/ServerConfig.java b/src/main/java/io/ebean/config/ServerConfig.java
index bfb0b3999..0d0bbd70e 100644
--- a/src/main/java/io/ebean/config/ServerConfig.java
+++ b/src/main/java/io/ebean/config/ServerConfig.java
@@ -33,6 +33,7 @@ import io.ebean.util.StringHelper;
import org.avaje.datasource.DataSourceConfig;
import javax.sql.DataSource;
+import java.time.Clock;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -362,6 +363,11 @@ public class ServerConfig {
*/
private String uuidStateFile = "ebean-uuid.state";
+ /**
+ * The clock used for setting the timestamps (e.g. @UpdatedTimestamp) on objects.
+ */
+ private Clock clock = Clock.systemUTC();
+
private List idGenerators = new ArrayList<>();
private List findControllers = new ArrayList<>();
private List persistControllers = new ArrayList<>();
@@ -507,6 +513,20 @@ public class ServerConfig {
}
+ /**
+ * Get the clock used for setting the timestamps (e.g. @UpdatedTimestamp) on objects.
+ */
+ public Clock getClock() {
+ return clock;
+ }
+
+ /**
+ * Set the clock used for setting the timestamps (e.g. @UpdatedTimestamp) on objects.
+ */
+ public void setClock(final Clock clock) {
+ this.clock = clock;
+ }
+
/**
* Return the slow query time in millis.
*/
diff --git a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java
index 307cc3d44..89fe28b6b 100644
--- a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java
+++ b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java
@@ -119,10 +119,10 @@ import javax.persistence.NonUniqueResultException;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
+import java.time.Clock;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -151,6 +151,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private final DataTimeZone dataTimeZone;
+ /**
+ * Clock to use for WhenModified and WhenCreated.
+ */
+ private Clock clock;
+
private final CallStackFactory callStackFactory;
/**
@@ -278,6 +283,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
this.beanLoader = new DefaultBeanLoader(this);
this.jsonContext = config.createJsonContext(this);
this.dataTimeZone = config.getDataTimeZone();
+ this.clock = config.getServerConfig().getClock();
DocStoreIntegration docStoreComponents = config.createDocStoreIntegration(this);
this.transactionManager = config.createTransactionManager(docStoreComponents.updateProcessor());
@@ -507,6 +513,16 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return this;
}
+ @Override
+ public long clockNow() {
+ return clock.millis();
+ }
+
+ @Override
+ public void setClock(Clock clock) {
+ this.clock = clock;
+ }
+
@Override
public BeanState getBeanState(Object bean) {
if (bean instanceof EntityBean) {
diff --git a/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java b/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java
index 20c6e04b7..3c658f34d 100644
--- a/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java
+++ b/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java
@@ -1275,7 +1275,7 @@ public final class PersistRequestBean extends PersistRequest implements BeanP
*/
public long now() {
if (now == 0) {
- now = System.currentTimeMillis();
+ now = ebeanServer.clockNow();
}
return now;
}
diff --git a/src/test/java/io/ebean/ExtendedServerTest.java b/src/test/java/io/ebean/ExtendedServerTest.java
index aef467e69..5f756e9ad 100644
--- a/src/test/java/io/ebean/ExtendedServerTest.java
+++ b/src/test/java/io/ebean/ExtendedServerTest.java
@@ -1,15 +1,26 @@
package io.ebean;
+import org.junit.After;
import org.junit.Test;
import org.tests.model.basic.Customer;
import org.tests.model.basic.ResetBasicData;
+import java.time.Clock;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class ExtendedServerTest extends BaseTestCase {
+ @After
+ public void cleanup() {
+ Ebean.getDefaultServer()
+ .extended()
+ .setClock(Clock.systemUTC());
+ }
+
@Test
public void findList() {
@@ -38,4 +49,47 @@ public class ExtendedServerTest extends BaseTestCase {
}
}
+ @Test
+ public void mockClock() {
+
+ EbeanServer server = Ebean.getDefaultServer();
+ final Instant snapshot = Instant.now();
+ Instant backedSnapshot = snapshot.minus(1, ChronoUnit.DAYS);
+ Clock snapshotClock = Clock.fixed(backedSnapshot, Clock.systemUTC().getZone());
+
+ server.extended().setClock(snapshotClock);
+
+ ResetBasicData.reset();
+
+ int count = server
+ .find(Customer.class)
+ .where()
+ .gt("cretime", snapshot)
+ .findCount();
+ assertThat(count).isEqualTo(0);
+
+ int count2 = server
+ .find(Customer.class)
+ .where()
+ .ge("cretime", backedSnapshot)
+ .findCount();
+ assertThat(count2).isGreaterThan(0);
+
+ int count3 = server
+ .find(Customer.class)
+ .where()
+ .gt("updtime", snapshot)
+ .findCount();
+ assertThat(count3).isEqualTo(0);
+
+ int count4 = server
+ .find(Customer.class)
+ .where()
+ .ge("updtime", backedSnapshot)
+ .findCount();
+ assertThat(count4).isGreaterThan(0);
+
+
+ }
+
}
diff --git a/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java b/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java
index c83e84eb8..d52327463 100644
--- a/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java
+++ b/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java
@@ -55,6 +55,7 @@ import io.ebeaninternal.server.transaction.RemoteTransactionEvent;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import java.lang.reflect.Type;
+import java.time.Clock;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -85,6 +86,16 @@ public class TDSpiEbeanServer implements SpiEbeanServer {
return this;
}
+ @Override
+ public long clockNow() {
+ return System.currentTimeMillis();
+ }
+
+ @Override
+ public void setClock(Clock clock) {
+
+ }
+
@Override
public SpiLogManager log() {
return null;