#ReleaseStrategy - Spring Integration - Custom implementation - java

Please let me know if there is way to define #ReleaseStrategy with MessageGroup and associate it with #Aggregator.
I have POJO defined as below but not sure how would I associate a #Aggregator to it
public class FooReleaseStrategy {
#ReleaseStrategy
public boolean canRelease(MessageGroup group) {
return group.isComplete();
}
}
I have #Aggregator and #CorrelationStratgy defined part of configuration.
#Aggregator(inputChannel="sftpChannel" outputChannel="aggregateChannel")
public List<Message<?>> aggregateFiles(List<Message<?>> messages) {
return messages;
}
#CorrelationStrategy based on filename.
Would be very helpful if someone can shed some light on #ReleaseStrategy association with example if possible.
Based on the comments, I am planning on the create a aggregator factory bean to see if works for my use-case
#Bean
#ServiceActivator(inputChannel = "sftpChannel")
public FactoryBean<MessageHandler> aggregatorFactoryBean( ) {
AggregatorFactoryBean aggregatorBean = new AggregatorFactoryBean();
aggregatorBean.setProcessorBean(new CustomAggregator());
aggregatorBean.setMethodName("aggregate");
aggregatorBean.setMessageStore(new SimpleMessageStore());
aggregatorBean.setReleaseStrategy(messageGroup -> {
return messageGroup.isComplete();
});
aggregatorBean.setOutputChannel(aggregatorFileChannel());
aggregatorBean.setExpireGroupsUponTimeout(true);
aggregatorBean.setGroupTimeoutExpression(new ValueExpression<>(1000L));
aggregatorBean.setSendPartialResultOnExpiry(false);
aggregatorBean.setExpireGroupsUponCompletion(true);
return aggregatorBean;
}

If you want to use an #Aggregator, #ReleaseStrategy and #CorrelationStrategy, consider to configure an AggregatorFactoryBean as a #Bean and apply a #SerivceActivator annotation on it for those inputChannel and outputChannel.
See docs for more info: https://docs.spring.io/spring-integration/docs/5.4.0-M2/reference/html/message-routing.html#aggregator-annotations

When using that style of configuration, #Aggregator, #CorrelationStrategy and #ReleasStrategy are usually in the same bean.
You can, however, define a ReleaseStrategyFactoryBean bean that will provide an implementation of ReleaseStrategy based on your POJO method.
setTarget(myRSBean);
It will find the annotation.

Related

Is it possible to create a custom condition equivalate of #ConditionalOnMissingBean with Spring (no spring-boot)?

I am trying to implement a Spring condition that will load my bean only if there is no other beans for a certain class.
The desired behavior is similar to "#ConditionalOnMissingBean" but without using spring-boot.
I am using Spring versions 5.3.13.
Is that possible? thanks.
I found a solution by digging around spring-boot source code.
ConditionalOnBean will only work on beans defined inside a configuration (bean methods).
This is also recommended by spring-boot java doc for #ConditionalOnBean
The condition can only match the bean definitions that have been processed by the
application context so far and, as such, it is strongly recommended to use this
condition on auto-configuration classes only. If a candidate bean may be created by
another auto-configuration, make sure that the one using this condition runs after.
Here is the basics of the solution I came up with, this may be improved but the basics operate well.
The condition:
#Slf4j
class MissingBeanCondition implements ConfigurationCondition {
#Override
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.REGISTER_BEAN;
}
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
var targetBeanType = metadata.getAnnotations()
.get(ConditionalOnMissingBean.class)
.getValue("value", Class.class)
// TODO throw a more informative error
.orElseThrow(() -> new RuntimeException("Failed to evaluate MissingBeanCondition"));
try {
context.getBeanFactory().getBean(targetBeanType);
} catch (NoSuchBeanDefinitionException e) {
return true;
}
return false;
}
}
The annotation:
#Target({ ElementType.METHOD })
#Retention(RetentionPolicy.RUNTIME)
#Conditional(MissingBeanCondition.class)
public #interface ConditionalOnMissingBean {
Class<?> value();
}
Usage example:
#Bean
#Singleton
#ConditionalOnMissingBean(Provider.class)
public Provider myClass() {
return new DefaultProvider();
}

Spring boot: push message to specific topic for each request

