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.kahadb.util;
018
019import java.io.File;
020import java.io.FileInputStream;
021import java.io.FileOutputStream;
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.OutputStream;
025import java.util.Stack;
026
027public final class IOHelper {
028
029    protected static final int MAX_DIR_NAME_LENGTH;
030    protected static final int MAX_FILE_NAME_LENGTH;
031    private static final int DEFAULT_BUFFER_SIZE = 4096;
032
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 delete(File top) {
108        boolean result = true;
109        Stack<File> files = new Stack<File>();
110        // Add file to the stack to be processed...
111        files.push(top);
112        // Process all files until none remain...
113        while (!files.isEmpty()) {
114            File file = files.pop();
115            if (file.isDirectory()) {
116                File list[] = file.listFiles();
117                if (list == null || list.length == 0) {
118                    // The current directory contains no entries...
119                    // delete directory and continue...
120                    result &= file.delete();
121                } else {
122                    // Add back the directory since it is not empty....
123                    // and when we process it again it will be empty and can be
124                    // deleted safely...
125                    files.push(file);
126                    for (File dirFile : list) {
127                        if (dirFile.isDirectory()) {
128                            // Place the directory on the stack...
129                            files.push(dirFile);
130                        } else {
131                            // This is a simple file, delete it...
132                            result &= dirFile.delete();
133                        }
134                    }
135                }
136            } else {
137                // This is a simple file, delete it...
138                result &= file.delete();
139            }
140        }
141        return result;
142    }
143
144    private static boolean deleteFile(File fileToDelete) {
145        if (fileToDelete == null || !fileToDelete.exists()) {
146            return true;
147        }
148        boolean result = deleteChildren(fileToDelete);
149        result &= fileToDelete.delete();
150        return result;
151    }
152
153    private static boolean deleteChildren(File parent) {
154        if (parent == null || !parent.exists()) {
155            return false;
156        }
157        boolean result = true;
158        if (parent.isDirectory()) {
159            File[] files = parent.listFiles();
160            if (files == null) {
161                result = false;
162            } else {
163                for (int i = 0; i < files.length; i++) {
164                    File file = files[i];
165                    if (file.getName().equals(".")
166                            || file.getName().equals("..")) {
167                        continue;
168                    }
169                    if (file.isDirectory()) {
170                        result &= deleteFile(file);
171                    } else {
172                        result &= file.delete();
173                    }
174                }
175            }
176        }
177
178        return result;
179    }
180
181    public static void moveFile(File src, File targetDirectory) throws IOException {
182        if (!src.renameTo(new File(targetDirectory, src.getName()))) {
183            throw new IOException("Failed to move " + src + " to " + targetDirectory);
184        }
185    }
186
187    public static void copyFile(File src, File dest) throws IOException {
188        FileInputStream fileSrc = new FileInputStream(src);
189        FileOutputStream fileDest = new FileOutputStream(dest);
190        copyInputStream(fileSrc, fileDest);
191    }
192
193    public static void copyInputStream(InputStream in, OutputStream out) throws IOException {
194        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
195        int len = in.read(buffer);
196        while (len >= 0) {
197            out.write(buffer, 0, len);
198            len = in.read(buffer);
199        }
200        in.close();
201        out.close();
202    }
203
204    static {
205        MAX_DIR_NAME_LENGTH = Integer.getInteger("MaximumDirNameLength",200);
206        MAX_FILE_NAME_LENGTH = Integer.getInteger("MaximumFileNameLength",64);
207    }
208
209    public static void mkdirs(File dir) throws IOException {
210        if (dir.exists()) {
211            if (!dir.isDirectory()) {
212                throw new IOException("Failed to create directory '" + dir +"', regular file already existed with that name");
213            }
214
215        } else {
216            if (!dir.mkdirs()) {
217                throw new IOException("Failed to create directory '" + dir+"'");
218            }
219        }
220    }
221}