Java tutorial
/* * Copyright (C) 2016 Singular Studios (a.k.a Atom Tecnologia) - www.opensingular.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.opensingular.internal.lib.commons.util; import org.apache.commons.io.IOUtils; import org.opensingular.lib.commons.base.SingularException; import org.opensingular.lib.commons.lambda.IConsumerEx; import org.opensingular.lib.commons.util.TempFileUtils; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Objects; /** * Apoia na criao de arquivos ou diretriso temporarios garantindo a excluso dos mesmo ou na chmada do mtodo * close() ou no encerramento da execuo do cdigo informado. O objetivo no deixar lixo * para trs. H duas formas de uso: * <p> * <p>Mais recomendada, por ser mais garantido que no ficarao arquivos para trs:</p> * <pre> * TempFileProvider.create(this, tempFileProvider -> { * File dir = tempFileProvider.createTempDir(); * File file1 = tempFileProvider.createTempFile(); * File file2 = tempFileProvider.createTempFile("content.txt"); * byte[] content = ... * File file2 = tempFileProvider.createTempFile(content); * // do something * }); * //When the call is fineshed, all temp files and dirs will deleted * </pre> * <p> * <p>Forma alternativa:</p> * <pre> * try(TempFileProvider tempFileProvider = TempFileProvider.createForUseInTryClause(this) { * File dir = tempFileProvider.createTempDir(); * File file1 = tempFileProvider.createTempFile(); * File file2 = tempFileProvider.createTempFile("content.txt"); * byte[] content = ... * File file2 = tempFileProvider.createTempFile(content); * // do something * } * //When the call is fineshed, all temp files and dirs will deleted * </pre> * * @author Daniel C. Bordin 01/04/2017 */ public class TempFileProvider implements Closeable { static final String DEFAULT_FILE_SUFFIX = ".tmp"; static final String DEFAULT_FILE_PREFIX = "Singular_"; private final Class<?> owner; private final List<TempEntry> tempFiles = new ArrayList<>(); TempFileProvider(Class<?> owner) { this.owner = Objects.requireNonNull(owner); } /** * (Use preferencialmente {@link #create(Object, IConsumerEx)}) Cria um provedore de arquivos temporrios a ser * usado em uma clasula <code>try(){}</code>. Exemplo: * <pre> * try(TempFileProvider tempFileProvider = TempFileProvider.createForUseInTryClause(this) { * File file1 = tempFileProvider.createTempFile(); * // do something * } // temp files will automatically be deleted here * </pre> * Dispara exception seno conseguir apagar todos os arquivos temporrios criados (provavelmente por haver algum * stream ainda aberto em cima de um arquivo temporrio). * * @param requester do arquivos temporrio. O nome da classe ser usado como prefixo no arquivo temporrio. */ public static TempFileProvider createForUseInTryClause(@Nonnull Object requester) { return new TempFileProvider(resolverRequester(requester)); } /** * Cria um provedor de arquivos temporrios e executado o cdigo (<code>callback</code>) com esse provedor. Na * sada da chamada apaga todos os arquicos temporrios. Exemplo: * <pre> * TempFileProvider.create(this, tempFileProvider -> { * File file1 = tempFileProvider.createTempFile(); * // do something * } // temp files will automatically be deleted here * </pre> * Dispara exception seno conseguir apagar todos os arquivos temporrios criados (provavelmente por haver algum * stream ainda aberto em cima de um arquivo temporrio). * * @param requester do arquivos temporrio. O nome da classe ser usado como prefixo no arquivo temporrio. * @param callback cdigo a ser executado com o provedor de arquivos temporrios */ public static void create(@Nonnull Object requester, @Nonnull IConsumerEx<TempFileProvider, IOException> callback) { TempFileProvider tmp = createForUseInTryClause(requester); boolean hadException = true; try { callback.accept(tmp); hadException = false; } catch (IOException e) { throw SingularException.rethrow(e); } finally { if (hadException) { tmp.deleteQuietly(); } else { tmp.deleteOrException(); } } } private static Class<?> resolverRequester(Object requester) { Objects.requireNonNull(requester); return requester instanceof Class ? (Class<?>) requester : requester.getClass(); } /** * Cria uma diretrio temporrio (incluindo na lista para deleo automtica). O diretrio e seus arquivos de * contedo sero apagados juntos. */ public File createTempDir() { File f = createTempDir(DEFAULT_FILE_PREFIX); tempFiles.add(new TempEntry(f, true)); return f; } /** Cria uma diretrio temporrio com o prefixo informado. */ private static File createTempDir(@Nonnull String prefix) { File baseDir = new File(System.getProperty("java.io.tmpdir")); String baseName = prefix + System.currentTimeMillis() + "-"; for (int counter = 0; counter < 10000; ++counter) { File tempDir = new File(baseDir, baseName + counter); if (tempDir.mkdir()) { return tempDir; } } throw new IllegalStateException("Failed to create directory within 10000 attempts (tried " + baseName + "0 to " + baseName + 9999 + ')'); } /** Cria um arquivo temporrio (incluindo na lista para deleo automtica). */ @Nonnull public File createTempFile() { return createTempFile((String) null, (String) null); } /** Cria um arquivo temporrio j com o contedo informado (incluindo na lista para deleo automtica). */ @Nonnull public File createTempFile(@Nonnull byte[] content) { return createTempFile(new ByteArrayInputStream(content), null); } /** * Cria um arquivo temporrio j com o contedo informado (incluindo na lista para deleo automtica). * * @param suffix Texto para ser colocado como sufixo do nome do arquivo criado (em geral a extenso). Exemplo: * ".txt" ou "result.txt". Seno informado, o defult ".tmp" */ @Nonnull public File createTempFile(@Nonnull byte[] content, @Nullable String suffix) { return createTempFile(new ByteArrayInputStream(content), suffix); } /** Cria um arquivo temporrio j com o contedo informado (incluindo na lista para deleo automtica). */ @Nonnull public File createTempFile(@Nonnull InputStream content) { return createTempFile(content, null); } /** * Cria um arquivo temporrio j com o contedo informado (incluindo na lista para deleo automtica). * * @param suffix Texto para ser colocado como sufixo do nome do arquivo criado (em geral a extenso). Exemplo: * ".txt" ou "result.txt". Seno informado, o defult ".tmp" */ @Nonnull public File createTempFile(@Nonnull InputStream content, @Nullable String suffix) { File f = createTempFile(suffix); try (InputStream in = content; FileOutputStream fout = new FileOutputStream(f)) { IOUtils.copy(in, fout); } catch (IOException e) { throw SingularException.rethrow("Erro escrevendo contedo no arquivo temporrio", e); } return f; } /** * Cria um arquivo temporrio com o sufixo informado (incluindo na lista para deleo automtica). * * @param suffix Texto para ser colocado como sufixo do nome do arquivo criado (em geral a extenso). Exemplo: * ".txt" ou "result.txt". Seno informado, o defult ".tmp" */ @Nonnull public File createTempFile(@Nullable String suffix) { return createTempFile((String) null, suffix); } /** * Cria um arquivo temporrio com o prefixo e sufixo informados (incluindo na lista para deleo automtica). * * @param prefix Texto para ser colocado como prefixo do nome do arquivo criado. * @param suffix Texto para ser colocado como sufixo do nome do arquivo criado (em geral a extenso). Exemplo: * ".txt" ou "result.txt". Seno informado, o defult ".tmp" */ @Nonnull public File createTempFile(@Nullable String prefix, @Nullable String suffix) { File f = createTempFileInternal(prefix, suffix); tempFiles.add(new TempEntry(f, false)); return f; } @Nonnull private final File createTempFileInternal(@Nullable String prefix, @Nullable String suffix) { String p = prefix == null ? owner.getSimpleName() + '_' : prefix; String s = suffix == null ? DEFAULT_FILE_SUFFIX : suffix; try { return File.createTempFile(DEFAULT_FILE_PREFIX + p, s); } catch (IOException e) { throw SingularException.rethrow("Erro criando arquivo temporrio", e); } } /** * Cria um arquivo temporrio com o sufixo informado mas <b>no coloca na lista para deleo automtica</b>. Apenas * coloca como {@link File#deleteOnExit()}. * * @param suffix Texto para ser colocado como sufixo do nome do arquivo criado (em geral a extenso). Exemplo: * ".txt" ou "result.txt". Seno informado, o defult ".tmp" */ public final File createTempFileByDontPutOnDeleteList(String suffix) { File f = createTempFileInternal(null, suffix); f.deleteOnExit(); return f; } /** Chama {@link #deleteOrException()}. */ @Override public void close() { deleteOrException(); } /** * Tenta apagar os arquivos temporrios associados. No conseguindo apagar ou ocorrendo uma exception ao * chamar {@link File#delete()}, faz log do erro e dispara exception. * * @see TempFileUtils#deleteOrException(File, Class) */ public void deleteOrException() { delete(false); } /** * Tenta apagar os arquivos temporrios associados. . No conseguindo apagar ou ocorrendo uma exception ao * chamar {@link File#delete()}, faz log do erro mas no dispara exception. * * @see TempFileUtils#deleteAndFailQuietily(File, Class) */ public void deleteQuietly() { delete(true); } private void delete(boolean quietly) { Exception error = null; for (TempEntry entry : tempFiles) { try { if (entry.diretory) { recursiveDelete(entry.file, quietly); } else { deleteSingleFile(entry.file, quietly); } } catch (Exception e) { error = e; } } tempFiles.clear(); if (error != null) { throw SingularException.rethrow(error); } } private void recursiveDelete(File file, boolean quietly) { File[] files = file.listFiles(); if (files != null) { for (File each : files) { recursiveDelete(each, quietly); } } deleteSingleFile(file, quietly); } private void deleteSingleFile(File file, boolean quietly) { if (quietly) { TempFileUtils.deleteAndFailQuietily(file, owner); } else { TempFileUtils.deleteOrException(file, owner); } } private static class TempEntry { public final boolean diretory; public final File file; private TempEntry(File file, boolean diretory) { this.file = file; this.diretory = diretory; } } }