View Javadoc
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   * &nbsp;&nbsp;&nbsp;<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 }