Scheduler Spring boot - spring-batch

#Bean
public LockProvider lockProvider(DataSource dataSource) {
return new JdbcTemplateLockProvider(dataSource);
}
#Bean
public ScheduledLockConfiguration taskScheduler(LockProvider lockProvider) {
return ScheduledLockConfigurationBuilder
.withLockProvider(lockProvider)
.withPoolSize(10)
.withDefaultLockAtMostFor(Duration.ofMinutes(10))
.build();
}
My requirement is to run only single scheduler at only one instance in clustered enviroment. For this i am using shedlock, but problem is that at server startup i am getting the below exception, "java.lang.ClassCastException: net.javacrumbs.shedlock.spring.SpringLockableTaskSchedulerFactoryBean cannot be cast to org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler"
Help me on this.

You can easily do this with dlock. You simply do the following and add registrar to your xml config.
Java Code
#TryLock(name = "doSomeWork", owner = "serviceA", lockFor = ONE_MINUTE)
public void doSomeWork() {
//...
}
XML Config
<!-- A bean for the lock implementation. Note that there should be only one global implementation-->
<bean id="postgresLock" class="com.yusufaytas.dlock.jdbc.PostgresIntervalLock">
<constructor-arg type="javax.sql.DataSource" ref="lockDataSource"/>
</bean>
<!-- The lock gets auto-registered to the registrar -->
<bean id="lockRegistrar" class="com.yusufaytas.dlock.spring.IntervalLockRegistrar"/>

Related

unable to set/get the property values in the rowMapper

I'm trying to access the StepExecution in my RowMapper but unable to do so. I have a property set in the xml called 'prop1'. I expect this to be set but it is not setting.I also added a #BeforeStep method to the RowMapper hoping I can get the stepExecutionContext but this method is never invoked. Is there something else I need to do?
Here is my xml:
<bean id="bean1"
class="org.springframework.batch.item.database.JdbcCursorItemReader"
scope="step">
<property name="dataSource" ref="dataSource" />
<property name="sql"
value="${sql}"/>
<property name="fetchSize" value="${fetchSize}"></property>
<property name="rowMapper">
<bean id="rowMapper1" class="c.p.MyRowMapper" scope="step">
<property name="prop1" value="${prop1}"></property>
</property>
Here is my RowMapper:
public class MyRowMapper implements RowMapper<Object>{
private String prop1;
private StepExecution se;
public String getProp1() {
return stepFatpCount;
}
public void setProp1(String rop1) {
this. prop1 = prop1;
}
#BeforeStep
public void beforeStep(StepExecution stepExecution){
this.se = stepExecution;
}
}
I have some properties set in the stepExecutionContext before this step in another step and I want to use them here in the RowMapper. The same thing works in the ItemProcessor but not the RowMapper. Please let me know if I need to do something more for lazy binding or any other issue.
Thanks.
The step execution context is not shared between steps. Maybe you mean job execution context?
I suppose you could register rowMapper1 as a listener in the step (with the <listeners> tag), but if you just want to read some value from the job execution context you could use
value="#{jobExecutionContext['foobar']}"
If you do want to have injected some value from the step execution context, you just have to replace stepExecutionContext above.

Spring batch: Create file by setting name programmatically

I have a spring batch job (defined in xml) which generates the csv export.
Inside FlatFileItemWriter bean I am setting resource, where the name of file is set.
<bean id="customDataFileWriter" class="org.springframework.batch.item.file.FlatFileItemWriter" scope="step">
<property name="resource" value="file:/tmp/export/custom-export.csv"/>
...
Now I need to set this file name taking account a certain logic, so I need to set the file name from some java class. Any ideas?
Use the different builder classes of spring batch (job builder, step builder, and so on). Have a look at https://blog.codecentric.de/en/2013/06/spring-batch-2-2-javaconfig-part-1-a-comparison-to-xml/ to get an idea.
You can implement your own FlatFileItemWriter to override the method setResource and add your own logic to rename the file.
Here's an example implementation :
#Override
public void setResource(Resource resource) {
if (resource instanceof ClassPathResource) {
// Convert resource
ClassPathResource res = (ClassPathResource) resource;
try {
String path = res.getPath();
// Do something to "path" here
File file = new File(path);
// Check for permissions to write
if (file.canWrite() || file.createNewFile()) {
file.delete();
// Call parent setter with new resource
super.setResource(new FileSystemResource(file.getAbsolutePath()));
return;
}
} catch (IOException e) {
// File could not be read/written
}
}
// If something went wrong or resource was delegated to MultiResourceItemWriter,
// call parent setter with default resource
super.setResource(resource);
}
Another possibility exists with the use of jobParameters, if your logic can be applied before job is launched. See 5.4 Late Binding of Spring Batch Documentation.
Example :
<bean id="flatFileItemReader" scope="step" class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="resource" value="#{jobParameters['input.file.name']}" />
</bean>
You can also use a MultiResourceItemWriter with a custom ResourceSuffixCreator. That will let you create 1 to n files with a common filename pattern.
Here's an example of the method getSuffix of a custom ResourceSuffixCreator :
#Override
public String getSuffix(int index) {
// Your logic
if (true)
return "XXX" + index;
else
return "";
}

