001/*
002 * Copyright 2002-2017 the original author or authors.
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 *      https://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 org.springframework.core;
018
019import org.springframework.lang.Nullable;
020
021/**
022 * Helper class for implementing exception classes which are capable of
023 * holding nested exceptions. Necessary because we can't share a base
024 * class among different exception types.
025 *
026 * <p>Mainly for use within the framework.
027 *
028 * @author Juergen Hoeller
029 * @since 2.0
030 * @see NestedRuntimeException
031 * @see NestedCheckedException
032 * @see NestedIOException
033 * @see org.springframework.web.util.NestedServletException
034 */
035public abstract class NestedExceptionUtils {
036
037        /**
038         * Build a message for the given base message and root cause.
039         * @param message the base message
040         * @param cause the root cause
041         * @return the full exception message
042         */
043        @Nullable
044        public static String buildMessage(@Nullable String message, @Nullable Throwable cause) {
045                if (cause == null) {
046                        return message;
047                }
048                StringBuilder sb = new StringBuilder(64);
049                if (message != null) {
050                        sb.append(message).append("; ");
051                }
052                sb.append("nested exception is ").append(cause);
053                return sb.toString();
054        }
055
056        /**
057         * Retrieve the innermost cause of the given exception, if any.
058         * @param original the original exception to introspect
059         * @return the innermost exception, or {@code null} if none
060         * @since 4.3.9
061         */
062        @Nullable
063        public static Throwable getRootCause(@Nullable Throwable original) {
064                if (original == null) {
065                        return null;
066                }
067                Throwable rootCause = null;
068                Throwable cause = original.getCause();
069                while (cause != null && cause != rootCause) {
070                        rootCause = cause;
071                        cause = cause.getCause();
072                }
073                return rootCause;
074        }
075
076        /**
077         * Retrieve the most specific cause of the given exception, that is,
078         * either the innermost cause (root cause) or the exception itself.
079         * <p>Differs from {@link #getRootCause} in that it falls back
080         * to the original exception if there is no root cause.
081         * @param original the original exception to introspect
082         * @return the most specific cause (never {@code null})
083         * @since 4.3.9
084         */
085        public static Throwable getMostSpecificCause(Throwable original) {
086                Throwable rootCause = getRootCause(original);
087                return (rootCause != null ? rootCause : original);
088        }
089
090}