Search code examples
spring-bootspring-batchspring-batch-admin

read external properties files in spring batch admin


Premise : i'm new to spring batch.
I'm trying to customize the spring batch admin but , till now, i'm not been able to let my classes read some properties from an external file .
My jobs extract data from a third party database to print a report , therefore i need two datasource : one to gather the report information and one to store the job's status and metadata.
I've read the tutorial : http://docs.spring.io/spring-batch-admin/reference/customization.html
and many other tutorials and the following post load external config file in spring batch admin .
Yet i'm not been able to start the application . It must run on Tomcat 6 .
this is a screen shoot of my project tree, adapted by the official example :
enter image description here

this is my configuration class :

    @Configuration
@EnableBatchProcessing
public class RegistroGiornalieroConfiguration extends DefaultBatchConfigurer {

    private final static Logger log = LoggerFactory.getLogger(RegistroGiornalieroConfiguration.class);

    private final static Date data = null;

    @Autowired
    public JobBuilderFactory jobFactory;

    @Autowired
    public StepBuilderFactory stepFactory;


    @Autowired
    VolumiPropertySource volumiPropertySource;

    @Value("${batch.jdbc.driver}")
    private String driverName; 

    /**
     * datasource di default, viene resitituito dall'annotazione Autowired
     * @return
     * @throws SQLException
     */
    @Bean(name="springBatchDatasource")
    @Primary
    public DataSource springBatchDatasource() throws SQLException {
        final DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(driverName);//"com.mysql.jdbc.Driver");
//      dataSource.setUrl(env.getProperty("springBatchUrl"));//"jdbc:mysql://localhost/spring_batch_annotations");
//      dataSource.setUsername(env.getProperty("springBatchUser"));//"root");
//      dataSource.setPassword(env.getProperty("springBatchPassword"));//"root");
        return dataSource;
    }


    /**
     * Datasource secondario, viene restituito da getDatasource
     * @return
     * @throws SQLException
     */
    @Bean(name="estarDatasource")
    public DataSource estarDatasource() throws SQLException {
        final DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(driverName);//"com.mysql.jdbc.Driver");
//      dataSource.setUrl(env.getProperty("url"));//"jdbc:mysql://localhost/spring_batch_annotations");
//      dataSource.setUsername(env.getProperty("user"));//"root");
//      dataSource.setPassword(env.getProperty("password"));//"root");
        return dataSource;
    }


//  @PostConstruct
//  public void init()
//  {
//      Properties p = volumiPropertySource.getIniProperties();
//      MutablePropertySources sources = env.getPropertySources();
//      sources.addFirst(new PropertiesPropertySource("volumi",p));
//      env.getActiveProfiles();
//  }



