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.NotThreadSafe; 023 024import static java.util.Objects.requireNonNull; 025 026/** Indicates that the sole startup attempt ended before readiness. */ 027@NotThreadSafe 028public final class SokletStartupException extends SokletLifecycleException { 029 @NonNull 030 private final StartupDisposition startupDisposition; 031 032 SokletStartupException(@NonNull ShutdownResult shutdownResult) { 033 this(shutdownResult, null); 034 } 035 036 SokletStartupException(@NonNull ShutdownResult shutdownResult, 037 @Nullable Throwable cause) { 038 super(message(requireNonNull(shutdownResult).getStartupDisposition()), 039 shutdownResult, cause); 040 this.startupDisposition = shutdownResult.getStartupDisposition(); 041 if (this.startupDisposition == StartupDisposition.READY) 042 throw new IllegalArgumentException( 043 "SokletStartupException requires a non-ready result"); 044 } 045 046 SokletStartupException( 047 @NonNull InternalStartupDisposition startupDisposition, 048 @NonNull InternalShutdownResult shutdownResult) { 049 this(ShutdownResult.fromInternal(requireNonNull(shutdownResult))); 050 verifyInternalDisposition(startupDisposition, shutdownResult); 051 } 052 053 SokletStartupException( 054 @NonNull InternalStartupDisposition startupDisposition, 055 @NonNull InternalShutdownResult shutdownResult, 056 @NonNull Throwable cause) { 057 this(ShutdownResult.fromInternal(requireNonNull(shutdownResult), 058 requireNonNull(cause), null, null), cause); 059 verifyInternalDisposition(startupDisposition, shutdownResult); 060 } 061 062 /** @return disposition that prevented readiness */ 063 @NonNull 064 public StartupDisposition getStartupDisposition() { 065 return this.startupDisposition; 066 } 067 068 @NonNull 069 InternalStartupDisposition getInternalStartupDisposition() { 070 return InternalStartupDisposition.valueOf( 071 this.startupDisposition.name()); 072 } 073 074 private static void verifyInternalDisposition( 075 @NonNull InternalStartupDisposition startupDisposition, 076 @NonNull InternalShutdownResult shutdownResult) { 077 if (requireNonNull(shutdownResult).startupDisposition() 078 != requireNonNull(startupDisposition)) 079 throw new IllegalArgumentException( 080 "Startup exception disposition must match its shutdown result"); 081 } 082 083 @NonNull 084 private static String message(@NonNull StartupDisposition disposition) { 085 return "Soklet startup did not reach readiness: " 086 + requireNonNull(disposition); 087 } 088}