spring batch process using JobRepositoryFactoryBean - spring

I have a situation where I have to invoke the batch process and update the status of the job in my service layer. Here the JobRepositoryFactoryBean already consists of transaction manager so I should not annotate my service method with #Transactional, If I annotated I will get exception at runtime saying that "Existing transaction detected in job repository please fix and try again by removing #Transactional" If not annotated with #Transactional I am getting runtime exception saying that "no session found for current thread". Please help me in this to resolve it.

Services should not interact directly with JobRepositoryFactoryBean or the Job Repository itself. It’s not recommend to use the JobRepository API outside the Spring Batch infrastructure.
The Database tables let you follow the execution of batch jobs and see what happens at both the job and step levels for more information see here. In case you think you need an additional data persisted about the job add your own table.
To update the status of the Job itself use the
jobExecution.setExitStatus or jobExecution.setStatus API.
Read about the difference bewteeb ExitStatus and BatchStatus here.
I suggest using a a job listener in your case.
Job Listener are unlike tasklet or step listener are executed outside of the chunk’s transaction. See the following blog post here
#Component
public class JobMonitorListener {
private static Log LOGGER = LogFactory.getLog(JobMonitorListener.class);
#Autowired
JobMonitorService monitorService;
#BeforeJob
public void beforeJob(JobExecution jobExecution){
LOGGER.info("Before Job");
monitorService.persistAddtionalData(date);
}
#AfterJob
public void afterJob(JobExecution jobExecution){
LOGGER.info("Afetr Job");
monitorService.persistAddtionalData(date);
jobExecution.setExitStatus(new ExitStatus("Failed by Monitor"));
jobExecution.setStatus(BatchStatus.FAILED);
}
}
The service:
#Component
public class JobMonitorServiceImpl implements JobMonitorService {
#Transactional("transactionManager")
public void persistAddtionalData(Object) {
}
}
your job xml
<batch:job id="job">
<batch:listeners >
<batch:listener ref="jobMonitorListener"/>
</batch:listeners>
...
</batch:job>
Make sure you are using a transaction manager different then the one used by the JobRepostory. See my answer here for more Transaction Issue with Spring Batch JobRepository in Unit Test
Anyway using #Transactional with Spring batch is tricky (see the comment Spring Batch. Call methods with rollbackFor by Michael Minella the project lead of Spring Batch).

Related

Is it possible to test specific Spring REST endpoint security and avoid bootstrapping database connection?

We have a couple of Spring tests that call a secured controller endpoints. Our goal is to assure that absence of particular user roles will result into HTTP 403 status.
Our issue is that execution of those tests also bootstraps DB connection which we don't actually need.
I've already tried countless number of all kind of annotations and manual configurations to avoid initialization of DB connection but so far without luck. Can you please share example how to do that?
We use Spring Boot 2.7.
Yes, you can use #WebMvcTest, take a look at the docs. In summary, using #WebMvcTest will only bootstrap the Spring MVC components and avoid loading other application's layers. This annotation also automatically configures Spring Security for you, therefore you can test authentication/authorization rules.
Example:
#WebMvcTest(UserVehicleController.class)
class MyControllerTests {
#Autowired
private MockMvc mvc;
#MockBean
private UserVehicleService userVehicleService;
#Test
#WithMockUser(roles = "ADMIN")
void testAdminSuccess() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot"))
.willReturn(new VehicleDetails("Honda", "Civic"));
this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())
.andExpect(content().string("Honda Civic"));
}
#Test
#WithMockUser(roles = "USER")
void testUserForbidden() throws Exception {
this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isForbidden());
}
}

JPA transactional proxy within a thread issue

