fr.gouv.culture.thesaurus.autoload.VocabularyAutoload.java Source code

Java tutorial

Introduction

Here is the source code for fr.gouv.culture.thesaurus.autoload.VocabularyAutoload.java

Source

/*
 * This software is governed by the CeCILL-B license under French law and
 * abiding by the rules of distribution of free software. You can use,
 * modify and/or redistribute the software under the terms of the CeCILL-B
 * license as circulated by CEA, CNRS and INRIA at the following URL
 * "http://www.cecill.info".
 *
 * As a counterpart to the access to the source code and rights to copy,
 * modify and redistribute granted by the license, users are provided only
 * with a limited warranty and the software's author, the holder of the
 * economic rights, and the successive licensors have only limited
 * liability.
 *
 * In this respect, the user's attention is drawn to the risks associated
 * with loading, using, modifying and/or developing or reproducing the
 * software by the user in light of its specific status of free software,
 * that may mean that it is complicated to manipulate, and that also
 * therefore means that it is reserved for developers and experienced
 * professionals having in-depth computer knowledge. Users are therefore
 * encouraged to load and test the software's suitability as regards their
 * requirements in conditions enabling the security of their systems and/or
 * data to be ensured and, more generally, to use and operate it in the
 * same conditions as regards security.
 *
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL-B license and that you accept its terms.
 */

package fr.gouv.culture.thesaurus.autoload;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.StringWriter;
import java.nio.channels.FileLock;
import java.util.Hashtable;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.mail.EmailException;
import org.apache.log4j.HTMLLayout;
import org.apache.log4j.Logger;
import org.apache.log4j.WriterAppender;

import fr.gouv.culture.thesaurus.exception.InvalidParameterException;
import fr.gouv.culture.thesaurus.resources.ThesaurusApplication;
import fr.gouv.culture.thesaurus.util.MailUtil;

/**
 * Classe qui scrute priodiquement un rpertoire pour charger les vocabulaires
 * qui s'y trouvent. Si un fichier "lock.txt" se trouve dans le rpertoire,
 * aucun traitement n'est fait.
 * 
 * @author dhazard
 * 
 */
public class VocabularyAutoload implements Runnable {

    private static final Logger log = Logger.getLogger(VocabularyAutoload.class);

    private static final String LOCK_FILE = "lock.txt";

    private File lookupDir;
    private File successDir;
    private File failureDir;

    private int sleepTime = 10000;
    private int maxTriesByFile = 3;

    private String emailTo = null;

    private File lockFile;

    /**
     * Map containing the files currently waiting for being loaded
     * <p>
     * <b>key</b> : the file to load
     * <p>
     * <b>value</b> : the number of time loading failed (for retries)
     */
    private Hashtable<File, Integer> pendingVocabularyFiles = new Hashtable<File, Integer>();

    private FileFilter lookupFilter = new FileFilter() {

        private Pattern fileNamePattern = Pattern.compile(".+(\\.(?i)(rdf))$");

        @Override
        public boolean accept(File pathname) {
            return pathname.exists() && pathname.isFile() && pathname.canRead() && pathname.canWrite()
                    && fileNamePattern.matcher(pathname.getName()).matches();
        }
    };

