How to send a gcm xmpp message with Spring Integration? - java

I am new to Spring Integration and to Google Cloud Message. XmppConnectionFactoryBean is created successfully and I can autowire a XmppConnection in my service class.
#Configuration
class XmppConfig {
#Value("${gcm.sender_id}")
private String senderId;
#Value("${gcm.api_key}")
private String apiKey;
#Value("${gcm.host}")
private String host;
#Value("${gcm.port}")
private int port;
#Bean
public ConnectionConfiguration connectionConfiguration() {
ConnectionConfiguration connectionConfig = new ConnectionConfiguration(host, port);
connectionConfig.setSecurityMode(SecurityMode.enabled);
connectionConfig.setReconnectionAllowed(true);
connectionConfig.setRosterLoadedAtLogin(false);
connectionConfig.setSendPresence(false);
connectionConfig.setSocketFactory(SSLSocketFactory.getDefault());
return connectionConfig;
}
#Bean
public XmppConnectionFactoryBean xmppConnectionFactoryBean() {
XmppConnectionFactoryBean connectionFactoryBean = new XmppConnectionFactoryBean();
connectionFactoryBean.setUser(senderId);
connectionFactoryBean.setPassword(apiKey);
connectionFactoryBean.setConnectionConfiguration(connectionConfiguration());
return connectionFactoryBean;
}
}
Service class:
class MyServiceImpl implements MyService {
#Autowired
private XmppConnection xmppConnection;
}
Is that the right approach? How can I send an XMPP message to GCM? Should I use XmppConnection directly or some Spring messaging abstraction?
UPDATE
Created a MessageHandler and defined bean names.
#Bean(name = "xmppConnection")
public XmppConnectionFactoryBean xmppConnectionFactoryBean() {
XmppConnectionFactoryBean connectionFactoryBean = new XmppConnectionFactoryBean();
connectionFactoryBean.setUser(senderId);
connectionFactoryBean.setPassword(apiKey);
connectionFactoryBean.setConnectionConfiguration(connectionConfiguration());
return connectionFactoryBean;
}
#Bean(name = "gcmChannel")
public MessageChannel messageChannel() {
return new DirectChannel();
}
#Bean
#ServiceActivator(inputChannel = "gcmChannel")
public MessageHandler messageHandler() {
return new ChatMessageSendingMessageHandler();
}
#Autowired
#Qualifier("gcmChannel")
private MessageChannel messageChannel;

Of course, it would be better to use a specific Spring Integration Adapter on the matter. It is ChatMessageSendingMessageHandler.
And we right now in the merging phase for the Extensions support for that adapter: https://github.com/spring-projects/spring-integration/pull/1745. So, with the next Spring Integration 4.3 version you will have more GCM support there.
Right now as a workaround you have to create an XMPP message with GCM extension manually and send it to the channel for that ChatMessageSendingMessageHandler.

Related

TibcojmsConnectionFactory configuration issues

I am trying to work on a tibco JMS CDC product. I have issues setting up the configuration and could not find a solution to my problem.
import com.tibco.tibjms.TibjmsConnectionFactory;
import javax.jms.JMSException;
#Configuration
#EnableJms
public class TibcoBusConfiguration {
#Value("${ems.password}")
private String password;
#Value("${ems.port}")
private String port;
#Value("${ems.topic}")
private String queue;
#Value("${ems.server}")
private String server;
#Value("${ems.user}")
private String user;
#Bean(name = "tibjmsConnectionFactory")
public TibjmsConnectionFactory jmsConnectionFactory() throws javax.jms.JMSException {
final TibjmsConnectionFactory factory = new TibjmsConnectionFactory();
factory.setServerUrl(serverURL());
factory.setUserName(user);
factory.setUserPassword(password);
return factory;
}
#Bean
public JmsTemplate jmsTemplate(
#Autowired TibjmsConnectionFactory tibjmsConnectionFactory) throws JMSException {
final JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory(jmsConnectionFactory());
jmsTemplate.setDefaultDestinationName(queue);
jmsTemplate.setExplicitQosEnabled(true);
jmsTemplate.setDeliveryMode(DeliveryMode.PERSISTENT);
jmsTemplate.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
jmsTemplate.setSessionTransacted(false);
return jmsTemplate;
}
private String serverURL() {
return "tcp://" + server + ":" + port;
}
}
Unfortunately JmsTemplate only allows jakarta.jms.ConnectionFactory, how do I pass in the TibcoConnectionFactory, because casting is not allowed since the classes do not match. Is my understanding of this incorrect?
I have the following JAR's in my maven setup: tibjms.jar jakarta.jms-api-3.0 javax.jms-api-2.0
Thanks in Advance
Boot 3/Spring 6 moved from the javax to the jakarta namespace for JEE support - you either need to find a Tibco jar for JEE 9 or drop back to Boot 2.7/Spring 5.3.

How to understand async vs sync in Spring AMQP?

