Spring Integration - FtpInboundFileSynchronizer Comparator configuration with DSL - spring

Spring Integration's FtpInboundFileSynchronizer allows for the setting of a Comparator<FTPFile> to allow ordering of the downloads. The documentation says:
Starting with version 5.1, the synchronizer can be provided with a Comparator. This is useful when restricting the number of files fetched with maxFetchSize.
This is fine for #Bean configuration:
#Bean
public FtpInboundFileSynchronizer ftpInboundFileSynchronizer(...)
FtpInboundFileSynchronizer synchronizer = new FtpInboundFileSynchronizer(sessionFactory);
...
synchronizer.setComparator(comparator);
return synchronizer;
}
But if I want to programatically assemble flows, the Java DSL is encouraged.
StandardIntegrationFlow flow = IntegrationFlows
.from(Ftp.inboundAdapter(ftpFileSessionFactory, comparator)
.maxFetchSize(1)
...
The comparator in the Ftp.inboundAdapter(...) factory method is only for the comparison of files locally, after they have been downloaded. There are configuration settings that get passed to the synchronizer here (like remote directory, timestamp, etc.). But there is no setting for the synchronizer equivalent to setting it above.
Solution attempt:
The alternative is to create the synchronizer as non-bean, create the FtpInboundFileSynchronizingMessageSource in a similar way, and use IntegrationFlows.from(source) to assemble the synchronizer results in a runtime exception when the flow is registered with the flow context:
Creating EvaluationContext with no beanFactory
java.lang.RuntimeException: No beanFactory
at org.springframework.integration.expression.ExpressionUtils.createStandardEvaluationContext(ExpressionUtils.java:90) ~[spring-integration-core-5.3.2.RELEASE.jar:5.3.2.RELEASE]
at org.springframework.integration.file.remote.synchronizer.AbstractInboundFileSynchronizer.afterPropertiesSet(AbstractInboundFileSynchronizer.java:299) ~[spring-integration-file-5.3.2.RELEASE.jar:5.3.2.RELEASE]
That makes sense; the FtpInboundFileSynchronizer is not supposed to be constructed outside of a context. (Though this does appear to work.) But how, in that case, can I dynamically assemble ftp integration flows with a synchronizer configured with a Comparator<FTPFile>?

Looks like we have missed to expose that remoteComparator option in DSL.
Feel free to raise a GH issue or even contribute a fix: https://github.com/spring-projects/spring-integration/issues
As a workaround for dynamic flows, I really would suggest to go a separate FtpInboundFileSynchronizer and FtpInboundFileSynchronizingMessageSource and then use the mentioned IntegrationFlows.from(source). What you probably miss in your configuration is this API:
/**
* Add an object which will be registered as an {#link IntegrationFlow} dependant bean in the
* application context. Usually it is some support component, which needs an application context.
* For example dynamically created connection factories or header mappers for AMQP, JMS, TCP etc.
* #param bean an additional arbitrary bean to register into the application context.
* #return the current builder instance
*/
IntegrationFlowRegistrationBuilder addBean(Object bean);
I mean that FtpInboundFileSynchronizingMessageSource is OK to pass to the from() as a is, but synchronizer has to be added as an extra bean for registration.
Another more fancy way is to consider to use a new feature called DSL extensions: https://docs.spring.io/spring-integration/docs/5.3.2.RELEASE/reference/html/dsl.html#java-dsl-extensions
So, you can extend that FtpInboundChannelAdapterSpec to provide a missed option to configure for an internal synchronizer.

Related

Mule connector config needs dynamic attributes

I have develop a new Connector. This connector requires to be configured with two parameters, lets say:
default_trip_timeout_milis
default_trip_threshold
Challenge is, I want read ${myValue_a} and ${myValue_a} from an API, using an HTTP call, not from a file or inline values.
Since this is a connector, I need to make this API call somewhere before connectors are initialized.
FlowVars aren't an option, since they are initialized with the Flows, and this is happening before in the Mule app life Cycle.
My idea is to create an Spring Bean implementing Initialisable, so it will be called before Connectors are init, and here, using any java based libs (Spring RestTemplate?) , call API, get values, and store them somewhere (context? objectStore?) , so the connector can access them.
Make sense? Any other ideas?
Thanks!
mmm you could make a class that will create the properties in the startup and in this class obtain the API properties via http request. Example below:
public class PropertyInit implements InitializingBean,FactoryBean {
private Properties props = new Properties();
#Override
public Object getObject() throws Exception {
return props;
}
#Override
public Class getObjectType() {
return Properties.class;
}
}
Now you should be able to load this property class with:
<context:property-placeholder properties-ref="propertyInit"/>
Hope you like this idea. I used this approach in a previous project.
I want to give you first a strong warning on doing this. If you go down this path then you risk breaking your application in very strange ways because if any other components depend on this component you are having dynamic components on startup, you will break them, and you should think if there are other ways to achieve this behaviour instead of using properties.
That said the way to do this would be to use a proxy pattern, which is a proxy for the component you recreate whenever its properties are changed. So you will need to create a class which extends Circuit Breaker, which encapsulates and instance of Circuit Breaker which is recreated whenever its properties change. These properties must not be used outside of the proxy class as other components may read these properties at startup and then not refresh, you must keep this in mind that anything which might directly or indirectly access these properties cannot do so in their initialisation phase or your application will break.
It's worth taking a look at SpringCloudConfig which allows for you to have a properties server and then all your applications can hot-reload those properties at runtime when they change. Not sure if you can take that path in Mule if SpringCloud is supported yet but it's a nice thing to know exists.

Override a Service in Grails using Spring Bean declaration

I am creating a new plugin containing CustomService which is intended to replace an existing service from an existing plugin. Following the pattern found in custom security implementations and shown here, I've added the configuration to the resources.groovy, oldService(path.to.new.CustomService). I've also tried adding all injected classes into the closure for this service.
(Actual service names are RegistrationPersonRegistrationCompositeService and NewRegistrationPersonRegistrationCompositeService in code block)
I dont want the original application code to have any reference to the new plugin. However, BuildConfig at the application level will require plugin.location entry. My resource.groovy mods are in the new plugin. I have not had success in this endeavor. Am I modifying the wrong resources.groovy? If this change is required in the original application code, I've lost the ability to leave the original code unaltered. I'm not extending the original Service nor using override annotation. My intent is to replace the service (Spring bean) on start-up. The new plugin has a dependency on the old plugin in an attempt to manage order of operations in loading these classes.
Does it matter that the old service is previously injected in a controller? this would require me to override the controller in the new plugin in the same fashion and inject the correct service for desired behavior?
I've found documentation showing that within a plugin, the resources.groovy will be ignored. Also, building the resources.groovy into a war is problematic. I have not found a solution. I'm getting no error that I can share, just that the desired behavior is missing; the original service is handling the requests.
//was resource.groovy - now renamed to serviceOverRide.groovy - still located in \grails-app\conf\spring of plugin
//tried this with and without the BeanBuilder. Theory: I'm missing the autowire somehow
import org.springframework.context.ApplicationContext
import grails.spring.BeanBuilder
def bb = new BeanBuilder()
bb.beans {
registrationPersonRegistrationCompositeService(path.to.services.registration.NewRegistrationPersonRegistrationCompositeService) { bean ->
bean.autowire = true
registrationRestrictionCompositeService = ref("registrationRestrictionCompositeService")
registrationPersonTermVerificationService = ref("registrationPersonTermVerificationService")
}
classRegistrationController(path.to.services.registration.ClassRegistrationController) { bean ->
bean.autowire = true
selfServiceLookupService = ref("selfServiceLookupService")
registrationPersonRegistrationCompositeService = ref("registrationPersonRegistrationCompositeService")
}
}
ApplicationContext appContext = bb.createApplicationContext()
Additional information: Added the following lines to the PluginGrailsPlugin.groovy. The original service is still handling these requests
def dependsOn = ['appPersonRegistration': '1.0.20 > *']
List loadAfter = ['appPersonRegistration']
def doWithSpring = {
registrationPersonCourseRegistrationCompositeService(path.to.new.registration.TccRegistrationPersonCourseRegistrationCompositeService)
}
def doWithApplicationContext = { applicationContext ->
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL)
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory()
beanFactory.registerBeanDefinition("registrationPersonCourseRegistrationCompositeService", BeanDefinitionBuilder.rootBeanDefinition(TccRegistrationPersonCourseRegistrationCompositeService.class.getName()).getBeanDefinition())
}
I highly recommend you read the section of the documentation on Plugins. The reason why I recommend this is because plugins:
Do not include, or make use of resources.groovy
Provide a means through doWithSpring to effect the spring application
Following the information in the documentation you should have no issue overriding the service in the application context.
You must implement your changes to the application context using doWithSpring this is the key to solving your issues.
In this implementation, I had a utility method in a service for which I was attempting to provide an override. Problem is, the Aspect works as a proxy and must override a method that is called directly from another class. In my classRegistrationController, I was calling service processRegistration() which in turn called applyRules(). Example-only method names used. Since the service was calling its own utility, there was no opportunity for the proxy/wrapper to circumvent the call to applyRules(). Once this was discovered, I refactored the code in this fashion: Controller calls processRegistration as it always had. After returning, another call is made to the service, processLocalRules(). The new method is an empty placeholder intended to be overridden by the client's custom logic. The plugin with Aspect works now using resources.groovy. I prefer the doWithSpring as Joshua explained for this reason: my intent to get the plugin to work without modification to the original app-config; otherwise resource.groovy is a valid approach. Upvoting Joshua's answer as it does satisfy the requirement and is cleaner. Thanks!