    /**
     * Constructeur.
     * 
     * @param lookupDir
     *            Rpertoire  scruter
     * @param successDir
     *            rpertoire de transfert des fichiers imports avec succs
     * @param failureDir
     *            rpertoire de transfert des fichiers en checs.
     * @throws InvalidParameterException
     */
    public VocabularyAutoload(File lookupDir, File successDir, File failureDir) throws InvalidParameterException {
        super();

        // Rpertoire  scruter.
        if (!lookupDir.exists() || !lookupDir.isDirectory()) {
            String message = "lookupDir n'est pas un rpertoire";
            log.error(message);
            throw new InvalidParameterException(message);
        } else if (!lookupDir.canRead() || !lookupDir.canWrite()) {
            String message = "lookupDir n'est pas accessible en lecture et/ou en criture";
            log.error(message);
            throw new InvalidParameterException(message);
        }
        this.lookupDir = lookupDir;

        // Rpertoire de succs
        checkMoveDir(successDir, "successDir");
        this.successDir = successDir;

        // Rpertoire d'chec
        checkMoveDir(failureDir, "failureDir");
        this.failureDir = failureDir;

        // Fichier de lock.
        lockFile = new File(this.lookupDir, LOCK_FILE);
        if (log.isInfoEnabled()) {
            log.info("Scrutation active pour l'ajout automatique de vocabulaires :");
            log.info("\tRpertoire scrut : " + this.lookupDir.getAbsolutePath());
            log.info("\tRpertoire de dplacement des vocabulaires traits avec succs : "
                    + this.successDir.getAbsolutePath());
            log.info("\tRpertoire de dplacement des vocabulaires en chec : "
                    + this.failureDir.getAbsolutePath());
            log.info(
                    "\tNom du fichier de lock utilis (si ce fichier existe, l'ajout automatique est dsactiv le temps de son existence) : "
                            + this.lockFile.getAbsolutePath());
            log.info("\tIntervalle de scrutation (dfaut): " + this.sleepTime + "ms");
            log.info("\tNombre de scrutations avant chec (dfaut): " + this.maxTriesByFile);
        }
    }

    /**
     * Vrifie qu'un rpertoire de destination (succs ou chec) existe (le cr
     * sinon), est un rpertoire et est accessible en criture.
     * 
     * @param moveDir
     *            le repertoire  vrifier.
     * @param propertyName
     *            Le nom de la proprit (pour les logs)
     * @throws InvalidParameterException
     *             Si le repertoire n'est pas un rpertoire ou n'est pas
     *             accessible en criture.
     */
    private void checkMoveDir(File moveDir, String propertyName) throws InvalidParameterException {
        if (!moveDir.exists()) {
            log.warn(propertyName + " n'existe pas et va tre cr");
            moveDir.mkdir();
        } else if (!moveDir.isDirectory()) {
            String message = propertyName + " n'est pas un rpertoire";
            log.error(message);
            throw new InvalidParameterException(message);
        } else if (!moveDir.canWrite()) {
            String message = propertyName + " n'est pas accessible en criture";
            log.error(message);
            throw new InvalidParameterException(message);
        }
    }

    /**
     * Temps d'attente en milliseconde avant le prochain traitement.
     * 
     * @param sleepTime
     *            the sleepTime to set
     */
    public void setSleepTime(int sleepTime) {
        this.sleepTime = sleepTime;
        log.info("Intervalle de scrutation pass  : " + this.sleepTime + "ms");
    }

    /**
     * Temps d'attente en milliseconde avant le prochain traitement.
     * 
     * @param maxTries
     *            nombre d'essai maximum pour un fichier avant d'chouer
     */
    public void setMaxTriesByFile(int maxTries) {
        this.maxTriesByFile = maxTries;
        log.info("Nombre de scrutations avant chec pass  : " + this.maxTriesByFile);
    }

    /**
     * Set email "to" for failure logs.
     * 
     * @param emailTo
     *            the emailTo to set
     */
    public void setEmailTo(String emailTo) {
        this.emailTo = emailTo;
    }

    @Override
    public void run() {
        boolean run = true;
        if (log.isInfoEnabled()) {
            log.info("Dmarrage du thread de scrutation pour l'ajout automatique de vocabulaires");
        }
        while (run) {

            process();

            try {
                Thread.sleep(this.sleepTime);
            } catch (InterruptedException e) {
                run = false;
            }

        }
    }

