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; 020 021import javax.annotation.concurrent.NotThreadSafe; 022import java.time.Duration; 023 024import static java.util.Objects.requireNonNull; 025 026/** Indicates that core shutdown completed but bounded application cleanup did not. */ 027@NotThreadSafe 028public final class SokletShutdownCleanupException 029 extends SokletLifecycleException { 030 @NonNull 031 private final ShutdownCleanupFailureReason shutdownCleanupFailureReason; 032 @NonNull 033 private final Duration shutdownCleanupTimeout; 034 035 SokletShutdownCleanupException( 036 @NonNull ShutdownCleanupFailureReason shutdownCleanupFailureReason, 037 @NonNull Duration shutdownCleanupTimeout, 038 @NonNull ShutdownResult shutdownResult, 039 @NonNull Throwable cause) { 040 super(cleanupFailureMessage(requireNonNull(shutdownCleanupFailureReason)), 041 requireNonNull(shutdownResult), requireNonNull(cause)); 042 this.shutdownCleanupFailureReason = shutdownCleanupFailureReason; 043 this.shutdownCleanupTimeout = requireNonNull(shutdownCleanupTimeout); 044 if (!shutdownResult.isComplete()) 045 throw new IllegalArgumentException( 046 "Cleanup failure requires a complete core result"); 047 } 048 049 SokletShutdownCleanupException( 050 @NonNull ShutdownCleanupFailureReason shutdownCleanupFailureReason, 051 @NonNull Duration shutdownCleanupTimeout, 052 @NonNull InternalShutdownResult shutdownResult, 053 @NonNull Throwable cause) { 054 this(shutdownCleanupFailureReason, shutdownCleanupTimeout, 055 ShutdownResult.fromInternal(requireNonNull(shutdownResult)), cause); 056 } 057 058 /** @return cleanup failure reason */ 059 @NonNull 060 public ShutdownCleanupFailureReason getShutdownCleanupFailureReason() { 061 return this.shutdownCleanupFailureReason; 062 } 063 064 /** @return configured cleanup timeout */ 065 @NonNull 066 public Duration getShutdownCleanupTimeout() { 067 return this.shutdownCleanupTimeout; 068 } 069 070 @NonNull 071 private static String cleanupFailureMessage( 072 @NonNull ShutdownCleanupFailureReason shutdownCleanupFailureReason) { 073 return switch (requireNonNull(shutdownCleanupFailureReason)) { 074 case FAILED -> "Standalone Soklet cleanup failed"; 075 case TIMED_OUT -> "Standalone Soklet cleanup timed out; its daemon " 076 + "action may remain live"; 077 }; 078 } 079}