Handling java-side exceptions in .NET howto
This howto explains how to handle java exceptions thrown in the java-part of a distributed application and forwarded with IIOP.NET to the .NET part of the application.
This document assumes you are using Java 1.4 or later
The code for this tutorial can be found in Examples/Exceptions/JavaSideExceptions .
Mapping
Java exception classes are extensions of java.lang.Exception . In IDL, each java exception class TException is mapped to a CORBA exception TEx containing a custom-serialized valuetype TException . The IDLToCLSCompiler generates a .NET exception TEx and an abstract class TException .
Because exceptions are serialized (i.e. CORBA valuetypes), their translation to .NET is not automated and requires the implementation of the methods contained in the class. Each exception class used requires the implementation of a class ExceptionImpl . The complexity and the number of methods to provide depends mostly on the java-side implementation of the java.lang.Exception class. In our example using Java JDK 1.4, this requires more than 20 methods in 3 classes (java.lang.Exception relies on java.lang.Throwable and java.lang.StackTraceElement ).
Step 1: create a custom exception or use an existing java exception class
Custom exceptions must inherit from java.lang.Exception and implement java.io.Serializable .
import java.io.Serializable;
import java.rmi.Remote;
import java.rmi.RemoteException;
public class CustomException extends Exception implements Serializable {
public String reason;
public CustomException(String message, String reason) throws java.rmi.RemoteException {
super(message);
this.reason = reason;
}
}
|
Step 2: create IDL description
Create the IDL description for your java object as usual. Because the exception is mentioned in some throws clause, there is no need to explicitely generate the IDL for your exception.
Step 3: create .NET stubs
Use the IDLToCLSCompiler to generate the .NET stubs. Take note of which valuetypes must be locally implemented.
Step 4: implement valuetype classes
The compiler generates an abstract class CustomException , you must implement CustomExceptionImpl implementing all the abstract methods of CustomException . Because this class indirectly inherits from java.lang.Exception , the methods defined there must also be implemented here. The methods depend on the java implementation of Exception; the following example shows the code for the Sun JDK 1.4.
For each custom exception, you must create an implementation class. Below, the implementation class CustomExceptionImpl for class CustomException .
namespace tutorial {
using java.lang;
public class CustomExceptionImpl: CustomException {
private ExceptionCommon m_data = new ExceptionCommon();
public CustomExceptionImpl() : base() {
}
public override void Deserialise(Corba.DataInputStream stream) {
m_data.Deserialise(stream);
reason = stream.read_WStringValue();
}
public override void Serialize(Corba.DataOutputStream stream) {
m_data.Serialise(stream);
}
public override Throwable initCause(Throwable arg) {
return null;
}
public override string toString() {
return ToString();
}
public override Throwable fillInStackTrace() {
return null;
}
public override Throwable cause {
get { return m_data.Cause; }
}
public override string localizedMessage {
get { return m_data.Msg; }
}
public override string message {
get { return m_data.Msg; }
}
public override void printStackTrace__() {
}
public override void printStackTrace__java_io_PrintStream(java.io.PrintStream arg) {
}
public override void printStackTrace__java_io_PrintWriter(java.io.PrintWriter arg) {
}
public override StackTraceElement[] stackTrace {
get { return m_data.Trace; }
set { }
}
public override string ToString() {
return base.ToString() + "; msg: " + m_data.Msg;
}
}
}
|
The data and code for java.lang.Exception is factored out in an own class ExceptionCommon to allow code reuse when more than one exception class is used. Each exception class must deserialize the java.lang.Exception fields before deserializing its own fields. This code depends on the java-side implementation!
namespace java.lang {
///
/// used to Deserialise the java.lang.Exception data
///
public class ExceptionCommon {
private java.lang.Throwable m_cause;
private string m_msg;
private java.lang.StackTraceElement[] m_trace;
public java.lang.Throwable Cause {
get {
return m_cause;
}
}
public string Msg {
get {
return m_msg;
}
}
public java.lang.StackTraceElement[] Trace {
get {
return m_trace;
}
}
public void Deserialise(Corba.DataInputStream stream) {
stream.read_octet(); // ignore format version: java RMI specific
stream.read_boolean(); // ignore default read object: java RMI specific
m_cause = (java.lang.Throwable)stream.read_ValueOfType(typeof(java.lang.Throwable));
m_msg = stream.read_WStringValue();
object boxedTrace = stream.read_Value();
if (boxedTrace != null) {
m_trace = (StackTraceElement[])((BoxedValueBase) boxedTrace).Unbox();
}
}
public void Serialise(Corba.DataOutputStream stream) {
throw new omg.org.CORBA.NO_IMPLEMENT(2876, omg.org.CORBA.CompletionStatus.Completed_MayBe);
}
}
public class StackTraceElementImpl : StackTraceElement {
public override int hashCode() {
return GetHashCode();
}
public override string toString() {
return "StackTraceElement:\n" + "in class: " + m_declaringClass +
" (file: " + m_fileName + "); method: " + m_methodName;
}
public override bool equals([ObjectIdlTypeAttribute(IdlTypeObject.Any)] object arg) {
return this.Equals(arg);
}
public override string className {
get { return m_declaringClass; }
}
public override string fileName {
get { return m_fileName; }
}
public override int lineNumber {
get { return m_lineNumber; }
}
public override string methodName {
get { return m_methodName; }
}
public override bool nativeMethod {
get { return false; }
}
}
[Serializable]
public class ThrowableImpl : Throwable {
private ExceptionCommon m_data = new ExceptionCommon();
public ThrowableImpl() : base() {
}
public override void Deserialise(Corba.DataInputStream stream) {
m_data.Deserialise(stream);
}
public override void Serialize(Corba.DataOutputStream stream) {
m_data.Serialise(stream);
}
public override Throwable initCause(Throwable arg) {
return null;
}
public override string toString() {
return ToString();
}
public override Throwable fillInStackTrace() {
return null;
}
public override Throwable cause {
get { return m_data.Cause; }
}
public override string localizedMessage {
get { return m_data.Msg; }
}
public override string message {
get { return m_data.Msg; }
}
public override void printStackTrace__() {
}
public override void printStackTrace__java_io_PrintStream(java.io.PrintStream arg) {
}
public override void printStackTrace__java_io_PrintWriter(java.io.PrintWriter arg) {
}
public override StackTraceElement[] stackTrace {
get { return m_data.Trace; }
set { }
}
public override string ToString() {
return base.ToString() + "; msg: " + m_data.Msg;
}
}
}
|
Step 5: use the exception
To access the exception, use the wrapped class CustomEx , which is automatically generated by the compiler.
try {
....
service.fail();
....
} catch (CustomEx je) {
// extract exception from wrapper
CustomException ce = je.value;
....
Console.WriteLine("Java-side exception: {0}\nReason: {1}", ce.message, ce.reason);
}
|
|