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.core.schedule.progression.ContainerProgressionTracker; 030import org.ametys.plugins.core.impl.schedule.AbstractStaticSchedulable; 031 032/** 033 * Abstract schedulable to delete files or folders. 034 */ 035public abstract class AbstractDeleteFilesSchedulable extends AbstractStaticSchedulable 036{ 037 /** File Helper */ 038 protected FileHelper _fileHelper; 039 040 @Override 041 public void service(ServiceManager manager) throws ServiceException 042 { 043 super.service(manager); 044 _fileHelper = (FileHelper) manager.lookup(FileHelper.ROLE); 045 } 046 047 /** 048 * Get the needed values so the script can run 049 * @param context the context 050 * @return the configuration with needed values for deletion 051 */ 052 protected abstract DeleteFilesConfiguration _getConfiguration(JobExecutionContext context); 053 054 @Override 055 public void execute(JobExecutionContext context, ContainerProgressionTracker progressionTracker) throws Exception 056 { 057 DeleteFilesConfiguration configuration = _getConfiguration(context); 058 059 Path root = configuration.getRootPath(); 060 if (root == null) 061 { 062 throw new IOException("The path is empty."); 063 } 064 065 if (Files.exists(root)) 066 { 067 DirectoryStream.Filter<Path> fileFilter = _createFileFilter(configuration); 068 069 if (configuration.deleteRoot() || Files.isRegularFile(root)) 070 { 071 _delete(root, configuration, fileFilter); 072 } 073 else 074 { 075 try (DirectoryStream<Path> entries = Files.newDirectoryStream(root)) 076 { 077 for (Path entry : entries) 078 { 079 _delete(entry, configuration, fileFilter); 080 } 081 } 082 } 083 } 084 } 085 086 /** 087 * Delete all files corresponding to the file filter into the file tree. 088 * @param path the path to delete (can be a file or a directory) 089 * @param configuration the configuration for deletion 090 * @param fileFilter the file filter to apply 091 * @throws IOException if an error occured while exploring or deleting files 092 */ 093 protected void _delete(Path path, DeleteFilesConfiguration configuration, DirectoryStream.Filter<Path> fileFilter) throws IOException 094 { 095 _fileHelper.delete(path, fileFilter, configuration.deleteRecursively(), configuration.deleteEmptyFolders()); 096 } 097 098 /** 099 * Create the file filter to delete files. 100 * @param configuration The delete files configuration 101 * @return a file filter 102 */ 103 protected DirectoryStream.Filter<Path> _createFileFilter(DeleteFilesConfiguration configuration) 104 { 105 return new DirectoryStream.Filter<>() 106 { 107 @Override 108 public boolean accept(Path entry) throws IOException 109 { 110 return Files.getLastModifiedTime(entry) 111 .toInstant() 112 .isBefore(configuration.getAgeLimit()); 113 } 114 }; 115 } 116 117 /** 118 * Inner class to store configuration for deletion. 119 */ 120 protected static class DeleteFilesConfiguration 121 { 122 private Path _rootPath; 123 private boolean _deleteRoot; 124 private boolean _deleteRecursively; 125 private boolean _deleteEmptyFolders; 126 private Instant _ageLimit; 127 128 /** 129 * Constructor. 130 * Default values: 131 * - ageLimit = now 132 * - deleteRoot = false 133 * - deleteRecursively = true 134 * - deleteEmptyFolders = true 135 * @param rootPath The root path for deletion 136 */ 137 public DeleteFilesConfiguration(Path rootPath) 138 { 139 _rootPath = rootPath; 140 _ageLimit = Instant.now(); 141 _deleteRoot = false; 142 _deleteRecursively = true; 143 _deleteEmptyFolders = true; 144 } 145 146 /** 147 * Get the root folder or file to delete 148 * @return a Directory or a file, that will be deleted/emptied 149 */ 150 public Path getRootPath() 151 { 152 return _rootPath; 153 } 154 155 /** 156 * Set to <code>true</code> to delete the root folder. 157 * @param deleteRoot <code>true</code> to delete the root folde 158 */ 159 public void setDeleteRoot(boolean deleteRoot) 160 { 161 _deleteRoot = deleteRoot; 162 } 163 164 /** 165 * Do the root needs to be deleted or emptied ? 166 * @return <code>true</code> to delete the root folder 167 */ 168 public boolean deleteRoot() 169 { 170 return _deleteRoot; 171 } 172 173 /** 174 * Set to <code>true</code> to delete files recursively. 175 * @param deleteRecursively <code>true</code> to delete files recursively 176 */ 177 public void setDeleteRecursively(boolean deleteRecursively) 178 { 179 _deleteRecursively = deleteRecursively; 180 } 181 182 /** 183 * Do the folders needs to be deleted recursively ? 184 * @return <code>true</code> to delete files recursively 185 */ 186 public boolean deleteRecursively() 187 { 188 return _deleteRecursively; 189 } 190 191 /** 192 * Set to <code>true</code> to delete empty folders. 193 * @param deleteEmptyFolders <code>true</code> to delete empty folders 194 */ 195 public void setDeleteEmptyFolders(boolean deleteEmptyFolders) 196 { 197 _deleteEmptyFolders = deleteEmptyFolders; 198 } 199 200 /** 201 * When a folder is empty, do it needs to be deleted ? 202 * @return <code>true</code> to delete empty folders 203 */ 204 public boolean deleteEmptyFolders() 205 { 206 return _deleteEmptyFolders; 207 } 208 209 /** 210 * Set the age limit of files to delete. 211 * @param ageLimit The age limit 212 */ 213 public void setAgeLimit(Instant ageLimit) 214 { 215 _ageLimit = ageLimit; 216 } 217 218 /** 219 * Only delete files older than this instant. 220 * @return instant, only older files will be deleted 221 */ 222 public Instant getAgeLimit() 223 { 224 return _ageLimit; 225 } 226 } 227}