001/* 002 * Java Genetic Algorithm Library (jenetics-7.1.0). 003 * Copyright (c) 2007-2022 Franz Wilhelmstötter 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 * 017 * Author: 018 * Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com) 019 */ 020package io.jenetics.internal.util; 021 022import static java.lang.String.format; 023import static java.util.Objects.requireNonNull; 024 025import java.util.ArrayList; 026import java.util.Arrays; 027import java.util.Collection; 028import java.util.Collections; 029import java.util.List; 030import java.util.function.Function; 031import java.util.function.Supplier; 032 033/** 034 * Interfaces and classes for handling resource ({@link AutoCloseable}) objects. 035 * The common use cases are shown as follows: 036 * <p><b>Wrapping <em>non</em>-closeable values</b></p> 037 * <pre>{@code 038 * final Value<Path, IOException> file = Value.of( 039 * Files.createFile(Path.of("some_file")), 040 * Files::deleteIfExists 041 * ); 042 * 043 * // Automatically delete the file after the test. 044 * try (file) { 045 * Files.write(file.get(), "foo".getBytes()); 046 * final var writtenText = Files.readString(file.get()); 047 * assert "foo".equals(writtenText); 048 * } 049 * }</pre> 050 * 051 * <p><b>Building complex closeable values</b></p> 052 * <pre>{@code 053 * final Value<Stream<Object>, IOException> result = Value.build(resources -> { 054 * final var fin = resources.add(new FileInputStream(file.toFile()), Closeable::close); 055 * final var bin = resources.add(new BufferedInputStream(fin), Closeable::close); 056 * final var oin = resources.add(new ObjectInputStream(bin), Closeable::close); 057 * 058 * return Stream.generate(() -> readNextObject(oin)) 059 * .takeWhile(Objects::nonNull); 060 * }); 061 * 062 * try (result) { 063 * result.get().forEach(System.out::println); 064 * } 065 * }</pre> 066 * 067 * <p><b>Wrapping several closeables into one</b></p> 068 * <pre>{@code 069 * try (var __ = ExtendedCloseable.of(c1, c2, c3)) { 070 * ... 071 * } 072 * }</pre> 073 * 074 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 075 * @since 6.2 076 * @version 6.3 077 */ 078public class Lifecycle { 079 080 /* ************************************************************************* 081 * General interfaces. 082 * ************************************************************************/ 083 084 /** 085 * Runnable task/method, which might throw an exception {@code E}. 086 * 087 * @param <E> the exception which might be thrown 088 */ 089 @FunctionalInterface 090 public interface ThrowingRunnable<E extends Exception> { 091 092 /** 093 * Running the task. 094 * 095 * @throws E if an error occurs while running the task 096 */ 097 void run() throws E; 098 099 } 100 101 /** 102 * A method which takes an argument and can throw an exception. 103 * 104 * @param <A> the argument type 105 * @param <E> the exception type 106 */ 107 @FunctionalInterface 108 public interface ThrowingConsumer<A, E extends Exception> { 109 110 /** 111 * Performs this operation on the given argument. 112 * 113 * @param arg the input argument 114 * @throws E if an error occurs while executing the operation 115 */ 116 void accept(final A arg) throws E; 117 118 } 119 120 /** 121 * A function which takes an argument and can throw an exception. 122 * 123 * @param <A> the argument type 124 * @param <R> the return type 125 * @param <E> the exception type 126 */ 127 @FunctionalInterface 128 public interface ThrowingFunction<A, R, E extends Exception> { 129 130 /** 131 * Applies this function to the given argument. 132 * 133 * @param arg the function argument 134 * @return the function result 135 * @throws E if an error occurs while applying the function 136 */ 137 R apply(final A arg) throws E; 138 139 } 140 141 /* ************************************************************************* 142 * Lifecycle interfaces/classes. 143 * ************************************************************************/ 144 145 /** 146 * Extends the {@link AutoCloseable} with methods for wrapping the thrown 147 * exception into <em>unchecked</em> exceptions or ignoring them. 148 * 149 * @param <E> the exception thrown by the {@link #close()} method 150 */ 151 @FunctionalInterface 152 public interface ExtendedCloseable<E extends Exception> 153 extends AutoCloseable 154 { 155 156 @Override 157 void close() throws E; 158 159 /** 160 * Calls the {@link #close()} method and wraps thrown {@link Exception} 161 * into an {@link RuntimeException}, mapped by the given {@code mapper}. 162 * 163 * @throws RuntimeException if the {@link #close()} method throws 164 * an {@link Exception} 165 */ 166 default void uncheckedClose( 167 final Function< 168 ? super E, 169 ? extends RuntimeException> mapper 170 ) { 171 try { 172 close(); 173 } catch (Exception e) { 174 @SuppressWarnings("unchecked") 175 final var error = (E)e; 176 throw mapper.apply(error); 177 } 178 } 179 180 /** 181 * Calls the {@link #close()} method and ignores every thrown exception. 182 */ 183 default void silentClose() { 184 silentClose(null); 185 } 186 187 /** 188 * Calls the {@link #close()} method and ignores every thrown exception. 189 * If the given {@code previousError} is <em>non-null</em>, the thrown 190 * exception is appended to the list of suppressed exceptions. 191 * 192 * @param previousError the error, which triggers the close of the given 193 * {@code closeables} 194 */ 195 default void silentClose(final Throwable previousError) { 196 try { 197 close(); 198 } catch (Exception suppressed) { 199 if (previousError != null) { 200 previousError.addSuppressed(suppressed); 201 } 202 } 203 } 204 205 /** 206 * Wraps a given {@code release} method and returns an 207 * {@link ExtendedCloseable}. 208 * 209 * @param release the release method to wrap 210 * @return a new extended closeable with the given underlying 211 * {@code release} method 212 * @throws NullPointerException if the given {@code release} method is 213 * {@code null} 214 */ 215 static <E extends Exception> ExtendedCloseable<E> 216 of(final ThrowingRunnable<? extends E> release) { 217 return release::run; 218 } 219 220 /** 221 * Create a new {@code ExtendedCloseable} object with the given initial 222 * release <em>methods</em>>. The given list of objects are closed in 223 * reversed order. 224 * 225 * @see #of(ThrowingRunnable...) 226 * 227 * @param releases the initial release methods 228 * @return a new closeable object which collects the given 229 * {@code releases} 230 * @throws NullPointerException if one of the {@code releases} is 231 * {@code null} 232 */ 233 static <E extends Exception> ExtendedCloseable<E> 234 of(final Collection<? extends ThrowingRunnable<? extends E>> releases) { 235 final List<ThrowingRunnable<? extends E>> list = new ArrayList<>(); 236 releases.forEach(c -> list.add(requireNonNull(c))); 237 Collections.reverse(list); 238 239 return () -> Lifecycle.invokeAll(ThrowingRunnable::run, list); 240 } 241 242 /** 243 * Create a new {@code ExtendedCloseable} object with the given initial 244 * release <em>methods</em>>. The given list of objects are closed in 245 * reversed order. 246 * 247 * @see #of(Collection) 248 * 249 * @param releases the release methods 250 * @return a new closeable object which collects the given 251 * {@code releases} 252 * @throws NullPointerException if one of the {@code releases} is 253 * {@code null} 254 */ 255 @SafeVarargs 256 static <E extends Exception> ExtendedCloseable<E> 257 of(final ThrowingRunnable<? extends E>... releases) { 258 return of(Arrays.asList(releases)); 259 } 260 261 } 262 263 /** 264 * This class represents a <em>closeable</em> value. It is useful in cases 265 * where the object value doesn't implement the {@link AutoCloseable} 266 * interface but needs some cleanup work to do after usage. In the following 267 * example the created {@code file} is automatically deleted when leaving the 268 * {@code try} block. 269 * 270 * <pre>{@code 271 * // Create the closeable file. 272 * final Value<Path, IOException> file = Value.of( 273 * Files.createFile(Path.of("some_file")), 274 * Files::deleteIfExists 275 * ); 276 * 277 * // Automatically delete the file after the test. 278 * try (file) { 279 * Files.write(file.get(), "foo".getBytes()); 280 * final var writtenText = Files.readString(file.get()); 281 * assert "foo".equals(writtenText); 282 * } 283 * }</pre> 284 * 285 * @see #of(Object, ThrowingConsumer) 286 * @see #build(ThrowingFunction) 287 * 288 * @param <T> the value type 289 */ 290 public static final class Value<T, E extends Exception> 291 implements Supplier<T>, ExtendedCloseable<E> 292 { 293 294 private final T _value; 295 private final ThrowingConsumer<? super T, ? extends E> _release; 296 297 private Value( 298 final T value, 299 final ThrowingConsumer<? super T, ? extends E> release 300 ) { 301 _value = requireNonNull(value); 302 _release = requireNonNull(release); 303 } 304 305 @Override 306 public T get() { 307 return _value; 308 } 309 310 @Override 311 public void close() throws E { 312 _release.accept(get()); 313 } 314 315 @Override 316 public String toString() { 317 return format("Value[%s]", get()); 318 } 319 320 /** 321 * Applies the give {@code block} to the already created closeable value. 322 * If the {@code block} throws an exception, the resource value is 323 * released, by calling the defined <em>release</em> method. The typical 324 * use case for this method is when additional initialization of the 325 * value is needed. 326 * 327 * <pre>{@code 328 * final var file = CloseableValue.of( 329 * Files.createFile(Path.of("some_file")), 330 * Files::deleteIfExists 331 * ); 332 * // Trying to do additional setup, e.g. setting the 'delete-on-exit' 333 * // flag. 334 * file.trying(f -> f.toFile().deleteOnExit()); 335 * 336 * try (file) { 337 * // Do something with temp file. 338 * } 339 * }</pre> 340 * 341 * @param block the codec block which is applied to the value 342 * @param releases additional release methods, which are called in the 343 * case of an error 344 * @param <E> the thrown exception type 345 * @throws E if applying the {@code block} throws an exception 346 */ 347 @SafeVarargs 348 public final <E extends Exception> void trying( 349 final ThrowingConsumer<? super T, ? extends E> block, 350 final ThrowingRunnable<? extends E>... releases 351 ) 352 throws E 353 { 354 try { 355 block.accept(get()); 356 } catch (Throwable error) { 357 ExtendedCloseable.of(releases).silentClose(error); 358 silentClose(error); 359 throw error; 360 } 361 } 362 363 /** 364 * Create a new closeable value with the given resource {@code value} 365 * and its {@code release} method. 366 * 367 * @param value the actual resource value 368 * @param release the {@code release} method for the given {@code value} 369 * @param <T> the value type 370 * @return a new closeable value 371 * @throws NullPointerException if one of the arguments is {@code null} 372 */ 373 public static <T, E extends Exception> Value<T, E> of( 374 final T value, 375 final ThrowingConsumer<? super T, ? extends E> release 376 ) { 377 return new Value<>(value,release); 378 } 379 380 /** 381 * Opens a kind of {@code try-catch} with resources block. The difference 382 * is, that the resources, registered with the 383 * {@link Resources#add(Object, ThrowingConsumer)} method, are only closed 384 * in the case of an error. If the <em>value</em> could be created, the 385 * caller is responsible for closing the opened <em>resources</em> by 386 * calling the {@link Value#close()} method. 387 * 388 * <pre>{@code 389 * final Value<Stream<Object>, IOException> result = Value.build(resources -> { 390 * final var fin = resources.add(new FileInputStream(file.toFile()), Closeable::close); 391 * final var bin = resources.add(new BufferedInputStream(fin), Closeable::close); 392 * final var oin = resources.add(new ObjectInputStream(bin), Closeable::close); 393 * 394 * return Stream.generate(() -> readNextObject(oin)) 395 * .takeWhile(Objects::nonNull); 396 * }); 397 * 398 * try (result) { 399 * result.get().forEach(System.out::println); 400 * } 401 * }</pre> 402 * 403 * @see Resources 404 * 405 * @param builder the builder method 406 * @param <T> the value type of the created <em>closeable</em> value 407 * @param <BE> the exception type which might be thrown while building 408 * the value 409 * @param <VE> the exception type which might be thrown when releasing 410 * the returned <em>closeable</em> value 411 * @return the built closeable value 412 * @throws BE in the case of an error. If this exception is thrown, all 413 * already <em>registered</em> resources are closed. 414 * @throws NullPointerException if the given {@code builder} is 415 * {@code null} 416 */ 417 public static <T, BE extends Exception, VE extends Exception> Value<T, VE> 418 build( 419 final ThrowingFunction< 420 ? super Resources<VE>, 421 ? extends T, 422 ? extends BE> builder 423 ) 424 throws BE 425 { 426 requireNonNull(builder); 427 428 final var resources = new Resources<VE>(); 429 try { 430 return Value.of( 431 builder.apply(resources), 432 value -> resources.close() 433 ); 434 } catch (Throwable error) { 435 resources.silentClose(error); 436 throw error; 437 } 438 } 439 440 } 441 442 /** 443 * This class allows to collect one or more {@link AutoCloseable} objects 444 * into one. The registered closeable objects are closed in reverse order. 445 * <p> 446 * Using the {@code Resources} class can simplify the the creation of 447 * dependent input streams, where it might be otherwise necessary to create 448 * nested {@code try-with-resources} blocks. 449 * 450 * <pre>{@code 451 * try (var resources = new Resources<IOException>()) { 452 * final var fin = resources.add(new FileInputStream(file), Closeable::close); 453 * if (fin.read() != -1) { 454 * return; 455 * } 456 * final var oin = resources.add(new ObjectInputStream(fin), Closeable::close); 457 * // ... 458 * } 459 * }</pre> 460 * 461 * @see Value#build(ThrowingFunction) 462 */ 463 public static final class Resources<E extends Exception> 464 implements ExtendedCloseable<E> 465 { 466 467 private final List<ThrowingRunnable<? extends E>> _resources = new ArrayList<>(); 468 469 /** 470 * Create a new {@code Resources} object, initialized with the given 471 * resource <em>release</em> methods. 472 * 473 * @param releases the release methods 474 */ 475 public Resources( 476 final Collection<? extends ThrowingRunnable<? extends E>> releases 477 ) { 478 _resources.addAll(releases); 479 } 480 481 /** 482 * Create a new {@code Resources} object, initialized with the given 483 * resource <em>release</em> methods. 484 * 485 * @param releases the release methods 486 */ 487 @SafeVarargs 488 public Resources(final ThrowingRunnable<? extends E>... releases) { 489 this(Arrays.asList(releases)); 490 } 491 492 /** 493 * Create a new, empty {@code Resources} object. 494 */ 495 public Resources() { 496 } 497 498 /** 499 * Registers the given {@code resource} to the list of managed 500 * resources. 501 * 502 * @param resource the new resource to register 503 * @param release the method, which <em>releases</em> the the acquired 504 * resource 505 * @param <C> the resource type 506 * @return the registered resource 507 * @throws NullPointerException if one of the given arguments is 508 * {@code null} 509 */ 510 public <C> C add( 511 final C resource, 512 final ThrowingConsumer<? super C, ? extends E> release 513 ) { 514 requireNonNull(resource); 515 requireNonNull(release); 516 517 _resources.add(() -> release.accept(resource)); 518 return resource; 519 } 520 521 @Override 522 public void close() throws E { 523 if (!_resources.isEmpty()) { 524 ExtendedCloseable.of(_resources).close(); 525 } 526 } 527 528 } 529 530 /* ************************************************************************* 531 * Helper methods. 532 * ************************************************************************/ 533 534 private Lifecycle() { 535 } 536 537 /** 538 * Invokes the {@code method} on all given {@code objects}, no matter if one 539 * of the method invocations throws an exception. The first exception thrown 540 * is rethrown after invoking the method on the remaining objects, all other 541 * exceptions are swallowed. 542 * 543 * <pre>{@code 544 * final var streams = new ArrayList<InputStream>(); 545 * streams.add(new FileInputStream(file1)); 546 * streams.add(new FileInputStream(file2)); 547 * streams.add(new FileInputStream(file3)); 548 * // ... 549 * invokeAll(Closeable::close, streams); 550 * }</pre> 551 * 552 * @param <A> the closeable object type 553 * @param <E> the exception type 554 * @param objects the objects where the methods are called. 555 * @param method the method which is called on the given object. 556 * @throws E the first exception thrown by the one of the method 557 * invocation. 558 */ 559 static <A, E extends Exception> void invokeAll( 560 final ThrowingConsumer<? super A, ? extends E> method, 561 final Collection<? extends A> objects 562 ) 563 throws E 564 { 565 raise(invokeAll0(method, objects)); 566 } 567 568 private static <E extends Exception> void raise(final Throwable error) 569 throws E 570 { 571 if (error instanceof RuntimeException e) { 572 throw e; 573 } else if (error instanceof Error e) { 574 throw e; 575 } else if (error != null) { 576 @SuppressWarnings("unchecked") 577 final var e = (E)error; 578 throw e; 579 } 580 } 581 582 private static final int MAX_SUPPRESSED = 5; 583 584 /** 585 * Invokes the {@code method}> on all given {@code objects}, no matter if one 586 * of the method invocations throws an exception. The first exception thrown 587 * is returned, all other exceptions are swallowed. 588 * 589 * @param objects the objects where the methods are called. 590 * @param method the method which is called on the given object. 591 * @return the first exception thrown by the method invocation or {@code null} 592 * if no exception has been thrown 593 */ 594 static <A, E extends Exception> Throwable invokeAll0( 595 final ThrowingConsumer<? super A, ? extends E> method, 596 final Collection<? extends A> objects 597 ) { 598 int suppressedCount = 0; 599 Throwable error = null; 600 for (var object : objects) { 601 if (error != null) { 602 try { 603 method.accept(object); 604 } catch (Exception suppressed) { 605 if (suppressedCount++ < MAX_SUPPRESSED) { 606 error.addSuppressed(suppressed); 607 } 608 } 609 } else { 610 try { 611 method.accept(object); 612 } catch (VirtualMachineError|ThreadDeath|LinkageError e) { 613 throw e; 614 } catch (Throwable e) { 615 error = e; 616 } 617 } 618 } 619 620 return error; 621 } 622 623}