001/*
002 * Copyright 2022-2026 Revetware LLC.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.soklet;
018
019import org.jspecify.annotations.NonNull;
020import org.jspecify.annotations.Nullable;
021
022import javax.annotation.concurrent.ThreadSafe;
023import java.util.List;
024import java.util.Optional;
025
026import static java.util.Objects.requireNonNull;
027
028/** Immutable terminal evidence for one framework-managed shutdown component. */
029@ThreadSafe
030public final class ShutdownComponentResult {
031        @NonNull
032        private final ShutdownComponentType shutdownComponentType;
033        @NonNull
034        private final ShutdownComponentDisposition
035                        shutdownComponentDisposition;
036        @NonNull
037        private final List<@NonNull Throwable> throwables;
038        @Nullable
039        private final ResidualActivityEvidence residualActivityEvidence;
040
041        ShutdownComponentResult(
042                        @NonNull ShutdownComponentType shutdownComponentType,
043                        @NonNull ShutdownComponentDisposition
044                                        shutdownComponentDisposition,
045                        @NonNull List<? extends @NonNull Throwable> throwables,
046                        @Nullable ResidualActivityEvidence residualActivityEvidence) {
047                this.shutdownComponentType = requireNonNull(shutdownComponentType);
048                this.shutdownComponentDisposition = requireNonNull(
049                                shutdownComponentDisposition);
050                this.throwables = List.copyOf(requireNonNull(throwables));
051                this.residualActivityEvidence = residualActivityEvidence;
052        }
053
054        /** @return shutdown component type */
055        @NonNull
056        public ShutdownComponentType getShutdownComponentType() {
057                return this.shutdownComponentType;
058        }
059
060        /** @return shutdown component disposition */
061        @NonNull
062        public ShutdownComponentDisposition
063        getShutdownComponentDisposition() {
064                return this.shutdownComponentDisposition;
065        }
066
067        /**
068         * Returns throwables in deterministic observation order. Throwable instances
069         * retain their exact identities and may contain application-sensitive data.
070         * Competing failures retained by the lifecycle coordinator are separate entries
071         * rather than suppressed exceptions added to the primary failure.
072         *
073         * @return immutable throwable list
074         */
075        @NonNull
076        public List<@NonNull Throwable> getThrowables() {
077                return this.throwables;
078        }
079
080        /** @return residual-activity evidence, otherwise empty */
081        @NonNull
082        public Optional<@NonNull ResidualActivityEvidence>
083        getResidualActivityEvidence() {
084                return Optional.ofNullable(this.residualActivityEvidence);
085        }
086
087        /**
088         * Returns a bounded, single-line diagnostic with the component type,
089         * disposition, Throwable count and residual-activity categories. Does not
090         * invoke Throwable methods or render residual-summary text.
091         *
092         * @return compact component diagnostic; not a serialization format
093         */
094        @Override
095        @NonNull
096        public String toString() {
097                return "ShutdownComponentResult{shutdownComponentType=" + this.shutdownComponentType.name()
098                                + ", shutdownComponentDisposition=" + this.shutdownComponentDisposition.name()
099                                + ", throwableCount=" + this.throwables.size()
100                                + ", residualActivityTypes=" + (this.residualActivityEvidence == null ? "[]"
101                                                : this.residualActivityEvidence.getResidualActivityTypes()) + '}';
102        }
103}