In my Controller I have injected (#Autowired) this Service, which implements Runnable (I need multi-threading) and I call it like so:
Thread t = new Thread(service);
t.start();
t.join();
Then, in my Service's run() I call this Repository (simple JPARepository), which is injected in Service also with #Autowired:
repository.save(someEntity);
The problem is that it doesn't persist the entity with id=1. The transactional proxy (and Hibernate connection pool) is initialized after the unsuccessful saving of the first entity. After that, it works fine.
Can anyone point me to the right direction with my issue: how to force the Thread to initialize the Hibernate transactional proxy before persisting the first entity?
You should consider to start the thread after Spring context is refreshed. This is safer because all your beans may be in an inconsistent state.
#EventListener(ContextRefreshedEvent.class)
public void handleContextStart() {
// ...
}

Can i use spring ItemProcessor from spring batch standalone?

My requirement is to read CSV,XML,JSON, excel file format through file upload functionality using spring controller. Read the file transform and save into the database.
I want to use genericItem processor like Spring Batch ItemProcessor to read the above file formats. My transform logic will be common for all and then save it to the Database.
Is there any way by which i can use spring batch ItemProcessor in standalone way without creating the batch job or is there any open source tool which can read the file in above formats?
You can use apache camel to get the desired flow. There are lot of code examples out in internet to configure apache camel with spring-boot.
Here in this example, I assume that data is posted to /csv , /excel , /xml end points and data is routed to direct:records. From direct:records we have a custom processor to process the data. We can have multiple steps like that.
#Component
public class StudentRoute extends RouteBuilder {
#Override
public void configure() {
restConfiguration()
.component("servlet")
.bindingMode(RestBindingMode.json);
rest("/student").produces("application/json")
.post("/csv").to("direct:records")
.post("/excel").to("direct:records")
.post("/xml").to("direct:records");
from("direct:records")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
Object value = exchange.getIn().getBody(Object.class);
//write your logic here
}
});
}
}
Also follow this link to get more about Apache Camel. Spring Boot, Apache Camel, and Swagger UI

Spring Batch - interrupt thread when job stops

On stopping a job the manual says
The shutdown is not immediate, since there is no way to force immediate shutdown, especially if the execution is currently in developer code that the framework has no control over, such as a business service.
My long-running steps performs checks for Thread.currentThread().isInterrupted() and throws InterruptedException. Surely there is some way to interrupt the thread running the job?
(Note: I'm using SimpleAsyncTaskExecutor with TaskletStep)
Create a bean to implement
ApplicationListener<ContextClosedEvent> onApplicationEvent()
Then you can shut down the task.
#Component
class ContextClosedHandler implements ApplicationListener<ContextClosedEvent> {
#Autowired SimpleAsyncTaskExecutor executor;
void onApplicationEvent(ContextClosedEvent event) {
executor.shutdown();
}
}
This post about the lifecycle of beans might help as well
http://technicalmumbojumbo.wordpress.com/2011/04/13/spring-framework-an-introduction-part-ii-object-lifecycle-autowiring-internationalizationi18n/

How to delay spring beans startup?

Having spring application (actually grails app) that runs apache-activemq server as spring bean and couple of apache-camel routes. Application use hibernate to work with database. The problem is simple. Activemq+Camel starts up BEFORE grails injects special methods into hibernate domain objects (actually save/update methods etc). So, if activemq already has some data on startup - camel starts processing messages w/o having grails DAO methods injected. This fails with grails.lang.MissingMethodException. Must delay activemq/camel startup before Grails injects special methods into domain objects.
If all these are defined as spring bean, you can use
<bean id="activeMqBean" depends-on="anotherBean" />
This will make sure anotherBean is initialized before activeMqBean
can you move MQ managment into a plugin? It would increase modularity and if you declare in plugin-descriptor
def loadAfter = ['hibernate']
you should have the desired behavior. Works for JBPM plugin
I am not sure in your case but lazy loading may also help e.g.
<bean id="lazybean" class="com.xxx.YourBean" lazy-init="true">
A lazily-initialized bean indicates to the IoC container to create bean instance when it is first requested. This can help you delay the loading of beans you want.
I know this question is pretty old, but I am now facing the same problem in the year 2015 - and this thread does not offer a solution for me.
I came up with a custom processor bean having a CountDownLatch, which I count down after bootstrapping the application. So the messages will be idled until the app has started fully and its working for me.
/**
* bootstrap latch processor
*/
#Log4j
class BootstrapLatchProcessor implements Processor {
private final CountDownLatch latch = new CountDownLatch(1)
#Override
void process(Exchange exchange) throws Exception {
if(latch.count > 0){
log.info "waiting for bootstrapped # ${exchange.fromEndpoint}"
latch.await()
}
exchange.out = exchange.in
}
/**
* mark the application as bootstrapped
*/
public void setBootstrapped(){
latch.countDown()
}
}
Then use it as a bean in your application and call the method setBootstrapped in your Bootstrap.groovy
Then in your RouteBuilder you put the processor between your endpoint and destination for all routes you expect messages coming in before the app has started:
from("activemq:a.in ").processRef('bootstrapProcessor').to("bean:handlerService?method=handle")

Resources