Yomna Hesham
Yomna Hesham

Reputation: 472

Enforce subclass to add @Schedule to a method in super abstract class in Java Spring

I have a super abstract class that has some common implemented methods and other abstract methods to be implemented by a subclass. One of the common implemented methods is a method to be annotated as @Scheduled, but I want the subclass to define how this schedule should be defined (fixed delay, fixed rate or cron .. etc). How to implement such behaviour ?

One approach I thought of is to override the method to be scheduled in the subclass such that it just call its corresponding method in the super class and add the @Scheduled on it with the desired definition, but I don't know how to enforce the subclass to do so as this method is not abstract.

Super Abstract Class

public abstract class SuperClass {
       public abstract void x();
       public void y() { 
              // Some implementation
       } 
    
       // Method to be scheduled. 
       public void scheduledMethod() {
              x();
              y();
       }
}

Subclass

public class Subclass extends SuperClass {
       @Override
       public void x() { 
              // Some implementation
       }

       // How to enforce the developer to add this ? 
       @Scheduled(cron = "0 0 0 * * ?")
       public void scheduledMethod(){
              super.scheduledMethod();
       } 
}

Upvotes: 4

Views: 1583

Answers (3)

M. Justin
M. Justin

Reputation: 21122

One thing I've done in the past in projects with constraints on a class hierarchy that I could not easily check at compile or runtime was to write a unit test that iterated through every concrete (non-abstract) instance of the class and checked whether it met that constraint.

Since this is using Spring, we probably care more that each bean of this type matches the constraint than whether each subclass on the classpath does.

The constraint in this case is that the @Scheduled annotation is present (or meta-present) on the given method in the subclass. The presence of the annotation can be easily achieved using reflection given the Class object of the subclass.

Putting this together, we can write a unit test utilizing this technique:

import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;

@SpringBootTest
public class SuperClassTest {
    @Autowired
    private List<SuperClass> beansOfThisType;

    @Test
    public void allBeansMustBeScheduled() {
        for (SuperClass bean : beansOfThisType) {
            boolean annotationPresent = AnnotationUtils.getAnnotation(ClassUtils.getMethod(bean.getClass(), "scheduledMethod"), Scheduled.class) != null;
            assertTrue(annotationPresent, "@Scheduled annotation missing from scheduledMethod for " + bean.getClass());
        }
    }
}

Checking every object of the type on the classpath rather than just the Spring beans would be a very similar approach; the difference would be the mechanism to get the list of Class objects to check. Getting the matching objects from the classpath is non-straightforward enough that it's outside the scope of this answer. How do you find all subclasses of a given class in Java? lists a number of ways to accomplish it.

Upvotes: 0

M. Justin
M. Justin

Reputation: 21122

One option would be to check that the @Scheduled annotation is present (or meta-present) at bean creation time. This can be easily achieved using reflection in a @PostConstruct method on the superclass:

import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;

public abstract class SuperClass {
    ...

    @PostConstruct
    public void enforceScheduling() {
        boolean annotationPresent = AnnotationUtils.getAnnotation(ClassUtils.getMethod(getClass(), "scheduledMethod"), Scheduled.class) != null;
        if (!annotationPresent) {
            throw new IllegalStateException("@Scheduled annotation missing from scheduledMethod");
        }
    }
}

This will cause an exception to be thrown at application start-up time when Spring attempts to create the bean, failing fast and making it very obvious what is missing.

Upvotes: 1

Hasan Can Saral
Hasan Can Saral

Reputation: 3288

I couldn't get my head around how you could use @Scheduled but, I've an alternative:

  1. In your abstract class, require a method to be implemented by subclasses to return the schedule:
public String getCronString();
  1. Programmatically schedule the task using Scheduler using the method getCronString() that's implemented in your subclasses, to return the cron schedule. Few examples on how to programmatically schedule tasks with Spring boot:

Basically, if your subclasses are not implementing public String getCronString(); your code won't compile.

Upvotes: 2

Related Questions