BRK2DirectoryScanner.java
/*
* Copyright (C) 2022 B3Partners B.V.
*
* SPDX-License-Identifier: MIT
*
*/
package nl.b3p.brmo.service.scanner;
import static nl.b3p.brmo.persistence.staging.AutomatischProces.ProcessingStatus.ERROR;
import static nl.b3p.brmo.persistence.staging.AutomatischProces.ProcessingStatus.PROCESSING;
import static nl.b3p.brmo.persistence.staging.AutomatischProces.ProcessingStatus.WAITING;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import javax.persistence.Transient;
import javax.xml.stream.XMLStreamException;
import javax.xml.transform.TransformerException;
import nl.b3p.brmo.loader.BrmoFramework;
import nl.b3p.brmo.loader.entity.Brk2Bericht;
import nl.b3p.brmo.loader.util.BrmoException;
import nl.b3p.brmo.loader.xml.Brk2SnapshotXMLReader;
import nl.b3p.brmo.persistence.staging.AutomatischProces;
import nl.b3p.brmo.persistence.staging.BRK2ScannerProces;
import nl.b3p.brmo.persistence.staging.Bericht;
import nl.b3p.brmo.persistence.staging.LaadProces;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.comparator.NameFileComparator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.stripesstuff.stripersist.Stripersist;
/**
* Directory scanner for BRK 2 berichten in xml formaat.
*
* @author Mark Prins
*/
public class BRK2DirectoryScanner extends AbstractExecutableProces {
private static final Log log = LogFactory.getLog(BRK2DirectoryScanner.class);
private final BRK2ScannerProces config;
@Transient private ProgressUpdateListener listener;
public BRK2DirectoryScanner(BRK2ScannerProces config) {
this.config = config;
}
/**
* Leest de xml berichten uit de directory in in de database, indien geconfigureerd worden
* bestanden gearchiveerd.
*
* @throws BrmoException als de directory niet lees/blader/schrijfbaar is
*/
@Override
public void execute() throws BrmoException {
this.execute(
new ProgressUpdateListener() {
@Override
public void total(long total) {}
@Override
public void progress(long progress) {}
@Override
public void exception(Throwable t) {
log.error(t);
}
@Override
public void updateStatus(String status) {}
@Override
public void addLog(String log) {}
});
}
@Override
public void execute(ProgressUpdateListener listener) {
final EntityManager em = Stripersist.getEntityManager();
this.listener = listener;
config.setStatus(PROCESSING);
StringBuilder sb = new StringBuilder(AutomatischProces.LOG_NEWLINE);
String oldLog = config.getLogfile();
if (oldLog != null) {
if (oldLog.length() > OLD_LOG_LENGTH) {
sb.append(oldLog.substring(oldLog.length() - OLD_LOG_LENGTH / 10));
} else {
sb.append(oldLog);
}
}
String msg =
String.format(
"De BRK 2 scanner met ID %d is gestart op %tc.",
config.getId(), Calendar.getInstance());
log.info(msg);
listener.addLog(msg);
sb.append(msg);
// validatie van de directories, kunnen we lezen/bladeren en evt. schrijven?
final File scanDirectory = new File(this.config.getScanDirectory());
if (!scanDirectory.isDirectory() || !scanDirectory.canExecute()) {
config.setStatus(ERROR);
msg = String.format("De scan directory '%s' is geen executable directory", scanDirectory);
config.setLogfile(msg);
config.setSamenvatting("Er is een fout opgetreden, details staan in de logs");
this.listener.exception(new BrmoException(msg));
return;
}
// validatie archief directory
final String aDir = this.config.getArchiefDirectory();
File archiefDirectory = null;
if (aDir != null) {
archiefDirectory = new File(aDir);
archiefDirectory.mkdirs();
if (!archiefDirectory.isDirectory() || !archiefDirectory.canWrite()) {
config.setStatus(ERROR);
msg =
String.format(
"De archief directory '%s' is geen beschrijfbare directory, er worden geen bestanden gearchiveerd.",
archiefDirectory);
sb.append(msg);
config.setSamenvatting("Er is een fout opgetreden, details staan in de logs");
log.error(msg);
archiefDirectory = null;
this.listener.exception(new BrmoException(msg));
}
if (!scanDirectory.canWrite()) {
config.setStatus(ERROR);
msg =
String.format(
"De scan directory '%s' is geen beschrijfbare directory, er worden geen bestanden gearchiveerd.",
scanDirectory);
sb.append(msg);
config.setSamenvatting("Er is een fout opgetreden, details staan in de logs");
log.error(msg);
archiefDirectory = null;
this.listener.exception(new BrmoException(msg));
}
}
config.setLogfile(sb.toString());
File[] files =
scanDirectory.listFiles((File dir, String name) -> name.toLowerCase().endsWith(".xml"));
Arrays.sort(files, NameFileComparator.NAME_COMPARATOR);
processXMLFiles(files, scanDirectory, archiefDirectory, em);
}
/**
* verwerk een bestandenlijst.
*
* @param files array met xml bestanden
* @param scanDirectory te scannen directory
* @param archiefDirectory waar de gescande files worden neergezet als de archief optie actief is
* @param em de te gebruiken EntityManager
*/
private void processXMLFiles(
File[] files, File scanDirectory, File archiefDirectory, EntityManager em) {
StringBuilder sb = new StringBuilder(AutomatischProces.LOG_NEWLINE + config.getLogfile());
String msg;
int filterAlVerwerkt = 0;
int aantalGeladen = 0;
int progress = 0;
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
listener.total(files.length);
for (File f : files) {
if (f.isDirectory()) {
continue;
}
msg = String.format("Bestand %s is gevonden in %s.", f, scanDirectory);
log.info(msg);
listener.addLog(msg);
sb.append(AutomatischProces.LOG_NEWLINE).append(msg).append(AutomatischProces.LOG_NEWLINE);
if (this.isDuplicaatLaadProces(f, BrmoFramework.BR_BRK2)) {
msg = String.format(" Bestand %s is een duplicaat en wordt overgeslagen.", f);
listener.addLog(msg);
log.info(msg);
sb.append(msg).append(AutomatischProces.LOG_NEWLINE);
filterAlVerwerkt++;
} else {
try {
LaadProces lp = new LaadProces();
lp.setBestand_naam(getBestandsNaam(f));
lp.setBestand_datum(getBestandsDatum(f));
lp.setSoort(BrmoFramework.BR_BRK2);
lp.setStatus(LaadProces.STATUS.STAGING_OK);
lp.setOpmerking(
String.format(
"Bestand geladen van %s op %s", f.getAbsolutePath(), sdf.format(new Date())));
lp.setAutomatischProces(
Stripersist.getEntityManager().find(AutomatischProces.class, config.getId()));
Bericht b = new Bericht();
b.setLaadprocesid(lp);
b.setDatum(lp.getBestand_datum());
b.setSoort(BrmoFramework.BR_BRK2);
b.setStatus_datum(new Date());
String fileContent = FileUtils.readFileToString(f, StandardCharsets.UTF_8);
fileContent = fileContent.replaceAll("\u0000.*", "");
b.setBr_orgineel_xml(fileContent);
try {
Brk2SnapshotXMLReader reader =
new Brk2SnapshotXMLReader(
new ByteArrayInputStream(
b.getBr_orgineel_xml().getBytes(StandardCharsets.UTF_8)));
Brk2Bericht bericht = reader.next();
if (bericht.getDatum() != null) {
b.setDatum(bericht.getDatum());
}
b.setBr_xml(bericht.getBrXml());
b.setVolgordenummer(bericht.getVolgordeNummer());
lp.setBestand_naam_hersteld(
bericht.getRestoredFileName(lp.getBestand_datum(), bericht.getVolgordeNummer()));
// Als objectRef niet opgehaald kan worden,dan kan het
// bericht niet verwerkt worden.
String objectRef = bericht.getObjectRef();
if (objectRef != null && !objectRef.isEmpty()) {
b.setObject_ref(bericht.getObjectRef());
b.setStatus(Bericht.STATUS.STAGING_OK);
b.setOpmerking("Klaar voor verwerking.");
} else {
b.setStatus(Bericht.STATUS.STAGING_NOK);
b.setOpmerking(
"Object Ref niet gevonden in bericht-xml, neem contact op met leverancier.");
}
} catch (XMLStreamException | TransformerException e) {
b.setStatus(Bericht.STATUS.STAGING_NOK);
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
msg = String.format("Fout bij parsen BRK 2 bericht (bestand %s): %s ", f, sw);
b.setOpmerking(msg);
log.warn(msg);
}
try {
em.persist(lp);
em.persist(b);
em.merge(this.config);
em.flush();
em.getTransaction().commit();
em.clear();
aantalGeladen++;
msg =
String.format(
" Bestand %s is geladen en heeft status: %s. (Leverde bericht id: %s, met status: %s.)",
f, lp.getStatus(), b.getId(), b.getStatus());
} catch (PersistenceException pe) {
// insert van bericht of lp is mislukt, waarschijnlijk duplicaat bericht
// een laadproces maken met duplicaat status en de xml van het bericht in de
// opmerking stoppen
if (!em.getTransaction().isActive()) {
em.getTransaction().begin();
}
em.getTransaction().rollback();
em.getTransaction().begin();
lp.setId(null);
log.warn(
"Opslaan van bericht uit laadproces "
+ lp.getBestand_naam()
+ " is mislukt. Object referentie is: "
+ b.getObject_ref(),
pe);
log.warn("Duplicaat bericht: " + b.getObject_ref() + ":" + b.getBr_orgineel_xml());
lp.setOpmerking(
lp.getOpmerking()
+ ": Fout, duplicaat bericht. Berichtinhoud: \n\n"
+ b.getBr_xml());
b = null;
lp.setStatus(LaadProces.STATUS.STAGING_DUPLICAAT);
lp.setAutomatischProces(em.find(AutomatischProces.class, this.config.getId()));
em.merge(this.config);
em.persist(lp);
em.flush();
em.getTransaction().commit();
em.clear();
}
log.info(msg);
this.listener.addLog(msg);
sb.append(msg).append(AutomatischProces.LOG_NEWLINE);
} catch (IOException io) {
// mogelijk bij openen en lezen xml bestand
log.error(io.getLocalizedMessage());
sb.append(io.getLocalizedMessage()).append(AutomatischProces.LOG_NEWLINE);
listener.exception(io);
}
}
listener.progress(++progress);
if (archiefDirectory != null) {
// verplaats naar archief
try {
FileUtils.copyFileToDirectory(f, archiefDirectory);
boolean succes = FileUtils.deleteQuietly(f);
if (succes) {
msg = String.format(" Bestand %s is naar archief %s verplaatst.", f, archiefDirectory);
} else {
msg =
String.format(
" Bestand %s is naar archief %s verplaatst, maar origineel kon niet verwijderd worden.",
f, archiefDirectory);
}
} catch (IOException e) {
msg =
String.format(
" Bestand %s is NIET naar archief %s verplaatst, oorzaak: (%s).",
f, archiefDirectory, e.getLocalizedMessage());
log.error(msg);
}
}
log.info(msg);
this.listener.addLog(msg);
sb.append(msg).append(AutomatischProces.LOG_NEWLINE);
}
msg = String.format("Klaar met run op %tc", Calendar.getInstance());
log.info(msg);
listener.updateStatus(msg);
listener.addLog(msg);
sb.append(msg);
listener.addLog("\n\n**** resultaat ****");
listener.addLog("\nAantal bestanden die al waren geladen: " + filterAlVerwerkt);
listener.addLog("\nAantal bestanden geladen: " + aantalGeladen + "\n");
config.setStatus(WAITING);
config.setLogfile(sb.toString());
config.setLastrun(new Date());
config.updateSamenvattingEnLogfile(
"Aantal bestanden die al waren verwerkt: "
+ filterAlVerwerkt
+ AutomatischProces.LOG_NEWLINE
+ "Aantal bestanden geladen: "
+ aantalGeladen
+ AutomatischProces.LOG_NEWLINE);
em.merge(config);
}
}