JiBX
  1. JiBX
  2. JIBX-228

ArrayIndexOutOfBoundsException caused by org.jibx.extras.DomMapperBase

    Details

    • Type: Bug Bug
    • Status: Closed Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: JiBX 1.1.6
    • Fix Version/s: JiBX 1.2.2
    • Component/s: core
    • Labels:
      None
    • Environment:
      JiBX v1.1.6a
      JDK v1.5.0_06
      Eclipse IDE "Ganymede" v3.4 + JiBX eclipse plugin activated
    • Testcase included:
      yes
    • Patch Submitted:
      Yes
    • Number of attachments :
      2

      Description

      Hello,

      I refactored a web service application that made heavy usage of DOM generation/parsing to use JiBX instead.

      While working on this, I wrote a couple JiBX bindings that rely on the "org.jibx.extras.DomElementMapper" class that comes as a part of JiBX extras.

      Using this mapper to marshall arbitrary portions of DOM trees raised a number of ArrayIndexOutOfBoundsException that prevented my application to success in generating response messages.

      I drilled down these error causes into JiBX source code, and here is what I found (note: all references to source code below are taken from v1.1.6a).

      1) Lookup of known namespaces & prefixes
      ==================================

      In class "org.jibx.extras.DomMapperBase", in many places inside the method "findNamespaceIndex" you return -1 if you don't find a namespace prefix among the list owned by the m_xmlWriter object.
      Here is an example on line 123:

      [...]
      int index = m_xmlWriter.getPrefixIndex(prefix);
      if (index >= 0)

      { return getNamespaceUri(index).equals(uri) ? index : -1; }

      else

      { // line 122 return -1; // line 123 }

      [...]

      This -1 value greatly increases the chances of getting an ArrayIndexOutOfBoundsException from method "getNamespacePrefix(int index)" in class "org.jibx.runtime.impl.XMLWriterNamespaceBase".

      The -1 value results from a failure in finding the exact given prefix in the list of prefixes the m_xmlWriter object owns.
      I think that looking for the namespace itself inside the list of namespace URIs owned by the m_xmlWriter object will do the right job, while suppressing many of the exceptions.
      To me it sounds valid, because it happens that the namespace URI is known but just associated to a different prefix.

      During the marshalling of various DOM elements, I got bothered by this missing lookup in several places, so I implemented it into a new private method:

      /**

      • Get index number for declared namespace.
        *
      • @param uri namespace URI (empty string if none)
      • @return namespace index number, or <code>-1</code> if not declared or
      • masked
        */
        private int findNamespaceURIIndex(String uri) {
        String[] uris = m_xmlWriter.getNamespaces();
        for (int i = 0; i < uris.length; i++)
        Unknown macro: { if(uris[i].equals(uri)) { return i; } }

        return -1;
        }

      Then I updated the "findNamespaceIndex" method accordingly, in the 3 following locations (lines 108, 115, and 123):

      [...]
      } else

      { return findNamespaceURIIndex(uri); // line 108 }

      } else

      { return -1; }

      } else

      { return m_defaultNamespaceURI.equals(uri) ? m_defaultNamespaceIndex : findNamespaceURIIndex(uri); // line 115 }

      } else {
      int index = m_xmlWriter.getPrefixIndex(prefix);
      if (index >= 0)

      { return getNamespaceUri(index).equals(uri) ? index : -1; }

      else

      { return findNamespaceURIIndex(uri); // line 123 }

      [...]

      2) Discovery of namespaces & prefixes
      ==============================

      I also had troubles with the strategy for handling some namespaces declarations coming from a DOM tree, with the same kind of symptoms showing up: ArrayIndexOutOfBoundsException rising from class "org.jibx.runtime.impl.StreamWriterBase", more exactly from method "writePrefix".

      For that one I had to look closely at method "marshalElement" in class "org.jibx.extras.DomElementMapper", stepping into the algorithm for getting the right namespace index for the current element.

      Actually, I think the following assignment at line 227 (by the way is the "==" operator wanted here, or is it "equals()" ?):

      [...]
      if (uri == decluri)

      { nsi = defind; // line 227 }

      [...]

      Will occasionally de-activates the lookup for the right index among newly discovered namespaces, which is made at line 251:

      [...]
      for (int i = 0; i < length; i++) {
      prefs[i] = (String)nss.get(i*2);
      uris[i] = (String)nss.get(i*2+1);
      nums[i] = base + i;
      if (nsi < 0 && uri.equals(uris[i])) { // line 251
      if ((prefix == null && prefs[i] == "") ||
      (prefix != null && prefix.equals(prefs[i])))

      { nsi = base + i; }

      }
      }
      [...]

      This may result in an incorrect index being used, because the lookup made at line 251 takes into account the index shift introduced by the addition of new namespaces, while this assignment does not.
      I therefore remove it.

      However the main issue with that method in my humble opinion is the fact we don't check that we have found the element's namespace index just after we checked all the attributes for namespaces declarations.
      I therefore added the following statements just before line 233:

      [...]
      /*

      • if we get there without having found an index for the current
      • element's namespace, we add it to the list.
        */
        if(nsi == -1 && (nss == null || !nss.contains(uri))) { if(nss == null) nss = new ArrayList(); nss.add(prefix == null ? "" : prefix); nss.add(uri == null ? "" : uri); }

        [...]

      I don't alter the value of "nsi", so the aforementioned lookup located at line 251 will assign it with the right value.

      I attach a sample Eclipse project to this report, it contains a sample set of Java classes, XML file, and binding that exhibits the 2nd issue (I cannot easily cause the 1st one to happen).
      The code I bundle features a JUnit test case in class "test.jibx.junit.TestDomMapper".

      I include my version of the "DomMapperBase" class, which enables the code I wrote to work fine.
      I also include a visual summary of the differences between the original version, of the class and mine, hoping this will help.

      Regards,
      Alex.

        Activity

        Hide
        Alexandre Delarge added a comment -

        Sorry I haven't used the preview feature, and did not see the code excerpts get broken by the formatting.

        Here is the properly formatted text I submitted, which should be more readable.

        regards,
        Alex.

        Show
        Alexandre Delarge added a comment - Sorry I haven't used the preview feature, and did not see the code excerpts get broken by the formatting. Here is the properly formatted text I submitted, which should be more readable. regards, Alex.
        Alexandre Delarge made changes -
        Field Original Value New Value
        Attachment jira-jibx-228.txt [ 35682 ]
        Hide
        Alexandre Delarge added a comment -

        While conducing further testings with other, more complex, SOAP messages, I realized that changing the mechanism for the lookup of known namespaces & prefixes (1st issue I reported) does cause other problems because of the method "closeNamespaces" in class "org.jibx.runtime.impl.XMLWriterNamespaceBase".

        The method "closeNamespaces" removes the prefixes associated to namespaces declarations that are nested deep inside an element. As I understand it, this cleanup happens right after the element got marshalled, and right before the next element's sibling is processed.

        With the changes I made in the lookup of known namespaces, the following appears: if the sibling refers to a namespace of which prefix got cleaned up, then a NullPointerException will rise from method "writePrefix" in class "org.jibx.runtime.impl.GenericXMLWriter", because the cleaning can assign the prefix's value with a null String.

        Consequently, it appears the changes I made to the method "findNamespaceIndex" in class "org.jibx.extras.DomElementMapper" by introducing a new method "findNamespaceURIIndex" are dangerous, as they introduce this other issue at least.

        The changes made in the context of the 2nd seem to stay valid so far though.

        Regards,
        Alex.

        Show
        Alexandre Delarge added a comment - While conducing further testings with other, more complex, SOAP messages, I realized that changing the mechanism for the lookup of known namespaces & prefixes (1st issue I reported) does cause other problems because of the method "closeNamespaces" in class "org.jibx.runtime.impl.XMLWriterNamespaceBase". The method "closeNamespaces" removes the prefixes associated to namespaces declarations that are nested deep inside an element. As I understand it, this cleanup happens right after the element got marshalled, and right before the next element's sibling is processed. With the changes I made in the lookup of known namespaces, the following appears: if the sibling refers to a namespace of which prefix got cleaned up, then a NullPointerException will rise from method "writePrefix" in class "org.jibx.runtime.impl.GenericXMLWriter", because the cleaning can assign the prefix's value with a null String. Consequently, it appears the changes I made to the method "findNamespaceIndex" in class "org.jibx.extras.DomElementMapper" by introducing a new method "findNamespaceURIIndex" are dangerous, as they introduce this other issue at least. The changes made in the context of the 2nd seem to stay valid so far though. Regards, Alex.
        Dennis Sosnoski made changes -
        Assignee Dennis Sosnoski [ dsosnoski ]
        Hide
        genadi genov added a comment -

        Hi,

        I get the ArrayIndexOutOfBoundsException because of this bug. I unmarshall a given xml message and then try to marshall exactly the same unmarshalled content. The marshalling process fails in StreamWriterBase:writePrefix(int index) method. The method is called with index -1 and bytes = m_prefixBytes[index]; fails (of course). If I change the index value from -1 to 0 in debug mode every time this method is called with index == -1, then everything works fine .

        Will this bug be fixed in a new release in the near future?

        Show
        genadi genov added a comment - Hi, I get the ArrayIndexOutOfBoundsException because of this bug. I unmarshall a given xml message and then try to marshall exactly the same unmarshalled content. The marshalling process fails in StreamWriterBase:writePrefix(int index) method. The method is called with index -1 and bytes = m_prefixBytes [index] ; fails (of course). If I change the index value from -1 to 0 in debug mode every time this method is called with index == -1, then everything works fine . Will this bug be fixed in a new release in the near future?
        Hide
        Dennis Sosnoski added a comment -

        Added test cases for various combinations of namespace declarations and usages, fixed code to correctly check for existing namespace declarations on marshalling and add new ones where necessary.

        Show
        Dennis Sosnoski added a comment - Added test cases for various combinations of namespace declarations and usages, fixed code to correctly check for existing namespace declarations on marshalling and add new ones where necessary.
        Dennis Sosnoski made changes -
        Status Open [ 1 ] Resolved [ 5 ]
        Resolution Fixed [ 1 ]
        Fix Version/s JiBX 1.2.2 [ 15120 ]
        Fix Version/s JiBX 1.1.6 [ 14068 ]
        Dennis Sosnoski made changes -
        Status Resolved [ 5 ] Closed [ 6 ]

          People

          • Assignee:
            Dennis Sosnoski
            Reporter:
            Alexandre Delarge
          • Votes:
            1 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: