public class BatchUpdateException extends java.sql.BatchUpdateException {

  /**
   * The message utility instance we use to find messages It's primed with the name of the client
   * message bundle so that it knows to look there if the message isn't found in the shared message
   * bundle.
   */
  private static final MessageUtil msgutil_ = SqlException.getMessageUtil();

  public BatchUpdateException(
      LogWriter logWriter,
      ClientMessageId msgid,
      Object[] args,
      int[] updateCounts,
      SqlException cause) {
    super(
        msgutil_.getCompleteMessage(msgid.msgid, args),
        ExceptionUtil.getSQLStateFromIdentifier(msgid.msgid),
        ExceptionUtil.getSeverityFromIdentifier(msgid.msgid),
        updateCounts);

    if (logWriter != null) {
      logWriter.traceDiagnosable(this);
    }

    if (cause != null) {
      initCause(cause);
      setNextException(cause.getSQLException());
    }
  }

  // Syntactic sugar constructors to make it easier to create
  // a BatchUpdateException with substitution parameters
  public BatchUpdateException(
      LogWriter logWriter, ClientMessageId msgid, Object[] args, int[] updateCounts) {
    this(logWriter, msgid, args, updateCounts, null);
  }

  public BatchUpdateException(LogWriter logWriter, ClientMessageId msgid, int[] updateCounts) {
    this(logWriter, msgid, (Object[]) null, updateCounts);
  }

  public BatchUpdateException(
      LogWriter logWriter, ClientMessageId msgid, Object arg1, int[] updateCounts) {
    this(logWriter, msgid, new Object[] {arg1}, updateCounts);
  }
}
  public BatchUpdateException(
      LogWriter logWriter,
      ClientMessageId msgid,
      Object[] args,
      int[] updateCounts,
      SqlException cause) {
    super(
        msgutil_.getCompleteMessage(msgid.msgid, args),
        ExceptionUtil.getSQLStateFromIdentifier(msgid.msgid),
        ExceptionUtil.getSeverityFromIdentifier(msgid.msgid),
        updateCounts);

    if (logWriter != null) {
      logWriter.traceDiagnosable(this);
    }

    if (cause != null) {
      initCause(cause);
      setNextException(cause.getSQLException());
    }
  }
  // returnTokensOnly is true only when exception tracing is enabled so
  // that we don't try to go to the server for a message while we're in
  // the middle of parsing an Sqlca reply.
  // Without this, if e.getMessage() fails, we would have infinite recursion
  // when TRACE_DIAGNOSTICS is on  because tracing occurs within the exception constructor.
  public static void printTrace(
      SqlException e,
      java.io.PrintWriter printWriter,
      String messageHeader,
      boolean returnTokensOnly) {
    String header;
    synchronized (printWriter) {
      while (e != null) {
        header = messageHeader + "[" + "SQLException@" + Integer.toHexString(e.hashCode()) + "]";
        printWriter.println(header + " java.sql.SQLException");

        java.lang.Throwable throwable = e.getCause();
        if (throwable != null) {
          printTrace(throwable, printWriter, header);
        }
        Sqlca sqlca = ((Diagnosable) e).getSqlca();
        if (sqlca != null) {
          printTrace(sqlca, printWriter, header);
          // JDK stack trace calls e.getMessage(), so we must set some state on the sqlca that says
          // return tokens only.
          ((Sqlca) sqlca).returnTokensOnlyInMessageText(returnTokensOnly);
        }

        printWriter.println(header + " SQL state  = " + e.getSQLState());
        printWriter.println(header + " Error code = " + String.valueOf(e.getErrorCode()));
        if (((Diagnosable) e).getSqlca() == null) { // Too much has changed, so escape out here.
          printWriter.println(header + " Message    = " + e.getMessage());
        } else { // This is server-side error.
          sqlca = ((Diagnosable) e).getSqlca();
          if (returnTokensOnly) {
            // print message tokens directly.
            printWriter.println(
                header
                    + " Tokens     = "
                    + sqlca.getSqlErrmc()); // a string containing error tokens only
          } else {
            // Try to get message text from server.
            String message = e.getMessage();
            if (!sqlca.messageTextRetrievedContainsTokensOnly_) { // got the message text.
              printWriter.println(header + " Message    = " + message);
            } else { // got only message tokens.
              SqlException mysteryException = sqlca.exceptionThrownOnStoredProcInvocation_;
              if (mysteryException != null
                  && (mysteryException.getErrorCode() == -440
                      || mysteryException.getErrorCode() == -444)) {
                printWriter.println(
                    header
                        + " Unable to obtain message text from server."
                        + " Only message tokens are available."
                        + " The stored procedure SYSIBM.SQLCAMESSAGE is not installed on server."
                        + " Contact your DBA.");
              } else {
                printWriter.println(
                    header
                        + " Error occurred while trying to obtain message text from server. "
                        + "Only message tokens are available.");
              }
              printWriter.println(header + " Tokens     = " + message);
            }
          }
        }

        printWriter.println(header + " Stack trace follows");
        e.printStackTrace(printWriter);

        if (e instanceof Diagnosable) {
          sqlca = (Sqlca) ((Diagnosable) e).getSqlca();
          if (sqlca != null) {
            // JDK stack trace calls e.getMessage(), now that it is finished,
            // we can reset the state on the sqlca that says return tokens only.
            sqlca.returnTokensOnlyInMessageText(false);
          }
        }

        e = e.getNextException();
      }

      printWriter.flush();
    }
  }