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.camel.util;
018    
019    import java.io.File;
020    import java.io.IOException;
021    import java.util.Iterator;
022    import java.util.Locale;
023    import java.util.Stack;
024    
025    /**
026     * File utilities
027     */
028    public final class FileUtil {
029        
030        private static final int RETRY_SLEEP_MILLIS = 10;
031        private static File defaultTempDir;
032    
033        private FileUtil() {
034        }
035    
036        /**
037         * Normalizes the path to cater for Windows and other platforms
038         */
039        public static String normalizePath(String path) {
040            // special handling for Windows where we need to convert / to \\
041            if (path != null && System.getProperty("os.name").startsWith("Windows") && path.indexOf('/') >= 0) {
042                return path.replace('/', '\\');
043            }
044            return path;
045        }
046        
047        private static synchronized File getDefaultTempDir() {
048            if (defaultTempDir != null
049                && defaultTempDir.exists()) {
050                return defaultTempDir;
051            }
052            
053            String s = null;
054            try {
055                s = System.getProperty(FileUtil.class.getName() + ".TempDirectory");
056            } catch (SecurityException e) {
057                //Ignorable, we'll use the default
058            }
059            if (s == null) {
060                int x = (int)(Math.random() * 1000000);
061                s = System.getProperty("java.io.tmpdir");
062                File checkExists = new File(s);
063                if (!checkExists.exists()) {
064                    throw new RuntimeException("The directory " 
065                                           + checkExists.getAbsolutePath() 
066                                           + " does not exist, please set java.io.tempdir"
067                                           + " to an existing directory");
068                }
069                File f = new File(s, "camel-tmp-" + x);
070                while (!f.mkdir()) {
071                    x = (int)(Math.random() * 1000000);
072                    f = new File(s, "camel-tmp-" + x);
073                }
074                defaultTempDir = f;
075                Thread hook = new Thread() {
076                    @Override
077                    public void run() {
078                        removeDir(defaultTempDir);
079                    }
080                };
081                Runtime.getRuntime().addShutdownHook(hook);            
082            } else {
083                //assume someone outside of us will manage the directory
084                File f = new File(s);
085                f.mkdirs();
086                defaultTempDir = f;
087            }
088            return defaultTempDir;
089        }
090    
091        public static void mkDir(File dir) {
092            if (dir == null) {
093                throw new RuntimeException("dir attribute is required");
094            }
095    
096            if (dir.isFile()) {
097                throw new RuntimeException("Unable to create directory as a file "
098                                        + "already exists with that name: " + dir.getAbsolutePath());
099            }
100    
101            if (!dir.exists()) {
102                boolean result = doMkDirs(dir);
103                if (!result) {
104                    String msg = "Directory " + dir.getAbsolutePath()
105                                 + " creation was not successful for an unknown reason";
106                    throw new RuntimeException(msg);
107                }
108            }
109        }
110    
111        /**
112         * Attempt to fix possible race condition when creating directories on
113         * WinXP, also Windows2000. If the mkdirs does not work, wait a little and
114         * try again.
115         */
116        private static boolean doMkDirs(File f) {
117            if (!f.mkdirs()) {
118                try {
119                    Thread.sleep(RETRY_SLEEP_MILLIS);
120                    return f.mkdirs();
121                } catch (InterruptedException ex) {
122                    return f.mkdirs();
123                }
124            }
125            return true;
126        }
127    
128        public static void removeDir(File d) {
129            String[] list = d.list();
130            if (list == null) {
131                list = new String[0];
132            }
133            for (int i = 0; i < list.length; i++) {
134                String s = list[i];
135                File f = new File(d, s);
136                if (f.isDirectory()) {
137                    removeDir(f);
138                } else {
139                    delete(f);
140                }
141            }
142            delete(d);
143        }
144    
145        public static void delete(File f) {
146            if (!f.delete()) {
147                if (isWindows()) {
148                    System.gc();
149                }
150                try {
151                    Thread.sleep(RETRY_SLEEP_MILLIS);
152                } catch (InterruptedException ex) {
153                    // Ignore Exception
154                }
155                if (!f.delete()) {
156                    f.deleteOnExit();
157                }
158            }
159        }
160    
161        private static boolean isWindows() {
162            String osName = System.getProperty("os.name").toLowerCase(Locale.US);
163            return osName.indexOf("windows") > -1;
164        }
165    
166        public static File createTempFile(String prefix, String suffix) throws IOException {
167            return createTempFile(prefix, suffix, null, false);
168        }
169        
170        public static File createTempFile(String prefix, String suffix, File parentDir,
171                                   boolean deleteOnExit) throws IOException {
172            File result = null;
173            File parent = (parentDir == null)
174                ? getDefaultTempDir()
175                : parentDir;
176                
177            if (suffix == null) {
178                suffix = ".tmp";
179            }
180            if (prefix == null) {
181                prefix = "camel";
182            } else if (prefix.length() < 3) {
183                prefix = prefix + "camel";
184            }
185            result = File.createTempFile(prefix, suffix, parent);
186    
187            //if parentDir is null, we're in our default dir
188            //which will get completely wiped on exit from our exit
189            //hook.  No need to set deleteOnExit() which leaks memory.
190            if (deleteOnExit && parentDir != null) {
191                result.deleteOnExit();
192            }
193            return result;
194        }
195    
196        /**
197         * Strip any leading separators
198         */
199        public static String stripLeadingSeparator(String name) {
200            if (name == null) {
201                return null;
202            }
203            while (name.startsWith("/") || name.startsWith(File.separator)) {
204                name = name.substring(1);
205            }
206            return name;
207        }
208    
209        /**
210         * Strip any trailing separators
211         */
212        public static String stripTrailingSeparator(String name) {
213            if (name == null) {
214                return null;
215            }
216            while (name.endsWith("/") || name.endsWith(File.separator)) {
217                name = name.substring(0, name.length() - 1);
218            }
219            return name;
220        }
221    
222        /**
223         * Strips any leading paths
224         */
225        public static String stripPath(String name) {
226            if (name == null) {
227                return null;
228            }
229            int pos = name.lastIndexOf('/');
230            if (pos == -1) {
231                pos = name.lastIndexOf(File.separator);
232            }
233            if (pos != -1) {
234                return name.substring(pos + 1);
235            }
236            return name;
237        }
238    
239        /**
240         * Returns only the leading path (returns <tt>null</tt> if no path)
241         */
242        public static String onlyPath(String name) {
243            if (name == null) {
244                return null;
245            }
246            int pos = name.lastIndexOf('/');
247            if (pos == -1) {
248                pos = name.lastIndexOf(File.separator);
249            }
250            if (pos != -1) {
251                return name.substring(0, pos);
252            }
253            // no path
254            return null;
255        }
256    
257        /**
258         * Compacts a path by stacking it and reducing <tt>..</tt>
259         */
260        public static String compactPath(String path) {
261            // only normalize path if it contains .. as we want to avoid: path/../sub/../sub2 as this can leads to trouble
262            if (path.indexOf("..") == -1) {
263                return path;
264            }
265    
266            Stack<String> stack = new Stack<String>();
267            String[] parts = path.split(File.separator);
268            for (String part : parts) {
269                if (part.equals("..") && !stack.isEmpty()) {
270                    // only pop if there is a previous path
271                    stack.pop();
272                } else {
273                    stack.push(part);
274                }
275            }
276    
277            // build path based on stack
278            StringBuilder sb = new StringBuilder();
279            for (Iterator it = stack.iterator(); it.hasNext();) {
280                sb.append(it.next());
281                if (it.hasNext()) {
282                    sb.append(File.separator);
283                }
284            }
285    
286            return sb.toString();
287        }
288    
289    }