/*
* ALMA - Atacama Large Millimiter Array
* (c) European Southern Observatory, 2002
* Copyright by ESO (in the framework of the ALMA collaboration),
* All rights reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
package alma.acs.exceptions;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import org.omg.CORBA.UserException;
import alma.ACSErr.ErrorTrace;
import alma.acs.util.UTCUtility;
/**
* @author hsommer
* created Sep 26, 2003 10:53:40 AM
*/
public class CorbaExceptionConverter
{
public static final String PROPERTY_JAVAEXCEPTION_CLASS = "javaex.class";
public static final String PROPERTY_JAVAEXCEPTION_MESSAGE = "javaex.msg";
/**
* Converts an <code>ErrorTrace</code> object to a Java <code>Throwable</code>.
* The chain of caused-by exceptions is translated, too.
* <p>
* See the comment on class substitution at
* {@link DefaultAcsJException}.
*
* @param et
* @return Throwable current implementation always returns a subclass of
* <code>AcsJException</code>, but don't rely on it yet.
*/
static Throwable recursiveGetThrowable(ErrorTrace et)
{
// check if underlying Java exception class is stored as a property
String classname = ErrorTraceManipulator.getProperty(et, PROPERTY_JAVAEXCEPTION_CLASS);
String message = ErrorTraceManipulator.getProperty(et, PROPERTY_JAVAEXCEPTION_MESSAGE);
if (message == null) {
message = "";
}
Throwable thr = null;
// TODO: define some fields in ErrorTrace that hold the IDL information
// about the exception name (module, name).
// Then (based on naming conventions) we can attempt to construct
// the right AcsJ-style exception from the error trace even if
// the ErrorTrace was originally not produced in Java.
// An alternative solution would be a map that goes from (type, code)
// to the matching exception class. This seems difficult to maintain across modules though.
if (classname != null)
{
// try if that class is available to be reconstructed
try
{
Class<? extends Throwable> exClass = Class.forName(classname).asSubclass(Throwable.class);
if (AcsJException.class.isAssignableFrom(exClass))
{
if (exClass != DefaultAcsJException.class) { // DefaultAcsJException has a non-standard constructor and will be constructed below.
Constructor<? extends Throwable> msgCtor = exClass.getConstructor(String.class);
thr = msgCtor.newInstance(new Object[]{message});
}
}
else {
// non-ACS Java exceptions we don't reconstruct directly
// because we'd lose the additional information like line number etc. that ErrorTrace has
// and that will be copied below.
thr = new DefaultAcsJException(message, 0, 0, et.shortDescription, classname);
}
}
catch (Exception e)
{
message = "failed to reconstruct Java exception '" + classname + "'. Original msg was '" + message + "'.";
// just leave thr == null
}
}
if (thr == null)
{
// default ex represents ErrorTrace that comes from other languages than Java,
// or when reconstruction of the same Java exception failed for whatever reason.
thr = new DefaultAcsJException(message, et.errorType, et.errorCode, et.shortDescription);
}
resurrectThrowable(thr, et);
return thr;
}
private static void resurrectThrowable(Throwable thr, ErrorTrace et)
{
if (thr == null || et == null)
{
throw new NullPointerException("resurrectThrowable: parameters must not be null!");
}
if (thr instanceof AcsJException)
{
// data specific to AcsJException
AcsJException acsJEx = (AcsJException) thr;
acsJEx.m_host = et.host;
acsJEx.m_process = et.process;
acsJEx.m_file = et.file;
acsJEx.m_method = et.routine;
acsJEx.m_line = et.lineNum;
// todo check if error type and code match
//et.errorType == acsJEx.getErrorType();
//et.errorCode == acsJEx.getErrorCode();
acsJEx.m_severity = et.severity;
acsJEx.m_threadName = et.thread;
acsJEx.m_timeMilli = UTCUtility.utcOmgToJava(et.timeStamp);
acsJEx.m_properties = ErrorTraceManipulator.getProperties(et);
}
// We redundantly put source information also into a fake StackTrace,
// which then looks better when logging such an AcsJException directly,
// even though the rest of a single java exception's stack trace did not get
// forwarded over corba/ErrorTrace.
StackTraceElement[] stackTrace = new StackTraceElement[1];
stackTrace[0] = new StackTraceElement("---", et.routine, et.file, et.lineNum);
thr.setStackTrace(stackTrace);
if (et.previousError != null && et.previousError.length > 0 &&
et.previousError[0] != null)
{
ErrorTrace etCause = et.previousError[0];
if (etCause != null)
{
// recursion
Throwable thrCause = recursiveGetThrowable(etCause);
thr.initCause(thrCause);
}
}
}
public static void convertErrorTraceToJavaException(ErrorTrace et, AcsJException jEx)
{
resurrectThrowable(jEx, et);
}
/**
* Checks if a given Throwable is of a CORBA type generated by the ACS error system.
* If so, the embedded error trace is converted to a chain of AcsJ-style exceptions.
* <p>
* The check is based on the fact that all ACS error system exceptions derive
* from <code>org.omg.CORBA.UserException</code>, and have a field <code>errorTrace</code>
* of type <code>alma.ACSErr.ErrorTrace</code>.
*
* @param thr
* @return The converted Throwable, or <code>thr</code> itself if no conversion is necessary,
* or if the conversion failed with an exception.
*/
public static Throwable convertHiddenErrorTrace(Throwable thr)
{
// thr may be null when a Completion is constructed from another completion
// that does not have an associated error trace
if (thr == null) {
return null;
}
// all ACS error system exceptions derive from org.omg.CORBA.UserException
if (UserException.class.isAssignableFrom(thr.getClass())) {
// check if thr is of an exception class generated by the ACS error system, then use its ErrorTrace
try {
Field errorTraceField = thr.getClass().getField("errorTrace");
ErrorTrace etCause = (ErrorTrace) errorTraceField.get(thr);
thr = recursiveGetThrowable(etCause);
}
catch (Exception ex) {
// ignore: we assume that thr is not an ACS-style exception
}
}
return thr;
}
}