blob: f3b565a5b517c1462e7c384cb6f2ac0a0f031957 [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.
*
* @see #logAtHingePoint(Message, ErrorMessage, String)
* @return true if the logging stack traces is enabled.
*/
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)
* @return the preffered log level.
*/
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;
}
}
}
}
}