001/* 002 * SPDX-FileCopyrightText: none 003 * SPDX-License-Identifier: CC0-1.0 004 */ 005 006package gov.nist.secauto.metaschema.cli.processor; 007 008import org.apache.logging.log4j.LogBuilder; 009import org.apache.logging.log4j.LogManager; 010import org.apache.logging.log4j.Logger; 011 012import edu.umd.cs.findbugs.annotations.NonNull; 013import edu.umd.cs.findbugs.annotations.Nullable; 014import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 015 016/** 017 * Records information about the exit status of a CLI command. 018 * <p> 019 * This abstract class provides base functionality for handling CLI command exit 020 * statuses, including error logging and throwable management. Implementing 021 * classes must provide the {@link #getMessage()} implementation to define the 022 * status message content. 023 */ 024public abstract class AbstractExitStatus implements ExitStatus { 025 private static final Logger LOGGER = LogManager.getLogger(AbstractExitStatus.class); 026 027 @NonNull 028 private final ExitCode exitCode; 029 030 private Throwable throwable; 031 032 /** 033 * Construct a new exit status based on the provided {@code exitCode}. 034 * 035 * @param exitCode 036 * the exit code 037 */ 038 public AbstractExitStatus(@NonNull ExitCode exitCode) { 039 this.exitCode = exitCode; 040 } 041 042 @Override 043 public ExitCode getExitCode() { 044 return exitCode; 045 } 046 047 /** 048 * Get the associated throwable. 049 * 050 * @return the throwable or {@code null} 051 */ 052 @Override 053 public Throwable getThrowable() { 054 return throwable; 055 } 056 057 @Override 058 @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "intended as a exposed property") 059 public ExitStatus withThrowable(@NonNull Throwable throwable) { 060 this.throwable = throwable; 061 return this; 062 } 063 064 /** 065 * Determines the appropriate LogBuilder based on the exit code status. For 066 * non-positive exit codes (success/info), returns an INFO level builder. For 067 * positive exit codes (errors), returns an ERROR level builder. 068 * 069 * @return the appropriate LogBuilder based on exit status, or {@code null} if 070 * logging is disabled at the determined level 071 */ 072 @Nullable 073 private LogBuilder getLogBuilder() { 074 LogBuilder logBuilder = null; 075 if (getExitCode().getStatusCode() <= 0) { 076 if (LOGGER.isInfoEnabled()) { 077 logBuilder = LOGGER.atInfo(); 078 } 079 } else if (LOGGER.isErrorEnabled()) { 080 logBuilder = LOGGER.atError(); 081 } 082 return logBuilder; 083 } 084 085 /** 086 * Generates and logs a message based on the current exit status. The message is 087 * logged at either INFO level (for success/info status) or ERROR level (for 088 * error status). 089 * 090 * @param showStackTrace 091 * if {@code true} and a throwable is present, includes the stack trace 092 * in the log 093 */ 094 @Override 095 public void generateMessage(boolean showStackTrace) { 096 LogBuilder logBuilder = getLogBuilder(); 097 if (logBuilder == null) { 098 return; 099 } 100 101 boolean useStackTrace = showStackTrace && throwable != null; 102 if (useStackTrace) { 103 logBuilder.withThrowable(throwable); 104 } 105 106 String message = getMessage(); 107 if (throwable != null && message == null) { 108 message = throwable.getLocalizedMessage(); 109 } 110 111 if (message != null && !message.isEmpty()) { 112 logBuilder.log(message); 113 } else if (useStackTrace) { 114 // log the throwable 115 logBuilder.log(); 116 } // else avoid an empty log line 117 } 118 119}