I'm currently reading through Spring AMQP's official sample project along with it's corresponding explanations from Spring AMQP docs. The project involves an sync and async version, and the two only differs slightly. Here's the async version:
Producer config:
#Configuration
public class ProducerConfiguration {
protected final String helloWorldQueueName = "hello.world.queue";
#Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
template.setRoutingKey(this.helloWorldQueueName);
return template;
}
#Bean
public ConnectionFactory connectionFactory() {
return new CachingConnectionFactory();
}
#Bean
public ScheduledProducer scheduledProducer() {
return new ScheduledProducer();
}
#Bean
public BeanPostProcessor postProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
}
static class ScheduledProducer {
#Autowired
private volatile RabbitTemplate rabbitTemplate;
private final AtomicInteger counter = new AtomicInteger();
#Scheduled(fixedRate = 3000)
public void sendMessage() {
rabbitTemplate.convertAndSend("Hello World " + counter.incrementAndGet());
}
}
}
Consumer config:
#Configuration
public class ConsumerConfiguration {
protected final String helloWorldQueueName = "hello.world.queue";
#Bean
public ConnectionFactory connectionFactory() {
return new CachingConnectionFactory();
}
#Bean
public SimpleMessageListenerContainer listenerContainer() {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory());
container.setQueueNames(this.helloWorldQueueName);
container.setMessageListener(new MessageListenerAdapter(new HelloWorldHandler()));
return container;
}
#Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
template.setRoutingKey(this.helloWorldQueueName);
template.setDefaultReceiveQueue(this.helloWorldQueueName);
return template;
}
#Bean
public Queue helloWorldQueue() {
return new Queue(this.helloWorldQueueName);
}
}
HelloWorldHandler:
public class HelloWorldHandler {
public void handleMessage(String text) {
System.out.println("Received: " + text);
}
}
As the docs explains:
Since this sample demonstrates asynchronous message reception, the producing side is designed to continuously send messages (if it were a message-per-execution model like the synchronous version, it would not be quite so obvious that it is, in fact, a message-driven consumer). The component responsible for continuously sending messages is defined as an inner class within the ProducerConfiguration. It is configured to run every three seconds.
I failed to understand what's "async" about this code, since, from my understanding, in a basic "synchronous fashion", operations like amqpTemplate.converAndSend() and amqpTemplate.receiveAndConvert() already peforms Rabbitmq's async actions, neither producer nor consumer are blocking when sending/receiving messages.
So, how's async in this example manifested? And how to understand async vs sync in the Spring AMQP context?
With async, the MessageListener is invoked by the framework; messages arrive whenever they are available.
With sync, the application calls a receive method which either returns immediately if no message is available, or blocks until a message arrives or a timeout expires.
In the sync case, the application controls when messages are received, with async, the framework is in control.

java grpc server for a bi-directional stream that connect activemq to push & get message from activemq

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).

How to use the method amazonSQSClient() in the main class?

I am new in Spring Boot. Does anyone know how to call the method amazonSQSClient() in the main class?
#Configuration
public class AWSConfiguration {
#Value("${aws.access.key.encrypted}")
private String amazonAWSAccessKey;
#Value("${aws.secret.key.encrypted}")
private String amazonAWSSecretKey;
#Value("${aws.region.encrypted}")
private String amazonAWSRegion;
#Bean
public AmazonSQS amazonSQSClient() {
AWSCredentials awsCredentials = new BasicAWSCredentials(amazonAWSAccessKey, amazonAWSSecretKey);
AmazonSQS client = AmazonSQSClientBuilder.standard()
.withRegion(amazonAWSRegion)
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials)).build();
return client;
}
}
You shouldn't need to. That's the point of using the #Bean annotation, Spring will inject an instance of AmazonSQS whenever you need one. So, you might have a class like this that uses the SQS client:
#Service
public class QueueService {
private AmazonSQS amazonSQS;
#Autowired
public QueueService(AmazonSQS sqs) {
this.amazonSQS = sqs;
}
}
The Spring framework is smart enough to see that it needs to inject an instance of AmazonSQS, and by marking the method amazonSqsClient with the #Bean annotation, you are telling Spring to use that method whenever it needs to create an instance of AmazonSQS.

How do I provide runtime specific configuration metadata to Spring?

I'm writing a client/server app and configuring it with Spring.
My client interface handles marshalling requests to the server and handling the responses.
At the moment, I have a factory that looks something like:
public class ClientFactory {
private ApplicationContext ctx;
public ClientFactory(){
ctx = new AnnotationConfigApplicationContext(MyConfig.class);
}
public MyClient(String host, int port){
MyClient client = ...
// create a connection to the server
return client;
}
}
Now, MyClient has a bunch of dependencies that I would like to inject, so I would like to create the MyClient instance using Spring and use #Inject annotations to inject the dependencies.
How do I pass the host/port as configuration metadata into the Spring configuration? If I can't what is the recommended alternative. I could do all the wiring myself, but then that is what Spring is for.
Jeff
You should check configuration part of the spring reference. For example you can create beans like this with spring 3.x.
#Configuration
// spring config that loads the properties file
#ImportResource("classpath:/properties-config.xml")
public class AppConfig {
/**
* Using property 'EL' syntax to load values from the
* jetProperties value
*/
private #Value("#{jetProperties['jetBean.name']}") String name;
private #Value("#{jetProperties['jetBean.price']}") Long price;
private #Value("#{jetProperties['jetBean.url']}") URL url;
/**
* Create a jetBean within the Spring Application Context
* #return a bean
*/
public #Bean(name = "jetBean")
JetBean jetBean() {
JetBean bean = new JetBeanImpl();
bean.setName(name);
bean.setPrice(price);
bean.setUrl(url);
return bean;
}
}
I solved this using a static configuration class.
public class ClientFactory {
private ApplicationContext ctx;
public ClientFactory(){
ctx = new AnnotationConfigApplicationContext(MyConfig.class,ServerConfig.class);
}
public MyClient(String host, int port){
MyClient client = ...
// create a connection to the server
return client;
}
#Data
#AllArgsConstructor
public static class ServerDetails{
private int port;
private String host;
}
#Configuration
public static class ServerConfig{
static String host;
static int port;
#Bean
public void serverDetails(){
return new ServerDetails(host, port);
}
}
}
It feels very clunky though. Is there a better way?

Categories