From 76a0e045854b81a428ce9d40f7a4f813d5fec5c9 Mon Sep 17 00:00:00 2001 From: Henry Harris Date: Thu, 10 Jul 2025 13:23:43 -0700 Subject: [PATCH 1/2] add BaseServiceException --- .../java/api/errors/BaseServiceException.java | 92 +++++++++++++++++++ .../api/errors/EndpointServiceException.java | 51 ++-------- .../java/api/errors/ServiceException.java | 56 ++--------- 3 files changed, 107 insertions(+), 92 deletions(-) create mode 100644 errors/src/main/java/com/palantir/conjure/java/api/errors/BaseServiceException.java diff --git a/errors/src/main/java/com/palantir/conjure/java/api/errors/BaseServiceException.java b/errors/src/main/java/com/palantir/conjure/java/api/errors/BaseServiceException.java new file mode 100644 index 000000000..d16b79e2f --- /dev/null +++ b/errors/src/main/java/com/palantir/conjure/java/api/errors/BaseServiceException.java @@ -0,0 +1,92 @@ +/* + * (c) Copyright 2025 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.palantir.conjure.java.api.errors; + +import com.palantir.logsafe.Arg; +import java.util.List; +import javax.annotation.Nullable; + +public abstract class BaseServiceException extends RuntimeException { + + protected final ErrorType errorType; + + protected final List> args; // unmodifiable + + protected final String errorInstanceId; + + protected final String unsafeMessage; + protected final String noArgsMessage; + + /** + * Creates a new exception for the given error. All {@link com.palantir.logsafe.Arg parameters} are propagated to + * clients; they are serialized via {@link Object#toString}. + */ + BaseServiceException(ErrorType errorType, Arg... parameters) { + this(errorType, null, parameters); + } + + /** + * As above, but additionally records the cause of this exception. + */ + BaseServiceException(ErrorType errorType, @Nullable Throwable cause, Arg... args) { + // TODO(rfink): Memoize formatting? + super(cause); + + this.errorType = errorType; + this.errorInstanceId = ServiceExceptionUtils.generateErrorInstanceId(cause); + + // Note that instantiators cannot mutate List<> args since it comes through copyToList in all code paths. + this.args = ServiceExceptionUtils.arrayToUnmodifiableList(args); + + this.unsafeMessage = ServiceExceptionUtils.renderUnsafeMessage(exceptionName(), errorType, args); + this.noArgsMessage = ServiceExceptionUtils.renderNoArgsMessage(exceptionName(), errorType); + } + + /** + * The name of the exception. Typically, the class name. + */ + protected abstract String exceptionName(); + + /** + * The {@link ErrorType} that gave rise to this exception. + */ + public ErrorType getErrorType() { + return errorType; + } + + /** + * A unique identifier for (this instance of) this error. + */ + public String getErrorInstanceId() { + return errorInstanceId; + } + + @Override + public String getMessage() { + // Including all args here since any logger not configured with safe-logging will log this message. + return unsafeMessage; + } + + public String getLogMessage() { + // Not returning safe args here since the safe-logging framework will log this message + args explicitly. + return noArgsMessage; + } + + public List> getArgs() { + return args; + } +} diff --git a/errors/src/main/java/com/palantir/conjure/java/api/errors/EndpointServiceException.java b/errors/src/main/java/com/palantir/conjure/java/api/errors/EndpointServiceException.java index d8008c160..4271e06b7 100644 --- a/errors/src/main/java/com/palantir/conjure/java/api/errors/EndpointServiceException.java +++ b/errors/src/main/java/com/palantir/conjure/java/api/errors/EndpointServiceException.java @@ -18,65 +18,26 @@ import com.palantir.logsafe.Arg; import com.palantir.logsafe.SafeLoggable; -import java.util.List; import javax.annotation.Nullable; /* * This is identical to ServiceException, but is used in Conjure-generated code to indicate that an exception was thrown * from a service endpoint. */ -public abstract class EndpointServiceException extends RuntimeException implements SafeLoggable { +public class EndpointServiceException extends BaseServiceException implements SafeLoggable { + private static final String EXCEPTION_NAME = "EndpointServiceException"; - private final ErrorType errorType; - private final List> args; // This is an unmodifiable list. - private final String errorInstanceId; - private final String unsafeMessage; - private final String noArgsMessage; - /** - * Creates a new exception for the given error. All {@link com.palantir.logsafe.Arg parameters} are propagated to - * clients. - */ public EndpointServiceException(ErrorType errorType, Arg... parameters) { - this(errorType, null, parameters); + super(errorType, parameters); } - /** As above, but additionally records the cause of this exception. */ public EndpointServiceException(ErrorType errorType, @Nullable Throwable cause, Arg... args) { - super(cause); - this.errorInstanceId = ServiceExceptionUtils.generateErrorInstanceId(cause); - this.errorType = errorType; - this.args = ServiceExceptionUtils.arrayToUnmodifiableList(args); - this.unsafeMessage = ServiceExceptionUtils.renderUnsafeMessage(EXCEPTION_NAME, errorType, args); - this.noArgsMessage = ServiceExceptionUtils.renderNoArgsMessage(EXCEPTION_NAME, errorType); - } - - /** The {@link ErrorType} that gave rise to this exception. */ - public final ErrorType getErrorType() { - return errorType; - } - - /** A unique identifier for (this instance of) this error. */ - public final String getErrorInstanceId() { - return errorInstanceId; - } - - /** A string that includes the exception name, error type, and all arguments irrespective of log-safety. */ - @Override - public final String getMessage() { - // Including all args here since any logger not configured with safe-logging will log this message. - return unsafeMessage; - } - - /** A string that includes the exception name and error type, without any arguments. */ - @Override - public final String getLogMessage() { - return noArgsMessage; + super(errorType, cause, args); } - /** The list of arguments. */ @Override - public final List> getArgs() { - return args; + protected String exceptionName() { + return EXCEPTION_NAME; } } diff --git a/errors/src/main/java/com/palantir/conjure/java/api/errors/ServiceException.java b/errors/src/main/java/com/palantir/conjure/java/api/errors/ServiceException.java index 6b2e985d7..f91fde403 100644 --- a/errors/src/main/java/com/palantir/conjure/java/api/errors/ServiceException.java +++ b/errors/src/main/java/com/palantir/conjure/java/api/errors/ServiceException.java @@ -21,62 +21,24 @@ import java.util.List; import javax.annotation.Nullable; -/** A {@link ServiceException} thrown in server-side code to indicate server-side {@link ErrorType error states}. */ -public final class ServiceException extends RuntimeException implements SafeLoggable { - private static final String EXCEPTION_NAME = "ServiceException"; - private final ErrorType errorType; - private final List> args; // unmodifiable +/** + * A {@link ServiceException} thrown in server-side code to indicate server-side {@link ErrorType error states}. + */ +public final class ServiceException extends BaseServiceException implements SafeLoggable { - private final String errorInstanceId; - private final String unsafeMessage; - private final String noArgsMessage; + private static final String EXCEPTION_NAME = "ServiceException"; - /** - * Creates a new exception for the given error. All {@link com.palantir.logsafe.Arg parameters} are propagated to - * clients; they are serialized via {@link Object#toString}. - */ public ServiceException(ErrorType errorType, Arg... parameters) { - this(errorType, null, parameters); + super(errorType, parameters); } - /** As above, but additionally records the cause of this exception. */ public ServiceException(ErrorType errorType, @Nullable Throwable cause, Arg... args) { - // TODO(rfink): Memoize formatting? - super(cause); - - this.errorInstanceId = ServiceExceptionUtils.generateErrorInstanceId(cause); - this.errorType = errorType; - // Note that instantiators cannot mutate List<> args since it comes through copyToList in all code paths. - this.args = ServiceExceptionUtils.arrayToUnmodifiableList(args); - this.unsafeMessage = ServiceExceptionUtils.renderUnsafeMessage(EXCEPTION_NAME, errorType, args); - this.noArgsMessage = ServiceExceptionUtils.renderNoArgsMessage(EXCEPTION_NAME, errorType); - } - - /** The {@link ErrorType} that gave rise to this exception. */ - public ErrorType getErrorType() { - return errorType; - } - - /** A unique identifier for (this instance of) this error. */ - public String getErrorInstanceId() { - return errorInstanceId; - } - - @Override - public String getMessage() { - // Including all args here since any logger not configured with safe-logging will log this message. - return unsafeMessage; - } - - @Override - public String getLogMessage() { - // Not returning safe args here since the safe-logging framework will log this message + args explicitly. - return noArgsMessage; + super(errorType, cause, args); } @Override - public List> getArgs() { - return args; + protected String exceptionName() { + return EXCEPTION_NAME; } /** From 9c642df9b7dcf476e2545a8f5e10ae1a9bbc323c Mon Sep 17 00:00:00 2001 From: Henry Harris Date: Thu, 10 Jul 2025 13:58:21 -0700 Subject: [PATCH 2/2] check --- .../java/api/errors/BaseServiceException.java | 20 +++++++++---------- .../api/errors/EndpointServiceException.java | 2 +- .../java/api/errors/SerializableError.java | 8 ++++---- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/errors/src/main/java/com/palantir/conjure/java/api/errors/BaseServiceException.java b/errors/src/main/java/com/palantir/conjure/java/api/errors/BaseServiceException.java index d16b79e2f..d51c0f1c1 100644 --- a/errors/src/main/java/com/palantir/conjure/java/api/errors/BaseServiceException.java +++ b/errors/src/main/java/com/palantir/conjure/java/api/errors/BaseServiceException.java @@ -22,14 +22,14 @@ public abstract class BaseServiceException extends RuntimeException { - protected final ErrorType errorType; + private final ErrorType errorType; - protected final List> args; // unmodifiable + private final List> args; // unmodifiable - protected final String errorInstanceId; + private final String errorInstanceId; - protected final String unsafeMessage; - protected final String noArgsMessage; + private final String unsafeMessage; + private final String noArgsMessage; /** * Creates a new exception for the given error. All {@link com.palantir.logsafe.Arg parameters} are propagated to @@ -64,29 +64,29 @@ public abstract class BaseServiceException extends RuntimeException { /** * The {@link ErrorType} that gave rise to this exception. */ - public ErrorType getErrorType() { + public final ErrorType getErrorType() { return errorType; } /** * A unique identifier for (this instance of) this error. */ - public String getErrorInstanceId() { + public final String getErrorInstanceId() { return errorInstanceId; } @Override - public String getMessage() { + public final String getMessage() { // Including all args here since any logger not configured with safe-logging will log this message. return unsafeMessage; } - public String getLogMessage() { + public final String getLogMessage() { // Not returning safe args here since the safe-logging framework will log this message + args explicitly. return noArgsMessage; } - public List> getArgs() { + public final List> getArgs() { return args; } } diff --git a/errors/src/main/java/com/palantir/conjure/java/api/errors/EndpointServiceException.java b/errors/src/main/java/com/palantir/conjure/java/api/errors/EndpointServiceException.java index 4271e06b7..05f7059d5 100644 --- a/errors/src/main/java/com/palantir/conjure/java/api/errors/EndpointServiceException.java +++ b/errors/src/main/java/com/palantir/conjure/java/api/errors/EndpointServiceException.java @@ -24,7 +24,7 @@ * This is identical to ServiceException, but is used in Conjure-generated code to indicate that an exception was thrown * from a service endpoint. */ -public class EndpointServiceException extends BaseServiceException implements SafeLoggable { +public final class EndpointServiceException extends BaseServiceException implements SafeLoggable { private static final String EXCEPTION_NAME = "EndpointServiceException"; diff --git a/errors/src/main/java/com/palantir/conjure/java/api/errors/SerializableError.java b/errors/src/main/java/com/palantir/conjure/java/api/errors/SerializableError.java index 43e970549..7ba4da1e5 100644 --- a/errors/src/main/java/com/palantir/conjure/java/api/errors/SerializableError.java +++ b/errors/src/main/java/com/palantir/conjure/java/api/errors/SerializableError.java @@ -50,8 +50,8 @@ public abstract class SerializableError implements Serializable { /** * A fixed code word identifying the type of error. For errors generated from {@link ServiceException}, this * corresponds to the {@link ErrorType#code} and is part of the service's API surface. Clients are given access to - * the server-side error code via {@link RemoteException#getError} and typically switch&dispatch on the error code - * and/or name. + * the server-side error code via {@link RemoteException#getError} and typically switch and dispatch on the error + * code and/or name. */ @JsonProperty("errorCode") @Value.Default @@ -62,9 +62,9 @@ public String errorCode() { } /** - * A fixed name identifying the error. For errors generated from {@link ServiceException}, this corresponding to the + * A fixed name identifying the error. For errors generated from {@link ServiceException}, this corresponds to the * {@link ErrorType#name} and is part of the service's API surface. Clients are given access to the service-side - * error name via {@link RemoteException#getError} and typically switch&dispatch on the error code and/or name. + * error name via {@link RemoteException#getError} and typically switch and dispatch on the error code and/or name. */ @JsonProperty("errorName") @Value.Default