Announcement Announcement Module
Collapse
No announcement yet.
injection issue after moving to java config Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • injection issue after moving to java config

    Hey guys,

    I'm trying to run spring batch using java config instead of xml. But I can't figure out how to make sure that the injected @Configurations (AppConfig and TestDataSourceConfig) are injected before for example the jobRepositoryFactoryBean is created. So what happens here is I always run into a NPE, when I try to set the datasource, cause the required configurations have not been injected into the execution configuration. Please see the txt file attached for full stacktrace.

    Does this issue have anything to do with me using FactoryBeans designed for XML or am I missing something else here? If so, how can I resolve the issue other than extracting all the creation logic from the factory beans?

    By the way, I'm using:

    spring 3.2.3.RELEASE
    spring.batch 2.2.0.RELEASE
    spring.batch.admin 1.3.0.BUILD-SNAPSHOT

    Thanks for any help you can give me guys!

    Regards

    Jan

    Unit Test:
    Code:
    package de.jomar.test;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.test.context.ActiveProfiles;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import org.springframework.test.context.support.AnnotationConfigContextLoader;
    
    import de.jomar.test.config.DatabaseConfigurations;
    import de.jomar.test.config.TestDataSourceConfig;
    
    @ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = {
    		AppConfig.class, ExecutionConfig.class, TestDataSourceConfig.class })
    @RunWith(value = SpringJUnit4ClassRunner.class)
    @ActiveProfiles(value = DatabaseConfigurations.TEST)
    public class SpringContextTest {
    
    	@Test
    	public void testSpringContextInitialization() {
    
    	}
    
    }
    Configuration classes:

    Code:
    package de.jomar.test.config;
    
    import javax.sql.DataSource;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Profile;
    import org.springframework.jdbc.datasource.DataSourceTransactionManager;
    import org.springframework.jdbc.datasource.DriverManagerDataSource;
    import org.springframework.transaction.PlatformTransactionManager;
    
    @Configuration
    @Profile(DatabaseConfigurations.TEST)
    public class TestDataSourceConfig {
    
    	@Bean
    	public DataSource dataSource() {
    
    		return new DriverManagerDataSource("jdbc:hsqldb:mem:memorydb", "sa", "");
    
    	}
    
    	@Bean
    	public PlatformTransactionManager transactionManager() {
    		return new DataSourceTransactionManager(dataSource());
    	}
    
    }
    Code:
    package de.jomar.test;
    
    import javax.inject.Inject;
    
    import org.springframework.batch.admin.service.SimpleJobService;
    import org.springframework.batch.admin.service.SimpleJobServiceFactoryBean;
    import org.springframework.batch.core.configuration.JobRegistry;
    import org.springframework.batch.core.configuration.support.MapJobRegistry;
    import org.springframework.batch.core.explore.support.JobExplorerFactoryBean;
    import org.springframework.batch.core.launch.JobLauncher;
    import org.springframework.batch.core.launch.support.SimpleJobLauncher;
    import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean;
    import org.springframework.batch.core.scope.StepScope;
    import org.springframework.beans.factory.config.Scope;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
    import org.springframework.core.env.Environment;
    import org.springframework.scheduling.annotation.EnableScheduling;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    
    import de.jomar.test.config.TestDataSourceConfig;
    
    @Configuration
    @EnableScheduling
    @PropertySource(value = "classpath:/org/springframework/batch/admin/bootstrap/batch.properties")
    public class ExecutionConfig {
    
    	@Inject
    	private Environment env;
    
    	@Inject
    	private AppConfig appConfig;
    
    	@Inject
    	private TestDataSourceConfig dataSourceConfig;
    
    	@Bean
    	public PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
    		PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();
    		pspc.setIgnoreResourceNotFound(false);
    		pspc.setIgnoreUnresolvablePlaceholders(false);
    		pspc.setOrder(1);
    		return pspc;
    	}
    
    	@Bean
    	public Scope stepScope() {
    		return new StepScope();
    	}
    
    	@Bean
    	public ThreadPoolTaskExecutor jobLauncherTaskExecutor() {
    		ThreadPoolTaskExecutor tpte = new ThreadPoolTaskExecutor();
    		tpte.setCorePoolSize(6);
    		return tpte;
    	}
    
    	@Bean
    	public JobLauncher jobLauncher() throws Exception {
    		SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
    		jobLauncher.setJobRepository(jobRepository().getJobRepository());
    		jobLauncher.setTaskExecutor(jobLauncherTaskExecutor());
    		return jobLauncher;
    	}
    
    	@Bean
    	public JobExplorerFactoryBean jobExplorer() throws Exception {
    		JobExplorerFactoryBean jobExplorerFactory = new JobExplorerFactoryBean();
    		jobExplorerFactory.setDataSource(dataSourceConfig.dataSource());
    		return jobExplorerFactory;
    	}
    
    	@Bean
    	public JobRegistry jobRegistry() {
    		return new MapJobRegistry();
    	}
    
    	@Bean
    	public JobRepositoryFactoryBean jobRepository() throws Exception {
    		JobRepositoryFactoryBean jobRepositoryFactory = new JobRepositoryFactoryBean();
    		jobRepositoryFactory.setDataSource(dataSourceConfig.dataSource());
    		jobRepositoryFactory.setTransactionManager(dataSourceConfig
    				.transactionManager());
    		return jobRepositoryFactory;
    	}
    
    	@Bean
    	SimpleJobServiceFactoryBean jobService() throws Exception {
    		SimpleJobServiceFactoryBean jobServiceFactory = new SimpleJobServiceFactoryBean();
    		jobServiceFactory.setDataSource(dataSourceConfig.dataSource());
    		jobServiceFactory.setJobLauncher(jobLauncher());
    		jobServiceFactory.setJobRepository(jobRepository().getJobRepository());
    		jobServiceFactory.setJobLocator(jobRegistry());
    		return jobServiceFactory;
    	}
    
    	@Scheduled(fixedDelayString = "${batch.job.service.reaper.interval")
    	public void removeInactiveExecutions() throws Exception {
    		((SimpleJobService) jobService().getObject())
    				.removeInactiveExecutions();
    	}
    
    }

  • #2
    1. Your PropertySourcesPlaceholderConfigurer returning method should be static.
    2. Your @Scheduled method should not be part of your configuration but of an application bean.
    3. Why are you injecting the TestDataSourceConfig, you should inject the DataSource instead (I would move the transaction manager to the ExecutionConfig or another global configuration but not the test stuff). .

    Comment


    • #3
      Originally posted by Marten Deinum View Post
      1. Your PropertySourcesPlaceholderConfigurer returning method should be static.

      Thanks!
      That actually fixed the issue. I've read several times that those kind of beans should be static, I don't know how I could overlook that... So thanks again.

      2. Your @Scheduled method should not be part of your configuration but of an application bean.
      OK, thanks. I thought both ways would be ok.

      3. Why are you injecting the TestDataSourceConfig, you should inject the DataSource instead (I would move the transaction manager to the ExecutionConfig or another global configuration but not the test stuff). .
      I like the code to be more concise, injecting the config gives others a much better overview of where dependencies are being injected from. However in production I would replace the TestConfig with a DatasourceConfig interface or something to be more concise and achieve looser coupling at the same time.

      Comment

      Working...
      X