All Downloads are FREE. Search and download functionalities are using the official Maven repository.

at.spardat.xma.boot.cleanup.CleanerDeamon Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2003, 2007 s IT Solutions AT Spardat GmbH .
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     s IT Solutions AT Spardat GmbH - initial API and implementation
 *******************************************************************************/

/*
 * Created on 06.11.2003
 */
package at.spardat.xma.boot.cleanup;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Properties;
import java.util.SimpleTimeZone;
import java.util.TimerTask;

import at.spardat.xma.boot.BootRuntime;
import at.spardat.xma.boot.Statics;
import at.spardat.xma.boot.cache.FileCache;
import at.spardat.xma.boot.logger.ILogger;
import at.spardat.xma.boot.logger.LogLevel;
import at.spardat.xma.boot.logger.Logger;
import at.spardat.xma.boot.util.Util;

/**
 * @author s3595
 *
 */
public class CleanerDeamon extends TimerTask {

    /* fallbacks */
    private final static long LOGFILE_TIMEOUT_DEFAULT_MILLISEC = 2592000L * 1000;

    private final static long APPLICATION_TIMEOUT_DEFAULT_MILLISEC = 15552000L * 1000;

    /* cleaner */
    private Cleaner cleaner;

    /* configuration properties */
    private Properties props;

    /* logger */
    private ILogger log;

    /* file cache reference */
    private FileCache fc;

    /* runtime data directory */
    private File datadir;

    /* logfiles will be removed after this timeout */
    private long logfileTimeoutMilliSec = LOGFILE_TIMEOUT_DEFAULT_MILLISEC;

    /* applications will be removed from cache, after this timeout */
    private long applicationTimeoutMilliSec = APPLICATION_TIMEOUT_DEFAULT_MILLISEC;

    public CleanerDeamon(Cleaner cleanerIn) {
        cleaner = cleanerIn;
        props = cleaner.getProperties();
        log = Logger.getLogger("boot.cleaner.daemon");
        datadir = BootRuntime.getInstance().getDataDirectory();

        fc = (FileCache) FileCache.getInstance();
        if (fc == null) {
            log.warning("file cache not found. cleaner will not run");
        }

        try {
            String strLogfileTimeout = props.getProperty(Statics.CFG_PROP_CLEANUP_LOGFILE_TIMEOUT);
            logfileTimeoutMilliSec = Integer.parseInt(strLogfileTimeout) * 1000;
            if (logfileTimeoutMilliSec <= 0)
                logfileTimeoutMilliSec = LOGFILE_TIMEOUT_DEFAULT_MILLISEC;

        } catch (Exception e) {
            log.log(LogLevel.WARNING, "Could not read Property: "
                    + Statics.CFG_PROP_CLEANUP_LOGFILE_TIMEOUT, e);
        }

        try {
            String strApplicationTimeout = props
                    .getProperty(Statics.CFG_PROP_CLEANUP_APPLICATION_TIMEOUT);
            applicationTimeoutMilliSec = Integer.parseInt(strApplicationTimeout) * 1000;
            if (applicationTimeoutMilliSec <= 0)
                applicationTimeoutMilliSec = APPLICATION_TIMEOUT_DEFAULT_MILLISEC;

        } catch (Exception e) {
            log.log(LogLevel.WARNING, "Could not read Property: "
                    + Statics.CFG_PROP_CLEANUP_APPLICATION_TIMEOUT, e);
        }

    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Runnable#run()
     */
    public void run() {
        if (datadir.exists() == false)
            return;
        log.info("starting daemon cleanup");

        cleanupLogfiles();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            log.warning(e.getMessage());
        }

        cleanupApplications();

        cleanupShared();
    }

    public boolean cancel() {
        log.info("daemon cancel");
        return super.cancel();
    }

