package fr.gael.dhus.datastore.scanner;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicInteger;
import fr.gael.dhus.service.FileScannerService;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import fr.gael.dhus.database.object.FileScanner;
import fr.gael.dhus.database.object.Product;
import fr.gael.dhus.spring.context.ApplicationContextProvider;
public class FileScannerWrapper
{
private static final Logger LOGGER = LogManager.getLogger(FileScannerWrapper.class);
final Long fs_id;
final AtomicInteger startCounter;
final AtomicInteger endCounter;
final AtomicInteger errorCounter;
final AtomicInteger totalProcessed;
String scannerStatus;
String scannerMessage;
String processingErrors = "";
public FileScannerWrapper (final FileScanner persistent_scanner)
{
this.startCounter = new AtomicInteger (0);
this.endCounter = new AtomicInteger (0);
this.errorCounter = new AtomicInteger (0);
this.totalProcessed = new AtomicInteger (0);
this.fs_id = persistent_scanner.getId ();
}
/**
* Case of error during processing: informations are accumulated to be
* displayed to the user.
*/
public synchronized void error (Product p, Throwable e)
{
if ((e!=null) && (e.getMessage ()!=null))
{
String message = "";
if (p != null)
{
String o = p.getOrigin ();
if (o!=null)
{
String file=o.substring (o.lastIndexOf ("/")+1, o.length ());
message="(" + file + ")";
}
}
processingErrors +=e.getMessage () + message + "<br>\n";
}
errorCounter.incrementAndGet ();
// As far as endIngestion is not called in case of error, it is
// necessary to run it manually.
if ((endCounter.get () + errorCounter.get ()) >= getTotalProcessed ())
{
processingsDone(null);
}
}
/**
* Called on fatal error: the scanner crashed and no processing
* are expected passed this event. scanner status forced to ERROR,
* and error message is reported.
*/
public synchronized void fatalError (Throwable e)
{
// Force the scanner status to ERROR.
scannerStatus = fr.gael.dhus.database.object.FileScanner.STATUS_ERROR;
processingsDone(e.getMessage ());
}
/**
* Called at products ingestion start.
*/
public synchronized void startIngestion ()
{
startCounter.incrementAndGet ();
}
/**
* End of a product ingestion: check if the scanner is finished, and all
* processing are completed, in this case, it modifies the scanner status
* and message to inform user of finished processings.
*/
public synchronized void endIngestion ()
{
endCounter.incrementAndGet ();
LOGGER.info("End of product ingestion: processed=" +
endCounter.get () + ", error=" + errorCounter.get () + ", inbox=" +
(totalProcessed.get () - (endCounter.get () + errorCounter.get ())) +
", total=" + totalProcessed.get () + ".");
// Total number of product processed shall be coherent with
// passed/non-passed number of products.
if ((endCounter.get () + errorCounter.get ()) >= getTotalProcessed ())
{
this.scannerStatus = FileScanner.STATUS_OK;
processingsDone(null);
}
}
/**
* Notifies that the scanned finished its processing.
* If the status is "ERROR"
* @param status
* @param message
*/
public void setScannerDone (String status, String message)
{
this.scannerStatus = status;
this.scannerMessage = message;
// CASE of scanner stopped before first processing or no processing
// to be performed.
// If all processing started are finished and all processing
// provided by the scanner to the processing manager are taken into
// account.
if ((startCounter.get () >= (endCounter.get () + errorCounter.get ())) &&
(startCounter.get () >= getTotalProcessed ()))
{
processingsDone(null);
}
}
/**
* Notifies the scanner that the processings are done.
*/
protected synchronized void processingsDone(String ended_message)
{
LOGGER.info(
"Scanner and processings are completed: update the UI status.");
SimpleDateFormat sdf = new SimpleDateFormat (
"EEEE dd MMMM yyyy - HH:mm:ss", Locale.ENGLISH);
String processing_message = "Ingestion completed at " +
sdf.format (new Date ()) + "<br>\nwith " + endCounter.get () +
" products processed and " + errorCounter.get () +
" error" + (errorCounter.get () >1?"s":"") +
" during this processing.<br>\n";
if (!processingErrors.isEmpty ())
processing_message += "<u>Processing error(s):</u><br>\n" +
processingErrors;
if (ended_message!= null)
{
processing_message += ended_message + "<br>\n";
}
FileScannerService fs_service =
ApplicationContextProvider.getBean (FileScannerService.class);
if (fs_id != null)
{
// Set the scanner info
FileScanner persistentScanner = fs_service.getFileScanner (fs_id);
persistentScanner.setStatus (scannerStatus);
persistentScanner.setStatusMessage (truncateMessageForDB(
persistentScanner.getStatusMessage () + scannerMessage +
"<br>\n" + processing_message));
fs_service.updateFileScanner (persistentScanner);
}
}
/**
* Total processed is the effective number of products that are submitted
* to the processing manager to be ingested. This count excludes the
* products recognized as already ingested, or products that generates
* exception during submission. This count includes submitted products
* even if they causes exception during processing steps.
*
* To be able to return this value, scanner execution should be finished
* with a status. Otherwise, the method waits for the availability.
*/
public int getTotalProcessed ()
{
return totalProcessed.get ();
}
public void setTotalProcessed (int total_processed)
{
totalProcessed.set (total_processed);
}
public void incrementTotalProcessed ()
{
totalProcessed.incrementAndGet ();
}
/**
* Database status message length is limited to 4096
* @since 0.4.0
* @param message to truncate
* @return truncated message
*/
private String truncateMessageForDB (String message)
{
if (message.length ()>4096)
{
return message.substring (0, 4090)+"...";
}
return message;
}
}