Spring Batch Late Binding Within SQL Using Java Config - spring

We are converting xml-based spring batch configuration to java config.
In xml form of JdbcCursorItemReader we were using late binding:
SELECT * FROM MY_TABLE_#{jobParameters[param1]}
How can this be implemented using Java config syntax?

You can achieve this as follows:
#Bean
#StepScope
public JdbcCursorItemReader jdbcCursorItemReader(#Value("#{jobParameters['param1']}") String param1) {
return new JdbcCursorItemReaderBuilder<>()
.sql("SELECT * FROM MY_TABLE_" + param1)
// set other properties
.build();
}
The reference documentation contains a toggle on each page that allows you to see examples in either Java and Xml configuration. This can be helpful in your migration. See example here: https://docs.spring.io/spring-batch/4.0.x/reference/html/readersAndWriters.html#readersAndWriters

Related

Change value at Runtime in Spring Boot

I have the below code .Can I change the ThreadPoolExecutor thread number size at run time ?
I am using spring boot.
#Configuration
public class ExecutorConfig
{
#Value(numberOfThreads)
private String numberOfThreads ; // numberOfThreads is configured app.properties file
#Bean
public ThreadPoolExecutor executorConfig()
{
ThreadPoolExecutor e = Executors.newFixedThreadPool(numberOfThreads);
return e;
}
}
One option is to add a set method for the property numberOfThread and then provide a way to update it, like a new endpoint. But if your app restarts it will still get the previous value from application.properties.
Other option is to use Spring Cloud Config, but this may or may not be overkill for your case.
Also, this answer goes a bit deeper with some code examples to force a reload.

How do you supply a ClockProvider to Spring v5 (Spring Boot v2)?

What is the proper way to supply your own ClockProvider to the ValidatorFactory configuration in Spring v5 (Spring Boot v2) so that it's used everywhere the Bean Validations Validator is injected?
Use Case: You want to provide a buffer on what it considers "Present", such as described in this blog post to account for a reasonable amount of clock drift.
The simplest solution, that also keeps all Spring defaults untouched, is to override method postProcessConfiguration():
#Configuration
class TimeConfiguration {
#Bean
static Clock clock() {
return Clock.fixed(
Instant.ofEpochSecond(1700000000), ZoneOffset.UTC
);
}
#Bean
static LocalValidatorFactoryBean defaultValidator(Clock clock) {
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean() {
#Override
protected void postProcessConfiguration(
javax.validation.Configuration<?> configuration) {
configuration.clockProvider(() -> clock);
}
};
MessageInterpolatorFactory interpolatorFactory =
new MessageInterpolatorFactory();
factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
return factoryBean;
}
}
Spring 5 is only runtime compatible with Bean Validation 2.0, which introduced ClockProvider you'd like to use. See next code from Spring sources. I think there are two ways you can go from this. You can try to use xml configuration for validator and specify clock provider there. It'll look something like this in your validation.xml:
<validation-config
xmlns="http://xmlns.jcp.org/xml/ns/validation/configuration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/validation/configuration
http://xmlns.jcp.org/xml/ns/validation/configuration/validation-configuration-2.0.xsd"
version="2.0">
// any other configurations...
<clock-provider>com.acme.ClockProvider</clock-provider>
</validation-config>
Another option, if you don't like xml, would be to try define and use your own LocalValidatorFactoryBean.
Also note that maybe for your usecase it'll be useful to use a relatively new feature introduced in Hibernate Validator - temporal validation tolerance, which allow to specify a tolerance for temporal constraints. For more details about it see the documentation. This tolerance can also be set in xml as well as programatically.
Check this blogpost
#Bean
#ConditionalOnMissingBean(ClockProvider.class) // to allow tests to overwrite it
public ClockProvider getClockProvider() {
return () -> Clock.systemDefaultZone();
}

How do a I setup mongodb messagestore in aggregator using annotation

