1 /*-
2 * #%L
3 * io.earcam.unexceptional
4 * %%
5 * Copyright (C) 2016 - 2017 earcam
6 * %%
7 * SPDX-License-Identifier: (BSD-3-Clause OR EPL-1.0 OR Apache-2.0 OR MIT)
8 *
9 * You <b>must</b> choose to accept, in full - any individual or combination of
10 * the following licenses:
11 * <ul>
12 * <li><a href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause</a></li>
13 * <li><a href="https://www.eclipse.org/legal/epl-v10.html">EPL-1.0</a></li>
14 * <li><a href="https://www.apache.org/licenses/LICENSE-2.0">Apache-2.0</a></li>
15 * <li><a href="https://opensource.org/licenses/MIT">MIT</a></li>
16 * </ul>
17 * #L%
18 */
19 package io.earcam.unexceptional;
20
21 import static io.earcam.unexceptional.Exceptional.uncheck;
22
23 import java.io.IOException;
24 import java.util.function.Supplier;
25
26 /**
27 * <p>
28 * Succinctly handle {@link AutoCloseable}s that throw checked {@link Exception}s.
29 * </p>
30 *
31 * <p>
32 * There are two families of static methods; {@code closeAfterApplying(...)} and {@code closeAfterAccepting(...)}, the
33 * former <i>applies</i> checked functions (the use-case being <b>read</b>) while the later <i>accepts</i> checked
34 * consumers (the use-case being <b>write</b>)
35 * </p>
36 *
37 * <p>
38 * <b>Example</b>: this zero-branch, single-instruction, one-liner will find a free port number:
39 * </p>
40 *
41 * <p>
42 * <code> int port = Closing.closeAfterApplying(ServerSocket::new, 0, ServerSocket::getLocalPort);</code>
43 * </p>
44 *
45 * <p>
46 * <b>Motivation</b>: I/O stream instances typically (and necessarily) requires handling a lot of possible
47 * {@link IOException}s - by invoking:
48 * </p>
49 * <ul>
50 * <li>Constructor</li>
51 * <li>A read/write call</li>
52 * <li>A call to {@link AutoCloseable#close()}</li>
53 * <li>A read/write call, then subsequently in the call to {@link AutoCloseable#close()} - via
54 * {@code try}-with-resources</li>
55 * </ul>
56 *
57 * <p>
58 * That's a lot of branches to cover - often it is irrelevant to the application which branch actually throws, but
59 * regardless test coverage suffers without some needless stub/mocked throw-on-create/read/write/close tests.
60 * </p>
61 *
62 * @since 0.3.0
63 */
64 public final class Closing {
65
66 private Closing()
67 {
68 throw new IllegalStateException("Why on earth would you want to instantiate this?");
69 }
70
71
72 /**
73 * <p>
74 * Ultra-shorthand for {@link AutoCloseable}/{@link java.io.Closeable}, obvious use for {@link java.io.InputStream}
75 * </p>
76 *
77 * @param create a function applying {@code t} to produce an {@link AutoCloseable} of type {@code <C>}
78 * @param t the argument to apply to the {@code create} function
79 * @param convert a function applied to the {@link AutoCloseable} to produce the result
80 *
81 * @param <C> {@link AutoCloseable} type
82 * @param <T> {@code create} function argument type
83 * @param <R> the result type
84 *
85 * @return the result of applying the {@code convert} function
86 */
87 public static <C extends AutoCloseable, T, R> R closeAfterApplying(CheckedFunction<T, C, ?> create, T t, CheckedFunction<C, R, ?> convert)
88 {
89 C closeable = Exceptional.apply(create, t);
90 return closeAfterApplying(closeable, convert);
91 }
92
93
94 /**
95 * Applies the function to the closeable, returning the result and closing the closable - checked exceptions are
96 * rethrown as unchecked.
97 *
98 * @param closeable the closeable subject of the {@code convert} function
99 * @param convert the function consuming the closeable and supplying the result
100 *
101 * @param <C> the auto-closeable type, to apply and close
102 * @param <R> the result type
103 *
104 * @return the result of applying {@code convert} function to the {@code closeable} argument
105 */
106 public static <C extends AutoCloseable, R> R closeAfterApplying(C closeable, CheckedFunction<C, R, ?> convert)
107 {
108 try(C autoClose = closeable) {
109 return Exceptional.apply(convert, autoClose);
110 } catch(Exception e) {
111 throw uncheck(e);
112 }
113 }
114
115
116 /**
117 * Applies the bi-function to the closeable, returning the result and closing the closable - checked exceptions are
118 * rethrown as unchecked.
119 *
120 * @param closeable the closeable subject of the {@code convert} bi-function
121 * @param instance the second argument for the bi-function
122 * @param convert the function consuming the closeable and supplying the result
123 *
124 * @param <C> the auto-closeable type, will be applied and closed
125 * @param <U> the type of second argument to bi-function apply
126 * @param <R> the result type
127 *
128 * @return the result of applying {@code convert} function to the {@code closeable} argument
129 */
130 public static <C extends AutoCloseable, U, R, E extends Throwable> R closeAfterApplying(C closeable, U instance, CheckedBiFunction<C, U, R, E> convert)
131 {
132 try(C autoClose = closeable) {
133 return Exceptional.apply(convert, autoClose, instance);
134 } catch(Exception e) {
135 throw uncheck(e);
136 }
137 }
138
139
140 /**
141 * Applies the {@code create} function to {@code t}, resulting in a {@link AutoCloseable} which is closed after
142 * being consumed.
143 * Checked exceptions are rethrown as unchecked.
144 *
145 * @param create the function creating the {@link AutoCloseable}
146 * @param t the argument that the {@code create} function is applied to
147 * @param consume the consumer of the {@link AutoCloseable}
148 *
149 * @param <C> the auto-closeable type, to be created, consumed and closed
150 * @param <T> the function's argument type, used to create the auto-closeable
151 */
152 public static <C extends AutoCloseable, T> void closeAfterAccepting(CheckedFunction<T, C, ?> create, T t, CheckedConsumer<C, ?> consume)
153 {
154 C closeable = Exceptional.apply(create, t);
155 closeAfterAccepting(closeable, consume);
156 }
157
158
159 /**
160 * Consumes the {@code closeable} before closing. Checked exceptions are rethrown as unchecked.
161 *
162 * @param closeable the closeable to be consumed and closed
163 * @param consume the consumer of the {@link AutoCloseable}
164 *
165 * @param <C> the auto-closeable type
166 */
167 public static <C extends AutoCloseable> void closeAfterAccepting(C closeable, CheckedConsumer<C, ?> consume)
168 {
169 try(C autoClose = closeable) {
170 Exceptional.accept(consume, autoClose);
171 } catch(Exception e) {
172 throw uncheck(e);
173 }
174 }
175
176
177 public static <C extends AutoCloseable, T, U> void closeAfterAccepting(CheckedFunction<T, C, ?> create, T t, U instance, CheckedBiConsumer<C, U, ?> consume)
178 {
179 C closeable = Exceptional.apply(create, t);
180 closeAfterAccepting(closeable, instance, consume);
181 }
182
183
184 /**
185 * Consumes both the {@code closeable} and {@code instance} before closing. Checked exceptions are rethrown as
186 * unchecked.
187 *
188 * @param closeable the closeable to be consumed and closed
189 * @param instance the instance to consume
190 * @param consume the consumer of the {@link AutoCloseable}
191 *
192 * @param <C> the auto-closeable type to consume
193 * @param <U> the type of consumer's second argument
194 */
195 public static <C extends AutoCloseable, U> void closeAfterAccepting(C closeable, U instance, CheckedBiConsumer<C, U, ?> consume)
196 {
197 try(C autoClose = closeable) {
198 Exceptional.accept(consume, autoClose, instance);
199 } catch(Exception e) {
200 throw uncheck(e);
201 }
202 }
203
204 public static class AutoClosed<T, E extends Exception> implements AutoCloseable, Supplier<T> {
205
206 private final T instance;
207 private final CheckedConsumer<T, E> closeMethod;
208
209
210 AutoClosed(T instance, CheckedConsumer<T, E> closeMethod)
211 {
212 this.instance = instance;
213 this.closeMethod = closeMethod;
214 }
215
216
217 @Override
218 public T get()
219 {
220 return instance;
221 }
222
223
224 @Override
225 public void close() throws E
226 {
227 closeMethod.accept(instance);
228 }
229 }
230
231
232 /**
233 * Reshape instances to fit try-catch autoclose.
234 *
235 * Example usage:
236 * <code>
237 * try(AutoClosed<VirtualMachine, IOException> vm = Closing.autoClosing(getCurrentVm(), VirtualMachine::detach)) {
238 * vm.get().loadAgent(x, y);
239 * }
240 * </code>
241 *
242 * @param instance
243 * @param closeMethod
244 * @return
245 *
246 * @since 0.5.0
247 */
248 public static <T, E extends Exception> AutoClosed<T, E> autoClosing(T instance, CheckedConsumer<T, E> closeMethod)
249 {
250 return new AutoClosed<>(instance, closeMethod);
251 }
252 }