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}