spring receive emails without xml (using annotations only)

I need to periodically check about 30 mailboxes and want to do this with annotations only. I know how to do it with XML files, it looks like this:
<mail:inbound-channel-adapter id="ImapAdapter"
store-uri="imaps://${login}:${pass}#${host}:993/inbox"
channel="testReceiveEmailChannel"
should-delete-messages="false"
should-mark-messages-as-read="true"
auto-startup="true"
java-mail-properties="javaMailProperties">
<int:poller fixed-delay="200"
time-unit="SECONDS"
task-executor="asyncTaskExecutor"/>
</mail:inbound-channel-adapter>
<int:channel id="testReceiveEmailChannel">
<int:interceptors>
<int:wire-tap channel="logger"/>
</int:interceptors>
</int:channel>
<int:service-activator input-channel="testReceiveEmailChannel"
ref="testMailReceiverService"
method="receive"/>
<bean id="testMailReceiverService" class="com.myproject.email.EmailReceiverService">
<property name="mailBox" value="${login}"/>
</bean>
<int:logging-channel-adapter id="logger" level="DEBUG"/>
I know that Spring 4+ have #InboundChannelAdapter but I dont know how to use it. Actually I am new in Spring, so any helps very appreciated!
You are looking into the correct way - #InboundChannelAdapter. If you take a look to the Documentation properly, you'll see something like this:
#Bean
#InboundChannelAdapter(value = "testReceiveEmailChannel", poller = #Poller(fixedDelay = "200000", taskExecutor = "asyncTaskExecutor"))
public MessageSource<javax.mail.Message> mailMessageSource(MailReceiver mailReceiver) {
MailReceivingMessageSource mailReceivingMessageSource = new MailReceivingMessageSource(mailReceiver);
// other setters here
return mailReceivingMessageSource;
}
Where MailReceiver is something like this:
#Bean
public MailReceiver imapMailReceiver(#Value("imaps://${login}:${pass}#${host}:993/inbox") storeUrl) {
ImapMailReceiver imapMailReceiver = new ImapMailReceiver(storeUrl);
// other setters here
return imapMailReceiver;
}
and so with other #Beans for MessageChannel and #ServiceActivator for your EmailReceiverService.
Consider as a tool for Java Configuration the Spring Integration Java DSL.

Write a Spring batch custom item writer

I need to write a Spring batch custom item writer that uses a footer, but I can't use the delegate pattern.
Is there another way to write a Spring batch custom item writer?
Thank you in advance.
Create a custom ItemWriter that implements ItemStream (to manage restartability and footer writing) and overwrite the next methods:
ItemWrite.write(List<> items): write items and during writing perform necessary data calculation for footer
ItemStream.update(ExecutionContext): save calculated footer data in write() method
ItemStream.open(ExecutionContext): restore previously saved footer data
ItemStream.close(): do real footer writing (directly in your own writer or using a callback)
Check here
Basically you need to create a class that implements ItemWriter<YourModel> and FlatFileFooterCallback
In the write method, enter how data will be written and in the writeFooter the footer of the file.
Then declare your class as a bean and put it as a writer in your job.
I've found the solution. I can't write a custom itemwriter, but I created a bean out and I have overridden the toString method. In this method I have set the output to the file as needed. Then, I created a PassThroughLineAggregator type itemwriter. This itemwriter calls the toString method of the bean output. And that's all!!
Here's the code:
MOH_Diaria_Bean_Out.java:
package es.lac.absis.batch.app.percai.domain;
import java.util.ArrayList;
import java.util.List;
public class MOH_Diaria_Bean_Out {
List<MOH_Diaria_Bean> listaBeans = new ArrayList<MOH_Diaria_Bean>();
public List<MOH_Diaria_Bean> getListaBeans() {
return listaBeans;
}
public void setListaBeans(List<MOH_Diaria_Bean> listaBeans) {
this.listaBeans = listaBeans;
}
public void add (MOH_Diaria_Bean bean){
listaBeans.add(bean);
}
#Override
public String toString() {
// TODO Auto-generated method stub
String salida="";
for (int j=0; j<listaBeans.size(); j++) {
MOH_Diaria_Bean bean = listaBeans.get(j);
salida = salida + bean.toString();
if (j<(listaBeans.size()-1)) {
salida = salida + "\n";
}
}
return salida;
}
}
ItemWriter:
<bean id="MOH_FusionadoFicheros_Writer" class="es.lac.absis.batch.arch.internal.writer.AbsisFlatFileItemWriter">
<property name="resource">
<bean class="es.lac.absis.batch.arch.internal.util.AbsisFileSystemResource">
<constructor-arg ref="filePCA00020"></constructor-arg>
</bean>
</property>
<property name="encoding" value="ISO8859_1"></property>
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.PassThroughLineAggregator">
</bean>
</property>
</bean>

