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.apache.kahadb.util;
018    
019    import java.io.File;
020    import java.io.FileInputStream;
021    import java.io.FileOutputStream;
022    import java.io.IOException;
023    import java.io.InputStream;
024    import java.io.OutputStream;
025    import java.util.Stack;
026    
027    public 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    }