I am trying to add an aggregator to my code.
Couple of problems I am facing.
1. How do I setup a messagestore using annotations only.
2. Is there any design of aggregator works ? basically some picture explaining the same.
#MessageEndpoint
public class Aggregator {
#Aggregator(inputChannel = "abcCH",outputChannel = "reply",sendPartialResultsOnExpiry = "true")
public APayload aggregatingMethod(List<APayload> items) {
return items.get(0);
}
#ReleaseStrategy
public boolean canRelease(List<Message<?>> messages){
return messages.size()>2;
}
#CorrelationStrategy
public String correlateBy(Message<AbcPayload> message) {
return (String) message.getHeaders().get(RECEIVED_MESSAGE_KEY);
}
}
In the Reference Manual we have a note:
Annotation configuration (#Aggregator and others) for the Aggregator component covers only simple use cases, where most default options are sufficient. If you need more control over those options using Annotation configuration, consider using a #Bean definition for the AggregatingMessageHandler and mark its #Bean method with #ServiceActivator:
And a bit below:
Starting with the version 4.2 the AggregatorFactoryBean is available, to simplify Java configuration for the AggregatingMessageHandler.
So, actually you should configure AggregatorFactoryBean as a #Bean and with the #ServiceActivator(inputChannel = "abcCH",outputChannel = "reply").
Also consider to use Spring Integration Java DSL to simplify your life with the Java Configuration.

What configuration enables the evaluation of #Value annotations?

I'm tying to do a very minimal programmatic/annotation based configuration of Spring, to do some command line stuff and I want to be able to inject value of some bean values from System properties.
I'm using the #Value like this:
#Value("${MigrateDb.task:default}")
private String task;
It's sort of working, but it's not evaluating the value definition, I'm just getting "${MigrateDb.task:default}" in the actual field, instead of Spring evaluating it and giving me the value of the Migrate.db.task system property (or default).
What do I need to add to my Configuration class to enable this behaviour?
try using it this way:
#Value("${MigrateDb.task:default}")
private String task;
XML Config:
<context:property-placeholder
location="your.filelocation.properties" />`
Java Config :
#Bean
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
PropertyPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertyPlaceholderConfigurer();
propertyPlaceholderConfigurer.setLocation(new ClassPathResource("file.properties"));
return propertyPlaceholderConfigurer;
}
From ShadowRay's answer, the minimum code to enable the requested behaviour is:
#Bean
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer(){
return new PropertyPlaceholderConfigurer();
}
Method should be static as per: https://stackoverflow.com/a/14943106/924597

How do I add things to the /info endpoint in spring boot programmatically?

How do I add things to the /info endpoint in Spring Boot programmatically? The documentation states that this is possible for the /health endpoint through the use of HealthIndicator interface. Is there something for the /info endpoint as well?
I would like to add operating system name and version and other runtime info there.
In Spring Boot 1.4, you are able to declare InfoContributer beans to make this a whole lot easier:
#Component
public class ExampleInfoContributor implements InfoContributor {
#Override
public void contribute(Info.Builder builder) {
builder.withDetail("example",
Collections.singletonMap("key", "value"));
}
}
See http://docs.spring.io/spring-boot/docs/1.4.0.RELEASE/reference/htmlsingle/#production-ready-application-info-custom for more info.
The accepted answer actually clobbers the InfoEndpoint and does not add to it.
One way I found to add to the info is, in a #Configuration class, add an #Autowired method that adds extra properties following the info.* conventions to the environment. Then InfoEndpoint will pick them up when its invoked.
You can do something like the following:
#Autowired
public void setInfoProperties(ConfigurableEnvironment env) {
/* These properties will show up in the Spring Boot Actuator /info endpoint */
Properties props = new Properties();
props.put("info.timeZone", ZoneId.systemDefault().toString());
env.getPropertySources().addFirst(new PropertiesPropertySource("extra-info-props", props));
}
One way to do what you want (in the event that you have totally custom properties you need to display) is to declare a bean of type InfoEndpoint which will override the default.
#Bean
public InfoEndpoint infoEndpoint() {
final LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
map.put("test", "value"); //put whatever other values you need here
return new InfoEndpoint(map);
}
As you can see from the code above, the map can contain whatever info you need.
In the event that the data you want to show can be retrieved by the environment and is not custom, you do not need to override the InfoEndpoint bean, but you can simply add properties to the properties file with a prefix of info. One example where the OS name is evaluated is the following:
info.os = ${os.name}
In the code above, Spring Boot will evaluate the right-hand expression before returning the property in the /info endpoint.
A final note is that there is a ton of environment information that is already available in the /env endpoint
Update
As pointed out by #shabinjo, in newer Spring Boot versions there is no InfoEndpoint constructor that accepts a map.
You can however use the following snippet:
#Bean
public InfoEndpoint infoEndpoint() {
final Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("test", "value"); //put whatever other values you need here
return new InfoEndpoint(new MapInfoContributor(map));
}
The code above will completely override the default info that would end-up in /info.
To overcome this issue one could add the following bean
#Bean
public MapInfoContributor mapInfoContributor() {
return new MapInfoContributor(new HashMap<String, Object>() {{
put("test", "value");
}});
}
It should be possible to add a custom PropertySource inside an ApplicationListener to add custom info.* properties to the environment (see this answer for an example: How to Override Spring-boot application.properties programmatically)

Resources