Spring websocket stomp sock js activemq durable subcription - java

As per the documentation of activemq we need to set the http://activemq.apache.org/stomp client-id header to have durable subscriptions.
I set the client-id in connect headers and activemq.subscriptionName in subscription headers as shown below, however I am not seeing the desired behavior. Do we need to set anything on the web socket configuration and message side too?
Here is the subscription code
var headers = {
// additional header
'client-id': 'my-client-id'
};
var subscription_headers = {
// additional header
'activemq.subscriptionName': 'my-client-id'
};
var connect = function () {
var socket = new SockJS( webSocketUrl );
stompClient = Stomp.over( socket );
stompClient.connect( headers, function ( frame ) {
console.log( 'Connected: ' + frame );
stompClient.subscribe( topic, function ( message ) {
.....
.....
}, subscription_headers);
}, function(frame) {
console.log("Web socket disconnected");
});
}
Websocket configuration
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfiguration extends AbstractWebSocketMessageBrokerConfigurer {
#Autowired
#Value("${spring.websocket.activemq.relay.host}")
private String relayHost;
#Autowired
#Value("${spring.websocket.activemq.relay.port}")
private int relayPort;
#Autowired
#Value("${spring.activemq.user}")
private String activeMqLogin;
#Autowired
#Value("${spring.activemq.password}")
private String activeMqPassword;
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableStompBrokerRelay("/queue/", "/topic/")
.setRelayHost(relayHost)
.setRelayPort(relayPort)
.setSystemLogin(activeMqLogin)
.setSystemPasscode(activeMqPassword);
registry.setApplicationDestinationPrefixes("/testbrkr");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/exWs").withSockJS();
}
}

This worked, passing the headers directly in the function as shown
var connect = function () {
var socket = new SockJS( webSocketUrl );
stompClient = Stomp.over( socket );
stompClient.connect( {"client-id": "my-client-id"},, function ( frame ) {
console.log( 'Connected: ' + frame );
stompClient.subscribe( topic, function ( message ) {
.....
.....
}, {"activemq.subscriptionName": "my-client-id"});
}, function(frame) {
console.log("Web socket disconnected");
});
}

Related

spring-integration-mail Receiver mail for read email program in spring boot

