001/* 002 * Copyright 2020 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.cms.schedule; 017 018import java.io.IOException; 019import java.nio.file.DirectoryStream; 020import java.nio.file.Files; 021import java.nio.file.Path; 022import java.time.Instant; 023 024import org.apache.avalon.framework.service.ServiceException; 025import org.apache.avalon.framework.service.ServiceManager; 026import org.quartz.JobExecutionContext; 027 028import org.ametys.core.file.FileHelper; 029import org.ametys.plugins.core.impl.schedule.AbstractStaticSchedulable; 030 031/** 032 * Abstract schedulable to delete files or folders. 033 */ 034public abstract class AbstractDeleteFilesSchedulable extends AbstractStaticSchedulable 035{ 036 /** File Helper */ 037 protected FileHelper _fileHelper; 038 039 @Override 040 public void service(ServiceManager manager) throws ServiceException 041 { 042 super.service(manager); 043 _fileHelper = (FileHelper) manager.lookup(FileHelper.ROLE); 044 } 045 046 /** 047 * Get the needed values so the script can run 048 * @param context the context 049 * @return the configuration with needed values for deletion 050 */ 051 protected abstract DeleteFilesConfiguration _getConfiguration(JobExecutionContext context); 052 053 @Override 054 public void execute(JobExecutionContext context) throws Exception 055 { 056 DeleteFilesConfiguration configuration = _getConfiguration(context); 057 058 Path root = configuration.getRootPath(); 059 if (root == null) 060 { 061 throw new IOException("The path is empty."); 062 } 063 064 if (Files.exists(root)) 065 { 066 DirectoryStream.Filter<Path> fileFilter = _createFileFilter(configuration); 067 068 if (configuration.deleteRoot() || Files.isRegularFile(root)) 069 { 070 _delete(root, configuration, fileFilter); 071 } 072 else 073 { 074 try (DirectoryStream<Path> entries = Files.newDirectoryStream(root)) 075 { 076 for (Path entry : entries) 077 { 078 _delete(entry, configuration, fileFilter); 079 } 080 } 081 } 082 } 083 } 084 085 /** 086 * Delete all files corresponding to the file filter into the file tree. 087 * @param path the path to delete (can be a file or a directory) 088 * @param configuration the configuration for deletion 089 * @param fileFilter the file filter to apply 090 * @throws IOException if an error occured while exploring or deleting files 091 */ 092 protected void _delete(Path path, DeleteFilesConfiguration configuration, DirectoryStream.Filter<Path> fileFilter) throws IOException 093 { 094 _fileHelper.delete(path, fileFilter, configuration.deleteRecursively(), configuration.deleteEmptyFolders()); 095 } 096 097 /** 098 * Create the file filter to delete files. 099 * @param configuration The delete files configuration 100 * @return a file filter 101 */ 102 protected DirectoryStream.Filter<Path> _createFileFilter(DeleteFilesConfiguration configuration) 103 { 104 return new DirectoryStream.Filter<>() 105 { 106 @Override 107 public boolean accept(Path entry) throws IOException 108 { 109 return Files.getLastModifiedTime(entry) 110 .toInstant() 111 .isBefore(configuration.getAgeLimit()); 112 } 113 }; 114 } 115 116 /** 117 * Inner class to store configuration for deletion. 118 */ 119 protected static class DeleteFilesConfiguration 120 { 121 private Path _rootPath; 122 private boolean _deleteRoot; 123 private boolean _deleteRecursively; 124 private boolean _deleteEmptyFolders; 125 private Instant _ageLimit; 126 127 /** 128 * Constructor. 129 * Default values: 130 * - ageLimit = now 131 * - deleteRoot = false 132 * - deleteRecursively = true 133 * - deleteEmptyFolders = true 134 * @param rootPath The root path for deletion 135 */ 136 public DeleteFilesConfiguration(Path rootPath) 137 { 138 _rootPath = rootPath; 139 _ageLimit = Instant.now(); 140 _deleteRoot = false; 141 _deleteRecursively = true; 142 _deleteEmptyFolders = true; 143 } 144 145 /** 146 * Get the root folder or file to delete 147 * @return a Directory or a file, that will be deleted/emptied 148 */ 149 public Path getRootPath() 150 { 151 return _rootPath; 152 } 153 154 /** 155 * Set to <code>true</code> to delete the root folder. 156 * @param deleteRoot <code>true</code> to delete the root folde 157 */ 158 public void setDeleteRoot(boolean deleteRoot) 159 { 160 _deleteRoot = deleteRoot; 161 } 162 163 /** 164 * Do the root needs to be deleted or emptied ? 165 * @return <code>true</code> to delete the root folder 166 */ 167 public boolean deleteRoot() 168 { 169 return _deleteRoot; 170 } 171 172 /** 173 * Set to <code>true</code> to delete files recursively. 174 * @param deleteRecursively <code>true</code> to delete files recursively 175 */ 176 public void setDeleteRecursively(boolean deleteRecursively) 177 { 178 _deleteRecursively = deleteRecursively; 179 } 180 181 /** 182 * Do the folders needs to be deleted recursively ? 183 * @return <code>true</code> to delete files recursively 184 */ 185 public boolean deleteRecursively() 186 { 187 return _deleteRecursively; 188 } 189 190 /** 191 * Set to <code>true</code> to delete empty folders. 192 * @param deleteEmptyFolders <code>true</code> to delete empty folders 193 */ 194 public void setDeleteEmptyFolders(boolean deleteEmptyFolders) 195 { 196 _deleteEmptyFolders = deleteEmptyFolders; 197 } 198 199 /** 200 * When a folder is empty, do it needs to be deleted ? 201 * @return <code>true</code> to delete empty folders 202 */ 203 public boolean deleteEmptyFolders() 204 { 205 return _deleteEmptyFolders; 206 } 207 208 /** 209 * Set the age limit of files to delete. 210 * @param ageLimit The age limit 211 */ 212 public void setAgeLimit(Instant ageLimit) 213 { 214 _ageLimit = ageLimit; 215 } 216 217 /** 218 * Only delete files older than this instant. 219 * @return instant, only older files will be deleted 220 */ 221 public Instant getAgeLimit() 222 { 223 return _ageLimit; 224 } 225 } 226}