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

Reply via email to