I am using spring integration 4.2.4.RELEASE and I came across a bug. I am trying to create a inbound-channel using a custom directory based on "WatchServiceDirectoryScanner". When I try to inject this scanner I get the error.
"The 'filter' and 'locker' options must be present on the provided external 'scanner': ". No matter what combination of properties I tried it does not work. The reason is because even I supply a locker and filters to my custom scanner the "FileReadingMessageSource" in spring creates their own. Therefore when the it asserts
Assert.state(!(this.scannerExplicitlySet && (this.filter != null || this.locker != null)),
"The 'filter' and 'locker' options must be present on the provided external 'scanner': "
+ this.scanner);
it fails. There is a filter being "FileListFilterFactoryBean.initializeFileListFilter" no matter what combination has been set the code below will create one and the whole things just fails.
// no filters are provided
else if (Boolean.FALSE.equals(this.preventDuplicates)) {
filtersNeeded.add(new AcceptAllFileListFilter<File>());
}
else { // preventDuplicates is either TRUE or NULL
filtersNeeded.add(new AcceptOnceFileListFilter<File>());
}
I have read the post: How to skip the settings of filter and locker
but it does not work for me.
Has anybody find a solution for this?
Here is a sample XML configuration of creating the inbound channel.
<bean id="inboundChannel_3522_filter" class="org.springframework.integration.file.filters.CompositeFileListFilter">
<constructor-arg>
<list>
<bean class="org.springframework.integration.file.filters.RegexPatternFileListFilter">
<constructor-arg value="^Test_(20160216).TXT$" />
</bean>
</list>
</constructor-arg>
</bean>
<bean id="inboundChannel_3522_nio_locker" class="org.springframework.integration.file.locking.NioFileLocker" />
<bean id="inboundChannel_3522_scanner" class="com.test.spring.integraton.file.NonRecursiveWatchServiceDirectoryScanner"
>
<constructor-arg value="e:\data\incoming\Test-V2" />
<property name="filter" ref="inboundChannel_3522_filter"/>
<property name="locker" ref="inboundChannel_3522_nio_locker"/>
</bean>
<file:inbound-channel-adapter id="inboundChannel_3522" auto-create-directory="true" scanner="inboundChannel_3522_scanner"
directory="file:/c:/data/incoming/TEST/" prevent-duplicates="false" ignore-hidden="false" >
<integration:poller fixed-rate="1000"/>
</file:inbound-channel-adapter>
Basically I want to know if there is any way to override the bean FileReadingMessageSource so that I can change the Assert?
I have figure out how to get around this bug. So I create a scanner and I inject the filters and locker to the scanner. I create a custom FileReadingMessageSourceFactoryBean that skips creating filter and locker if a scanner has been injected. The CustomFileReadingMessageSourceFactoryBean with the changes is shown below. The changes occurs at line 172. The rest it is the same.
if (this.scanner != null)
{
this.source.setScanner(this.scanner);
}
else
{
if (this.filter != null)
{
if (this.locker == null)
{
this.source.setFilter(this.filter);
}
else
{
CompositeFileListFilter<File> compositeFileListFilter = new CompositeFileListFilter<File>();
compositeFileListFilter.addFilter(this.filter);
compositeFileListFilter.addFilter(this.locker);
this.source.setFilter(compositeFileListFilter);
this.source.setLocker(locker);
}
}
else if (this.locker != null)
{
CompositeFileListFilter<File> compositeFileListFilter = new CompositeFileListFilter<File>();
compositeFileListFilter.addFilter(new FileListFilterFactoryBean().getObject());
compositeFileListFilter.addFilter(this.locker);
this.source.setFilter(compositeFileListFilter);
this.source.setLocker(locker);
}
}
The new Spring XML looks as below. The difference is that we add the bean inboundChanel_3522.adapter.source at the end. What this does is when the inbound channel looks for a bean of type FileReadingMessageSourceFactoryBean it will append to its id the ".adpater.source" and if one exist it will use that otherwise it will create a default. In this case it will use our CustomFileReadingMessageSourceFactoryBean which has the logic to skip creating additional filters. It is very important that the custom bean comes after inbound-channel-adapter otherwise the bean factory will override it. Finally here is how the XML should look like.
<bean id="inboundChannel_3522_filter" class="org.springframework.integration.file.filters.CompositeFileListFilter">
<constructor-arg>
<list>
<bean class="org.springframework.integration.file.filters.RegexPatternFileListFilter">
<constructor-arg value="^Test_(20160216).TXT$" />
</bean>
</list>
</constructor-arg>
</bean>
<bean id="inboundChannel_3522_nio_locker" class="org.springframework.integration.file.locking.NioFileLocker" />
<bean id="inboundChannel_3522_scanner" class="com.test.spring.integraton.file.NonRecursiveWatchServiceDirectoryScanner">
<constructor-arg value="e:\data\incoming\Test-V2" />
<property name="filter" ref="inboundChannel_3522_filter"/>
<property name="locker" ref="inboundChannel_3522_nio_locker"/>
</bean>
<file:inbound-channel-adapter id="inboundChannel_3522" auto-create-directory="true"
scanner="inboundChannel_3522_scanner"
directory="file:/c:/data/incoming/TEST/" prevent-duplicates="false" ignore-hidden="false" >
<integration:poller fixed-rate="1000"/>
</file:inbound-channel-adapter>
<bean id="inboundChannel_3522.adapter.source" class="com.test.spring.integraton.file.CustomFileReadingMessageSourceFactoryBean">
<property name="scanner" ref="inboundChannel_3522_scanner"/>
<property name="directory" value="e:\data\incoming\Test-V2"/>
<property name="autoCreateDirectory" value="true"/>
</bean>
Related
Below is the config
<jms:outbound-channel-adapter id="someId" channel="inputChannel"
connection-factory="${connection.factory}" destination="queue">
<jms:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onSuccessExpression" value="T(Boolean).TRUE"/>
<property name="successChannelName" value="afterSuccessDeliveryMessageChannel"/>
<property name="onFailureExpression" value="T(Boolean).FALSE"/>
<property name="failureChannelName" value="failureChannel"/>
</bean>
<bean id="retryWithBackoffAdviceSession"
class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice">
<property name="retryTemplate">
<bean class="org.springframework.retry.support.RetryTemplate">
<property name="retryPolicy">
<bean class="org.springframework.retry.policy.SimpleRetryPolicy">
<property name="maxAttempts" value="5"/>
</bean>
</property>
</bean>
</property>
<property name="recoveryCallback">
<bean class="org.springframework.integration.handler.advice.ErrorMessageSendingRecoverer">
<constructor-arg ref="failureChannel"/>
</bean>
</property>
</bean>
</jms:request-handler-advice-chain>
</jms:outbound-channel-adapter>
I am retrying message 5 times and then using recoveryCallBack logging message to some DB.
It works fine retry 5 times and call failureChannel channel but once it calls the failureChannel then again it pass to afterSuccessDeliveryMessageChannel.
I am not sure what I am doing wrong here?
I am expecting once it failed it should go failedChannel NOT again back to afterSuccessDeliveryMessageChannel.
Your problem is like this:
Your Retry advice sends to the failureChannel from the recoveryCallback and exits, successfully.
Then when we look into the ExpressionEvaluatingRequestHandlerAdvice code, we see this logic:
try {
Object result = callback.execute();
if (this.onSuccessExpression != null) {
evaluateSuccessExpression(message);
}
return result;
}
So, since there is no exception calling callback, it goes to the successChannel configured.
To make it fail and go to the failureChannel configured, you should consider to not use that recoveryCallback. Then RequestHandlerRetryAdvice will throw an exception which is going to be caught by the ExpressionEvaluatingRequestHandlerAdvice and sent to that failureChannel. There won't be onSuccessExpression evaluation since we will end up with an exception.
I don't have much experience working with Spring Context and I don't know if this is possible...I'm trying to set into a Spring XML file a variable to define a bean reference (not a property).
Now I have a specidific xml:
keyIntegrator-key1.xml
<import resource="classpath:/events/key-events.xml" />
<context:annotation-config />
<bean id="keyIntegrator" class="com.emulated.KeySimulator" >
<property name="readList">
<list>
<bean class="com.emulated.ListEventGenerator">
<property name="eventList">
<ref bean="key-1-ok"/>
</property>
</bean>
</list>
</property>
</bean>
All the keys were defined in another xml file (key-events.xml).
I have to load in Java runtime the bean "keyIntegrator" with only one key, that is a input parameter in the Java program (I use the param to decide the xml file to load)
My question is if it's possible to define a variable inside the xml file and get the referenced bean using this variable:
Something like this:
keyIntegrator-generic.xml
<import resource="classpath:/events/key-events.xml" />
<context:annotation-config />
<bean id="keyIntegrator" class="com.emulated.KeySimulator" >
<property name="readList">
<list>
<bean class="com.emulated.ListEventGenerator">
<property name="eventList">
<ref bean="key-{inputKeyParam}-ok"/>
</property>
</bean>
</list>
</property>
</bean>
In the Java program I will need to pass the param to get the bean, something like this:
keySimulatorBean = (KeySimulator) context.getBean("keyIntegrator", "1");
There any way possible to make this ?
Thank you very much!
Thank you very much, it worked for me :)
I setted the system property value in the Java code:
System.setProperty("inputKeyParam", "1");
It is possible to do using Spring Expression Language.
For example reference to the bean can be defined using system property
<ref bean="key-#{systemProperties.inputKeyParam}-ok"/>
It will allow to reference different beans depending on provided VM option value, e.g. -DinputKeyParam=1
I'm trying to add a simple redirect into a web application built in Restlets, and it's proving non-trivial. The task is a simple one: I want to actually redirect all missing files from a web application to the same static file.
I'm using org.restlet.routing.Redirector with the following values (I'm using Spring injection):
<bean name="router" class="org.restlet.ext.spring.SpringRouter">
<constructor-arg ref="trackerComponentChildContext" />
<property name="attachments">
<map>
<entry key="/api" value-ref="apiRouter" />
<entry key="/statics" value-ref="staticsDirectory" />
<entry key="/" value-ref="staticsRedirector" />
</map>
</property>
</bean>
<bean id="staticsRedirector" class="ca.uhnresearch.pughlab.tracker.restlets.CustomRedirector">
<constructor-arg ref="trackerComponentChildContext" />
<constructor-arg value="{o}/statics/index.html" />
<constructor-arg value="7" />
</bean>
I can play with the file hierarchy relatively simply, but I just want to send anything that doesn't match either /api or /statics to /statics/index.html within the same application.
Restlet is almost getting it, and it does seem now to pick up the reference to the correct file, it just doesn't quite serve it.
I've put a working copy of the whole thing (including Thierry's suggestions below) at: https://github.com/morungos/restlet-spring-static-files. What I'd like to happen is something like the equivalent sequential attempts below:
curl http://localhost:8080/statics/**/* to hit the corresponding /statics/**/*
curl http://localhost:8080 to hit the main /statics/index.html
curl http://localhost:8080/**/* to hit the main /statics/index.html
I made some tests regarding your issue and I can't figure out how to have your message :-(. Perhaps it's because I haven't the whole code.
In fact, I saw a problem at the level of the SpringRouter itself. I would like to attach the redirector with an attachDefault and not an attach("/", ...) / attach("", ...). The method setDefaultAttachment actually does an attach("", ...).
So I made work something with the following updates:
Create a custom SpringRouter
public class CustomSpringRouter extends SpringRouter {
public void setDefaultAttachment(Object route) {
if (route instanceof Redirector) {
this.attachDefault((Restlet) route);
} else {
super.setDefaultAttachment(route);
}
}
}
Create a custom Redirector. I got the context from the component instead of a child context.
public class CustomRedirector extends Redirector {
public CustomRedirector(Component component, String targetPattern, int mode) {
super(component.getContext(), targetPattern, mode);
}
}
I then use the following Spring 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.xsd">
<bean id="myComponent" class="org.restlet.ext.spring.SpringComponent">
<property name="defaultTarget" ref="router" />
</bean>
<bean name="router" class="test.CustomSpringRouter">
<property name="attachments">
<map>
<entry key="/api" value-ref="apiRouter" />
<entry key="/statics" value-ref="staticsDirectory" />
</map>
</property>
<property name="defaultAttachment" ref="staticsRedirector" />
</bean>
<bean id="staticsRedirector" class="test.CustomRedirector">
<constructor-arg ref="myComponent" />
<constructor-arg value="{o}/statics/index.html" />
<constructor-arg value="7" />
</bean>
<bean name="apiRouter" class="org.restlet.ext.spring.SpringRouter">
(...)
</bean>
(...)
</beans>
Hope it helps you,
Thierry
So I currently am working on a project that uses Spring to inject a schema location for the xsd when calling an xsl, something like:
<bean id="transformer" class="com.mypackage.Transformer">
<property name="xsl" value="sample.xsl" />
<property name="params">
<map>
<entry key="schemaLocation" value-ref="schema" />
</map>
</property>
</bean>
<bean id="schema" class="java.lang.String">
<constructor-arg value="http://www.sample.com/schema/sampleSchema.xsd" />
</bean>
This works fine when you use a url as the schema location, but say for example you want to refer to a schema that is brought in on the classpath as a maven dependency. I've found that using something like 'classpath:sampleSchema.xsd' doesn't work. I would've thought this kind of behaviour was fairly common, is there an accepted workaround to this? Create a custom class that looks up the schema on the classpath and returns its path as a string?
OK so I thought I would share how I fixed this.
Two spring beans:
<bean id="schema" factory-method="getSchemaLocation" factory-bean="schemaFinder" />
<bean id="schemaFinder" class="com.mypackage.SchemaFinder">
<constructor-arg value="NAME_OF_SCHEMA" />
</bean>
And a class:
public class SchemaFinder
{
private String schemaLocation;
public SchemaFinder(String schemaName) throws Exception
{
try
{
URL absoluteSchemaUrl = getClass().getClassLoader().getResource(schemaName);
setSchemaLocation(absoluteSchemaUrl.toString());
}
catch (Exception e)
{
//whatever you want here
}
}
public void setSchemaLocation(String schemaLocation)
{
this.schemaLocation = schemaLocation;
}
public String getSchemaLocation()
{
return this.schemaLocation;
}
}
Quite easy when you think about it...
I have a service which refers to a single source.
<bean id="XYZService" class="com.services.impl.DataService1">
<constructor-arg ref="DataSource1" />
</bean>
<bean id="DataSource1" class="com.source.impl.DataSource1">
<constructor-arg ref="DBDataSource"/>
<constructor-arg value="xyz"/>
</bean>
<bean id="DataSource2" class="com.source.impl.DataSource2">
<constructor-arg ref="MsgDataSource"/>
<constructor-arg value="xyz"/>
</bean>
Now if i want to perform a conditional check and my service should be able listen to particular source based on a input variable something like below.
<bean id="XYZService" class="com.services.impl.DataService1">
<constructor-arg ref=" $VARIABLE == true ? DataSource1 : DataSource2" />
</bean>
I did tried SPEL however no luck. I am beginner in spring. Any help will be appreciated.
Thanks.
There are many solutions. Here are two: You can use profiles for this. Define two profiles, define the DataSource beans with the same name but different profiles. (docs)
Alternatively, you can use a single bean and a static factory method (docs).
<bean id="DataSource" class="com.source.impl.DataSourceFactory"
factory-method="createInstance"/>
Inside of DataSourceFactory.createInstance(), you can check the flag and then create the correct data source in plain Java.
The latter is a bit easier to understand, IMO. Using profiles allows you to keep everything in XML (but you should really consider switching to the Java Configuration). The drawback with profiles is that you must not forget to activate at least one of the bean won't be defined.
A third option is to use three XML files and then modify the list of XML files that should be parsed when you pass it to the ApplicationContext. But that only works if you have control over this part of the code.
Assuming you are using Spring 3.1 or later, Spring Profiles may be the best solution.
Using the example of Production and Dev/QA environments, common bean declarations go in a shared file
<beans>
<bean id="XYZService" class="com.services.impl.DataService1">
<constructor-arg ref="DataSource" />
</bean>
</beans>
A separate configuration contains production references
<beans profiles="prod">
<bean id="DataSource" class="com.source.impl.DataSource1">
<constructor-arg ref="DBDataSource"/>
<constructor-arg value="xyz"/>
</bean>
</beans>
Another contains dev references
<beans profile="dev">
<bean id="DataSource" class="com.source.impl.DataSource2">
<constructor-arg ref="MsgDataSource"/>
<constructor-arg value="xyz"/>
</bean>
</beans>
To activate the given profile add -Dspring.profiles.active=prod to your JVM arguments
You can find more info here
Another approach uses factory methods.
<bean id="DataSource" class="com.source.impl.DataSourceFactory" factory-method="getInstance">
<constructor-arg value="#{VARIABLE}" />
</bean>
The above fragment assumes that you want your factory method to explcitly invoke the constructor of each of your services. If you dead set on using Spring to create the instances you can pass each datasource implementation as constructor arguments and use the constructor method as a simple dispatcher.
You need something like this:
<constructor-arg
ref="#{systemProperties.variable == 'true' ? 'DataSource1' : 'DataSource2'}" />
where "variable" is set like -Dvariable=true.