kerner1000
kerner1000

Reputation: 3558

Get progress information during JAXB de-/serialization

Is there a way to register some progress monitor on JAXB Marshaller and Unmarshaller? I would like to show some progress information in my GUI while data is de-/serialized.

I see that you can set a Unmarshaller.Listener and Marshaller.Listener, which have a "before" and "after" method. Nevertheless, I do not see any straight forward way to get the total number of elements to serialize.

I would need that obviously to calculate some "percentage done" info.

Upvotes: 1

Views: 217

Answers (2)

Frédéric
Frédéric

Reputation: 583

Would doing a more low-level approach by leveraging on the InputStream be an acceptable solution?

E.g.

import java.io.IOException;
import java.io.InputStream;
import java.util.function.DoubleConsumer;

public class InputStreamWithProgressDecorator extends InputStream {
    /** Input stream to be decorated */ private final InputStream inputStream;
    /** Amount of byte read */ private long position = 0L;
    /** File size */ private final long length;
    /** Mark */ private int mark = 0;
    /** Consumer of the progress */ private final DoubleConsumer callBack;
    
    public InputStreamWithProgressDecorator(final InputStream is, final long l, final DoubleConsumer cb) {
        inputStream = is;
        length = l;
        callBack = cb;
    }

    private void setPosition(final long fp) {
        position = fp;
        callBack.accept(getProgress());
    }

    public double getProgress() {
        return length == 0L ? 100d : ((double) position) * 100d / ((double) length);
    }

    public long getPosition() {
        return position;
    }

    @Override
    public int read(byte[] b) throws IOException {
        final int rc = inputStream.read(b);
        setPosition(position + rc);
        return rc;
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        final int rc = inputStream.read(b, off, len);
        setPosition(position + rc);
        return rc;
    }

    @Override
    public byte[] readAllBytes() throws IOException {
        final byte[] result = inputStream.readAllBytes();
        setPosition(position + result.length);
        return result;
    }

    @Override
    public byte[] readNBytes(int len) throws IOException {
        final byte[] result = inputStream.readNBytes(len);
        setPosition(position + result.length);
        return result;
    }

    @Override
    public int readNBytes(byte[] b, int off, int len) throws IOException {
        final int rc = inputStream.readNBytes(b, off, len);
        setPosition(position + rc);
        return rc;
    }

    @Override
    public long skip(long n) throws IOException {
        final long rc = inputStream.skip(n);
        setPosition(position + rc);
        return rc;
    }

    @Override
    public int available() throws IOException {
        return inputStream.available();
    }

    @Override
    public void close() throws IOException {
        inputStream.close();
    }

    @Override
    public synchronized void mark(int readlimit) {
        inputStream.mark(readlimit);
        mark = readlimit;
    }

    @Override
    public synchronized void reset() throws IOException {
        inputStream.reset();
        setPosition(mark);
    }

    @Override
    public boolean markSupported() {
        return inputStream.markSupported();
    }

    @Override
    public int read() throws IOException {
        final int c = inputStream.read();
        setPosition(position + 1);
        return c;
    }
}
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.function.DoubleConsumer;

public class Demo1 {

    public static void main(String[] args) throws IOException {
        final File file = new File(args[0]);
        final DoubleConsumer callBack = p -> System.out.printf("%.0f%%\n", p);
        try (final FileInputStream fis = new FileInputStream(file); final InputStreamWithProgressDecorator is = new InputStreamWithProgressDecorator(fis, file.length(), callBack)) {
            // Simulating JAXB unmarshaller reads
            byte[] buffer = is.readNBytes(1024);
            while (buffer.length != 0) buffer = is.readNBytes(1024); 
        }
    }
}

Or if you have a FileInputStream with a separate Thread approach :

public class FileInputStreamReadProgressThread extends Thread implements UncaughtExceptionHandler {
    /** Input stream */ private final FileInputStream fileInputStream;
    /** File size */ private final long length;
    /** Read progress in percents */ private double progress = 0d;
    /** Exception from thread */ private Throwable exception = null;
    /** Consumer of the progress */ private final DoubleConsumer callBack;

    public FileInputStreamReadProgressThread(final FileInputStream fis, final long l, final DoubleConsumer cb) {
        fileInputStream = fis;
        length = l;
        callBack = cb;
        setUncaughtExceptionHandler(this);
        setName(getClass().getSimpleName());
    }

    public double getProgress() { return progress; }
    public Throwable getException() { return exception; }
    @Override public void uncaughtException(final Thread t, final Throwable e) { exception = e; }
    
    @Override
    public void run() {
        try {
            long position = -1L;
            final FileChannel channel = fileInputStream.getChannel();
            while (!isInterrupted() && channel.isOpen() && position < length) {
                position = channel.position();
                progress = length == 0L ? 100d : ((double)position) * 100d / ((double)length);
                callBack.accept(progress);
                sleep(100L);
            } 
        } catch (final IOException e) {
            exception = e;
        } catch (final InterruptedException e) {
            // Do nothing
        }
    }
}
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.Channels;
import java.util.function.DoubleConsumer;

public class Demo2 {

    public static void main(String[] args) throws IOException {
        final File file = new File(args[0]);
        final DoubleConsumer callBack = p -> System.out.printf("%.0f%%\n", p);
        try (final FileInputStream fis = new FileInputStream(file); final InputStream is = Channels.newInputStream(fis.getChannel())) {
            final FileInputStreamReadProgressThread readProgressThread = new FileInputStreamReadProgressThread(fis, file.length(), callBack);
            readProgressThread.start();
            // Simulating JAXB unmarshaller reads
            is.readAllBytes();
        }
    }
}

Upvotes: 1

TedTrippin
TedTrippin

Reputation: 3662

Is it ok to parse before unmarshalling?

If so, assuming you have a list of objects, you could do something like...

    final String tagName = *** name of tag you are counting ***;
    InputStream in = *** stream of your xml ***;

    SAXParserFactory spf = SAXParserFactory.newInstance();
    SAXParser saxParser = spf.newSAXParser();
    final AtomicInteger counter = new AtomicInteger();
    saxParser.parse(in, new DefaultHandler() {
        @Override
        public void startElement (String uri, String localName, String qName, Attributes attributes) {
            if (localName.equals(tagName))
                counter.incrementAndGet();
        }
    });

Upvotes: 1

Related Questions