    @Bean
    @StepScope
    public JdbcCursorItemReader<RegistroGiornaliero> reader( 
            @Value("#{jobParameters[data]}")
            Date data)
        {
        System.out.println("reader");

//       select     protocol.nprotoc,protocol.dataprot,protocol.arrpar,protocol.numdoc,
//          protocol.datadoc,protocol.OGGETTO,protocol.ANNULLATO,protocol.USERINS as protuser,
//          protimg.DOCID,protimg.USERINS imguser ,protimg.PRINCIPALE,
//          assegna.livello,assegna.possesso,assegna.SEQUENZA,
//          organigramma.LIVELLO,organigramma.DESCRIZIONE,
//          protente.ente
//          from 
//         protocol left outer join protimg on protocol.NPROTOC = protimg.NPROTOC 
//         left outer join protente on protocol.NPROTOC = protente.NPROTOC
//         left outer join assegna on protocol.NPROTOC = assegna.NPROTOC
//         left outer join organigramma on assegna.LIVELLO = organigramma.LIVELLO
//          where 
//              protocol.dataprot = 20160616 and protocol.NPROTOC = '201600014709'
//          order by protocol.nprotoc,assegna.SEQUENZA;     


         StringBuilder sb = new StringBuilder("select")
            .append(" protocol.nprotoc,protocol.dataprot,protocol.arrpar,protocol.numdoc,")
            .append(" protocol.datadoc,protocol.OGGETTO,protocol.ANNULLATO,protocol.USERINS as protuser,")
            .append(" protimg.DOCID,protimg.USERINS imguser ,protimg.PRINCIPALE,")
            .append(" assegna.possesso,assegna.SEQUENZA,")
            .append(" organigramma.LIVELLO,organigramma.DESCRIZIONE,")
            .append(" protente.ente ")
            .append(" from protocol left outer join protimg on protocol.NPROTOC = protimg.NPROTOC  ")
            .append(" left outer join protente on protocol.NPROTOC = protente.NPROTOC  ")
            .append(" left outer join assegna on protocol.NPROTOC = assegna.NPROTOC  ")
            .append(" left outer join organigramma on assegna.LIVELLO = organigramma.LIVELLO  ")
            .append(" where ")
            .append(" protocol.dataprot = ? ")
            .append(" order by protocol.nprotoc,assegna.SEQUENZA "); 


//      StringBuilder sb = new StringBuilder("select")
//          .append(" protocol.nprotoc,protocol.dataprot,protocol.arrpar,protocol.numdoc,")
//          .append(" protocol.datadoc,protocol.OGGETTO,protocol.ANNULLATO,protocol.USERINS as protuser, ")
//          .append(" protimg.DOCID,protimg.USERINS imguser ,protimg.PRINCIPALE, ")
//          .append(" assegna.livello,assegna.possesso,assegna.SEQUENZA, ")
//          .append(" organigramma.LIVELLO as livelloOrg,organigramma.DESCRIZIONE, ")
//          .append(" protente.ente ")
//          .append(" from protocol,protimg,protente,assegna,operatori,organigramma ")
//          .append(" where ")
//          .append(" protocol.NPROTOC = protimg.NPROTOC and protocol.NPROTOC = assegna.NPROTOC ")
//          .append(" and protocol.NPROTOC = protente.NPROTOC and assegna.LIVELLO = organigramma.LIVELLO ")
//          .append(" and protocol.dataprot = ? ");
        JdbcCursorItemReader<RegistroGiornaliero> reader = new JdbcCursorItemReader<RegistroGiornaliero>();
        try {
            reader.setDataSource(estarDatasource());
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        reader.setPreparedStatementSetter(new ParameterSetter(data));
        reader.setSql(sb.toString());
        reader.setRowMapper(estarRowMapper());
        reader.setVerifyCursorPosition(false);
        return reader;
    }

    @Bean
    public RegistroGiornalieroProcessor processorGenerazioneRegistro() {
        return new RegistroGiornalieroProcessor();
    }

    @Bean
    public ItemWriter<RegistroGiornaliero> pdfwriter() {
        return new PDFItemWriter<RegistroGiornaliero>();
    }


    @Bean
    public JobExecutionListener listener() {
        return new JobCompletionNotificationListener();
    }

    @Bean
    public RegistroGiornalieroRowMapper estarRowMapper() {
        return new RegistroGiornalieroRowMapper();
    }



    @Bean 
    public Job generaRegistroGiornaliero()
    {
          return jobFactory
                    .get("generaRegistroGiornaliero")
                    .incrementer(new RunIdIncrementer())
                    .flow(leggiDocumentiProtocollo())
                    .end()
                    .build(); 
    }


    @Bean 
    public Step leggiDocumentiProtocollo()
    {
        return stepFactory.get("leggiDocumentiProtocollo")
                .<RegistroGiornaliero, RegistroGiornaliero> chunk(10)
                .reader(reader(data))
                .processor(processorGenerazioneRegistro())
                .writer(pdfwriter())
                .build();
    }



    @Override
    public JobRepository getJobRepository() {


        JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
        // use the autowired data source
        try {
            factory.setDataSource(springBatchDatasource());
        } catch (SQLException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        factory.setTransactionManager(getTransactionManager());

        try {
            factory.afterPropertiesSet();
            return factory.getObject();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            //e.printStackTrace();
        }
        return null;
    }



}

this is my job configuration (registrogiornaliero.xml):

<?xml version="1.0" encoding="utf-8"?>
<beans  xmlns="http://www.springframework.org/schema/beans" 
        xmlns:context="http://www.springframework.org/schema/context" 
        xmlns:tx="http://www.springframework.org/schema/tx" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-3.0.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">        
    <context:annotation-config/>
    <context:component-scan base-package="it.infogroup.estav.registrogiornaliero"/>
</beans>

and finally this is my env.xml , as by stackoverflow post :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- Use this to set additional properties on beans at run time -->
    <bean id="placeholderProperties"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:/org/springframework/batch/admin/bootstrap/batch.properties</value>
                <value>classpath:batch-default.properties</value>
                <!-- <value>classpath:batch-${ENVIRONMENT:hsql}.properties</value>-->
                <value>classpath:batch-${ENVIRONMENT:mysql}.properties</value>
                <!-- here we load properties from external config folder -->
                <value>file:${app.conf}</value>
             </list>
        </property>
        <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
        <property name="ignoreResourceNotFound" value="false" />
        <property name="ignoreUnresolvablePlaceholders" value="false" />
        <property name="order" value="1" />
    </bean>
</beans>

any help will be appreciated


Solution

  • Disclaimer : i'm new to spring batch , therefore this could not be the best solution :

    1. i used the propertysource annotation on my configuration class, i've removed it and changed the env-context.xml as shown below :

      http://www.springframework.org/schema/beans/spring-beans.xsd">

      <!-- Use this to set additional properties on beans at run time -->
      <bean id="placeholderProperties"
          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
          <property name="locations">
              <list>
                  <value>classpath:/org/springframework/batch/admin/bootstrap/batch.properties</value>
                  <value>${app.conf}/batch-default.properties</value>
                  <!-- <value>classpath:batch-${ENVIRONMENT:hsql}.properties</value>-->
                  <value>${app.conf}/batch-${ENVIRONMENT}.properties</value>
                  <!-- here we load properties from external config folder -->
                  <value>${app.conf}/estardatasource.properties</value>
               </list>
          </property>
          <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
          <property name="ignoreResourceNotFound" value="false" />
          <property name="ignoreUnresolvablePlaceholders" value="false" />
          <property name="order" value="1" />
      </bean>
      

    ${app.conf} is a jvm argument.
    2. I've replaced the use of the Environment class with placeholder variables es. :

    @Value("${volumi}")
    private String volumi;
    

    Problem number 2 : multiple datasources , there are a lot of posts which describe different solutions, including overriding the DefaultBatchConfigurer class and its getJobRepository method .
    The only solution that has worked for me is adding

    @ComponentScan(basePackageClasses = DefaultBatchConfigurer.class) 
    

    to the configuration class, as described (and clearly explained) in Use of multiple DataSources in Spring Batch

    And override data-source-context.xml file : i've placed it under /src/main/resources/META-INF/spring/batch/override/ .
    Its content is :

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:jdbc="http://www.springframework.org/schema/jdbc"
        xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="estarDatasource" name="estarDatasource" primary="false"
            class="org.apache.commons.dbcp.BasicDataSource">
            <property name="driverClassName" value="${batch.jdbc.driver}" />
            <property name="url" value="${url}" />
            <property name="username" value="${user}" />
            <property name="password" value="${password}" />
            <property name="testWhileIdle" value="${batch.jdbc.testWhileIdle}"/>
            <property name="validationQuery" value="${batch.jdbc.validationQuery}"/>
        </bean>
    
        <!--  Initialise the database if enabled: -->
        <jdbc:initialize-database data-source="estarDatasource" enabled="${batch.data.source.init}" ignore-failures="DROPS">
            <jdbc:script location="${batch.drop.script}"/>
            <jdbc:script location="${batch.schema.script}"/>
            <jdbc:script location="${batch.business.schema.script}"/>
        </jdbc:initialize-database>
    
    
        <bean id="dataSource" autowire-candidate="true" primary="true" 
            class="org.apache.commons.dbcp.BasicDataSource">
            <property name="driverClassName" value="${batch.jdbc.driver}" />
            <property name="url" value="${batch.jdbc.url}" />
            <property name="username" value="${batch.jdbc.user}" />
            <property name="password" value="${batch.jdbc.password}" />
            <property name="testWhileIdle" value="${batch.jdbc.testWhileIdle}"/>
            <property name="validationQuery" value="${batch.jdbc.validationQuery}"/>
        </bean>
    
        <bean id="transactionManager"
            class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource" />
        </bean>
    
        <!--  Initialise the database if enabled: -->
        <jdbc:initialize-database data-source="dataSource" enabled="${batch.data.source.init}" ignore-failures="DROPS">
            <jdbc:script location="${batch.drop.script}"/>
            <jdbc:script location="${batch.schema.script}"/>
            <jdbc:script location="${batch.business.schema.script}"/>
        </jdbc:initialize-database>
    
    </beans>
    

    Hope this help some other beginner and i'd be glad to know if this is an appropriate solution , and , eventually , how to read the properties into the Environment class.