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

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git


The following commit(s) were added to refs/heads/master by this push:
     new b6f72e73e Add builders for LockingVisitors implementations
b6f72e73e is described below

commit b6f72e73ee96feb63e17096933fd5b7f313fb77b
Author: Gary D. Gregory <[email protected]>
AuthorDate: Fri Jun 20 10:28:32 2025 -0400

    Add builders for LockingVisitors implementations
---
 src/changes/changes.xml                            |   1 +
 .../lang3/concurrent/locks/LockingVisitors.java    | 258 +++++++++++++++++++++
 .../concurrent/locks/LockingVisitorsTest.java      | 133 +++++++++--
 3 files changed, 370 insertions(+), 22 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index d6c6a7812..f5a435a89 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -136,6 +136,7 @@ The <action> type attribute can be add,update,fix,remove.
     <action                   type="add" dev="ggregory" due-to="Gary 
Gregory">Add 
org.apache.commons.lang3.concurrent.locks.LockingVisitors.reentrantLockVisitor(Object).</action>
     <action                   type="add" dev="ggregory" due-to="Gary 
Gregory">Add 
org.apache.commons.lang3.concurrent.locks.LockingVisitors.create(Object, 
ReentrantLock).</action>
     <action                   type="add" dev="ggregory" due-to="Gary 
Gregory">Add 
org.apache.commons.lang3.concurrent.locks.LockingVisitors.ReentrantLockVisitor.</action>
+    <action                   type="add" dev="ggregory" due-to="Gary 
Gregory">Add builders for LockingVisitors implementations.</action>
     <!-- UPDATE -->
     <action                   type="update" dev="ggregory" due-to="Gary 
Gregory, Dependabot">Bump org.apache.commons:commons-parent from 73 to 85 
#1267, #1277, #1283, #1288, #1302, #1377.</action>
     <action                   type="update" dev="ggregory" due-to="Gary 
Gregory, Dependabot">[site] Bump org.codehaus.mojo:taglist-maven-plugin from 
3.1.0 to 3.2.1 #1300.</action>
diff --git 
a/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java 
b/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java
index d613cc440..0e165de3a 100644
--- 
a/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java
+++ 
b/src/main/java/org/apache/commons/lang3/concurrent/locks/LockingVisitors.java
@@ -24,6 +24,7 @@
 import java.util.concurrent.locks.StampedLock;
 import java.util.function.Supplier;
 
+import org.apache.commons.lang3.builder.AbstractSupplier;
 import org.apache.commons.lang3.function.Failable;
 import org.apache.commons.lang3.function.FailableConsumer;
 import org.apache.commons.lang3.function.FailableFunction;
