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;
022
023import static java.util.Objects.requireNonNull;
024
025/**
026 * Sanitized checked failure from an application request-state protector.
027 *
028 * @author <a href="https://www.revetkn.com">Mark Allen</a>
029 */
030@NotThreadSafe
031public final class McpRequestStateProtectionException extends Exception {
032        private static final long serialVersionUID = 1L;
033        @NonNull
034        private final Reason reason;
035
036        /**
037         * Sanitized protection-failure category.
038         *
039         * <p>
040         * This type may gain reviewed constants in Soklet 4.x; use a forward-compatible default when switching over its values.
041         *
042         * @author <a href="https://www.revetkn.com">Mark Allen</a>
043         */
044        public enum Reason {
045                /** Protected state is invalid, unauthenticated, or context-inappropriate. */
046                INVALID_STATE,
047
048                /** The application protection service is temporarily unavailable. */
049                PROTECTOR_UNAVAILABLE
050        }
051
052        /** @return invalid-state failure without sensitive details */
053        @NonNull
054        public static McpRequestStateProtectionException fromInvalidState() {
055                return new McpRequestStateProtectionException(Reason.INVALID_STATE);
056        }
057
058        /** @return protector-unavailable failure without sensitive details */
059        @NonNull
060        public static McpRequestStateProtectionException fromProtectorUnavailable() {
061                return new McpRequestStateProtectionException(
062                                Reason.PROTECTOR_UNAVAILABLE);
063        }
064
065        private McpRequestStateProtectionException(@NonNull Reason reason) {
066                super(reason == Reason.INVALID_STATE ? "Request state is invalid."
067                                : "Request-state protection is unavailable.");
068                this.reason = requireNonNull(reason);
069        }
070
071        /** @return sanitized failure category */
072        @NonNull
073        public Reason getReason() {
074                return this.reason;
075        }
076}