You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2022/03/21 13:19:18 UTC

[commons-lang] branch master updated: Refactor to create the mapping function once.

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 85751a1  Refactor to create the mapping function once.
85751a1 is described below

commit 85751a118e38f23b9009ccab1cdd3ea3b1d2dc5d
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Mon Mar 21 09:19:13 2022 -0400

    Refactor to create the mapping function once.
    
    Add FutureTasks.
---
 src/changes/changes.xml                            |  1 +
 .../commons/lang3/concurrent/FutureTasks.java      | 45 ++++++++++++
 .../apache/commons/lang3/concurrent/Memoizer.java  | 82 ++++++++--------------
 .../commons/lang3/concurrent/FutureTasksTest.java  | 40 +++++++++++
 4 files changed, 117 insertions(+), 51 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 9720770..e0197e3 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -122,6 +122,7 @@ The <action> type attribute can be add,update,fix,remove.
     <action                   type="add" dev="ggregory" due-to="Gary Gregory">Update ArchUtils.getProcessor(String) for "aarch64".</action>
     <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add JavaVersion.JAVA_18.</action>
     <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add TimeZones.toTimeZone(TimeZone).</action>
+    <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add FutureTasks.</action>
     <!-- UPDATE -->
     <action                   type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump spotbugs-maven-plugin from 4.2.0 to 4.5.0.0 #735, #808, #822, #834.</action>
     <action                   type="update" dev="ggregory" due-to="Dependabot, XenoAmess">Bump actions/cache from v2.1.4 to v2.1.7 #742, #752, #764, #833.</action>
