Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Java

File Cleaner

4.76/5 (6 votes)
15 Nov 2011CPL6 min read 22.6K   921  
File Cleaner - Zip & delete old files

Introduction

'File Cleaner' tool is developed to ZIP and/or delete files older than X number of DAYS/MONTHS/YEARS. It is developed for making some extra free space on system or servers where too many LOG/TEXT files are getting generated.

This tool has been successfully tested on Windows XP/Vista/7, Windows Server 2000/2003, Windows Developer Preview (Windows 8), Ubuntu 11, Fedora 14, UNIX - Sun Solaris, Mac OS X Lion.

Background

One of my friends was discussing with me about the production Batch Component servers and the disk space consumed by the batch jobs over the period of time.

For example, 'Server A' is a batch component server and having around 100+ batch jobs configured. Most of the jobs are configured to run daily and consume around 3 GB of disk space per-day which includes INPUT files, OUTPUT files, all kind of LOG files, etc. With this fact, if server has 500 GB of SAN allocated, the disk will be full within 5 months or so.

Problem

Now adding 500 GB of SAN every 5 months is not advisable in a real production environment. Also most of the batch components do generate LOG file(s) but do not cleanup which is good and helps in real production environment to investigate issue(s).

Solution

As an outcome of the above scenario, there is a business need for some file cleanup tool. Most of the time, respective 'Server Support' and/or 'Application Support' teams write operating system specific micro scripts to achieve this which even my friend has done to his production environment.

Now suppose we have 20 servers out of which 10 are Windows, 4 are Linux, 5 UNIX and 1 OS X. With this, there will be at least 3 different versions of operating system specific micro scripts. Being a technical guy, I will not prefer to have 3 different versions of micro scripts, so I developed this small tool using JAVA.

Since most of the operating systems come with basic version of JAVA run-time installed, I choose to develop this tool using Core JAVA.

Using the Tool

The tool is very simple to configure and use. We need to execute JAR file using JAVA run-time and pass the '.properties' file as argument. The '.properties' file contains the configuration or parameters used by the tool. A sample '.properties' file is as below.

sample.properties

# +--------------------------------+
# | FileCleanerLauncher properties |
# +--------------------------------+
# if zip_files is not provided, true will be used
zip_files = true
# if delete_files is not provided, true will be used
delete_files = true
# +-----------------------+
# | FileFilter properties |
# +-----------------------+
directory = D:\\
file_expression = *.txt
# Expected values [days, months, years]
keep_last_type = days
keep_last = 1
# +-----------------------+
# | FileZipper properties |
# +-----------------------+
# if zip_directory is not provided, directory of FileFilter will be used
#zip_directory =
zip_file_format = a-%tF.zip
# if buffer_size is not provided, default value 1024 will be used
#buffer_size = 2048 

JAR File Execution

Java
java -jar FileCleaner.jar sample.properties 

The Code

This tool is a very basic JAVA console application. The code is the collection of 4 core classes and 2 helper classes each described below.

PropertiesConfiguration Class

This class facilitates reading settings from '.properties' file. The '.properties' file can be default one as 'filecleaner.properties' or custom passed as a command line argument as showed under 'JAR file execution:'

Java
private static String DEFAULT_PROPERTIES_FILENAME = "filecleaner.properties";
private static PropertiesConfiguration instance = null;

