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 */ 017 package org.fusesource.hawtdb.util; 018 019 import java.io.File; 020 import java.io.FileDescriptor; 021 import java.io.FileInputStream; 022 import java.io.FileOutputStream; 023 import java.io.IOException; 024 import java.io.InputStream; 025 import java.io.OutputStream; 026 027 /** 028 */ 029 public final class IOHelper { 030 protected static final int MAX_DIR_NAME_LENGTH; 031 protected static final int MAX_FILE_NAME_LENGTH; 032 private static final int DEFAULT_BUFFER_SIZE = 4096; 033 private IOHelper() { 034 } 035 036 public static String getDefaultDataDirectory() { 037 return getDefaultDirectoryPrefix() + "activemq-data"; 038 } 039 040 public static String getDefaultStoreDirectory() { 041 return getDefaultDirectoryPrefix() + "amqstore"; 042 } 043 044 /** 045 * Allows a system property to be used to overload the default data 046 * directory which can be useful for forcing the test cases to use a target/ 047 * prefix 048 */ 049 public static String getDefaultDirectoryPrefix() { 050 try { 051 return System.getProperty("org.apache.activemq.default.directory.prefix", ""); 052 } catch (Exception e) { 053 return ""; 054 } 055 } 056 057 /** 058 * Converts any string into a string that is safe to use as a file name. 059 * The result will only include ascii characters and numbers, and the "-","_", and "." characters. 060 * 061 * @param name 062 * @return 063 */ 064 public static String toFileSystemDirectorySafeName(String name) { 065 return toFileSystemSafeName(name, true, MAX_DIR_NAME_LENGTH); 066 } 067 068 public static String toFileSystemSafeName(String name) { 069 return toFileSystemSafeName(name, false, MAX_FILE_NAME_LENGTH); 070 } 071 072 /** 073 * Converts any string into a string that is safe to use as a file name. 074 * The result will only include ascii characters and numbers, and the "-","_", and "." characters. 075 * 076 * @param name 077 * @param dirSeparators 078 * @param maxFileLength 079 * @return 080 */ 081 public static String toFileSystemSafeName(String name,boolean dirSeparators,int maxFileLength) { 082 int size = name.length(); 083 StringBuffer rc = new StringBuffer(size * 2); 084 for (int i = 0; i < size; i++) { 085 char c = name.charAt(i); 086 boolean valid = c >= 'a' && c <= 'z'; 087 valid = valid || (c >= 'A' && c <= 'Z'); 088 valid = valid || (c >= '0' && c <= '9'); 089 valid = valid || (c == '_') || (c == '-') || (c == '.') || (c=='#') 090 ||(dirSeparators && ( (c == '/') || (c == '\\'))); 091 092 if (valid) { 093 rc.append(c); 094 } else { 095 // Encode the character using hex notation 096 rc.append('#'); 097 rc.append(HexSupport.toHexFromInt(c, true)); 098 } 099 } 100 String result = rc.toString(); 101 if (result.length() > maxFileLength) { 102 result = result.substring(result.length()-maxFileLength,result.length()); 103 } 104 return result; 105 } 106 107 public static boolean deleteFile(File fileToDelete) { 108 if (fileToDelete == null || !fileToDelete.exists()) { 109 return true; 110 } 111 boolean result = deleteChildren(fileToDelete); 112 result &= fileToDelete.delete(); 113 return result; 114 } 115 116 public static boolean deleteChildren(File parent) { 117 if (parent == null || !parent.exists()) { 118 return false; 119 } 120 boolean result = true; 121 if (parent.isDirectory()) { 122 File[] files = parent.listFiles(); 123 if (files == null) { 124 result = false; 125 } else { 126 for (int i = 0; i < files.length; i++) { 127 File file = files[i]; 128 if (file.getName().equals(".") 129 || file.getName().equals("..")) { 130 continue; 131 } 132 if (file.isDirectory()) { 133 result &= deleteFile(file); 134 } else { 135 result &= file.delete(); 136 } 137 } 138 } 139 } 140 141 return result; 142 } 143 144 145 public static void moveFile(File src, File targetDirectory) throws IOException { 146 if (!src.renameTo(new File(targetDirectory, src.getName()))) { 147 throw new IOException("Failed to move " + src + " to " + targetDirectory); 148 } 149 } 150 151 public static void copyFile(File src, File dest) throws IOException { 152 FileInputStream fileSrc = new FileInputStream(src); 153 FileOutputStream fileDest = new FileOutputStream(dest); 154 copyInputStream(fileSrc, fileDest); 155 } 156 157 public static void copyInputStream(InputStream in, OutputStream out) throws IOException { 158 byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; 159 int len = in.read(buffer); 160 while (len >= 0) { 161 out.write(buffer, 0, len); 162 len = in.read(buffer); 163 } 164 in.close(); 165 out.close(); 166 } 167 168 static { 169 MAX_DIR_NAME_LENGTH = Integer.valueOf(System.getProperty("MaximumDirNameLength","200")).intValue(); 170 MAX_FILE_NAME_LENGTH = Integer.valueOf(System.getProperty("MaximumFileNameLength","64")).intValue(); 171 } 172 173 174 public static void mkdirs(File dir) throws IOException { 175 if (dir.exists()) { 176 if (!dir.isDirectory()) { 177 throw new IOException("Failed to create directory '" + dir +"', regular file already existed with that name"); 178 } 179 180 } else { 181 if (!dir.mkdirs()) { 182 throw new IOException("Failed to create directory '" + dir+"'"); 183 } 184 } 185 } 186 187 public interface IOStrategy { 188 void sync(FileDescriptor fdo) throws IOException; 189 } 190 191 static final IOStrategy IO_STRATEGY = createIOStrategy(); 192 193 private static IOStrategy createIOStrategy() { 194 195 // On OS X, the fsync system call does not fully flush the hardware buffers.. 196 // to do that you have to do an fcntl call, and the only way to do that is to 197 // do some JNI. 198 String os = System.getProperty("os.name"); 199 // if( "Mac OS X".equals(os) ) { 200 // 201 // // We will gracefully fall back to default JDK file sync behavior 202 // // if the JNA library is not in the path, and we can't set the 203 // // FileDescriptor.fd field accessible. 204 // try { 205 // final Field field = FileDescriptor.class.getDeclaredField("fd"); 206 // field.setAccessible(true); 207 // // Try to dynamically load the JNA impl of the CLibrary interface.. 208 // final CLibrary lib = getCLibrary(); 209 // return new IOStrategy() { 210 // static final int F_FULLFSYNC = 51; 211 // public void sync(FileDescriptor fd) throws IOException { 212 // try { 213 // int id = field.getInt(fd); 214 // lib.fcntl(id, F_FULLFSYNC); 215 // } catch (Exception e) { 216 // throw IOExceptionSupport.create(e); 217 // } 218 // } 219 // }; 220 // } catch (Throwable ignore) { 221 // // Perhaps we should issue a warning here so folks know that 222 // // the disk syncs are not going to be of very good quality. 223 // } 224 // } 225 226 return new IOStrategy() { 227 public void sync(FileDescriptor fd) throws IOException { 228 fd.sync(); 229 } 230 }; 231 } 232 233 // @SuppressWarnings("unchecked") 234 // public static CLibrary getCLibrary() throws ClassNotFoundException, IllegalAccessException, NoSuchFieldException { 235 // Class clazz = IOHelper.class.getClassLoader().loadClass("org.apache.activemq.util.os.JnaCLibrary"); 236 // final CLibrary lib = (CLibrary) clazz.getField("INSTANCE").get(null); 237 // return lib; 238 // } 239 240 static public void sync(FileDescriptor fd) throws IOException { 241 IO_STRATEGY.sync(fd); 242 } 243 244 }