Details

    • Type: New Feature
    • Status: Closed
    • Priority: Major
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: 3.6
    • Component/s: lang.concurrent.*
    • Labels:
      None

      Description

      I am currently using a class like the Memoizer class [1] from "Java
      Concurrency in Practice" [2], a great book.

      It would fit perfectly in org.apache.commons.lang3.concurrent.

      [1] http://jcip.net/listings/Memoizer.java
      [2] http://jcip.net/

      There is no licensing issue because the code is in the public domain:

      ---------- Forwarded message ----------
      From: Brian Goetz <brian@briangoetz.com>
      Date: Tue, Aug 9, 2011 at 5:40 PM
      Subject: Re: Apache Commons Lang and Memoizer
      To: Gary Gregory <ggregory@apache.org>, Tim Peierls <tim@peierls.net>
      
      
      No license issues -- the code is in the public domain:
      
         Written by Brian Goetz and Tim Peierls with assistance from members of
         JCP JSR-166 Expert Group and released to the public domain, as explained at
         http://creativecommons.org/licenses/publicdomain
      
      
      Code for the samples can be downloaded from http://www.jcip.net/listings.html.
      
      Cheers,
      -Brian
      
      
      On 8/9/2011 5:38 PM, Gary Gregory wrote:
      >
      > Hi Brian,
      >
      > I would like to include a Memoizer in the next release of Apache
      > Commons Lang [1].
      >
      > Can we use the Memoizer pattern from "Java Concurrency in Practice"? I
      > think I would reuse the code from the class Memoizer and change names,
      > things like that.
      >
      > We are talking about this on the Lang mailing list and are wondering
      > if there are any licensing issues.
      >
      > [1] https://commons.apache.org/lang/
      >
      
      
      
      -- 
      Thank you,
      Gary
      
      http://garygregory.wordpress.com/
      http://garygregory.com/
      http://people.apache.org/~ggregory/
      http://twitter.com/GaryGregory
      
      1. LANG-740.patch
        8 kB
        James Sawle

        Issue Links

          Activity

          Hide
          jamessawle James Sawle added a comment - - edited

          This is a proposed implementation of the Memoizer class for discussion. If rough implementation agreed then tests will be generated during the tidy-up/changes.

          The class differs from the implementation by Goetz et al. by allowing the setting of whether failed computations should be cached. This is mentioned as a note in the book. The default behaviour is cache the values.

          All checked exceptions thrown by a given calculation are wrapped as unchecked IllegalStateExceptions, this is to allow for as lightweight an implementation as possible. A checked Exception could be generated as a wrapper for the Computable interface, and then used within the Memoizer as an alternative.

          Any comments welcome.

          Show
          jamessawle James Sawle added a comment - - edited This is a proposed implementation of the Memoizer class for discussion. If rough implementation agreed then tests will be generated during the tidy-up/changes. The class differs from the implementation by Goetz et al. by allowing the setting of whether failed computations should be cached. This is mentioned as a note in the book. The default behaviour is cache the values. All checked exceptions thrown by a given calculation are wrapped as unchecked IllegalStateExceptions, this is to allow for as lightweight an implementation as possible. A checked Exception could be generated as a wrapper for the Computable interface, and then used within the Memoizer as an alternative. Any comments welcome.
          Hide
          britter Benedikt Ritter added a comment -

          Gary Gregory do you have time to take the lead on reviewing this patch?

          Show
          britter Benedikt Ritter added a comment - Gary Gregory do you have time to take the lead on reviewing this patch?
          Hide
          jamessawle James Sawle added a comment -

          sorry I have been quiet for a while, been a busy period at work. Any progress on this?

          Show
          jamessawle James Sawle added a comment - sorry I have been quiet for a while, been a busy period at work. Any progress on this?
          Hide
          britter Benedikt Ritter added a comment -

          I'm setting this to be included in the next release (3.5)

          Show
          britter Benedikt Ritter added a comment - I'm setting this to be included in the next release (3.5)
          Hide
          jamessawle James Sawle added a comment -

          Are they any comments about the structure or the change to the behaviour (caching failed calculations)?

          If not will tidy up the code against the current master and write tests. Also under which package do you think this fits best?

          Show
          jamessawle James Sawle added a comment - Are they any comments about the structure or the change to the behaviour (caching failed calculations)? If not will tidy up the code against the current master and write tests. Also under which package do you think this fits best?
          Hide
          britter Benedikt Ritter added a comment -

          The code looks good to me. I think it should go to lang.concurrent, WDYT?

          Show
          britter Benedikt Ritter added a comment - The code looks good to me. I think it should go to lang.concurrent, WDYT?
          Hide
          jamessawle James Sawle added a comment -

          Sounds, good. Starting writing tests for this over the weekend, but is non-trivial. Will be interested in comments when finished, but will probably not be till the end of the week.

          Show
          jamessawle James Sawle added a comment - Sounds, good. Starting writing tests for this over the weekend, but is non-trivial. Will be interested in comments when finished, but will probably not be till the end of the week.
          Hide
          githubbot ASF GitHub Bot added a comment -

          GitHub user jamessawle opened a pull request:

          https://github.com/apache/commons-lang/pull/80

          LANG-740 - Implementation of a Memomizer

          Enclosed is an implementation of the Memoizer class, along with some unit tests through the main flow of the code.

          Is a copy of https://github.com/apache/commons-lang/pull/63 against the new 'master' branch

          You can merge this pull request into a Git repository by running:

          $ git pull https://github.com/rightmove/commons-lang LANG-740

          Alternatively you can review and apply these changes as the patch at:

          https://github.com/apache/commons-lang/pull/80.patch

          To close this pull request, make a commit to your master/trunk branch
          with (at least) the following in the commit message:

          This closes #80


          commit 56569b24859ca5b2972679377b145bb5680d96e8
          Author: jamessawle <jamessawle@hotmail.com>
          Date: 2015-05-04T21:23:49Z

          LANG-740 - Implementation of a Memomizer


          Show
          githubbot ASF GitHub Bot added a comment - GitHub user jamessawle opened a pull request: https://github.com/apache/commons-lang/pull/80 LANG-740 - Implementation of a Memomizer Enclosed is an implementation of the Memoizer class, along with some unit tests through the main flow of the code. Is a copy of https://github.com/apache/commons-lang/pull/63 against the new 'master' branch You can merge this pull request into a Git repository by running: $ git pull https://github.com/rightmove/commons-lang LANG-740 Alternatively you can review and apply these changes as the patch at: https://github.com/apache/commons-lang/pull/80.patch To close this pull request, make a commit to your master/trunk branch with (at least) the following in the commit message: This closes #80 commit 56569b24859ca5b2972679377b145bb5680d96e8 Author: jamessawle <jamessawle@hotmail.com> Date: 2015-05-04T21:23:49Z LANG-740 - Implementation of a Memomizer
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user coveralls commented on the pull request:

          https://github.com/apache/commons-lang/pull/80#issuecomment-98855709

          [![Coverage Status](https://coveralls.io/builds/2484924/badge)](https://coveralls.io/builds/2484924)

          Coverage decreased (-0.03%) to 93.33% when pulling *56569b24859ca5b2972679377b145bb5680d96e8 on rightmove:LANG-740* into *1cb5573ada7dc7ec240fca6afe4eeb6e2c793d8e on apache:master*.

          Show
          githubbot ASF GitHub Bot added a comment - Github user coveralls commented on the pull request: https://github.com/apache/commons-lang/pull/80#issuecomment-98855709 [! [Coverage Status] ( https://coveralls.io/builds/2484924/badge)](https://coveralls.io/builds/2484924 ) Coverage decreased (-0.03%) to 93.33% when pulling * 56569b24859ca5b2972679377b145bb5680d96e8 on rightmove: LANG-740 * into * 1cb5573ada7dc7ec240fca6afe4eeb6e2c793d8e on apache:master *.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user britter commented on the pull request:

          https://github.com/apache/commons-lang/pull/80#issuecomment-99165036

          Hello @jamessawle,

          thank you for this PR. It looks very good to me. However there are a few this we need to adjust before I can apply it. Please have a look at my inline comments.

          Thank you!

          Show
          githubbot ASF GitHub Bot added a comment - Github user britter commented on the pull request: https://github.com/apache/commons-lang/pull/80#issuecomment-99165036 Hello @jamessawle, thank you for this PR. It looks very good to me. However there are a few this we need to adjust before I can apply it. Please have a look at my inline comments. Thank you!
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user britter commented on a diff in the pull request:

          https://github.com/apache/commons-lang/pull/80#discussion_r29696821

          — Diff: src/main/java/org/apache/commons/lang3/concurrent/Computable.java —
          @@ -0,0 +1,37 @@
          +/*
          + * 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;
          +
          +/**
          + * <p>Definition of an interface for a wrapper around a calculation that takes a single parameter and returns a result.</p>
          + * <p/>
          + * <p>This interface allows for wrapping a calculation into a class so that it maybe passed around an application.</p>
          + *
          + * @param <A> the type of the input to the calculation
          + * @param <V> the type of the output of the calculation
          + */
          +public interface Computable<A, V> {
          +
          + /**
          — End diff –

          Please only use spaces for indentation

          Show
          githubbot ASF GitHub Bot added a comment - Github user britter commented on a diff in the pull request: https://github.com/apache/commons-lang/pull/80#discussion_r29696821 — Diff: src/main/java/org/apache/commons/lang3/concurrent/Computable.java — @@ -0,0 +1,37 @@ +/* + * 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; + +/** + * <p>Definition of an interface for a wrapper around a calculation that takes a single parameter and returns a result.</p> + * <p/> + * <p>This interface allows for wrapping a calculation into a class so that it maybe passed around an application.</p> + * + * @param <A> the type of the input to the calculation + * @param <V> the type of the output of the calculation + */ +public interface Computable<A, V> { + + /** — End diff – Please only use spaces for indentation
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user britter commented on a diff in the pull request:

          https://github.com/apache/commons-lang/pull/80#discussion_r29696865

          — Diff: src/test/java/org/apache/commons/lang3/concurrent/MemoizerTest.java —
          @@ -0,0 +1,112 @@
          +package org.apache.commons.lang3.concurrent;
          +
          +import org.easymock.EasyMockRunner;
          +import org.easymock.Mock;
          +import org.junit.Test;
          +import org.junit.runner.RunWith;
          +
          +import static org.easymock.EasyMock.expect;
          +import static org.easymock.EasyMock.replay;
          +import static org.junit.Assert.assertEquals;
          +import static org.junit.Assert.fail;
          +
          +@RunWith(EasyMockRunner.class)
          +public class MemoizerTest {
          +
          + @Mock
          + private Computable<Integer, Integer> computable;
          +
          + @Test
          + public void testOnlyCallComputableOnceIfDoesNotThrowException() throws Exception

          { + Integer input = 1; + Memoizer<Integer, Integer> memoizer = new Memoizer<Integer, Integer>(computable); + expect(computable.compute(input)).andReturn(input); + replay(computable); + + assertEquals("Should call computable first time", input, memoizer.compute(input)); + assertEquals("Should not call the computable the second time", input, memoizer.compute(input)); + }

          +
          + @Test(expected = IllegalStateException.class)
          + public void testDefaultBehaviourNotToRecalculateExecutionExceptions() throws Exception {
          + Integer input = 1;
          + Integer answer = 3;
          — End diff –

          answer is unused

          Show
          githubbot ASF GitHub Bot added a comment - Github user britter commented on a diff in the pull request: https://github.com/apache/commons-lang/pull/80#discussion_r29696865 — Diff: src/test/java/org/apache/commons/lang3/concurrent/MemoizerTest.java — @@ -0,0 +1,112 @@ +package org.apache.commons.lang3.concurrent; + +import org.easymock.EasyMockRunner; +import org.easymock.Mock; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +@RunWith(EasyMockRunner.class) +public class MemoizerTest { + + @Mock + private Computable<Integer, Integer> computable; + + @Test + public void testOnlyCallComputableOnceIfDoesNotThrowException() throws Exception { + Integer input = 1; + Memoizer<Integer, Integer> memoizer = new Memoizer<Integer, Integer>(computable); + expect(computable.compute(input)).andReturn(input); + replay(computable); + + assertEquals("Should call computable first time", input, memoizer.compute(input)); + assertEquals("Should not call the computable the second time", input, memoizer.compute(input)); + } + + @Test(expected = IllegalStateException.class) + public void testDefaultBehaviourNotToRecalculateExecutionExceptions() throws Exception { + Integer input = 1; + Integer answer = 3; — End diff – answer is unused
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user britter commented on a diff in the pull request:

          https://github.com/apache/commons-lang/pull/80#discussion_r29696898

          — Diff: src/test/java/org/apache/commons/lang3/concurrent/MemoizerTest.java —
          @@ -0,0 +1,112 @@
          +package org.apache.commons.lang3.concurrent;
          +
          +import org.easymock.EasyMockRunner;
          +import org.easymock.Mock;
          +import org.junit.Test;
          +import org.junit.runner.RunWith;
          +
          +import static org.easymock.EasyMock.expect;
          +import static org.easymock.EasyMock.replay;
          +import static org.junit.Assert.assertEquals;
          +import static org.junit.Assert.fail;
          +
          +@RunWith(EasyMockRunner.class)
          +public class MemoizerTest {
          +
          + @Mock
          + private Computable<Integer, Integer> computable;
          +
          + @Test
          + public void testOnlyCallComputableOnceIfDoesNotThrowException() throws Exception

          { + Integer input = 1; + Memoizer<Integer, Integer> memoizer = new Memoizer<Integer, Integer>(computable); + expect(computable.compute(input)).andReturn(input); + replay(computable); + + assertEquals("Should call computable first time", input, memoizer.compute(input)); + assertEquals("Should not call the computable the second time", input, memoizer.compute(input)); + }

          +
          + @Test(expected = IllegalStateException.class)
          + public void testDefaultBehaviourNotToRecalculateExecutionExceptions() throws Exception {
          + Integer input = 1;
          + Integer answer = 3;
          + Memoizer<Integer, Integer> memoizer = new Memoizer<Integer, Integer>(computable);
          + InterruptedException interruptedException = new InterruptedException();
          + expect(computable.compute(input)).andThrow(interruptedException);
          + replay(computable);
          +
          + try

          { + memoizer.compute(input); + fail(); + }

          + catch (Throwable ex)

          { + //Should always be thrown the first time + }

          +
          + memoizer.compute(input);
          + }
          +
          + @Test(expected = IllegalStateException.class)
          + public void testDoesNotRecalculateWhenSetToFalse() throws Exception {
          + Integer input = 1;
          + Integer answer = 3;
          — End diff –

          answer is unused

          Show
          githubbot ASF GitHub Bot added a comment - Github user britter commented on a diff in the pull request: https://github.com/apache/commons-lang/pull/80#discussion_r29696898 — Diff: src/test/java/org/apache/commons/lang3/concurrent/MemoizerTest.java — @@ -0,0 +1,112 @@ +package org.apache.commons.lang3.concurrent; + +import org.easymock.EasyMockRunner; +import org.easymock.Mock; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +@RunWith(EasyMockRunner.class) +public class MemoizerTest { + + @Mock + private Computable<Integer, Integer> computable; + + @Test + public void testOnlyCallComputableOnceIfDoesNotThrowException() throws Exception { + Integer input = 1; + Memoizer<Integer, Integer> memoizer = new Memoizer<Integer, Integer>(computable); + expect(computable.compute(input)).andReturn(input); + replay(computable); + + assertEquals("Should call computable first time", input, memoizer.compute(input)); + assertEquals("Should not call the computable the second time", input, memoizer.compute(input)); + } + + @Test(expected = IllegalStateException.class) + public void testDefaultBehaviourNotToRecalculateExecutionExceptions() throws Exception { + Integer input = 1; + Integer answer = 3; + Memoizer<Integer, Integer> memoizer = new Memoizer<Integer, Integer>(computable); + InterruptedException interruptedException = new InterruptedException(); + expect(computable.compute(input)).andThrow(interruptedException); + replay(computable); + + try { + memoizer.compute(input); + fail(); + } + catch (Throwable ex) { + //Should always be thrown the first time + } + + memoizer.compute(input); + } + + @Test(expected = IllegalStateException.class) + public void testDoesNotRecalculateWhenSetToFalse() throws Exception { + Integer input = 1; + Integer answer = 3; — End diff – answer is unused
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user britter commented on a diff in the pull request:

          https://github.com/apache/commons-lang/pull/80#discussion_r29696927

          — Diff: src/test/java/org/apache/commons/lang3/concurrent/MemoizerTest.java —
          @@ -0,0 +1,112 @@
          +package org.apache.commons.lang3.concurrent;
          — End diff –

          Apache license header is missing

          Show
          githubbot ASF GitHub Bot added a comment - Github user britter commented on a diff in the pull request: https://github.com/apache/commons-lang/pull/80#discussion_r29696927 — Diff: src/test/java/org/apache/commons/lang3/concurrent/MemoizerTest.java — @@ -0,0 +1,112 @@ +package org.apache.commons.lang3.concurrent; — End diff – Apache license header is missing
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user britter commented on a diff in the pull request:

          https://github.com/apache/commons-lang/pull/80#discussion_r29697345

          — Diff: src/test/java/org/apache/commons/lang3/concurrent/MemoizerTest.java —
          @@ -0,0 +1,112 @@
          +package org.apache.commons.lang3.concurrent;
          +
          +import org.easymock.EasyMockRunner;
          +import org.easymock.Mock;
          +import org.junit.Test;
          +import org.junit.runner.RunWith;
          +
          +import static org.easymock.EasyMock.expect;
          +import static org.easymock.EasyMock.replay;
          +import static org.junit.Assert.assertEquals;
          +import static org.junit.Assert.fail;
          +
          +@RunWith(EasyMockRunner.class)
          +public class MemoizerTest {
          — End diff –

          Wouldn't it make sense to create a test that uses an executor service to let several threads in parallel access the same Memoizer instance?

          Show
          githubbot ASF GitHub Bot added a comment - Github user britter commented on a diff in the pull request: https://github.com/apache/commons-lang/pull/80#discussion_r29697345 — Diff: src/test/java/org/apache/commons/lang3/concurrent/MemoizerTest.java — @@ -0,0 +1,112 @@ +package org.apache.commons.lang3.concurrent; + +import org.easymock.EasyMockRunner; +import org.easymock.Mock; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +@RunWith(EasyMockRunner.class) +public class MemoizerTest { — End diff – Wouldn't it make sense to create a test that uses an executor service to let several threads in parallel access the same Memoizer instance?
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user peteryhwong commented on a diff in the pull request:

          https://github.com/apache/commons-lang/pull/80#discussion_r29721658

          — Diff: src/main/java/org/apache/commons/lang3/concurrent/Memoizer.java —
          @@ -0,0 +1,133 @@
          +/*
          + * 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.CancellationException;
          +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;
          +
          +/**
          + * <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.</p>
          + * <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.</p>
          + * <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 Concurreny in Practice as a sample.</p>
          + *
          + * @param <A> the type of the input to the calculation
          + * @param <V> the type of the output of the calculation
          + */
          +public class Memoizer<A, V> implements Computable<A, V> {
          + private final ConcurrentMap<A, Future<V>> cache
          + = new ConcurrentHashMap<A, Future<V>>();
          + private final Computable<A, V> c;
          + private final boolean recalculate;
          +
          + /**
          + * <p>Constructs a Memoizer for the provided Computable calculation.</p>
          + * <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.</p>
          + *
          + * @param c the computation whose results should be memorized
          + */
          + public Memoizer(Computable<A, V> c)

          { + this(c, false); + }

          +
          + /**
          + * <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.</p>
          + *
          + * @param c 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(Computable<A, V> c, boolean recalculate)

          { + this.c = c; + this.recalculate = recalculate; + }

          +
          + /**
          + * <p>This method will return the result of the calculation and cache it, if it has not previously been calculated.</p>
          + * <p/>
          + * <p>This cache will also cache exceptions that occur during the computation if the

          {@code recalculate}

          parameter is
          + * 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
          + * @return the result of the calculation
          + * @throws InterruptedException thrown if the calculation is interrupted
          + * @throws IllegalStateException a wrapper around any checked exception that occurs during the computation of the result
          + */
          + public V compute(final A arg) throws InterruptedException, IllegalStateException {
          — End diff –

          Please use Overrides annotation. Moreover, IlegalStateException is a runtime (unchecked) exception and so need not be declared at the method signature.

          Show
          githubbot ASF GitHub Bot added a comment - Github user peteryhwong commented on a diff in the pull request: https://github.com/apache/commons-lang/pull/80#discussion_r29721658 — Diff: src/main/java/org/apache/commons/lang3/concurrent/Memoizer.java — @@ -0,0 +1,133 @@ +/* + * 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.CancellationException; +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; + +/** + * <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.</p> + * <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.</p> + * <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 Concurreny in Practice as a sample.</p> + * + * @param <A> the type of the input to the calculation + * @param <V> the type of the output of the calculation + */ +public class Memoizer<A, V> implements Computable<A, V> { + private final ConcurrentMap<A, Future<V>> cache + = new ConcurrentHashMap<A, Future<V>>(); + private final Computable<A, V> c; + private final boolean recalculate; + + /** + * <p>Constructs a Memoizer for the provided Computable calculation.</p> + * <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.</p> + * + * @param c the computation whose results should be memorized + */ + public Memoizer(Computable<A, V> c) { + this(c, false); + } + + /** + * <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.</p> + * + * @param c 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(Computable<A, V> c, boolean recalculate) { + this.c = c; + this.recalculate = recalculate; + } + + /** + * <p>This method will return the result of the calculation and cache it, if it has not previously been calculated.</p> + * <p/> + * <p>This cache will also cache exceptions that occur during the computation if the {@code recalculate} parameter is + * 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 + * @return the result of the calculation + * @throws InterruptedException thrown if the calculation is interrupted + * @throws IllegalStateException a wrapper around any checked exception that occurs during the computation of the result + */ + public V compute(final A arg) throws InterruptedException, IllegalStateException { — End diff – Please use Overrides annotation. Moreover, IlegalStateException is a runtime (unchecked) exception and so need not be declared at the method signature.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user coveralls commented on the issue:

          https://github.com/apache/commons-lang/pull/203

          [![Coverage Status](https://coveralls.io/builds/8563811/badge)](https://coveralls.io/builds/8563811)

          Coverage increased (+0.01%) to 93.552% when pulling *f9b98b6955d570b7c8bda815d11d491ca1a0ffe0 on PascalSchumacher:memomizer* into *c37a911d3a1541adc25c8d76f717015b2266d123 on apache:master*.

          Show
          githubbot ASF GitHub Bot added a comment - Github user coveralls commented on the issue: https://github.com/apache/commons-lang/pull/203 [! [Coverage Status] ( https://coveralls.io/builds/8563811/badge)](https://coveralls.io/builds/8563811 ) Coverage increased (+0.01%) to 93.552% when pulling * f9b98b6955d570b7c8bda815d11d491ca1a0ffe0 on PascalSchumacher:memomizer * into * c37a911d3a1541adc25c8d76f717015b2266d123 on apache:master *.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user asfgit closed the pull request at:

          https://github.com/apache/commons-lang/pull/203

          Show
          githubbot ASF GitHub Bot added a comment - Github user asfgit closed the pull request at: https://github.com/apache/commons-lang/pull/203
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user asfgit closed the pull request at:

          https://github.com/apache/commons-lang/pull/80

          Show
          githubbot ASF GitHub Bot added a comment - Github user asfgit closed the pull request at: https://github.com/apache/commons-lang/pull/80
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user PascalSchumacher commented on the issue:

          https://github.com/apache/commons-lang/pull/80

          Thanks!

          Show
          githubbot ASF GitHub Bot added a comment - Github user PascalSchumacher commented on the issue: https://github.com/apache/commons-lang/pull/80 Thanks!

            People

            • Assignee:
              garydgregory Gary Gregory
              Reporter:
              garydgregory Gary Gregory
            • Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Development