JAXB with XMLStreamReader and StreamFilter does not finalize

355 views Asked by At

I've tried to use the an XMLStreamReader in combination with a StreamFilter to partially read from a XML file. The core method for this looks like this:

public ScheduleList getScheduleListFromXMLFile(File scheduleListFile, ScheduleTimeRange scheduleTimeRange) {

    ScheduleList scheduleList = null;
    try {
        JAXBContext context = JAXBContext.newInstance(this.getJAXBContextClasses());
        Unmarshaller unMarsh = context.createUnmarshaller();

        InputStream inputStream = new FileInputStream(scheduleListFile);

        XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
        XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(inputStream);

        // --- Check if a system state filter is to be applied ------------
        if (scheduleTimeRange!=null && scheduleTimeRange.isValidTimeRange()==true) {
            // --- Add a filter to the XMLStreamReader -------------------- 
            xmlStreamReader = xmlInputFactory.createFilteredReader(xmlStreamReader, new ScheduleList_XMLStreamFilter(scheduleTimeRange));
        }

        JAXBElement<ScheduleList> jaxbElement = (JAXBElement<ScheduleList>) unMarsh.unmarshal(xmlStreamReader);
        xmlStreamReader.close();
        inputStream.close();

        if (jaxbElement!=null) {
            // --- Check if the loaded object is of the required type -----
            Object loadedObject = jaxbElement.getValue();
            if (loadedObject instanceof ScheduleList) {
                scheduleList = (ScheduleList) loadedObject;
            } else {
                JOptionPane.showMessageDialog(this.getOwnerFrame(), getWarningString(), "Wrong object type", JOptionPane.ERROR_MESSAGE);
            }
        }

    } catch (JAXBException e) {
        if (e.getCause() instanceof InstantiationException) {
            // --- Try to load based on the previous version ---
            scheduleList = EomModelUpdater.getScheduleListFromXMLFile(scheduleListFile);
        } else {
            e.printStackTrace();
        }

    } catch (IOException | XMLStreamException ex) {
        ex.printStackTrace();

    } finally {
        convertToTreeScheduleList(scheduleList);            
    }
    return scheduleList;
}

In case that the 'scheduleTimeRange' is null, no filter will be applied and the unmarshal operation works fine. In case that a filter is applied, the operation does not return from the method / line

JAXBElement<ScheduleList> jaxbElement = (JAXBElement<ScheduleList>) unMarsh.unmarshal(xmlStreamReader);

, even the filter applied tells me that the end of the document is reached. The 'accept(XMLStreamReader reader)' method of our filter that implements a StreamFilter looks like this:

public boolean accept(XMLStreamReader reader) {

    if (reader.isStartElement()==true) {
        // --- Starting elements --------------------
        if (reader.getLocalName().equals("TechnicalSystemStateList")==true) {
            // --- Set default accept ---------------
            this.acceptSystemState = true;

        } else if (reader.getLocalName().equals("GlobalTime")==true) {
            // --- Check if TSSE can be accepted ---- 
            try {
                String globalTimeText = reader.getElementText();
                Long globalTime = this.parseLong(globalTimeText);
                if (globalTime!=null) {
                    this.acceptSystemState = this.scheduleTimeRange.isWithinTimeRange(globalTime);
                }

            } catch (XMLStreamException xmlStreamEx) {
                xmlStreamEx.printStackTrace();
            }
        }

    } else if (reader.isEndElement()==true) {
        // --- Ending elements ----------------------
        if (reader.getLocalName().equals("TechnicalSystemStateList")) {
            return this.acceptSystemState;
        }
    }


    try {
        if (reader.hasNext()==false) {
            System.out.println("Found end of document");

        }

    } catch (XMLStreamException e) {
        e.printStackTrace();
    }

    return true;
}

I have no clue what's wrong here and would be very happy about a hint. Currently, we're working with Java 8 (jdk1.8.0_191).

1

There are 1 answers

0
CDerksen On

After hours of searching for the reason that the XMLStreamReader did not termintate, I found the answer in the bridge() method of the StAXStreamConnector. As to see in the code below, the while loop only terminates if we are at the end of an element and if the depth is 0. What I did (and assumed to be right) with my StreamFilter was to not accept those end tags, where my filter criteria found 'somthing out of range'. In the consequence the variable depth was not decreased.

I wonder why this implementation does not look for END_DOCUMENT ... !?

            // --- some further code above ---
            OUTER:
            while(true) {
                // These are all of the events listed in the javadoc for
                // XMLEvent.
                // The spec only really describes 11 of them.
                switch (event) {
                    case XMLStreamConstants.START_ELEMENT :
                        handleStartElement();
                        depth++;
                        break;
                    case XMLStreamConstants.END_ELEMENT :
                        depth--;
                        handleEndElement();
                        if(depth==0)    break OUTER;
                        break;
                    case XMLStreamConstants.CHARACTERS :
                    case XMLStreamConstants.CDATA :
                    case XMLStreamConstants.SPACE :
                        handleCharacters();
                        break;
                    // otherwise simply ignore
                }
                event=staxStreamReader.next();
            }
            // --- some further code below ---

Finally, I wrote my own SAX parser/handler in order to be able to filter elements - which in turn increased the file reading speed in comparison to JAXB.