I copied code from git for read emails I tired many but facing same as below errors in console while running the application. Please help me resolve this:
enter image description here
This is my Application.yaml : Here my specified username and password are correct only
logging:
level:
root: INFO
org.ygaros: DEBUG
spring:
mail:
username: arunofficial302#gmail.com
password: ***************
properties:
protocol: imaps
host: imap.gmail.com
port: 993
This is my config file:
package com.htc.mailOperation.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.mail.ImapMailReceiver;
import org.springframework.integration.mail.MailReceiver;
import org.springframework.integration.mail.MailReceivingMessageSource;
import org.springframework.messaging.Message;
import com.htc.mailOperation.service.MailReceiverService;
import javax.mail.internet.MimeMessage;
#Configuration
#EnableIntegration
public class MailConfig {
private static final Logger log = LoggerFactory.getLogger(MailConfig.class);
#Autowired
private MailReceiverService receiverService;
#Value("${spring.mail.username}")
private String USERNAME;
#Value("${spring.mail.password}")
private String PASSWORD;
#Value("${spring.mail.properties.port}")
private int IMAP_PORT;
#Value("${spring.mail.properties.protocol}")
private String IMAP_PROTOCOL;
#Value("${spring.mail.properties.host}")
private String IMAP_HOST;
/*
Define a channel where we send and get the messages (mails in this example).
MessageSource is sending messages to this channel.
DirectChannel is a SubscribableChannel.
*/
#Bean("incomingEmailsChannel")
public DirectChannel defaultChannel() {
DirectChannel directChannel = new DirectChannel();
directChannel.setDatatypes(MimeMessage.class);
return directChannel;
}
#Bean
public MailReceiver imapMailReceiver() {
String url = String.format(
"%s://%s:%s#%s:%s/INBOX", IMAP_PROTOCOL, USERNAME, PASSWORD, IMAP_HOST, IMAP_PORT
);
log.debug("IMAP url: {}", url.replace(PASSWORD, "x".repeat(8)));
ImapMailReceiver imapMailReceiver = new ImapMailReceiver(url);
imapMailReceiver.setShouldMarkMessagesAsRead(true);
imapMailReceiver.setShouldDeleteMessages(false);
/*
Attach content to message because by default the MimeMessage
doesn't contain content body.
*/
imapMailReceiver.setSimpleContent(true);
//imapMailReceiver.setMaxFetchSize(1);
return imapMailReceiver;
}
/*
Provide MessageSource of Mails as spring integration Messages from ImapMailReceiver to be sent
through incomingEmailsChannel.
Poller with defined rate at which the messages are pushed to the channel
(if there are any) every 5 sec.
*/
#Bean
#InboundChannelAdapter(
channel = "incomingEmailsChannel",
poller = #Poller(fixedDelay = "5000")
)
public MailReceivingMessageSource mailMessageSource(MailReceiver mailReceiver) {
return new MailReceivingMessageSource(mailReceiver);
}
/*
Connect (subscribe) to incomingEmailsChannel channel and provide method to
handle each individual messages.
*/
#ServiceActivator(inputChannel = "incomingEmailsChannel")
public void receive(Message<MimeMessage> message) {
receiverService.handleReceivedMail(message.getPayload());
}
}
This is my service file:
package com.htc.mailOperation.service;
import org.apache.commons.mail.util.MimeMessageParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.stereotype.Service;
import javax.activation.DataSource;
import javax.mail.internet.MimeMessage;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
#Service
public class MailReceiverService {
private static final Logger log = LoggerFactory.getLogger(MailReceiverService.class);
private static final String ATTACHMENTS_FOLDER_STORE = "data";
public void handleReceivedMail(MimeMessage receivedMessage){
try {
MimeMessageParser mimeMessageParser = new MimeMessageParser(receivedMessage);
mimeMessageParser.parse();
logMail(mimeMessageParser);
saveAttachments(mimeMessageParser);
/*
To delete downloaded email
Setting those flag will delete messages only when we use folder.close(true);
message.setFlag(Flags.Flag.DELETED, true);
*/
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
private void logMail(MimeMessageParser mimeMessageParser) throws Exception {
log.debug("From: {} To: {} Subject: {}",
mimeMessageParser.getFrom(), mimeMessageParser.getTo(), mimeMessageParser.getSubject());
log.debug("Mail content: {}", mimeMessageParser.getPlainContent());
}
private void saveAttachments(MimeMessageParser parser){
List<DataSource> attachments = parser.getAttachmentList();
log.debug("Email has {} attachment files", attachments.size());
attachments.forEach(dataSource -> {
String fileName = dataSource.getName();
if(fileName != null && fileName.length() > 0){
String rootDirPath = new FileSystemResource("").getFile().getAbsolutePath();
createDirectoryIfNotExists(rootDirPath);
String attachmentFilePath = rootDirPath +
File.separator +
ATTACHMENTS_FOLDER_STORE +
File.separator +
fileName;
File attachmentFile = new File(attachmentFilePath);
log.info("Attachment file saved to: {}", attachmentFilePath);
try(
InputStream in = dataSource.getInputStream();
OutputStream out = new FileOutputStream(attachmentFile)
){
copyStream(in, out);
}catch(IOException e){
log.error("Failed to save file.", e);
}
}
});
}
private void createDirectoryIfNotExists(String directoryPath) {
Path of = Path.of(directoryPath);
if (!Files.exists(of)) {
try {
Files.createDirectories(of);
} catch (IOException e) {
log.error("An error: {} on creating: {}", e, directoryPath);
}
}
}
private void copyStream(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[8192]; //just my standard base buffer
int readByteCount;
while(-1 != (readByteCount = in.read(buffer))){
out.write(buffer, 0, readByteCount);
}
}
}
can someone please help me with this?
You need to convert your username and password to x-www-form-urlencoded format before build url.
For example:
...
public MailReceiver imapMailReceiver() {
USERNAME = URLEncoder.encode(USERNAME, StandardCharsets.UTF_8.name());
PASSWORD = URLEncoder.encode(PASSWORD, StandardCharsets.UTF_8.name());
String url = String.format(
"%s://%s:%s#%s:%s/INBOX", IMAP_PROTOCOL, USERNAME, PASSWORD, IMAP_HOST, IMAP_PORT
);
...

Vertx 3.6.3: Unable to launch HTTPS server with PFX option

I am using Vertx 3.6.3. I am trying to run an HTTPS server verticle, but unfortunately, verticle is not getting deployed. Could you please let me know where I am doing it wrong?
Here is my verticle:
HTTPSVerticle:
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.json.JsonObject;
import io.vertx.core.net.PfxOptions;
public class HTTPSVerticle extends AbstractVerticle {
#Override
public void start(Future<Void> httpsServerStarted) throws Exception {
int port = config().getJsonObject("http", new JsonObject()).getInteger("port", 8000);
boolean useSsl = config().getJsonObject("http", new JsonObject()).getBoolean("useSsl", false);
String sslCertPath = config().getJsonObject("http", new JsonObject()).getString("sslCertPath", "");
String sslCertPassword = config().getJsonObject("http", new JsonObject()).getString("sslCertPassword", "");
HttpServerOptions httpServerOptions = new HttpServerOptions();
System.out.println(useSsl);
if (useSsl)
httpServerOptions
.setSsl(true)
//.setClientAuth(ClientAuth.REQUIRED)
.setPfxTrustOptions(
new PfxOptions().setPath(sslCertPath).setPassword(sslCertPassword)
);
vertx.createHttpServer(httpServerOptions).requestHandler(httpReq -> {
httpReq.response().end("Hello encrypted world");
}).listen(port, fut -> {
if (fut.succeeded()) {
System.out.println("Verticle now listening on port: " + port);
httpsServerStarted.complete();
}
else {
httpsServerStarted.fail(fut.cause());
System.out.println("Error while starting HTTP server");
}
});
}
}
Here is my test case:
TestHTTPSVerticle:
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpClientOptions;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
#RunWith(VertxUnitRunner.class)
public class TestHTTPSVerticle {
private static Vertx vertx;
#BeforeClass
public static void setUp(TestContext context) {
DeploymentOptions opts = new DeploymentOptions()
.setConfig(new JsonObject().put("http", new JsonObject()
.put("useSsl", true)
.put("sslCertPath", "test.pfx")
.put("sslCertPassword", "abcd")));
vertx = Vertx.vertx();
vertx.deployVerticle(HTTPSVerticle.class.getName(), opts, context.asyncAssertSuccess());
}
#AfterClass
public static void tearDown(TestContext context) {
vertx.close(context.asyncAssertSuccess());
}
#Test
public void testHttpsServerMessage(TestContext context) {
Async async = context.async();
System.out.println("Connecting to server...");
vertx.createHttpClient().get(8000, "localhost", "/loremipsum", respHandler -> respHandler.bodyHandler(respBody -> {
System.out.println(respBody);
context.assertTrue(respBody.toString().equals("Hello encrypted world"));
async.complete();
})).end();
}
}
Its not letting me submit it without elaborating, so redundant elaboration follows:
I am using vertx config mechanism to fetch port, useSsl, sslCertPath and sslCertPassword
I am using HttpServerOptions for configuring SSL settings for http server
When server is started successfully, it should print Verticle now listening on port: 8000
In case, server fails to start, it should print Error while starting HTTP server
But, It never invokes listen's handler with AsyncResult.

Angular 2 + Vert.x: send-receive body of HTTP requests

I made a simple REST application using the Vert.x framework server side and Angular 2 (v6.0.9) client side. What I want to do is make the server display the received data. However I can not figure out how to retrieve the HTTP request body: both routingContext.getBodyAsString() and routingContext.getBodyAsJson() returns null. As a temporary solution, I managed to display all the sent data through the path parameters using getParam("data") method. What am I doing wrong?
Server code
package test.post;
import java.util.HashSet;
import java.util.Set;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.json.Json;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.CorsHandler;
import io.vertx.ext.web.handler.StaticHandler;
public class Server extends AbstractVerticle {
#Override
public void start() throws Exception {
Router router = Router.router(vertx);
Set<String> allowedHeaders = new HashSet<>();
allowedHeaders.add("x-requested-with");
allowedHeaders.add("Access-Control-Allow-Origin");
allowedHeaders.add("origin");
allowedHeaders.add("Content-Type");
allowedHeaders.add("accept");
allowedHeaders.add("X-PINGARUNER");
Set<HttpMethod> allowedMethods = new HashSet<>();
allowedMethods.add(HttpMethod.GET);
allowedMethods.add(HttpMethod.POST);
allowedMethods.add(HttpMethod.OPTIONS);
allowedMethods.add(HttpMethod.DELETE);
allowedMethods.add(HttpMethod.PATCH);
allowedMethods.add(HttpMethod.PUT);
router.route().handler(CorsHandler.create("*").allowedHeaders(allowedHeaders).allowedMethods(allowedMethods));
router.post("/test/post/handler/:data").handler(this::receive);
// Serve the static pages
router.route().handler(StaticHandler.create());
vertx.createHttpServer().requestHandler(router::accept).listen(8080);
System.out.println("Service running");
}
private void receive(RoutingContext routingContext) {
System.out.println("received post request");
System.out.println(routingContext.getBodyAsString());
System.out.println(routingContext.getBodyAsJson());
System.out.println(routingContext.request().getParam("data"));
routingContext.response().putHeader("Content-Type", "application/json").end(Json.encodePrettily("ok"));
}
public static void main(String[] args) {
Vertx vertx = Vertx.vertx();
Server service = new Server();
vertx.deployVerticle(service);
}
}
Client app.component.ts code
import { Component } from '#angular/core';
import { RequestService } from './request.service';
#Component({
selector: 'app-root',
template: `
<h2>Click to send post request</h2>
<button type="submit" (click)=makePostRequest()>Send Post Request</button>
`,
styleUrls: ['./app.component.css']
})
export class AppComponent {
constructor(private requestService: RequestService) { }
makePostRequest() {
this.requestService.sendRequest().subscribe(response => console.log(response));
}
}
Client request.service.ts code
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from "#angular/common/http";
import { catchError, tap } from 'rxjs/operators';
import { Observable, of } from 'rxjs';
export class User {
email: string;
password: string;
address: string;
username: string;
}
#Injectable({
providedIn: 'root'
})
export class RequestService {
constructor(private http: HttpClient) { }
sendRequest(): Observable<any> {
let user = new User();
user.username = 'Admin';
user.email = 'admin#gmail.com';
user.password = 'admin';
user.address = 'somewhere';
console.log(user);
let url = 'http://127.0.0.1:8080/test/post/handler/' + JSON.stringify(user);
let headers = new HttpHeaders({ 'Content-Type': 'application/json' });
let options: { headers, responseType: 'json' };
return this.http.post(url, JSON.stringify(user), options).pipe(
tap(response => console.log(response))
);
}
}
You need to enable body handler in order to read request body, e.g:
router.route("/test/post*").handler(BodyHandler.create());
router.post("/test/post/handler/:data").handler(this::receive);
or enable it globally:
router.route().handler(BodyHandler.create())

