001/* 002 * Java Genetic Algorithm Library (jenetics-7.2.0). 003 * Copyright (c) 2007-2023 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.io.Closeable; 026import java.io.IOException; 027import java.util.ArrayList; 028import java.util.Arrays; 029import java.util.Collection; 030import java.util.Collections; 031import java.util.List; 032import java.util.function.Function; 033import java.util.function.Supplier; 034 035/** 036 * Interfaces and classes for handling resource ({@link AutoCloseable}) objects. 037 * The common use cases are shown as follows: 038 * <p><b>Wrapping <em>non</em>-closeable values</b></p> 039 * <pre>{@code 040 * final Value<Path, IOException> file = new Value<>( 041 * Files.createFile(Path.of("some_file")), 042 * Files::deleteIfExists 043 * ); 044 * 045 * // Automatically delete the file after the test. 046 * try (file) { 047 * Files.write(file.get(), "foo".getBytes()); 048 * final var writtenText = Files.readString(file.get()); 049 * assert "foo".equals(writtenText); 050 * } 051 * }</pre> 052 * 053 * <p><b>Building complex closeable values</b></p> 054 * <pre>{@code 055 * final IOValue<Stream<Object>> result = new IOValue<>(resources -> { 056 * final var fin = resources.add(new FileInputStream(file.toFile())); 057 * final var bin = resources.add(new BufferedInputStream(fin)); 058 * final var oin = resources.add(new ObjectInputStream(bin)); 059 * 060 * return Stream.generate(() -> readNextObject(oin)) 061 * .takeWhile(Objects::nonNull); 062 * }); 063 * 064 * try (result) { 065 * result.get().forEach(System.out::println); 066 * } 067 * }</pre> 068 * 069 * <p><b>Wrapping several closeables into one</b></p> 070 * <pre>{@code 071 * try (var __ = ExtendedCloseable.of(c1, c2, c3)) { 072 * ... 073 * } 074 * }</pre> 075 * 076 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 077 * @since 6.2 078 * @version 7.2 079 */ 080public class Lifecycle { 081 082 /* ************************************************************************* 083 * Throwing functional interfaces. 084 * ************************************************************************/ 085 086 /** 087 * Runnable task/method, which might throw an exception {@code E}. 088 * 089 * @param <E> the exception which might be thrown 090 */ 091 @FunctionalInterface 092 public interface ThrowingRunnable<E extends Exception> { 093 094 /** 095 * Running the task. 096 * 097 * @throws E if an error occurs while running the task 098 */ 099 void run() throws E; 100 101 } 102 103 /** 104 * A method which takes an argument and can throw an exception. 105 * 106 * @param <A> the argument type 107 * @param <E> the exception type 108 */ 109 @FunctionalInterface 110 public interface ThrowingConsumer<A, E extends Exception> { 111 112 /** 113 * Performs this operation on the given argument. 114 * 115 * @param arg the input argument 116 * @throws E if an error occurs while executing the operation 117 */ 118 void accept(final A arg) throws E; 119 120 } 121 122 /** 123 * A function which takes an argument and can throw an exception. 124 * 125 * @param <A> the argument type 126 * @param <R> the return type 127 * @param <E> the exception type 128 */ 129 @FunctionalInterface 130 public interface ThrowingFunction<A, R, E extends Exception> { 131 132 /** 133 * Applies this function to the given argument. 134 * 135 * @param arg the function argument 136 * @return the function result 137 * @throws E if an error occurs while applying the function 138 */ 139 R apply(final A arg) throws E; 140 141 } 142 143 /* ************************************************************************* 144 * Lifecycle interfaces/classes. 145 * ************************************************************************/ 146 147 /** 148 * Extends the {@link AutoCloseable} with methods for wrapping the thrown 149 * exception into <em>unchecked</em> exceptions or ignoring them. 150 * 151 * @param <E> the exception thrown by the {@link #close()} method 152 */ 153 @FunctionalInterface 154 public interface ExtendedCloseable<E extends Exception> 155 extends AutoCloseable 156 { 157 158 @Override 159 void close() throws E; 160 161 /** 162 * Calls the {@link #close()} method and wraps thrown {@link Exception} 163 * into an {@link RuntimeException}, mapped by the given {@code mapper}. 164 * 165 * @throws RuntimeException if the {@link #close()} method throws 166 * an {@link Exception} 167 */ 168 default void uncheckedClose( 169 final Function< 170 ? super E, 171 ? extends RuntimeException> mapper 172 ) { 173 try { 174 close(); 175 } catch (Exception e) { 176 @SuppressWarnings("unchecked") 177 final var error = (E)e; 178 throw mapper.apply(error); 179 } 180 } 181 182 /** 183 * Calls the {@link #close()} method and ignores every thrown exception. 184 */ 185 default void silentClose() { 186 silentClose(null); 187 } 188 189 /** 190 * Calls the {@link #close()} method and ignores every thrown exception. 191 * If the given {@code previousError} is <em>non-null</em>, the thrown 192 * exception is appended to the list of suppressed exceptions. 193 * 194 * @param previousError the error, which triggers the close of the given 195 * {@code closeables} 196 */ 197 default void silentClose(final Throwable previousError) { 198 try { 199 close(); 200 } catch (Exception suppressed) { 201 if (previousError != null) { 202 previousError.addSuppressed(suppressed); 203 } 204 } 205 } 206 207 /** 208 * Wraps a given {@code release} method and returns an 209 * {@link ExtendedCloseable}. 210 * 211 * @param release the release method to wrap 212 * @return a new extended closeable with the given underlying 213 * {@code release} method 214 * @throws NullPointerException if the given {@code release} method is 215 * {@code null} 216 */ 217 static <E extends Exception> ExtendedCloseable<E> 218 of(final ThrowingRunnable<? extends E> release) { 219 return release::run; 220 } 221 222 /** 223 * Create a new {@code ExtendedCloseable} object with the given initial 224 * release <em>methods</em>>. The given list of objects is closed in 225 * reversed order. 226 * 227 * @see #of(ThrowingRunnable...) 228 * 229 * @param releases the initial release methods 230 * @return a new closeable object which collects the given 231 * {@code releases} 232 * @throws NullPointerException if one of the {@code releases} is 233 * {@code null} 234 */ 235 static <E extends Exception> ExtendedCloseable<E> 236 of(final Collection<? extends ThrowingRunnable<? extends E>> releases) { 237 final List<ThrowingRunnable<? extends E>> list = new ArrayList<>(); 238 releases.forEach(c -> list.add(requireNonNull(c))); 239 Collections.reverse(list); 240 241 return () -> Lifecycle.invokeAll(ThrowingRunnable::run, list); 242 } 243 244 /** 245 * Create a new {@code ExtendedCloseable} object with the given initial 246 * release <em>methods</em>>. The given list of objects is closed in 247 * reversed order. 248 * 249 * @see #of(Collection) 250 * 251 * @param releases the release methods 252 * @return a new closeable object which collects the given 253 * {@code releases} 254 * @throws NullPointerException if one of the {@code releases} is 255 * {@code null} 256 */ 257 @SafeVarargs 258 static <E extends Exception> ExtendedCloseable<E> 259 of(final ThrowingRunnable<? extends E>... releases) { 260 return of(Arrays.asList(releases)); 261 } 262 263 } 264 265 /** 266 * This class represents a <em>closeable</em> value. It is useful in cases 267 * where the object value doesn't implement the {@link AutoCloseable} 268 * interface but needs some cleanup work to do after usage. In the following 269 * example the created {@code file} is automatically deleted when leaving the 270 * {@code try} block. 271 * 272 * <pre>{@code 273 * // Create the closeable file. 274 * final Value<Path, IOException> file = new Value<>( 275 * Files.createFile(Path.of("some_file")), 276 * Files::deleteIfExists 277 * ); 278 * 279 * // Automatically delete the file after the test. 280 * try (file) { 281 * Files.write(file.get(), "foo".getBytes()); 282 * final var writtenText = Files.readString(file.get()); 283 * assert "foo".equals(writtenText); 284 * } 285 * }</pre> 286 * 287 * @param <T> the value type 288 */ 289 public static sealed class Value<T, E extends Exception> 290 implements Supplier<T>, ExtendedCloseable<E> 291 { 292 293 T _value; 294 ThrowingConsumer<? super T, ? extends E> _release; 295 296 private Value() { 297 } 298 299 /** 300 * Create a new closeable value with the given resource {@code value} 301 * and its {@code release} method. 302 * 303 * @param value the actual resource value 304 * @param release the {@code release} method for the given {@code value} 305 * @throws NullPointerException if the {@code release} function is 306 * {@code null} 307 */ 308 public Value( 309 final T value, 310 final ThrowingConsumer<? super T, ? extends E> release 311 ) { 312 _value = value; 313 _release = requireNonNull(release); 314 } 315 316 /** 317 * Opens a kind of {@code try-catch} with resources block. The difference 318 * is, that the resources, registered with the 319 * {@link Resources#add(Object, ThrowingConsumer)} method, are only closed 320 * in the case of an error. If the <em>value</em> could be created, the 321 * caller is responsible for closing the opened <em>resources</em> by 322 * calling the {@link Value#close()} method. 323 * 324 * <pre>{@code 325 * final Value<Stream<Object>, IOException> result = new Value<>(resources -> { 326 * final var fin = resources.add(new FileInputStream(file.toFile()), Closeable::close); 327 * final var bin = resources.add(new BufferedInputStream(fin), Closeable::close); 328 * final var oin = resources.add(new ObjectInputStream(bin), Closeable::close); 329 * 330 * return Stream.generate(() -> readNextObject(oin)) 331 * .takeWhile(Objects::nonNull); 332 * }); 333 * 334 * try (result) { 335 * result.get().forEach(System.out::println); 336 * } 337 * }</pre> 338 * 339 * @see Resources 340 * 341 * @param builder the builder method 342 * @param <BE> the exception type which might be thrown while building 343 * the value 344 * @throws BE in the case of an error. If this exception is thrown, all 345 * already <em>registered</em> resources are closed. 346 * @throws NullPointerException if the given {@code builder} is 347 * {@code null} 348 */ 349 public <BE extends Exception> Value( 350 final ThrowingFunction< 351 ? super Resources<E>, 352 ? extends T, 353 ? extends BE> builder 354 ) 355 throws BE 356 { 357 requireNonNull(builder); 358 359 final var resources = new Resources<E>(); 360 try { 361 _value = builder.apply(resources); 362 _release = value -> resources.close(); 363 } catch (Throwable error) { 364 resources.silentClose(error); 365 throw error; 366 } 367 } 368 369 @Override 370 public T get() { 371 return _value; 372 } 373 374 @Override 375 public void close() throws E { 376 _release.accept(get()); 377 } 378 379 @Override 380 public String toString() { 381 return format("Value[%s]", get()); 382 } 383 384 /** 385 * Applies the give {@code block} to the already created closeable value. 386 * If the {@code block} throws an exception, the resource value is 387 * released, by calling the defined <em>release</em> method. The typical 388 * use case for this method is when additional initialization of the 389 * value is needed. 390 * 391 * <pre>{@code 392 * final var file = CloseableValue.of( 393 * Files.createFile(Path.of("some_file")), 394 * Files::deleteIfExists 395 * ); 396 * // Trying to do additional setup, e.g. setting the 'delete-on-exit' 397 * // flag. 398 * file.trying(f -> f.toFile().deleteOnExit()); 399 * 400 * try (file) { 401 * // Do something with temp file. 402 * } 403 * }</pre> 404 * 405 * @param block the codec block which is applied to the value 406 * @param releases additional release methods, which are called in the 407 * case of an error 408 * @param <E> the thrown exception type 409 * @throws E if applying the {@code block} throws an exception 410 */ 411 @SafeVarargs 412 public final <E extends Exception> void trying( 413 final ThrowingConsumer<? super T, ? extends E> block, 414 final ThrowingRunnable<? extends E>... releases 415 ) 416 throws E 417 { 418 try { 419 block.accept(get()); 420 } catch (Throwable error) { 421 ExtendedCloseable.of(releases).silentClose(error); 422 silentClose(error); 423 throw error; 424 } 425 } 426 427 } 428 429 /** 430 * This class represents a <em>closeable</em> value. It is useful in cases 431 * where the object value doesn't implement the {@link AutoCloseable} 432 * interface but needs some cleanup work to do after usage. In the following 433 * example the created {@code file} is automatically deleted when leaving the 434 * {@code try} block. 435 * 436 * <pre>{@code 437 * // Create the closeable file. 438 * final IOValue<Path> file = new IOValue<>( 439 * Files.createFile(Path.of("some_file")), 440 * Files::deleteIfExists 441 * ); 442 * 443 * // Automatically delete the file after the test. 444 * try (file) { 445 * Files.write(file.get(), "foo".getBytes()); 446 * final var writtenText = Files.readString(file.get()); 447 * assert "foo".equals(writtenText); 448 * } 449 * }</pre> 450 * 451 * @param <T> the value type 452 */ 453 public static final class IOValue<T> 454 extends Value<T, IOException> 455 implements Closeable 456 { 457 458 /** 459 * Create a new closeable value with the given resource {@code value} 460 * and its {@code release} method. 461 * 462 * @param value the actual resource value 463 * @param release the {@code release} method for the given {@code value} 464 * @throws NullPointerException if the {@code release} function is 465 * {@code null} 466 */ 467 public IOValue( 468 final T value, 469 final ThrowingConsumer<? super T, ? extends IOException> release 470 ) { 471 super(value, release); 472 } 473 474 /** 475 * Opens a kind of {@code try-catch} with resources block. The difference 476 * is, that the resources, registered with the 477 * {@link Resources#add(Object, ThrowingConsumer)} method, are only closed 478 * in the case of an error. If the <em>value</em> could be created, the 479 * caller is responsible for closing the opened <em>resources</em> by 480 * calling the {@link Value#close()} method. 481 * 482 * <pre>{@code 483 * final IOValue<Stream<Object>> result = new IOValue<>(resources -> { 484 * final var fin = resources.add(new FileInputStream(file.toFile())); 485 * final var bin = resources.add(new BufferedInputStream(fin)); 486 * final var oin = resources.add(new ObjectInputStream(bin)); 487 * 488 * return Stream.generate(() -> readNextObject(oin)) 489 * .takeWhile(Objects::nonNull); 490 * }); 491 * 492 * try (result) { 493 * result.get().forEach(System.out::println); 494 * } 495 * }</pre> 496 * 497 * @see Resources 498 * 499 * @param builder the builder method 500 * @param <BE> the exception type which might be thrown while building 501 * the value 502 * @throws BE in the case of an error. If this exception is thrown, all 503 * already <em>registered</em> resources are closed. 504 * @throws NullPointerException if the given {@code builder} is 505 * {@code null} 506 */ 507 public <BE extends Exception> IOValue( 508 final ThrowingFunction< 509 ? super IOResources, 510 ? extends T, 511 ? extends BE> builder 512 ) 513 throws BE 514 { 515 requireNonNull(builder); 516 517 final var resources = new IOResources(); 518 try { 519 _value = builder.apply(resources); 520 _release = value -> resources.close(); 521 } catch (Throwable error) { 522 resources.silentClose(error); 523 throw error; 524 } 525 } 526 527 } 528 529 /** 530 * This class allows collecting one or more {@link AutoCloseable} objects 531 * into one. The registered closeable objects are closed in reverse order. 532 * <p> 533 * Using the {@code Resources} class can simplify the creation of 534 * dependent input streams, where it might be otherwise necessary to create 535 * nested {@code try-with-resources} blocks. 536 * 537 * <pre>{@code 538 * try (var resources = new Resources<IOException>()) { 539 * final var fin = resources.add(new FileInputStream(file), Closeable::close); 540 * if (fin.read() != -1) { 541 * return; 542 * } 543 * final var oin = resources.add(new ObjectInputStream(fin), Closeable::close); 544 * // ... 545 * } 546 * }</pre> 547 */ 548 public static sealed class Resources<E extends Exception> 549 implements ExtendedCloseable<E> 550 { 551 552 private final List<ThrowingRunnable<? extends E>> _resources = new ArrayList<>(); 553 554 /** 555 * Create a new {@code Resources} object, initialized with the given 556 * resource <em>release</em> methods. 557 * 558 * @param releases the release methods 559 */ 560 public Resources( 561 final Collection<? extends ThrowingRunnable<? extends E>> releases 562 ) { 563 _resources.addAll(releases); 564 } 565 566 /** 567 * Create a new {@code Resources} object, initialized with the given 568 * resource <em>release</em> methods. 569 * 570 * @param releases the release methods 571 */ 572 @SafeVarargs 573 public Resources(final ThrowingRunnable<? extends E>... releases) { 574 this(Arrays.asList(releases)); 575 } 576 577 /** 578 * Create a new, empty {@code Resources} object. 579 */ 580 public Resources() { 581 } 582 583 /** 584 * Registers the given {@code resource} to the list of managed 585 * resources. 586 * 587 * @param resource the new resource to register 588 * @param release the method, which <em>releases</em> the acquired 589 * resource 590 * @param <C> the resource type 591 * @return the registered resource 592 * @throws NullPointerException if one of the given arguments is 593 * {@code null} 594 */ 595 public <C> C add( 596 final C resource, 597 final ThrowingConsumer<? super C, ? extends E> release 598 ) { 599 requireNonNull(resource); 600 requireNonNull(release); 601 602 _resources.add(() -> release.accept(resource)); 603 return resource; 604 } 605 606 /** 607 * Registers the given {@code resource} to the list of managed 608 * resources. 609 * 610 * @param resource the new resource to register 611 * @param <C> the resource type 612 * @return the registered resource 613 * @throws NullPointerException if one of the given arguments is 614 * {@code null} 615 */ 616 public <C extends ExtendedCloseable<? extends E>> C add(final C resource) { 617 return add(resource, C::close); 618 } 619 620 @Override 621 public void close() throws E { 622 if (!_resources.isEmpty()) { 623 ExtendedCloseable.of(_resources).close(); 624 } 625 } 626 627 } 628 629 /** 630 * This class allows collecting one or more {@link AutoCloseable} objects 631 * into one. The registered closeable objects are closed in reverse order. 632 * <p> 633 * Using the {@code IOResources} class can simplify the creation of 634 * dependent input streams, where it might be otherwise necessary to create 635 * nested {@code try-with-resources} blocks. 636 * 637 * <pre>{@code 638 * try (var resources = new IOResources()) { 639 * final var fin = resources.add(new FileInputStream(file)); 640 * if (fin.read() != -1) { 641 * return; 642 * } 643 * final var oin = resources.add(new ObjectInputStream(fin)); 644 * // ... 645 * } 646 * }</pre> 647 */ 648 public static final class IOResources extends Resources<IOException> { 649 650 /** 651 * Create a new {@code IOResources} object, initialized with the given 652 * resource <em>release</em> methods. 653 * 654 * @param releases the release methods 655 */ 656 public IOResources( 657 final Collection<? extends ThrowingRunnable<? extends IOException>> releases 658 ) { 659 super(releases); 660 } 661 662 /** 663 * Create a new {@code IOResources} object, initialized with the given 664 * resource <em>release</em> methods. 665 * 666 * @param releases the release methods 667 */ 668 @SafeVarargs 669 public IOResources( 670 final ThrowingRunnable<? extends IOException>... releases 671 ) { 672 super(releases); 673 } 674 675 /** 676 * Create a new, empty {@code IOResources} object. 677 */ 678 public IOResources() { 679 } 680 681 /** 682 * Registers the given {@code resource} to the list of managed 683 * resources. 684 * 685 * @param resource the new resource to register 686 * @param <C> the resource type 687 * @return the registered resource 688 * @throws NullPointerException if one of the given arguments is 689 * {@code null} 690 */ 691 public <C extends Closeable> C add(final C resource) { 692 return add(resource, Closeable::close); 693 } 694 695 } 696 697 /* ************************************************************************* 698 * Helper methods. 699 * ************************************************************************/ 700 701 private Lifecycle() { 702 } 703 704 /** 705 * Invokes the {@code method} on all given {@code objects}, no matter if one 706 * of the method invocations throws an exception. The first exception thrown 707 * is rethrown after invoking the method on the remaining objects, all other 708 * exceptions are swallowed. 709 * 710 * <pre>{@code 711 * final var streams = new ArrayList<InputStream>(); 712 * streams.add(new FileInputStream(file1)); 713 * streams.add(new FileInputStream(file2)); 714 * streams.add(new FileInputStream(file3)); 715 * // ... 716 * invokeAll(Closeable::close, streams); 717 * }</pre> 718 * 719 * @param <A> the closeable object type 720 * @param <E> the exception type 721 * @param objects the objects where the methods are called. 722 * @param method the method which is called on the given object. 723 * @throws E the first exception thrown by the one of the method 724 * invocation. 725 */ 726 static <A, E extends Exception> void invokeAll( 727 final ThrowingConsumer<? super A, ? extends E> method, 728 final Iterable<? extends A> objects 729 ) 730 throws E 731 { 732 raise(invokeAll0(method, objects)); 733 } 734 735 private static <E extends Exception> void raise(final Throwable error) 736 throws E 737 { 738 if (error instanceof RuntimeException e) { 739 throw e; 740 } else if (error instanceof Error e) { 741 throw e; 742 } else if (error != null) { 743 @SuppressWarnings("unchecked") 744 final var e = (E)error; 745 throw e; 746 } 747 } 748 749 private static final int MAX_SUPPRESSED = 5; 750 751 /** 752 * Invokes the {@code method} on all given {@code objects}, no matter if one 753 * of the method invocations throws an exception. The first exception thrown 754 * is returned, all other exceptions are swallowed. 755 * 756 * @param objects the objects where the methods are called. 757 * @param method the method which is called on the given object. 758 * @return the first exception thrown by the method invocation or {@code null} 759 * if no exception has been thrown 760 */ 761 static <A, E extends Exception> Throwable invokeAll0( 762 final ThrowingConsumer<? super A, ? extends E> method, 763 final Iterable<? extends A> objects 764 ) { 765 int suppressedCount = 0; 766 Throwable error = null; 767 for (var object : objects) { 768 if (error != null) { 769 try { 770 method.accept(object); 771 } catch (Exception suppressed) { 772 if (suppressedCount++ < MAX_SUPPRESSED) { 773 error.addSuppressed(suppressed); 774 } 775 } 776 } else { 777 try { 778 method.accept(object); 779 } catch (VirtualMachineError|LinkageError e) { 780 throw e; 781 } catch (Throwable e) { 782 error = e; 783 } 784 } 785 } 786 787 return error; 788 } 789 790}