at.spardat.xma.boot.cleanup.CleanerDeamon Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* 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);
}
}