This is an automated email from the ASF dual-hosted git repository.
jerryshao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new 4deb09451a [#12998] fix(catalogs): Preserve upstream error messages
when wrapping exceptions (#12999)
4deb09451a is described below
commit 4deb09451ad0a672151cfa2d471a8e8636bd4a1c
Author: MaSai <[email protected]>
AuthorDate: Wed Sep 9 19:28:31 2026 +0800
[#12998] fix(catalogs): Preserve upstream error messages when wrapping
exceptions (#12999)
### What changes were proposed in this pull request?
- Add `ExceptionMessages` helper to append the underlying cause message
when wrapping exceptions.
- Update catalog catch-all wraps (Kafka, Glue, Hive/HMS, Fileset, Model,
Lance, Doris, Paimon, Hadoop FS) to use it.
- Kafka: map invalid configuration on create/alter to
`IllegalArgumentException` (4xx).
- Glue: keep upstream text in default errors; map
`AccessDeniedException` to `ForbiddenException`.
- Add unit tests for `ExceptionMessages` and `GlueExceptionConverter`.
### Why are the changes needed?
Connectors were replacing actionable upstream errors with generic
messages. Operators only saw the stack trace. Client-caused failures
were also returned as 500.
Fix: #12998
### Does this PR introduce _any_ user-facing change?
Yes. Failed catalog operations may return richer `message` text
(upstream reason included). Some Kafka invalid-config and Glue
access-denied failures map to 400/403 instead of 500.
### How was this patch tested?
- Unit tests: `TestExceptionMessages`, `TestGlueExceptionConverter`
- Suggested local run:
`./gradlew spotlessApply :common:test --tests
org.apache.gravitino.utils.TestExceptionMessages
:catalogs:catalog-glue:test --tests
org.apache.gravitino.catalog.glue.TestGlueExceptionConverter -PskipITs`
Made with [Cursor](https://cursor.com)
---------
Co-authored-by: Cursor <[email protected]>
---
.../catalog/fileset/FilesetCatalogOperations.java | 37 ++---
.../catalog/glue/GlueExceptionConverter.java | 13 +-
.../catalog/glue/GlueTableOperations.java | 11 +-
.../catalog/glue/TestGlueExceptionConverter.java | 6 +-
.../catalog/hive/HiveCatalogOperations.java | 3 +-
.../catalog/hive/HiveTableOperations.java | 9 +-
.../catalog/hive/HiveViewCatalogOperations.java | 19 +--
.../catalog/hive/TrinoNativeViewCodec.java | 3 +-
.../doris/operation/DorisTableOperations.java | 3 +-
.../catalog/kafka/KafkaCatalogOperations.java | 70 ++++++----
.../generic/GenericCatalogOperations.java | 7 +-
.../lakehouse/lance/LanceTableOperations.java | 24 ++--
.../lakehouse/paimon/utils/CatalogUtils.java | 3 +-
.../catalog/model/ModelCatalogOperations.java | 29 ++--
.../catalog/hadoop/fs/FileSystemUtils.java | 7 +-
.../catalog/hadoop/fs/HDFSFileSystemProxy.java | 5 +-
.../gravitino/hive/client/HiveClientFactory.java | 5 +-
.../gravitino/hive/client/HiveClientImpl.java | 5 +-
.../hive/client/HiveExceptionConverter.java | 6 +-
.../gravitino/hive/client/ProxyHiveClientImpl.java | 3 +-
.../org/apache/gravitino/hive/client/Util.java | 3 +-
.../gravitino/hive/kerberos/HmsKerberosClient.java | 3 +-
.../apache/gravitino/utils/ExceptionMessages.java | 155 +++++++++++++++++++++
.../gravitino/utils/TestExceptionMessages.java | 143 +++++++++++++++++++
24 files changed, 457 insertions(+), 115 deletions(-)
diff --git
a/catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogOperations.java
b/catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogOperations.java
index 930429a2d0..1903f95924 100644
---
a/catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogOperations.java
+++
b/catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogOperations.java
@@ -121,6 +121,7 @@ import org.apache.gravitino.secret.SecretAlterChanges;
import org.apache.gravitino.secret.SecretManager;
import org.apache.gravitino.secret.SecretMaterial;
import org.apache.gravitino.secret.SecretMaterialsHolder;
+import org.apache.gravitino.utils.ExceptionMessages;
import org.apache.gravitino.utils.FilesetUtil;
import org.apache.gravitino.utils.NameIdentifierUtil;
import org.apache.gravitino.utils.NamespaceUtil;
@@ -369,7 +370,7 @@ public class FilesetCatalogOperations extends
ManagedSchemaOperations
.map(f -> NameIdentifier.of(namespace, f.name()))
.toArray(NameIdentifier[]::new);
} catch (IOException e) {
- throw new RuntimeException("Failed to list filesets under namespace " +
namespace, e);
+ throw ExceptionMessages.wrap("Failed to list filesets under namespace "
+ namespace, e);
}
}
@@ -391,7 +392,7 @@ public class FilesetCatalogOperations extends
ManagedSchemaOperations
} catch (NoSuchEntityException exception) {
throw new NoSuchFilesetException(exception, FILESET_DOES_NOT_EXIST_MSG,
ident);
} catch (IOException ioe) {
- throw new RuntimeException("Failed to load fileset %s" + ident, ioe);
+ throw ExceptionMessages.wrap("Failed to load fileset %s" + ident, ioe);
}
}
@@ -438,7 +439,7 @@ public class FilesetCatalogOperations extends
ManagedSchemaOperations
.toArray(FileInfo[]::new);
} catch (IOException e) {
- throw new RuntimeException("Failed to list files in fileset" +
filesetIdent, e);
+ throw ExceptionMessages.wrap("Failed to list files in fileset" +
filesetIdent, e);
}
}
@@ -466,7 +467,7 @@ public class FilesetCatalogOperations extends
ManagedSchemaOperations
throw new FilesetAlreadyExistsException("Fileset %s already exists",
ident);
}
} catch (IOException ioe) {
- throw new RuntimeException("Failed to check if fileset " + ident + "
exists", ioe);
+ throw ExceptionMessages.wrap("Failed to check if fileset " + ident + "
exists", ioe);
}
SchemaEntity schemaEntity;
@@ -476,7 +477,7 @@ public class FilesetCatalogOperations extends
ManagedSchemaOperations
} catch (NoSuchEntityException exception) {
throw new NoSuchSchemaException(exception, SCHEMA_DOES_NOT_EXIST_MSG,
schemaIdent);
} catch (IOException ioe) {
- throw new RuntimeException("Failed to load schema " + schemaIdent, ioe);
+ throw ExceptionMessages.wrap("Failed to load schema " + schemaIdent,
ioe);
}
// For external fileset, the storageLocation must be set.
@@ -579,7 +580,7 @@ public class FilesetCatalogOperations extends
ManagedSchemaOperations
}
} catch (IOException ioe) {
- throw new RuntimeException("Failed to create fileset " + ident, ioe);
+ throw ExceptionMessages.wrap("Failed to create fileset " + ident, ioe);
}
}
@@ -618,7 +619,7 @@ public class FilesetCatalogOperations extends
ManagedSchemaOperations
// exception into the catalog API's documented missing-schema exception.
throw new NoSuchSchemaException(exception, SCHEMA_DOES_NOT_EXIST_MSG,
schemaIdent);
} catch (IOException ioe) {
- throw new RuntimeException("Failed to create fileset " + ident, ioe);
+ throw ExceptionMessages.wrap("Failed to create fileset " + ident, ioe);
}
return FilesetImpl.builder()
@@ -681,7 +682,7 @@ public class FilesetCatalogOperations extends
ManagedSchemaOperations
throw new NoSuchFilesetException(FILESET_DOES_NOT_EXIST_MSG, ident);
}
} catch (IOException ioe) {
- throw new RuntimeException("Failed to load fileset " + ident, ioe);
+ throw ExceptionMessages.wrap("Failed to load fileset " + ident, ioe);
}
SecretMaterialsHolder writtenSecretMaterials = new SecretMaterialsHolder();
@@ -713,12 +714,12 @@ public class FilesetCatalogOperations extends
ManagedSchemaOperations
.withAuditInfo(updatedFilesetEntity.auditInfo())
.build();
} catch (IOException ioe) {
- throw new RuntimeException("Failed to update fileset " + ident, ioe);
+ throw ExceptionMessages.wrap("Failed to update fileset " + ident, ioe);
} catch (NoSuchEntityException nsee) {
throw new NoSuchFilesetException(nsee, FILESET_DOES_NOT_EXIST_MSG,
ident);
} catch (AlreadyExistsException aee) {
// This is happened when renaming a fileset to an existing fileset name.
- throw new RuntimeException(
+ throw ExceptionMessages.wrap(
"Fileset with the same name " + ident.name() + " already exists",
aee);
} finally {
if (!alterCommitted) {
@@ -757,9 +758,9 @@ public class FilesetCatalogOperations extends
ManagedSchemaOperations
LOG.warn("Fileset {} does not exist", ident);
return false;
} catch (UncheckedIOException uioe) {
- throw new RuntimeException("Failed to delete fileset " + ident,
uioe.getCause());
+ throw ExceptionMessages.wrap("Failed to delete fileset " + ident,
uioe.getCause());
} catch (IOException ioe) {
- throw new RuntimeException("Failed to delete fileset " + ident, ioe);
+ throw ExceptionMessages.wrap("Failed to delete fileset " + ident, ioe);
}
}
@@ -819,7 +820,7 @@ public class FilesetCatalogOperations extends
ManagedSchemaOperations
throw new SchemaAlreadyExistsException("Schema %s already exists",
ident);
}
} catch (IOException ioe) {
- throw new RuntimeException("Failed to check if schema " + ident + "
exists", ioe);
+ throw ExceptionMessages.wrap("Failed to check if schema " + ident + "
exists", ioe);
}
Map<String, Path> schemaPaths = getAndCheckSchemaPaths(ident.name(),
properties);
@@ -863,7 +864,7 @@ public class FilesetCatalogOperations extends
ManagedSchemaOperations
}
} catch (IOException ioe) {
- throw new RuntimeException(
+ throw ExceptionMessages.wrap(
"Failed to create schema " + ident + " location " +
schemaPath, ioe);
}
}
@@ -880,7 +881,7 @@ public class FilesetCatalogOperations extends
ManagedSchemaOperations
throw new NoSuchSchemaException(SCHEMA_DOES_NOT_EXIST_MSG, ident);
}
} catch (IOException ioe) {
- throw new RuntimeException("Failed to check if schema " + ident + "
exists", ioe);
+ throw ExceptionMessages.wrap("Failed to check if schema " + ident + "
exists", ioe);
}
// note: we need to invalidate the related fileset cache when the schema
rename change is
@@ -1034,7 +1035,7 @@ public class FilesetCatalogOperations extends
ManagedSchemaOperations
LOG.warn("Schema {} does not exist", ident);
return false;
} catch (IOException ioe) {
- throw new RuntimeException("Failed to delete schema " + ident + "
location", ioe);
+ throw ExceptionMessages.wrap("Failed to delete schema " + ident + "
location", ioe);
}
}
@@ -1192,7 +1193,7 @@ public class FilesetCatalogOperations extends
ManagedSchemaOperations
+ locationName);
}
} catch (IOException e) {
- throw new RuntimeException(
+ throw ExceptionMessages.wrap(
"Failed to check if fileset catalog location exists: " +
v, e);
}
}
@@ -1524,7 +1525,7 @@ public class FilesetCatalogOperations extends
ManagedSchemaOperations
"Interrupted when getting FileSystem for path: {}, possibly the
server is"
+ " shutting down or catalog is been dropped",
path);
- throw new RuntimeException("Interrupted when getting FileSystem for
path: " + path, e);
+ throw ExceptionMessages.wrap("Interrupted when getting FileSystem for
path: " + path, e);
} catch (ExecutionException e) {
Throwable cause = e.getCause();
if (cause instanceof IOException) {
diff --git
a/catalogs/catalog-glue/src/main/java/org/apache/gravitino/catalog/glue/GlueExceptionConverter.java
b/catalogs/catalog-glue/src/main/java/org/apache/gravitino/catalog/glue/GlueExceptionConverter.java
index c0ca31966c..533e105411 100644
---
a/catalogs/catalog-glue/src/main/java/org/apache/gravitino/catalog/glue/GlueExceptionConverter.java
+++
b/catalogs/catalog-glue/src/main/java/org/apache/gravitino/catalog/glue/GlueExceptionConverter.java
@@ -19,11 +19,14 @@
package org.apache.gravitino.catalog.glue;
import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.exceptions.ForbiddenException;
import org.apache.gravitino.exceptions.NoSuchSchemaException;
import org.apache.gravitino.exceptions.NoSuchTableException;
import org.apache.gravitino.exceptions.SchemaAlreadyExistsException;
import org.apache.gravitino.exceptions.TableAlreadyExistsException;
+import org.apache.gravitino.utils.ExceptionMessages;
import software.amazon.awssdk.awscore.exception.AwsErrorDetails;
+import software.amazon.awssdk.services.glue.model.AccessDeniedException;
import software.amazon.awssdk.services.glue.model.AlreadyExistsException;
import software.amazon.awssdk.services.glue.model.EntityNotFoundException;
import software.amazon.awssdk.services.glue.model.GlueException;
@@ -49,7 +52,10 @@ final class GlueExceptionConverter {
return new SchemaAlreadyExistsException(e, "%s already exists", context);
}
if (e instanceof InvalidInputException) {
- return new IllegalArgumentException(context + ": " + e.getMessage(), e);
+ return ExceptionMessages.illegalArgument(context, e);
+ }
+ if (e instanceof AccessDeniedException) {
+ return new ForbiddenException(e, "Glue error: %s: %s", context,
awsErrorDetail(e));
}
return new RuntimeException("Glue error: " + context + ": " +
awsErrorDetail(e), e);
}
@@ -69,7 +75,10 @@ final class GlueExceptionConverter {
return new TableAlreadyExistsException(e, "%s already exists", context);
}
if (e instanceof InvalidInputException) {
- return new IllegalArgumentException(context + ": " + e.getMessage(), e);
+ return ExceptionMessages.illegalArgument(context, e);
+ }
+ if (e instanceof AccessDeniedException) {
+ return new ForbiddenException(e, "Glue error: %s: %s", context,
awsErrorDetail(e));
}
return new RuntimeException("Glue error: " + context + ": " +
awsErrorDetail(e), e);
}
diff --git
a/catalogs/catalog-glue/src/main/java/org/apache/gravitino/catalog/glue/GlueTableOperations.java
b/catalogs/catalog-glue/src/main/java/org/apache/gravitino/catalog/glue/GlueTableOperations.java
index 4891932f29..7b5af82f49 100644
---
a/catalogs/catalog-glue/src/main/java/org/apache/gravitino/catalog/glue/GlueTableOperations.java
+++
b/catalogs/catalog-glue/src/main/java/org/apache/gravitino/catalog/glue/GlueTableOperations.java
@@ -32,6 +32,7 @@ import org.apache.gravitino.rel.expressions.literals.Literals;
import org.apache.gravitino.rel.partitions.IdentityPartition;
import org.apache.gravitino.rel.partitions.Partition;
import org.apache.gravitino.rel.partitions.Partitions;
+import org.apache.gravitino.utils.ExceptionMessages;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.services.glue.GlueClient;
@@ -99,7 +100,7 @@ class GlueTableOperations implements TableOperations,
SupportsPartitions {
nextToken = resp.nextToken();
} while (nextToken != null);
} catch (GlueException e) {
- throw new RuntimeException("Failed to list partitions for table " +
tableName, e);
+ throw ExceptionMessages.wrap("Failed to list partitions for table " +
tableName, e);
}
return names.toArray(new String[0]);
}
@@ -121,7 +122,7 @@ class GlueTableOperations implements TableOperations,
SupportsPartitions {
nextToken = resp.nextToken();
} while (nextToken != null);
} catch (GlueException e) {
- throw new RuntimeException("Failed to list partitions for table " +
tableName, e);
+ throw ExceptionMessages.wrap("Failed to list partitions for table " +
tableName, e);
}
return partitions.toArray(new Partition[0]);
}
@@ -141,7 +142,7 @@ class GlueTableOperations implements TableOperations,
SupportsPartitions {
throw new NoSuchPartitionException(
e, "Partition %s does not exist in table %s", partitionName,
tableName);
} catch (GlueException e) {
- throw new RuntimeException("Failed to get partition " + partitionName,
e);
+ throw ExceptionMessages.wrap("Failed to get partition " + partitionName,
e);
}
}
@@ -181,7 +182,7 @@ class GlueTableOperations implements TableOperations,
SupportsPartitions {
throw new PartitionAlreadyExistsException(
e, "Partition %s already exists in table %s", partition.name(),
tableName);
} catch (GlueException e) {
- throw new RuntimeException("Failed to add partition " +
partition.name(), e);
+ throw ExceptionMessages.wrap("Failed to add partition " +
partition.name(), e);
}
LOG.info("Added partition {} to {}.{}", partition.name(), dbName,
tableName);
@@ -210,7 +211,7 @@ class GlueTableOperations implements TableOperations,
SupportsPartitions {
} catch (EntityNotFoundException e) {
return false;
} catch (GlueException e) {
- throw new RuntimeException("Failed to drop partition " + partitionName,
e);
+ throw ExceptionMessages.wrap("Failed to drop partition " +
partitionName, e);
}
}
diff --git
a/catalogs/catalog-glue/src/test/java/org/apache/gravitino/catalog/glue/TestGlueExceptionConverter.java
b/catalogs/catalog-glue/src/test/java/org/apache/gravitino/catalog/glue/TestGlueExceptionConverter.java
index 14ad7d557d..7e88924137 100644
---
a/catalogs/catalog-glue/src/test/java/org/apache/gravitino/catalog/glue/TestGlueExceptionConverter.java
+++
b/catalogs/catalog-glue/src/test/java/org/apache/gravitino/catalog/glue/TestGlueExceptionConverter.java
@@ -18,11 +18,11 @@
*/
package org.apache.gravitino.catalog.glue;
-import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.apache.gravitino.exceptions.ForbiddenException;
import org.apache.gravitino.exceptions.NoSuchSchemaException;
import org.apache.gravitino.exceptions.NoSuchTableException;
import org.apache.gravitino.exceptions.SchemaAlreadyExistsException;
@@ -58,7 +58,7 @@ public class TestGlueExceptionConverter {
RuntimeException converted = GlueExceptionConverter.toSchemaException(e,
"schema drop_me");
- assertEquals(RuntimeException.class, converted.getClass());
+ assertInstanceOf(ForbiddenException.class, converted);
assertSame(e, converted.getCause());
String message = converted.getMessage();
assertTrue(message.contains("schema drop_me"), message);
@@ -81,7 +81,7 @@ public class TestGlueExceptionConverter {
RuntimeException converted = GlueExceptionConverter.toTableException(e,
"table ctas_test");
- assertEquals(RuntimeException.class, converted.getClass());
+ assertInstanceOf(ForbiddenException.class, converted);
assertSame(e, converted.getCause());
String message = converted.getMessage();
assertTrue(message.contains("table ctas_test"), message);
diff --git
a/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/HiveCatalogOperations.java
b/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/HiveCatalogOperations.java
index b07a1c4468..d1f6c5d0a2 100644
---
a/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/HiveCatalogOperations.java
+++
b/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/HiveCatalogOperations.java
@@ -88,6 +88,7 @@ import
org.apache.gravitino.rel.expressions.transforms.Transform;
import org.apache.gravitino.rel.expressions.transforms.Transforms;
import org.apache.gravitino.rel.indexes.Index;
import org.apache.gravitino.rel.types.Type;
+import org.apache.gravitino.utils.ExceptionMessages;
import org.apache.gravitino.utils.PrincipalUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -491,7 +492,7 @@ public class HiveCatalogOperations
return new HiveTableHandle(table, clientPool);
} catch (InterruptedException e) {
- throw new RuntimeException(
+ throw ExceptionMessages.wrap(
"Failed to load Hive table " + tableIdent.name() + " from Hive
metastore", e);
}
}
diff --git
a/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/HiveTableOperations.java
b/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/HiveTableOperations.java
index 5ba130a8f3..bf78f57f3e 100644
---
a/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/HiveTableOperations.java
+++
b/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/HiveTableOperations.java
@@ -36,6 +36,7 @@ import org.apache.gravitino.hive.HiveTable;
import org.apache.gravitino.rel.SupportsPartitions;
import org.apache.gravitino.rel.partitions.IdentityPartition;
import org.apache.gravitino.rel.partitions.Partition;
+import org.apache.gravitino.utils.ExceptionMessages;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -56,7 +57,7 @@ public class HiveTableOperations implements TableOperations,
SupportsPartitions
.clientPool()
.run(c -> c.listPartitionNames(tableHandle.table(), (short)
-1).toArray(new String[0]));
} catch (InterruptedException e) {
- throw new RuntimeException(
+ throw ExceptionMessages.wrap(
"Failed to list partition names of table " + tableHandle.name() +
"from Hive Metastore",
e);
}
@@ -70,7 +71,7 @@ public class HiveTableOperations implements TableOperations,
SupportsPartitions
.run(c -> c.listPartitions(tableHandle.table(), (short) -1))
.toArray(new Partition[0]);
} catch (InterruptedException e) {
- throw new RuntimeException(
+ throw ExceptionMessages.wrap(
"Failed to list partitions of table " + tableHandle.name() + "from
Hive Metastore", e);
}
}
@@ -81,7 +82,7 @@ public class HiveTableOperations implements TableOperations,
SupportsPartitions
return tableHandle.clientPool().run(c ->
c.getPartition(tableHandle.table(), partitionName));
} catch (InterruptedException e) {
- throw new RuntimeException(
+ throw ExceptionMessages.wrap(
"Failed to get partition "
+ partitionName
+ " of table "
@@ -172,7 +173,7 @@ public class HiveTableOperations implements
TableOperations, SupportsPartitions
return false;
} catch (InterruptedException e) {
- throw new RuntimeException(
+ throw ExceptionMessages.wrap(
"Failed to get partition "
+ partitionName
+ " of table "
diff --git
a/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/HiveViewCatalogOperations.java
b/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/HiveViewCatalogOperations.java
index 7fcd812e5e..aed09d2865 100644
---
a/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/HiveViewCatalogOperations.java
+++
b/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/HiveViewCatalogOperations.java
@@ -54,6 +54,7 @@ import org.apache.gravitino.rel.View;
import org.apache.gravitino.rel.ViewCatalog;
import org.apache.gravitino.rel.ViewChange;
import org.apache.gravitino.rel.types.Types;
+import org.apache.gravitino.utils.ExceptionMessages;
import org.apache.gravitino.utils.PrincipalUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -114,7 +115,7 @@ class HiveViewCatalogOperations implements ViewCatalog {
.map(name -> NameIdentifier.of(namespace, name))
.toArray(NameIdentifier[]::new);
} catch (InterruptedException e) {
- throw new RuntimeException("Failed to list Hive views in " + namespace,
e);
+ throw ExceptionMessages.wrap("Failed to list Hive views in " +
namespace, e);
}
}
@@ -183,9 +184,9 @@ class HiveViewCatalogOperations implements ViewCatalog {
} catch (TableAlreadyExistsException e) {
throw new ViewAlreadyExistsException(e, "View %s already exists in Hive
Metastore", ident);
} catch (InterruptedException e) {
- throw new RuntimeException("Failed to create Hive view " + ident, e);
+ throw ExceptionMessages.wrap("Failed to create Hive view " + ident, e);
} catch (Exception e) {
- throw new RuntimeException("Failed to create Hive view " + ident, e);
+ throw ExceptionMessages.wrap("Failed to create Hive view " + ident, e);
}
}
@@ -359,9 +360,9 @@ class HiveViewCatalogOperations implements ViewCatalog {
} catch (UnsupportedOperationException e) {
throw e;
} catch (InterruptedException e) {
- throw new RuntimeException("Failed to alter Hive view " + ident, e);
+ throw ExceptionMessages.wrap("Failed to alter Hive view " + ident, e);
} catch (Exception e) {
- throw new RuntimeException("Failed to alter Hive view " + ident, e);
+ throw ExceptionMessages.wrap("Failed to alter Hive view " + ident, e);
}
}
@@ -406,9 +407,9 @@ class HiveViewCatalogOperations implements ViewCatalog {
} catch (NoSuchTableException e) {
return false;
} catch (InterruptedException e) {
- throw new RuntimeException("Failed to drop Hive view " + ident, e);
+ throw ExceptionMessages.wrap("Failed to drop Hive view " + ident, e);
} catch (Exception e) {
- throw new RuntimeException("Failed to drop Hive view " + ident, e);
+ throw ExceptionMessages.wrap("Failed to drop Hive view " + ident, e);
}
}
@@ -449,9 +450,9 @@ class HiveViewCatalogOperations implements ViewCatalog {
} catch (NoSuchTableException e) {
throw new NoSuchViewException(e, "View %s does not exist in Hive
Metastore", ident);
} catch (InterruptedException e) {
- throw new RuntimeException("Failed to load Hive view " + ident, e);
+ throw ExceptionMessages.wrap("Failed to load Hive view " + ident, e);
} catch (Exception e) {
- throw new RuntimeException("Failed to load Hive view " + ident, e);
+ throw ExceptionMessages.wrap("Failed to load Hive view " + ident, e);
}
}
diff --git
a/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/TrinoNativeViewCodec.java
b/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/TrinoNativeViewCodec.java
index 8962aa67d6..d5d1f8fc07 100644
---
a/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/TrinoNativeViewCodec.java
+++
b/catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/TrinoNativeViewCodec.java
@@ -31,6 +31,7 @@ import java.util.Locale;
import javax.annotation.Nullable;
import org.apache.gravitino.rel.types.Type;
import org.apache.gravitino.rel.types.Types;
+import org.apache.gravitino.utils.ExceptionMessages;
/**
* Encodes and decodes Trino/Presto's native "Presto View" HMS view format, so
that views created by
@@ -134,7 +135,7 @@ final class TrinoNativeViewCodec {
try {
bytes = MAPPER.writeValueAsBytes(root);
} catch (JsonProcessingException e) {
- throw new RuntimeException("Failed to encode Trino native view
definition", e);
+ throw ExceptionMessages.wrap("Failed to encode Trino native view
definition", e);
}
return VIEW_PREFIX + Base64.getEncoder().encodeToString(bytes) +
VIEW_SUFFIX;
}
diff --git
a/catalogs/catalog-jdbc-doris/src/main/java/org/apache/gravitino/catalog/doris/operation/DorisTableOperations.java
b/catalogs/catalog-jdbc-doris/src/main/java/org/apache/gravitino/catalog/doris/operation/DorisTableOperations.java
index b002af801b..8d0bf8464a 100644
---
a/catalogs/catalog-jdbc-doris/src/main/java/org/apache/gravitino/catalog/doris/operation/DorisTableOperations.java
+++
b/catalogs/catalog-jdbc-doris/src/main/java/org/apache/gravitino/catalog/doris/operation/DorisTableOperations.java
@@ -70,6 +70,7 @@ import org.apache.gravitino.rel.indexes.Index;
import org.apache.gravitino.rel.indexes.Indexes;
import org.apache.gravitino.rel.partitions.ListPartition;
import org.apache.gravitino.rel.partitions.RangePartition;
+import org.apache.gravitino.utils.ExceptionMessages;
/** Table operations for Apache Doris. */
public class DorisTableOperations extends JdbcTableOperations {
@@ -215,7 +216,7 @@ public class DorisTableOperations extends
JdbcTableOperations {
.toString());
}
} catch (Exception e) {
- throw new RuntimeException("Failed to get the number of backend
servers", e);
+ throw ExceptionMessages.wrap("Failed to get the number of backend
servers", e);
}
}
diff --git
a/catalogs/catalog-kafka/src/main/java/org/apache/gravitino/catalog/kafka/KafkaCatalogOperations.java
b/catalogs/catalog-kafka/src/main/java/org/apache/gravitino/catalog/kafka/KafkaCatalogOperations.java
index 980b92b542..85a5feaa29 100644
---
a/catalogs/catalog-kafka/src/main/java/org/apache/gravitino/catalog/kafka/KafkaCatalogOperations.java
+++
b/catalogs/catalog-kafka/src/main/java/org/apache/gravitino/catalog/kafka/KafkaCatalogOperations.java
@@ -66,6 +66,7 @@ import org.apache.gravitino.messaging.TopicChange;
import org.apache.gravitino.meta.AuditInfo;
import org.apache.gravitino.meta.SchemaEntity;
import org.apache.gravitino.storage.IdGenerator;
+import org.apache.gravitino.utils.ExceptionMessages;
import org.apache.gravitino.utils.NamespaceUtil;
import org.apache.gravitino.utils.PrincipalUtils;
import org.apache.kafka.clients.admin.AdminClient;
@@ -153,10 +154,9 @@ public class KafkaCatalogOperations implements
CatalogOperations, SupportsSchema
adminClient = AdminClient.create(adminClientConfig);
} catch (KafkaException e) {
if (e.getCause() instanceof ConfigException) {
- throw new IllegalArgumentException(
- "Invalid configuration for Kafka AdminClient: " +
e.getCause().getMessage(), e);
+ throw ExceptionMessages.illegalArgument("Invalid configuration for
Kafka AdminClient", e);
}
- throw new RuntimeException("Failed to create Kafka AdminClient", e);
+ throw ExceptionMessages.wrap("Failed to create Kafka AdminClient", e);
}
createDefaultSchemaIfNecessary();
}
@@ -173,13 +173,9 @@ public class KafkaCatalogOperations implements
CatalogOperations, SupportsSchema
.map(name -> NameIdentifier.of(namespace, name))
.toArray(NameIdentifier[]::new);
} catch (ExecutionException e) {
- throw new RuntimeException(
- String.format(
- "Failed to list topics under the schema %s: %s",
- namespace, e.getCause().getMessage()),
- e);
+ throw ExceptionMessages.wrap("Failed to list topics under the schema " +
namespace, e);
} catch (InterruptedException e) {
- throw new RuntimeException("Failed to list topics under the schema " +
namespace, e);
+ throw ExceptionMessages.wrap("Failed to list topics under the schema " +
namespace, e);
}
}
@@ -226,10 +222,10 @@ public class KafkaCatalogOperations implements
CatalogOperations, SupportsSchema
if (e.getCause() instanceof UnknownTopicOrPartitionException) {
throw new NoSuchTopicException(e, "Topic %s does not exist", ident);
} else {
- throw new RuntimeException("Failed to load topic " + ident.name() + "
from Kafka", e);
+ throw ExceptionMessages.wrap("Failed to load topic " + ident.name() +
" from Kafka", e);
}
} catch (InterruptedException e) {
- throw new RuntimeException("Failed to load topic " + ident.name() + "
from Kafka", e);
+ throw ExceptionMessages.wrap("Failed to load topic " + ident.name() + "
from Kafka", e);
}
LOG.info("Loaded topic {} from Kafka", ident);
@@ -297,18 +293,16 @@ public class KafkaCatalogOperations implements
CatalogOperations, SupportsSchema
throw new TopicAlreadyExistsException(e, "Topic %s already exists",
ident);
} else if (e.getCause() instanceof InvalidReplicationFactorException) {
- throw new IllegalArgumentException(
- "Invalid replication factor for topic " + ident +
e.getCause().getMessage(), e);
+ throw ExceptionMessages.illegalArgument("Invalid replication factor
for topic " + ident, e);
} else if (e.getCause() instanceof InvalidConfigurationException) {
- throw new IllegalArgumentException(
- "Invalid properties for topic " + ident +
e.getCause().getMessage(), e);
+ throw ExceptionMessages.illegalArgument("Invalid properties for topic
" + ident, e);
} else {
- throw new RuntimeException("Failed to create topic in Kafka" + ident,
e);
+ throw ExceptionMessages.wrap("Failed to create topic in Kafka " +
ident, e);
}
} catch (InterruptedException e) {
- throw new RuntimeException("Failed to create topic in Kafka" + ident, e);
+ throw ExceptionMessages.wrap("Failed to create topic in Kafka " + ident,
e);
}
}
@@ -381,10 +375,10 @@ public class KafkaCatalogOperations implements
CatalogOperations, SupportsSchema
if (e.getCause() instanceof UnknownTopicOrPartitionException) {
return false;
} else {
- throw new RuntimeException("Failed to drop topic " + ident.name() + "
from Kafka", e);
+ throw ExceptionMessages.wrap("Failed to drop topic " + ident.name() +
" from Kafka", e);
}
} catch (InterruptedException e) {
- throw new RuntimeException("Failed to drop topic " + ident.name() + "
from Kafka", e);
+ throw ExceptionMessages.wrap("Failed to drop topic " + ident.name() + "
from Kafka", e);
}
}
@@ -397,7 +391,7 @@ public class KafkaCatalogOperations implements
CatalogOperations, SupportsSchema
.map(s -> NameIdentifier.of(namespace, s.name()))
.toArray(NameIdentifier[]::new);
} catch (IOException e) {
- throw new RuntimeException("Failed to list schemas under namespace " +
namespace, e);
+ throw ExceptionMessages.wrap("Failed to list schemas under namespace " +
namespace, e);
}
}
@@ -426,7 +420,7 @@ public class KafkaCatalogOperations implements
CatalogOperations, SupportsSchema
} catch (NoSuchEntityException exception) {
throw new NoSuchSchemaException(exception, "Schema %s does not exist",
ident);
} catch (IOException ioe) {
- throw new RuntimeException("Failed to load schema " + ident, ioe);
+ throw ExceptionMessages.wrap("Failed to load schema " + ident, ioe);
}
}
@@ -534,8 +528,16 @@ public class KafkaCatalogOperations implements
CatalogOperations, SupportsSchema
Collections.singletonMap(topicName,
NewPartitions.increaseTo(newPartitionCount)))
.all()
.get();
- } catch (Exception e) {
- throw new RuntimeException("Failed to increase partition count for topic
" + topicName, e);
+ } catch (ExecutionException e) {
+ if (e.getCause() instanceof InvalidConfigurationException
+ || e.getCause() instanceof IllegalArgumentException) {
+ throw ExceptionMessages.illegalArgument(
+ "Failed to increase partition count for topic " + topicName, e);
+ }
+ throw ExceptionMessages.wrap("Failed to increase partition count for
topic " + topicName, e);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw ExceptionMessages.wrap("Failed to increase partition count for
topic " + topicName, e);
}
}
@@ -546,10 +548,19 @@ public class KafkaCatalogOperations implements
CatalogOperations, SupportsSchema
.incrementalAlterConfigs(Collections.singletonMap(topicResource,
alterConfigOps))
.all()
.get();
- } catch (UnknownTopicOrPartitionException e) {
- throw new NoSuchTopicException(e, "Topic %s does not exist", topicName);
- } catch (Exception e) {
- throw new RuntimeException("Failed to alter topic properties for topic "
+ topicName, e);
+ } catch (ExecutionException e) {
+ if (e.getCause() instanceof UnknownTopicOrPartitionException) {
+ throw new NoSuchTopicException(e, "Topic %s does not exist",
topicName);
+ }
+ if (e.getCause() instanceof InvalidConfigurationException
+ || e.getCause() instanceof IllegalArgumentException) {
+ throw ExceptionMessages.illegalArgument(
+ "Failed to alter topic properties for topic " + topicName, e);
+ }
+ throw ExceptionMessages.wrap("Failed to alter topic properties for topic
" + topicName, e);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw ExceptionMessages.wrap("Failed to alter topic properties for topic
" + topicName, e);
}
}
@@ -590,7 +601,8 @@ public class KafkaCatalogOperations implements
CatalogOperations, SupportsSchema
return;
}
} catch (IOException e) {
- throw new RuntimeException("Failed to check if schema " +
defaultSchemaIdent + " exists", e);
+ throw ExceptionMessages.wrap(
+ "Failed to check if schema " + defaultSchemaIdent + " exists", e);
}
// Create the default schema
@@ -617,7 +629,7 @@ public class KafkaCatalogOperations implements
CatalogOperations, SupportsSchema
try {
store.put(defaultSchema, true /* overwrite */);
} catch (IOException ioe) {
- throw new RuntimeException("Failed to create default schema for Kafka
catalog", ioe);
+ throw ExceptionMessages.wrap("Failed to create default schema for Kafka
catalog", ioe);
}
}
}
diff --git
a/catalogs/catalog-lakehouse-generic/src/main/java/org/apache/gravitino/catalog/lakehouse/generic/GenericCatalogOperations.java
b/catalogs/catalog-lakehouse-generic/src/main/java/org/apache/gravitino/catalog/lakehouse/generic/GenericCatalogOperations.java
index 445b87dcc4..11497e4d68 100644
---
a/catalogs/catalog-lakehouse-generic/src/main/java/org/apache/gravitino/catalog/lakehouse/generic/GenericCatalogOperations.java
+++
b/catalogs/catalog-lakehouse-generic/src/main/java/org/apache/gravitino/catalog/lakehouse/generic/GenericCatalogOperations.java
@@ -66,6 +66,7 @@ import org.apache.gravitino.rel.expressions.sorts.SortOrder;
import org.apache.gravitino.rel.expressions.transforms.Transform;
import org.apache.gravitino.rel.indexes.Index;
import org.apache.gravitino.storage.IdGenerator;
+import org.apache.gravitino.utils.ExceptionMessages;
/** Operations for interacting with a generic lakehouse catalog in Apache
Gravitino. */
public class GenericCatalogOperations implements CatalogOperations,
SupportsSchemas, TableCatalog {
@@ -352,11 +353,9 @@ public class GenericCatalogOperations implements
CatalogOperations, SupportsSche
} else if (t instanceof IllegalArgumentException) {
throw (IllegalArgumentException) t;
} else if (t instanceof IOException) {
- throw new RuntimeException(
- String.format("Failed to load table %s: %s", tableIdent,
t.getMessage()), t);
+ throw ExceptionMessages.wrap("Failed to load table " + tableIdent, t);
} else {
- throw new RuntimeException(
- String.format("Unexpected exception when loading table %s",
tableIdent), t);
+ throw ExceptionMessages.wrap("Unexpected exception when loading table
" + tableIdent, t);
}
}
}
diff --git
a/catalogs/catalog-lakehouse-generic/src/main/java/org/apache/gravitino/catalog/lakehouse/lance/LanceTableOperations.java
b/catalogs/catalog-lakehouse-generic/src/main/java/org/apache/gravitino/catalog/lakehouse/lance/LanceTableOperations.java
index f951b56348..696e6c2da0 100644
---
a/catalogs/catalog-lakehouse-generic/src/main/java/org/apache/gravitino/catalog/lakehouse/lance/LanceTableOperations.java
+++
b/catalogs/catalog-lakehouse-generic/src/main/java/org/apache/gravitino/catalog/lakehouse/lance/LanceTableOperations.java
@@ -66,6 +66,7 @@ import org.apache.gravitino.rel.expressions.sorts.SortOrder;
import org.apache.gravitino.rel.expressions.transforms.Transform;
import org.apache.gravitino.rel.indexes.Index;
import org.apache.gravitino.storage.IdGenerator;
+import org.apache.gravitino.utils.ExceptionMessages;
import org.apache.gravitino.utils.PrincipalUtils;
import org.lance.Dataset;
import org.lance.ReadOptions;
@@ -303,7 +304,7 @@ public class LanceTableOperations extends
ManagedTableOperations {
} catch (NoSuchTableException e) {
return false;
} catch (Exception e) {
- throw new RuntimeException("Failed to purge Lance dataset for table " +
ident, e);
+ throw ExceptionMessages.wrap("Failed to purge Lance dataset for table "
+ ident, e);
}
}
@@ -337,7 +338,7 @@ public class LanceTableOperations extends
ManagedTableOperations {
} catch (NoSuchTableException e) {
return false;
} catch (Exception e) {
- throw new RuntimeException("Failed to drop Lance dataset for table " +
ident, e);
+ throw ExceptionMessages.wrap("Failed to drop Lance dataset for table " +
ident, e);
}
}
@@ -355,7 +356,7 @@ public class LanceTableOperations extends
ManagedTableOperations {
&& e.getMessage().contains("Not found:")) {
LOG.warn("Lance dataset at {} was already deleted, skipping.",
location);
} else {
- throw new RuntimeException("Failed to delete Lance dataset at " +
location, e);
+ throw ExceptionMessages.wrap("Failed to delete Lance dataset at " +
location, e);
}
}
}
@@ -433,7 +434,7 @@ public class LanceTableOperations extends
ManagedTableOperations {
}
throw e;
} catch (Exception e) {
- throw new RuntimeException("Failed to create Lance dataset at location "
+ location, e);
+ throw ExceptionMessages.wrap("Failed to create Lance dataset at location
" + location, e);
}
}
@@ -491,7 +492,9 @@ public class LanceTableOperations extends
ManagedTableOperations {
} catch (Exception e) {
if (forAlter) {
throw new IllegalStateException(
- "Failed to load Lance schema before altering table " + ident, e);
+ ExceptionMessages.withCause(
+ "Failed to load Lance schema before altering table " + ident,
e),
+ e);
}
LOG.debug(
"Failed to load Lance schema from location {} for table {}. Return
stored metadata.",
@@ -577,9 +580,9 @@ public class LanceTableOperations extends
ManagedTableOperations {
} catch (NoSuchEntityException e) {
throw new NoSuchTableException(e, "Table %s does not exist", ident);
} catch (EntityAlreadyExistsException e) {
- throw new IllegalArgumentException("Failed to repair table " + ident, e);
+ throw ExceptionMessages.illegalArgument("Failed to repair table " +
ident, e);
} catch (IOException e) {
- throw new RuntimeException("Failed to repair table " + ident, e);
+ throw ExceptionMessages.wrap("Failed to repair table " + ident, e);
}
}
@@ -680,9 +683,10 @@ public class LanceTableOperations extends
ManagedTableOperations {
} catch (NoSuchEntityException e) {
throw new NoSuchTableException(e, "Table %s does not exist", ident);
} catch (EntityAlreadyExistsException e) {
- throw new IllegalArgumentException("Failed to record empty version for
table " + ident, e);
+ throw ExceptionMessages.illegalArgument(
+ "Failed to record empty version for table " + ident, e);
} catch (IOException e) {
- throw new RuntimeException("Failed to record empty version for table " +
ident, e);
+ throw ExceptionMessages.wrap("Failed to record empty version for table "
+ ident, e);
}
}
@@ -807,7 +811,7 @@ public class LanceTableOperations extends
ManagedTableOperations {
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
- throw new RuntimeException(
+ throw ExceptionMessages.wrap(
"Failed to handle alterations to Lance dataset at location " +
location, e);
}
}
diff --git
a/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/utils/CatalogUtils.java
b/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/utils/CatalogUtils.java
index 5cc099f226..aae1317095 100644
---
a/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/utils/CatalogUtils.java
+++
b/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/utils/CatalogUtils.java
@@ -43,6 +43,7 @@ import
org.apache.gravitino.catalog.lakehouse.paimon.authentication.Authenticati
import
org.apache.gravitino.catalog.lakehouse.paimon.authentication.kerberos.KerberosConfig;
import
org.apache.gravitino.catalog.lakehouse.paimon.ops.PaimonBackendCatalogWrapper;
import org.apache.gravitino.exceptions.ConnectionFailedException;
+import org.apache.gravitino.utils.ExceptionMessages;
import org.apache.hadoop.conf.Configuration;
import org.apache.paimon.catalog.Catalog;
import org.apache.paimon.catalog.CatalogContext;
@@ -88,7 +89,7 @@ public class CatalogUtils {
Catalog catalog = loadCatalogBackendWithKerberosAuth(paimonConfig,
configuration);
return new PaimonBackendCatalogWrapper(catalog, kerberosClient);
} catch (Exception e) {
- throw new RuntimeException("Failed to login with kerberos", e);
+ throw ExceptionMessages.wrap("Failed to login with kerberos", e);
}
} else {
throw new UnsupportedOperationException(
diff --git
a/catalogs/catalog-model/src/main/java/org/apache/gravitino/catalog/model/ModelCatalogOperations.java
b/catalogs/catalog-model/src/main/java/org/apache/gravitino/catalog/model/ModelCatalogOperations.java
index 1f2d14c6e0..1d0c232366 100644
---
a/catalogs/catalog-model/src/main/java/org/apache/gravitino/catalog/model/ModelCatalogOperations.java
+++
b/catalogs/catalog-model/src/main/java/org/apache/gravitino/catalog/model/ModelCatalogOperations.java
@@ -54,6 +54,7 @@ import org.apache.gravitino.model.ModelCatalog;
import org.apache.gravitino.model.ModelChange;
import org.apache.gravitino.model.ModelVersion;
import org.apache.gravitino.model.ModelVersionChange;
+import org.apache.gravitino.utils.ExceptionMessages;
import org.apache.gravitino.utils.NameIdentifierUtil;
import org.apache.gravitino.utils.NamespaceUtil;
import org.apache.gravitino.utils.PrincipalUtils;
@@ -101,7 +102,7 @@ public class ModelCatalogOperations extends
ManagedSchemaOperations
} catch (NoSuchEntityException e) {
throw new NoSuchSchemaException(e, "Schema %s does not exist",
namespace);
} catch (IOException ioe) {
- throw new RuntimeException("Failed to list models under namespace " +
namespace, ioe);
+ throw ExceptionMessages.wrap("Failed to list models under namespace " +
namespace, ioe);
}
}
@@ -116,7 +117,7 @@ public class ModelCatalogOperations extends
ManagedSchemaOperations
} catch (NoSuchEntityException e) {
throw new NoSuchModelException(e, "Model %s does not exist", ident);
} catch (IOException ioe) {
- throw new RuntimeException("Failed to get model " + ident, ioe);
+ throw ExceptionMessages.wrap("Failed to get model " + ident, ioe);
}
}
@@ -146,7 +147,7 @@ public class ModelCatalogOperations extends
ManagedSchemaOperations
try {
store.put(model, false /* overwrite */);
} catch (IOException e) {
- throw new RuntimeException("Failed to register model " + ident, e);
+ throw ExceptionMessages.wrap("Failed to register model " + ident, e);
} catch (EntityAlreadyExistsException e) {
throw new ModelAlreadyExistsException(e, "Model %s already exists",
ident);
} catch (NoSuchEntityException e) {
@@ -163,7 +164,7 @@ public class ModelCatalogOperations extends
ManagedSchemaOperations
try {
return store.delete(ident, Entity.EntityType.MODEL);
} catch (IOException ioe) {
- throw new RuntimeException("Failed to delete model " + ident, ioe);
+ throw ExceptionMessages.wrap("Failed to delete model " + ident, ioe);
}
}
@@ -180,7 +181,7 @@ public class ModelCatalogOperations extends
ManagedSchemaOperations
} catch (NoSuchEntityException e) {
throw new NoSuchModelException(e, "Model %s does not exist", ident);
} catch (IOException ioe) {
- throw new RuntimeException("Failed to list model versions for model " +
ident, ioe);
+ throw ExceptionMessages.wrap("Failed to list model versions for model "
+ ident, ioe);
}
}
@@ -197,7 +198,7 @@ public class ModelCatalogOperations extends
ManagedSchemaOperations
} catch (NoSuchEntityException e) {
throw new NoSuchModelException(e, "Model %s does not exist", ident);
} catch (IOException ioe) {
- throw new RuntimeException("Failed to list model version infos for model
" + ident, ioe);
+ throw ExceptionMessages.wrap("Failed to list model version infos for
model " + ident, ioe);
}
}
@@ -254,7 +255,7 @@ public class ModelCatalogOperations extends
ManagedSchemaOperations
try {
store.put(modelVersion, false /* overwrite */);
} catch (IOException e) {
- throw new RuntimeException("Failed to link model version " + ident, e);
+ throw ExceptionMessages.wrap("Failed to link model version " + ident, e);
} catch (EntityAlreadyExistsException e) {
throw new ModelVersionAliasesAlreadyExistException(
e, "Model version alias already exists in %s", ident);
@@ -306,7 +307,7 @@ public class ModelCatalogOperations extends
ManagedSchemaOperations
throw new NoSuchModelException("Model %s does not exist", ident);
}
} catch (IOException ioe) {
- throw new RuntimeException("Failed to alter model " + ident, ioe);
+ throw ExceptionMessages.wrap("Failed to alter model " + ident, ioe);
}
try {
@@ -320,12 +321,12 @@ public class ModelCatalogOperations extends
ManagedSchemaOperations
return toModelImpl(updatedModelEntity);
} catch (IOException ioe) {
- throw new RuntimeException("Failed to load model " + ident, ioe);
+ throw ExceptionMessages.wrap("Failed to load model " + ident, ioe);
} catch (NoSuchEntityException nsee) {
throw new NoSuchModelException(nsee, "Model %s does not exist", ident);
} catch (EntityAlreadyExistsException eaee) {
// This is happened when renaming a model to an existing model name.
- throw new RuntimeException("Model already exist " + ident.name(), eaee);
+ throw ExceptionMessages.wrap("Model already exist " + ident.name(),
eaee);
}
}
@@ -416,7 +417,7 @@ public class ModelCatalogOperations extends
ManagedSchemaOperations
throw new NoSuchModelVersionException("Model version %s does not
exist", ident);
}
} catch (IOException ioe) {
- throw new RuntimeException("Failed to alter model version " + ident,
ioe);
+ throw ExceptionMessages.wrap("Failed to alter model version " + ident,
ioe);
}
try {
@@ -430,7 +431,7 @@ public class ModelCatalogOperations extends
ManagedSchemaOperations
return toModelVersionImpl(updatedModelVersionEntity);
} catch (IOException ioe) {
- throw new RuntimeException("Failed to load model version " + ident, ioe);
+ throw ExceptionMessages.wrap("Failed to load model version " + ident,
ioe);
} catch (NoSuchEntityException nsee) {
throw new NoSuchModelVersionException(nsee, "Model Version %s does not
exist", ident);
}
@@ -546,7 +547,7 @@ public class ModelCatalogOperations extends
ManagedSchemaOperations
} catch (NoSuchEntityException e) {
throw new NoSuchModelVersionException(e, "Model version %s does not
exist", ident);
} catch (IOException ioe) {
- throw new RuntimeException("Failed to get model version " + ident, ioe);
+ throw ExceptionMessages.wrap("Failed to get model version " + ident,
ioe);
}
}
@@ -597,7 +598,7 @@ public class ModelCatalogOperations extends
ManagedSchemaOperations
try {
return store.delete(ident, Entity.EntityType.MODEL_VERSION);
} catch (IOException ioe) {
- throw new RuntimeException("Failed to delete model version " + ident,
ioe);
+ throw ExceptionMessages.wrap("Failed to delete model version " + ident,
ioe);
}
}
diff --git
a/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/FileSystemUtils.java
b/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/FileSystemUtils.java
index 0c1b00cbee..10265761d5 100644
---
a/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/FileSystemUtils.java
+++
b/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/FileSystemUtils.java
@@ -32,6 +32,7 @@ import java.util.ServiceLoader;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;
import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.utils.ExceptionMessages;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
@@ -158,7 +159,7 @@ public class FileSystemUtils {
gravitinoFileSystemCredentialsProvider.setConf(conf);
return gravitinoFileSystemCredentialsProvider;
} catch (Exception e) {
- throw new RuntimeException("Failed to create
GravitinoFileSystemCredentialProvider", e);
+ throw ExceptionMessages.wrap("Failed to create
GravitinoFileSystemCredentialProvider", e);
}
}
@@ -220,7 +221,7 @@ public class FileSystemUtils {
return configuration;
} catch (Exception e) {
- throw new RuntimeException("Failed to create configuration", e);
+ throw ExceptionMessages.wrap("Failed to create configuration", e);
}
}
@@ -231,7 +232,7 @@ public class FileSystemUtils {
writeElement(writer, VALUE_TAG, value);
writer.writeEndElement();
} catch (Exception e) {
- throw new RuntimeException("Failed to write property: " + key, e);
+ throw ExceptionMessages.wrap("Failed to write property: " + key, e);
}
}
diff --git
a/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/HDFSFileSystemProxy.java
b/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/HDFSFileSystemProxy.java
index a4ac6f3b21..fff38762c9 100644
---
a/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/HDFSFileSystemProxy.java
+++
b/catalogs/hadoop-common/src/main/java/org/apache/gravitino/catalog/hadoop/fs/HDFSFileSystemProxy.java
@@ -39,6 +39,7 @@ import
org.apache.gravitino.catalog.hadoop.auth.KerberosClient;
import org.apache.gravitino.catalog.hadoop.fs.kerberos.AuthenticationConfig;
import org.apache.gravitino.catalog.hadoop.fs.kerberos.KerberosConfig;
import org.apache.gravitino.exceptions.GravitinoRuntimeException;
+import org.apache.gravitino.utils.ExceptionMessages;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
@@ -159,7 +160,7 @@ public class HDFSFileSystemProxy implements
MethodInterceptor {
if (RuntimeException.class.isAssignableFrom(e.getClass())) {
throw (RuntimeException) e;
}
- throw new RuntimeException("Failed to invoke method", e);
+ throw ExceptionMessages.wrap("Failed to invoke method", e);
}
});
}
@@ -190,7 +191,7 @@ public class HDFSFileSystemProxy implements
MethodInterceptor {
this.kerberosRealm = client.getRealm();
return ugi;
} catch (IOException e) {
- throw new RuntimeException("Failed to login with Kerberos", e);
+ throw ExceptionMessages.wrap("Failed to login with Kerberos", e);
}
}
}
diff --git
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/HiveClientFactory.java
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/HiveClientFactory.java
index 01c2c5b228..9da4905b11 100644
---
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/HiveClientFactory.java
+++
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/HiveClientFactory.java
@@ -34,6 +34,7 @@ import
org.apache.gravitino.exceptions.GravitinoRuntimeException;
import org.apache.gravitino.hive.kerberos.AuthenticationConfig;
import org.apache.gravitino.hive.kerberos.HmsKerberosClient;
import org.apache.gravitino.utils.ClassLoaderResourceCleanerUtils;
+import org.apache.gravitino.utils.ExceptionMessages;
import org.apache.gravitino.utils.PrincipalUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.UserGroupInformation;
@@ -110,7 +111,7 @@ public final class HiveClientFactory {
"Failed to connect to Hive Metastore using cached Hive version {}",
classLoader.getHiveVersion(),
e);
- throw new RuntimeException("Failed to connect to Hive Metastore", e);
+ throw ExceptionMessages.wrap("Failed to connect to Hive Metastore", e);
}
}
@@ -247,7 +248,7 @@ public final class HiveClientFactory {
kerberosClient.login();
} catch (Exception e) {
- throw new RuntimeException("Failed to initialize kerberos client", e);
+ throw ExceptionMessages.wrap("Failed to initialize kerberos client", e);
}
}
diff --git
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/HiveClientImpl.java
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/HiveClientImpl.java
index 25b32cef44..f8f5427018 100644
---
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/HiveClientImpl.java
+++
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/HiveClientImpl.java
@@ -23,6 +23,7 @@ import java.util.Properties;
import org.apache.gravitino.hive.HivePartition;
import org.apache.gravitino.hive.HiveSchema;
import org.apache.gravitino.hive.HiveTable;
+import org.apache.gravitino.utils.ExceptionMessages;
import org.apache.hadoop.security.UserGroupInformation;
/**
@@ -180,7 +181,7 @@ public class HiveClientImpl implements HiveClient {
try {
shim.close();
} catch (Exception e) {
- throw new RuntimeException("Failed to close HiveClient", e);
+ throw ExceptionMessages.wrap("Failed to close HiveClient", e);
}
}
@@ -189,7 +190,7 @@ public class HiveClientImpl implements HiveClient {
try {
return UserGroupInformation.getCurrentUser();
} catch (Exception e) {
- throw new RuntimeException("Failed to get current user", e);
+ throw ExceptionMessages.wrap("Failed to get current user", e);
}
}
}
diff --git
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/HiveExceptionConverter.java
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/HiveExceptionConverter.java
index 3eff0eaae4..cc262a6f51 100644
---
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/HiveExceptionConverter.java
+++
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/HiveExceptionConverter.java
@@ -33,6 +33,7 @@ import
org.apache.gravitino.exceptions.NonEmptySchemaException;
import org.apache.gravitino.exceptions.PartitionAlreadyExistsException;
import org.apache.gravitino.exceptions.SchemaAlreadyExistsException;
import org.apache.gravitino.exceptions.TableAlreadyExistsException;
+import org.apache.gravitino.utils.ExceptionMessages;
/**
* Utility class to convert Hive exceptions to Gravitino exceptions. This
class handles various
@@ -190,7 +191,10 @@ public class HiveExceptionConverter {
if (isConnectionKeyword(lowerMessage) ||
exceptionClassName.contains("TransportException")) {
return new ConnectionFailedException(
- cause, "Failed to connect to Hive Metastore: %s", target.name());
+ cause,
+ "%s",
+ ExceptionMessages.withCause(
+ "Failed to connect to Hive Metastore: " + target.name(), cause));
}
if (cause instanceof RuntimeException) {
diff --git
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/ProxyHiveClientImpl.java
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/ProxyHiveClientImpl.java
index 6363148cdf..bf84b4a430 100644
---
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/ProxyHiveClientImpl.java
+++
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/ProxyHiveClientImpl.java
@@ -27,6 +27,7 @@ import java.lang.reflect.UndeclaredThrowableException;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Properties;
+import org.apache.gravitino.utils.ExceptionMessages;
import org.apache.hadoop.security.UserGroupInformation;
/**
@@ -66,7 +67,7 @@ public class ProxyHiveClientImpl implements InvocationHandler
{
new ProxyHiveClientImpl(client, ugi));
} catch (IOException | InterruptedException ex) {
- throw new RuntimeException("Failed to create Kerberos Hive client", ex);
+ throw ExceptionMessages.wrap("Failed to create Kerberos Hive client",
ex);
}
}
diff --git
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/Util.java
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/Util.java
index 4cf18cb95f..0b7c88ef9b 100644
---
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/Util.java
+++
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/client/Util.java
@@ -26,6 +26,7 @@ import java.util.Arrays;
import java.util.Properties;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.utils.ExceptionMessages;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.slf4j.Logger;
@@ -53,7 +54,7 @@ public class Util {
properties.forEach((k, v) -> config.set(k.toString(), v.toString()));
resolveMetastoreUriHosts(config);
} catch (Exception e) {
- throw new RuntimeException("Failed to create configuration", e);
+ throw ExceptionMessages.wrap("Failed to create configuration", e);
}
}
diff --git
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/kerberos/HmsKerberosClient.java
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/kerberos/HmsKerberosClient.java
index ccd255ecc8..615649aad1 100644
---
a/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/kerberos/HmsKerberosClient.java
+++
b/catalogs/hive-metastore-common/src/main/java/org/apache/gravitino/hive/kerberos/HmsKerberosClient.java
@@ -32,6 +32,7 @@ import java.util.Properties;
import java.util.concurrent.ScheduledExecutorService;
import org.apache.gravitino.catalog.hadoop.auth.KerberosAuthUtils;
import org.apache.gravitino.hive.client.HiveClient;
+import org.apache.gravitino.utils.ExceptionMessages;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hive.thrift.DelegationTokenIdentifier;
import org.apache.hadoop.io.Text;
@@ -108,7 +109,7 @@ public class HmsKerberosClient implements Closeable {
return proxyUser;
} catch (Exception e) {
- throw new RuntimeException("Failed to create proxy user for Kerberos
Hive client", e);
+ throw ExceptionMessages.wrap("Failed to create proxy user for Kerberos
Hive client", e);
}
}
diff --git
a/common/src/main/java/org/apache/gravitino/utils/ExceptionMessages.java
b/common/src/main/java/org/apache/gravitino/utils/ExceptionMessages.java
new file mode 100644
index 0000000000..4818c3c6b1
--- /dev/null
+++ b/common/src/main/java/org/apache/gravitino/utils/ExceptionMessages.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.gravitino.utils;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Set;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import javax.annotation.Nullable;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * Helpers for preserving underlying system error messages when wrapping
exceptions.
+ *
+ * <p>Catalog and server code often adds operation context when rethrowing.
Context is useful, but
+ * it must not replace the upstream message that operators need to act on.
+ */
+public final class ExceptionMessages {
+
+ private ExceptionMessages() {}
+
+ /**
+ * Returns a non-blank diagnostic message from {@code throwable} or its
cause chain.
+ *
+ * <p>Transparent wrappers such as {@link ExecutionException} are skipped
when selecting candidate
+ * messages. Among remaining frames, the shallowest non-blank message is
preferred so nested
+ * {@link #wrap(String, Throwable)} / {@link #withCause(String, Throwable)}
context is not
+ * discarded. When a deeper non-blank reason is not already contained in
that message, it is
+ * appended.
+ *
+ * @param throwable the throwable to inspect, may be null
+ * @return a useful message, or null if none is available
+ */
+ @Nullable
+ public static String usefulMessage(@Nullable Throwable throwable) {
+ if (throwable == null) {
+ return null;
+ }
+
+ Set<Throwable> visited = Collections.newSetFromMap(new
IdentityHashMap<>());
+ String firstUseful = null;
+ String lastUseful = null;
+ Throwable current = throwable;
+ while (current != null) {
+ if (!visited.add(current)) {
+ break;
+ }
+
+ Throwable cause = current.getCause();
+ if (!isTransparentWrapper(current)) {
+ String message = current.getMessage();
+ // new RuntimeException(executionException) copies cause.toString() as
the detail
+ // message; ignore that synthetic text and keep walking into the real
cause.
+ boolean syntheticTransparentMessage =
+ cause != null
+ && isTransparentWrapper(cause)
+ && message != null
+ && message.equals(cause.toString());
+ if (StringUtils.isNotBlank(message) && !syntheticTransparentMessage) {
+ if (firstUseful == null) {
+ firstUseful = message;
+ }
+ lastUseful = message;
+ }
+ }
+
+ if (cause == null || cause == current) {
+ break;
+ }
+ current = cause;
+ }
+
+ if (firstUseful == null) {
+ return null;
+ }
+ if (lastUseful == null || firstUseful.equals(lastUseful) ||
firstUseful.contains(lastUseful)) {
+ return firstUseful;
+ }
+ return firstUseful + ": " + lastUseful;
+ }
+
+ /**
+ * Combines operation context with the underlying cause message.
+ *
+ * <p>If {@code context} already contains the useful cause message, {@code
context} is returned
+ * unchanged. If there is no useful cause message, {@code context} is
returned as-is.
+ *
+ * @param context operation/object context such as {@code "Failed to alter
topic X"}
+ * @param throwable the underlying failure
+ * @return a message that preserves both context and the upstream reason
when available
+ */
+ public static String withCause(String context, @Nullable Throwable
throwable) {
+ String useful = usefulMessage(throwable);
+ if (StringUtils.isBlank(useful)) {
+ return context;
+ }
+ if (StringUtils.isBlank(context)) {
+ return useful;
+ }
+ if (context.contains(useful)) {
+ return context;
+ }
+ return context + ": " + useful;
+ }
+
+ /**
+ * Wraps {@code throwable} in a {@link RuntimeException} whose message
includes both {@code
+ * context} and the underlying cause message.
+ *
+ * @param context operation/object context
+ * @param throwable the underlying failure
+ * @return a runtime exception suitable for rethrowing from catalog
operations
+ */
+ public static RuntimeException wrap(String context, Throwable throwable) {
+ return new RuntimeException(withCause(context, throwable), throwable);
+ }
+
+ /**
+ * Creates an {@link IllegalArgumentException} whose message includes both
{@code context} and the
+ * underlying cause message.
+ *
+ * @param context operation/object context
+ * @param throwable the underlying failure
+ * @return an illegal-argument exception for client-caused failures
+ */
+ public static IllegalArgumentException illegalArgument(String context,
Throwable throwable) {
+ return new IllegalArgumentException(withCause(context, throwable),
throwable);
+ }
+
+ private static boolean isTransparentWrapper(Throwable throwable) {
+ return throwable instanceof ExecutionException
+ || throwable instanceof CompletionException
+ || throwable instanceof InvocationTargetException
+ || throwable instanceof UndeclaredThrowableException;
+ }
+}
diff --git
a/common/src/test/java/org/apache/gravitino/utils/TestExceptionMessages.java
b/common/src/test/java/org/apache/gravitino/utils/TestExceptionMessages.java
new file mode 100644
index 0000000000..79d8b9ec9a
--- /dev/null
+++ b/common/src/test/java/org/apache/gravitino/utils/TestExceptionMessages.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.gravitino.utils;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestExceptionMessages {
+
+ @Test
+ public void testUsefulMessageUnwrapsTransparentWrappers() {
+ Throwable root = new IllegalArgumentException("root reason");
+ Throwable mid = new ExecutionException(root);
+ Throwable top = new RuntimeException(mid);
+
+ Assertions.assertEquals("root reason",
ExceptionMessages.usefulMessage(top));
+ }
+
+ @Test
+ public void testUsefulMessageKeepsImmediateContextAndAppendsDeeperReason() {
+ Throwable root = new IllegalArgumentException("root reason");
+ Throwable mid = new ExecutionException(root);
+ Throwable top = new RuntimeException("wrapper", mid);
+
+ Assertions.assertEquals("wrapper: root reason",
ExceptionMessages.usefulMessage(top));
+ }
+
+ @Test
+ public void testUsefulMessageIgnoresBlankMessages() {
+ Throwable blankCause = new IOException(" ");
+ Throwable cause = new IOException("HMS connection refused", blankCause);
+
+ Assertions.assertEquals(
+ "Failed to load table: HMS connection refused",
+ ExceptionMessages.withCause("Failed to load table", cause));
+ Assertions.assertEquals("HMS connection refused",
ExceptionMessages.usefulMessage(cause));
+ }
+
+ @Test
+ public void testUsefulMessageBlankOnlyChainReturnsNull() {
+ Throwable blank = new IOException("\n\t ");
+ Assertions.assertNull(ExceptionMessages.usefulMessage(blank));
+ Assertions.assertEquals(
+ "Failed to load table", ExceptionMessages.withCause("Failed to load
table", blank));
+ }
+
+ @Test
+ public void testUsefulMessageStopsOnTwoNodeCycle() {
+ RuntimeException a = new RuntimeException("a");
+ RuntimeException b = new RuntimeException("b");
+ a.initCause(b);
+ b.initCause(a);
+
+ Assertions.assertEquals("a: b", ExceptionMessages.usefulMessage(a));
+ }
+
+ @Test
+ public void testUsefulMessageStopsOnThreeNodeCycle() {
+ RuntimeException a = new RuntimeException("a");
+ RuntimeException b = new RuntimeException("b");
+ RuntimeException c = new RuntimeException("c");
+ a.initCause(b);
+ b.initCause(c);
+ c.initCause(a);
+
+ Assertions.assertEquals("a: c", ExceptionMessages.usefulMessage(a));
+ }
+
+ @Test
+ public void testNestedWrapPreservesIntermediatePropertyContext() {
+ Throwable root = new IOException("write failed");
+ Throwable inner = ExceptionMessages.wrap("Failed to write property:
fs.s3a.endpoint", root);
+ RuntimeException outer = ExceptionMessages.wrap("Failed to create
configuration", inner);
+
+ Assertions.assertTrue(
+ outer.getMessage().contains("fs.s3a.endpoint"),
+ "nested wrap must keep intermediate property context, got: " +
outer.getMessage());
+ Assertions.assertEquals(
+ "Failed to create configuration: Failed to write property:
fs.s3a.endpoint: write failed",
+ outer.getMessage());
+ }
+
+ @Test
+ public void testWithCauseAppendsUpstreamMessage() {
+ Throwable cause =
+ new IllegalArgumentException(
+ "Invalid value nonsense for configuration cleanup.policy: String
must be one of:"
+ + " compact, delete");
+
+ String combined =
+ ExceptionMessages.withCause("Failed to alter topic properties for
topic prop_probe", cause);
+
+ Assertions.assertEquals(
+ "Failed to alter topic properties for topic prop_probe: Invalid value
nonsense for"
+ + " configuration cleanup.policy: String must be one of: compact,
delete",
+ combined);
+ }
+
+ @Test
+ public void testWithCauseDoesNotDuplicateMessage() {
+ String context = "Failed to alter topic: bad value";
+ Throwable cause = new IllegalArgumentException("bad value");
+
+ Assertions.assertEquals(context, ExceptionMessages.withCause(context,
cause));
+ }
+
+ @Test
+ public void testWrapPreservesCauseAndMessage() {
+ Throwable cause = new IllegalStateException("glue denied");
+ RuntimeException wrapped = ExceptionMessages.wrap("Glue error: schema
drop_me", cause);
+
+ Assertions.assertEquals("Glue error: schema drop_me: glue denied",
wrapped.getMessage());
+ Assertions.assertSame(cause, wrapped.getCause());
+ }
+
+ @Test
+ public void testIllegalArgumentPreservesCauseAndMessage() {
+ Throwable cause = new IllegalArgumentException("not allowed");
+ IllegalArgumentException wrapped =
+ ExceptionMessages.illegalArgument("Invalid properties for topic t1",
cause);
+
+ Assertions.assertEquals("Invalid properties for topic t1: not allowed",
wrapped.getMessage());
+ Assertions.assertSame(cause, wrapped.getCause());
+ }
+}