001/*
002 * Java Genetic Algorithm Library (jenetics-8.0.0).
003 * Copyright (c) 2007-2024 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 * {@snippet lang="java":
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 * }
052 *
053 * <p><b>Building complex closeable values</b></p>
054 * {@snippet lang="java":
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 * }
068 *
069 * <p><b>Wrapping several closeables into one</b></p>
070 * {@snippet lang="java":
071 * try (var __ = ExtendedCloseable.of(c1, c2, c3)) {
072 *     // ...
073 * }
074 * }
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         * {@snippet lang="java":
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         * }
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                 * {@snippet lang="java":
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                 * }
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                 * {@snippet lang="java":
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                 * }
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         * {@snippet lang="java":
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         * }
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                 * {@snippet lang="java":
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                 * }
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         * {@snippet lang="java":
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         * }
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         * {@snippet lang="java":
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         * }
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         * {@snippet lang="java":
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         * }
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                switch (error) {
739                        case null -> {}
740                        case RuntimeException e -> throw e;
741                        case Error e -> throw e;
742                        default ->  {
743                                @SuppressWarnings("unchecked")
744                                final var e = (E)error;
745                                throw e;
746                        }
747                }
748        }
749
750        private static final int MAX_SUPPRESSED = 5;
751
752        /**
753         * Invokes the {@code method} on all given {@code objects}, no matter if one
754         * of the method invocations throws an exception. The first exception thrown
755         * is returned, all other exceptions are swallowed.
756         *
757         * @param objects the objects where the methods are called.
758         * @param method the method which is called on the given object.
759         * @return the first exception thrown by the method invocation or {@code null}
760         *         if no exception has been thrown
761         */
762        static <A, E extends Exception> Throwable invokeAll0(
763                final ThrowingConsumer<? super A, ? extends E> method,
764                final Iterable<? extends A> objects
765        ) {
766                int suppressedCount = 0;
767                Throwable error = null;
768                for (var object : objects) {
769                        if (error != null) {
770                                try {
771                                        method.accept(object);
772                                } catch (Exception suppressed) {
773                                        if (suppressedCount++ < MAX_SUPPRESSED) {
774                                                error.addSuppressed(suppressed);
775                                        }
776                                }
777                        } else {
778                                try {
779                                        method.accept(object);
780                                } catch (VirtualMachineError|LinkageError e) {
781                                        throw e;
782                                } catch (Throwable e) {
783                                        error = e;
784                                }
785                        }
786                }
787
788                return error;
789        }
790
791}