/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2013 ForgeRock AS. All Rights Reserved
*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
* http://forgerock.org/license/CDDLv1.0.html
* See the License for the specific language governing
* permission and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at http://forgerock.org/license/CDDLv1.0.html
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*/
package org.identityconnectors.framework.impl.api.remote;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import org.identityconnectors.common.Assertions;
import org.identityconnectors.framework.common.exceptions.ConnectorException;
/**
* RemoteWrappedException wraps every exception which are received from Remote
* Connector Server.
* <p/>
* <b>This Exception is not allowed to use in Connectors!!!</b>
* <p/>
*
*
*
* This type of exception is not allowed to be serialise because this exception
* represents any after deserialization.
*
* This code example show how to get the remote stack trace and how to use the
* same catches to handle the exceptions regardless its origin.
*
* <pre>
* <code>
* String stackTrace = null;
* try {
* try {
* facade.getObject(ObjectClass.ACCOUNT, uid, null);
* } catch (RemoteWrappedException e) {
* stackTrace = e.getStackTrace()
* }
* } catch (Throwable t) {
* if (null != stackTrace) {
* System.err.println(stackTrace);
* } else {
* t.printStackTrace();
* }
* }
* <code>
* </pre>
*
* @author Laszlo Hordos
* @since 1.4
*/
public final class RemoteWrappedException extends ConnectorException {
private static final long serialVersionUID = 1L;
public static final String FIELD_CLASS = "class";
public static final String FIELD_MESSAGE = "message";
public static final String FIELD_CAUSE = "cause";
public static final String FIELD_STACK_TRACE = "stackTrace";
private final String stackTrace;
/**
* <pre>
* <code>
* {
* "class": "org.identityconnectors.framework.common.exceptions.ConnectorIOException",
* "message": "Sample Error Message",
* "cause": {
* "class": "java.net.SocketTimeoutException",
* "message": "Sample Error Message",
* "cause": {
* "class": "edu.example.CustomException",
* "message": "Sample Error Message"
* }
* },
* "stackTrace": "full stack trace for logging"
* }
* </code>
* </pre>
*/
private Map<String, Object> exception = null;
/**
* @see org.identityconnectors.framework.common.exceptions.ConnectorException#ConnectorException(String)
*/
RemoteWrappedException(final Map<String, Object> exception) {
super((String) exception.get(FIELD_MESSAGE));
this.exception = exception;
this.stackTrace = (String) exception.get(FIELD_STACK_TRACE);
}
public RemoteWrappedException(final String throwableClass, final String message,
final RemoteWrappedException cause, final String stackTrace) {
super(message);
exception = new HashMap<String, Object>(4);
exception.put(FIELD_CLASS, Assertions.blankChecked(throwableClass, "throwableClass"));
exception.put(FIELD_MESSAGE, message);
if (null != cause) {
exception.put(FIELD_CAUSE, cause.exception);
}
if (null != stackTrace) {
exception.put(FIELD_STACK_TRACE, stackTrace);
}
this.stackTrace = stackTrace;
}
/**
* Gets the class name of the original exception.
*
* This value is constructed by {@code throwable.getClass().getName()}.
*
* @return name of the original exception.
*/
public String getExceptionClass() {
return (String) exception.get(FIELD_CLASS);
}
/**
* Checks if the exception is the expected class.
*
* @param expected
* the expected throwable class.
* @return {@code true} if the class name are equals.
*/
public boolean is(Class<? extends Throwable> expected) {
if (null == expected) {
return false;
}
String className = ((String) exception.get(FIELD_CLASS));
String classExpected = expected.getName();
return classExpected.equalsIgnoreCase(className);
}
/**
* Returns the cause of original throwable or {@code null} if the cause is
* nonexistent or unknown. (The cause is the throwable that caused the
* original throwable to get thrown.)
*
* @return the cause of this throwable or {@code null} if the cause is
* nonexistent or unknown.
*/
@Override
@SuppressWarnings("unchecked")
public RemoteWrappedException getCause() {
Object o = exception.get(FIELD_CAUSE);
if (o instanceof Map) {
return new RemoteWrappedException((Map<String, Object>) o);
} else {
return null;
}
}
@Override
public void printStackTrace(PrintStream s) {
if (null == stackTrace) {
super.printStackTrace(s);
} else {
s.println(stackTrace);
}
}
@Override
public void printStackTrace(PrintWriter s) {
if (null == stackTrace) {
super.printStackTrace(s);
} else {
s.println(stackTrace);
}
}
public String readStackTrace() {
return stackTrace;
}
/**
* Gets the stack trace from a Throwable as a String.
*
*
* @param throwable
* the {@code Throwable} to be examined
* @return the stack trace as generated by the exception's
* {@code printStackTrace(PrintWriter)} method
*/
public static String getStackTrace(Throwable throwable) {
StringWriter sw = new StringWriter();
throwable.printStackTrace(new PrintWriter(sw, true));
return sw.getBuffer().toString();
}
/**
* Wraps the Throwable into a RemoteWrappedException instance.
*
* @param ex
* Exception to wrap or cast and return.
* @return a <code>RemoteWrappedException</code> that either <i>is</i> the
* specified exception or <i>contains</i> the specified exception.
*/
public static RemoteWrappedException wrap(Throwable ex) {
if (null == ex) {
return null;
}
// don't bother to wrap a exception that is already a
// RemoteWrappedException.
if (ex instanceof RemoteWrappedException) {
return (RemoteWrappedException) ex;
}
return new RemoteWrappedException(convert(ex));
}
/**
* Converts the throwable object to a new Map object that representing
* itself.
*
* @param throwable
* the {@code Throwable} to be converted
* @return the Map representing the throwable.
*/
public static HashMap<String, Object> convert(Throwable throwable) {
HashMap<String, Object> exception = null;
if (null != throwable) {
exception = new HashMap<String, Object>(4);
exception.put(FIELD_CLASS, throwable.getClass().getName());
exception.put(FIELD_MESSAGE, throwable.getMessage());
if (null != throwable.getCause()) {
exception.put(FIELD_CAUSE, buildCause(throwable.getCause()));
}
exception.put(FIELD_STACK_TRACE, getStackTrace(throwable));
}
return exception;
}
private static Map<String, Object> buildCause(Throwable throwable) {
Map<String, Object> cause =
new HashMap<String, Object>(null != throwable.getCause() ? 3 : 2);
cause.put(FIELD_CLASS, throwable.getClass().getName());
cause.put(FIELD_MESSAGE, throwable.getMessage());
if (null != throwable.getCause()) {
cause.put(FIELD_CAUSE, buildCause(throwable.getCause()));
}
return cause;
}
}