The type EnableBinding is deprecated, The type StreamListener is deprecated - Spring Cloud Stream - spring

I am using Spring Boot version 2.4.2 and doing Spring Cloud Stream and Spring Integration using #InboundChannelAdapter .
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.integration.annotation.InboundChannelAdapter;
#EnableBinding(value = Source.class)
public class TransactionPublisher {
#InboundChannelAdapter(channel = Source.OUTPUT)
public String sendTransactionDetails() {
return "{name:\"T1\", amount: \"1000\", transactionFor : \"Purchase\"}";
}
}
RabbitmqReceiverApplication.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;
#EnableBinding(value = Sink.class)
#SpringBootApplication
public class RabbitmqReceiverApplication {
public static void main(String[] args) {
SpringApplication.run(RabbitmqReceiverApplication.class, args);
}
#StreamListener(Sink.INPUT)
public void log(String message) {
System.out.println(message);
}
}
What could be the alternative ? how to refactor the above code then? A lot of things are happening with the pace getting difficult to understand whats coming and whats going off ?

See the documentation.
e.g. On the consumer side...
#Bean
Consumer<String> log() {
return str -> {
System.out.println(str);
};
}
The binding name is log-in-0.
On the producer side, it's a Supplier<String> bean with binding name sendTransactionDetails-out-0.

Related

Open tracer Jaeger Trace not getting reflect for apache camel

I am new to apache camel and open tracing with Jaeger, I trying to get the traces for apache camel in Jaeger UI but its not getting captured through Open Tracing.
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
import org.springframework.stereotype.Component;
#Component
public class RouteBuilderClient {
public void test() throws Exception {
DefaultCamelContext camelContext = new DefaultCamelContext();
camelContext.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
from("timer:first-timer")
.to("log:first-timer");
}
});
camelContext.start();
camelContext.stop();
}
}
However if if I extend the RouteBuilder class(Below is the sample code) and then I override the configure method then traces are getting generated for Apache camel. Is there any way without extending the routeBuilder I can get the traces?
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;
#Component
public class RouteBuilderClient extends RouteBuilder {
#Override
public void configure() throws Exception {
from("timer:first-timer")
.to("log:first-timer");
}
}
My controller class:
import org.apache.camel.opentracing.starter.CamelOpenTracing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
#CamelOpenTracing
public class JaegarClientApplication {
public static void main(String[] args) {
SpringApplication.run(JaegarClientApplication.class, args);
}
}
Try adding this in your setup() method, before starting the Camel context:
OpenTracingTracer ottracer = new OpenTracingTracer();
ottracer.init(camelContext);
camelContext.start();
Below is my code that Worked successfully.
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.opentracing.OpenTracingTracer;
public class RouteBuilderClient3 {
public void test() throws Exception {
RouteBuilder builder = new RouteBuilder() {
public void configure() {
from("timer:first-timer3").transform().constant("Rajesh client 3")
.to("log:first-timer3");
}
};
CamelContext myCamelContext = new DefaultCamelContext();
myCamelContext.addRoutes(builder);
OpenTracingTracer ottracer = new OpenTracingTracer();
ottracer.init(myCamelContext);
myCamelContext.start();
// myCamelContext.stop();
}
}

KafkaBindingRebalanceListener Bean not autowired by KafkaMessageChannelBinder Bean

