Please bear with me on this long-winded email. If someone can tell me what I'm doing
wrong, that would be greatly appreciated! If not, I will submit to Bugzilla. This bug
arises when the mapping file specifies nested elements, some elements contain a
class-defined object, and not all elements are present in the XML input. The nested
levels are declared with the location attribute of bind-xml. My interpretation of what
is going wrong is that Castor is actually attempting to determine the type of an
underlying private member var, even though public get/set methods exist.
The problem can be demonstrated with the code that follows, using
castor-0.9.5.3-xml.jar and xercesImpl.jar version 2.6.1. First I present two XML
inputs - one unmarshals OK, the other throws exception. Then I show the exception.
FInally I show the mapping.xml and Java sources.
Messages have optionally up to 4 levels: levelA, levelB, levelC, levelD. levels C and
D are optional. levelB and levelD contain a field of type string, and my Java class
has corresponding String get/set methods. The bug is manifest when the underlying
private member is a user-defined class; there is no bug if I change the private member
to String!
None of the source is generated with Castor tools - I am working with legacy code.
Code has been somewhat simplified and names changed to protect the guilty :-)
Here are two sample inputs.
=====================
This one unmarshals without error, it contains elements down to levelD: FILENAME:
test2_ex2.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE message SYSTEM "pm_to_pki.dtd">
<message>
<levelA>
<code>3</code> <!-- Send certificate data -->
<levelB1>
<CA>4572e42722c3e68bc108a01b2fc59b96</CA>
</levelB1>
<levelB2>
<levelC>
<levelD>
<certUUID>7c7d61a577d8ed6b2f75db2188e2b8d0</certUUID>
<certStatusFlags>-1</certStatusFlags> <!-- active -->
<certBlob>
-----BEGIN CERTIFICATE-----
MIIC9DCCA, etc.
-----END CERTIFICATE-----
</certBlob>
</levelD>
</levelC>
</levelB2>
</levelA>
</message>
=====================
The next throws an exception, it contains elements only down to levelB: FILENAME
test3_ex2.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE message SYSTEM "pm_to_pki.dtd">
<message>
<levelA>
<code>4</code>
<levelB1>
<CA>4572e42722c3e68bc108a01b2fc59b96</CA>
</levelB1>
</levelA>
</message>
==========
Exception:
#startElement: levelB1
wrapper-element: levelB1
#startElement: CA
#characters: 4572e42722c3e68bc108a01b2fc59b96
#endElement: CA
#endElement: levelB1
#endElement: levelA
#endElement: message
java.lang.IllegalStateException: Field access error: CertUUID(java.lang.String) access
resulted in exception: java.lang.reflect.InvocationTargetException
at
org.exolab.castor.mapping.loader.FieldHandlerImpl.getValue(FieldHandlerImpl.java:383)
at org.exolab.castor.xml.FieldValidator.validate(FieldValidator.java:195)
at
org.exolab.castor.xml.util.XMLClassDescriptorImpl.validate(XMLClassDescriptorImpl.java:892)
at org.exolab.castor.xml.Validator.validate(Validator.java:122)
at org.exolab.castor.xml.UnmarshalHandler.endElement(UnmarshalHandler.java:843)
at
org.exolab.castor.xml.UnmarshalHandler.endElement(UnmarshalHandler.java:1038)
at org.apache.xerces.parsers.AbstractSAXParser.endElement(Unknown Source)
at org.apache.xerces.impl.dtd.XMLNSDTDValidator.endNamespaceScope(Unknown
Source)
at org.apache.xerces.impl.dtd.XMLDTDValidator.handleEndElement(Unknown Source)
at org.apache.xerces.impl.dtd.XMLDTDValidator.endElement(Unknown Source)
at org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanEndElement(Unknown
Source)
at
org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown
Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown
Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
at org.exolab.castor.xml.Unmarshaller.unmarshal(Unmarshaller.java:605)
at main.main(main.java:36)
========================
DTD: FILENAME pm_to_pki.dtd
<!-- DTD file for passing messages from the Policy Manager
to the PKID to perform requests services. This definition
may later grow to encompass other request/response needs.
-->
<!-- main elements -->
<!ELEMENT message (levelA+)>
<!ELEMENT levelA (code, levelB1?, levelB2?)>
<!ELEMENT code (#PCDATA)>
<!ELEMENT levelB1 (CA?)>
<!ELEMENT levelB2 (count?, levelC?)>
<!-- levelB1 elements -->
<!ELEMENT CA (#PCDATA)>
<!-- levelB2 elements -->
<!ELEMENT count (#PCDATA)>
<!-- levelC elements, it must contain levelD -->
<!ELEMENT levelC (levelD)>
<!-- levelD elements -->
<!ELEMENT levelD (certUUID,certStatusFlags,certBlob?)>
<!ELEMENT certUUID (#PCDATA)>
<!ELEMENT certStatusFlags (#PCDATA)>
<!ELEMENT certBlob (#PCDATA)>
========= SOURCES =========
===== MAPPING FILE ReplyPkiMapping.xml
<?xml version="1.0"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Object Mapping DTD Version 1.0//EN"
"http://castor.exolab.org/mapping.dtd">
<mapping>
<class name="MessageObj">
<map-to xml="message"/>
<field name="MessageCode" type="short">
<bind-xml name="code" location="levelA" node="element"/>
</field>
<field name="CA" type="string">
<bind-xml name="CA" location="levelA/levelB1" node="element"/>
</field>
<field name="DataCount" type="int">
<bind-xml name="count" location="levelA/levelB2" node="element"/>
</field>
<field name="CertUUID" type="string">
<bind-xml name="certUUID" location="levelA/levelB2/levelC/levelD"
node="element"/>
</field>
<field name="CertStatusFlags" type="int">
<bind-xml name="certStatusFlags" location="levelA/levelB2/levelC/levelD"
node="element"/>
</field>
<field name="CertBlob" type="string">
<bind-xml name="certBlob" location="levelA/levelB2/levelC/levelD"
node="element"/>
</field>
</class>
</mapping>
===== FILE main.java
import org.exolab.castor.mapping.Mapping;
import org.exolab.castor.mapping.MappingException;
import org.exolab.castor.xml.Unmarshaller;
import org.exolab.castor.xml.Marshaller;
import java.io.IOException;
import java.io.FileReader;
import java.io.OutputStreamWriter;
import org.xml.sax.InputSource;
public class main {
public static void main(String args[]) {
Mapping mapping = new Mapping();
try {
mapping.loadMapping( "ReplyPkiMapping.xml" );
MessageObj mo = new MessageObj();
Unmarshaller unmar = new Unmarshaller(mo);
unmar.setMapping(mapping);
// test2_ex2.xml is a file that contains element data down to levelD
// unmarshal test2 and no exception occurs.
// test3_ex2.xml is a file that contains element data only down to levelB.
// unmarshal test3 and get java.lang.reflect.InvocationTargetException,
// which occurs on an element in levelD - but the input has no levelD.
// The declaration of the offending element in the MessageObj class is
crucial -
// if private member var is declared UUID, exception occurs. If declared
String,
// no problem occurs. Evidently Castor examines the class definition not
during
// setMapping, but later, when actually performing unmarshal.
// unmar.unmarshal(new InputSource(new FileReader("test2_ex2.xml")));
unmar.unmarshal(new InputSource(new FileReader("test3_ex2.xml")));
System.out.println("MessageObj: reply =");
mo.printReply();
} catch (Exception e) {
e.printStackTrace();
return;
}
}
}
======= FILE MessageObj.java
import java.net.*;
import java.io.*;
public class MessageObj
{
protected short _msgCode;
public short getMessageCode() { return _msgCode; }
public void setMessageCode(short val) { _msgCode = val; }
// This member appears in LevelB of the XML.
// A value for this element is always present in XML inputs for unmarshalling.
// Note that the type is a new class, and not one of the types defined for Castor,
// but in the mapping file, ReplyPkiMapping.xml, the type is given as 'string'.
private UUID _caUUID = null;
public String getCA() { return _caUUID.toString(); }
public void setCA( String val ) { _caUUID = new UUID(val); }
/* GDN - here's where it gets truly bizarre ...
* If private field _certUUID is declared as String (and get/set are adjusted),
* then the file test3_ex2.xml will unmarshal with no errors.
* If private field _certUUID is declared as UUID, then unmarshal will throw
* java.lang.reflect.InvocationTargetException when unmarshalling the same file.
*
* Note that expected get/set methods exist and you would think that Castor
* would go no further, since the underlying member var is private. I even tried
* explicitly declaring the names of get and set as an attribute of the element,
* but this was no help.
* I even tried changing the private member var name from '_certUUID' to 'xyz'
* and it still threw an exception even though the public set/get methods were
unchanged.
*
* One last puzzle - there is another field of type UUID, this is _caUUID.
* This field does not cause a problem - is it because it is always present in the
* xml input, but certUUID is optional? Or is it because caUUID appears in levelB,
* but certUUID appears in levelD?
*/
// private String _certUUID = null;
private UUID _certUUID = null;
public String getCertUUID()
{
// return _certUUID;
return _certUUID.toString();
}
public void setCertUUID( String val )
{
// _certUUID = val;
_certUUID = new UUID(val);
}
private int _certStatusFlags = 0;
public int getCertStatusFlags() { return _certStatusFlags; }
public void setCertStatusFlags(int val) { _certStatusFlags = val; }
private int _dataCnt = 0; // Expected number of data blob replies
public int getDataCount() { return _dataCnt; }
public void setDataCount( int val ) { _dataCnt = val; }
String _certBlob;
public String getCertBlob() { return _certBlob; }
public void setCertBlob(String val) { _certBlob = val; }
// constructor for message replies
public MessageObj()
{
}
// constructor for message requests
public MessageObj(short msgCode, String theCA)
{
_msgCode = msgCode;
_caUUID = new UUID(theCA);
}
public void printReply()
{
System.out.println(" Ca UUID: " + getCA());
System.out.println(" DataCnt: " + getDataCount());
System.out.println(" MsgCode: " + getMessageCode());
System.out.println(" Cert UUID: " + getCertUUID());
System.out.println(" CertStatusFlags: " + getCertStatusFlags());
if (getCertBlob() != null)
{
System.out.println(" MsgBlob Length: " + getCertBlob().length());
System.out.println(" MsgBlob: " + new String(getCertBlob()));
}
}
}
------
Glenn Nelson in Scotts Valley
mailto:[EMAIL PROTECTED]
Office: 831-440-6484
Mobile: 831-419-8929
-----------------------------------------------------------
If you wish to unsubscribe from this mailing, send mail to
[EMAIL PROTECTED] with a subject of:
unsubscribe castor-dev