ternary operator in spring configuration file - java

I have a situation where I need to check the profile maven is running on and then according to that I need to configure task scheduler. There are two profiles one is 'global' and the other one is 'nonglobal', what I did is:
<task:scheduler id="customerPortalTaskScheduler" pool-size="1" />
<task:scheduled-tasks scheduler="customerPortalTaskScheduler">
<task:scheduled ref="SubscriptionService" method="updateNextDistributionDateForAllCurrentUsers" cron="${nhst.ncp.instance} == 'global' ? #{customerportal['globalUpdateDistributionDateServiceTuesday.CronTrigger']} : #{customerportal['updateDistributionDateServiceMondayThursday.CronTrigger']}" />
<task:scheduled ref="SubscriptionService" method="updateNextDistributionDateForAllCurrentUsers" cron="${nhst.ncp.instance} == 'global' ? #{customerportal['globalUpdateDistributionDateServiceWednesday.CronTrigger']} : #{customerportal['updateDistributionDateServiceFriday.CronTrigger']}" />
<task:scheduled ref="SubscriptionService" method="updateNextDistributionDateForAllCurrentUsers" cron="${nhst.ncp.instance} == 'global' ? #{customerportal['globalUpdateDistributionDateServiceThursday.CronTrigger']} : #{customerportal['updateDistributionDateServiceWeekend.CronTrigger']}" />
</task:scheduled-tasks>
${nhst.ncp.instance} is instance of maven profile. It would say whether its global or nonglobal profile. It does work fine, because properties file are being loaded properly.
With the above configuration, I am getting an error which is there in screenshot.
Any ideas how to solve this?

Do not depend on profile activated. Depend on local configuration file:
<context:property-placeholder ignore-resource-not-found="true" location="file:/etc/mumbojumbo/app.config.properties"/>
in /etc/mumbojumbo/app.config.properties
cron.schedule = blabla
You can always provide a sensible default value. So you only have to override it where you want.
<task:scheduled ref="SubscriptionService" method="updateNextDistributionDateForAllCurrentUsers" cron="${cron.schedule:defaultvalue}" />
Something like that?

Related

How to make fixed-rate dynamic in XML based task schedulers?

<task:scheduled-tasks>
<task:scheduled ref="testBean" method="testMethod" fixed-rate="1000"/>
</task:scheduled-tasks>
In the above snippet, I want to pass fixed-rate as variable fetched from the config file. How can I do that?
P.S. I don't want to move to the annotation-based scheduler.
After going through various articles and documentation I found a way to achieve the same. So sharing the same.
I created a configuration bean of a loader class. Loader class is responsbile to fetch the configs (either from file or any configuration management tool) and set in the system properties.
<bean id="configuration" class="com.test.config.loader">
</bean>
Say one the property is like
database:mysql
It can be accessed like
<task:scheduled-tasks>
<task:scheduled ref="testBean" method="testMethod" fixed-rate="#{configuration['database']}"/>
</task:scheduled-tasks>

How to solve exception with inbound-channel-adapter poller?

Сould not find an answer.
Spring version 5.0.6.
My config:
<int:channel id="data"/>
<int:inbound-channel-adapter
id="dataAdapter"
channel="data"
auto-startup="false"
ref="dataGetter"
method="myMessageSource">
<int:poller max-messages-per-poll="10"/>
</int:inbound-channel-adapter>
<beans:bean
class="org.endpoints.DataGetter"
id="dataGetter"/>
Throw exception:
Configuration problem: A <poller> must have one and only one trigger configuration.
If no poller:
No poller has been defined for channel-adapter 'dataAdapter', and no default poller is available within the context.
How to properly setup the poller?
Look at your config:
<int:poller max-messages-per-poll="10"/>
You don't meet the condition to have or fixed-delay, or fixed-rate, or cron, or just trigger reference: https://docs.spring.io/spring-integration/docs/5.0.6.RELEASE/reference/html/messaging-endpoints-chapter.html#endpoint-namespace

can't set a different filter in file:inbound-channel-adapter and scanner?

