I am trying to create a Generic eventPublisher for my microservices.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.messaging.support.MessageBuilder;
#EnableBinding(TaskEventStreams.class)
public class EventPublisher {
private final TaskEventStreams source;
#Autowired
public EventPublisher(TaskEventStreams source) {
this.source = source;
}
public void publishEvent(BaseEvent event) {
publishEvent(event, event.getTenant());
}
public void publishEvent(BaseEvent event, String key) {
source.taskEventOutput().send(
MessageBuilder
.withPayload(event)
.copyHeaders(event.getHeaders())
.setHeader("partitionKey", key)
.build());
}
}
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;
public interface TaskEventStreams {
String TASK_EVENT_OUTPUT = "task-event-output";
#Output(TASK_EVENT_OUTPUT)
MessageChannel taskEventOutput();
}
Application properties
spring:
cloud:
stream:
default-binder: kafka
kafka:
binder:
brokers: localhost:9092
auto-create-topics: true
bindings:
task-event-output:
destination: tasks
group: tasks
binder: kafka
contentType: application/json
This service will only publish the events. These events then later can be consumed by other services. But when I am trying to publish the event I am getting below exception
#SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Main {
#Autowired
EventPublisher eventPublisher;
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
System.out.println("Hello world!");
}
#PostConstruct
public void init() {
BaseEvent event = TaskCreatedEvent.builder().tenant("tenant").build();
eventPublisher.publishEvent(event);
}
}
Exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'main': Invocation of init method failed; nested exception is org.springframework.messaging.MessageDeliveryException: Dispatcher has no subscribers for channel 'task-event-output'.; nested exception is org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers, failedMessage=GenericMessage [payload=byte[255], headers={id=06b61f95-24d8-7f35-3e57-9ae262c2ca47, partitionKey=tenant, contentType=application/json, tenant=tenant, timestamp=1675063077441}], failedMessage=GenericMessage [payload=byte[255], headers={id=06b61f95-24d8-7f35-3e57-9ae262c2ca47, partitionKey=tenant, contentType=application/json, tenant=tenant, timestamp=1675063077441}]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor
Is there a limitation that without consumer we cannot have producer? or am I missing something with this
#PostConstruct
public void init() {
BaseEvent event = TaskCreatedEvent.builder().tenant("tenant").build();
eventPublisher.publishEvent(event);
}
That's too early to interact with low-level resource. You really cannot send message to the broker while your application is still in a configuration phase.
See into an ApplicationRunner instead: https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.spring-application.command-line-runner
Related
Using Spring Boot, how can I inject my JMS topic from a config property? I want to define the topic/queue name in application.properties to be injected at runtime.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Service;
#Service
public class DispatcherService {
#Autowired ConfigProperties cp;
private final String dest = "my-topic";
private final String dest2 = cp.getTopic();
// WORKS
#JmsListener(destination = "my-topic")
public void receive1(String message) {
...
}
// WORKS
#JmsListener(destination = dest)
public void receive2(String message) {
...
}
// DOES NOT WORK - "Attribute must be constant"
#JmsListener(destination = dest2)
public void receive3(String message) {
...
}
// DOES NOT WORK - can I inject this here, somehow?
#JmsListener(destination = #Value("${topic}"))
public void receive4(String message) {
...
}
}
Try this:
#JmsListener(destination = "${my-topic}")
#JmsListener(destination = "\${config.property.key}"), This should take the topic/queue name from configs.
I am trying to write a java grpc server for a bi-directional stream that connect activemq to push & get message from activemq
I am able to instantiate a Stream observer on the server. However the problem is once i get data from grpc client on invocation of the onNext function on the Server StreamObserver. I am trying to push the message in activemq using JMSTemplate .. I am getting jmsTemplate as null always .
I am configuring JMSTemplate as below to create JMSTemplate,
#Configuration
#EnableJms
public class JmsConfig {
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(
ConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory factory
= new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setConcurrency("5-10");
return factory;
}
}
application.yml
spring:
application-name: bidirectional-server
activemq:
broker-url: tcp://localhost:61616
user: admin
password: admin
packages.trust-all: true
HelloService.java
#GrpcService
public class HelloService extends HelloServiceGrpc.HelloServiceImplBase {
/**
* #param responseObserver
*/
#Override
public StreamObserver<HelloEnvelope> transfer(StreamObserver<HelloEnvelope> responseObserver) {
return new TransferHelloRequestStreaming(responseObserver);
}
}
TransferHelloRequestStreaming.java
#Slf4j
#Service
public class TransferHelloRequestStreaming implements StreamObserver<HelloEnvelope> {
private StreamObserver<HelloEnvelope> transferHelloResponseStreamObserver;
private HelloEnvelope transferHelloResponse = null;
public TransferMessageRequestStreaming() {
}
public TransferHelloRequestStreaming(StreamObserver<HelloEnvelope> transferHelloResponseStreamObserver) {
this.transferHelloResponseStreamObserver = transferHelloResponseStreamObserver;
}
#Override
public void onNext(HelloEnvelope transferHelloRequest) {
new HelloProducer().sendTo("input",transferHelloRequest);
}
#Override
public void onError(Throwable throwable) {
}
#Override
public void onCompleted() {
this.transferHelloResponseStreamObserver.onCompleted();
}
}
HelloProducer.java
#Service
#Slf4j
public class HelloProducer {
#Autowired
JmsTemplate jmsTemplate;
public void sendTo(String destination, MessageEnvelope transferHelloRequest) {
jmsTemplate.convertAndSend(destination, transferHelloRequest);
}
}
HelloService.proto
syntax = "proto3";
import "google/protobuf/any.proto";
option java_multiple_files = true;
option java_outer_classname = "HelloProto";
package com.server.grpcserver;
service HelloService {
rpc transfer(stream HelloEnvelope) returns
(stream HelloEnvelope) {
}
}
message HelloEnvelope {
map<string, google.protobuf.Any> header =1;
bytes body =2;
}
Here in the HelloProducer.java always i always get jmsTemplate as null. Once i get data from grpc client in the onNext() , i want to push the data in activeMQ.
new HelloProducer()
You need to #Autowire the HelloProducer as well - you are creating a new instance (that is not managed by Spring, so it doesn't get any auto wiring).
I want to rewrite this Spring AMQP code without annotations:
#SpringBootApplication
public class So51009346Application {
public static final String QUEUE_PROCESSING_TRANSACTION = "q1";
public static void main(String[] args) {
SpringApplication.run(So51009346Application.class, args);
}
#Bean
public ApplicationRunner runner(RabbitTemplate template) {
return args -> {
ReplyObject reply = (ReplyObject) template.convertSendAndReceive("ex", "rk", new RequestObject());
System.out.println(reply);
};
}
#Bean
public SimpleMessageListenerContainer container(ConnectionFactory cf, Listener listener) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(cf);
container.setQueueNames(QUEUE_PROCESSING_TRANSACTION);
container.setMessageListener(new MessageListenerAdapter(listener, "process"));
return container;
}
#Bean
public Queue queue() {
return new Queue(QUEUE_PROCESSING_TRANSACTION);
}
#Bean
public TopicExchange te() {
return new TopicExchange("ex");
}
#Bean
public Binding binding() {
return BindingBuilder.bind(queue()).to(te()).with("rk");
}
}
class RequestObject implements Serializable {
private static final long serialVersionUID = 1L;
}
class ReplyObject implements Serializable {
private static final long serialVersionUID = 1L;
}
#Component
class Listener {
public ReplyObject process(RequestObject ro) {
return new ReplyObject();
}
}
I tried this simple Java code without annotations:
Producer code:
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import org.plugin.database.bean.TransactionsBean;
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.Connection;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
#WebListener
public class ContextServer implements ServletContextListener {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
Connection connection = connectionFactory.createConnection();
AmqpAdmin admin = new RabbitAdmin(connectionFactory);
admin.declareExchange(new TopicExchange(EXCHANGE_PROCESSING));
admin.declareQueue(new Queue(QUEUE_PROCESSING_TRANSACTION, false));
admin.declareBinding(BindingBuilder.bind(new Queue(QUEUE_PROCESSING_TRANSACTION, false)).to(new TopicExchange(EXCHANGE_PROCESSING)).with(ROUTING_KEY_PROCESSING_TRANSACTION));
RabbitTemplate template = new RabbitTemplate(connectionFactory);
TransactionsBean obj = new TransactionsBean();
obj.setMerchant_id(232323);
template.setReplyTimeout(600000);
TransactionsBean reply = (TransactionsBean) template.convertSendAndReceive(EXCHANGE_PROCESSING, ROUTING_KEY_PROCESSING_TRANSACTION, obj);
System.out.println("!!!!! Received Transaction_id " + reply.getTransaction_id());
#Override
public final void contextDestroyed(final ServletContextEvent sce) {
}
}
Consumer code:
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import org.plugin.database.bean.TransactionsBean;
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Component;
#SpringBootApplication
#WebListener
public class ContextServer implements ServletContextListener {
private static String QUEUE_PROCESSING_TRANSACTION = "processing-process-queue";
private static final String EXCHANGE_PROCESSING = "processing";
private static final String ROUTING_KEY_PROCESSING_TRANSACTION = "processing.trx.process";
#Override
public final void contextInitialized(final ServletContextEvent contextEvent) {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
AmqpAdmin admin = new RabbitAdmin(connectionFactory);
admin.declareExchange(new TopicExchange(EXCHANGE_PROCESSING));
admin.declareQueue(new Queue(QUEUE_PROCESSING_TRANSACTION, false));
admin.declareBinding(BindingBuilder.bind(new Queue(QUEUE_PROCESSING_TRANSACTION, false))
.to(new TopicExchange(EXCHANGE_PROCESSING)).with(ROUTING_KEY_PROCESSING_TRANSACTION));
RabbitTemplate template = new RabbitTemplate(connectionFactory);
}
#Bean
public SimpleMessageListenerContainer container(ConnectionFactory cf, Listener listener) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(cf);
container.setQueueNames(QUEUE_PROCESSING_TRANSACTION);
container.setMessageListener(new MessageListenerAdapter(listener, "process"));
container.setMessageConverter(new SerializerMessageConverter());//basic converter for java.io.Serializable POJO
return container;
}
#Override
public final void contextDestroyed(final ServletContextEvent sce) {
}
#Component
class Listener {
public TransactionsBean process(TransactionsBean ro) {
TransactionsBean obj = new TransactionsBean();
obj.setTransaction_id("some_id");
return obj;
}
}
}
Error stack:
13:20:44,797 INFO [org.springframework.amqp.rabbit.listener.DirectReplyToMessageListenerContainer] (ServerService Thread Pool -- 79) Container initialized for queues: [amq.rabbitmq.reply-to]
13:20:44,803 INFO [org.springframework.amqp.rabbit.listener.DirectReplyToMessageListenerContainer] (ServerService Thread Pool -- 79) SimpleConsumer [queue=amq.rabbitmq.reply-to, consumerTag=amq.ctag-oWxWZJEPgZyP-gRxWe-Ifg identity=42eb5972] started
13:20:49,846 ERROR [org.jboss.msc.service.fail] (ServerService Thread Pool -- 79) MSC000001: Failed to start service jboss.undertow.deployment.default-server.default-host./rest_api: org.jboss.msc.service.StartException in service jboss.undertow.deployment.default-server.default-host./rest_api: java.lang.RuntimeException: java.lang.NullPointerException
at org.wildfly.extension.undertow//org.wildfly.extension.undertow.deployment.UndertowDeploymentService$1.run(UndertowDeploymentService.java:81)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:514)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at org.jboss.threads#2.3.1.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads#2.3.1.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1985)
at org.jboss.threads#2.3.1.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1487)
at org.jboss.threads#2.3.1.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1378)
at java.base/java.lang.Thread.run(Thread.java:844)
at org.jboss.threads#2.3.1.Final//org.jboss.threads.JBossThread.run(JBossThread.java:485)
Caused by: java.lang.RuntimeException: java.lang.NullPointerException
at io.undertow.servlet//io.undertow.servlet.core.DeploymentManagerImpl.deploy(DeploymentManagerImpl.java:251)
at org.wildfly.extension.undertow//org.wildfly.extension.undertow.deployment.UndertowDeploymentService.startContext(UndertowDeploymentService.java:96)
at org.wildfly.extension.undertow//org.wildfly.extension.undertow.deployment.UndertowDeploymentService$1.run(UndertowDeploymentService.java:78)
... 8 more
Caused by: java.lang.NullPointerException
at deployment.rest_api.war//org.rest.api.context.ContextServer.contextInitialized(ContextServer.java:43)
at io.undertow.servlet//io.undertow.servlet.core.ApplicationListeners.contextInitialized(ApplicationListeners.java:187)
at io.undertow.servlet//io.undertow.servlet.core.DeploymentManagerImpl$1.call(DeploymentManagerImpl.java:215)
at io.undertow.servlet//io.undertow.servlet.core.DeploymentManagerImpl$1.call(DeploymentManagerImpl.java:184)
at io.undertow.servlet//io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:42)
at io.undertow.servlet//io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at org.wildfly.extension.undertow//org.wildfly.extension.undertow.security.SecurityContextThreadSetupAction.lambda$create$0(SecurityContextThreadSetupAction.java:105)
at org.wildfly.extension.undertow//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1526)
at org.wildfly.extension.undertow//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1526)
at org.wildfly.extension.undertow//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1526)
at org.wildfly.extension.undertow//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1526)
at io.undertow.servlet//io.undertow.servlet.core.DeploymentManagerImpl.deploy(DeploymentManagerImpl.java:249)
... 10 more
13:20:49,885 ERROR [org.jboss.as.controller.management-operation] (DeploymentScanner-threads - 2) WFLYCTL0013: Operation ("deploy") failed - address: ([("deployment" => "rest_api.war")]) - failure description: {"WFLYCTL0080: Failed services" => {"jboss.undertow.deployment.default-server.default-host./rest_api" => "java.lang.RuntimeException: java.lang.NullPointerException
Caused by: java.lang.RuntimeException: java.lang.NullPointerException
Caused by: java.lang.NullPointerException"}}
13:20:49,908 INFO [org.jboss.as.server] (DeploymentScanner-threads - 2) WFLYSRV0010: Deployed "rest_api.war" (runtime-name : "rest_api.war")
Message from Producer is successfully send.
I get NPE at this line in Producer: reply.getTransaction_id()
What is the proper way to implement the Consumer without annotations with basic Java code?
As I explained in my answer to your previous question, if you are not familiar with Spring, the best way to get started is to use Spring Boot. Go to http://start.spring.io and build a new project after selecting RabbitMQ as a dependency. That is exactly what I did for the example code there.
If you insist on rolling your own code and not using Spring to manage the dependencies and lifecycles of these components; you need to do that work yourself - calling afterPropertiesSet(), start() etc.
I get NPE
That is entirely insufficient information. You need to show the complete stack trace, and all of your code, including the TransactionsBean.
Trust me; "without annotations" means you have to write a lot more code yourself.
EDIT
System.out.println(" !!!!!!!! Transaction_id " + receivedobj.getTransaction_id());
Of course you'll get an NPE - you don't actually check the returned object is not null; the template has a default reply timeout of 5 seconds, after which null is returned.
The container won't do anything until it is start()ed; again; please learn to use Spring to manage the lifecycle of these objects.
There multiple problems here:
As mentioned above by #Gary Russell there is no connection timeout at first
The root problem of receiving null is that convertSendAndReceive operate with POJO which should be serializable and Serializer should be described on Consumer side:
#Bean
public SimpleMessageListenerContainer container(ConnectionFactory cf, Listener listener) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(cf);
container.setQueueNames(QUEUE_PROCESSING_TRANSACTION);
container.setMessageListener(new MessageListenerAdapter(listener, "process"));
container.setMessageConverter(new SerializerMessageConverter());//basic converter for java.io.Serializable POJO
return container;
}
I am new to Spring Boot and coding for my college project work and I need some help.
I want to create a spring boot application, which starts a factory class that will create and starts 10 synchronous clients. These clients must be listening constantly to the queues. Below is my code. I am not sure, if I am on right track and need some help. Is my implementation correct? How can I make sure that 10 synchronous clients are created? How do I use identifier to identify which message client processed the message ?
Application.java
#SpringBootApplication
public class Application {
#Bean
public MesFactory mesFactory(){
return new MesFactory();
}
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
}
public class MesFactory {
private ExecutorService executorService =
Executors.newFixedThreadPool(10);
#PostConstruct
public void build(){
executorService.execute(() -> new MesClient());
}
}
#Component
#EnableJms
public class MesClient {
private static final Log log = LogFactory.getLog(MesClient.class);
#Autowired
private JmsTemplate jmsTemplate;
#JmsListener(destination = "incoming-messages-queue")
public void receive(String message) {
System.out.println("Received <" + message + ">");
doPerformMessage(message);
}
public void doPerformMessage(String message){
// parsing happens here
}
#Bean
ConnectionFactory connectionFactory() {
return new RMQConnectionFactory();
}
#Scheduled(fixedRate = 100000000L)
public void sendMessage() {
String message= "Hi World";
// Coerce a javax.jms.MessageCreator
MessageCreator messageCreator = (Session session) -> {
return session.createTextMessage(message);
};
// And publish to RabbitMQ using Spring's JmsTemplate
jmsTemplate.send("incoming-messages-queue", messageCreator);
}
}
Set the boot properties...
spring.jms.listener.concurrency=10
spring.jms.listener.max-concurrency=10
The listener container will start 10 threads - each consuming from the same queue; they will invoke the JMS listener method.
See the Spring Boot documentation - Application Properties and scroll down to the JMS section.
To me, this appears to be just about the simplest possible spring integration example. I'm trying to learn from the si4demo. But when I run it, I get this exception:
Exception in thread "main"
org.springframework.messaging.MessageDeliveryException: Dispatcher has
no subscribers for channel 'application.inbox'.; nested exception is
org.springframework.integration.MessageDispatchingException:
Dispatcher has no subscribers
Where am I going wrong? Doesn't the defined flow create a subscription to the inbox channel?
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.IntegrationComponentScan;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.messaging.MessageChannel;
#Configuration
#ComponentScan
#IntegrationComponentScan
public class App {
public static void main(String[] args) {
try (ConfigurableApplicationContext ctx = SpringApplication.run(App.class, args)) {
final Gateway gateway = ctx.getBean(Gateway.class);
final String rs = gateway.send("hullo");
System.out.println(rs);
}
}
private static final String INBOX = "inbox";
#MessagingGateway(defaultRequestChannel = INBOX)
public interface Gateway {
String send(String msg);
}
#Bean
public IntegrationFlow flow() {
return IntegrationFlows.from(INBOX)
.transform(p -> "world")
.get();
}
#Bean(name = INBOX)
public MessageChannel inbox() {
return new DirectChannel();
}
}
Looks like you have missed the main player - #EnableIntegraion:
Starting with version 4.0, the #EnableIntegration annotation has been introduced, to allow the registration of Spring Integration infrastructure beans (see JavaDocs). This annotation is required when only Java & Annotation configuration is used, e.g. with Spring Boot and/or Spring Integration Messaging Annotation support and Spring Integration Java DSL with no XML integration configuration.
http://docs.spring.io/spring-integration/docs/4.3.0.BUILD-SNAPSHOT/reference/html/overview.html#configuration-enable-integration