Spring Data MongoDB: Unit tests with repositories

How is it supposed to build some tests with the repository approach in Spring Data MongoDB? I would like to set the test database for my tests since I don't want to use the production database for this purpose. It should be probably possible but I have no idea. This is my application context:
<?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:context="http://www.springframework.org/schema/context"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xmlns:neo4j="http://www.springframework.org/schema/data/neo4j"
xsi:schemaLocation=
"http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/data/mongo
http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/data/neo4j
http://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd">
<!-- Default bean name is 'mongo' -->
<mongo:mongo host="${mongo.host}" port="${mongo.port}">
<mongo:options connections-per-host="8"
threads-allowed-to-block-for-connection-multiplier="4"
connect-timeout="${mongo.connect-timeout}"
max-wait-time="${mongo.max-wait-time}"
auto-connect-retry="true"
socket-keep-alive="true"
socket-timeout="${mongo.socket-timeout}"
slave-ok="true"
write-number="1"
write-timeout="0"
write-fsync="true"/>
</mongo:mongo>
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg ref="mongo" />
<constructor-arg name="databaseName" value="${mongo.db}" />
</bean>
<context:component-scan base-package="domain.company.group.project.data.repositories"/>
<!-- MongoDB repositories -->
<mongo:repositories base-package="domain.company.group.project.data.repositories.mongodb"/>
<!-- some other stuff -->
</beans>
And let's say I have a simple repository as follows:
public interface LocationRepository extends MongoRepository<Location, String>, LocationRepositoryCustom {
}
where LocationRepositoryImpl is the class implementing all my custom methods for a certain Location (domain object) class. My test class looks like:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"/test-context.xml"})
public class LocationRepositoryTest {
#Autowired
private LocationRepository locationRepository;
/* Some tests... */
}
I have tried to embed a MongoDB instance within my running tests (as explained here) but it does not work: the connection to the test database is established but the mongo template seems not able to be overwritten as all save methods keep inserting data to the "production" database.
I am using Spring 3.2.0 and Spring Data Mongo 1.1.0.RELEASE. I am using Junit for testing.
Any suggestions?
Thank you in advance.
Jaranda,
I faced the same problem last week and coincidentally I heard about Fongo, "an in-memory java implementation of mongo."
So I decide to use it to test my custom repositories and worked perfectly to me. Below is an example of how to configure Spring to use Fongo in JUnit tests. Note that I'm not using xml configuration.
Hope that will be useful!
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class LocationRepositoryTest {
private static final String PLAYER_ID = ObjectId.get().toString();
#Autowired private LocationRepositoryCustom playerRepository;
#Autowired private MongoTemplate mongoTemplate;
/* Some tests... */
#Configuration
static class LocationRepositoryTestConfiguration {
#Bean
public Mongo mongo() {
// Configure a Fongo instance
return new Fongo("mongo-test").getMongo();
}
#Bean
public MongoTemplate mongoTemplate() {
return new MongoTemplate(mongo(), "collection-name");
}
#Bean
public LocationRepositoryCustom playerRepository() {
// This is necessary if MongoTemplate is an argument of custom implementation constructor
return new LocationRepositoryCustomImpl(mongoTemplate());
}
}
}