Search code examples
javaspringnullpointerexceptionscheduled-tasks

NullPointerException at TaskScheduler


I have a TaskScheduler class like this:

public class NotificationManger {

@Autowired
private TaskScheduler scheduler;

private ScheduledFuture<?> sc;

public NotificationManger(WebSocketMessage webSocketMessage, String userName, Date date, Date time) {
    scheduleTask(webSocketMessage, userName, combine(date,time));
}

public void scheduleTask(WebSocketMessage webSocketMessage, String userName, Date startTime) 
  { 
    Runnable r = new NotificationSenderTask(webSocketMessage, userName);

    sc = scheduler.schedule(r, new Date());//here I get a  NullPointerException 
  
   }
}

and a Runnable class like this:

class NotificationSenderTask implements Runnable {

@Autowired
SimpMessagingTemplate webSocketMessageTemplate;

private String userName;

private WebSocketMessage webSocketMessage;

public NotificationSenderTask(WebSocketMessage webSocketMessage, String userName) {
    this.webSocketMessage = webSocketMessage;
    this.userName = userName;
}

public void run() {

    webSocketMessageTemplate.convertAndSendToUser(userName, "/notification", webSocketMessage);;
}

}

and I call the TaskScheduler: NotificationManger mn = new NotificationManger(new WebSocketMessage(n), user.getEmail(),date1,date2 );

but I'm getting

java.lang.NullPointerException: null

at sc = scheduler.schedule(r, new Date());

maybe I'm using the runnable class wrong?

@EnableWebMvc
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
@Configuration
@ComponentScan(basePackages = "gestionprojet.java")
@PropertySource("classpath:application.properties")
@Import({PersistenceJPAConfig.class, WebSecurityConfig.class})
public class WebConfig extends WebMvcConfigurerAdapter {...}

Solution

  • AutowireHelper to the rescue

    Instead of refactoring all the code, the OP actually found a library to force autowiring into a non-spring-managed bean:

    The library is: https://github.com/alv-ch/alv-ch-java

    it can be used this way:

    public class NotificationManager {
        @Autowired
        private TaskScheduler scheduler;
    
        public NotificationManager(WebSocketMessage webSocketMessage, String userName, Date date, Date time) {
            AutowireHelper.autowire(this, scheduler);
            scheduleTask(webSocketMessage, userName, combine(date,time));
        }
        ...
    }
    

    This will autowire the TaskScheduler into NotificationManager, even if it is not spring managed. You can see AutowireHelper source code here.


    Another possible solution

    1. Annotate NotificationManager with @Service and remove constructor:

    @Service
    public class NotificationManager {
    
        @Autowired
        private TaskScheduler scheduler;
    
        private ScheduledFuture<?> sc;
    
        public void scheduleTask(WebSocketMessage webSocketMessage, String userName, Date date, Date time) {
            scheduleTask(webSocketMessage, userName, combine(date,time));
        }
    
        public void scheduleTask(WebSocketMessage webSocketMessage, String userName, Date startTime) 
        { 
            Runnable r = new NotificationSenderTask(webSocketMessage, userName);
            sc = scheduler.schedule(r, new Date());
    
       }
    }
    

    2. Autowire NotificationManager

    Then, at the location where you used to do NotificationManager mn = new NotificationManager(...), do this instead:

    @Autowired 
    private NotificationManager notificationManager;
    
    ...
    notificationManager.scheduleTask(new WebSocketMessage(n), user.getEmail(),date1,date2);
    

    For this to work, wherever this is, it also needs to be inside a spring-managed bean.