Frazz
Frazz

Reputation: 29

How to determine that JAXB is done marshalling to file

On a server there is a process that picks up files from a specific directory every specific interval (say 5 minutes). The files that are being pickup is are generated by a webservice. JAXB marshalling converts the files from objects to xml files. The issue is it occurs frequently that files are being picked up before they are done. A solution is to place the files in a temp directory or give them a temporary specific extension to let the polling process know to skip these. Afterwards the files can be moved to the processing directory or extension can be changed so that the polling process can pick them up.

The method looks like this;

    private void writeToFile(Object obj, String outputFileName) {
    LOG.debug("Executing operation writeToFile using filename '{}' and object of type '{}'.", outputFileName, obj.getClass());

    try {
        Marshaller jaxbMarshaller = JAXB_CONTEXT.createMarshaller();
        jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        jaxbMarshaller.marshal(obj, new File(outputFileName));
        LOG.debug("Wrote request to file '{}'.", outputFileName);
    } catch (JAXBException e) {
        LOG.error("Exception occurred while writing request to file:", e);
    }
    LOG.debug("Done executing operation writeToFile.");
}

My question is hot to dtermine that the marshalling proces is done so that the file can be released for further processing?

Upvotes: 0

Views: 1646

Answers (2)

Saran
Saran

Reputation: 146

Please download the code from this link [https://turreta.com/2017/03/07/jaxb-perform-pre-and-post-processing-with-unmarshaller-listener/] Add the below Person Marshall Listener code to the source, as this will be called for each node have checked the root Node instance inside of Person (this needs to be modified as your base node) in both before and After Marshall Methods, also used a count of 2 because the start and end node calls this each time, as this should be called only on end node check if count == 2

package com.turreta.jaxb.unmarshaller.listener;

    public class PersonMarshallListener extends javax.xml.bind.Marshaller.Listener {

            int beforeCount = 1;
            int afterCount = 1;

          @Override
           public void beforeMarshal(Object source) {
              super.beforeMarshal(source);
              if (source instanceof Person) {

                  if (beforeCount == 2) {
                      beforeCount = 1;
                      System.out.println("BEFORE MARSHAL");
                  } else {
                      beforeCount++;
                  }
              }

           }

           @Override
           public void afterMarshal(Object source) {
               super.afterMarshal(source);

               if (source instanceof Person) {

                   if (afterCount == 2) {

                       afterCount = 1;
                       System.out.println("AFTER MARSHAL");
    //                 System.out.println("This will be called once the marshall has been completed");
                   } else {
                       afterCount++;
                   }
               }


           }

    }

And replace the DemoApp with the following code

package com.turreta.jaxb.unmarshaller.listener;

import java.io.FileInputStream;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;

public class DemoApp {
    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Person.class);

        XMLInputFactory xif = XMLInputFactory.newFactory();
        FileInputStream xml = new FileInputStream("src/main/resources/person.xml");
        XMLStreamReader xsr = xif.createXMLStreamReader(xml);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        PersonUnmarshallListener pul = new PersonUnmarshallListener();
        unmarshaller.setListener(pul);

        Person person = (Person) unmarshaller.unmarshal(xsr);
        System.out.println(person);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setListener(new PersonMarshallListener());
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(person, System.out);


    }
}

Upvotes: 1

Tom Van Rossom
Tom Van Rossom

Reputation: 1470

Here is some code, how I solved it (Spring Integration)

You create a buffer to store files that are not old enough and process them later.

I know it is not perfect (with the sleep), but it works. The better solution is not working with file-exchanges, but transferring the data with REST-api's.

import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.integration.core.MessageSource;
import org.springframework.messaging.Message;

import java.io.File;
import java.util.List;

public class LastModifiedFileReadingMessageSource implements MessageSource<File> {

    private final Logger log = LoggerFactory.getLogger(LastModifiedFileReadingMessageSource.class);
    private final MessageSource fileReadingMessageSource;
    private final List<Message<File>> buffer = Lists.newArrayList();
    private final long age = 60;

    public LastModifiedFileReadingMessageSource(MessageSource fileReadingMessageSource) {
        this.fileReadingMessageSource = fileReadingMessageSource;
    }

    public Message<File> receive() {
        Message<File> message = fileReadingMessageSource.receive();
        while (message != null) {
            if (isOldEnough(message)) {
                return message;
            } else {
                buffer.add(message);
                log.info("Buffering file which is not old enough: {}; Buffer size: ", message, buffer.size());
            }
            message = fileReadingMessageSource.receive();
        }
        while (!buffer.isEmpty()) {
            message = buffer.stream().filter(this::isOldEnough).findFirst().orElse(null);
            if (message != null) {
                buffer.remove(message);
                log.info("Use file from buffer: {}; Buffer size: ", message, buffer.size());
                return message;
            }
            sleep();
        }
        return message;
    }

    private void sleep() {
        try {
            log.info("Go to sleep for a while... ({} seconds)", age);
            Thread.sleep(this.age * 1000);
        } catch (InterruptedException e) {
            log.error("Thread.sleep was never a good idea ;(", e);
        }
    }

    private boolean isOldEnough(Message<File> message) {
        long now = System.currentTimeMillis() / 1000;
        return (message.getPayload().lastModified() / 1000 + this.age <= now);
    }
}

Upvotes: 0

Related Questions