    /**
     * Deletes files in log dir if lastModified is older than logfileTimeout
     *
     * @since version_number
     * @author s3460
     */
    private void cleanupLogfiles() {
        try {
            log.log(LogLevel.ALL, "cleanup check for logfiles ");

            File logdir = new File(datadir, Statics.LOG_DIRNAME);
            if (!logdir.exists()) {
                log.warning("log directory does not exist: "+logdir.getAbsolutePath());
                return;
            }

            File[] logfiles = logdir.listFiles();
            for (int i = 0; i < logfiles.length; i++) {
                File logfile = logfiles[i];
                long lastModified = logfile.lastModified();

                if (lastModified + logfileTimeoutMilliSec < System.currentTimeMillis()) {
                    log.log(LogLevel.INFO, "removing logfile: {0}", logfile.toString());
                    delete(logfile);
                }
            }
        } catch (Exception ex) {
            log.log(LogLevel.WARNING, "exception during logfile cleanup: ", ex);
        }

    }

    /**
     * if cachedir exists, start recurse over dirs
     *
     * @since version_number
     * @author s3460
     */
    private void cleanupApplications() {
        try {
            File cachedir = new File(datadir, Statics.CACHE_DIRNAME);
            if (!cachedir.exists()) {
                log.warning("cache root directory not found: "+cachedir.getAbsolutePath());
                return;
            }

            recurse(cachedir);

        } catch (Exception ex) {
            log.log(LogLevel.WARNING, "exception during application cleanup: ", ex);
        }
    }

    /**
     * Iterates over the Files in SHARED_DIRNAME. Timed out Files are deleted.
     *
     * @since version_number
     * @author s3460
     */
    private void cleanupShared() {
        try {
            log.log(LogLevel.ALL, "cleanup check for shared resources");
            File sharedDir = new File(datadir.getAbsolutePath() + File.separator
                    + Statics.CACHE_DIRNAME, Statics.SHARED_DIRNAME);
            if (!sharedDir.exists()) {
                log.info("shared root directory not found: "+sharedDir.getAbsolutePath());
                return;
            }

            File[] files = sharedDir.listFiles();
            for (int i = 0; files != null && i < files.length; i++) {
                deleteOldSharedRes(files[i]);
            }

            if (sharedDir.listFiles().length == 0) {
                delete(sharedDir);
            }

        } catch (Exception ex) {
            log.log(LogLevel.WARNING, "exception during shared resource cleanup: ", ex);
        }
    }

    /**
     * Reads the .ifo Files and deletes timed out Files.
     *
     * @param ifoFile
     * @since version_number
     * @author s3460
     */
    private void deleteOldSharedRes(File ifoFile) {
        InputStream is = null;
        OutputStream os = null;
        try {
            if (ifoFile.getName().endsWith(Statics.FILEINFO_EXT)) {
                //              jar file to .ifo file
                File file = new File(ifoFile.getAbsolutePath().substring(0,
                        ifoFile.getAbsolutePath().lastIndexOf(".")));
                //              delete .ifo files without jars
                if (!file.exists()) {
                    log.log(LogLevel.INFO,
                            "cached shared .ifo file without resource. removing: {0}", ifoFile
                                    .getAbsolutePath());
                    delete(ifoFile);
                    return;
                }
                long lastStarted = 0;
                try {
                    Properties adProps = new Properties();
                    //read Properties
                    is = new FileInputStream(ifoFile);
                    adProps.load(is);
                    String strAppStarted = adProps.getProperty(Statics.APPLICATION_STARTED);
                    //store startup time if not yet set
                    if (strAppStarted != null) {
                        lastStarted = Long.parseLong(strAppStarted);
                    } else {
                        lastStarted = System.currentTimeMillis();
                        adProps.setProperty(Statics.APPLICATION_STARTED, Long.toString(lastStarted));
                        os = new FileOutputStream(ifoFile);
                        adProps.store(os, "");
                    }
                } catch (Exception exc) {
                    log.log(LogLevel.WARNING,"error handling timestamp in '"+ifoFile.getAbsolutePath()+"'",exc);
                    // if .ifo file corrupted lastStarted stays zero -> it will be deleted
                }
                if (lastStarted + applicationTimeoutMilliSec < System.currentTimeMillis()) {
                    Util.close(is, ifoFile.getAbsolutePath());
                    //shared res of running application can't be deleted
                    log.log(LogLevel.INFO, "cached shared resource expired. removing: {0}",
                            file.getAbsolutePath());
                    if (delete(file)) {
                        delete(ifoFile);
                    }
                }

            } else {
                //delete jars without .ifo files
                File file = ifoFile;
                ifoFile = new File(file.getAbsolutePath() + Statics.FILEINFO_EXT);
                if (!ifoFile.exists()) {
                    log.log(LogLevel.INFO,
                            "cached shared resource without .ifo file. removing: {0}", file
                                    .getAbsolutePath());
                    delete(file);
                }
            }
        } catch (Exception e) {
            log.log(LogLevel.WARNING, "exception during cleanup of shared resource '"+ifoFile.getAbsolutePath()+"': ", e);
        } finally {
            Util.close(is, ifoFile.getAbsolutePath());
            Util.close(os, ifoFile.getAbsolutePath());
        }
    }