I am using pub sub integration with spring boot, for which my configuration class look like this:
#Configuration
public class PubSubConfiguration {
#Value("${spring.pubsub.topic.name}")
private String topicName;
#Bean
#ServiceActivator(inputChannel = "MyOutputChannel")
public PubSubMessageHandler messageSender(PubSubTemplate pubsubTemplate) {
return new PubSubMessageHandler(pubsubTemplate, topicName);
}
#MessagingGateway(defaultRequestChannel = "MyOutputChannel")
public interface PubsubOutboundGateway {
void sendToPubsub(String attribute);
}
}
So now, I was calling only sendToPubSub method which add payload into topic from my app, like this:
#Autowired
private PubSubConfiguration.PubsubOutboundGateway outboundGateway;
// used line in my code wherever is needed.
outboundGateway.sendToPubsub(jsonInString);
The above code is just meant for one topic which i loaded from application property file.
But now I wanted to make my topic name is dynamically added into messageSender, how to do that.
To override the default topic you can use the GcpPubSubHeaders.TOPIC header.
final Message<?> message = MessageBuilder
.withPayload(msg.getPayload())
.setHeader(GcpPubSubHeaders.TOPIC, "newTopic").build();
and modify your sendToPubsub(Message<byte[]> message) to use message as input.
Refer for more information
Consider creating a BeanFactory to generate a PubSubMessageHandler Bean given a topic name. PubSubMessageHandler also has a setTopic() method, which may be of use.

Can we have a factory class as spring bean and have a factory method returning multiple spring beans based on the condition?

I want to return multiple spring beans based on the condition in the factory class.
Is this a good practice?
Any better ways to write the following piece of code?.
Any other design patterns suitable here?
Below is the code snippet:
package com.test;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
#Component
public class InstanceFactory {
#Resource(name = "instance1")
private Instance instance1;
#Resource(name = "instance2")
private Instance instance2;
public Instance getService(Condition condition) {
if (condition.one() && condition.two()) {
return instance2;
} else {
return instance1;
}
}
}
It depends on what you want to achieve. Factory Pattern is meant to create objects but what you are returning are objects already create somewhere else (Spring in this case). If you want to create beans that will be managed by Spring there are several ways:
#Conditional(YourConditionImplementation.class): This annotation added on a method of a #Configuration annotated class will allow you to create a bean when the given condition is fullfilled. Example here: https://javapapers.com/spring/spring-conditional-annotation/
You can uses as well BeanFactory to inject the definition of your bean (DefinitionBean) into the container. Example here: https://www.logicbig.com/tutorials/spring-framework/spring-core/bean-definition.html
Now, if you want an object that determine what object of type Instance fits better for some need then your approach is ok, but it is not technically a factory :)
When designing something like that I would face that solution considering two design patterns:
Strategy pattern: In order to replace repetitive if else every time you need to evaluate more instances.
Decorator pattern: Trying to make every condition as configurable as possible. They can be composed (decorated) for one or more predicates.
Considering these two pattens you might achieve something like this:
First, define which conditions will identify a given instance:
public enum InstanceType {
INSTANCE_TYPE_1(Condition::isOne, Condition::isTwo),
INSTANCE_TYPE_2(Condition::isOne, Condition::isThree),
...;
private List<Predicate<Condition>> evaluators;
#SafeVarargs
InstanceType(final Predicate<Condition>... evaluators) {
this.evaluators = Arrays.asList(evaluators);
}
public boolean evaluate(final Condition condition) {
return evaluators.stream().allMatch(it -> it.test(condition));
}
}
Then, you should link every instance implementation to an specific instance type:
#Component
public class InstanceOne implements Instance {
#Override
public InstanceType getType() {
return InstanceType.INSTANCE_TYPE_1;
}
}
Finally, a class to config where defining the relation between types and instances as EnumMap
#Configuration
public class InstanceFactoryConfig {
#Autowired
private List<Instance> instances;
#Bean
public EnumMap<InstanceType, Instance> instancesMap() {
EnumMap<InstanceType, Instance> instanceEnumMap = new EnumMap<>(InstanceType.class);
instances.forEach(i -> instanceEnumMap.put(i.getType(), i));
return instanceEnumMap;
}
}
Thus, you InstanceFactory can be replaced to something like this:
public class InstanceFactory {
#Autowire
private final EnumMap<InstanceType, Instance> instancesMap;
public void getInstance(Condition condition) {
instancesMap.get(getInstanceType(condition)).doSomething();
}
private InstanceType getInstanceType(Condition condition) {
return Arrays.stream(InstancesType.values())
.filter(evaluator -> evaluator.evaluate(condition))
.findFirst().orElseThrow(() -> new RuntimeException("Instance type not found"));
}
}
As you can see, you InstanceFactory is less prone to be modified. This means, every time you need you add a new instance implementation you only need to modify the InstanceType enum. Hope this is helps.
You can use spring existing FactoryBean interface and implement your own logic
It’s one of the best approaches to create beans in spring framework
Here is the link with example :
https://www.baeldung.com/spring-factorybean
See:
Spring Profile
The active profile is set by properties and based on the value you assign to the profile, Spring will load different beans for the same interface.
So it might be exactly what you need.

Referencing a bean with dependencies using Spring Java Config