ManagedServiceFactory using Blueprint

The registration of a ManagedServiceFactory follows can be done as :-
private ServiceRegistration factoryService;
public void start(BundleContext context) {
Dictionary props = new Hashtable();
props.put("service.pid", "test.smssenderfactory");
factoryService = context.registerService(ManagedServiceFactory.class.getName(),
new SmsSenderFactory(), props);
}
How can one do this in a blueprint(Sample example would be highly useful) ?
Apache aries has support for Managed services. You can find examples in the integration tests of Apache Aries blueprint at https://svn.apache.org/repos/asf/aries/trunk/blueprint/blueprint-itests/src/test/resources/ManagedServiceFactoryTest.xml. In this case can change the code of your SMSSenderFactory not to implement ManagedServiceFactory directly as blueprint does it for you.
You can also embed your code (that you gave as a sample in your question) into the init-method of your bean if that was your question. In that case the ManagedServiceFactory should be unregistered in the destroy method of the bean.
Third option is to leave blueprint out of the game. You already have a ManagedServiceFactory implementation, why don't you simply register it in the Activator of the Bundle?
Fourth option is to use Declarative Services with one of the annotation set. In that case Metatype information will be also generated and you can configure your SMSSender via the webconsole.
I suggest you to use the last option.

How to configure StepExecutionListener with Spring Integration DSL

