We have a spring application which is having a dynamic queue listener connecting to queue in rabbitmq. Let's say I have a total of 5 listener consumer connected to 5 queues from my spring application to rabbitmq.
Now if Network fluctuation/failure happens then each time, first one of my 5 connected queues is stopped retrying to rabbitmq.
I have debugged code through spring-amqp class and found out that on creating a connection with rabbitmq (when network failure happens), it fails to connect to it and throw org.springframework.amqp.AmqpIOException particular exception which is not handled in retry function so that that queue is removed from retried queue list.
My Main class:
#Slf4j
#SpringBootApplication(exclude = {ClientAutoConfiguration.class})
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = "com.x.x.repositories")
#EntityScan(basePackages = "com.x.x.entities")
public class Main
{
#PostConstruct
void configuration()
{
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
}
/**
* The main method.
*
* #param args the arguments
*/
public static void main(String[] args)
{
ConfigurableApplicationContext context = SpringApplication.run(Main.class, args);
RabbitMQListenerUtil queueRegisterUtil = context.getBean(RabbitMQListenerUtil.class);
try
{
queueRegisterUtil.registerSpecifiedListenerForAllInstance();
}
catch (Exception e)
{
log.error(e.getMessage(), e);
}
}
}
Class which is used to create 5 consumer/listener
/**
* The Class RabbitMQListenerUtil.
*/
#Component
#Slf4j
public class RabbitMQListenerUtil
{
#Autowired
private ApplicationContext applicationContext;
public void registerSpecifiedListenerForAllInstance()
{
try
{
log.debug("New Listener has been register for instane name : ");
Thread.sleep(5000);
registerNewListener("temp1");
registerNewListener("temp2");
registerNewListener("temp3");
registerNewListener("temp4");
registerNewListener("temp5");
}
catch (Exception e)
{
}
}
/**
* This method will add new listener bean for given queue name at runtime
*
* #param queueName - Queue name
* #return Configurable application context
*/
public void registerNewListener(String queueName)
{
AnnotationConfigApplicationContext childAnnotaionConfigContext = new AnnotationConfigApplicationContext();
childAnnotaionConfigContext.setParent(applicationContext);
ConfigurableEnvironment environmentConfig = childAnnotaionConfigContext.getEnvironment();
Properties listenerProperties = new Properties();
listenerProperties.setProperty("queue.name", queueName + "_queue");
PropertiesPropertySource pps = new PropertiesPropertySource("props", listenerProperties);
environmentConfig.getPropertySources().addLast(pps);
childAnnotaionConfigContext.register(RabbitMQListenerConfig.class);
childAnnotaionConfigContext.refresh();
}
}
Class which create dynamic listener for queue consumer
/**
* The Class RabbitMQListenerConfig.
*/
#Configuration
#Slf4j
#EnableRabbit
public class RabbitMQListenerConfig
{
/** The Constant ALLOW_MESSAGE_REQUEUE. */
private static final boolean ALLOW_MESSAGE_REQUEUE = true;
/** The Constant MULTIPLE_MESSAGE_FALSE. */
private static final boolean MULTIPLE_MESSAGE_FALSE = false;
/**
* Listen.
*
* #param msg the msg
* #param channel the channel
* #param queue the queue
* #param deliveryTag the delivery tag
* #throws IOException Signals that an I/O exception has occurred.
*/
#RabbitListener(queues = "${queue.name}")
public void listen(Message msg, Channel channel, #Header(AmqpHeaders.CONSUMER_QUEUE) String queue, #Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws IOException
{
int msgExecutionStatus = 0;
try
{
String message = new String(msg.getBody(), StandardCharsets.UTF_8);
log.info(message);
}
catch (Exception e)
{
log.error(e.toString());
log.error(e.getMessage(), e);
}
finally
{
ackMessage(channel, deliveryTag, msgExecutionStatus);
}
}
/**
* Ack message.
*
* #param channel the channel
* #param deliveryTag the delivery tag
* #param msgExecutionStatus the msg execution status
* #throws IOException Signals that an I/O exception has occurred.
*/
protected void ackMessage(Channel channel, long deliveryTag, int msgExecutionStatus) throws IOException
{
if (msgExecutionStatus == Constants.MESSAGE_DELETE_FOUND_EXCEPTION)
{
channel.basicNack(deliveryTag, MULTIPLE_MESSAGE_FALSE, ALLOW_MESSAGE_REQUEUE);
}
else
{
channel.basicAck(deliveryTag, MULTIPLE_MESSAGE_FALSE);
}
}
/**
* Bean will create from this with given name.
*
* #param name - Queue name-
* #return the queue
*/
#Bean
public Queue queue(#Value("${queue.name}") String name)
{
return new Queue(name);
}
/**
* RabbitAdmin Instance will be created which is required to create new Queue.
*
* #param cf - Connection factory
* #return the rabbit admin
*/
#Bean
public RabbitAdmin admin(ConnectionFactory cf)
{
return new RabbitAdmin(cf);
}
}
Application log :
https://pastebin.com/NQWdmdTH
I have tested this multiple times and each time my first connected queue is being stopped from connecting .
========================= UPDATE 1=============================
Code to reconnect stopped consumer :
https://pastebin.com/VnUrhdLP
Caused by: java.net.UnknownHostException: rabbitmqaind1.hqdev.india
There is something wrong with your network.
Related
I am trying to consume an AWS queue using Spring boot with JMS, and I am having a problem throwing exceptions in my consumer method.
Every time I try to throw a custom exception in my consumer method, to log into an Aspect, the following message is returned:
errorCause=java.lang.IllegalStateException: No thread-bound request
found: Are you referring to request attributes outside of an actual
web request, or processing a request outside of the originally
receiving thread? If you are actually operating within a web request
and still receive this message, your code is probably running outside
of DispatcherServlet/DispatcherPortlet: In this case, use
RequestContextListener or RequestContextFilter to expose the current
request., errorMessage=Error listener queue,
date=2018-06-29T17:45:26.290, type=InvoiceRefuseConsumer]
I have already created a RequestContextListener bean but I did not succeed.
Could someone tell me what might be causing this error?
Here is my code:
Module 1 - Queue consumer
#Service
public class InvoiceRefuseConsumer extends AbstractQueue implements IQueueConsumer{
#Autowired
private InvoiceRefuseService invoiceRefuseService;
#JmsListener(destination = "${amazon.sqs.queue-to-be-consumed}")
#Override
public void listener(#Payload String message) throws ApplicationException {
try {
//Convert the payload received by the queue to the InvoiceFuseParam object
InvoiceRefuseParam param = convertToPojo(message, InvoiceRefuseParam.class);
// Set the type and reason of the refused invoice
param.setType(InvoiceRefuseType.INVOICE_TREATMENT.getId());
if(param.getReasonCode().equals(InvoiceRefuseTypeOperationType.TYPE_OPERATION_INSERT.getDesc())) {
// Persist data information
invoiceRefuseService.save(param);
} else if(param.getReasonCode().equals(InvoiceRefuseTypeOperationType.TYPE_OPERATION_DELETE.getDesc())) {
// Remove refused invoice
invoiceRefuseService.delete(param.getKeyAccess(), param.getType());
}
} catch(Exception e) {
throw new ApplicationException("Error listener queue", e);
}
}
}
Module 2 - Service operations
#Service
public class InvoiceRefuseService {
/**
* automatically initiates the InvoiceRefuseCrud
*/
#Autowired
private InvoiceRefuseCrud invoiceRefuseCrud;
/**
* automatically initiates the SupplierCrud
*/
#Autowired
private SupplierCrud supplierCrud;
/**
* automatically initiates the SequenceDao
*/
#Autowired
private SequenceDao sequenceDao;
/**
* automatically initiates the InvoiceRefuseDao
*/
#Autowired
private InvoiceRefuseDao invoiceRefuseDao;
/**
* automatically initiates the OrderConsumerService
*/
#Autowired
private OrderConsumerService orderConsumerService;
/**
* automatically initiates the InvoiceOrderService
*/
#Autowired
private InvoiceOrderService invoiceOrderService;
/**
* automatically initiates the BranchWarehouseTypeDao
*/
#Autowired
private BranchWarehouseTypeDao branchWarehouseTypeDao;
/**
* Method created to delete a invoice refuse
* #param key
* #param type
* #throws ApplicationException
*/
#Transactional
public void delete(String key, int type) throws ApplicationException {
try {
// Search for the refused invoices
List<InvoiceRefuseModel> lsInvoiceRefuseModel = invoiceRefuseCrud.findBykeyAccessAndType(key, type);
if(ApplicationUtils.isEmpty(lsInvoiceRefuseModel)){
throw new FieldValidationException(getKey("key.notfound"));
}
// Remove refused invoice and cascate with the the scheduling order
invoiceRefuseCrud.deleteAll(lsInvoiceRefuseModel);
} catch (Exception e) {
throw new ApplicationException(getKey("api.delete.error"), e);
}
}
/**
* Method created to save a new invoice refuse
* #param param
* #throws ApplicationException
*/
#OneTransaction
public void save(InvoiceRefuseParam param) throws ApplicationException {
try {
for (String orderNumber : param.getOrderNumbers()) {
// Verify if the invoice refused key already exists
Optional.ofNullable(invoiceRefuseCrud.findBykeyAccessAndType(param.getKeyAccess(), param.getType()))
.filter(invoiceRefuses -> invoiceRefuses.isEmpty())
.orElseThrow(() -> new ApplicationException(getKey("invoice.alread.exists")));
// Convert to model
InvoiceRefuseModel model = convertToSaveModel(param, orderNumber);
// Save data on database
InvoiceRefuseModel result = invoiceRefuseCrud.save(model);
// Associate new refused invoice with the scheduled order
associateInvoiceRefusedToSchedulingOrder(result);
}
} catch (Exception e) {
throw new ApplicationException(getKey("api.save.error"), e);
}
}
/**
* Method creates to associate a refused invoice to the scheduling order
* #param invoiceRefuseModel
* #throws ApplicationException
*/
public void associateInvoiceRefusedToSchedulingOrder(InvoiceRefuseModel invoiceRefuseModel) throws ApplicationException{
// Search for the scheduled order
List<InvoiceOrderModel> lsInvoiceOrderModel = invoiceOrderService.findByNuOrder(invoiceRefuseModel.getNuOrder());
for (InvoiceOrderModel orderModel : lsInvoiceOrderModel) {
// Verify if its a SAP order
boolean isOrderSap = Optional
.ofNullable(branchWarehouseTypeDao.findByIdBranch(orderModel.getNuReceiverPlant()))
.filter(branch -> branch.getNaLoadPoint() != null)
.isPresent();
if (isOrderSap) {
// Update the order status
invoiceOrderService.updateStatus(orderModel);
}
}
}
/**
* Method created to convert from param to model
* #param param
* #param orderNumber
* #return InvoiceRefuseModel
* #throws ApplicationException
*/
private InvoiceRefuseModel convertToSaveModel(InvoiceRefuseParam param, String orderNumber) throws ApplicationException{
OrderParam orderParam = new OrderParam();
orderParam.getLsOrdeNumber().add(orderNumber);
// Search for SAP orders
OrderDataPojo orderSap = Optional.ofNullable(orderConsumerService.findAll(orderParam))
.filter(ordersSap -> ordersSap.getOrders().size() > 0)
.orElseThrow(() -> new ApplicationException(getKey("ordersap.notfound")));
// Convert to model
InvoiceRefuseModel model = new InvoiceRefuseModel();
model.setNuOrder(orderNumber);
model.setCdCompany(BranchMatrixType.MATRIX.getCdCompany());
model.setDsMessage(param.getReasonDescription());
model.setDtIssue(param.getIssueDate());
model.setKeyAccess(param.getKeyAccess());
model.setNuGuid(param.getGuid());
model.setNuInvoice(param.getInvoiceNumber() + param.getInvoiceSerialNumber());
model.setTsCreation(new Date());
model.setNuInvoiceSerial(param.getInvoiceSerialNumber());
model.setNuIssuerPlant(orderSap.getOrders().stream().map(o -> o.getHeader().getIssuerPlant()).findFirst().get());
model.setNuReceiverPlant(orderSap.getOrders().stream().map(o -> o.getHeader().getReceiverPlant()).findFirst().get());
model.setType(param.getType());
model.setCdInvoiceRefuseMessage(param.getReasonCode());
// Passing these fields is required for refused invoices, but they are not received for notes in treatment
if(param.getType().equals(InvoiceRefuseType.INVOICE_REFUSED.getId())) {
model.setIsEnableReturn(BooleanType.getByBool(param.getIsEnableReturn()).getId());
model.setDtRefuse(param.getRefuseDate());
}
// Search for the issuing supplier
SupplierModel supplierModelIssuer = findSupplier(param.getDocumentIdIssuer());
model.setCdSupplierIssuer(supplierModelIssuer.getCdSupplier());
// Search for the receiver supplier
SupplierModel supplierModelReceiver = findSupplier(param.getDocumentIdIssuer());
model.setCdSupplierReceiver(supplierModelReceiver.getCdSupplier());
// Set the primary key
InvoiceRefuseModelId id = new InvoiceRefuseModelId();
id.setCdInvoiceRefuse(sequenceDao.nextIntValue(SequenceName.SQ_INVOICE_REFUSE));
model.setId(id);
return model;
}
/**
* Method created to search for a supplier
* #param documentId
* #return SupplierModel
* #throws ApplicationException
*/
private SupplierModel findSupplier(String documentId) throws ApplicationException{
// Search for the supplier
SupplierModel model = supplierCrud.findTop1ByNuDocumentIdAndCdCompany(documentId, BranchMatrixType.MATRIX.getCdCompany());
if(model == null){
throw new ApplicationException(getKey("supplier.notfound"));
}
return model;
}
/**
* Method created to find a refused invoice and return the result by page
* #param param
* #param pageable
* #return Page<InvoiceRefuseModel>
* #throws ApplicationException
*/
public Page<InvoiceRefuseModel> findRefuseInvoice(InvoiceRefuseFilterParam param, Pageable pageable) throws ApplicationException {
return invoiceRefuseDao.findRefuseInvoice(param, pageable);
}
/**
* Method created to find a refused invoice and return the result by list
* #param param
* #return List<InvoiceRefuseModel>
* #throws ApplicationException
*/
public List<InvoiceRefuseModel> findRefuseInvoice(InvoiceRefuseFilterParam param) throws ApplicationException {
return invoiceRefuseDao.findRefuseInvoice(param);
}
/**
* Method created to find a refused invoice by order number and return the result by list
* #param nuOrder
* #return List<InvoiceRefuseModel>
*/
public List<InvoiceRefuseModel> findByNuOrder(String nuOrder){
return invoiceRefuseDao.findByNuOrder(nuOrder);
}
}
I have developed what I think is a good solution in Spring Boot and Integration, using the Java DSL, with DirectChannel and QueueChannel beans. This is based upon the example code RouterTests#testMethodInvokingRouter2.
Now I want to move it into ActiveMQ. If I import ActiveMQAutoConfiguration I get an instance of ConnectionFactory. But how do I replace the following beans with the JMS equivalents?:
#Bean(name = "failed-channel")
public MessageChannel failedChannel() {
return new DirectChannel();
}
#Bean(name = "retry-channel")
public MessageChannel retryChannel() {
return new QueueChannel();
}
#Bean(name = "exhausted-channel")
public MessageChannel exhaustedChannel() {
return new QueueChannel();
}
Is there any easy way to do this or am I barking up the wrong tree?
Complete code below
#ContextConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#DirtiesContext
public class RetryRouterTests {
/** Failed download attempts are sent to this channel to be routed by {#link ContextConfiguration#failedDownloadRouting( ) } */
#Autowired
#Qualifier("failed-channel")
private MessageChannel failed;
/** Retry attempts for failed downloads are sent to this channel by {#link ContextConfiguration#failedDownloadRouting( ) }*/
#Autowired
#Qualifier("retry-channel")
private PollableChannel retryChannel;
/** Failed download attempts which will not be retried, are sent to this channel by {#link ContextConfiguration#failedDownloadRouting( ) }*/
#Autowired
#Qualifier("exhausted-channel")
private PollableChannel exhaustedChannel;
/**
* Unit test of {#link ContextConfiguration#failedDownloadRouting( ) } and {#link RetryRouter}.
*/
#Test
public void retryRouting() {
final int limit = 2;
Message<?> message = failed( 0, limit);
for ( int attempt = 0 ; attempt <= limit * 2; attempt++ ){
this.failed.send( message );
if ( attempt < limit){
message = this.retryChannel.receive( );
assertEquals( payload( 0 ) , message.getPayload( ) );
assertNull(this.exhaustedChannel.receive( 0 ) );
}else{
assertEquals( payload( 0 ) , this.exhaustedChannel.receive( ).getPayload( ) );
assertNull( this.retryChannel.receive( 0 ) );
}
}
}
private Message<String> failed( int attempt , int limit ) {
return MessageBuilder
.withPayload( payload( attempt ) )
.setHeader("limit", limit)
.build();
}
private String payload (int attempt){
return "download attempt "+attempt;
}
#Configuration
#Import({/*ActiveMQAutoConfiguration.class,*/ IntegrationAutoConfiguration.class})
public static class ContextConfiguration {
#Bean(name = "failed-channel")
public MessageChannel failedChannel() {
return new DirectChannel();
}
#Bean(name = "retry-channel")
public MessageChannel retryChannel() {
return new QueueChannel();
}
#Bean(name = "exhausted-channel")
public MessageChannel exhaustedChannel() {
return new QueueChannel();
}
/**
* Decides if a failed download attempt can be retried or not, based upon the number of attempts already made
* and the limit to the number of attempts that may be made. Logic is in {#link RetryRouter}.
* <p>
* The number of download attempts already made is maintained as a header {#link #attempts},
* and the limit to the number of attempts is another header {#link #retryLimit} which is originally setup upstream as
* a header by {#link DownloadDispatcher} from retry configuration.
* <p>
* Messages for failed download attempts are listened to on channel {#link #failedChannel()},
* are routed to to {#link #retryChannel()} for another attempt or are routed to {#link #exhaustedChannel()} when there no more retries to be made.
* <p>
* Refer http://stackoverflow.com/questions/34693248/how-to-increment-a-message-header for how to increment the attempts header.
*
* #return the {#link IntegrationFlow} defining retry routing message flows
*/
#Bean
public IntegrationFlow failedDownloadRouting() {
return IntegrationFlows.from( "failed-channel" )
.handle( logMessage ( "failed" ) )
// adds download attempt counter when it is absent, which is the first iteration only
.enrichHeaders( h -> h.header("attempts", new AtomicInteger( 0 ) ) )
// incremented the download attempt counter for every retry
.handle( new GenericHandler<Message<String>>( ) {
#Override
public Object handle( Message<String> payload , Map<String,Object> headers ) {
((AtomicInteger)headers.get( "attempts" )).getAndIncrement();
return payload;
}})
.handle( logMessage ( "incremented" ) )
.route( new RetryRouter( ) )
.get();
}
/**
* Decides if a failed download attempt can be retried or not, based upon the number of attempts already made
* and the limit to the number of attempts that may be made.
* <p>
*/
private static class RetryRouter {
/**
* #param attempts current accumulated number of failed download attempts
* #param limit maximum number of download attempts
* #return String channel name into which the message will be routed
*/
#Router
public String route(#Header("attempts") AtomicInteger attempts , #Header("limit") Integer limit) {
if ( attempts.intValue( ) <= limit.intValue( ) ){
return "retry-channel";
}
return "exhausted-channel";
}
}
}
}
It's not clear what you mean by "replace" in this context.
If you mean channels equivalent to those, which are backed by a JMS queue (for persistence reasons only), then use Jms.channel(cf) for a SubscribableChannel (DirectChannel is a SubscribableChannel) and Jms.pollableChannel(cf) (QueueChannel is a PollableChannel).
For completeness, a PublishSubscribeChannel can be replaced by a Jms.publishSubscribeChannel(cf).
However, if you simply want to send the contents of those channels to Jms for distribution to other systems, it's better to use a Jms.outboundAdapter() subscribed to the DirectChannel or polling the QueueChannel.
JMS-backed channels are not intended for message distribution; they are intended to provide message persistence in order to prevent data loss.
I use Hazelcast as a non-persistent queue between two applications running in a Tomcat.
Problem: QueueListener stops listening to its queue. This means, until a certain point, the following line appears periodically in the log, then it disappears:
LOGGER.debug("No messages on {}, {}", queueName, QueueListener.this.getClass().getSimpleName());
There is no error in the logs. I have several class that extends the QueueListener, all of them listen to a different named queue. One of them just stops and I have no clue why, except one thing: it happens right after handling an item. The descendant class's handle method logs the item I can see that in the logs. Then the "No messages on {queuename}" loglines just disappear. The executor had 2 threads. Both stopped, not sure if at once.
The descendant class's handle method executes a Http request and logs the response. Note that the response did not appear in the logs for the previous two handle call, before the listener stopped.
The descendant class's handle method does not have any catch block so it will not swallow any Exceptions. No exception was logged in the QueueListener.
My question, how to proceed to find the cause of this? Where to look for it?
The application that send messages into this queue runs in the same Tomcat as the one that listens to this queue. Multicast is enabled (see full HazelCast config bellow). There is an other Tomcat that runs on the same host and some other Tomcats running on different hosts, all connecting to this same Hazelcast instance. They're using the same confing.
Hazelcast version: 2.6
QueueListener.java:
package com.mi6.publishers;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
public abstract class QueueListener<T> {
private static final long TIMEOUT = 10000L;
private static final Logger LOGGER = LoggerFactory.getLogger(QueueListener.class);
/**
* queue which is processed
*/
private IQueue<T> queue;
private final String queueName;
#Autowired
private HazelcastInstance instance;
private ExecutorService svc;
private final int threadCount;
private volatile boolean shutdown = false;
/**
* Constructor
*
* #param queueName
* #param threadCount
*/
public QueueListener(String queueName, int threadCount) {
this.queueName = queueName;
this.threadCount = threadCount;
}
/**
* #PostConstuct Start background threads
*/
#PostConstruct
public void init() {
LOGGER.info("Constructing hazelcast listener for {}", getClass().getSimpleName());
if (instance != null) {
queue = instance.getQueue(queueName);
svc = Executors.newFixedThreadPool(threadCount);
for (int i = 0; i < threadCount; i++) {
svc.submit(new Runnable() {
#Override
public void run() {
while (!shutdown) {
try {
T item = queue.poll(TIMEOUT, TimeUnit.MILLISECONDS);
if (item != null) {
handle(item);
} else {
LOGGER.debug("No messages on {}, {}", queueName, QueueListener.this.getClass().getSimpleName());
}
} catch (InterruptedException ex) {
// do nothing if interrupted
} catch (Exception ex) {
LOGGER.error("Error while receiving messages from queue:{}", queueName);
LOGGER.error("Error while receiving messages", ex);
}
}
}
});
}
} else {
throw new IllegalStateException("Hazelcast instance cannot be null");
}
}
/**
* call before stop
*/
#PreDestroy
public void destroy() {
shutdown = true;
if (svc != null) {
svc.shutdown();
}
}
/**
* Event handler
*
* #param item
*/
public abstract void handle(T item);
public String getQueueName() {
return queueName;
}
}
This is how Hazelcast is configured:
#Value("${hazelcast.multicast:True}")
private Boolean hazelcastMulticast;
#Value("${hazelcast.group:groupNameNotSet}")
private String hazelcastGroup;
#Bean(destroyMethod = "shutdown")
public HazelcastInstance hazelcastInstance() {
Config cfg = new Config();
cfg.setInstanceName(hazelcastGroup);
NetworkConfig network = cfg.getNetworkConfig();
network.setPortAutoIncrement(true);
Join join = network.getJoin();
join.getMulticastConfig().setEnabled(hazelcastMulticast);
cfg.getGroupConfig().setName(hazelcastGroup);
cfg.getGroupConfig().setPassword(hazelcastGroup);
QueueConfig sms = new QueueConfig();
sms.setName("some-queue-name1");
cfg.addQueueConfig(sms);
QueueConfig flash = new QueueConfig();
flash.setName("some-queue-name2");
cfg.addQueueConfig(flash);
QueueConfig apns = new QueueConfig();
apns.setName("some-queue-name3");
cfg.addQueueConfig(apns);
QueueConfig gcm = new QueueConfig();
gcm.setName("some-queue-name4");
cfg.addQueueConfig(gcm);
return Hazelcast.newHazelcastInstance(cfg);
}
Disclaimer - this is for an assignment, I'm trying to figure out why my code is giving me an error.
Background:
So the assignment is basically to make edits to SimpleChat, a client-server framework for which the teacher has given us the code. I am trying to implement a chat console from the server side, and implement actions that begin with hashtags. So I have a "handleMessageFromServer" method in my server file (called "EchoServer"), and am trying to call it from the server console (aptly named "Server Console"). I'll put the relevant code below, and explain it:
I'll edit what I've put, I'll put the whole files with comments.
Below is the "EchoServer" file. The more important bits relevant to my question are the "handleMessageFromServer" method, which I am trying to call from the other file and pass in information, as well as the EchoServer constructor, which I am using in the other file to create an instance.
import java.io.*;
import ocsf.server.*;
import common.*;
/**
* This class overrides some of the methods in the abstract
* superclass in order to give more functionality to the server.
*
* #author Dr Timothy C. Lethbridge
* #author Dr Robert Laganière
* #author François Bélanger
* #author Paul Holden
* #version July 2000
*/
public class EchoServer extends AbstractServer
{
//Class variables *************************************************
/**
* The default port to listen on.
*/
final public static int DEFAULT_PORT = 5555;
//Constructors ****************************************************
/*
* An interface type variable, will allow the implementation of the
* Display method in the client.
*
*/
public ChatIF server;
/**
* Constructs an instance of the echo server.
*
* #param port The port number to connect on.
*/
public EchoServer(int port, ChatIF server)
{
super(port);
this.server = server;
}
//Instance methods ************************************************
/**
* This method handles any messages received from the client.
*
* #param msg The message received from the client.
* #param client The connection from which the message originated.
*/
public void handleMessageFromClient
(Object msg, ConnectionToClient client)
{
System.out.println("Message received: " + msg + " from " + client);
this.sendToAllClients(msg);
}
/**
* This method handles any messages received from the server console.
*
* #param msg The message received from the server.
* #param server The connection from which the message originated.
*/
public void handleMessageFromServer(String message)
{
if (message.charAt(0) == '#')
{
serverCommand(message);
}
else
{
server.display(message);
this.sendToAllClients("SERVER MSG> " + message);
}
}
/*This method allows us to run commands from the server console
*
*/
public void serverCommand(String command)
{
if (command.equalsIgnoreCase("quit")) System.exit(0); /////Shuts the system down
else if (command.equalsIgnoreCase("#stop")) stopListening(); ///Stops listening for connections
else if (command.equalsIgnoreCase("#close")) ///////closes all connections
{
try close();
catch(IOException ex) {
server.display("Could not close connection");
}
}
else if (command.toLowerCase().startsWith("#setport")) /////Sets port when not listening
{
if (!isListening() && getNumberOfClients() == 0)
{
//////If there are no connected clients, and
//////we're not listening for new ones, we can
////assume that the server is closed (close() has been
////called.
String portNum = command.substring(s.indexOf("<") + 1)
portNum = portnum.substring(0, s.indexOf(">"));
int num = Integer.parseInt(portNum);
setPort(num);
server.display("The server port has been changed to port" + getPort());
}
else
{
server.display("Port cannot be changed");
}
else if (command.equalsIgnoreCase("#start")) ///////starts listening for clients if not already
{
if (!isListening())
{
try listen();
catch (Exception ex)
{
server.display("Could not listen for clients!");
}
}
else
{
server.display("Already listening for clients");
}
}
else if (message.equalsIgnoreCase("#getport")) //////gets the port number
{
server.display("Current port: " + Integer.toString(getPort()));
}
}
}
/**
* This method overrides the one in the superclass. Called
* when the server starts listening for connections.
*/
protected void serverStarted()
{
System.out.println
("Server listening for connections on port " + getPort());
}
/**
* This method overrides the one in the superclass. Called
* when the server stops listening for connections.
*/
protected void serverStopped()
{
System.out.println
("Server has stopped listening for connections.");
}
//Class methods ***************************************************
/**
* This method is responsible for the creation of
* the server instance (there is no UI in this phase).
*
* #param args[0] The port number to listen on. Defaults to 5555
* if no argument is entered.
*/
public static void main(String[] args)
{
int port = 0; //Port to listen on
try
{
port = Integer.parseInt(args[0]); //Get port from command line
}
catch(Throwable t)
{
port = DEFAULT_PORT; //Set port to 5555
}
EchoServer sv = new EchoServer(port);
try
{
sv.listen(); //Start listening for connections
}
catch (Exception ex)
{
System.out.println("ERROR - Could not listen for clients!");
}
}
}
//End of EchoServer class
Below is the ServerConsole file, where I am creating an instance of the EchoServer class so that I may pass in information. The purpose of this file is to create an environment where the server side can send text for the SimpleChat application. The text then gets passed back to the EchoServer class, where it can be used for commands (all the commands begin with hashtags). I am currently getting an error in the "accept" method, when trying to call echoServer.handleMessageFromServer(message). The error is "The method handleMessageFromServer(String) is undefined for the type EchoServer".
import java.io.*;
import client.*;
import common.*;
/**
* This class constructs the UI for a chat client. It implements the
* chat interface in order to activate the display() method.
* Warning: Some of the code here is cloned in ServerConsole
*
* #author François Bélanger
* #author Dr Timothy C. Lethbridge
* #author Dr Robert Laganière
* #version July 2000
*/
public class ServerConsole implements ChatIF
{
//Class variables *************************************************
/**
* The default port to connect on.
*/
final public static int DEFAULT_PORT = 5555;
//Instance variables **********************************************
/**
* The instance of the server that created this ConsoleChat.
*/
//Constructors ****************************************************
EchoServer echoServer;
/**
* Constructs an instance of the ClientConsole UI.
*
* #param host The host to connect to.
* #param port The port to connect on.
*/
public ServerConsole(int port)
{
this.echoServer = new EchoServer(port);
}
//Instance methods ************************************************
/**
* This method waits for input from the console. Once it is
* received, it sends it to the client's message handler.
*/
public void accept()
{
try
{
BufferedReader fromConsole =
new BufferedReader(new InputStreamReader(System.in));
String message;
while (true)
{
message = fromConsole.readLine();
///////////////ADDED FOR E50B MA/ND
echoServer.handleMessageFromServer(message);
///////////////ADDED FOR E50B MA/ND
}
}
catch (Exception ex)
{
System.out.println
("Unexpected error while reading from console!");
}
}
/**
* This method overrides the method in the ChatIF interface. It
* displays a message onto the screen.
*
* #param message The string to be displayed.
*/
public void display(String message)
{
System.out.println(message);
}
//Class methods ***************************************************
/**
* This method is responsible for the creation of the Client UI.
*
* #param args[0] The host to connect to.
*/
public static void main(String[] args)
{
int port = 0; //The port number
try
{
String host = args[0];
}
catch(ArrayIndexOutOfBoundsException e)
{
String host = "localhost";
}
ServerConsole chat= new ServerConsole(DEFAULT_PORT);
chat.accept(); //Wait for console data
}
}
//End of ConsoleChat class
I think I might not be constructing the instance right, but any help is greatly appreciated guys!
Your echoserver class has the following contructor:
public EchoServer(int port, ChatIF server)
{
super(port);
this.server = server;
}
note it takes in two parameters.
your call to the EchoServer is however only injecting a port
this.echoServer = new EchoServer(port);
Without seeing all your code my guess would be that Echoserver extends some other Server class that does not have the method you want.
I'm doing a POC to push Data from a (Java) server, though LCDS 3.1 's DataService using RTMP.
Configuration is OK. Adobe Air client DataMessage to server (+Assembler saving in DB) : OK
I found lots of examples with AsyncMessage, but as This is an RTMP destination through a DataService service, I must send a DataMessage.
Appearently, there are some bugs (or I am missing things/good API doc!).
So please, could you help me?
Here is the code that does the push. The key method is doPush()
package mypackage.lcds.service.ds.impl;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import mypackage.lcds.service.ds.DataPushService;
import mypackage.model.dto.AbstractDto;
import mypackage.model.exception.DsPushException;
import flex.data.messages.DataMessage;
import flex.messaging.MessageBroker;
import flex.messaging.messages.Message;
import flex.messaging.services.MessageService;
import flex.messaging.util.UUIDUtils;
/**
* Implementation of {#link DataPushService}.
*/
// see http://forums.adobe.com/thread/580667
// MessageCLient :
// http://livedocs.adobe.com/livecycle/8.2/programLC/programmer/lcds/help .html?content=lcconnections_2.html
#Service
public final class DataPushServiceImpl implements DataPushService {
private static final Logger LOG = Logger.getLogger(DataPushServiceImpl.class);
/**
* Destination name for Data-service.<br>
* See data-management-config.XML.
*/
private static final String DESTINATION_NAME__POC_DS_XCHANGE = "poc-ds-xchange";
/**
* See data-management-config.XML.
*/
private static final String PUSH_DTO_SERVICE__NAME = "data-service";
/**
* set "manually" by Spring (contexts workaround; not autowired).
*/
private MessageBroker messageBroker = null;
/**
* Does the push of a single DTO.<br>
* Only subscriberId's that are {#link Long} values will be used. Other Id's do not get a Message sent.
*
* #param dto
* {#link AbstractDto} object.
* #param subscriberIds
* {#link Set} of LCDS Message subscriber IDs {#link Long}. If null, sends to all connected clients.
*
* #throws DsPushException
* if any error
*/
#SuppressWarnings("unchecked")
private void doPush(final AbstractDto dto, final Set<Long> subscriberIds)
throws DsPushException {
Set<?> ids = new HashSet<Object>();
// obtain message service by means of message broker
MessageService messageService = this.getMessageService();
DataMessage message = this.createMessage(dto, messageService);
// fill ids
if ((subscriberIds == null) || (subscriberIds.isEmpty())) {
if (LOG.isDebugEnabled()) {
LOG.debug("Sending message all currently connected subscriberIds ");
}
Set idsFromDS = messageService.getSubscriberIds(message, true);
if ((idsFromDS != null) && (!idsFromDS.isEmpty())) {
CollectionUtils.addAll(ids, idsFromDS.iterator());
}
} else {
CollectionUtils.addAll(ids, subscriberIds.iterator());
}
if (ids.isEmpty()) {
if (LOG.isDebugEnabled()) {
LOG.debug("No subscriberId to send the Message to.");
LOG.debug("Known subscribers : " + messageService.getSubscriberIds(message, true).toString());
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Sending message to subscriberIds : " + subscriberIds.toString());
LOG.debug("Known subscribers : " + messageService.getSubscriberIds(message, true).toString());
}
// send messages to all subscriberIds 1 by 1
Object responsePayload = null;
boolean isSent = false;
for (Object id : ids) {
if (id instanceof Long) {
try {
message.setHeader(Message.DESTINATION_CLIENT_ID_HEADER, id);
if (LOG.isDebugEnabled()) {
LOG.debug("Sending LCDS DataMessage to subscriber [" + id + "] \n" + message.toString(2));
}
responsePayload = messageService.serviceMessage(message, true);
// no exception ==> means OK?
// TODO TEST retuned payload
isSent = true;
} catch (Exception e) {
LOG.error("Error while sending message to subscriberId " + id, e);
isSent = false;
} finally {
if (LOG.isDebugEnabled()) {
LOG.debug("Message sent to '" + String.valueOf(id) + "' : " + String.valueOf(isSent));
}
}
} else if (LOG.isDebugEnabled()) {
LOG.debug("Avoiding subscriber ID (not a Long value) : " + String.valueOf(id));
}
}
}
}
/**
* {#inheritDoc}
*
* #see DataPushService#pushToAllClients(AbstractDto)
*/
// TODO test : if client is not connected, does LCDS record it for later (offline mode on the server?)
public void pushToAllClients(final AbstractDto dto) throws DsPushException {
this.doPush(dto, null);
}
public void pushTo1Client(AbstractDto dto, Long subscriberId) throws DsPushException {
Set<Long> subscriberIds = new HashSet<Long>();
subscriberIds.add(subscriberId);
this.doPush(dto, subscriberIds);
}
/**
* {#inheritDoc}<br>
* subscriberIds refer to the 'clientId' set by the client app when it subscribes to the DS destination.
*
* #see DataPushService#pushToClients(AbstractDto, Set)
*/
public void pushToClients(final AbstractDto dto, final Set<Long> subscriberIds) throws DsPushException {
this.doPush(dto, subscriberIds);
}
#SuppressWarnings("unchecked")
private DataMessage createMessage(final AbstractDto dto, final MessageService messageService) {
DataMessage msg = new DataMessage();
msg.setClientId(getServerId());
msg.setTimestamp(System.currentTimeMillis());
msg.setMessageId(UUIDUtils.createUUID(true));
msg.setCorrelationId(msg.getMessageId()); // TODO OK messageId == CorrelationId ?
msg.setDestination(DESTINATION_NAME__POC_DS_XCHANGE);
msg.setBody(dto);
msg.setOperation(DataMessage.CREATE_AND_SEQUENCE_OPERATION); // TODO OK operation?
Map identity = new HashMap(2);
// see data-management-config.xml
identity.put("id", dto.getId());
msg.setIdentity(identity);
// FIXME set priority. How?
if (LOG.isDebugEnabled()) {
LOG.debug("LCDS DataMessage created : \n" + msg.toString(2));
}
return msg;
}
private Object getServerId() {
// FIXME OK?
return "X-BACKEND";
}
/**
* Get the current {#link MessageBroker}'s service layer.
*
* #return {#link MessageService} to use for push data
*/
private MessageService getMessageService() {
if (LOG.isDebugEnabled()) {
LOG.debug("Getting MessageBroker's DataService service ");
}
// Was : return (MessageService) MessageBroker.getMessageBroker(null).getService(PUSH_DTO_SERVICE__NAM E);
return (MessageService) this.messageBroker.getService(PUSH_DTO_SERVICE__NAME);
}
/**
* Set the messageBroker. For SPring.
*
* #param messageBroker
* the messageBroker to set
*/
public void setMessageBroker(final MessageBroker messageBroker) {
this.messageBroker = messageBroker;
}
}
NOTE : the messagebroker is set once through Spring. It works for this POC.
I have a Servlet that saves a DTO to the DB and then tries to push it through the service. All seems OK, but I get a NullPointerException (NPE).
Here is the Tomcat 6 LOG (it sends to subscriberID '99' ):
LCDS DataMessage created :
Flex Message (flex.data.messages.DataMessage)
operation = create_and_sequence
id = {id=3203}
clientId = X-BACKEND
correlationId = 7E6C3051-FA0F-9183-4745-B90ACACD71EA
destination = poc-ds-xchange
messageId = 7E6C3051-FA0F-9183-4745-B90ACACD71EA
timestamp = 1297412881050
timeToLive = 0
body = mypackage.model.dto.XchangeDto[id=3203[clientId=2[userId=123456[text= InterActionServlet Test]
09:28:01,065 DEBUG [impl.DataPushServiceImpl] Sending message to subscriberIds : [99]
09:28:01,065 DEBUG [impl.DataPushServiceImpl] Known subscribers : [99]
09:28:01,065 DEBUG [impl.DataPushServiceImpl] Sending LCDS DataMessage to subscriber [99]
Flex Message (flex.data.messages.DataMessage)
operation = create_and_sequence
id = {id=3203}
clientId = X-BACKEND
correlationId = 7E6C3051-FA0F-9183-4745-B90ACACD71EA
destination = poc-ds-xchange
messageId = 7E6C3051-FA0F-9183-4745-B90ACACD71EA
timestamp = 1297412881050
timeToLive = 0
body = mypackage.model.dto.XchangeDto[id=3203[clientId=2[userId=123456[text= InterActionServlet Test]
hdr(DSDstClientId) = 99
09:28:02,456 ERROR [impl.DataPushServiceImpl] Error while sending message to subscriberId 99
java.lang.NullPointerException
at flex.data.adapters.JavaAdapter.invokeAssemblerSync(JavaAdapter.java:1 741)
at flex.data.adapters.JavaAdapter.invokeBatchOperation(JavaAdapter.java: 1630)
at flex.data.adapters.JavaAdapter.invoke(JavaAdapter.java:658)
at flex.messaging.services.MessageService.serviceMessage(MessageService. java:318)
at flex.messaging.services.MessageService.serviceMessage(MessageService. java:233)
at mypackage.lcds.service.ds.impl.DataPushServiceImpl.doPush(DataPushSer viceImpl.java:142)
at mypackage.lcds.service.ds.impl.DataPushServiceImpl.pushTo1Client(Data PushServiceImpl.java:178)
at mypackage.servlet.InteractionServlet.push(InteractionServlet.java:75)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker. doInvokeMethod(HandlerMethodInvoker.java:421)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker. invokeHandlerMethod(HandlerMethodInvoker.java:136)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandle rAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:326)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandle rAdapter.handle(AnnotationMethodHandlerAdapter.java:313)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(Dispatch erServlet.java:875)
at org.springframework.web.servlet.DispatcherServlet.doService(Dispatche rServlet.java:807)
at org.springframework.web.servlet.FrameworkServlet.processRequest(Frame workServlet.java:571)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServl et.java:501)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:690)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(Appl icationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationF ilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperV alve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextV alve.java:175)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.j ava:128)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.j ava:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineVal ve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.jav a:263)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java :844)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.proce ss(Http11Protocol.java:584)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:44 7)
at java.lang.Thread.run(Unknown Source)
09:28:02,472 DEBUG [impl.DataPushServiceImpl] Message sent to '99' : false
==> what am I doing wrong?
I cannot trace the code (I do not have the source), but the exception thrown is just not helping at all.
Am I missing a header to set?
Thank you so much for your help,
For the records, I found it ;o)
The base to know is that the DataServiceTransaction api is A MUST-HAVE, if you're using LC DataService.
For deails see Adobe forums thread
for the records here, here's my working (basic) code :
/**
* ASSUMPTION : the client Flex/Air apps set the desired userId (= filter) as a fillParameter of the
* DataService.fill() method. This will filter output based on {#link AbstractDto#getUserId()}.
*/
#Service
public final class DataPushServiceImpl implements DataPushService {
private static final Logger LOG = Logger.getLogger(DataPushServiceImpl.class);
/* *********** V2 : DataServiceTransaction.createItem() ********* */
/**
* Does the push of a single DTO.
*
* #param dto
* {#link AbstractDto} object. Contains the {#link AbstractDto#getUserId()} that is used by clients to
* filter data in the DataService.fill() method (used by the Assembler).
*
* #throws DsPushException
* if any error
*/
private boolean doPushViaTransaction(final AbstractDto dto) throws DsPushException {
if (LOG.isDebugEnabled()) {
LOG.debug("Sending message through DataServiceTransaction (see userId field) : " + dto.toString());
}
// One MUST instantiate a DataServiceTransaction to be able to send anything (NullPointerException)
DataServiceTransaction dtx = null;
boolean isOwnerOfTx = false;
boolean isSent = false;
try {
// if already in an Assembler, we do have a tx ==> no commit nor rollback!
dtx = DataServiceTransaction.getCurrentDataServiceTransaction();
if (dtx == null) {
// new one, no JTA ==> ourselves : commit or rollback
isOwnerOfTx = true;
//MessageBroker instantiated with SpringFlex is auto-named
dtx = DataServiceTransaction.begin("_messageBroker", false);
}
isSent = this.doTransactionSend(dto, dtx);
} catch (Exception e) {
// Log exception, but no impact on the back-end business ==> swallow Exception
LOG.error("Could not send the creation to LCDS", e);
if (isOwnerOfTx) {
dtx.rollback();
}
} finally {
try {
if (isOwnerOfTx && (dtx != null)) {
dtx.commit();
}
} catch (Exception e) {
// swallow
LOG.error("Could not send the creation to LCDS (#commit of the DataServiceTransaction)", e);
}
}
return isSent;
}
private boolean doTransactionSend(final AbstractDto dto, final DataServiceTransaction dtx) {
boolean isSent = false;
if (dto == null) {
LOG.error("The given DTO is null! Nothing happens");
} else {
try {
dtx.createItem(FlexUtils.DESTINATION_NAME__POC_DS, dto);
isSent = true; // no problem
} catch (Exception e) {
// Log exception, but no impact on the business
LOG.error("Could not send the creation to LCDS for DTO " + dto.toString(), e);
} finally {
if (LOG.isDebugEnabled()) {
LOG.debug("DTO : " + dto.toString() + "\n sent : " + String.valueOf(isSent));
}
}
}
return isSent;
}
//implementation of DataPushService interface
/**
* {#inheritDoc}
*
* #see DataPushService#pushNewDTo(AbstractDto, java.lang.Long)
*/
#Transactional(rollbackFor = DsPushException.class)
public boolean pushNewDTo(final AbstractDto dto, final Long subscriberId) throws DsPushException {
return this.doPushViaTransaction(dto);
}
}
Enjoy and thank you for your time!
G.