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}