public static void init(String propertiesFile) {
    if (null == instance) {
        try {
            instance = new PropertiesConfiguration(propertiesFile);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public static void init() {
    init(DEFAULT_PROPERTIES_FILENAME);
}  

Helper Class

I was missing a good 'String' function of .NET called 'IsNullOrEmpty' so I just created it as a helper function in this class.

Java
public final class Helper {

   public static Boolean hasString(String string) {
       return !((null == string) || string.isEmpty());
   }
} 

FileFilter Class

This class is the first step for this tool. It is responsible for filtering and generating the list of files to be ZIPped and/or deleted. It has four settings to read from '.properties' file. It has two methods which filter the files based on parameters and returns the list as String array.

  • directory: Valid directory or folder path to look for files. Path syntax differs based on 'Operating System'.
  • file_expression: Valid JAVA string expression to filter files. This can also include wildcard characters like '?' and '*'.
  • keep_last_type: Value can be DAYS/MONTHS/YEARS indicating the type of deduction to be done from System Date. If the value is DAYS, number of days will be deducted. If value is MONTHS, number of months will be deducted. If value is YEARS, number of years will be deducted.
  • keep_last: Integer value indicating files to be filtered older then 'X' number of days/months/years. While filtering the files, this value is deducted from the current System Date based on value specified under 'keep_last_type'.
    Java
    public FileFilter() throws Exception {
    
        String value = null;
    
        value = PropertiesConfiguration.getPropertyValue("directory");
        if (!Helper.hasString(value)) {
            throw new Exception("Invalid property value: directory");
        }
        this.directory = value;
    
        value = PropertiesConfiguration.getPropertyValue("file_expression");
        if (!Helper.hasString(value)) {
            throw new Exception("Invalid property value: file_expression");
        }
        this.fileExpression = value;
    
        value = PropertiesConfiguration.getPropertyValue("keep_last_type");
        if (!Helper.hasString(value)) {
            throw new Exception("Invalid property value: keep_last_type");
        }
    
        value = value.toUpperCase();
        if ("DAYS".equals(value)) {
            this.keepLastType = Calendar.DATE;
        } else if ("MONTHS".equals(value)) {
            this.keepLastType = Calendar.MONTH;
        } else if ("YEARS".equals(value)) {
            this.keepLastType = Calendar.YEAR;
        } else {
            throw new Exception("Invalid property value: keep_last_type");
        }
    
        value = PropertiesConfiguration.getPropertyValue("keep_last");
        this.keepLast = Integer.parseInt(value);
    } 
  • getKeepDate - method: Returns the Date which should be compared with System Date.
    Java
    protected Date getKeepDate() {
    
        Calendar calendar = Calendar.getInstance();
        calendar.add(this.keepLastType, (this.keepLast * -1));
    
        return calendar.getTime();
    } 
  • getFileNames - method: Returns the list of filtered files as String array. The returned array can be passed to other two steps ZIP and Delete.
    Java
    public String[] getFileNames() throws FileNotFoundException {
    
        String matchExpression = fileExpression.replace("?", ".?").replace("*", ".*");
        Date keepDate = this.getKeepDate();
    
        File dir = new File(this.directory);
        if (!dir.isDirectory() || !dir.exists()) {
            throw new FileNotFoundException(this.directory);
        }
    
        ArrayList<string> filteredFileNames = new ArrayList<string>();
        String[] fileNames = dir.list();
    
        for (String fileName : fileNames) {
            if (!fileName.matches(matchExpression)) {
                continue;
            }
    
            File file = new File(dir, fileName);
            if (!file.isFile() || !file.exists()) {
                continue;
            }
    
            Date lastModified = new Date(file.lastModified());
            if (lastModified.after(keepDate)) {
                continue;
            }
    
            filteredFileNames.add(file.toString());
        }
    
        return filteredFileNames.toArray(new String[filteredFileNames.size()]);
    } 

FileZipper Class

This class is the second step for this tool. It is responsible for compressing the list of provided files to a ZIP/tar.gz file. It has three settings to read from '.properties' file. It has two methods to compress and create ZIP/tar.gz file.

  • zip_directory: Valid directory or folder path to create compressed file. If the value is not defined or provided, it will use 'directory' settings of 'FileFilter' class. Path syntax differs based on 'Operating System'.
  • zip_file_format: Valid compressed file name format. For more details on valid formats, please refer to 'java.util.Formatter class'.
  • buffer_size: Integer value indicating the size of buffer in byes to be used for read/write files. If not provided, default value 1024 bytes will be used. The performance can be boosted by setting big buffer size for large files.
    Java
    public FileZipper() throws Exception {
    
        String value = null;
    
        value = PropertiesConfiguration.getPropertyValue("zip_directory");
        if (!Helper.hasString(value)) {
            value = PropertiesConfiguration.getPropertyValue("directory");
            if (!Helper.hasString(value)) {
                throw new Exception("Invalid property value: zip_directory");
            }
        }
        this.directory = value;
    
        value = PropertiesConfiguration.getPropertyValue("zip_file_format");
        if (!Helper.hasString(value)) {
            throw new Exception("Invalid property value: zip_file_format");
        }
        this.zipFileFormat = value;
    
        value = PropertiesConfiguration.getPropertyValue("buffer_size");
        if (!Helper.hasString(value)) {
            value = "1024";
        }
        this.bufferSize = Integer.parseInt(value);
    } 
  • getZipFileName - method: Returns ZIP/tar.gz file path as string.
    Java
    protected String getZipFileName() {
    
        Formatter formatter = new Formatter();
        formatter.format(zipFileFormat, Calendar.getInstance());
    
        return this.directory.concat(formatter.toString());
    } 
  • zipFiles - method: Compresses the provided files as String array and returns newly created compress file path as string. It uses 'BEST_COMPRESSION' as compress level which can compress normal text file to around 95%.
    Java
    public String zipFiles(String[] fileNames) throws IOException {
    
        if (null == fileNames || 0 == fileNames.length) {
            return null;
        }
    
        String zipFileName = this.getZipFileName();
    
        FileOutputStream fos = new FileOutputStream(zipFileName);
        ZipOutputStream zos = new ZipOutputStream(fos);
        zos.setLevel(Deflater.BEST_COMPRESSION);
    
        int bytesRead;
        byte[] buffer = new byte[this.bufferSize];
        CRC32 crc = new CRC32();
    
        for (String fileName : fileNames) {
            File file = new File(fileName);
            if (!file.isFile() || !file.exists()) {
                continue;
            }
    
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
            crc.reset();
    
            while ((bytesRead = bis.read(buffer)) != -1) {
                crc.update(buffer, 0, bytesRead);
            }
            bis.close();
    
            bis = new BufferedInputStream(new FileInputStream(file));
    
            ZipEntry entry = new ZipEntry(file.getName());
            entry.setCrc(crc.getValue());
            zos.putNextEntry(entry);
    
            while ((bytesRead = bis.read(buffer)) != -1) {
                zos.write(buffer, 0, bytesRead);
            }
    
            bis.close();
        }
    
        zos.close();
    
        return zipFileName;
    } 

FileDeleter Class

This class is the third step for this tool. This is a very simple class and it just deletes the files provided as String array.

Java
public void deleteFileNames(String[] fileNames) {

    for (String fileName : fileNames) {
        File file = new File(fileName);
        if (!file.isFile() || !file.exists()) {
            continue;
        }

        file.delete();
    }
} 

FileCleanerLauncher Class

This class is the main class. It also has two settings to read from '.properties' file and has four methods.

Java
public static void main(String[] args) {

    if (args.length > 0) {
        PropertiesConfiguration.init(args[0]);
    } else {
        PropertiesConfiguration.init();
    }

    FileCleanerLauncher launcher = new FileCleanerLauncher();
    launcher.run();
    launcher = null;
} 
  • zip_files: 'true' if files need to be compressed, 'false' otherwise. If not provided, default value is 'false'. If set as 'true', files will be compressed using 'FileZipper' class. If set as 'false', files will NOT be compressed.
  • delete_files: 'true' if files need to be deleted, 'false' otherwise. If not provided, default value is 'false'. If set as 'true', files will be deleted using 'FileDelete' class. If set as 'false', files will NOT be deleted.
    Note: If both are set to 'false', nothing will be done. In other case, if 'zip_files' is set to 'false' and 'delete_files' set to 'true', files will be deleted without backup and cannot be recovered. And if 'zip_files' is set to 'true' and 'delete_files' set to 'false', files will be compressed but will NOT be deleted which does not free space on disk.
    Java
    public FileCleanerLauncher() {
    
        String value = null;
    
        value = PropertiesConfiguration.getPropertyValue("zip_files");
        if (!Helper.hasString(value)) {
            value = "true";
        }
        this.zipFiles = Boolean.parseBoolean(value);
    
        value = PropertiesConfiguration.getPropertyValue("delete_files");
        if (!Helper.hasString(value)) {
            value = "true";
        }
        this.deleteFiles = Boolean.parseBoolean(value);
    } 
  • run - method: It just calls three different methods defined as each step.
    Java
    public void run() {
    
        try {
            String[] fileNames = this.getFileNames();
            this.zipFiles(fileNames);
            this.deleteFiles(fileNames);
        } catch (Exception e) {
            e.printStackTrace();
        }
    } 
  • getFileNames - method: With help of 'FileFilter' class, it gets the filtered file list as String array.
    Java
    protected String[] getFileNames() throws Exception {
    
        String[] fileNames = null;
    
        FileFilter filter = new FileFilter();
        fileNames = filter.getFileNames();
        filter = null;
    
        return fileNames;
    } 
  • zipFiles - method: With help of 'FileZipper' class, it compresses provided file list and returns file path of newly created compressed file. If 'zip_files' is set to 'false', this step will be skipped.
    Java
    protected String zipFiles(String[] fileNames) throws Exception {
    
        if (!this.zipFiles) {
            return null;
        }
    
        String zipFileName = null;
    
        FileZipper zipper = new FileZipper();
        zipFileName = zipper.zipFiles(fileNames);
        zipper = null;
    
        return zipFileName;
    } 
  • deleteFiles - method: With the help of 'FileDelete' class, it deletes provided file list. If 'delete_files' is set to 'false', this step will be skipped.
    Java
    protected void deleteFiles(String[] fileNames) {
    
        if (!this.deleteFiles) {
            return;
        }
    
        FileDeleter deleter = new FileDeleter();
        deleter.deleteFileNames(fileNames);
        deleter = null;
    } 

Conclusion

I am not much of an expert in the JAVA world, but it was a very good experience understanding ZIP file concept in JAVA. Also testing on multiple platforms was a challenge and it was achieved with the help of 'Virtual Box'.

History

  • 15th November, 2011: Initial post

License

This article, along with any associated source code and files, is licensed under The Common Public License Version 1.0 (CPL)