blob: 27e37bf576b52d253c4eaf680cbfe3afe663baa7 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 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.PropertyStringResourceLoader;
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";
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
//----------------------------------
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>.
*
* @return the default initial text for use in the log output generated by <code>logAtHingePoint()</code>.
*/
public String getDefaultLogMessageIntro() {
return "Error handling message: ";
}
//----------------------------------
// extendedData
//----------------------------------
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
//----------------------------------
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.
*
* @return true if the logging stack traces is enabled.
* @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>.
*
* @return the preffered log level.
* @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) {
try {
MessageBroker broker = FlexContext.getMessageBroker();
resourceLoader = broker != null ? broker.getSystemSettings().getResourceLoader()
: new PropertyStringResourceLoader();
} catch (NoClassDefFoundError exception) // Could happen in client mode.
{
return new PropertyStringResourceLoader();
}
}
return resourceLoader;
}
//----------------------------------
// rootCauseErrorMessage
//----------------------------------
public Object getRootCauseErrorMessage() {
if (rootCause == null)
return null;
// FIXME: serialize number field.
return rootCause instanceof MessageException ?
((MessageException) rootCause).createErrorMessage() : rootCause;
}
//----------------------------------
// statusCode
//----------------------------------
protected int statusCode;
/**
* Returns the HTTP status code of the exception.
*
* @return The HTTP status code of the exception.
*/
public int getStatusCode() {
return statusCode;
}
/**
* Sets the HTTP status code of the exception.
*
* @param statusCode The HTTP status code of the exception.
*/
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
//--------------------------------------------------------------------------
//
// Public Methods
//
//--------------------------------------------------------------------------
/**
* Creates an error message from the exception.
*
* @return The error message.
*/
public ErrorMessage createErrorMessage() {
ErrorMessage msg = new ErrorMessage();
msg.faultCode = code != null ? code : "Server.Processing";
msg.faultString = message;
msg.faultDetail = details;
msg.rootCause = getRootCauseErrorMessage();
if (extendedData != null)
msg.extendedData = extendedData;
if (statusCode != 0)
msg.setHeader(Message.STATUS_CODE_HEADER, statusCode);
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;
}
}
}
}
}