/* * Copyright (C) 2011 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jboss.errai.bus.client.util; import org.jboss.errai.bus.client.api.RoutingFlag; import org.jboss.errai.bus.client.api.base.DefaultErrorCallback; import org.jboss.errai.bus.client.api.base.MessageBuilder; import org.jboss.errai.bus.client.api.base.MessageCallbackFailure; import org.jboss.errai.bus.client.api.base.MessageDeliveryFailure; import org.jboss.errai.bus.client.api.messaging.Message; import org.jboss.errai.bus.client.api.messaging.MessageBus; import org.jboss.errai.bus.client.protocols.BusCommand; import org.jboss.errai.common.client.protocols.MessageParts; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The <tt>ErrorHelper</tt> class facilitates handling and sending error messages to the correct * place */ public class ErrorHelper { private static final Logger logger = LoggerFactory.getLogger(ErrorHelper.class); /** * Creates the stacktrace for the error message and sends it via conversation to the * <tt>ClientBusErrors</tt> subject * * @param bus * - the <tt>MessageBus</tt> that has received the <tt>message</tt> and * <tt>errorMessage</tt> * @param message * - the message that has encountered the error * @param errorMessage * - the error message produced * @param e * - the exception received */ public static void sendClientError(MessageBus bus, Message message, String errorMessage, Throwable e) { if (DefaultErrorCallback.CLIENT_ERROR_SUBJECT.equals(message.getSubject())) { /** * Trying to send an error to the client when the client obviously can't receive it! */ logger.error("*** An error occured that could not be delivered to the client."); logger.error("Error Message: " + message.get(String.class, "ErrorMessage")); logger.error("Details : " + message.get(String.class, "AdditionalDetails").replaceAll("<br/>", "\n").replaceAll(" ", " ")); } else { if (e != null) { /* * Some code calling this method properly set the value, where others incorrectly set it to * null (because they do not check the cause). This check is here for the case that the * exception has not been correctly set so that it is still marshaled to the client. */ if (!message.hasResource("Exception") || message.getResource(Object.class, "Exception") == null) { message.setResource("Exception", maybeUnwrap(e)); } StringBuilder a = new StringBuilder("<tt><br/>").append(e.getClass().getName()).append(": ").append(e.getMessage()).append( "<br/>"); String str; // Let's build-up the stacktrace. for (StackTraceElement sel : e.getStackTrace()) { str = sel.toString(); if (str != null && str.trim().length() != 0) a.append("    at ").append(str).append("<br/>"); } // And add the entire causal chain. while ((e = e.getCause()) != null) { String msg = e.getMessage(); if (msg == null) msg = "<No Message>"; a.append("Caused by: ").append(e.getClass().getName()).append(": ").append(msg.trim()).append("<br/>"); for (StackTraceElement sel : e.getStackTrace()) { str = sel.toString(); if (str != null && str.trim().length() != 0) a.append("    at ").append(str).append("<br/>"); } } sendClientError(bus, message, errorMessage, a.append("</tt>").toString()); } else { sendClientError(bus, message, errorMessage, "No additional details."); } } } private static Throwable maybeUnwrap(Throwable e) { while ((e instanceof MessageDeliveryFailure || e instanceof MessageCallbackFailure) && e.getCause() != null) { e = e.getCause(); } return e; } /** * Sends the error message via conversation to the <tt>ClientBusErrors</tt> subject * * @param bus * - the <tt>MessageBus</tt> that has received the <tt>message</tt> and * <tt>errorMessage</tt> * @param message * - the message that has encountered the error * @param errorMessage * - the error message produced * @param additionalDetails * - the stacktrace represented as a <tt>String</tt> */ public static void sendClientError(MessageBus bus, Message message, String errorMessage, String additionalDetails) { try { if (DefaultErrorCallback.CLIENT_ERROR_SUBJECT.equals(message.getSubject())) { /** * Trying to send an error to the client when the client obviously can't receive it! */ logger.error("*** An error occured that could not be delivered to the client."); logger.error("Error Message: " + message.get(String.class, "ErrorMessage")); logger.error("Details : " + message.get(String.class, "AdditionalDetails").replaceAll("<br/>", "\n").replaceAll(" ", " ")); } else { final String errorTo = message.get(String.class, MessageParts.ErrorTo); if (errorTo != null) { final String subject = errorTo; MessageBuilder.createConversation(message) .toSubject(subject) .with("ErrorMessage", errorMessage) .with("AdditionalDetails", additionalDetails) .with(MessageParts.ErrorTo, message.get(String.class, MessageParts.ErrorTo)) .with(MessageParts.Throwable, message.getResource(Object.class, "Exception")) .noErrorHandling().sendNowWith(bus); } } } catch (RuntimeException e) { // note: this is handled this way, because this is shared server and client code. if (e.getClass().getName().equals("org.jboss.errai.bus.server.QueueUnavailableException")) { // ignore. } throw e; } } public static void sendClientError(MessageBus bus, String queueId, String errorMessage, String additionalDetails) { try { MessageBuilder.createMessage() .toSubject(DefaultErrorCallback.CLIENT_ERROR_SUBJECT) .with("ErrorMessage", errorMessage) .with("AdditionalDetails", additionalDetails) .with(MessageParts.SessionID, queueId) .flag(RoutingFlag.NonGlobalRouting) .noErrorHandling().sendNowWith(bus); } catch (RuntimeException e) { // note: this is handled this way, because this is shared server and client code. if (e.getClass().getName().equals("org.jboss.errai.bus.server.QueueUnavailableException")) { // ignore. } throw e; } } /** * Sends a disconnect command message to the client bus * * @param bus * - the bus responsible for sending messages for the server * @param message * - the message that has encountered the error */ public static void disconnectRemoteBus(MessageBus bus, Message message) { MessageBuilder.createConversation(message) .toSubject("ClientBus") .command(BusCommand.Disconnect) .noErrorHandling().sendNowWith(bus); } /** * Handles the failed delivery of a message, and sends the error to the appropriate place * * @param bus * - the <tt>MessageBus</tt> that has received the <tt>message</tt> and * <tt>errorMessage</tt> * @param message * - the message that has encountered the error * @param errorMessage * - the error message produced * @param e * - the exception received * @param disconnect * - true if the bus should be disconnected after the error has been sent */ public static void handleMessageDeliveryFailure(MessageBus bus, Message message, String errorMessage, Throwable e, boolean disconnect) { String logMessage = "*** Message delivery failure ***" + "\nBus: " + bus.toString() + "\nMessage: " + message + "\nerrorMessage: " + errorMessage + "\nexception: " + e + "\ndisconnect: " + disconnect; if (!(e instanceof MessageDeliveryFailure && ((MessageDeliveryFailure) e).isRpcEndpointException())) { if (e != null) { logger.error(logMessage); } else { logger.error(logMessage, e); } } try { if (message.getErrorCallback() != null) { if (!message.getErrorCallback().error(message, e)) { return; } } sendClientError(bus, message, errorMessage, e); // if (e != null) throw new MessageDeliveryFailure(e); } finally { if (disconnect) disconnectRemoteBus(bus, message); } } }