In the following Spring Java Config:
#Configuration
#EnableAutoConfiguration
#ComponentScan("my.package")
public class Config {
#Bean
public BasicBean basicBean1() {
return new BasicBean("1");
}
#Bean
public BasicBean basicBean2() {
return new BasicBean("2");
}
#Bean
public ComplexBean complexBeanByParameters(List<BasicBean> basicBeans) {
return new ComplexBean(basicBeans);
}
#Bean
public ComplexBean complexBeanByReferences() {
return new ComplexBean(Arrays.asList(basicBean1(), basicBean2()));
}
}
I can create two ComplexBeans using either parameter injection, which is elegant, but has shortcomings if a have a few other beans of BasicBean type and only want a few (the parameters can of course be of type BasicBean and enumerate by name the beans I'm interested of, but it could turn out to be a very long list, at least for arguments sake). In case I wish to reference the beans directly I might use the complexBeanByReferences style, which could be useful in case of ordering or some other consideration.
But say I want to use the complexBeanByReference style to reference the bean complexBeanByParameters, that is something along the line of:
#Bean
public ComplexBeanRegistry complexBeanRegistry() {
return new ComplexBeanRegistry(
Arrays.asList(
complexBeanByParameters(), // but this will not work!
complexBeanByReferences()
)
);
}
How would I reference complexBeanByParameters, without having to specify a list of dependencies to complexBeanRegistry? Which, the latter in all honesty should be completely oblivious of.
There is the option to just use
public ComplexBeanRegistry complexBeanRegistry(List<ComplexBeans> complexBeans) {...}
of course, but this might not be an option in certain cases, specifically when using the CacheConfigurer from spring-context. In this case the Java Config is intended to
create the beans
by implementing CacheConfigurer, override the default instances of the CacheManager and KeyGenerator beans.
The requirement to implement CacheConfigurer means I can't change the signature to use parameter injection.
So the question is, is there a way to reference complexBeanByParameters using the "direct" reference style?
Maybe you could reference it with separation by Qualifier:
#Bean
#Qualifier("complexBeanParam")
public ComplexBean complexBeanByParameters(List<BasicBean> basicBeans) {
return new ComplexBean(basicBeans);
}
#Bean
#Qualifier("complexBeanRef")
public ComplexBean complexBeanByReferences() {
return new ComplexBean(Arrays.asList(basicBean1(), basicBean2()));
}
and for example autowire:
#Autowired
#Qualifier("complexBeanParam")
private ComplexBean beanParam;

Why is there a different result of an method when modified its object with a portable extension and applied an interceptor on the same object too?

Maybe you can give a hint, where to find a solution for this problem.
Currently I discover CDI portable extensions, like the sample shown here Wrapping an InjectionTarget.
This CDI portable extension reads values from properties files and configures fields of a Java object.
Here is a snippet of the extension:
public <T> void checkForPropertyFileAnnotation(
final #Observes ProcessInjectionTarget<T> pit) {
AnnotatedType<T> at = pit.getAnnotatedType();
if (!at.isAnnotationPresent(PropertyFile.class)) {
return;
}
// found annotation
[...load properties...]
[...assign properties to fields...]
[...create new wrapped InjectionTarget...]
pit.setInjectionTarget([created InjectionTarget]);
}
Running this extension on an example class does what it should do. But when I apply a, for example LoggingInterceptor like shown here simple cdi interceptor, the extension seems not working.
The logging interceptor:
#Log #Interceptor
public class LoggingInterceptor {
#AroundInvoke
public Object log(InvocationContext ctx) throws Exception {
Logger logger = Logger.getLogger(ctx.getTarget().getClass().getName());
logger.info("before");
Object result = ctx.proceed();
logger.info("after");
return result;
}
}
The sample-class looks like:
#Named
#Model // to use EL in jsp/jsf
#Log // the interceptor annotation
#PropertyFile("myprops.txt") // the annotation used within the extension
public class MyProperties {
#Property("version")
Integer version;
#Property("appname")
String appname;
public Integer getVersion() {
return version;
}
public String getAppname() {
return appname;
}
}
The content of the result-page:
<h:body bgcolor="white">
#{myProperties.appname} v#{myProperties.version}
</h:body>
It's not really true, the extension works, means it injects the appropriate values into the desired fields as I can see in the log-file, but after the interceptor gets applied, the injected values are gone.
Do you have any idea why this can happen? Maybe the extension handles a different instance of the class than the interceptor.
Thanx in advance for your reply!
You're twiddling fields on a proxy class, and then the getters are getting the values from the underlying instance.
I had the same problem. Both the interceptor and extension work separately, but when used together, the interceptor stops working. This is due to a bug in OpenWebBeans CDI implementation where interceptors are only processed when the injection target is an instance of an OpenWebBeans specific InjectionTargetImpl.
See: https://issues.apache.org/jira/browse/OWB-897

Categories

Resources