code: public static String getMessage( final Throwable    ex )
      {
          String      message = ex.getMessage();
  
          //
          //    It *appears* as though the SQLException hasn't been
          //    converted to nest?  It seems like it has it's own
          //    method of nesting, not sure why.  I don't know
          //    why Sun wouldn't have converted it.  Maybe they
          //    did, but just left these getNextException methods
          //    on for compatibility?   In my java source code, they
          //    aren't deprecated, though.
          //
          if ( ex instanceof SQLException )
          {
              String    sqlMessage = getSqlExceptionMessage( (SQLException) ex );
              if ( ! StringUtils.isBlank( sqlMessage ) )
              {
                  if ( ! StringUtils.isBlank( message ) )
                  {
                      message  += "\n" + sqlMessage;
                  }
                  else
                  {
                      message = sqlMessage;
                  }
              }
          }
  
  
          Throwable    cause = ex.getCause();
  
          if ( ex instanceof SamSixException
               && ((SamSixException) ex).isShowThisCauseOnly() )
          {
              return message;
          }
  
          if ( cause != null )
          {
              String causeMessage = ExceptionUtils.getMessage( cause );
  
              if ( ! StringUtils.isBlank( causeMessage ) )
              {
                  if ( ! StringUtils.isBlank( message ) )
                  {
                      //
                      //    ALWAYS use "broadest first" when showing error messages.
                      //    Otherwise, error messages end up with some non-human readable thing at the top,
                      //    confusing users with deeply technical details. This is especially important for user errors.
                      //
                      //    broadest/non-broadest should be used for stack traces only.
                      //
  
                      message = message + "\n" + causeMessage;
                  }
                  else
                  {
                      message = causeMessage;
                  }
              }
          }
  
  
          if ( ! StringUtils.isBlank( message ) )
          {
              return message;
          }
  
          return ex.getClass().getName() + ":  An error has been detected.";
      }
docs: Static method to recursively get the message text from the
  entire stack.  Messages are concatenated with the linefeed
  character.  This allows for a more informational
  message to be displayed to the user.
  
  @param ex exception
  @return total message