001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.eclipse.aether.internal.test.util;
020
021import java.io.BufferedOutputStream;
022import java.io.File;
023import java.io.FileInputStream;
024import java.io.FileOutputStream;
025import java.io.IOException;
026import java.io.OutputStream;
027import java.io.RandomAccessFile;
028import java.nio.charset.StandardCharsets;
029import java.nio.file.Files;
030import java.util.ArrayList;
031import java.util.Collection;
032import java.util.Properties;
033import java.util.UUID;
034
035/**
036 * Provides utility methods to read and write (temporary) files.
037 */
038public class TestFileUtils {
039
040    private static final File TMP = new File(
041            System.getProperty("java.io.tmpdir"),
042            "aether-" + UUID.randomUUID().toString().substring(0, 8));
043
044    static {
045        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
046            try {
047                deleteFile(TMP);
048            } catch (IOException e) {
049                e.printStackTrace();
050            }
051        }));
052    }
053
054    private TestFileUtils() {
055        // hide constructor
056    }
057
058    @Deprecated
059    public static void deleteTempFiles() throws IOException {
060        deleteFile(TMP);
061    }
062
063    public static void deleteFile(File file) throws IOException {
064        if (file == null) {
065            return;
066        }
067
068        Collection<File> undeletables = new ArrayList<>();
069
070        delete(file, undeletables);
071
072        if (!undeletables.isEmpty()) {
073            throw new IOException("Failed to delete " + undeletables);
074        }
075    }
076
077    private static void delete(File file, Collection<File> undeletables) {
078        String[] children = file.list();
079        if (children != null) {
080            for (String child : children) {
081                delete(new File(file, child), undeletables);
082            }
083        }
084
085        if (!del(file)) {
086            undeletables.add(file.getAbsoluteFile());
087        }
088    }
089
090    private static boolean del(File file) {
091        for (int i = 0; i < 10; i++) {
092            if (file.delete() || !file.exists()) {
093                return true;
094            }
095        }
096        return false;
097    }
098
099    @Deprecated
100    public static boolean mkdirs(File directory) {
101        if (directory == null) {
102            return false;
103        }
104
105        if (directory.exists()) {
106            return false;
107        }
108        if (directory.mkdir()) {
109            return true;
110        }
111
112        File canonDir = null;
113        try {
114            canonDir = directory.getCanonicalFile();
115        } catch (IOException e) {
116            return false;
117        }
118
119        File parentDir = canonDir.getParentFile();
120        return (parentDir != null && (mkdirs(parentDir) || parentDir.exists()) && canonDir.mkdir());
121    }
122
123    /**
124     * @throws IOException if an I/O error occurs
125     * @deprecated use @TempDir (JUnit 5) Or TemporaryFolder (JUnit 4) instead
126     */
127    @Deprecated
128    public static File createTempFile(String contents) throws IOException {
129        return createTempFile(contents.getBytes(StandardCharsets.UTF_8), 1);
130    }
131
132    @Deprecated
133    /**
134     * @throws IOException if an I/O error occurs
135     * @deprecated use @TempDir (JUnit 5) Or TemporaryFolder (JUnit 4) instead
136     */
137    public static File createTempFile(byte[] pattern, int repeat) throws IOException {
138        mkdirs(TMP);
139        File tmpFile = File.createTempFile("tmpfile-", ".data", TMP);
140        writeBytes(tmpFile, pattern, repeat);
141        return tmpFile;
142    }
143
144    /**
145     * Creates a temporary directory.
146     *
147     * @return the temporary directory
148     * @throws IOException if an I/O error occurs
149     * @deprecated use @TempDir (JUnit 5) Or TemporaryFolder (JUnit 4) instead
150     */
151    @Deprecated
152    public static File createTempDir() throws IOException {
153        return createTempDir("");
154    }
155
156    /**
157     * Creates a temporary directory.
158     *
159     * @return the temporary directory
160     * @throws IOException if an I/O error occurs
161     * @deprecated use {@code @TempDir} (JUnit 5) or {@code TemporaryFolder} (JUnit 4) instead
162     */
163    @Deprecated
164    public static File createTempDir(String suffix) throws IOException {
165        mkdirs(TMP);
166        File tmpFile = File.createTempFile("tmpdir-", suffix, TMP);
167        deleteFile(tmpFile);
168        mkdirs(tmpFile);
169        return tmpFile;
170    }
171
172    public static long copyFile(File source, File target) throws IOException {
173        long total = 0;
174
175        FileInputStream fis = null;
176        OutputStream fos = null;
177        try {
178            fis = new FileInputStream(source);
179
180            mkdirs(target.getParentFile());
181
182            fos = new BufferedOutputStream(new FileOutputStream(target));
183
184            for (byte[] buffer = new byte[1024 * 32]; ; ) {
185                int bytes = fis.read(buffer);
186                if (bytes < 0) {
187                    break;
188                }
189
190                fos.write(buffer, 0, bytes);
191
192                total += bytes;
193            }
194
195            fos.close();
196            fos = null;
197
198            fis.close();
199            fis = null;
200        } finally {
201            try {
202                if (fos != null) {
203                    fos.close();
204                }
205            } catch (final IOException e) {
206                // Suppressed due to an exception already thrown in the try block.
207            } finally {
208                try {
209                    if (fis != null) {
210                        fis.close();
211                    }
212                } catch (final IOException e) {
213                    // Suppressed due to an exception already thrown in the try block.
214                }
215            }
216        }
217
218        return total;
219    }
220
221    /**
222     * Reads the contents of a file into a byte array.
223     *
224     * @param file the file to read
225     * @return the contents of the file as a byte array
226     * @throws IOException if an I/O error occurs
227     * @deprecated use {@code Files.readAllBytes(Path)} instead
228     */
229    @Deprecated
230    public static byte[] readBytes(File file) throws IOException {
231        RandomAccessFile in = null;
232        try {
233            in = new RandomAccessFile(file, "r");
234            byte[] actual = new byte[(int) in.length()];
235            in.readFully(actual);
236            in.close();
237            in = null;
238            return actual;
239        } finally {
240            try {
241                if (in != null) {
242                    in.close();
243                }
244            } catch (final IOException e) {
245                // Suppressed due to an exception already thrown in the try block.
246            }
247        }
248    }
249
250    @Deprecated
251    public static void writeBytes(File file, byte[] pattern, int repeat) throws IOException {
252        file.deleteOnExit();
253        file.getParentFile().mkdirs();
254        OutputStream out = null;
255        try {
256            out = new BufferedOutputStream(new FileOutputStream(file));
257            for (int i = 0; i < repeat; i++) {
258                out.write(pattern);
259            }
260            out.close();
261            out = null;
262        } finally {
263            try {
264                if (out != null) {
265                    out.close();
266                }
267            } catch (final IOException e) {
268                // Suppressed due to an exception already thrown in the try block.
269            }
270        }
271    }
272
273    public static String readString(File file) throws IOException {
274        byte[] content = Files.readAllBytes(file.toPath());
275        return new String(content, StandardCharsets.UTF_8);
276    }
277
278    @Deprecated
279    public static void writeString(File file, String content) throws IOException {
280        writeBytes(file, content.getBytes(StandardCharsets.UTF_8), 1);
281    }
282
283    @Deprecated
284    public static void writeString(File file, String content, long timestamp) throws IOException {
285        writeBytes(file, content.getBytes(StandardCharsets.UTF_8), 1);
286        file.setLastModified(timestamp);
287    }
288
289    public static void readProps(File file, Properties props) throws IOException {
290        FileInputStream fis = null;
291        try {
292            fis = new FileInputStream(file);
293            props.load(fis);
294            fis.close();
295            fis = null;
296        } finally {
297            try {
298                if (fis != null) {
299                    fis.close();
300                }
301            } catch (final IOException e) {
302                // Suppressed due to an exception already thrown in the try block.
303            }
304        }
305    }
306
307    public static void writeProps(File file, Properties props) throws IOException {
308        file.getParentFile().mkdirs();
309
310        FileOutputStream fos = null;
311        try {
312            fos = new FileOutputStream(file);
313            props.store(fos, "aether-test");
314            fos.close();
315            fos = null;
316        } finally {
317            try {
318                if (fos != null) {
319                    fos.close();
320                }
321            } catch (final IOException e) {
322                // Suppressed due to an exception already thrown in the try block.
323            }
324        }
325    }
326}