    /**
     * Deletes aFile and writes log message if delete fails
     *
     * @param aFile
     * @return @since version_number
     * @author s3460
     */
    private boolean delete(File aFile) {
        if (!aFile.delete()) {
            log.log(LogLevel.WARNING,
                    "this file cannot be deleted: {0}", aFile
                            .getAbsolutePath());
            return false;
        } else {
            return true;
        }
    }

    /**
     * recurse as long over the cache dirs as the file "xma-app.xml" is found.
     * Empty dirs are deleted on recursion return.
     *
     * @param filedir
     * @since version_number
     * @author s3460
     */
    private void recurse(File filedir) {

        File fapp = new File(filedir, Statics.APP_DISCRIPTOR);
        if (fapp.exists()) {
            log.log(LogLevel.ALL, "check for cleanup. application : " + fapp.toString());
            cleanupApplication(filedir);
        } else {
            File[] files = filedir.listFiles();
            for (int i = 0; i < files.length; i++) {
                File next = files[i];
                // log_.log(LogLevel.ALL, "next: " + next.toString());

                if (next.isDirectory()) {
                    recurse(next);
                }
            }//for
        }//elseif

        if (filedir.isDirectory() && filedir.listFiles().length == 0) {
            delete(filedir);
        }
    }

    /**
     * reads xma-app.xml.ifo APPLICATION_STARTED if null: writes current time
     * else: APPLICATION_STARTED older than timeout -> delete files and dirs
     *
     * @param filedir
     * @since version_number
     * @author s3460
     */
    private void cleanupApplication(File filedir) {
        try {
            File fapp = new File(filedir, Statics.APP_DISCRIPTOR);
            File fappInfo = new File(filedir, Statics.APP_DISCRIPTOR + Statics.FILEINFO_EXT);
            if (!fapp.exists() || !fappInfo.exists()) {
                log.log(LogLevel.WARNING, "application descriptor (or .ifo) not found: "
                        + fapp.toString());
                return;
            }
            Properties adProps = new Properties();
            InputStream is = null;
            try {
                is = new FileInputStream(fappInfo);
                adProps.load(is);
            } finally {
                Util.close(is,fappInfo.getAbsolutePath());
            }
            String strAppStarted = adProps.getProperty(Statics.APPLICATION_STARTED);
            if (strAppStarted == null) {
                adProps.setProperty(Statics.APPLICATION_STARTED, Long.toString(System
                        .currentTimeMillis()));
                OutputStream os = null;
                try {
                    os = new FileOutputStream(fappInfo);
                    adProps.store(os, "");
                } finally {
                    Util.close(os,fappInfo.getAbsolutePath());
                }
            } else {
                long lastStarted = Long.parseLong(strAppStarted);
                if (lastStarted + applicationTimeoutMilliSec < System.currentTimeMillis()) {
                    log.log(LogLevel.INFO, "cached application expired. removing: {0}", fapp
                            .toString());
                    /* remove application imediately */
                    if (delete(fapp)) {
                        delete(fappInfo);
                    }

                    removeApplicationRecursive(filedir);

                } else {
                    cleanupQueryCache(filedir);
                }
            }
        } catch (Exception ex) {
            log.log(LogLevel.WARNING, "exception on cleanup of application '"+filedir.getAbsolutePath()+"':", ex);
        }
    }

