ScheduledAnnotationBeanPostProcessor does not finishRegistration()
Asked Answered
D

0

7

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;
    }
}
Didymous answered 1/11, 2016 at 5:51 Comment(3)
Can you post a small, fully portable, sample project on GitHub that easily reproduces the problem?Roturier
not sure how easy that would be. this is starting not to be a small project anymoreDidymous
Doesn't need to be a github project. Provide a minimal reproducible example, emphasis on minimal. We don't need a web application for this. We need the minimal configuration of @EnableScheduling, a @Scheduled method, and a main method to drive the program. Take out all the irrelevant pieces.Messere

© 2022 - 2024 — McMap. All rights reserved.