Documentation is pretty straight forward which suggests exposing a Bean of type KafkaBindingRebalanceListener and onPartitiosnAssigned method would be called internally. I'm trying to do the same and somehow while spring framework creates its KafkaMessageChannelBinder Bean the ObjectProvider.getIfUnique() always return null as it not able to find the required bean. It seems when application starts SpringFramework strats creating its Beans first and isnt able to find the Rebalance Listener Bean as it is not yet created. Following are the three code snippets from project. Please help if im missing anything to instruct application to create Beans in application package first before going to Spring Framework.
RebalanceListener
package io.spring.dataflow.sample.seekoffset.config;
import org.apache.kafka.clients.consumer.Consumer;
import org.apache.kafka.common.TopicPartition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.stream.binder.kafka.KafkaBindingRebalanceListener;
import org.springframework.stereotype.Component;
import java.util.Collection;
#Component
public class KafkaRebalanceListener implements KafkaBindingRebalanceListener {
Logger logger = LoggerFactory.getLogger(SeekOffsetConfig.class);
#Override
public void onPartitionsAssigned(String bindingName, Consumer<?, ?> consumer, Collection<TopicPartition> partitions, boolean initial) {
logger.debug("onPartitionsAssigned");
}
}
ConfigClass
package io.spring.dataflow.sample.seekoffset.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.messaging.Message;
#EnableBinding(Sink.class)
public class SeekOffsetConfig {
Logger logger = LoggerFactory.getLogger(SeekOffsetConfig.class);
#StreamListener(Sink.INPUT)
public void receiveMessage(Message<String> message) {
logger.debug("receiveMessage()");
}
}
ApplicationClass
package io.spring.dataflow.sample.seekoffset;
import io.spring.dataflow.sample.seekoffset.config.KafkaRebalanceListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication
public class SeekOffsetApplication {
Logger logger = LoggerFactory.getLogger(SeekOffsetApplication.class);
public static void main(String[] args) {
SpringApplication.run(SeekOffsetApplication.class, args);
}
}
What version are you using? This works fine for me with Boot 2.3.2 and Hoxton.SR6:
#SpringBootApplication
#EnableBinding(Sink.class)
public class So63157778Application {
public static void main(String[] args) {
SpringApplication.run(So63157778Application.class, args);
}
#StreamListener(Sink.INPUT)
public void listen(String in) {
System.out.println(in);
}
#Bean
KafkaBindingRebalanceListener rebal() {
return new KafkaBindingRebalanceListener() {
#Override
public void onPartitionsAssigned(String bindingName, Consumer<?, ?> consumer,
Collection<TopicPartition> partitions, boolean initial) {
System.out.println(bindingName + " assignments: " + partitions + ", initial call :" + initial);
}
};
}
}
input assignments: [input-0], initial call :true
This works for me too:
#SpringBootApplication
#EnableBinding(Sink.class)
public class So63157778Application {
public static void main(String[] args) {
SpringApplication.run(So63157778Application.class, args);
}
#StreamListener(Sink.INPUT)
public void listen(String in) {
System.out.println(in);
}
}
#Component
class Foo implements KafkaBindingRebalanceListener {
#Override
public void onPartitionsAssigned(String bindingName, Consumer<?, ?> consumer,
Collection<TopicPartition> partitions, boolean initial) {
System.out.println(bindingName + " assignments: " + partitions + ", initial call :" + initial);
}
}

Spring Integration how to use Control Bus with JavaConfig, no DSL

I'm having a few issues with Spring Integration and the control bus. I need to turn auto-start off on an InboundChannelAdapter. However when I do this I can't get the ControlBus to start the channel adapter.
I've searched for an answer online, but most of the examples use XML configuration.
Here is the entirety of my code:
package com.example.springintegrationdemo;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.integration.annotation.InboundChannelAdapter;
import org.springframework.integration.annotation.Poller;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.config.ExpressionControlBusFactoryBean;
import org.springframework.integration.core.MessageSource;
import org.springframework.integration.file.FileReadingMessageSource;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.support.GenericMessage;
import java.io.File;
#SpringBootApplication
#EnableIntegration
public class SpringIntegrationDemoApplication {
#Bean
public MessageChannel fileChannel() {
return new DirectChannel();
}
#Bean(name = "fileMessageSource")
#InboundChannelAdapter(channel = "fileChannel", poller = #Poller(fixedDelay = "1000"),autoStartup = "false")
public MessageSource<File> fileMessageSource() {
FileReadingMessageSource fileReadingMessageSource = new FileReadingMessageSource();
fileReadingMessageSource.setDirectory(new File("lz"));
return fileReadingMessageSource;
}
#Bean
#ServiceActivator(inputChannel = "fileChannel")
public MessageHandler messageHandler() {
MessageHandler messageHandler = message -> {
File f = (File) message.getPayload();
System.out.println(f.getAbsolutePath());
};
return messageHandler;
}
#Bean
MessageChannel controlChannel() {
return new DirectChannel();
}
#Bean
#ServiceActivator(inputChannel = "controlChannel")
ExpressionControlBusFactoryBean controlBus() {
ExpressionControlBusFactoryBean expressionControlBusFactoryBean = new ExpressionControlBusFactoryBean();
return expressionControlBusFactoryBean;
}
#Bean
CommandLineRunner commandLineRunner(#Qualifier("controlChannel") MessageChannel controlChannel) {
return (String[] args)-> {
System.out.println("Starting incoming file adapter: ");
boolean sent = controlChannel.send(new GenericMessage<>("#fileMessageSource.start()"));
System.out.println("Sent control message successfully? " + sent);
while(System.in.available() == 0) {
Thread.sleep(50);
}
};
}
public static void main(String[] args) {
SpringApplication.run(SpringIntegrationDemoApplication.class, args);
}
}
The message is sent to the control bus component successfully, but the inbound channel adapter never starts.
I would appreciate any help.
Thanks,
Dave
See here: https://docs.spring.io/spring-integration/docs/current/reference/html/configuration.html#annotations_on_beans
The fileMessageSource bean name is exactly for the FileReadingMessageSource. A SourcePollingChannelAdapter created from the InboundChannelAdapter has this bean name: springIntegrationDemoApplication.fileMessageSource.inboundChannelAdapter.
The #EndpointId can help you to simplify it.
In other words: everything is OK with your config, only the problem that you don't use the proper endpoint id to start the SourcePollingChannelAdapter.

