According to the documentation spring batch admin is very easy to embed into the existing application. Simply copying web.xml and index.jsp then adding needed dependencies i
The short answer is that you won't want to use @EnableBatchProcessing
with Spring Batch Admin. SBA provides a number of beans on a global scale that the @EnableBatchProcessing
also provides. SBA 2.0 (currently in development) will probably fill the gaps between what is currently there and what @EnableBatchProcessing
provides (specifically providing the JobBuilderFactory
and StepBuilderFactory
).
To get yourself running, you should be able to (I haven't tired this myself) configure in the META-INF/spring/batch/override/
directory a JobBuilderFactory
and a StepBuilderFactory
for global use. From there, you can use XML files in the META-INF/spring/batch/jobs
directory that do nothing more than component scan for your @Configuration
classes. However, leave off the @EnableBatchProcessing
because of the duplication of beans.
For the record, this isn't an Spring Boot issue since @EnableBatchProcessing
is a Spring Batch annotation, not a Boot one.
Spring Batch Admin 2.0-BUILD-SNAPSHOT introduce a new Annoation @EnableBatchAdmin
for easy to integrate with spring boot.
There is also a samples project https://github.com/spring-projects/spring-batch-admin-samples.
to complete the answer, here is the code to create the two beans once you disable the @EnableBatchProcessing annotation
@Autowired
JobRepository jobRepository;
@Autowired
PlatformTransactionManager transactionManager;
@Bean
public JobBuilderFactory jobBuilderFactory() {
return new JobBuilderFactory(jobRepository);
}
@Bean
public StepBuilderFactory stepBuilderFactory() {
return new StepBuilderFactory(jobRepository, transactionManager);
}
This ClassCastException is caused by
classpath:/org/springframework/batch/admin/web/resources/servlet-config.xml
loading
META-INF/spring/batch/servlet/resources/resource-context.xml
which contains
<mvc:annotation-driven />
This conflicts with the mvc configuration in the Spring Java configuration class. The following class can be used to embed Spring Batch Admin within an existing application that uses Java configuration.
@Configuration
@EnableWebMvc
@ImportResource({"classpath*:/META-INF/spring/batch/bootstrap/**/*.xml"
, "classpath*:/META-INF/spring/batch/override/**/*.xml"
, "classpath*:/org/springframework/batch/admin/web/resources/webapp-config.xml"
, "classpath*:/META-INF/spring/batch/servlet/manager/**/*.xml"
, "classpath:base-menu-config.xml"
})
public class SpringBatchAdminConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("classpath:/META-INF/");
}
@Bean
public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() {
return new SimpleControllerHandlerAdapter();
}
@Bean
public BeanNameUrlHandlerMapping beanNameUrlHandlerMapping() {
return new BeanNameUrlHandlerMapping();
}
@Bean
public BeanNameViewResolver beanNameViewResolver() {
return new BeanNameViewResolver();
}
@Bean(name = "defaultResources")
public PropertiesFactoryBean defaultResources() {
return new PropertiesFactoryBean();
}
@Bean(name = "jsonResources")
public PropertiesFactoryBean jsonResources() {
return new PropertiesFactoryBean();
}
@Bean
public HomeController homeController() throws IOException {
HomeController homeController = new HomeController();
homeController.setDefaultResources(defaultResources().getObject());
homeController.setJsonResources(jsonResources().getObject());
return homeController;
}
@Bean
public MenuManager menuManager() {
return new MenuManager();
}
@Bean(name = "freemarkerConfig")
public HippyFreeMarkerConfigurer hippyFreeMarkerConfigurer() {
HippyFreeMarkerConfigurer hippyFreeMarkerConfigurer = new HippyFreeMarkerConfigurer();
hippyFreeMarkerConfigurer.setTemplateLoaderPaths("/WEB-INF/web", "classpath:/org/springframework/batch/admin/web");
hippyFreeMarkerConfigurer.setPreferFileSystemAccess(false);
hippyFreeMarkerConfigurer.setFreemarkerVariables(Collections.singletonMap("menuManager", (Object) menuManager()));
Properties freemarkerSettings = new Properties();
freemarkerSettings.put("default_encoding", "UTF-8");
freemarkerSettings.put("output_encoding", "UTF-8");
hippyFreeMarkerConfigurer.setFreemarkerSettings(freemarkerSettings);
return hippyFreeMarkerConfigurer;
}
public AjaxFreeMarkerView parentLayout() {
AjaxFreeMarkerView ajaxFreeMarkerView = new AjaxFreeMarkerView();
FreeMarkerViewResolver freeMarkerViewResolver = new FreeMarkerViewResolver();
freeMarkerViewResolver.setExposeSpringMacroHelpers(false);
freeMarkerViewResolver.setAllowRequestOverride(true);
ajaxFreeMarkerView.setViewResolver(freeMarkerViewResolver);
Properties attributes = new Properties();
attributes.put("titleCode", "home.title");
attributes.put("titleText", "Spring Batch Admin");
ajaxFreeMarkerView.setAttributes(attributes);
return ajaxFreeMarkerView;
}
@Value("#{resourceService.servletPath}")
private String servletPath;
@Bean(name="standard")
public AjaxFreeMarkerView standard() {
AjaxFreeMarkerView standard = parentLayout();
standard.setUrl("/layouts/html/standard.ftl");
standard.setContentType("text/html;charset=UTF-8");
standard.getAttributesMap().put("body", "/layouts/html/home.ftl");
standard.getAttributesMap().put("servletPath", servletPath);
return standard;
}
@Bean(name="standard.rss")
public AjaxFreeMarkerView standardRss() {
AjaxFreeMarkerView standardRss = parentLayout();
standardRss.setUrl("/layouts/html/standard.ftl");
standardRss.setContentType("text/xml");
standardRss.getAttributesMap().put("body", "/layouts/rss/home.ftl");
standardRss.getAttributesMap().put("servletPath", servletPath);
return standardRss;
}
@Bean(name="standard.json")
public AjaxFreeMarkerView standardJson() {
AjaxFreeMarkerView standardJson = parentLayout();
standardJson.setUrl("/layouts/json/standard.ftl");
standardJson.setContentType("application/json");
standardJson.getAttributesMap().put("body", "/layouts/json/home.ftl");
standardJson.getAttributesMap().put("servletPath", servletPath);
return standardJson;
}
@Bean(name="home")
public AjaxFreeMarkerView home() {
return standard();
}
@Bean(name="home.json")
public AjaxFreeMarkerView homeJson() {
AjaxFreeMarkerView homeJson = standardJson();
homeJson.getAttributesMap().put("body", "/layouts/json/home.ftl");
return homeJson;
}
}
A single XML file is also required for the abstract base menu which is referenced elsewhere in the Spring Batch Admin project. This is required as abstract beans can not be provided from a Spring Java configuration.
<?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-2.5.xsd">
<bean id="baseMenu" abstract="true">
<property name="prefix" value="#{resourceService.servletPath}" />
</bean>
</beans>
Maven dependencies. Take care to ensure only a single version of the base Spring framework is pulled in by Maven.
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-admin-manager</artifactId>
<version>1.3.1.RELEASE</version>
</dependency>
<dependency>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>runtime</scope>
<version>1.8.0.10</version>
</dependency>
Spring batch also expects in a default configuration for the following files to exist at the root of the classpath.
batch-default.properties
# Default placeholders for database platform independent features
batch.remote.base.url=http://localhost:8080/spring-batch-admin-sample
# Non-platform dependent settings that you might like to change
batch.job.configuration.file.dir=/tmp/config
build.artifactId=1
build.version=1
build.buildNumber=1
build.timestamp=1
log.enableConsole=true
batch-hsql.properties
# Placeholders batch.*
# for HSQLDB:
batch.jdbc.driver=org.hsqldb.jdbcDriver
batch.jdbc.url=jdbc:hsqldb:mem:testdb;sql.enforce_strict_size=true
# Override and use this one in for a separate server process so you can inspect
# the results (or add it to system properties with -D to override at run time).
# batch.jdbc.url=jdbc:hsqldb:hsql://localhost:9005/samples
batch.jdbc.user=sa
batch.jdbc.password=
batch.database.incrementer.class=org.springframework.jdbc.support.incrementer.HsqlMaxValueIncrementer
batch.schema.script=classpath*:/org/springframework/batch/core/schema-hsqldb.sql
batch.drop.script=classpath*:/org/springframework/batch/core/schema-drop-hsqldb.sql
batch.business.schema.script=classpath:/business-schema-hsqldb.sql
# Non-platform dependent settings that you might like to change
# batch.data.source.init=true
business-schedule-hsqldb.sql
DROP TABLE ERROR_LOG IF EXISTS;
CREATE TABLE ERROR_LOG (
JOB_NAME CHAR(20) ,
STEP_NAME CHAR(20) ,
MESSAGE VARCHAR(300) NOT NULL
) ;
I've a working version here based on the same example (I forked the original one): https://github.com/vesperaba/spring-batch-admin-spring-boot.
I followed Michael Minella advice and I overwrote the SpringBatch property holder with a custom one.
I also added a job to check it's working now