/*************************************************************************
*
* ADOBE CONFIDENTIAL __________________
*
* [2002] - [2007] Adobe Systems Incorporated All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains the property of Adobe Systems Incorporated and its suppliers, if any. The intellectual and technical concepts contained herein are
* proprietary to Adobe Systems Incorporated and its suppliers and may be covered by U.S. and Foreign Patents, patents in process, and are protected by trade secret or copyright law. Dissemination of
* this information or reproduction of this material is strictly forbidden unless prior written permission is obtained from Adobe Systems Incorporated.
**************************************************************************/
package flex.messaging;
import java.util.Map;
import flex.messaging.log.Log;
import flex.messaging.log.LogCategories;
import flex.messaging.log.LogEvent;
import flex.messaging.messages.ErrorMessage;
import flex.messaging.messages.Message;
import flex.messaging.util.ExceptionUtil;
import flex.messaging.util.ResourceLoader;
import flex.messaging.util.StringUtils;
/**
* The MessageException class is the basic exception type used throughout the server. This class is extended to support more specific exception types.
*/
public class MessageException extends LocalizedException
{
// --------------------------------------------------------------------------
//
// Static Constants
//
// --------------------------------------------------------------------------
// Message exception code strings.
public static final String CODE_SERVER_RESOURCE_UNAVAILABLE = "Server.ResourceUnavailable";
/** @exclude **/
static final long serialVersionUID = 3310842114461162689L;
// --------------------------------------------------------------------------
//
// Constructors
//
// --------------------------------------------------------------------------
/**
* Default constructor.
*/
public MessageException()
{
}
/**
* Construct a message specifying a ResourceLoader to be used to load localized strings.
*
* @param loader
*/
public MessageException(ResourceLoader loader)
{
super(loader);
}
/**
* Constructor with a message.
*
* @param message
* The detailed message for the exception.
*/
public MessageException(String message)
{
setMessage(message);
}
/**
* Constructs a new exception with the specified message and the <code>Throwable</code> as the root cause.
*
* @param message
* The detailed message for the exception.
* @param t
* The root cause of the exception.
*/
public MessageException(String message, Throwable t)
{
setMessage(message);
setRootCause(t);
}
/**
* Constructs a new exception with the specified <code>Throwable</code> as the root cause.
*
* @param t
* The root cause of the exception.
*/
public MessageException(Throwable t)
{
String rootMessage = t.getMessage();
if (rootMessage == null)
rootMessage = t.toString();
setMessage(rootMessage);
setRootCause(t);
}
// --------------------------------------------------------------------------
//
// Properties
//
// --------------------------------------------------------------------------
// ----------------------------------
// code
// ----------------------------------
/** @exclude **/
protected String code;
/**
* Returns the code of the exception.
*
* @return Code of the exception.
*/
public String getCode()
{
return code;
}
/**
* Sets the code of the exception.
*
* @param code
* Code of the exception.
*/
public void setCode(String code)
{
this.code = code;
}
// ----------------------------------
// defaultLogMessageIntro
// ----------------------------------
/**
* Returns the default initial text for use in the log output generated by <code>logAtHingePoint()</code>.
*/
public String getDefaultLogMessageIntro()
{
return "Error handling message: ";
}
// ----------------------------------
// extendedData
// ----------------------------------
/** @exclude **/
protected Map extendedData;
/**
* Returns the extended data of the exception.
*
* @return The extended data of the exception.
*/
public Map getExtendedData()
{
return extendedData;
}
/**
* Sets the extended data of the exception.
*
* @param extendedData
* The extended data of the exception.
*/
public void setExtendedData(Map extendedData)
{
this.extendedData = extendedData;
}
// ----------------------------------
// errorMessage
// ----------------------------------
/** @exclude **/
protected ErrorMessage errorMessage;
/**
* Returns the error message of the exception.
*
* @return The error message of the exception.
*/
public ErrorMessage getErrorMessage()
{
if (errorMessage == null)
{
errorMessage = createErrorMessage();
}
return errorMessage;
}
/**
* Sets the error message of the exception.
*
* @param errorMessage
* The error message of the exception.
*/
public void setErrorMessage(ErrorMessage errorMessage)
{
this.errorMessage = errorMessage;
}
// ----------------------------------
// logStackTraceEnabled
// ----------------------------------
/**
* Indicates whether logging of this exception should include a full stack trace or not. Default is true.
*
* @see #logAtHingePoint(Message, ErrorMessage, String)
*/
public boolean isLogStackTraceEnabled()
{
return true;
}
// ----------------------------------
// logged
// ----------------------------------
protected boolean logged;
/**
* Indicates whether this exception has already been logged by a call to <code>logAtHingPoint()</code>. Manual logging for an exception can use <code>setLogged(true)</code> to suppress any further
* automatic logging of the exception.
*
* @return true if the exception has been logged; otherwise false.
*/
public boolean isLogged()
{
return logged;
}
/**
* Records whether this exception has been logged.
*
* @param value
* true if the exception has been logged; otherwise false.
*/
public void setLogged(boolean value)
{
logged = value;
}
// ----------------------------------
// peferredLogLevel
// ----------------------------------
/**
* Returns the preferred log level for this exception instance. The default value is <code>LogEvent.ERROR</code>.
*
* @see #logAtHingePoint(Message, ErrorMessage, String)
*/
public short getPreferredLogLevel()
{
return LogEvent.ERROR;
}
// ----------------------------------
// resourceLoader
// ----------------------------------
/**
* Returns the <code>ResourceLoader</code> used to load localized strings.
*
* @return The <code>ResourceLoader</code> used to load localized strings.
*/
@Override
protected ResourceLoader getResourceLoader()
{
if (resourceLoader == null)
resourceLoader = MessageBroker.getSystemSettings().getResourceLoader();
return resourceLoader;
}
// ----------------------------------
// rootCauseErrorMessage
// ----------------------------------
/** @exclude **/
public Object getRootCauseErrorMessage()
{
// FIXME: serialize number field.
if (rootCause != null)
{
if (rootCause instanceof MessageException)
{
return ((MessageException) rootCause).createErrorMessage();
}
else
{
return rootCause;
}
}
return null;
}
// --------------------------------------------------------------------------
//
// Public Methods
//
// --------------------------------------------------------------------------
/**
* Creates an error message from the exception.
*
* @return The error message.
*/
public ErrorMessage createErrorMessage()
{
ErrorMessage msg = new ErrorMessage();
if (code == null)
{
msg.faultCode = "Server.Processing";
}
else
{
msg.faultCode = code;
}
msg.faultString = message;
msg.faultDetail = details;
msg.rootCause = getRootCauseErrorMessage();
if (extendedData != null)
{
msg.extendedData = extendedData;
}
return msg;
}
/**
* Invoked at hinge-points in server processing where catch-all exception logging is performed. This method uses <code>isLogged()</code> and <code>setLogged()</code> to avoid repeat logging of the
* same exception and uses <code>getPreferredLogLevel()</code> which may be overridden in subclasses to control the log level that the logging is output at. The underlying exception stack traces
* are also conditionally included in log output if the exception class allows it and this is determined by invoking <code>isLogStackTraceEnabled()</code>
*
* @param inboundMessage
* The inbound message that triggered an exception during processing.
* @param outboundMessage
* The outbound <code>ErrorMessage</code>, which may be null depending on whether it has been generated or not at the point this method is invoked.
* @param logMessageIntro
* The beginning text for the log message, which may be null; default value is returned by <code>getDefaultLogMessageIntro()</code>.
*/
public void logAtHingePoint(Message inboundMessage, ErrorMessage outboundMessage, String logMessageIntro)
{
if (!isLogged())
{
setLogged(true);
short preferredLevel = getPreferredLogLevel();
// If the preferred level is less than the current Log level; return early.
if (preferredLevel < Log.getTargetLevel())
return;
// Construct core log output.
StringBuffer output = new StringBuffer();
output.append((logMessageIntro != null) ? logMessageIntro : getDefaultLogMessageIntro());
output.append(this.toString());
output.append(StringUtils.NEWLINE);
output.append(" incomingMessage: ");
output.append(inboundMessage);
output.append(StringUtils.NEWLINE);
if (outboundMessage != null)
{
output.append(" errorReply: ");
output.append(outboundMessage);
output.append(StringUtils.NEWLINE);
}
if (isLogStackTraceEnabled())
{
output.append(ExceptionUtil.exceptionFollowedByRootCausesToString(this));
output.append(StringUtils.NEWLINE);
}
switch (preferredLevel)
{
case LogEvent.FATAL:
{
Log.getLogger(LogCategories.MESSAGE_GENERAL).fatal(output.toString());
break;
}
case LogEvent.ERROR:
{
Log.getLogger(LogCategories.MESSAGE_GENERAL).error(output.toString());
break;
}
case LogEvent.WARN:
{
Log.getLogger(LogCategories.MESSAGE_GENERAL).warn(output.toString());
break;
}
case LogEvent.INFO:
{
Log.getLogger(LogCategories.MESSAGE_GENERAL).info(output.toString());
break;
}
case LogEvent.DEBUG:
{
Log.getLogger(LogCategories.MESSAGE_GENERAL).debug(output.toString());
break;
}
default:
{
Log.getLogger(LogCategories.MESSAGE_GENERAL).fatal("Failed to log exception for handling message due to an invalid preferred log level: " + preferredLevel);
break;
}
}
}
}
}