Below is part of my Spring Integration config :
<bean id="recursiveScanner" class="org.springframework.integration.file.RecursiveLeafOnlyDirectoryScanner" >
<property name="filter" ref="skipTmpFileFilter" />
</bean>
<bean id="skipTmpFileFilter" class="org.springframework.integration.file.filters.RegexPatternFileListFilter">
<constructor-arg value="^[^~].*"/>
</bean>
<file:inbound-channel-adapter directory="${inbound.folder}"
scanner="recursiveScanner"
id="fileChannel"
filter="fileNameFilter">
<integration:poller id="poller" fixed-delay="10000" />
</file:inbound-channel-adapter>
As you can see, I'd like to define 2 different filters :
one to skip temp files, in the recursiveScanner
one more advanced in which I have defined some other patterns, fileNameFilter (details of which are not relevant, so I'm not providing it)
What I see when I launch this in debug mode is that first, skipTmpFileFilter is set in recursiveScanner, but it is overwritten by fileNameFilter a bit after, making skipTmpFileFilter ineffective.
Is it the intended behavior or a bug ? I think it would make sense to be able to configure 2 different filters, one generic (in scanner) and one more specific (in the inbound adapter). Here, I'm kind of forced to use a composite filter.
Thanks
Vincent
If we take a look to the source code of FileReadingMessageSource, we'll see:
public void setFilter(FileListFilter<File> filter) {
Assert.notNull(filter, "'filter' must not be null");
this.scanner.setFilter(filter);
}
And there is no more stuff around the filter for the FileReadingMessageSource. Everything is delegated to the DirectoryScanner.
So, there is no any choice, unless provide only one filter option: or for the DirectoryScanner bean, or <file:inbound-channel-adapter>.
And yes: to have several filters in place you should use CompositeFileListFilter.
However I think we can protect that point of the override case.
Feel free to raise JIRA issue on the matter.

Is there a way to start the file:inbound-channel-adapter through code?

I have a situation where a particular file is to be copied from a location to another. The polling is not required as the action will be deliberately triggered. Also the directory from which the file is to be picked up is decided at run time.
I can have a configuration as follows:
<int-file:inbound-channel-adapter id="filesIn" directory="#outPathBean.getPath()" channel="abc" filter="compositeFilter" >
<int:poller id="poller" fixed-delay="5000" />
</int-file:inbound-channel-adapter>
<int:channel id="abc"/>
<int-file:outbound-channel-adapter channel="abc" id="filesOut"
directory-expression="file:${paths.root}"
delete-source-files="true" filename-generator="fileNameGenerator" />
filenamegenerator and composite filter classes are configured as well.
I am new to spring. Please point me in the right direction!!
You can use a FireOnceTrigger as discussed in this answer and start/stop the adapter as needed.
To get a reference to the adapter (a SourcePollingChannelAdapter), inject (or #Autowire etc.) it as a Lifecycle bean (start()/stop() etc).
Or you can do the whole thing programmatically using a FileReadingMessageSource, and discussed in this answer.
Sample for start/stop Adapter.incase its useful.
SourcePollingChannelAdapter sourcePollingChannelAdapter = (SourcePollingChannelAdapter) context
.getBean("filesIn"); //adapter id in the bean configuration
// Stop
if (sourcePollingChannelAdapter.isRunning()) {
sourcePollingChannelAdapter.stop();
}
// Set Cron Expression if required when start or use any triggers
CronTrigger cronTrigger = new CronTrigger("* * * * * ?");
sourcePollingChannelAdapter.setTrigger(cronTrigger);
// Start
if (!sourcePollingChannelAdapter.isRunning()) {
sourcePollingChannelAdapter.start();
}

Scheduling tasks to run once, using the Spring task namespace

I'm setting up a scheduled tasks scheme in spring, using the task namespace.
I want to schedule most tasks to fire according to a cron expression, and some to fire just once, a fixed delay after startup, and then never again (i.e. what setting repeatCount to 0 on a SimpleTriggerBean would achieve).
Is it possible to achieve this within the task namespace, or do I need to revert to defining beans for my triggers?
If you don't need an initial delay, you can make it run 'just once' on startup as follows:
<task:scheduled-tasks>
<!-- Long.MAX_VALUE ms = 3E8 years; will run on startup
and not run again for 3E8 years -->
<task:scheduled ref="myThing" method="doStuff"
fixed-rate="#{ T(java.lang.Long).MAX_VALUE }" />
</task:scheduled-tasks>
(Of course, if you think your code is going to run for longer than 3E8 years, you may need a different approach...)
If you need an initial delay, you can configure it as follows (I'm testing with Spring 3.1.1) - this doesn't require any additional dependencies and you don't have to write your own trigger, but you do have to configure the PeriodicTrigger provided by Spring:
<bean id="onstart" class="org.springframework.scheduling.support.PeriodicTrigger" >
<!-- Long.MAX_VALUE ms = 3E8 years; will run 5s after startup and
not run again for 3E8 years -->
<constructor-arg name="period" value="#{ T(java.lang.Long).MAX_VALUE }" />
<property name="initialDelay" value="5000" />
</bean>
<task:scheduled-tasks>
<task:scheduled ref="myThing" method="doStuff" trigger="onstart" />
</task:scheduled-tasks>
Spring 3.2 appears to support the "initial-delay" attribute directly, but I haven't tested this; I'd guess this works:
<task:scheduled-tasks>
<task:scheduled ref="myThing" method="doStuff"
fixed-rate="#{ T(java.lang.Long).MAX_VALUE }"
initial-delay="5000"/>
</task:scheduled-tasks>
My working example:
<bean id="whateverTriggerAtStartupTime" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="whateverJob"/>
<property name="repeatCount" value="0"/>
<property name="repeatInterval" value="10"/>
</bean>
If you have a look at the Task namespace XSD, you'll see that there are only three different configuration types: fixed-delay, fixed-rate and cron.
And if you look at the source of ScheduledTasksBeanDefinitionParser, you'll see that no more than one of these values are evaluated. Here is the relevant part:
String cronAttribute = taskElement.getAttribute("cron");
if (StringUtils.hasText(cronAttribute)) {
cronTaskMap.put(runnableBeanRef, cronAttribute);
}
else {
String fixedDelayAttribute = taskElement.getAttribute("fixed-delay");
if (StringUtils.hasText(fixedDelayAttribute)) {
fixedDelayTaskMap.put(runnableBeanRef, fixedDelayAttribute);
}
else {
String fixedRateAttribute = taskElement.getAttribute("fixed-rate");
if (!StringUtils.hasText(fixedRateAttribute)) {
parserContext.getReaderContext().error(
"One of 'cron', 'fixed-delay', or 'fixed-rate' is required",
taskElement);
// Continue with the possible next task element
continue;
}
fixedRateTaskMap.put(runnableBeanRef, fixedRateAttribute);
}
}
So there is no way to combine these attributes. In short: the namespace won't get you there.
This works and is way easier than the other answers.
// Will fire the trigger 1 + repeatCount number of times, start delay is in milliseconds
simple name: 'mySimpleTrigger', startDelay: 5000, repeatCount: 0

Categories