0

It might be a newbie question but I could not find the reason. I have a RetryOnException class to control the retry counts of certain tasks. I have different tasks to control and therefore I created different objects of this class.

The problem is, if I call the errorOccured() of one object, the numberOfTriesLeft of all my objects are changed. Seems to me that all objects are just referencing to the same object.

How can this happen and how do I fix it?

@Component
public class RetryOnException {

    private int numberOfRetries;    // retries
    private int numberOfTriesLeft;  // retries
    private long timeToWait;        // PauseMs

    @Autowired
    public RetryOnException(@Value("${exception.retries}") int numberOfRetries,
            @Value("${exception.pauseMs}") long timeToWait) {
        this.numberOfRetries = numberOfRetries;
        this.numberOfTriesLeft = numberOfRetries;
        this.timeToWait = timeToWait;
    }

    @Value("${exception.retries}")
    private int defaultRetries;

    /**
     * @return true if there are tries left
     */
    public boolean shouldRetry() {
        return numberOfTriesLeft > 0;
    }

    public void errorOccured() throws Exception {
        numberOfTriesLeft--;
        if (!shouldRetry()) {

            throw new Exception("Retry Failed: Total " + numberOfRetries
                    + " attempts made at interval " + getTimeToWait()
                    + "ms");
        }
        waitUntilNextTry();
    }

    public long getTimeToWait() {
        return timeToWait;
    }

    private void waitUntilNextTry() {
        try {
            Thread.sleep(getTimeToWait());
        } catch (InterruptedException ignored) {
        }
    }

    public void resetRetryCount() {
        numberOfTriesLeft = defaultRetries;
    }

}
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Felix Wong
  • 161
  • 5
  • 15
  • 1
    How do you create the `RetryOnException` objects? – Guy Feb 13 '20 at 12:33
  • 2
    Does this answer your question? [When annotating a class with @Component, does this mean it is a Spring Bean and Singleton?](https://stackoverflow.com/questions/5907501/when-annotating-a-class-with-component-does-this-mean-it-is-a-spring-bean-and) – Julien Feb 13 '20 at 12:34
  • 1
    Felix are those objects Autowired? @Component turns the class into a Spring bean, and hence, a Singleton. – Stultuske Feb 13 '20 at 12:35
  • Put @Scope('protype') in your RetryOnException class. – Tushar Jajodia Feb 13 '20 at 13:25

4 Answers4

1

It seems that you're using Spring IoC container to create your RetryOnException objects. Please note that the default scope of a bean is singletone. It means that a bean instance is shared across your application.

Vladimir Pligin
  • 1,547
  • 11
  • 18
0

You haven't pasted the code where you're using the RetryOnException class, so I am going to assume how you're using it. Based on the fact that it's annotated with @Component, I assume you are using @Autowired to inject this class into your other classes where you are using it.

Doing this will only use a single instance of this class (i.e new RetryOnException() will only be called once, and the same instance will be used wherever you use @Autowired). This is because you have made RetryOnException a spring managed singleton.

In order to ensure that all of the instances where you're using this class have isolated behavior, you will need to manually instantiate this class when you use it, so instead of using @Autowired, you need to instantiate it and manually pass through the values into the constructor like:

RetryOnException retryOnException = new RetryOnException(numberOfRetries, timeToWait);

You can let the consuming class access the @Values, and pass those into this constructor.

Ntokozo Zwane
  • 1,572
  • 14
  • 20
0

It seems that you use Spring. And when you create object of this class by @Autowired annotation it means that you will always get one instatnce of this object. You can read more about singleton pattern and it implementation in Spring to understand what is going on when you use @Component and @Autowired annotations.

Frofike
  • 321
  • 2
  • 6
  • Thanks all! Yes, I am using `@Autowired` to create the object. After annotating `@Scope("prototype")` to my class and it works as expected. Really learned something! Thank. – Felix Wong Feb 14 '20 at 02:49
0

Your @Component annotated class in spring is by-default a singleton i.e. only one object will be created and autowired to everyplace where you use it using @Autowired annotation.

I suggest you go through this quick tutorial for scopes in Spring.

Quick Bite: You can try to use @Scope("prototype") along with @Component to set the scope to prototype, which is exactly opposite to singleton, i.e. it will make new instance for every single place where it is referred using @Autowired. But in this case you have to pass the instance yourself to everyplace where you want to pass the existing object.

Vivek Gupta
  • 2,534
  • 3
  • 15
  • 28