@@ -94,6 +95,103 @@ public class LockingVisitors {
      */
     public static class LockVisitor<O, L> {
 
+        /**
+         * Builds {@link LockVisitor} instances.
+         *
+         * @param <O> the wrapped object type.
+         * @param <L> the wrapped lock type.
+         * @param <B> the builder type.
+         * @since 3.18.0
+         */
+        public static class LVBuilder<O, L, B extends LVBuilder<O, L, B>> 
extends AbstractSupplier<LockVisitor<O, L>, B, RuntimeException> {
+
+            /**
+             * The lock object, untyped, since, for example {@link 
StampedLock} does not implement a locking interface in
+             * Java 8.
+             */
+            L lock;
+
+            /**
+             * The guarded object.
+             */
+            O object;
+
+            /**
+             * Supplies the read lock, usually from the lock object.
+             */
+            private Supplier<Lock> readLockSupplier;
+
+            /**
+             * Supplies the write lock, usually from the lock object.
+             */
+            private Supplier<Lock> writeLockSupplier;
+
+            /**
+             * Constructs a new instance.
+             */
+            public LVBuilder() {
+                // empty
+            }
+
+            @Override
+            public LockVisitor<O, L> get() {
+                return new LockVisitor<>(this);
+            }
+
+            Supplier<Lock> getReadLockSupplier() {
+                return readLockSupplier;
+            }
+
+
+            Supplier<Lock> getWriteLockSupplier() {
+                return writeLockSupplier;
+            }
+
+            /**
+             * Set the lock used from accept methods.
+             *
+             * @param lock the lock.
+             * @return {@code this} instance.
+             */
+            public B setLock(final L lock) {
+                this.lock = lock;
+                return asThis();
+            }
+
+            /**
+             * Set the resource.
+             *
+             * @param object the resource.
+             * @return {@code this} instance.
+             */
+            public B setObject(final O object) {
+                this.object = object;
+                return asThis();
+            }
+
+            /**
+             * Supplies the read lock.
+             *
+             * @param readLockSupplier Supplies the read lock.
+             * @return {@code this} instance.
+             */
+            public B setReadLockSupplier(final Supplier<Lock> 
readLockSupplier) {
+                this.readLockSupplier = readLockSupplier;
+                return asThis();
+            }
+
+            /**
+             * Supplies the write lock.
+             *
+             * @param writeLockSupplier Supplies the write lock.
+             * @return {@code this} instance.
+             */
+            public B setWriteLockSupplier(final Supplier<Lock> 
writeLockSupplier) {
+                this.writeLockSupplier = writeLockSupplier;
+                return asThis();
+            }
+        }
+
         /**
          * The lock object, untyped, since, for example {@link StampedLock} 
does not implement a locking interface in
          * Java 8.
@@ -115,6 +213,18 @@ public static class LockVisitor<O, L> {
          */
         private final Supplier<Lock> writeLockSupplier;
 
+        /**
+         * Constructs an instance from a builder.
+         *
+         * @param builder The builder.
+         */
+        private LockVisitor(final LVBuilder<O, L, ?> builder) {
+            this.object = Objects.requireNonNull(builder.object, "object");
+            this.lock = Objects.requireNonNull(builder.lock, "lock");
+            this.readLockSupplier = 
Objects.requireNonNull(builder.readLockSupplier, "readLockSupplier");
+            this.writeLockSupplier = 
Objects.requireNonNull(builder.writeLockSupplier, "writeLockSupplier");
+        }
+
         /**
          * Constructs an instance.
          *
@@ -316,6 +426,54 @@ protected <T> T lockApplyUnlock(final Supplier<Lock> 
lockSupplier, final Failabl
      */
     public static class ReadWriteLockVisitor<O> extends LockVisitor<O, 
ReadWriteLock> {
 
+        /**
+         * Builds {@link LockVisitor} instances.
+         *
+         * @param <O> the wrapped object type.
+         * @since 3.18.0
+         */
+        public static class Builder<O> extends LVBuilder<O, ReadWriteLock, 
Builder<O>> {
+
+            /**
+             * Constructs a new instance.
+             */
+            public Builder() {
+                // empty
+            }
+
+            @Override
+            public ReadWriteLockVisitor<O> get() {
+                return new ReadWriteLockVisitor<>(this);
+            }
+
+            @Override
+            public Builder<O> setLock(final ReadWriteLock readWriteLock) {
+                setReadLockSupplier(readWriteLock::readLock);
+                setWriteLockSupplier(readWriteLock::writeLock);
+                return super.setLock(readWriteLock);
+            }
+        }
+
+        /**
+         * Creates a new builder.
+         *
+         * @param <O> the wrapped object type.
+         * @return a new builder.
+         * @since 3.18.0
+         */
+        public static <O> Builder<O> builder() {
+            return new Builder<>();
+        }
+
+        /**
+         * Constructs a new instance from a builder.
+         *
+         * @param builder a builder.
+         */
+        private ReadWriteLockVisitor(final Builder<O> builder) {
+            super(builder);
+        }
+
         /**
          * Creates a new instance with the given locked object. This 
constructor is supposed to be used for subclassing
          * only. In general, it is suggested to use {@link 
LockingVisitors#stampedLockVisitor(Object)} instead.
@@ -326,6 +484,7 @@ public static class ReadWriteLockVisitor<O> extends 
LockVisitor<O, ReadWriteLock
         protected ReadWriteLockVisitor(final O object, final ReadWriteLock 
readWriteLock) {
             super(object, readWriteLock, readWriteLock::readLock, 
readWriteLock::writeLock);
         }
+
     }
 
     /**
@@ -340,6 +499,56 @@ protected ReadWriteLockVisitor(final O object, final 
ReadWriteLock readWriteLock
      */
     public static class ReentrantLockVisitor<O> extends LockVisitor<O, 
ReentrantLock> {
 
+        /**
+         * Builds {@link LockVisitor} instances.
+         *
+         * @param <O> the wrapped object type.
+         * @since 3.18.0
+         */
+        public static class Builder<O> extends LVBuilder<O, ReentrantLock, 
Builder<O>> {
+
+            /**
+             * Constructs a new instance.
+             */
+            public Builder() {
+                // empty
+            }
+
+            @Override
+            public ReentrantLockVisitor<O> get() {
+                return new ReentrantLockVisitor<>(this);
+            }
+
+
+            @Override
+            public Builder<O> setLock(final ReentrantLock reentrantLock) {
+                setReadLockSupplier(() -> reentrantLock);
+                setWriteLockSupplier(() -> reentrantLock);
+                return super.setLock(reentrantLock);
+            }
+        }
+
+        /**
+         * Creates a new builder.
+         *
+         * @param <O> the wrapped object type.
+         * @return a new builder.
+         * @since 3.18.0
+         */
+        public static <O> Builder<O> builder() {
+            return new Builder<>();
+        }
+
+        /**
+         * Constructs a new instance from a builder.
+         *
+         * @param builder a builder.
+         */
+        private ReentrantLockVisitor(final Builder<O> builder) {
+            super(builder);
+        }
+
+
         /**
          * Creates a new instance with the given locked object. This 
constructor is supposed to be used for subclassing
          * only. In general, it is suggested to use {@link 
LockingVisitors#reentrantLockVisitor(Object)} instead.
@@ -363,6 +572,55 @@ protected ReentrantLockVisitor(final O object, final 
ReentrantLock reentrantLock
      */
     public static class StampedLockVisitor<O> extends LockVisitor<O, 
StampedLock> {
 
+        /**
+         * Builds {@link LockVisitor} instances.
+         *
+         * @param <O> the wrapped object type.
+         * @since 3.18.0
+         */
+        public static class Builder<O> extends LVBuilder<O, StampedLock, 
Builder<O>> {
+
+            /**
+             * Constructs a new instance.
+             */
+            public Builder() {
+                // empty
+            }
+
+            @Override
+            public StampedLockVisitor<O> get() {
+                return new StampedLockVisitor<>(this);
+            }
+
+
+            @Override
+            public Builder<O> setLock(final StampedLock stampedLock) {
+                setReadLockSupplier(stampedLock::asReadLock);
+                setWriteLockSupplier(stampedLock::asWriteLock);
+                return super.setLock(stampedLock);
+            }
+        }
+
+        /**
+         * Creates a new builder.
+         *
+         * @param <O> the wrapped object type.
+         * @return a new builder.
+         * @since 3.18.0
+         */
+        public static <O> Builder<O> builder() {
+            return new Builder<>();
+        }
+
+        /**
+         * Constructs a new instance from a builder.
+         *
+         * @param builder a builder.
+         */
+        private StampedLockVisitor(final Builder<O> builder) {
+            super(builder);
+        }
+
         /**
          * Creates a new instance with the given locked object. This 
constructor is supposed to be used for subclassing
          * only. In general, it is suggested to use {@link 
LockingVisitors#stampedLockVisitor(Object)} instead.
diff --git 
a/src/test/java/org/apache/commons/lang3/concurrent/locks/LockingVisitorsTest.java
 
b/src/test/java/org/apache/commons/lang3/concurrent/locks/LockingVisitorsTest.java
index 3ce1a38d5..9797644af 100644
--- 
a/src/test/java/org/apache/commons/lang3/concurrent/locks/LockingVisitorsTest.java
+++ 
b/src/test/java/org/apache/commons/lang3/concurrent/locks/LockingVisitorsTest.java
@@ -26,12 +26,15 @@
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.locks.StampedLock;
 import java.util.function.LongConsumer;
 
 import org.apache.commons.lang3.AbstractLangTest;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.ThreadUtils;
 import org.apache.commons.lang3.concurrent.locks.LockingVisitors.LockVisitor;
+import 
org.apache.commons.lang3.concurrent.locks.LockingVisitors.ReadWriteLockVisitor;
+import 
org.apache.commons.lang3.concurrent.locks.LockingVisitors.ReentrantLockVisitor;
 import 
org.apache.commons.lang3.concurrent.locks.LockingVisitors.StampedLockVisitor;
 import org.apache.commons.lang3.function.FailableConsumer;
 import org.junit.jupiter.api.Test;
@@ -95,16 +98,102 @@ protected void set(final boolean[] booleanArray, final int 
offset, final boolean
         }
     }
 
+    @ParameterizedTest
+    @ValueSource(booleans = { true, false })
+    void testBuilderLockVisitor(final boolean fair) {
+        final AtomicInteger obj = new AtomicInteger();
+        final ReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        // @formatter:off
+        final LockVisitor<AtomicInteger, ReadWriteLock> lockVisitor = new 
LockVisitor.LVBuilder()
+          .setObject(obj)
+          .setLock(lock)
+          .setReadLockSupplier(lock::readLock)
+          .setWriteLockSupplier(lock::writeLock)
+          .get();
+        // @formatter:on
+        lockVisitor.acceptReadLocked(AtomicInteger::incrementAndGet);
+        assertEquals(1, obj.get());
+        lockVisitor.acceptReadLocked(AtomicInteger::incrementAndGet);
+        assertEquals(2, obj.get());
+    }
+
+    @ParameterizedTest
+    @ValueSource(booleans = { true, false })
+    void testBuilderReadWriteLockVisitor(final boolean fair) {
+        final AtomicInteger obj = new AtomicInteger();
+        final ReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        // @formatter:off
+        final LockingVisitors.ReadWriteLockVisitor<AtomicInteger> lockVisitor 
= ReadWriteLockVisitor.<AtomicInteger>builder()
+          .setObject(obj)
+          .setLock(lock)
+          .get();
+        // @formatter:on
+        lockVisitor.acceptReadLocked(AtomicInteger::incrementAndGet);
+        assertEquals(1, obj.get());
+        lockVisitor.acceptReadLocked(AtomicInteger::incrementAndGet);
+        assertEquals(2, obj.get());
+    }
+
+    @ParameterizedTest
+    @ValueSource(booleans = { true, false })
+    void testBuilderReentrantLockVisitor(final boolean fair) {
+        final AtomicInteger obj = new AtomicInteger();
+        final ReentrantLock lock = new ReentrantLock(fair);
+        // @formatter:off
+        final LockingVisitors.ReentrantLockVisitor<AtomicInteger> lockVisitor 
= ReentrantLockVisitor.<AtomicInteger>builder()
+          .setObject(obj)
+          .setLock(lock)
+          .get();
+        // @formatter:on
+        lockVisitor.acceptReadLocked(AtomicInteger::incrementAndGet);
+        assertEquals(1, obj.get());
+        lockVisitor.acceptReadLocked(AtomicInteger::incrementAndGet);
+        assertEquals(2, obj.get());
+    }
+
+    @ParameterizedTest
+    @ValueSource(booleans = { true, false })
+    void testBuilderReentrantReadWriteLockVisitor(final boolean fair) {
+        final AtomicInteger obj = new AtomicInteger();
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        // @formatter:off
+        final LockingVisitors.ReadWriteLockVisitor<AtomicInteger> lockVisitor 
= ReadWriteLockVisitor.<AtomicInteger>builder()
+          .setObject(obj)
+          .setLock(lock)
+          .get();
+        // @formatter:on
+        lockVisitor.acceptReadLocked(AtomicInteger::incrementAndGet);
+        assertEquals(1, obj.get());
+        lockVisitor.acceptReadLocked(AtomicInteger::incrementAndGet);
+        assertEquals(2, obj.get());
+    }
+
+    @Test
+    void testBuilderReentrantStampedLockVisitor() {
+        final AtomicInteger obj = new AtomicInteger();
+        final StampedLock lock = new StampedLock();
+        // @formatter:off
+        final LockingVisitors.StampedLockVisitor<AtomicInteger> lockVisitor = 
StampedLockVisitor.<AtomicInteger>builder()
+          .setObject(obj)
+          .setLock(lock)
+          .get();
+        // @formatter:on
+        lockVisitor.acceptReadLocked(AtomicInteger::incrementAndGet);
+        assertEquals(1, obj.get());
+        lockVisitor.acceptReadLocked(AtomicInteger::incrementAndGet);
+        assertEquals(2, obj.get());
+    }
+
     @Test
     void testCreate() {
-        final AtomicInteger res = new AtomicInteger();
-        final ReadWriteLock rwLock = new ReentrantReadWriteLock();
-        LockingVisitors.create(res, 
rwLock).acceptReadLocked(AtomicInteger::incrementAndGet);
-        LockingVisitors.create(res, rwLock).acceptReadLocked(null);
-        assertEquals(1, res.get());
-        LockingVisitors.create(res, 
rwLock).acceptWriteLocked(AtomicInteger::incrementAndGet);
-        LockingVisitors.create(res, rwLock).acceptWriteLocked(null);
-        assertEquals(2, res.get());
+        final AtomicInteger obj = new AtomicInteger();
+        final ReadWriteLock lock = new ReentrantReadWriteLock();
+        LockingVisitors.create(obj, 
lock).acceptReadLocked(AtomicInteger::incrementAndGet);
+        LockingVisitors.create(obj, lock).acceptReadLocked(null);
+        assertEquals(1, obj.get());
+        LockingVisitors.create(obj, 
lock).acceptWriteLocked(AtomicInteger::incrementAndGet);
+        LockingVisitors.create(obj, lock).acceptWriteLocked(null);
+        assertEquals(2, obj.get());
     }
 
     @SuppressWarnings("deprecation")
@@ -114,35 +203,35 @@ void testDeprecatedConstructor() {
     }
 
     @Test
-    void testReentrantReadWriteLockExclusive() throws Exception {
-        // If our threads are running concurrently, then we expect to be no 
faster than running one after the other.
+    void testReentrantLock() throws Exception {
+        // If our threads are running concurrently, then we expect to be 
faster than running one after the other.
         final boolean[] booleanValues = new boolean[10];
-        runTest(DELAY, true, millis -> assertTrue(millis >= 
TOTAL_DELAY.toMillis()), booleanValues,
-                LockingVisitors.reentrantReadWriteLockVisitor(booleanValues));
+        runTest(DELAY, false, millis -> assertTrue(millis < 
TOTAL_DELAY.toMillis()), booleanValues, 
LockingVisitors.reentrantLockVisitor(booleanValues));
     }
 
-    @Test
-    void testReentrantReadWriteLockNotExclusive() throws Exception {
+    @ParameterizedTest
+    @ValueSource(booleans = { true, false })
+    void testReentrantLockFairness(final boolean fairness) throws Exception {
         // If our threads are running concurrently, then we expect to be 
faster than running one after the other.
         final boolean[] booleanValues = new boolean[10];
         runTest(DELAY, false, millis -> assertTrue(millis < 
TOTAL_DELAY.toMillis()), booleanValues,
-                LockingVisitors.reentrantReadWriteLockVisitor(booleanValues));
+                LockingVisitors.create(booleanValues, new 
ReentrantLock(fairness)));
     }
 
     @Test
-    void testReentrantLock() throws Exception {
-        // If our threads are running concurrently, then we expect to be 
faster than running one after the other.
+    void testReentrantReadWriteLockExclusive() throws Exception {
+        // If our threads are running concurrently, then we expect to be no 
faster than running one after the other.
         final boolean[] booleanValues = new boolean[10];
-        runTest(DELAY, false, millis -> assertTrue(millis < 
TOTAL_DELAY.toMillis()), booleanValues, 
LockingVisitors.reentrantLockVisitor(booleanValues));
+        runTest(DELAY, true, millis -> assertTrue(millis >= 
TOTAL_DELAY.toMillis()), booleanValues,
+                LockingVisitors.reentrantReadWriteLockVisitor(booleanValues));
     }
 
-    @ParameterizedTest
-    @ValueSource(booleans = { true, false })
-    void testReentrantLockFairness(final boolean fairness) throws Exception {
+    @Test
+    void testReentrantReadWriteLockNotExclusive() throws Exception {
         // If our threads are running concurrently, then we expect to be 
faster than running one after the other.
         final boolean[] booleanValues = new boolean[10];
         runTest(DELAY, false, millis -> assertTrue(millis < 
TOTAL_DELAY.toMillis()), booleanValues,
-                LockingVisitors.create(booleanValues, new 
ReentrantLock(fairness)));
+                LockingVisitors.reentrantReadWriteLockVisitor(booleanValues));
     }
 
     @Test

Reply via email to