What is the recommended way to communicate between IntergrationFlow and services? - spring

I'm looking into spring integration, more specifically the Java DSL.
The DSL is presented by the IntegrationFlows factory for the IntegrationFlowBuilder. This produces the IntegrationFlow component, which should be registered as a Spring bean (by using the #Bean annotation). The builder pattern is used to express arbitrarily complex structures as a hierarchy of methods that can accept lambdas as arguments.
In my mind then, I define a configuration class with a #Bean.
#Bean
fun ftpInboundIntegrationFlow(ftpSf: DefaultFtpSessionFactory?): IntegrationFlow {
val ftpSpecification = Ftp
.inboundStreamingAdapter(template())
.patternFilter("*.csv")
.remoteDirectory(fromFolder)
return IntegrationFlows
.from(ftpSpecification) { pc: SourcePollingChannelAdapterSpec ->
pc.poller { pm: PollerFactory ->
pm.cron(cronSyncExpression)
}
}
.transform(StreamTransformer())
.channel(doSomeBusinessLogic)
.get()
}
But I am rather confused how to, in my more specific service layer, handle this message.
I could, and it seems to work, create another #Bean in the service class.
#Bean
fun handleDoSomeBusinessLogicFlow(): IntegrationFlow {
return IntegrationFlows.from("doSomeBusinessLogic")
.handle { customers: List<SomeDomainModel>, headers: MessageHeaders ->
//Some repository and service logic
}
.get()
I tried looking at examples (https://github.com/spring-projects/spring-integration-samples/tree/833f55d662fb17edda6bcb000d952a3d00aa1dd8). But almost all of them are just creating all the logic in the same place. It feels strange to define a #Bean in the #Service class.

You are right: you can just have a business method in your service and use it from the .handle() instead of channel. You also can just use a #ServiceActivator on that method if you’d prefer loosely coupled distribution via channels. There is also an IntegrationFlowAdapter to have a service incapsulation, but still gain from flow definition.

Related

Managing multiple routes in Spring Cloud Gateway

I know Spring Cloud Gateway has multiple ways to configure routes:
using a Java-based DSL (eg: using RouteLocatorBuilder) and/or
property based configuration.
The offical Spring Cloud Gateway docs use properties to manage routes.
My questions are:
It is simple to configure routes for 2-3 microservices in a single file, but how do enterprise applications with so many microservices manage routes efficiently?
What is the recommend way to configure routes?
If using Java DSL, is it a good practice to use multiple Beans with the same return type. something like:
#Bean
RouteLocator bookRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("book_route", r -> r.method(HttpMethod.GET)
.and().path("/api/book/**")
.uri("lb://book-service"))
.build();
}
#Bean
RouteLocator chapterRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("chapter_route", r -> r.method(HttpMethod.GET)
.and().path("/api/chapter/**")
.uri("lb://chapter-service"))
.build();
}
This is a decently old post but this is something I recently ran into and wanted to share a solution.
Exposing multiple beans will not work - those two RouteLocator instances will collide with each other causing an error or you can allow spring to override the other bean - either way, its not going to work for you.
Instead, expose a Router that references other classes that expose functions that create your routes. Here is an example in Kotlin
Routes.kt
#Configuration
class Routes(
private val purchaseRoute: PurchaseRoute
) {
#Bean
fun router(routeLocatorBuilder: RouteLocatorBuilder): RouteLocator {
return routeLocatorBuilder.routes()
.route("purchase", purchaseRoute.route())
.build()
}
}
Router.kt interface all sub-routes will implement
interface Router {
fun route(): Function<PredicateSpec, Buildable<Route>>
}
PurchaseRoute.kt
#Configuration
class PurchaseRoute() : Router {
override fun route(): Function<PredicateSpec, Buildable<Route>> =
Function {
it.path("/api/purchase/**")
it.filters {
stripPrefix(1)
dedupeResponseHeader("Access-Control-Allow-Origin", "RETAIN_FIRST")
}
it.uri(routeConfigProperties.uri)
}
}
Then just create as many route classes as you like. The separation into multiple files dramatically cleaned up a gateway service I maintain.

Disable automatic registration of WebFilter

In a spring-boot 2.4 application, I have two SecurityWebFilterChains. For only one of them, I want to add some WebFilters via addFilterBefore().
#Configuration
#EnableWebFluxSecurity
class WebSecurityConfig {
#Bean
fun filter1(service: Service): WebFilter = Filter1(service)
#Bean
fun filter2(component: Component): WebFilter = Filter2(component)
#Bean
#Order(1)
fun apiSecurityConfiguration(
http: ServerHttpSecurity,
filter1: WebFilter,
filter2: WebFilter
): SecurityWebFilterChain = http
.securityMatcher(pathMatchers("/path/**"))
.addFilterBefore(filter1, SecurityWebFiltersOrder.AUTHENTICATION)
.addFilterAt(filter2, SecurityWebFiltersOrder.AUTHENTICATION)
.build()
#Bean
#Order(2)
fun actuatorSecurityConfiguration(
http: ServerHttpSecurity,
reactiveAuthenticationManager: ReactiveAuthenticationManager
): SecurityWebFilterChain = http
.securityMatcher(pathMatchers("/manage/**"))
.authenticationManager(reactiveAuthenticationManager)
.httpBasic { }
.build()
}
However, as those WebFilters are created as beans, they are registered automatically and are applied to all requests, seemingly outside the security chain.
For servlet filters, it is possible to disable this registration with a FilterRegistrationBean (see spring-boot documentation).
Is there a similar way for reactive WebFilters, or do I have to add additional URL filtering into those filters?
To find a solution, we first have to dig in a little deeper into how spring works and its internals.
All beans of type WebFilter are automatically added to the main web handling filter chain.
See spring boot documentation on the topic:
WebFilter beans found in the application context will be automatically used to filter each exchange.
So that happens even if you want them to be applied only to a specific spring-security filter chain.
(IMHO, it is a bit of a flaw of spring-security to re-use the Filter or WebFilter interfaces and not have something security-specific with the same signature.)
In code, the relevant part is in spring-web's WebHttpHandlerBuilder
public static WebHttpHandlerBuilder applicationContext(ApplicationContext context) {
// ...
List<WebFilter> webFilters = context
.getBeanProvider(WebFilter.class)
.orderedStream()
.collect(Collectors.toList());
builder.filters(filters -> filters.addAll(webFilters));
// ...
}
Which in turn is called in a spring-boot's HttpHandlerAutoConfiguration to create the main HttpHandler.
#Bean
public HttpHandler httpHandler(ObjectProvider<WebFluxProperties> propsProvider) {
HttpHandler httpHandler = WebHttpHandlerBuilder.applicationContext(this.applicationContext).build();
// ...
return httpHandler;
}
To prevent those filters to be applied to all exchanges, it might be possible to simply not create them as beans and create them manually, as suggested in a comment above. Then the BeanProvider will not find them and not add them to the HttpHandler. However, you leave IoC-country and lose autoconfiguration for the filters. Not ideal when those filters become more complex or when you have a lot of them.
So instead my solution is to manually configure a HttpHandler for my application, which does not add my security-specific filters to the global filter chain.
To make this work, I first declare a marker interface for my filters.
interface NonGlobalFilter
class MySecurityFilter : WebFilter, NonGlobalFilter {
// ...
}
Then, a configuration class is required where the custom HttpHandler is created. Conveniently, WebHttpHandlerBuilder has a method to manipulate its filter list with a Consumer.
This will prevent spring-boot to use its own HttpHandler from HttpHandlerAutoConfiguration because it is annotated with #ConditionalOnMissingBean(HttpHandler.class).
#Configuration
class WebHttpHandlerConfiguration(
private val applicationContext: ApplicationContext
) {
#Bean
fun httpHandler() = WebHttpHandlerBuilder
.applicationContext(applicationContext)
.filters {
it.removeIf {
webFilter -> webFilter is NonGlobalFilter
}
}
.build()
}
And that's it! As always, spring provides a lot of useful defaults out of the box, but when it gets in your way, there will be a means to adjust it as necessary.

How to expose endpoints REST lazily?

Scenario:
The Spring Boot application should expose its REST endpoints
only when a specific action occurs.
Is there any way in Spring to lazily expose endpoints, or even the whole HTTP subsystem?
In Apache CXF, we can do something like this:
void exposeEndpoints() {
EndpointImpl endpoint = new EndpointImpl(cxfBus, serviceImpl);
endpoint.publish();
}
How to do the same thing in Spring?
You could take a look at #RefreshScope.
I would define my #RestController beans as follows:
#Configuration
#RefreshScope
public ControllerConfig {
#Bean
#ConditionalOnProperty(value = "should.initialize.rest", havingValue = true)
public SomeController someController(){
....
}
#Bean
#ConditionalOnProperty(value = "should.initialize.rest", havingValue = true)
public SomeOtherController someOtherController(){
....
}
}
and if you start your application with property should.initialize.rest with value false in your application.properties:
should.initialize.rest=false
Then your controllers won't be registered/initialized. When the application is running, you could then update your application.properties to:
should.initialize.rest=true
and make a call to /refresh, then your ApplicationContext will reload/refresh, this time with your REST controllers. You can find more about #RefreshScope below:
https://www.baeldung.com/spring-reloading-properties
https://andressanchezblog.wordpress.com/2016/09/15/refresh-scope-in-spring-cloud/
The solution I have provided below is one of the ways and it may or may not be suitable in your case.
It situation seems like more of design concern rather than particular implementation.
I would suggest you to go with little change in the design.
Store the event in DB or Session (as per requirement) as Boolean.
Create Aspect using AspectJ or Spring's own AOP.
Create Before aspect with specific package pointcuts.
In the #Before advice, check for the boolean flag, if the condition for publishing
satisfies than use joinpoint.proceed() else throw some kind of error saying service not available.
Another way is to create custom Annotation with Aspects. You can use that annotation as per requirement and not on the whole service layer.
The benefit of first approach is that you have the control at generic level and the second approach at service level.
I would suggest to create a new child context with your HTTP subsystem. Something like this:
#Service
public class MyBusinessService {
#Autowired
private final ApplicationContext parentContext;
private AnnotationConfigWebApplicationContext webContext;
public void myBusinessMethod() {
this.webContext = new AnnotationConfigWebApplicationContext();
this.webContext.setParent(parentContext);
this.webContext.scan("com.mybusiness.service.webcomponents");
this.webContext.refresh();
this.webContext.start();
}
}
DISCLAIMER: This is proof-of-concept code, I did not try to compile or run this. But hopefully it is enough to illustrate the concept.

Spring Proxy Creation of Classes annotated with #Configuration or #Component

Spring uses either JDK dynamic proxies or CGLIB to create the proxy for a given target object. If a class is annotated with #Configuration, then CGLIB is used.
However, one limitation of Spring AOP is that once the call has finally reached the target object, any method calls that it may make on itself are going to be invoked against the this reference, and not the proxy. This piece of information is important to remember when using #Transactional and in other places as well.
So having that knowledge, in the code below, is Spring injecting the actual instance or the proxy of SimpleBean?
#Configuration
public class Config {
#Bean
public SimpleBean simpleBean() {
return new SimpleBean();
}
#Bean
public SimpleBeanConsumer simpleBeanConsumer() {
return new SimpleBeanConsumer(simpleBean()); //<---
}
}
And what is the behavior if a class is annotation with #Component?
Let me give you another perspective.
Say there is an another bean AnotherBeanConsumer that also needs a simpleBean. Simple Bean has a Singleton scope:
#Configuration
public class Config {
#Bean
public SimpleBean simpleBean() {
return new SimpleBean();
}
#Bean
public SimpleBeanConsumer simpleBeanConsumer() {
return new SimpleBeanConsumer(simpleBean());
}
#Bean
public AnotherBeanConsumer anotherBeanConsumer() {
return new AnotherBeanConsumer(simpleBean());
}
}
Now the question is, how its possible that two calls to simpleBean() made from different methods simpleBeanConsumer and anotherBeanConsumer return the same instance of the simple bean (since its a singleton obviously)?
IMO (and disclaimer, I'm not affiliated with spring or something), This is the main reason of creating proxies that wrap Configurations.
Now indeed Spring AOP has a limitation of calling methods just as you've stated, however who said that spring under-the-hood uses spring AOP? The bytecode instrumentation done on much lower levels doesn't have a limitation like this. After all creating a proxy means: "create a proxy object that will have the same interface but will alter the behavior", right?
For example if you use CGLIB that uses inheritance you could create a proxy out of configuration that looks like this (schematically):
class CGLIB_GENERATED_PROXY extends Config {
private Map<String, Object> singletonBeans;
public SimpleBean simpleBean() {
String name = getNameFromMethodNameMaybePrecached();
if(singletonBeans.get(name) != null) {
return singletonBeans.get(name);
}
else {
SimpleBean bean = super.simpleBean();
singletonBeans.put(name, bean);
return bean;
}
}
....
}
Of course its only a schematic picture, in real life there is an application context that basically provides the access to the map like this, but you get the point.
If its not enough, then there are some even more sophisticated frameworks that spring must make use of in order to load a configuration (like ASM)...
Here is an example:
If you use #ConditionalOnClass(A.class) and the class doesn't really exist in runtime, how spring can load the bytecode of the configuration that uses this configuration and not fail on something like NoClassDefFoundException?
My point is that it goes far beyond the spring AOP, and has its quirks :)
Having said that, nothing that I've describe above requires the real components to be always wrapped in Proxies of any kind. So in the most trivial case, when SimpleBean does not by itself have some annotations that require proxy generation (stuff like #Cached, #Transactional and so forth), Spring won't wrap the object of that type and you'll get a plain SimpleBean object.

Spring Integration Connecting a Gateway to a Service Activator

I've created a Gateway and a polling notificationChannel which the Gateway uses to route messages. I want a service activator to poll from the channel and do its thing. But I can't seem to grasp a few things about Spring Integration.
In this case would we need an IntegrationFlow Bean? Wouldn't calling the gateway method just send the message trough the channel and the service activator can just poll automatically when there is a new message?
ConfigurationClass:
#EnableIntegration
#Configuration
#IntegrationComponentScan
class IntegrationConfiguration {
#Bean
fun notificationChannel(): MessageChannel {
return MessageChannels.queue().get()
}
#Bean
fun integrationFlow(): IntegrationFlow {
TODO()
}
}
Gateway:
#MessagingGateway(defaultRequestChannel = "notificationChannel")
#Component
interface NotificationGateway {
fun sendNotification(bytes: ByteArray)
}
Service:
#Service
class NotificationService {
#ServiceActivator(inputChannel = "notificationChannel")
fun sendNotification(bytes: ByteArray) {
TODO()
}
}
I am new to Spring Integration and having a rough time since I can't find understandable documentation for my level of knowledge especially on Spring Integration DSL.
My main problem might be that I do now understand the use of the IntegrationFlow Bean
For a simple use-case like yours you indeed don't need an IntegrationFlow. The simple #ServiceActivator as you have now is fully enough to process messages from the notificationChannel. Only what you need is a #Poller in that #ServiceActivator configuration since your notificationChannel is a PollableChannel one and it is not subscribable one.
See Reference Manual for more info: https://docs.spring.io/spring-integration/docs/current/reference/html/#configuration-using-poller-annotation
Also pay attention to the paragraph in the beginning of the doc: https://docs.spring.io/spring-integration/docs/current/reference/html/#programming-considerations

Resources