This is an automated email from the ASF dual-hosted git repository.

reschke pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git


The following commit(s) were added to refs/heads/trunk by this push:
     new dd791eaa6c  OAK-11260: Create replacement for Guava Stopwatch  (#2218)
dd791eaa6c is described below

commit dd791eaa6c2ad255ee91c363c727f12baef5885f
Author: Julian Reschke <[email protected]>
AuthorDate: Thu Apr 10 11:28:32 2025 +0200

     OAK-11260: Create replacement for Guava Stopwatch  (#2218)
---
 oak-commons/pom.xml                                |   3 +-
 .../jackrabbit/oak/commons/time/Stopwatch.java     | 164 +++++++++++++++++++++
 .../jackrabbit/oak/commons/time/package-info.java  |  28 ++++
 .../commons/{StopWatch.java => TestStopWatch.java} |   2 +-
 .../oak/commons/json/JsopStreamTest.java           |   4 +-
 .../jackrabbit/oak/commons/json/JsopTest.java      |   4 +-
 .../jackrabbit/oak/commons/time/StopwatchTest.java |  90 +++++++++++
 7 files changed, 289 insertions(+), 6 deletions(-)

diff --git a/oak-commons/pom.xml b/oak-commons/pom.xml
index 3b7af3a58d..0f9c378232 100644
--- a/oak-commons/pom.xml
+++ b/oak-commons/pom.xml
@@ -57,7 +57,8 @@
               org.apache.jackrabbit.oak.commons.sort,
               org.apache.jackrabbit.oak.commons.properties,
               org.apache.jackrabbit.oak.commons.jdkcompat,
-              org.apache.jackrabbit.oak.commons.pio
+              org.apache.jackrabbit.oak.commons.pio,
+              org.apache.jackrabbit.oak.commons.time
             </Export-Package>
           </instructions>
         </configuration>
diff --git 
a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/time/Stopwatch.java
 
b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/time/Stopwatch.java
new file mode 100644
index 0000000000..bcf8278739
--- /dev/null
+++ 
b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/time/Stopwatch.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.jackrabbit.oak.commons.time;
+
+import org.apache.jackrabbit.oak.commons.conditions.Validate;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
+
+/**
+ * A stop watch based either on a {@link Supplier} of nanoseconds, or a {@link 
Clock}.
+ * <p>
+ * The accuracy of measurements depends on the precision of the time source, 
which likely depends on platform and
+ * configuration.
+ * <p>
+ * Inspired by Guava's.
+ */
+public final class Stopwatch {
+
+    private long startTime;
+    private long accumulated;
+    private boolean running;
+    private final Supplier<Long> ticker;
+
+    private Stopwatch(Supplier<Long> ticker) {
+        this.ticker = ticker;
+        this.accumulated = 0L;
+        this.startTime = ticker.get();
+        this.running = false;
+    }
+
+    /**
+     * @return a running stop watch, using {@link System#nanoTime()}.
+     */
+    public static Stopwatch createStarted() {
+        return new Stopwatch(Stopwatch::tick).start();
+    }
+
+    /**
+     * @return a running stop watch, using the supplied supplier.
+     */
+    public static Stopwatch createStarted(Supplier<Long> ticker) {
+        return new Stopwatch(ticker).start();
+    }
+
+    /**
+     * @return a running stop watch, using the supplied clock.
+     * <p>
+     * Note that only {@link Clock#millis()} will be used, thus the watch will 
have ms precision at most.
+     */
+    public static Stopwatch createStarted(Clock clock) {
+        return new Stopwatch(clockAsLongSupplier(clock)).start();
+    }
+
+    /**
+     * @return a non-running stop watch, using the supplied supplier.
+     */
+    public static Stopwatch createUnstarted(Supplier<Long> ticker) {
+        return new Stopwatch(ticker);
+    }
+
+    /**
+     * @return a non-running stop watch, using {@link System#nanoTime()}.
+     */
+    public static Stopwatch createUnstarted() {
+        return new Stopwatch(Stopwatch::tick);
+    }
+
+    /**
+     * Starts the stop watch, will fail when running.
+     * @return the stop watch
+     */
+    public Stopwatch start() {
+        Validate.checkState(!this.running, "Stopwatch already running.");
+        this.startTime = this.ticker.get();
+        this.running = true;
+        return this;
+    }
+
+    /**
+     * Stops the stop watch, will fail when not running.
+     * @return the stop watch
+     */
+    public Stopwatch stop() {
+        Validate.checkState(this.running, "Stopwatch not running.");
+        this.accumulated += elapsedNanos();
+        this.startTime = 0L;
+        this.running = false;
+        return this;
+    }
+
+    /**
+     * Resets the stop watch, and puts it into stopped state.
+     * @return the stop watch
+     */
+    public Stopwatch reset() {
+        this.accumulated = 0L;
+        this.startTime = 0;
+        this.running = false;
+        return this;
+    }
+
+    /**
+     * @return whether the stop watch is running
+     */
+    public boolean isRunning() {
+        return this.running;
+    }
+
+    /**
+     * Gets elapsed time using the supplied {@link TimeUnit}.
+     * @param timeunit time unit
+     * @return elapsed time in the specified unit
+     */
+    public long elapsed(TimeUnit timeunit) {
+        return timeunit.convert(elapsedNanos(), TimeUnit.NANOSECONDS);
+    }
+
+    /**
+     * Gets elapsed time as {@link Duration}.
+     * @return elapsed time
+     */
+    public Duration elapsed() {
+        return Duration.ofNanos(elapsedNanos());
+    }
+
+    @Override
+    public String toString() {
+        return Duration.ofNanos(elapsedNanos()).toString();
+    }
+
+    // private parts
+
+    private long elapsedNanos() {
+        long delta = this.running ? this.ticker.get() - this.startTime : 0;
+        return this.accumulated + delta;
+    }
+
+    private static long tick() {
+        return System.nanoTime();
+    }
+
+    private static Supplier<Long> clockAsLongSupplier(Clock clock) {
+        return () -> TimeUnit.MILLISECONDS.toNanos(clock.millis());
+    }
+}
diff --git 
a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/time/package-info.java
 
b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/time/package-info.java
new file mode 100644
index 0000000000..3e9fa9b326
--- /dev/null
+++ 
b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/time/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+/**
+ * Internal ("private") utilities related to timing...
+ */
+@Internal(since = "1.0.0")
+@Version("1.0.0")
+package org.apache.jackrabbit.oak.commons.time;
+import org.apache.jackrabbit.oak.commons.annotations.Internal;
+import org.osgi.annotation.versioning.Version;
+
diff --git 
a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/StopWatch.java 
b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/TestStopWatch.java
similarity index 98%
rename from 
oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/StopWatch.java
rename to 
oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/TestStopWatch.java
index 7677b33c56..ed64cdf6f9 100644
--- a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/StopWatch.java
+++ 
b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/TestStopWatch.java
@@ -19,7 +19,7 @@ package org.apache.jackrabbit.oak.commons;
 /**
  * A utility class to time an operation.
  */
-public class StopWatch {
+public class TestStopWatch {
 
     private static final long NANOS_PER_SECOND = 1000 * 1000 * 1000;
 
diff --git 
a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/json/JsopStreamTest.java
 
b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/json/JsopStreamTest.java
index 622a6c112b..2437decdda 100644
--- 
a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/json/JsopStreamTest.java
+++ 
b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/json/JsopStreamTest.java
@@ -17,7 +17,7 @@
 package org.apache.jackrabbit.oak.commons.json;
 
 import junit.framework.TestCase;
-import org.apache.jackrabbit.oak.commons.StopWatch;
+import org.apache.jackrabbit.oak.commons.TestStopWatch;
 
 public class JsopStreamTest extends TestCase {
 
@@ -25,7 +25,7 @@ public class JsopStreamTest extends TestCase {
     public static void main(String... args) {
         for (int k = 0; k < 5; k++) {
             String s = "Hello \"World\" Hello \"World\" Hello \"World\" Hello 
\"World\" Hello \"World\" Hello \"World\" ";
-            StopWatch timer = new StopWatch();
+            TestStopWatch timer = new TestStopWatch();
             JsopWriter w = k % 2 == 1 ? new JsopBuilder() : new JsopStream();
             for (int i = 0; i < 1000000; i++) {
                 w.value(s);
diff --git 
a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/json/JsopTest.java
 
b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/json/JsopTest.java
index 93e4e2f197..58cc316749 100644
--- 
a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/json/JsopTest.java
+++ 
b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/json/JsopTest.java
@@ -17,7 +17,7 @@
 package org.apache.jackrabbit.oak.commons.json;
 
 import junit.framework.TestCase;
-import org.apache.jackrabbit.oak.commons.StopWatch;
+import org.apache.jackrabbit.oak.commons.TestStopWatch;
 
 /**
  * Test the Jsop tokenizer and builder.
@@ -29,7 +29,7 @@ public class JsopTest extends TestCase {
         for (int k = 0; k < 5; k++) {
             // String s = "Hello World Hello World Hello World Hello World 
Hello World Hello World ";
             String s = "Hello \"World\" Hello \"World\" Hello \"World\" Hello 
\"World\" Hello \"World\" Hello \"World\" ";
-            StopWatch timer = new StopWatch();
+            TestStopWatch timer = new TestStopWatch();
             int t2 = 0;
             for (int i = 0; i < 1000000; i++) {
                 t2 += JsopBuilder.encode(s).length();
diff --git 
a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/time/StopwatchTest.java
 
b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/time/StopwatchTest.java
new file mode 100644
index 0000000000..1c7eb8f740
--- /dev/null
+++ 
b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/time/StopwatchTest.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.jackrabbit.oak.commons.time;
+
+import org.junit.Test;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class StopwatchTest {
+
+    private static final long CHECK_INTERVAL_SECONDS = 3;
+    private static final long CHECK_INTERVAL_MILLIS = 
TimeUnit.SECONDS.toMillis(CHECK_INTERVAL_SECONDS);
+
+    @Test
+    public void started() throws InterruptedException {
+        testStarted(Stopwatch.createStarted());
+    }
+
+    @Test
+    public void stopped() throws InterruptedException {
+        Stopwatch sw = Stopwatch.createUnstarted();
+        assertFalse(sw.isRunning());
+        testStarted(sw.start());
+    }
+
+    @Test
+    public void withClock() throws InterruptedException {
+        testStarted(Stopwatch.createStarted(Clock.systemUTC()));
+    }
+
+    @Test
+    public void specialTicker() {
+        final AtomicLong time = new AtomicLong(0);
+        Stopwatch sw = Stopwatch.createUnstarted(time::get);
+        assertEquals(0, sw.elapsed().toNanos());
+        sw.start();
+        time.set(123);
+        assertEquals(123, sw.elapsed().toNanos());
+        time.set(456);
+        sw.stop();
+        assertEquals(456, sw.elapsed().toNanos());
+
+        time.set(0);
+        sw.reset().start();
+
+        // tests internals
+        assertEquals(Duration.ofNanos(0).toString(), sw.toString());
+    }
+
+    private void testStarted(Stopwatch sw) throws InterruptedException {
+        letElapse(CHECK_INTERVAL_MILLIS);
+        sw.stop();
+        long millis = sw.elapsed().toMillis();
+        assertTrue("elapsed " + millis + "ms, expected ~" + 
CHECK_INTERVAL_MILLIS,
+                millis >= CHECK_INTERVAL_MILLIS - 1000 && millis <= 
CHECK_INTERVAL_MILLIS + 1000);
+        long seconds = sw.elapsed(TimeUnit.SECONDS);
+        assertTrue("elapsed " + seconds + "s, expected ~" + 
CHECK_INTERVAL_SECONDS,
+                seconds >= CHECK_INTERVAL_SECONDS - 1 && seconds <= 
CHECK_INTERVAL_SECONDS + 1);
+    }
+
+    private static void letElapse(long delta) throws InterruptedException {
+        long start = System.currentTimeMillis();
+        while (System.currentTimeMillis() < start + delta) {
+            Thread.sleep(1);
+        }
+    }
+}

Reply via email to