I am trying to configure a Spring Batch listener to send a message to a Spring Integration Gateway for StepExecution events.
The following link explains how to configure this with XML
http://docs.spring.io/spring-batch/trunk/reference/html/springBatchIntegration.html#providing-feedback-with-informational-messages
How can this be setup using Spring Integration DSL? I've found no way to configure a gateway with a service interface using DSL.
At the moment I worked around this by implementing an actual StepExecutionListener, and have this then calling an interface which is annotated with #MessagingGateway (calling the corresponding #Gateway method) in order to get a message to a channel. And I then setup an Integration DSL flow for this channel.
Is there a simpler way using DSL, avoiding that workaround? Is there some way to connect a Batch listener direct to a gateway, like one can using XML config?
Cheers,
Menno
First of all SI DSL is just an extension of existing SI Java and Annotation configuration, so it can be used together with any other Java config. Of course an XML #Import is also posible.
There is no gateway configuration in the DSL, because its methods can't be wired with linear IntegrationFlow. There is need to provide downstream flows for each method.
So, #MessagingGateway is a right way to go ahead:
#MessagingGateway(name = "notificationExecutionsListener", defaultRequestChannel = "stepExecutionsChannel")
public interface MyStepExecutionListener extends StepExecutionListener {}
From other side #MessagingGateway parsing as well as <gateway> tag parsing ends up with GatewayProxyFactoryBean definition. So, you just can declare that bean, if you don't want to introduce a new class:
#Bean
public GatewayProxyFactoryBean notificationExecutionsListener(MessageChannel stepExecutionsChannel) {
GatewayProxyFactoryBean gateway = new GatewayProxyFactoryBean(StepExecutionListener.class);
gateway.setDefaultRequestChannel(stepExecutionsChannel);
return gateway;
}
After the latest Milestone 3 I have an idea to introduce nested flows, when we may be able to introduce Gateway support for flows. Something like this:
#Bean
public IntegrationFlow gatewayFlow() {
return IntegrationFlows
.from(MyGateway.class, g ->
g.method("save", f -> f.transform(...)
.filter(...))
.method("delete", f -> f.handle(...)))
.handle(...)
.get();
}
However I'm not sure that it will simplify the life, as far as any nested Lambda just adds more noise and might break loosely coupling principle.

Springframework application context resource resolver

Does anyone know how to manipulate a spring application context to use a specified resource resolver. I have written an s3 resource to pull content from a security context from amazon s3, and a resource resolver to create these "resources" from s3://... type urls, and the local application context uses the right security credentials on load from the configured amazons3client. I've written an s3 ResourceLoader that preconfigure the AmazonS3 client for a newly constructed s3 resource.
It would be nice to be able to specify these resources in the context configuration as simply "s3://..." and rely on this resource resolver to create the right resource type, however, so far this requires overriding the spring ApplicationContext's getResource method inherited from DefaultResourceLoader to use my own resourceResolver implementation.
Another tack is to configure a variable resolver for resources matching the "s3://..." scheme to resolve resource types with that resource resolver.
I am hoping their is a spring guru out their that knows a better way to manipulate the infrastructure of the spring application context (ClasspathXMLAC for arguments sake) to make my S3 security needs very easy to deal with.
Other suggestions are welcome.
Use an implementation of Spring's GenericApplicationContext, which provides you with a #setResourceLoader method.
You mentioned ClassPathXmlApplicationContext, which is not a GenericApplicationContext, meaning that you're forced to take the uglier overriding route.
GenericXmlApplicationContext is a generally preferred alternative to CPXAC, and as the name suggests, is a GenericApplicationContext.
So you should be able to do the following:
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.setResourceLoader(new S3ResourceLoader());
ctx.load("s3:///some.bucket.name/path/to/my/spring.xml");
ctx.refresh();
...
Obviously, the S3ResourceLoader will need to be parameterized with keys, etc. Note that the S3ResourceLoader should probably extend DefaultResourceLoader in order to pick up all the other functionality available there, e.g. processing "classpath:" and other resource prefixes.
Order of invocation in the above example matters, i.e. the resource loader needs to be provided before #load is called with an s3: resource prefix for obvious reasons.

Resources