    private void process() {

        if (ThesaurusApplication.getThesaurusService() == null) {
            // On attend que le service soit instanci.
            return;
        }

        if (log.isDebugEnabled()) {
            log.debug("Scrutation du rpertoire : " + lookupDir.getPath());
        }

        // On regarde si "lock.txt" est positionn : si oui, on ne fait rien.
        if (lockFile.exists()) {
            if (log.isDebugEnabled()) {
                log.debug("Lock pos : pas de traitement.");
            }
            return;
        }

        String lotID = "" + System.currentTimeMillis();
        boolean hasErrors = false;
        Integer numberOfTries = null;
        StringWriter logWriter = new StringWriter();

        WriterAppender appender = new WriterAppender(new HTMLLayout(), logWriter);
        appender.setImmediateFlush(true);
        log.addAppender(appender);

        File[] vocabularies = this.lookupDir.listFiles(this.lookupFilter);
        if (vocabularies != null) {
            for (File vocabulary : vocabularies) {
                if (log.isDebugEnabled()) {
                    log.debug("Prparation  l'injection du fichier " + vocabulary.getPath());
                }
                // On regarde si "lock.txt" est positionn : si oui, on ne fait
                // rien.
                if (lockFile.exists()) {
                    if (log.isDebugEnabled()) {
                        log.debug("Lock pos : pas de traitement.");
                    }
                    log.removeAppender(appender);
                    appender.close();
                    return;
                }
                numberOfTries = pendingVocabularyFiles.get(vocabulary);
                numberOfTries = numberOfTries == null ? 1 : numberOfTries + 1;
                pendingVocabularyFiles.put(vocabulary, numberOfTries);
                if (log.isInfoEnabled()) {
                    log.info("Dbut du traitement de : " + vocabulary.getPath() + " - essai#" + numberOfTries);
                }

                // On traite le fichier
                boolean success = false;
                try {
                    // Import du vocabulaire.
                    ThesaurusApplication.getThesaurusService().load(vocabulary);
                    success = true;
                } catch (Throwable e) {
                    hasErrors = true;
                    StringBuilder message = new StringBuilder(
                            "Echec de chargement du vocabulaire : " + vocabulary.getPath());
                    if (numberOfTries < this.maxTriesByFile) {
                        message.append(" - Un nouvel essai sera fait lors de la prochaine scrutation.");
                    }
                    log.error(message.toString(), e);
                } finally {
                    // Si l'import est un succs ou que le nombre d'chec max
                    // est atteint
                    if (!hasErrors || numberOfTries >= this.maxTriesByFile) {
                        // On dplace le fichier dans le bon rpertoire.
                        File destFile = new File(success ? this.successDir : this.failureDir,
                                vocabulary.getName() + "." + lotID);
                        // On vrifie si le fichier existe toujours avant de le
                        // dplacer
                        // Sinon on peut tomber dans une boucle infinie si le
                        // fichier a t supprim manuellement pendant l'import
                        while (vocabulary.exists() && !vocabulary.renameTo(destFile)) {
                            try {
                                Thread.sleep(1000);
                            } catch (InterruptedException e) {
                                // On arrte le traitement, sinon cela peut
                                // bloquer l'arrt du serveur
                                log.warn(
                                        "chargement du vocabulaire interrompu, le fichier peut ne pas avoir t trait intgralement. Vocabulaire : "
                                                + vocabulary.getPath());
                                break;
                            }
                            if (log.isDebugEnabled()) {
                                log.debug("Dplacement en attente");
                            }
                        }
                        pendingVocabularyFiles.remove(vocabulary);
                        if (log.isInfoEnabled()) {
                            log.info("Fichier dplac vers : " + destFile.getPath());
                        }
                    }
                }
            }
        }

        log.removeAppender(appender);
        appender.close();

        if (hasErrors && numberOfTries >= this.maxTriesByFile) {
            try {
                // On crit le fichier avec le log d'erreur dans le rpertoire
                // d'erreur
                FileUtils.writeStringToFile(new File(failureDir, lotID + ".log.html"), logWriter.toString(),
                        "UTF-8");
            } catch (IOException ex) {
                log.warn("Ecriture du fichier de log impossible.", ex);
            }

            if (StringUtils.isNotEmpty(this.emailTo)) {
                MailUtil mail = MailUtil.getHtmlMail(this.emailTo,
                        "Echec du chargement automatique du vocabulaire (" + lotID + ")", logWriter.toString());

                try {
                    mail.send();
                } catch (EmailException e) {
                    log.error(e);
                }
            }
        }
    }
}