Multiple #SpringBootApplication Annotation in a Project

In my project created by SpringBoot,
I have added 2 main classes with #SpringBootApplication.
Because if I use STS I can choose one main application when start to debug.
But I found that while SpringDemoApplication is up ,RabbitMQApplication is also running.
Is this specification ? working appropriately?
Here this is sample to reproduce
https://github.com/MariMurotani/SpringDemo/tree/6_rabbitMQ
SpringDemoApplication
package demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
#SpringBootApplication
public class SpringDemoApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(SpringDemoApplication.class);
ApplicationContext context = application.run(args);
}
}
RabbitMQApplication
package demo;
import java.util.Date;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import demo.configs.Const;
import demo.dto.Mail;
#SpringBootApplication
public class RabbitMQApplication implements CommandLineRunner {
#Autowired
ApplicationContext context;
#Autowired
RabbitTemplate rabbitTemplate;
#Bean
Queue queue() {
return new Queue(Const.RabbitMQMessageQue, false);
}
#Bean
TopicExchange exchange() {
return new TopicExchange("spring-boot-exchange");
}
#Bean
Binding binding(Queue queue, TopicExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(Const.RabbitMQMessageQue);
}
#Bean
SimpleMessageListenerContainer container(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames(Const.RabbitMQMessageQue);
//container.setMessageListener(listenerAdapter);
return container;
}
/*
For asyncronized receiving
#Bean
Receiver receiver() {
return new Receiver();
}
#Bean
MessageListenerAdapter listenerAdapter(Receiver receiver) {
return new MessageListenerAdapter(receiver, "receiveMessage");
}*/
public static void main(String[] args) throws InterruptedException {
SpringApplication.run(RabbitMQApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
System.out.println("Waiting five seconds...");
while(0 < 1){
for(int i = 0 ; i < 5 ; i++){
String object = (String)rabbitTemplate.receiveAndConvert(Const.RabbitMQMessageQue);
if(object != null){
try{
System.out.println(new Date().toGMTString() + ": " + object);
ObjectMapper mapper = new ObjectMapper();
Mail mail = mapper.readValue(object, Mail.class);
System.out.println(mail.getToAddress() + " , " + mail.getStrContent());
}catch(Exception e){
System.out.println(e.getMessage());
}
}
}
Thread.sleep(10000);
}
}
}
The #SpringBootApplication annotation is a shortcut annotation for #Configuration, #EnableAutoConfiguration, and #ComponentScan.
http://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-using-springbootapplication-annotation.html
The default behavior of #ComponentScan is to look for #Configuration and #Component classes within the same package and all sub-packages of the annotated class. Since all your classes are in the same package, when you start any one of them Spring will find the others and treat them like #Configuration classes, and register their beans, etc.
So yes, this is expected behavior given your project setup. Put each #SpringBootApplication class in a separate subpackage if you don't want this to happen for local testing. If this moves beyond a demo at some point you'll probably want to come up with a better setup (subprojects for each #SpringBootApplication perhaps).
I recently faced the same scenario here and I solved with a simple solution.
My projected uses Maven and is configured with sub-modules like this:
my-parent
|__ my-main (depends on my-other module)
|__ my-other
Each module has its own main App class annotated with #SpringBootApplication. The problem is that both classes reside in the same package even though they are in different modules.
Once I start MyMainApp it also starts MyOtherApp. To avoid this I just had to do the following.
In the my-main module I have:
#SpringBootApplication
public class MyMainApp ... { ... }
and in the my-other module I have:
#SpringBootApplication
#ConditionalOnProperty(name = "my.other.active", havingValue = "true", matchIfMissing = false)
public class MyOtherApp ... { ... }
with application.properties with:
my.other.active=true
It works as expected.

Spring NoClassDefFoundError on org/springframework/transaction/support/TransactionSynchronization

Spring JMS is working against me, as the title say I get a NoClassDefFoundError, below you find the code.
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Queue;
import javax.jms.Session;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.JmsTemplate102;
import org.springframework.jms.core.MessageCreator;
public class JMSQueueSender {
private JmsTemplate jmsTemplate;
private Queue queue;
public void setConnectionFactory(ConnectionFactory cf) {
this.jmsTemplate = new JmsTemplate102(cf, false);
}
public void setQueue(Queue queue) {
this.queue = queue;
}
public void simpleSend() throws Exception {
this.jmsTemplate.send(this.queue, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage("hello queue world");
}
});
}
}
This is also the example code, I have a working code with IBM, but I'm trying to change it for a Spring JMS, but it isn't working. Can someone help me.
I guess you need to add spring-tx.jar (or org.springframework.transaction-*.jar with new style of names) to the classpath.
Try checking your JAVA_HOME and CLASSPATH settings.

Resources