connect to a spring websocket with org.asynchttpclient in java desktop

im trying to make an application who cominicates with a server made in spring-boot with websocket.
My server side configuration is the tutorial of spring-boot websocket
package ar.com.sourcesistemas.armController.websocket;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gs-guide-websocket").withSockJS();
}
}
This is my welcome controller:
package ar.com.sourcesistemas.armController;
import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
public class WelcomeController {
// inject via application.properties
#Value("${welcome.message:test}")
private String message = "Hello World";
#RequestMapping("/")
public String welcome(Map<String, Object> model) {
model.put("message", this.message);
return "welcome";
}
}
and this is the broadcaster:
package ar.com.sourcesistemas.armController;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import ar.com.sourcesistemas.armController.pruebas.Greeting;
import ar.com.sourcesistemas.armController.pruebas.HelloMessage;
#Controller
public class ArmController {
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {
Thread.sleep(1000); // simulated delay
return new Greeting("Hello, " + message.getName() + "!");
}
}
to connect to this websocket im using org.asynchttpclient, and my code is this:
AsyncHttpClient asyncHttpClient = new DefaultAsyncHttpClient();
String url = "ws://192.168.0.23:8080/gs-guide-websocket/app/topic/greetings";
AsyncHttpClient c = new DefaultAsyncHttpClient();
WebSocket websocket = c.prepareGet(url)
.execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketTextListener() {
public void onMessage(String message) {
System.out.println(message);
}
public void onOpen(WebSocket websocket) {
System.out.println("impĀ“rimo websocket");
}
public void onClose(WebSocket websocket) {
System.out.println("ni idea");
}
public void onError(Throwable t) {
}
}).build()).get();
Here i have 2 problems , the first one is :
when i run this program i have an error message in the server side it says "Origin cgeck enabled but transport 'greetings' does not support it".
The second one is:
To made a websocket you have 1 endpoint , in this case is "/gs-guide-websocket", and a broker you can subscribe. All data published in that broker will be broadcasted to all programs subscribed to it. In this case that channel to subscribe will be /app/toppic.
I cant figure out how to subscribe my java desktop application to this message broker.
MORE INFO
I can do this with jquery with this code :
<script src="jquery-3.1.1.min.js"></script>
<script src="sockjs.min.js"></script>
<script src="stomp.min.js"></script>
<script type="text/javascript">
var stompClient = null;
function connect() {
var socket = new SockJS('http://192.168.0.23:8080/gs-guide-websocket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/greetings', function (greeting) {
console.log(JSON.parse(greeting.body).content);
});
});
}
function showGreeting(message) {
$("#greetings").append("<tr><td>" + message + "</td></tr>");
}
function disconnect() {
if (stompClient != null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
$( document ).ready(function() {
connect();
});
but i cant reply this in a java desktop class.

RabbitMQ Play Java Akka

I am using Play Framework 2.2.2 and I am implementing a RabbitMQ consumer application using JavaAkka (Akka Actor System). So I have a MainActor that is initialized when the Play application comes up using Global.OnStart functions. The MainActor creates a RabbitMQ channel and then starts a consuming from a queue. Each message in that queue is a name of another queue that has to assigned to another child actor or sub actor that has to start consuming from the queue mentioned in the message. So essentially, I have one MainActor that is subscribed to ONE RabbitMQ queue and several child actors that are created by the Main actor, each of the child actor are subscribed to their own RabbitMQ queues. The problem is that I can't bring up more than 7 child actors for some reason. I suspect it is the while(true) construct in the child actor that waits for messages from RabbitMQ. Here is my implementation:
Main Actor:
import play.Logger;
import com.typesafe.config.ConfigFactory;
import java.io.IOException;
import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.actor.ActorRef;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.QueueingConsumer;
import play.libs.Akka;
import util.RabbitMQConnection;
public class MainActor extends UntypedActor {
#Override
public void onReceive(Object msg) throws Exception {
try{
Connection connection = RabbitMQConnection.getConnection();
Channel channel = connection.createChannel();
String main_queue_name = ConfigFactory.load().getString("rabbitmq.default_queue");
channel.queueDeclare(main_queue_name, false, false, false, null);
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(main_queue_name, true, consumer);
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" [x] Received '" + message + "'");
ActorRef childActor = getContext().actorOf(Props.create(childActor.class));
childActor.tell(message, getSelf());
}
}catch (Exception e){
System.out.println(e.toString());
}
}
}
Child Actor:
import play.Logger;
import com.typesafe.config.ConfigFactory;
import java.io.IOException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import play.libs.Akka;
import play.libs.Json;
import akka.actor.UntypedActor;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.QueueingConsumer;
import util.RabbitMQConnection;
public class childActor extends UntypedActor {
#Override
public void onReceive(Object msg) throws Exception {
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String queue_name = ow.writeValueAsString(msg);
try{
Connection connection = RabbitMQConnection.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(queue_name, false, false, false, null);
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(queue_name, true, consumer);
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
JsonNode jsonMsg = Json.parse(message);
// Call some function to process the message
}
}catch (Exception e){
System.out.println(e.toString());
}
}
}
I think you are not using the Actor correctly in this case. In my opinion you should not have a while(true) inside of a receive method for a given actor. Also, QueueingConsumer was deprecated and the rabbitmq guys recommend to implement you consumer using the interface Consumer or the default no-op implementation DefaultConsumer.
The way I would do it is:
Implement a customized consumer for rabbitmq that will send a message to the actor every time it gets something.
Use that implementation for the main actor. Send the queue name as a message and start a new child actor with the queue name as a constructor field.
Use that implementation for the child actors. Send the message received to the actor and do the JSON parsing in the actor itself.
Some code here: (WARNING: NOT COMPILED OR TEST)
Custom rabbitmq consumer:
public class MyCustomRabbitMQConsumer extends DefaultConsumer {
private ActorRef destinationActor;
public MyCustomRabbitMQConsumer(ActorRef destinationActor) {
this.destinationActor = destinationActor;
}
#Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) {
destinationActor.tell(new String(body));
}
}
Main actor:
import play.Logger;
import com.typesafe.config.ConfigFactory;
import java.io.IOException;
import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.actor.ActorRef;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.QueueingConsumer;
import play.libs.Akka;
import util.RabbitMQConnection;
public class MainActor extends UntypedActor {
private MyCustomRabbitMQConsumer rabbitConsumer;
#Override
public void preStart() {
Connection connection = RabbitMQConnection.getConnection();
Channel channel = connection.createChannel();
String main_queue_name = ConfigFactory.load().getString("rabbitmq.default_queue");
channel.queueDeclare(main_queue_name, false, false, false, null);
rabbitConsumer = new MyCustomRabbitMQConsumer(getSelf());
channel.basicConsume(main_queue_name, true, rabbitConsumer);
}
#Override
public void onReceive(Object msg) throws Exception {
if(msg instanceOf String) {
String queueName = (String) msg;
System.out.println(" [x] Received '" + queueName + "'");
getContext().actorOf(Props.create(childActor.class, queueName));
}
}
}
ChildActor:
import akka.actor.UntypedActor;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.QueueingConsumer;
import util.RabbitMQConnection;
public class ChildActor extends UntypedActor {
private MyCustomRabbitMQConsumer rabbitConsumer;
private String queueName;
public ChildActor(String queueName) {
this.queueName = queueName;
}
#Override
public void preStart() {
Connection connection = RabbitMQConnection.getConnection();
Channel channel = connection.createChannel();
String main_queue_name = ConfigFactory.load().getString("rabbitmq.default_queue");
channel.queueDeclare(queueName, false, false, false, null);
rabbitConsumer = new MyCustomRabbitMQConsumer(getSelf());
channel.basicConsume(queueName, true, rabbitConsumer);
}
#Override
public void onReceive(Object msg) throws Exception {
if(msg instanceOf String) {
String strMsg = (String) msg;
JsonNode jsonMsg = Json.parse(message);
// Call some function to process the message
}
}
}
This should work for n number of actors.

Categories