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