diff --git a/src/main/java/org/apache/commons/lang3/concurrent/FutureTasks.java b/src/main/java/org/apache/commons/lang3/concurrent/FutureTasks.java
new file mode 100644
index 0000000..ce325c8
--- /dev/null
+++ b/src/main/java/org/apache/commons/lang3/concurrent/FutureTasks.java
@@ -0,0 +1,45 @@
+/*
+ * 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.commons.lang3.concurrent;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
+
+/**
+ * Consists of utility methods that work with {@link FutureTask}.
+ *
+ * @since 3.13.0
+ */
+public class FutureTasks {
+
+    private FutureTasks() {
+        // No instances needed.
+    }
+
+    /**
+     * Creates a {@code FutureTask} and runs the given {@code Callable}.
+     *
+     * @param <V> The result type returned by this FutureTask's {@code get} methods.
+     * @param callable the Callable task.
+     * @return a new FutureTask.
+     */
+    public static <V> FutureTask<V> run(final Callable<V> callable) {
+        final FutureTask<V> futureTask = new FutureTask<>(callable);
+        futureTask.run();
+        return futureTask;
+    }
+}
diff --git a/src/main/java/org/apache/commons/lang3/concurrent/Memoizer.java b/src/main/java/org/apache/commons/lang3/concurrent/Memoizer.java
index ab3feea..d53318d 100644
--- a/src/main/java/org/apache/commons/lang3/concurrent/Memoizer.java
+++ b/src/main/java/org/apache/commons/lang3/concurrent/Memoizer.java
@@ -21,39 +21,33 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
-import java.util.concurrent.FutureTask;
+import java.util.function.Function;
 
 /**
  * <p>
- * Definition of an interface for a wrapper around a calculation that takes a
- * single parameter and returns a result. The results for the calculation will
- * be cached for future requests.
+ * Definition of an interface for a wrapper around a calculation that takes a single parameter and returns a result. The
+ * results for the calculation will be cached for future requests.
  * </p>
  * <p>
- * This is not a fully functional cache, there is no way of limiting or removing
- * results once they have been generated. However, it is possible to get the
- * implementation to regenerate the result for a given parameter, if an error
- * was thrown during the previous calculation, by setting the option during the
- * construction of the class. If this is not set the class will return the
- * cached exception.
+ * This is not a fully functional cache, there is no way of limiting or removing results once they have been generated.
+ * However, it is possible to get the implementation to regenerate the result for a given parameter, if an error was
+ * thrown during the previous calculation, by setting the option during the construction of the class. If this is not
+ * set the class will return the cached exception.
  * </p>
  * <p>
- * Thanks should go to Brian Goetz, Tim Peierls and the members of JCP JSR-166
- * Expert Group for coming up with the original implementation of the class. It
- * was also published within Java Concurrency in Practice as a sample.
+ * Thanks should go to Brian Goetz, Tim Peierls and the members of JCP JSR-166 Expert Group for coming up with the
+ * original implementation of the class. It was also published within Java Concurrency in Practice as a sample.
  * </p>
  *
- * @param <I>
- *            the type of the input to the calculation
- * @param <O>
- *            the type of the output of the calculation
+ * @param <I> the type of the input to the calculation
+ * @param <O> the type of the output of the calculation
  *
  * @since 3.6
  */
 public class Memoizer<I, O> implements Computable<I, O> {
 
     private final ConcurrentMap<I, Future<O>> cache = new ConcurrentHashMap<>();
-    private final Computable<I, O> computable;
+    private Function<? super I, ? extends Future<O>> mappingFunction;
     private final boolean recalculate;
 
     /**
@@ -61,13 +55,11 @@ public class Memoizer<I, O> implements Computable<I, O> {
      * Constructs a Memoizer for the provided Computable calculation.
      * </p>
      * <p>
-     * If a calculation is thrown an exception for any reason, this exception
-     * will be cached and returned for all future calls with the provided
-     * parameter.
+     * If a calculation is thrown an exception for any reason, this exception will be cached and returned for all future
+     * calls with the provided parameter.
      * </p>
      *
-     * @param computable
-     *            the computation whose results should be memorized
+     * @param computable the computation whose results should be memorized
      */
     public Memoizer(final Computable<I, O> computable) {
         this(computable, false);
@@ -75,48 +67,37 @@ public class Memoizer<I, O> implements Computable<I, O> {
 
     /**
      * <p>
-     * Constructs a Memoizer for the provided Computable calculation, with the
-     * option of whether a Computation that experiences an error should
-     * recalculate on subsequent calls or return the same cached exception.
+     * Constructs a Memoizer for the provided Computable calculation, with the option of whether a Computation that
+     * experiences an error should recalculate on subsequent calls or return the same cached exception.
      * </p>
      *
-     * @param computable
-     *            the computation whose results should be memorized
-     * @param recalculate
-     *            determines whether the computation should be recalculated on
-     *            subsequent calls if the previous call failed
+     * @param computable the computation whose results should be memorized
+     * @param recalculate determines whether the computation should be recalculated on subsequent calls if the previous call
+     *        failed
      */
     public Memoizer(final Computable<I, O> computable, final boolean recalculate) {
-        this.computable = computable;
         this.recalculate = recalculate;
+        this.mappingFunction = k -> FutureTasks.run(() -> computable.compute(k));
     }
 
     /**
      * <p>
-     * This method will return the result of the calculation and cache it, if it
-     * has not previously been calculated.
+     * This method will return the result of the calculation and cache it, if it has not previously been calculated.
      * </p>
      * <p>
-     * This cache will also cache exceptions that occur during the computation
-     * if the {@code recalculate} parameter in the constructor was set to
-     * {@code false}, or not set. Otherwise, if an exception happened on the
-     * previous calculation, the method will attempt again to generate a value.
+     * This cache will also cache exceptions that occur during the computation if the {@code recalculate} parameter in the
+     * constructor was set to {@code false}, or not set. Otherwise, if an exception happened on the previous calculation,
+     * the method will attempt again to generate a value.
      * </p>
      *
-     * @param arg
-     *            the argument for the calculation
+     * @param arg the argument for the calculation
      * @return the result of the calculation
-     * @throws InterruptedException
-     *             thrown if the calculation is interrupted
+     * @throws InterruptedException thrown if the calculation is interrupted
      */
     @Override
     public O compute(final I arg) throws InterruptedException {
         while (true) {
-            Future<O> future = cache.computeIfAbsent(arg, k -> {
-                final FutureTask<O> futureTask = new FutureTask<>(() -> computable.compute(arg));
-                futureTask.run();
-                return futureTask;
-            });
+            final Future<O> future = cache.computeIfAbsent(arg, mappingFunction);
             try {
                 return future.get();
             } catch (final CancellationException e) {
@@ -132,12 +113,11 @@ public class Memoizer<I, O> implements Computable<I, O> {
 
     /**
      * <p>
-     * This method launders a Throwable to either a RuntimeException, Error or
-     * any other Exception wrapped in an IllegalStateException.
+     * This method launders a Throwable to either a RuntimeException, Error or any other Exception wrapped in an
+     * IllegalStateException.
      * </p>
      *
-     * @param throwable
-     *            the throwable to laundered
+     * @param throwable the throwable to laundered
      * @return a RuntimeException, Error or an IllegalStateException
      */
     private RuntimeException launderException(final Throwable throwable) {
diff --git a/src/test/java/org/apache/commons/lang3/concurrent/FutureTasksTest.java b/src/test/java/org/apache/commons/lang3/concurrent/FutureTasksTest.java
new file mode 100644
index 0000000..da3fcd1
--- /dev/null
+++ b/src/test/java/org/apache/commons/lang3/concurrent/FutureTasksTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.commons.lang3.concurrent;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link FutureTask}.
+ */
+public class FutureTasksTest {
+
+    @Test
+    public void testRun() throws InterruptedException, ExecutionException {
+        final String data = "Hello";
+        final FutureTask<String> f = FutureTasks.run(() -> data);
+        assertTrue(f.isDone());
+        assertEquals(data, f.get());
+    }
+
+}