001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.activemq.util; 018 019import java.io.File; 020import java.io.FileInputStream; 021import java.io.FileOutputStream; 022import java.io.FilenameFilter; 023import java.io.IOException; 024import java.io.InputStream; 025import java.io.OutputStream; 026import java.nio.file.Files; 027import java.nio.file.Path; 028import java.nio.file.StandardCopyOption; 029import java.util.ArrayList; 030import java.util.List; 031import java.util.Stack; 032 033/** 034 * Collection of File and Folder utility methods. 035 */ 036public final class IOHelper { 037 038 protected static final int MAX_DIR_NAME_LENGTH; 039 protected static final int MAX_FILE_NAME_LENGTH; 040 private static final int DEFAULT_BUFFER_SIZE = 4096; 041 042 private IOHelper() { 043 } 044 045 public static String getDefaultDataDirectory() { 046 return getDefaultDirectoryPrefix() + "activemq-data"; 047 } 048 049 public static String getDefaultStoreDirectory() { 050 return getDefaultDirectoryPrefix() + "amqstore"; 051 } 052 053 /** 054 * Allows a system property to be used to overload the default data 055 * directory which can be useful for forcing the test cases to use a target/ 056 * prefix 057 */ 058 public static String getDefaultDirectoryPrefix() { 059 try { 060 return System.getProperty("org.apache.activemq.default.directory.prefix", ""); 061 } catch (Exception e) { 062 return ""; 063 } 064 } 065 066 /** 067 * Converts any string into a string that is safe to use as a file name. The 068 * result will only include ascii characters and numbers, and the "-","_", 069 * and "." characters. 070 * 071 * @param name 072 * @return 073 */ 074 public static String toFileSystemDirectorySafeName(String name) { 075 return toFileSystemSafeName(name, true, MAX_DIR_NAME_LENGTH); 076 } 077 078 public static String toFileSystemSafeName(String name) { 079 return toFileSystemSafeName(name, false, MAX_FILE_NAME_LENGTH); 080 } 081 082 /** 083 * Converts any string into a string that is safe to use as a file name. The 084 * result will only include ascii characters and numbers, and the "-","_", 085 * and "." characters. 086 * 087 * @param name 088 * @param dirSeparators 089 * @param maxFileLength 090 * @return 091 */ 092 public static String toFileSystemSafeName(String name, boolean dirSeparators, int maxFileLength) { 093 int size = name.length(); 094 StringBuffer rc = new StringBuffer(size * 2); 095 for (int i = 0; i < size; i++) { 096 char c = name.charAt(i); 097 boolean valid = c >= 'a' && c <= 'z'; 098 valid = valid || (c >= 'A' && c <= 'Z'); 099 valid = valid || (c >= '0' && c <= '9'); 100 valid = valid || (c == '_') || (c == '-') || (c == '.') || (c == '#') || (dirSeparators && ((c == '/') || (c == '\\'))); 101 102 if (valid) { 103 rc.append(c); 104 } else { 105 // Encode the character using hex notation 106 rc.append('#'); 107 rc.append(HexSupport.toHexFromInt(c, true)); 108 } 109 } 110 String result = rc.toString(); 111 if (result.length() > maxFileLength) { 112 result = result.substring(result.length() - maxFileLength, result.length()); 113 } 114 return result; 115 } 116 117 public static boolean delete(File top) { 118 boolean result = true; 119 Stack<File> files = new Stack<File>(); 120 // Add file to the stack to be processed... 121 files.push(top); 122 // Process all files until none remain... 123 while (!files.isEmpty()) { 124 File file = files.pop(); 125 if (file.isDirectory()) { 126 File list[] = file.listFiles(); 127 if (list == null || list.length == 0) { 128 // The current directory contains no entries... 129 // delete directory and continue... 130 result &= file.delete(); 131 } else { 132 // Add back the directory since it is not empty.... 133 // and when we process it again it will be empty and can be 134 // deleted safely... 135 files.push(file); 136 for (File dirFile : list) { 137 if (dirFile.isDirectory()) { 138 // Place the directory on the stack... 139 files.push(dirFile); 140 } else { 141 // This is a simple file, delete it... 142 result &= dirFile.delete(); 143 } 144 } 145 } 146 } else { 147 // This is a simple file, delete it... 148 result &= file.delete(); 149 } 150 } 151 return result; 152 } 153 154 public static boolean deleteFile(File fileToDelete) { 155 if (fileToDelete == null || !fileToDelete.exists()) { 156 return true; 157 } 158 boolean result = deleteChildren(fileToDelete); 159 result &= fileToDelete.delete(); 160 return result; 161 } 162 163 public static boolean deleteChildren(File parent) { 164 if (parent == null || !parent.exists()) { 165 return false; 166 } 167 boolean result = true; 168 if (parent.isDirectory()) { 169 File[] files = parent.listFiles(); 170 if (files == null) { 171 result = false; 172 } else { 173 for (int i = 0; i < files.length; i++) { 174 File file = files[i]; 175 if (file.getName().equals(".") || file.getName().equals("..")) { 176 continue; 177 } 178 if (file.isDirectory()) { 179 result &= deleteFile(file); 180 } else { 181 result &= file.delete(); 182 } 183 } 184 } 185 } 186 187 return result; 188 } 189 190 public static void moveFile(File src, File targetDirectory) throws IOException { 191 if (!src.renameTo(new File(targetDirectory, src.getName()))) { 192 193 // If rename fails we must do a true deep copy instead. 194 Path sourcePath = src.toPath(); 195 Path targetDirPath = targetDirectory.toPath(); 196 197 try { 198 Files.move(sourcePath, targetDirPath.resolve(sourcePath.getFileName()), StandardCopyOption.REPLACE_EXISTING); 199 } catch (IOException ex) { 200 throw new IOException("Failed to move " + src + " to " + targetDirectory + " - " + ex.getMessage()); 201 } 202 } 203 } 204 205 public static void moveFiles(File srcDirectory, File targetDirectory, FilenameFilter filter) throws IOException { 206 if (!srcDirectory.isDirectory()) { 207 throw new IOException("source is not a directory"); 208 } 209 210 if (targetDirectory.exists() && !targetDirectory.isDirectory()) { 211 throw new IOException("target exists and is not a directory"); 212 } else { 213 mkdirs(targetDirectory); 214 } 215 216 List<File> filesToMove = new ArrayList<File>(); 217 getFiles(srcDirectory, filesToMove, filter); 218 219 for (File file : filesToMove) { 220 if (!file.isDirectory()) { 221 moveFile(file, targetDirectory); 222 } 223 } 224 } 225 226 public static void copyFile(File src, File dest) throws IOException { 227 copyFile(src, dest, null); 228 } 229 230 public static void copyFile(File src, File dest, FilenameFilter filter) throws IOException { 231 if (src.getCanonicalPath().equals(dest.getCanonicalPath()) == false) { 232 if (src.isDirectory()) { 233 234 mkdirs(dest); 235 List<File> list = getFiles(src, filter); 236 for (File f : list) { 237 if (f.isFile()) { 238 File target = new File(getCopyParent(src, dest, f), f.getName()); 239 copySingleFile(f, target); 240 } 241 } 242 243 } else if (dest.isDirectory()) { 244 mkdirs(dest); 245 File target = new File(dest, src.getName()); 246 copySingleFile(src, target); 247 } else { 248 copySingleFile(src, dest); 249 } 250 } 251 } 252 253 static File getCopyParent(File from, File to, File src) { 254 File result = null; 255 File parent = src.getParentFile(); 256 String fromPath = from.getAbsolutePath(); 257 if (parent.getAbsolutePath().equals(fromPath)) { 258 // one level down 259 result = to; 260 } else { 261 String parentPath = parent.getAbsolutePath(); 262 String path = parentPath.substring(fromPath.length()); 263 result = new File(to.getAbsolutePath() + File.separator + path); 264 } 265 return result; 266 } 267 268 static List<File> getFiles(File dir, FilenameFilter filter) { 269 List<File> result = new ArrayList<File>(); 270 getFiles(dir, result, filter); 271 return result; 272 } 273 274 static void getFiles(File dir, List<File> list, FilenameFilter filter) { 275 if (!list.contains(dir)) { 276 list.add(dir); 277 String[] fileNames = dir.list(filter); 278 for (int i = 0; i < fileNames.length; i++) { 279 File f = new File(dir, fileNames[i]); 280 if (f.isFile()) { 281 list.add(f); 282 } else { 283 getFiles(dir, list, filter); 284 } 285 } 286 } 287 } 288 289 public static void copySingleFile(File src, File dest) throws IOException { 290 FileInputStream fileSrc = new FileInputStream(src); 291 FileOutputStream fileDest = new FileOutputStream(dest); 292 copyInputStream(fileSrc, fileDest); 293 } 294 295 public static void copyInputStream(InputStream in, OutputStream out) throws IOException { 296 try { 297 byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; 298 int len = in.read(buffer); 299 while (len >= 0) { 300 out.write(buffer, 0, len); 301 len = in.read(buffer); 302 } 303 } finally { 304 in.close(); 305 out.close(); 306 } 307 } 308 309 static { 310 MAX_DIR_NAME_LENGTH = Integer.getInteger("MaximumDirNameLength", 200); 311 MAX_FILE_NAME_LENGTH = Integer.getInteger("MaximumFileNameLength", 64); 312 } 313 314 public static int getMaxDirNameLength() { 315 return MAX_DIR_NAME_LENGTH; 316 } 317 318 public static int getMaxFileNameLength() { 319 return MAX_FILE_NAME_LENGTH; 320 } 321 322 public static void mkdirs(File dir) throws IOException { 323 if (dir.exists()) { 324 if (!dir.isDirectory()) { 325 throw new IOException("Failed to create directory '" + dir + 326 "', regular file already existed with that name"); 327 } 328 329 } else { 330 if (!dir.mkdirs()) { 331 throw new IOException("Failed to create directory '" + dir + "'"); 332 } 333 } 334 } 335}