| CallException.java |
/*
* $Id: CallException.java,v 1.37 2011/03/19 09:11:18 agoubard Exp $
*
* See the COPYRIGHT file for redistribution and use restrictions.
*/
package org.xins.common.service;
import org.xins.common.MandatoryArgumentChecker;
/**
* Root class for all exceptions that indicate a <code>ServiceCaller</code>
* call failed. This exception is typically only thrown by class
* {@link ServiceCaller} and subclasses.
*
* <p>Call exceptions can be linked. The first exception is then actually
* thrown to the caller. The caller can get the linked exceptions using
* {@link #getNext()}.
*
* @version $Revision: 1.37 $ $Date: 2011/03/19 09:11:18 $
* @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
*
* @since XINS 1.0.0
*/
public abstract class CallException extends Exception {
/**
* Short description of the reason. Cannot be <code>null</code>.
*/
private final String _shortReason;
/**
* The original request. Cannot be <code>null</code>.
*/
private final CallRequest _request;
/**
* Descriptor for the target that was attempted to be called. Cannot be
* <code>null</code>.
*/
private final TargetDescriptor _target;
/**
* The time elapsed between the time the call attempt was started and the
* time the call returned. The duration is in milliseconds and is always
* >= 0.
*/
private final long _duration;
/**
* A detailed description of the problem. Can be <code>null</code>.
*/
private String _detail;
/**
* The next linked <code>CallException</code>. Can be <code>null</code> if
* there is none or if it has not been set yet.
*/
private CallException _next;
/**
* The exception message. Is <code>null</code> if unset.
*/
private String _message;
/**
* Constructs a new <code>CallException</code> based on a short reason, the
* original request, target called, call duration, detail message and cause
* exception.
*
* @param shortReason
* the short reason, cannot be <code>null</code>.
*
* @param request
* the original request, cannot be <code>null</code>.
*
* @param target
* descriptor for the target that was attempted to be called, can be <code>null</code>.
*
* @param duration
* the call duration in milliseconds, must be >= 0.
*
* @param detail
* a detailed description of the problem, can be <code>null</code> if
* there is no more detail.
*
* @param cause
* the cause exception, can be <code>null</code>.
*
* @throws IllegalArgumentException
* if <code>shortReason == null
* || request == null
* || duration < 0</code>.
*/
protected CallException(String shortReason,
CallRequest request,
TargetDescriptor target,
long duration,
String detail,
Throwable cause)
throws IllegalArgumentException {
// Check preconditions
MandatoryArgumentChecker.check("shortReason", shortReason, "request", request);
if (duration < 0) {
throw new IllegalArgumentException(
"duration (" + duration + ") < 0");
}
// Associate this exception with the root cause
if (cause != null) {
initCause(cause);
}
// Store information in fields
_shortReason = shortReason;
_request = request;
_target = target;
_duration = duration;
_detail = detail;
}
/**
* Returns the detail message string of this exception.
*
* @return
* the detail message string of this exception, never <code>null</code>.
*/
public String getMessage() {
// Initialize the message if necessary
if (_message == null) {
StringBuffer buffer = new StringBuffer(495);
buffer.append(_shortReason);
buffer.append(" in ");
buffer.append(_duration);
buffer.append(" ms while executing ");
buffer.append(_request.describe());
buffer.append(" at ");
buffer.append(_target.getURL());
if (_detail == null) {
buffer.append('.');
} else {
buffer.append(": ");
buffer.append(_detail);
}
_message = buffer.toString();
}
if (_next != null) {
if (_message.endsWith(".")) {
return _message + " Followed by: " + _next.getMessage();
} else {
return _message + ". Followed by: " + _next.getMessage();
}
} else {
return _message;
}
}
/**
* Returns the original request.
*
* @return
* the original request, never <code>null</code>.
*/
public final CallRequest getRequest() {
return _request;
}
/**
* Returns the descriptor for the target that was attempted to be called.
*
* @return
* the target descriptor, can be <code>null</code>.
*/
public final TargetDescriptor getTarget() {
return _target;
}
/**
* Returns the call duration. This is defined as the time elapsed between
* the time the call attempt was started and the time the call returned.
* The duration is in milliseconds and is always >= 0.
*
* @return
* the call duration in milliseconds, always >= 0.
*/
public final long getDuration() {
return _duration;
}
/**
* Sets the next linked <code>CallException</code>. This method should be
* called either never or once during the lifetime of a
* <code>CallException</code> object.
*
* @param next
* the next linked <code>CallException</code>, not <code>null</code>.
*
* @throws IllegalStateException
* if the next linked <code>CallException</code> has already been set.
*
* @throws IllegalArgumentException
* if <code>next == null</code>.
*/
final void setNext(CallException next)
throws IllegalStateException, IllegalArgumentException {
// Check preconditions
if (_next != null) {
throw new IllegalStateException(
"Next linked CallException already set.");
}
MandatoryArgumentChecker.check("next", next);
// Store the reference
_next = next;
}
/**
* Gets the next linked <code>CallException</code>, if there is any.
*
* @return
* the next linked <code>CallException</code>, or <code>null</code> if
* there is none.
*/
public final CallException getNext() {
return _next;
}
/**
* Returns a detailed description of problem, if any.
*
* @return
* a detailed description, if available, otherwise <code>null</code>.
*/
public String getDetail() {
return _detail;
}
/**
* Indicates whether this call exception allow for the ServiceCaller to
* fail over another TargetDescriptor if available.
* Note that when an exception doesn't know if the call (or part of the call)
* has been executed, <code>false</code> should be returned.
*
* @return
* <code>true</code> if the service can fail over, <code>false</code> otherwise.
*/
public boolean isFailOverAllowed() {
return false;
}
}