Having the following code snippet:
try
{
UpdateCommand updateCommand = new UpdateCommand
{
Name = "Update"
};
await sendEndpoint.Send(updateCommand);
}
catch (BrokerUnreachableException ex)
{
}
catch (Exception ex)
{
}
When calling Send method and when there is no connection with RabbitMQ then exception is not being thrown. Is it supposed to work like this ?
I've tried to ConnectSendObserver to my ReceiveEndpoint like so:
ISendObserver sendObserver = new UpdateSendObserver();
cfg.ReceiveEndpoint(EventBusConstants.UpdateQueue, c => {
c.ConfigureConsumer<UpdateCommandConsumer> (ctx);
cfg.ConnectSendObserver(sendObserver);
});
But it doesn't hit any of the PreSend, PostSend, SendFault methods when there is no connection with RabbitMQ.
As explained in the GitHub Discussion, you can cancel the call to Send/Publish using a CancellationToken. The transport uses a retry policy if a connection to the broker is not available.
Related
In RabbitMQ,I have a failure queue, in which I have all the failed messages from different Queues. Now I want to give the functionality of 'Retry', so that administrator can again move the failed messages to their respective queue. The idea is something like that:
Above diagram is structure of my failure queue. After click on Retry link, message should move into original queue i.e. queue1, queue2 etc.
If you are looking for a Java code to do this, then you have to simply consume the messages you want to move and publish those messages to the required queue. Just look up on the Tutorials page of rabbitmq if you are unfamiliar with basic consuming and publishing operations.
It's not straight forward consume and publish. RabbitMQ is not designed in that way. it takes into consideration that exchange and queue both could be temporary and can be deleted. This is embedded in the channel to close the connection after single publish.
Assumptions:
- You have a durable queue and exchange for destination ( to send to)
- You have a durable queue for target ( to take from )
Here is the code to do so:
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.QueueingConsumer;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
public object shovelMessage(
String exchange,
String targetQueue,
String destinationQueue,
String host,
Integer port,
String user,
String pass,
int count) throws IOException, TimeoutException, InterruptedException {
if(StringUtils.isEmpty(exchange) || StringUtils.isEmpty(targetQueue) || StringUtils.isEmpty(destinationQueue)) {
return null;
}
CachingConnectionFactory factory = new CachingConnectionFactory();
factory.setHost(StringUtils.isEmpty(host)?internalHost.split(":")[0]:host);
factory.setPort(port>0 ? port: Integer.parseInt(internalPort.split(":")[1]));
factory.setUsername(StringUtils.isEmpty(user)? this.user: user);
factory.setPassword(StringUtils.isEmpty(pass)? this.pass: pass);
Channel tgtChannel = null;
try {
org.springframework.amqp.rabbit.connection.Connection connection = factory.createConnection();
tgtChannel = connection.createChannel(false);
tgtChannel.queueDeclarePassive(targetQueue);
QueueingConsumer consumer = new QueueingConsumer(tgtChannel);
tgtChannel.basicQos(1);
tgtChannel.basicConsume(targetQueue, false, consumer);
for (int i = 0; i < count; i++) {
QueueingConsumer.Delivery msg = consumer.nextDelivery(500);
if(msg == null) {
// if no message found, break from the loop.
break;
}
//Send it to destination Queue
// This repetition is required as channel looses the connection with
//queue after single publish and start throwing queue or exchange not
//found connection.
Channel destChannel = connection.createChannel(false);
try {
destChannel.queueDeclarePassive(destinationQueue);
SerializerMessageConverter serializerMessageConverter = new SerializerMessageConverter();
Message message = new Message(msg.getBody(), new MessageProperties());
Object o = serializerMessageConverter.fromMessage(message);
// for some reason msg.getBody() writes byte array which is read as a byte array // on the consumer end due to which this double conversion.
destChannel.basicPublish(exchange, destinationQueue, null, serializerMessageConverter.toMessage(o, new MessageProperties()).getBody());
tgtChannel.basicAck(msg.getEnvelope().getDeliveryTag(), false);
} catch (Exception ex) {
// Send Nack if not able to publish so that retry is attempted
tgtChannel.basicNack(msg.getEnvelope().getDeliveryTag(), true, true);
log.error("Exception while producing message ", ex);
} finally {
try {
destChannel.close();
} catch (Exception e) {
log.error("Exception while closing destination channel ", e);
}
}
}
} catch (Exception ex) {
log.error("Exception while creating consumer ", ex);
} finally {
try {
tgtChannel.close();
} catch (Exception e) {
log.error("Exception while closing destination channel ", e);
}
}
return null;
}
To requeue a message you can use the receiveAndReply method. The following code will move all messages from the dlq-queue to the queue-queue:
do {
val movedToQueue = rabbitTemplate.receiveAndReply<String, String>(dlq, { it }, "", queue)
} while (movedToQueue)
In the code example above, dlq is the source queue, { it } is the identity function (you could transform the message here), "" is the default exchange and queue is the destination queue.
I also have implemented something like that, so I can move messages from a dlq back to processing. Link: https://github.com/kestraa/rabbit-move-messages
Here is a more generic tool for some administrative/supporting tasks, the management-ui is not capable of.
Link: https://github.com/bkrieger1991/rabbitcli
It also allows you to fetch/move/dump messages from queues even with a filter on message-content or message-headers :)
Spring batch documentation recommends using the JmsItemReader, which is a wrapper around the JMSTemplate. However, I have discovered that the JMSTemplate has some issues - see http://activemq.apache.org/jmstemplate-gotchas.html .
This post came to my attention only because the queue was appearing to disappear before I could actually read the data of of it. The opportunity to miss messages seems like a fairly significant issue to me.
For consumers atleast try using DefaultMessageListenerContainer coupled with a SingleConnectionFactory or any such connection factory , it not need a scheduler to wake them up.there are log of examples explaining this , this one is really good in explaining stuff
http://bsnyderblog.blogspot.com/2010/05/tuning-jms-message-consumption-in.html
Here is the solution I ended up with. Since the query was about the "cleanest" way to listen to JMS from within a spring-batch step, I'm going to leave the question open for a while longer just in case there's a better way.
If someone can figure out why the code isn't formatting correctly, please let me know how to fix it.
1. In the a job listener, implement queue setup and teardown inside the beforeJob and afterJob events, respectively:
public void beforeJob(JobExecution jobExecution) {
try {
jobParameters = jobExecution.getJobParameters();
readerConnection = connectionFactory.createConnection();
readerConnection.start();
} catch (JMSException ex) {
// handle the exception as appropriate
}
}
public void afterJob(JobExecution jobExecution) {
try {
readerConnection.close();
} catch (JMSException e) {
// handle the exception as appropriate
}
}
2. In the reader, implement the StepListener and beforeStep / afterStep methods.
public void beforeStep(StepExecution stepExecution) {
this.stepExecution = stepExecution;
this.setJobExecution(stepExecution.getJobExecution());
try {
this.connection = jmsJobExecutionListener.getReaderConnection();
this.jmsSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
this.messageConsumer = jmsSession.createConsumer(jmsJobExecutionListener.getQueue());
}
catch (JMSException ex)
{
// handle the exception as appropriate
}
}
public ExitStatus afterStep(StepExecution stepExecution) {
try {
messageConsumer.close();
jmsSession.close();
} catch (JMSException e) {
// handle the exception as appropriate
}
return stepExecution.getExitStatus();
}
3. Implement the read() method:
public TreeModel<SelectedDataElementNode> read() throws Exception,
UnexpectedInputException, ParseException,
NonTransientResourceException {
Object result = null;
logger.debug("Attempting to receive message on connection: ", connection.toString());
ObjectMessage msg = (ObjectMessage) messageConsumer.receive();
logger.debug("Received: {}", msg.toString());
result = msg.getObject();
return result;
}
4. Add the listeners to the Spring Batch context as appropriate:
<batch:job id="doStuff">
<batch:listeners>
<batch:listener ref="jmsJobExecutionListener" />
</batch:listeners>
... snip ...
<batch:step id="step0003-do-stuff">
<batch:tasklet transaction-manager="jtaTransactionManager"
start-limit="100">
<batch:chunk reader="selectedDataJmsReader" writer="someWriter"
commit-interval="1" />
</batch:tasklet>
<batch:listeners>
<batch:listener ref="selectedDataJmsReader" />
</batch:listeners>
</batch:step>
</batch:job>
I'm facing problems to send e-mails with Java Mail on Glashfish 3.1.1.
The server doesn't throw any Exception, just send an empty message with only header to recipient.
Running Java Mail without the Glashfish, everything works fine.
public void sendHtmlMessage(String subject, String html){
// Creates a email Session and
// and Authenticator attached to it
Session session = getMailSession();
try{
MimeMessage message = createMessage(session, subject, MailSender.MIME_Type.TEXT_HTML, html); // creates message
transportSMTPMessage(session, message); // transport message
} catch(AddressException e){ log("Internet address is invalid!"); e.printStackTrace(); }
catch(NoSuchProviderException e){ log("Host is invalid!"); e.printStackTrace(); }
catch(MessagingException e){ log("Message is invalid!"); e.printStackTrace(); }
catch(Exception e){ log("Gereric Exception!"); e.printStackTrace(); }
}
// Helper to obtain Mail Session
private Session getMailSession(){
Properties props = new Properties();
return Session.getInstance(props,
new Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(userName, password);
}
});
}
// Helper to create MimeMessage
private MimeMessage createMessage(Session session, String subject, MailSender.MIME_Type mime, String content)
throws AddressException, MessagingException{
// Some code to create the message...
message.saveChanges();
return message;
}
// Helper to transpot the message thru the SMTP protocol
private void transportSMTPMessage(Session session, Message message)
throws NoSuchProviderException, MessagingException{
// Creates the transport connection and
// Send the message
Transport transport = session.getTransport("smtp");
transport.connect(host, userName, password);
transport.sendMessage(message, message.getAllRecipients());
transport.close();
}
I think everything's fine with the code, I just don't understand, why it doesn't work on Glassfish?
I'd be grateful if someone help me.
Thanks in advance.
Call session.setDebug(true) in your application after creating the Session. Then look at the server log file to see if the JavaMail debug output has any clues about what's going wrong. I'm assuming you're not getting any exceptions.
I have the next problem, I get a context from the LDAP setting Up JNDI Environment properties for the InitialContext as it showed below.
Then I restart the ldap server, and each time I try to get again the InitialContext, the next exception is thrown.
Code to get the Context.
Context ctx = null;
Hashtable ht = new Hashtable();
ht.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
ht.put(Context.PROVIDER_URL,"t3://localhost:7001");
ht.put(Context.SECURITY_PRINCIPAL,"USER1");
ht.put(Context.CREDENTIALS,"PASSWORD1");
try {
ctx = new InitialContext(ht);
}
catch (NamingException e) {
}
finally {
try {ctx.close();
}
catch (Exception e) {
// a failure occurred
}
}
Exception:
java.security.PrivilegedActionException: javax.naming.ConfigurationException: Call to NamingManager.getObjectInstance() failed:
[Root exception is java.lang.SecurityException:
[Security:090398]Invalid Subject: principals=[ADMIN]]; remaining name ''
BEA-090398
You are not providing a proper DN to authenticate with:
ht.put(Context.SECURITY_PRINCIPAL,"USER1");
You should have cn=user1,ou=something,dc=something,dc=com or the like.
I use WCF for a client server system.
When I add a service reference to IService on the server, a proxy class ServiceClient is generated.
My code looks like the following:
ServiceClient client = new ServiceClient();
try
{
client.Operation1();
}
catch(Exception ex)
{
// Handle Exception
}
try
{
client.Operation2();
}
catch(Exception ex)
{
// Handle Exception
}
The problem is that if there is a communication exception in the first call, the client's state changes to Faulted, and I don't know how to reopen it to make the second call. Is there a way to reopen it? or should I create a new one and replace the instance (It doesn't seem like an elegant way)?
Once a an ICommunicationObject (your WCF client object) is in a faulted state, the only way to "re-open" it is to create a new one.
ServiceClient client = new ServiceClient();
try
{
client.Operation1();
}
catch(Exception ex)
{
if (client.State == CommunicationState.Faulted)
{
client.Abort();
client = new ServiceClient();
}
}
try
{
client.Operation2();
}
catch(Exception ex)
{
// Handle Exception
}
If there is a communication exception on the first call which is causing a faulted state, you have to basically "re-create" the WCF client proxy. In your example I would probably do something like:
if (client.State == CommunicationState.Faulted)
client = new ServiceClient();
This would allow you to "re-open" the connection if it is faulted. It may seem a little overkill, but if you're getting a communication exception on the client side, there's probably something else going on (i.e.: server dead? server not responding?)
Good luck
Agree with the last answers, once failed, you need to abort. We use a combination of lambdas and a method like the following to do this:
public static void Use<TServiceInterface>(TServiceInterface proxy, Action handler)
{
Type proxyType = typeof(TServiceInterface);
IClientChannel channel = (IClientChannel)proxy;
try
{
handler();
_logSource.Log(LogLevel.Debug, string.Format("Closing client channel for '{0}' ...", proxyType.Name));
channel.Close();
_logSource.Log(LogLevel.Debug, string.Format("Client channel for '{0}' closed.", proxyType.Name));
}
catch
{
if (channel.State == CommunicationState.Faulted)
{
_logSource.Log(LogLevel.Debug, string.Format("Aborting client channel for '{0}' ...", proxyType.Name));
channel.Abort();
_logSource.Log(LogLevel.Debug, string.Format("Client channel for '{0}' aborted.", proxyType.Name));
}
else
{
_logSource.Log(LogLevel.Debug, string.Format("Closing client channel for '{0}' ...", proxyType.Name));
channel.Close();
_logSource.Log(LogLevel.Debug, string.Format("Client channel for '{0}' closed.", proxyType.Name));
}
throw;
}
}
This is a slight modification of a solution that is already on the .net, but it works great for handling proxies. You then can put multiple service calls in the same lambda expression, and pass it into the method.
This is most likely caused by an unhandled exception on the server side. WCF runtime by default terminates your service instance and puts the channel in faulted state in case of unhandled exception and you can no longer communicate over that channel. So you will need to establish a new session with the service. You should catch exceptions on the server side and send the soap faults by raising FaultException or defining FaultContract. There is also returnUnknownExceptionsAsFaults service behavior that you can use.