    /**
     * deletes the files and dirs of the application Empty dirs are deleted on
     * recursion return.
     *
     * @param filedir
     * @since version_number
     * @author s3460
     */
    private void removeApplicationRecursive(File filedir) {
        File[] files = filedir.listFiles();
        for (int i = 0; i < files.length; i++) {
            File next = files[i];

            if (next.isFile()) {
                log.log(LogLevel.ALL, "removing: {0}", next.toString());
                delete(next);
            }
            if (next.isDirectory()) {
                removeApplicationRecursive(next);
            }
            if (next.isDirectory() && next.listFiles().length == 0) {
                log.log(LogLevel.ALL, "removing: {0}", next.toString());
                delete(next);
            }
        }
    }

    /**
     * reads all .ifo files removes expired files from file cache
     *
     * @param filedir
     * @since version_number
     * @author s3460
     */
    private void cleanupQueryCache(File filedir) {
        /* check all queries for this application against the same timestamp */
        long now = System.currentTimeMillis();

        try {
            File querydir = new File(filedir, Statics.URL_TABULAR + Statics.URL_QUERY);
            if (!querydir.exists()) {
                return;
            }

            File[] queries = querydir.listFiles(new CleanupFileFilter(Statics.FILEINFO_EXT));

            for (int i = 0; i < queries.length; i++) {
                File next = queries[i];

                Properties props = new Properties();
                InputStream is = null;
                try {
                    is = new FileInputStream(next);
                    props.load(is);
                } finally {
                    if (is != null)
                        is.close();
                }

                String strResourceURL = props.getProperty(Statics.URL_NAME);
                URL url = new URL(strResourceURL);

                String strExpires = props.getProperty(Statics.HTTP_EXPIRES);
                long expires = 0L;
                try {
                    expires = Long.parseLong(strExpires);
                } catch (NumberFormatException ex) {
                    log.warning("numberformat error on property EXPIRES. file: " + next.toString());
                    expires = 0L;
                }
                if (expires < now) {
                    String strLastModified = props.getProperty(Statics.HTTP_LAST_MODIFIED);
                    long lastmodified = 0L;
                    try {
                        lastmodified = Long.parseLong(strLastModified);
                    } catch (NumberFormatException ex) {
                        log.warning("numberformat error on property HTTP_LAST_MODIFIED. file: "
                                + next.toString());
                        lastmodified = 0L;
                    }

                    if (lastmodified == 0L) {

                        deleteCachedResource(url, next);

                    } else {
                        String strLastUpdated = props.getProperty(Statics.strLastUpdated);
                        long lastupdated = 0L;
                        try {
                            lastupdated = Long.parseLong(strLastUpdated);
                        } catch (NumberFormatException ex) {
                            log.warning("numberformat error on property HTTP_LAST_MODIFIED. file: "
                                    + next.toString());
                            lastmodified = 0L;
                        }
                        if (lastupdated > expires) {
                            throw new RuntimeException("lastupdated > expired");
                        }

                        long heuristicTimeout = expires + 5L * (expires - lastupdated);

                        if (now > heuristicTimeout) {
                            DateFormat httpdate_ = new SimpleDateFormat(
                                    "EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US); //$NON-NLS-1$
                            httpdate_.setTimeZone(new SimpleTimeZone(0, "GMT")); //$NON-NLS-1$

                            log.log(LogLevel.ALL, "tabular query deleted: {0}", next.toString());
                            log.log(LogLevel.ALL, "tabular expired. expired: {0}", httpdate_
                                    .format(new Date(expires)));
                            log.log(LogLevel.ALL, "tabular last updated: {0}", httpdate_
                                    .format(new Date(lastupdated)));
                            log.log(LogLevel.ALL, "tabular last modified server: {0}", httpdate_
                                    .format(new Date(lastmodified)));

                            deleteCachedResource(url, next);
                        }
                    }
                }//if expired

            }

        } catch (Exception ex) {
            log.log(LogLevel.WARNING, "exception in cleanup of query cache of '"+filedir.getAbsolutePath()+"':", ex);
        }
    }

    private void deleteCachedResource(URL url, File file) {
        fc.invalidateResource(url);
    }

}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy