001/* 002 * SPDX-FileCopyrightText: none 003 * SPDX-License-Identifier: CC0-1.0 004 */ 005 006package dev.metaschema.core.util; 007 008import org.eclipse.jdt.annotation.Owning; 009 010import java.io.FilterOutputStream; 011import java.io.IOException; 012import java.io.OutputStream; 013 014import edu.umd.cs.findbugs.annotations.NonNull; 015import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 016 017/** 018 * Wraps a resource to make it {@link AutoCloseable}. 019 * 020 * @param <T> 021 * the resource type 022 * @param <E> 023 * the exception type that may be thrown if an error occurs when 024 * closing the resource 025 */ 026public final class AutoCloser<T, E extends Exception> implements AutoCloseable { 027 @NonNull 028 private final T resource; 029 @NonNull 030 private final Closer<T, E> closeLambda; 031 032 /** 033 * Adapt the the provided {@code resource} to be {@link AutoCloseable}, using a 034 * provided closer {@code lambda}. 035 * <p> 036 * The caller owns the returned wrapper and is responsible for closing it. 037 * 038 * @param <T> 039 * the resource's type 040 * @param <E> 041 * the exception type that can be thrown when closing 042 * @param resource 043 * the object to adapt 044 * @param lambda 045 * the lambda to use as a callback on close 046 * @return the resource wrapped in an {@link AutoCloseable} 047 */ 048 @Owning 049 @NonNull 050 public static <T, E extends Exception> AutoCloser<T, E> autoClose( 051 @NonNull T resource, 052 @NonNull Closer<T, E> lambda) { 053 return new AutoCloser<>(resource, lambda); 054 } 055 056 /** 057 * Adapt the provided {@code resource} to be {@link AutoCloseable}, using a 058 * provided closer {@code lambda}. 059 * 060 * @param resource 061 * the object to adapt 062 * @param lambda 063 * the lambda to use as a callback on close 064 */ 065 private AutoCloser(@NonNull T resource, @NonNull Closer<T, E> lambda) { 066 this.resource = resource; 067 this.closeLambda = lambda; 068 } 069 070 /** 071 * Get the wrapped resource. 072 * 073 * @return the resource object 074 */ 075 @NonNull 076 public T getResource() { 077 return resource; 078 } 079 080 @Override 081 @SuppressFBWarnings(value = "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION", 082 justification = "Generic exception is required for flexible resource closing") 083 public void close() throws E { 084 closeLambda.close(getResource()); 085 } 086 087 /** 088 * A callback interface representing a close operation. 089 * 090 * @param <T> 091 * the Java type of the object being closed 092 * @param <E> 093 * the Java type of the exception that can be thrown when closing 094 */ 095 @FunctionalInterface 096 public interface Closer<T, E extends Exception> { 097 /** 098 * This method is called to auto-close the resource. 099 * 100 * @param object 101 * the resource to auto-close 102 * @throws E 103 * the exception type that can be thrown when closing 104 */ 105 @SuppressFBWarnings("THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION") 106 void close(@NonNull T object) throws E; 107 } 108 109 /** 110 * Wraps the provided output stream to prevent the wrapped stream from being 111 * closed. 112 * <p> 113 * This is useful for protecting standard streams. i.e. {@link System#out}, 114 * {@link System#err}. 115 * <p> 116 * The caller owns the returned stream and is responsible for closing it. 117 * 118 * @param out 119 * the stream to wrap 120 * @return the new wrapped stream 121 */ 122 @Owning 123 @NonNull 124 public static OutputStream preventClose(OutputStream out) { 125 return new ClosePreventingOutputStream(out); 126 } 127 128 private static class ClosePreventingOutputStream 129 extends FilterOutputStream { 130 131 public ClosePreventingOutputStream(OutputStream out) { 132 super(out); 133 } 134 135 @Override 136 public void close() throws IOException { 137 // do nothing 138 } 139 } 140}