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 * Get the associated message. 066 * 067 * @return the message or {@code null} 068 */ 069 @Nullable 070 protected abstract String getMessage(); 071 072 /** 073 * Determines the appropriate LogBuilder based on the exit code status. For 074 * non-positive exit codes (success/info), returns an INFO level builder. For 075 * positive exit codes (errors), returns an ERROR level builder. 076 * 077 * @return the appropriate LogBuilder based on exit status, or {@code null} if 078 * logging is disabled at the determined level 079 */ 080 @Nullable 081 private LogBuilder getLogBuilder() { 082 LogBuilder logBuilder = null; 083 if (getExitCode().getStatusCode() <= 0) { 084 if (LOGGER.isInfoEnabled()) { 085 logBuilder = LOGGER.atInfo(); 086 } 087 } else if (LOGGER.isErrorEnabled()) { 088 logBuilder = LOGGER.atError(); 089 } 090 return logBuilder; 091 } 092 093 /** 094 * Generates and logs a message based on the current exit status. The message is 095 * logged at either INFO level (for success/info status) or ERROR level (for 096 * error status). 097 * 098 * @param showStackTrace 099 * if {@code true} and a throwable is present, includes the stack trace 100 * in the log 101 */ 102 @Override 103 public void generateMessage(boolean showStackTrace) { 104 LogBuilder logBuilder = getLogBuilder(); 105 if (logBuilder == null) { 106 return; 107 } 108 109 boolean useStackTrace = showStackTrace && throwable != null; 110 if (useStackTrace) { 111 logBuilder.withThrowable(throwable); 112 } 113 114 String message = getMessage(); 115 if (throwable != null && message == null) { 116 message = throwable.getLocalizedMessage(); 117 } 118 119 if (message != null && !message.isEmpty()) { 120 logBuilder.log(message); 121 } else if (useStackTrace) { 122 // log the throwable 123 logBuilder.log(); 124 } // else avoid an empty log line 125 } 126 127}