In doing some work trying to leverage the Tuscany databinding framework
to
transform business exceptions across a binding, I noticed a couple issues
relating to the need to handle both exception and fault DataType(s) .
Basically, I'd like to discuss:
* getting rid of the exception DataType and replacing it with simply a
fault DataType.
* decoupling the fault databinding from the way that the exception maps
to
a fault.
Let me explain:
Today, for Java interfaces, we build exception DataTypes during
theintrospection phase
and we set a databinding upon it. This DB identifies an exceptionHandler
which can (via getFaultType()) construct a fault DataType from the exc
DataType.
Different exception patterns result in different mappings between
exception and fault data types. For example, we have the pattern
defined
by JAX-WS Sec 2.5 in which the Java exception class wrappers a "fault
bean" returned by the exception's getFaultInfo(). I'm thinking we also
want to support POJO exceptions as well in certain contexts.
Today we would support the JAX-WS Sec. 2.5 pattern by constructing an
DataType wrappering the exception, introspecting the exception to find
thefault
and its databinding. This databinding's exception handler would, at
transform time, be used to construct a fault DataType representing
thefault bean, distinct from
the exception DataType. This more or less works the same for JAXB
andSDO, except that for SDO we have
the Tuscany-ism of looking at FAULT_ELEMENT instead of @WebFault ( which
I
mention below I'd like to change).
For the POJO exception pattern, on the other hand, the fault DataType
andexception DataType collapse into one as we see in
the logic of JavaBeansExceptionHandler.
I don't think we need the complexity of the two DataTypes; I think all
we
really need from the exception DataType is its "physical', i.e. the Java
Class of the exception type. We use this to match the exception we have
in-hand from the Java runtime with the DataType on the operation. It is
only the fault that gets transformed by the Tuscany databinding
framework.
Perhaps what would be better, instead of an exception DataType and fault
DataType, would be to simply have the fault DataType with an additional
"exceptionPhysical" member. (We still need ExceptionHandler
withgetFaultInfo()
and createFaultException() as well).
This would let us get the exception handlers out of the introspection
business.
If we look at DataBindingJavaInterfaceProcessor.processInterface() today,
and see how the various databindings' exception handlers' getFaultType()s
are called in order to find the right databinding to set on the exception
DataType, it does seem messy to have to rely on this same getFaultType()
method to work in a certain way and introspection time and transform
time.
The best reason, I think is elegance: if it is the actual fault DataType
which is interesting at transform time, and if we have an introspect
step,
let's have the introspect step set up this interesting fault
DataType. As an example, in working on some binding impl code in
thepast, I found myself calling this same getFaultType() method to get
the fault from the exc... making for at least three times this same
getFaultType() method was called (Java introspect, my binding
construction,
and transform time).
Then there is also performance to consider...
---------
In addition, (and to some degree this is a separate point), I think we
should recognize certain patterns in which a Java exception relates to
both
the Java type of the "fault bean" and to a logical type and decouple
these
patterns from association with one of the various databinding impls we
support.
So, as an example, if you take the pattern in JAX-WS Sec. 2.5, you can
have an exception with @WebFault(<fault elem>) with a getFaultInfo()
method returning a fault bean of any of the simple, SDO, or JAXB
databindings. I don't see the rationale for using FAULT_ELEMENT to
denote the logical type when the fault bean is an SDO vs. using
[EMAIL PROTECTED] for a JAXB; I think this pattern should be decoupled from
the choice of databinding, and we don't want the same algorithm (looking
for @WebFault and using reflection to find the type returned by
getFaultInfo(), etc.) to be re-coded for each databinding.
(Though I don't think Tuscany has it presently, I coded a Simple DB
ExceptionHandler which is largely just a copy of the SDOExceptionHandler,
wihch illustrates my point. Someone please correct me if Tuscany can
already handle simple-typed fault beans in the JAX-WS Sec 2.5 pattern).
In addition we'd have other patterns like POJO exception and maybe
theAxis2 exception which Simon Nash recently did some work regarding.
At this point I'd like to show some sample code, which is part of some
changes I've run with successfully (some of my own tests in my own
sandbox
- not Tuscany tests yet).
public DataType<?> getFaultFromExceptionDataType(DataType<?>
excDataType) {
//
// 1. Find the Java type of the fault bean, construct fault DT
andset exc physical
//
Object excPhysical = excDataType.getPhysical();
if (!(excPhysical instanceof Class)) {
return null; // Should we be throwing exc instead?
}
Class excClass = (Class)excPhysical;
Class faultBeanClass = null;
try {
Method method = excClass.getMethod("getFaultInfo",
EMPTY_CLASS_ARRAY);
faultBeanClass = method.getReturnType();
} catch (NoSuchMethodException e) {
faultBeanClass = null;
}
if (faultBeanClass == null) {
return null;
}
// Only the physical is set here.
DataType faultDataType = new DataTypeImpl(null, faultBeanClass,
null);
faultDataType.setExceptionPhysical(excClass);
//
// 2. Send through already-existing introspection routine
//
// Don't try to propagate annotations yet. Note the DB on
thefaultDataType
// is set by the databinding according to the implied contract we
have going.
dataBindingRegistry.introspectType(faultDataType, null);
//
// 3) instantiate an exception handler for this fault from
theappropriate DB
and
// set in the fault DT
//
DataBinding faultDataBinding =
dataBindingRegistry.getDataBinding(
faultDataType.getDataBinding());
ExceptionHandler excHandler =
faultDataBinding.getExceptionHandler();
faultDataType.setExceptionHandler(excHandler);
//
// 4) set the logical from the @WebFault (FAULT_ELEMENT for now)
//
QName faultElement = null;
try {
Field field = excClass.getField("FAULT_ELEMENT");
faultElement = (QName)field.get(null);
} catch (NoSuchFieldException e) {
// Ignore
} catch (Throwable e) {
// Ignore
}
if (faultElement == null) {
// Should we be responsible for calculating a default
fault_elem
// or leave this to someone else???
return null;
}
// Having set the physical and DB already, we now set the logical
of the fault
faultDataType.setLogical(new XMLType(faultElement, null));
return faultDataType;
}
The above is code that would be called in some form from
DataBindingJavaInterfaceProcessor.processInterface(), passing in the
exception DataType.
In this code, I'm finding the fault bean class through a
databinding-neutral
look at the getFaultInfo() return type. I'm then setting the DB on this
based on the normal DB.introspect() routine (i.e. the same introspection
of input and output types to find the databinding).
Step 4 is to set the logical, again in a databinding-neutral way.
I also have a step 3 where the ExceptionHandler is now stored in
theDataType. This is a non-trivial change which could probably
really use some improvement (it brings up build and intra-project
dependency issues). I wanted to have a working prototype before
posting this, however, and this is how I did it. The ExceptionHandler
still has the job of implementing createException() and getFaultInfo()
but
not getFaultType() any more
I also have a simple routine to do something similar for "POJO
exceptions"
which do not have a fault bean and which have a Java, not an XML
logical type.
public DataType<?> getFaultFromExceptionDataType(DataType<?>
excDataType) {
//
// 1. Fault physical and Exception physical are one and the same
//
Object excPhysical = excDataType.getPhysical();
if (!(excPhysical instanceof Class)) {
return null; // Should we be throwing exc instead?
}
Class excClass = (Class)excPhysical;
// Only the physical is set here.
DataType faultDataType = new DataTypeImpl(null, excClass, null);
faultDataType.setExceptionPhysical(excClass);
//
// 2. Send through already-existing introspection routine
//
// Don't try to propagate annotations yet. Note the DB on
thefaultDataType
// is set by the databinding according to the implied contract we
have going.
dataBindingRegistry.introspectType(faultDataType, null);
//
// 3) instantiate an exception handler for this fault from
theappropriate DB
and
// set in the fault DT
//
DataBinding faultDataBinding =
dataBindingRegistry.getDataBinding(
faultDataType.getDataBinding());
ExceptionHandler excHandler =
faultDataBinding.getExceptionHandler();
faultDataType.setExceptionHandler(excHandler);
//
// 4) set the logical to be the exc class as well
//
// Having set the physical and DB already, we now set the logical
of the fault
faultDataType.setLogical(excClass);
return faultDataType;
}
Assuming it gets that far, I'm not sure whether it would be better to
have
these pieces of code in classes which are added to some registry
(like the databinding registry), or whether it would be better to have
one, master, order in which the exception to fault
mapping is assumed to take place in Tuscany.
I coded up a parallel modification in WSDL introspection in the class:
org.apache.tuscany.sca.interfacedef.wsdl.introspect.WSDLOperation.
Basically I just need to set "FaultException" as the exceptionPhysical on
top of what had been done, as the "logical" was being set just fine
already.
------------------------------------------------------------------------------
So, this is getting long........
Where I see this going from here is.. if this sounds interesting to
people, it would be interesting to try incorporating
the Axis2 exc->fault mapping pattern with the example from the recent
JIRA
Simon worked on into this new view I'm working on.
Another pattern I could imagine would be the one mentioned in JAX-WS Sec.
3.7 in cases in which the exceptions don't conform to the Sec 2.5
pattern.
And it might be that my point about "decoupling" is only partially
right. Maybe the POJO exception case will always be associated
withJavaBeans DB
and maybe the JAX-WS Sec 3.7 case will always be associated withJAXB.
However, I wanted to get this posted before thinking through all those
permutations.
Thanks,
Scott