While debugging why my @scheduled methods are not firing I traced the issue in to ScheduledAnnotationBeanPostProcessor.
When afterSingletonsInstantiated() is called the applicationContent is already set so finishRegistration() does not get called.
And then onApplicationEvent() never seems to get called. The contextRefresh event is being published as my StartUpHouseKeeping class I have get call using
@EventListener
public void onApplicationEvent(ContextRefreshedEvent event) { ... }
If i stick a break point in afterSingletonsInstantiated() and manually call finishRegistration() things work correctly.
I'm using a java config and no web.xml
AppInit.java
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
private static final Logger log = LoggerFactory.getLogger(AppInitializer.class);
private static final String[] NO_CACHE_URLS = new String[]{"*.appcache", "*.webapp", "/static/js/*", "/static/css/*", "/api/*", "/ping"};
public AppInitializer() {
log.info("** App Initializer **");
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
}
@Override
protected WebApplicationContext createRootApplicationContext() {
// ((ConfigurableEnvironment)context.getEnvironment()).addActiveProfile(_env.getProperty("env", "local"));
return super.createRootApplicationContext();
}
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { AppConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
@Override
protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
final DispatcherServlet servlet = (DispatcherServlet) super.createDispatcherServlet(servletAppContext);
servlet.setThrowExceptionIfNoHandlerFound(true);
return servlet;
}
@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
registration.setInitParameter("trimSpace", "true");
registration.setInitParameter("throwExceptionIfNoHandlerFound", "true");
super.customizeRegistration(registration);
}
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
encodingFilter.setEncoding("UTF-8");
encodingFilter.setForceEncoding(true);
return new Filter[] { encodingFilter,
new MdcLoggingFilter(),
new HiddenHttpMethodFilter(),
new HttpPutFormContentFilter(),
new GZIPFilter(),
new DelegatingFilterProxy(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)};
}
@Override
protected void registerDispatcherServlet(ServletContext servletContext) {
log.info("Configuring servlet context");
servletContext.setInitParameter("defaultHtmlEscape", "tue");
servletContext.setInitParameter("throwExceptionIfNoHandlerFound", "true");
servletContext.addListener(new SessionListener());
servletContext.addFilter("UrlRewriteFilter", new UrlRewriteFilter()).addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD), true, "/*");
servletContext.addFilter("NoCacheControl", new NoCacheFilter()).addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, NO_CACHE_URLS);
super.registerDispatcherServlet(servletContext); // this ends up calling - getServletFilters()
}
}
AppConfig.java
@Configuration
@ComponentScan(basePackages="org.magic",
excludeFilters={ @ComponentScan.Filter(type=FilterType.ANNOTATION, value={ Controller.class }),
@ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value={ SwaggerConfig.class, WebConfig.class }) })
@EnableAspectJAutoProxy
@PropertySource(value = { "classpath:spring/application.${env:local}.properties" })
public class AppConfig {
....
}
ScheduleConfig.java
@Configuration
@EnableAsync
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer, AsyncConfigurer {
private static final Logger log = LoggerFactory.getLogger(ScheduleConfig.class);
public ScheduleConfig() {
log.info("loading scheduling config");
}
@Bean
public DistributiveEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster asyncMulticaster = new SimpleApplicationEventMulticaster();
asyncMulticaster.setTaskExecutor(getAsyncExecutor());
return new DistributiveEventMulticaster(asyncMulticaster, new SimpleApplicationEventMulticaster());
}
@Bean(name = "taskExecutor", destroyMethod="shutdownNow")
public Executor taskExecutor() {
ScheduledExecutorService delegateExecutor = Executors.newScheduledThreadPool(10);
return new DelegatingSecurityContextScheduledExecutorService(delegateExecutor, createSchedulerSecurityContext());
}
@Override
@Bean(name = "asyncExecutor")
public Executor getAsyncExecutor() {
ContextAwarePoolExecutor executor = new ContextAwarePoolExecutor(); //new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(15);
executor.setDaemon(true);
executor.setThreadNamePrefix("AsyncExecutor-");
executor.initialize();
return new DelegatingSecurityContextExecutor(executor);
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new MyAsyncUncaughtExceptionHandler();
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
}
private SecurityContext createSchedulerSecurityContext() {
SecurityContext context = SecurityContextHolder.createEmptyContext();
Collection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_USER");
Authentication authentication = new UsernamePasswordAuthenticationToken("nightly tasks", Credentials.SYSTEM, authorities);
context.setAuthentication(authentication);
return context;
}
}
@EnableScheduling
, a@Scheduled
method, and amain
method to drive the program. Take out all the irrelevant pieces. – Messere