This is an automated email from the ASF dual-hosted git repository.

cryptoe pushed a commit to branch 39.0.0
in repository https://gitbox.apache.org/repos/asf/druid.git


The following commit(s) were added to refs/heads/39.0.0 by this push:
     new 4c0c1cfdf18 lazy_serde (#20789) (#20798)
4c0c1cfdf18 is described below

commit 4c0c1cfdf186b0bb7fdb2dcbf1c885bd7f951a48
Author: Karan Kumar <[email protected]>
AuthorDate: Fri Oct 30 05:59:49 2026 +0530

    lazy_serde (#20789) (#20798)
---
 .../druid/iceberg/input/GlueIcebergCatalog.java    |  1 -
 .../druid/iceberg/input/HiveIcebergCatalog.java    |  4 +--
 .../apache/druid/iceberg/input/LocalCatalog.java   |  2 --
 .../iceberg/input/HiveIcebergCatalogTest.java      | 20 +++++++++++++++
 .../avro/SchemaRegistryBasedAvroBytesDecoder.java  | 21 +++++++++------
 .../SchemaRegistryBasedAvroBytesDecoderTest.java   | 15 +++++++++++
 .../druid/inputsource/hdfs/HdfsInputSource.java    | 10 +++-----
 .../inputsource/hdfs/HdfsInputSourceTest.java      | 14 ++++++++++
 .../DescriptorBasedProtobufBytesDecoder.java       | 30 +++++++++++++++-------
 .../protobuf/FileBasedProtobufBytesDecoder.java    |  3 +--
 .../InlineDescriptorProtobufBytesDecoder.java      |  3 +--
 .../SchemaRegistryBasedProtobufBytesDecoder.java   | 13 ++++++----
 .../FileBasedProtobufBytesDecoderTest.java         | 13 +++++-----
 .../InlineDescriptorProtobufBytesDecoderTest.java  | 15 +++++------
 ...chemaRegistryBasedProtobufBytesDecoderTest.java | 20 +++++++++++++++
 15 files changed, 130 insertions(+), 54 deletions(-)

diff --git 
a/extensions-contrib/druid-iceberg-extensions/src/main/java/org/apache/druid/iceberg/input/GlueIcebergCatalog.java
 
b/extensions-contrib/druid-iceberg-extensions/src/main/java/org/apache/druid/iceberg/input/GlueIcebergCatalog.java
index 5dfcebb062f..ccb9ff29681 100644
--- 
a/extensions-contrib/druid-iceberg-extensions/src/main/java/org/apache/druid/iceberg/input/GlueIcebergCatalog.java
+++ 
b/extensions-contrib/druid-iceberg-extensions/src/main/java/org/apache/druid/iceberg/input/GlueIcebergCatalog.java
@@ -78,7 +78,6 @@ public class GlueIcebergCatalog extends IcebergCatalog
         mapper
     );
     this.caseSensitive = caseSensitive == null ? true : caseSensitive;
-    this.catalog = retrieveCatalog();
   }
 
   @Override
diff --git 
a/extensions-contrib/druid-iceberg-extensions/src/main/java/org/apache/druid/iceberg/input/HiveIcebergCatalog.java
 
b/extensions-contrib/druid-iceberg-extensions/src/main/java/org/apache/druid/iceberg/input/HiveIcebergCatalog.java
index f3e47ab32bd..ad86c784991 100644
--- 
a/extensions-contrib/druid-iceberg-extensions/src/main/java/org/apache/druid/iceberg/input/HiveIcebergCatalog.java
+++ 
b/extensions-contrib/druid-iceberg-extensions/src/main/java/org/apache/druid/iceberg/input/HiveIcebergCatalog.java
@@ -80,10 +80,10 @@ public class HiveIcebergCatalog extends IcebergCatalog
     this.catalogUri = Preconditions.checkNotNull(catalogUri, "catalogUri 
cannot be null");
     this.catalogProperties = 
DynamicConfigProviderUtils.extraConfigAndSetStringMap(catalogProperties, 
DRUID_DYNAMIC_CONFIG_PROVIDER_KEY, mapper);
     this.caseSensitive = caseSensitive == null ? true : caseSensitive;
-    this.configuration = configuration;
+    // Copy the injected configuration: it is shared by all catalogs, so 
per-spec properties must not leak into it.
+    this.configuration = new Configuration(configuration);
     this.catalogProperties
         .forEach(this.configuration::set);
-    this.hiveCatalog = retrieveCatalog();
   }
 
   @Override
diff --git 
a/extensions-contrib/druid-iceberg-extensions/src/main/java/org/apache/druid/iceberg/input/LocalCatalog.java
 
b/extensions-contrib/druid-iceberg-extensions/src/main/java/org/apache/druid/iceberg/input/LocalCatalog.java
index 47198472474..80b8e3b334b 100644
--- 
a/extensions-contrib/druid-iceberg-extensions/src/main/java/org/apache/druid/iceberg/input/LocalCatalog.java
+++ 
b/extensions-contrib/druid-iceberg-extensions/src/main/java/org/apache/druid/iceberg/input/LocalCatalog.java
@@ -60,8 +60,6 @@ public class LocalCatalog extends IcebergCatalog
     this.warehousePath = warehousePath;
     this.catalogProperties = catalogProperties;
     this.caseSensitive = caseSensitive == null ? true : caseSensitive;
-    this.catalog = retrieveCatalog();
-
   }
 
   @JsonProperty
diff --git 
a/extensions-contrib/druid-iceberg-extensions/src/test/java/org/apache/druid/iceberg/input/HiveIcebergCatalogTest.java
 
b/extensions-contrib/druid-iceberg-extensions/src/test/java/org/apache/druid/iceberg/input/HiveIcebergCatalogTest.java
index 6c6489068d9..47cda038c7d 100644
--- 
a/extensions-contrib/druid-iceberg-extensions/src/test/java/org/apache/druid/iceberg/input/HiveIcebergCatalogTest.java
+++ 
b/extensions-contrib/druid-iceberg-extensions/src/test/java/org/apache/druid/iceberg/input/HiveIcebergCatalogTest.java
@@ -55,9 +55,29 @@ public class HiveIcebergCatalogTest
         new Configuration()
     );
     Assertions.assertEquals("hive", hiveCatalog.retrieveCatalog().name());
+    Assertions.assertEquals(0, 
hiveCatalogNullProps.getCatalogProperties().size());
+    hiveCatalogNullProps.retrieveCatalog();
     Assertions.assertEquals(2, 
hiveCatalogNullProps.getCatalogProperties().size());
   }
 
+  @Test
+  public void testCatalogPropertiesDoNotLeakIntoSharedConfiguration()
+  {
+    final Configuration sharedConf = new Configuration();
+    HashMap<String, Object> catalogMap = new HashMap<>();
+    catalogMap.put("fs.defaultFS", "hdfs://leaked");
+    final HiveIcebergCatalog hiveCatalog = new HiveIcebergCatalog(
+        FileUtils.createTempDir().getPath(),
+        "hdfs://testuri",
+        catalogMap,
+        null,
+        mapper,
+        sharedConf
+    );
+    hiveCatalog.retrieveCatalog();
+    Assertions.assertNotEquals("hdfs://leaked", 
sharedConf.get("fs.defaultFS"));
+  }
+
   @Test
   public void testAuthenticate()
   {
diff --git 
a/extensions-core/avro-extensions/src/main/java/org/apache/druid/data/input/avro/SchemaRegistryBasedAvroBytesDecoder.java
 
b/extensions-core/avro-extensions/src/main/java/org/apache/druid/data/input/avro/SchemaRegistryBasedAvroBytesDecoder.java
index c7c7438b1cd..1c3eaa105d6 100644
--- 
a/extensions-core/avro-extensions/src/main/java/org/apache/druid/data/input/avro/SchemaRegistryBasedAvroBytesDecoder.java
+++ 
b/extensions-core/avro-extensions/src/main/java/org/apache/druid/data/input/avro/SchemaRegistryBasedAvroBytesDecoder.java
@@ -24,6 +24,8 @@ import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
 import io.confluent.kafka.schemaregistry.ParsedSchema;
 import io.confluent.kafka.schemaregistry.avro.AvroSchema;
 import io.confluent.kafka.schemaregistry.client.CachedSchemaRegistryClient;
@@ -47,7 +49,7 @@ import java.util.Objects;
 
 public class SchemaRegistryBasedAvroBytesDecoder implements AvroBytesDecoder
 {
-  private final SchemaRegistryClient registry;
+  private final Supplier<SchemaRegistryClient> registry;
   private final String url;
   private final int capacity;
   private final List<String> urls;
@@ -72,11 +74,14 @@ public class SchemaRegistryBasedAvroBytesDecoder implements 
AvroBytesDecoder
     this.config = config;
     this.headers = headers;
     this.jsonMapper = jsonMapper;
-    if (url != null && !url.isEmpty()) {
-      this.registry = new CachedSchemaRegistryClient(this.url, this.capacity, 
DynamicConfigProviderUtils.extraConfigAndSetObjectMap(config, 
DRUID_DYNAMIC_CONFIG_PROVIDER_KEY, this.jsonMapper), 
DynamicConfigProviderUtils.extraConfigAndSetStringMap(headers, 
DRUID_DYNAMIC_CONFIG_PROVIDER_KEY, this.jsonMapper));
-    } else {
-      this.registry = new CachedSchemaRegistryClient(this.urls, this.capacity, 
DynamicConfigProviderUtils.extraConfigAndSetObjectMap(config, 
DRUID_DYNAMIC_CONFIG_PROVIDER_KEY, this.jsonMapper), 
DynamicConfigProviderUtils.extraConfigAndSetStringMap(headers, 
DRUID_DYNAMIC_CONFIG_PROVIDER_KEY, this.jsonMapper));
-    }
+    // Client is created lazily: its constructor reads keystores and other 
resources named in the config.
+    this.registry = Suppliers.memoize(() -> {
+      if (url != null && !url.isEmpty()) {
+        return new CachedSchemaRegistryClient(this.url, this.capacity, 
DynamicConfigProviderUtils.extraConfigAndSetObjectMap(config, 
DRUID_DYNAMIC_CONFIG_PROVIDER_KEY, this.jsonMapper), 
DynamicConfigProviderUtils.extraConfigAndSetStringMap(headers, 
DRUID_DYNAMIC_CONFIG_PROVIDER_KEY, this.jsonMapper));
+      } else {
+        return new CachedSchemaRegistryClient(this.urls, this.capacity, 
DynamicConfigProviderUtils.extraConfigAndSetObjectMap(config, 
DRUID_DYNAMIC_CONFIG_PROVIDER_KEY, this.jsonMapper), 
DynamicConfigProviderUtils.extraConfigAndSetStringMap(headers, 
DRUID_DYNAMIC_CONFIG_PROVIDER_KEY, this.jsonMapper));
+      }
+    });
   }
 
   @JsonProperty
@@ -118,7 +123,7 @@ public class SchemaRegistryBasedAvroBytesDecoder implements 
AvroBytesDecoder
     this.urls = null;
     this.config = null;
     this.headers = null;
-    this.registry = registry;
+    this.registry = () -> registry;
     this.jsonMapper = new ObjectMapper();
   }
 
@@ -136,7 +141,7 @@ public class SchemaRegistryBasedAvroBytesDecoder implements 
AvroBytesDecoder
     Schema schema;
 
     try {
-      ParsedSchema parsedSchema = registry.getSchemaById(id);
+      ParsedSchema parsedSchema = registry.get().getSchemaById(id);
       schema = parsedSchema instanceof AvroSchema ? ((AvroSchema) 
parsedSchema).rawSchema() : null;
     }
     catch (IOException ex1) {
diff --git 
a/extensions-core/avro-extensions/src/test/java/org/apache/druid/data/input/avro/SchemaRegistryBasedAvroBytesDecoderTest.java
 
b/extensions-core/avro-extensions/src/test/java/org/apache/druid/data/input/avro/SchemaRegistryBasedAvroBytesDecoderTest.java
index ea21728ebd2..c9741983105 100644
--- 
a/extensions-core/avro-extensions/src/test/java/org/apache/druid/data/input/avro/SchemaRegistryBasedAvroBytesDecoderTest.java
+++ 
b/extensions-core/avro-extensions/src/test/java/org/apache/druid/data/input/avro/SchemaRegistryBasedAvroBytesDecoderTest.java
@@ -111,6 +111,21 @@ public class SchemaRegistryBasedAvroBytesDecoderTest
     Assertions.assertNotEquals(decoder.hashCode(), 0);
   }
 
+  @Test
+  public void testClientNotCreatedOnDeserialization() throws Exception
+  {
+    // The truststore does not exist; creating the client would fail, so 
deserialization must not create it.
+    String json = "{\"urls\":[\"https://localhost\"],\"type\": 
\"schema_registry\", \"config\":"
+                  + 
"{\"schema.registry.ssl.truststore.location\":\"/nonexistent/truststore.jks\","
+                  + "\"schema.registry.ssl.truststore.password\":\"x\"}}";
+    ObjectMapper mapper = new DefaultObjectMapper();
+    mapper.setInjectableValues(
+        new InjectableValues.Std().addValue(ObjectMapper.class, new 
DefaultObjectMapper())
+    );
+
+    Assertions.assertDoesNotThrow(() -> 
mapper.readerFor(AvroBytesDecoder.class).readValue(json));
+  }
+
   @Test
   public void testParse() throws Exception
   {
diff --git 
a/extensions-core/hdfs-storage/src/main/java/org/apache/druid/inputsource/hdfs/HdfsInputSource.java
 
b/extensions-core/hdfs-storage/src/main/java/org/apache/druid/inputsource/hdfs/HdfsInputSource.java
index 1ec082fb906..bb7897f93cc 100644
--- 
a/extensions-core/hdfs-storage/src/main/java/org/apache/druid/inputsource/hdfs/HdfsInputSource.java
+++ 
b/extensions-core/hdfs-storage/src/main/java/org/apache/druid/inputsource/hdfs/HdfsInputSource.java
@@ -128,13 +128,9 @@ public class HdfsInputSource
 
   public static void verifyProtocol(Configuration conf, HdfsInputSourceConfig 
config, String pathString)
   {
-    Path path = new Path(pathString);
-    try {
-      throwIfInvalidProtocol(config, path.getFileSystem(conf).getScheme());
-    }
-    catch (IOException e) {
-      throw new RuntimeException(e);
-    }
+    // Resolve the scheme without creating a FileSystem, so a disallowed path 
never touches the network.
+    final String scheme = new Path(pathString).toUri().getScheme();
+    throwIfInvalidProtocol(config, scheme != null ? scheme : 
FileSystem.getDefaultUri(conf).getScheme());
   }
 
   private static void throwIfInvalidProtocol(HdfsInputSourceConfig config, 
String scheme)
diff --git 
a/extensions-core/hdfs-storage/src/test/java/org/apache/druid/inputsource/hdfs/HdfsInputSourceTest.java
 
b/extensions-core/hdfs-storage/src/test/java/org/apache/druid/inputsource/hdfs/HdfsInputSourceTest.java
index f88f30389b9..90c9b511df5 100644
--- 
a/extensions-core/hdfs-storage/src/test/java/org/apache/druid/inputsource/hdfs/HdfsInputSourceTest.java
+++ 
b/extensions-core/hdfs-storage/src/test/java/org/apache/druid/inputsource/hdfs/HdfsInputSourceTest.java
@@ -140,6 +140,20 @@ public class HdfsInputSourceTest extends 
InitializedNullHandlingTest
       assertTrue(exception.getMessage().contains("Only [druid] protocols are 
allowed"));
     }
 
+    @Test
+    public void testConstructorRejectsSchemeWithoutCreatingFileSystem()
+    {
+      // "nofs" has no FileSystem implementation; creating one would fail with 
a different error.
+      Throwable exception = assertThrows(IllegalArgumentException.class, () ->
+          HdfsInputSource.builder()
+              .paths("nofs://somehost/foo/bar")
+              .configuration(CONFIGURATION)
+              .inputSourceConfig(DEFAULT_INPUT_SOURCE_CONFIG)
+              .build()
+      );
+      assertTrue(exception.getMessage().contains("Only [hdfs] protocols are 
allowed"));
+    }
+
     @Test
     public void testConstructorWithDefaultHdfs()
     {
diff --git 
a/extensions-core/protobuf-extensions/src/main/java/org/apache/druid/data/input/protobuf/DescriptorBasedProtobufBytesDecoder.java
 
b/extensions-core/protobuf-extensions/src/main/java/org/apache/druid/data/input/protobuf/DescriptorBasedProtobufBytesDecoder.java
index a04ca012521..4911cea4631 100644
--- 
a/extensions-core/protobuf-extensions/src/main/java/org/apache/druid/data/input/protobuf/DescriptorBasedProtobufBytesDecoder.java
+++ 
b/extensions-core/protobuf-extensions/src/main/java/org/apache/druid/data/input/protobuf/DescriptorBasedProtobufBytesDecoder.java
@@ -25,7 +25,7 @@ import com.google.protobuf.ByteString;
 import com.google.protobuf.DescriptorProtos;
 import com.google.protobuf.Descriptors;
 import com.google.protobuf.DynamicMessage;
-import org.apache.druid.java.util.common.IAE;
+import org.apache.druid.error.DruidException;
 import org.apache.druid.java.util.common.parsers.ParseException;
 
 import javax.annotation.Nullable;
@@ -46,7 +46,7 @@ import java.util.TreeSet;
  */
 public abstract class DescriptorBasedProtobufBytesDecoder implements 
ProtobufBytesDecoder
 {
-  private Descriptors.Descriptor descriptor;
+  private volatile Descriptors.Descriptor descriptor;
 
   /**
    * An optional message Protobuf message type in the descriptor. Both short 
name and fully qualified name are accepted.
@@ -71,15 +71,30 @@ public abstract class DescriptorBasedProtobufBytesDecoder 
implements ProtobufByt
 
   public Descriptors.Descriptor getDescriptor()
   {
+    if (descriptor == null) {
+      initializeDescriptor();
+    }
     return descriptor;
   }
 
+  /**
+   * Loads the descriptor on first use. Subclasses may do I/O here, so it 
should not be called from a
+   * {@link com.fasterxml.jackson.annotation.JsonCreator} constructor.
+   */
   @VisibleForTesting
-  void initializeDescriptor()
+  synchronized void initializeDescriptor()
   {
     if (this.descriptor == null) {
-      final var descriptorSet = loadFileDescriptorSet();
-      this.descriptor = buildMessageDescriptor(descriptorSet);
+      try {
+        final var descriptorSet = loadFileDescriptorSet();
+        this.descriptor = buildMessageDescriptor(descriptorSet);
+      }
+      catch (ParseException e) {
+        // Not a ParseException: a bad descriptor must fail the task, not be 
counted as unparseable rows.
+        throw DruidException.forPersona(DruidException.Persona.OPERATOR)
+                            
.ofCategory(DruidException.Category.RUNTIME_FAILURE)
+                            .build(e, "%s", e.getMessage());
+      }
     }
   }
 
@@ -89,10 +104,7 @@ public abstract class DescriptorBasedProtobufBytesDecoder 
implements ProtobufByt
   @Override
   public DynamicMessage parse(ByteBuffer bytes)
   {
-    if (descriptor == null) {
-      throw new IAE("Descriptor needs to be initialized before parsing");
-    }
-
+    final Descriptors.Descriptor descriptor = getDescriptor();
     try {
       return DynamicMessage.parseFrom(descriptor, ByteString.copyFrom(bytes));
     }
diff --git 
a/extensions-core/protobuf-extensions/src/main/java/org/apache/druid/data/input/protobuf/FileBasedProtobufBytesDecoder.java
 
b/extensions-core/protobuf-extensions/src/main/java/org/apache/druid/data/input/protobuf/FileBasedProtobufBytesDecoder.java
index 1f5b19ab0b6..64c5c32dd5c 100644
--- 
a/extensions-core/protobuf-extensions/src/main/java/org/apache/druid/data/input/protobuf/FileBasedProtobufBytesDecoder.java
+++ 
b/extensions-core/protobuf-extensions/src/main/java/org/apache/druid/data/input/protobuf/FileBasedProtobufBytesDecoder.java
@@ -44,9 +44,8 @@ public class FileBasedProtobufBytesDecoder extends 
DescriptorBasedProtobufBytesD
     super(protoMessageType);
 
     Preconditions.checkNotNull(descriptorFilePath);
+    // Descriptor is loaded lazily on first use, not during deserialization.
     this.descriptorFilePath = descriptorFilePath;
-
-    initializeDescriptor();
   }
 
   @JsonProperty("descriptor")
diff --git 
a/extensions-core/protobuf-extensions/src/main/java/org/apache/druid/data/input/protobuf/InlineDescriptorProtobufBytesDecoder.java
 
b/extensions-core/protobuf-extensions/src/main/java/org/apache/druid/data/input/protobuf/InlineDescriptorProtobufBytesDecoder.java
index 3bc41b11241..0469d5a9688 100644
--- 
a/extensions-core/protobuf-extensions/src/main/java/org/apache/druid/data/input/protobuf/InlineDescriptorProtobufBytesDecoder.java
+++ 
b/extensions-core/protobuf-extensions/src/main/java/org/apache/druid/data/input/protobuf/InlineDescriptorProtobufBytesDecoder.java
@@ -44,8 +44,7 @@ public class InlineDescriptorProtobufBytesDecoder extends 
DescriptorBasedProtobu
 
     Preconditions.checkNotNull(descriptorString);
     this.descriptorString = descriptorString;
-
-    initializeDescriptor();
+    // Descriptor is loaded lazily on first use, not during deserialization.
   }
 
   @JsonProperty
diff --git 
a/extensions-core/protobuf-extensions/src/main/java/org/apache/druid/data/input/protobuf/SchemaRegistryBasedProtobufBytesDecoder.java
 
b/extensions-core/protobuf-extensions/src/main/java/org/apache/druid/data/input/protobuf/SchemaRegistryBasedProtobufBytesDecoder.java
index 45bd0995270..b9a48cddb3e 100644
--- 
a/extensions-core/protobuf-extensions/src/main/java/org/apache/druid/data/input/protobuf/SchemaRegistryBasedProtobufBytesDecoder.java
+++ 
b/extensions-core/protobuf-extensions/src/main/java/org/apache/druid/data/input/protobuf/SchemaRegistryBasedProtobufBytesDecoder.java
@@ -24,6 +24,8 @@ import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
 import com.google.protobuf.Descriptors;
 import com.google.protobuf.DynamicMessage;
 import io.confluent.kafka.schemaregistry.client.CachedSchemaRegistryClient;
@@ -52,7 +54,7 @@ public class SchemaRegistryBasedProtobufBytesDecoder 
implements ProtobufBytesDec
 
   private static final Logger LOGGER = new 
Logger(SchemaRegistryBasedProtobufBytesDecoder.class);
 
-  private final SchemaRegistryClient registry;
+  private final Supplier<SchemaRegistryClient> registry;
   private final String url;
   private final int capacity;
   private final List<String> urls;
@@ -86,7 +88,8 @@ public class SchemaRegistryBasedProtobufBytesDecoder 
implements ProtobufBytesDec
       throw new IAE("SchemaRegistryBasedProtobufBytesDecoder requires non-null 
URLs");
     }
 
-    this.registry = new CachedSchemaRegistryClient(
+    // Client is created lazily: its constructor reads keystores and other 
resources named in the config.
+    this.registry = Suppliers.memoize(() -> new CachedSchemaRegistryClient(
         baseUrls,
         this.capacity,
         Collections.singletonList(new ProtobufSchemaProvider()),
@@ -100,7 +103,7 @@ public class SchemaRegistryBasedProtobufBytesDecoder 
implements ProtobufBytesDec
             DRUID_DYNAMIC_CONFIG_PROVIDER_KEY,
             this.jsonMapper
         )
-    );
+    ));
   }
 
   @JsonProperty
@@ -147,7 +150,7 @@ public class SchemaRegistryBasedProtobufBytesDecoder 
implements ProtobufBytesDec
     this.urls = null;
     this.config = null;
     this.headers = null;
-    this.registry = registry;
+    this.registry = () -> registry;
     this.jsonMapper = new ObjectMapper();
   }
 
@@ -179,7 +182,7 @@ public class SchemaRegistryBasedProtobufBytesDecoder 
implements ProtobufBytesDec
     int length = bytes.limit() - 2 - 4;
     Descriptors.Descriptor descriptor;
     try {
-      ProtobufSchema schema = (ProtobufSchema) registry.getSchemaById(id);
+      ProtobufSchema schema = (ProtobufSchema) 
registry.get().getSchemaById(id);
       descriptor = schema.toDescriptor();
     }
     catch (RestClientException e) {
diff --git 
a/extensions-core/protobuf-extensions/src/test/java/org/apache/druid/data/input/protobuf/FileBasedProtobufBytesDecoderTest.java
 
b/extensions-core/protobuf-extensions/src/test/java/org/apache/druid/data/input/protobuf/FileBasedProtobufBytesDecoderTest.java
index 1e9b3fb9c88..dfac4032b34 100644
--- 
a/extensions-core/protobuf-extensions/src/test/java/org/apache/druid/data/input/protobuf/FileBasedProtobufBytesDecoderTest.java
+++ 
b/extensions-core/protobuf-extensions/src/test/java/org/apache/druid/data/input/protobuf/FileBasedProtobufBytesDecoderTest.java
@@ -22,7 +22,7 @@ package org.apache.druid.data.input.protobuf;
 import com.google.protobuf.Struct;
 import com.google.protobuf.Value;
 import com.google.protobuf.util.JsonFormat;
-import org.apache.druid.java.util.common.parsers.ParseException;
+import org.apache.druid.error.DruidException;
 import org.junit.jupiter.api.Test;
 
 import java.io.File;
@@ -115,8 +115,8 @@ public class FileBasedProtobufBytesDecoderTest
   public void testBadProto()
   {
     final var ex = assertThrows(
-        ParseException.class,
-        () -> new FileBasedProtobufBytesDecoder("proto_test_event.desc", 
"BadName")
+        DruidException.class,
+        () -> new FileBasedProtobufBytesDecoder("proto_test_event.desc", 
"BadName").getDescriptor()
     );
 
     assertEquals(
@@ -128,10 +128,9 @@ public class FileBasedProtobufBytesDecoderTest
   @Test
   public void testMalformedDescriptorUrl()
   {
-    final var ex = assertThrows(
-        ParseException.class,
-        () -> new FileBasedProtobufBytesDecoder("file:/nonexist.desc", 
"BadName")
-    );
+    // Construction must not touch the URL; the failure surfaces only on first 
use.
+    final var decoder = assertDoesNotThrow(() -> new 
FileBasedProtobufBytesDecoder("file:/nonexist.desc", "BadName"));
+    final var ex = assertThrows(DruidException.class, decoder::getDescriptor);
 
     assertEquals(
         "Failed to initialize descriptor at [file:/nonexist.desc]",
diff --git 
a/extensions-core/protobuf-extensions/src/test/java/org/apache/druid/data/input/protobuf/InlineDescriptorProtobufBytesDecoderTest.java
 
b/extensions-core/protobuf-extensions/src/test/java/org/apache/druid/data/input/protobuf/InlineDescriptorProtobufBytesDecoderTest.java
index a38dbbb56b0..603f94c327c 100644
--- 
a/extensions-core/protobuf-extensions/src/test/java/org/apache/druid/data/input/protobuf/InlineDescriptorProtobufBytesDecoderTest.java
+++ 
b/extensions-core/protobuf-extensions/src/test/java/org/apache/druid/data/input/protobuf/InlineDescriptorProtobufBytesDecoderTest.java
@@ -20,9 +20,9 @@
 package org.apache.druid.data.input.protobuf;
 
 import com.google.common.io.Files;
+import org.apache.druid.error.DruidException;
 import org.apache.druid.java.util.common.IAE;
 import org.apache.druid.java.util.common.StringUtils;
-import org.apache.druid.java.util.common.parsers.ParseException;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -78,8 +78,8 @@ public class InlineDescriptorProtobufBytesDecoderTest
   public void testBadProto()
   {
     final var ex = assertThrows(
-        ParseException.class,
-        () -> new InlineDescriptorProtobufBytesDecoder(descString, "BadName")
+        DruidException.class,
+        new InlineDescriptorProtobufBytesDecoder(descString, 
"BadName")::getDescriptor
     );
 
     assertEquals(
@@ -93,7 +93,7 @@ public class InlineDescriptorProtobufBytesDecoderTest
   {
     final var ex = assertThrows(
         IAE.class,
-        () -> new InlineDescriptorProtobufBytesDecoder("invalidString", 
"BadName")
+        new InlineDescriptorProtobufBytesDecoder("invalidString", 
"BadName")::getDescriptor
     );
 
     assertEquals(
@@ -106,11 +106,8 @@ public class InlineDescriptorProtobufBytesDecoderTest
   public void testMalformedDescriptorValidBase64InvalidDescriptor()
   {
     final var ex = assertThrows(
-        ParseException.class,
-        () -> new InlineDescriptorProtobufBytesDecoder(
-            "aGVsbG8gd29ybGQ=",
-            "BadName"
-        )
+        DruidException.class,
+        new InlineDescriptorProtobufBytesDecoder("aGVsbG8gd29ybGQ=", 
"BadName")::getDescriptor
     );
 
     assertEquals(
diff --git 
a/extensions-core/protobuf-extensions/src/test/java/org/apache/druid/data/input/protobuf/SchemaRegistryBasedProtobufBytesDecoderTest.java
 
b/extensions-core/protobuf-extensions/src/test/java/org/apache/druid/data/input/protobuf/SchemaRegistryBasedProtobufBytesDecoderTest.java
index 1a5a27e1f04..309ba7727a3 100644
--- 
a/extensions-core/protobuf-extensions/src/test/java/org/apache/druid/data/input/protobuf/SchemaRegistryBasedProtobufBytesDecoderTest.java
+++ 
b/extensions-core/protobuf-extensions/src/test/java/org/apache/druid/data/input/protobuf/SchemaRegistryBasedProtobufBytesDecoderTest.java
@@ -45,6 +45,7 @@ import java.nio.charset.StandardCharsets;
 import java.util.Collections;
 import java.util.Map;
 
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -116,6 +117,25 @@ public class SchemaRegistryBasedProtobufBytesDecoderTest
     assertEquals(capacity, 
schemaRegistryBasedProtobufBytesDecoder.getIdentityMapCapacity());
   }
 
+  @Test
+  public void testClientNotCreatedOnConstruction()
+  {
+    // The truststore does not exist; creating the client would fail, so 
construction must not create it.
+    final Map<String, Object> config = ImmutableMap.of(
+        "schema.registry.ssl.truststore.location", 
"/nonexistent/truststore.jks",
+        "schema.registry.ssl.truststore.password", "x"
+    );
+    final var decoder = assertDoesNotThrow(() -> new 
SchemaRegistryBasedProtobufBytesDecoder(
+        null,
+        null,
+        Collections.singletonList("https://localhost";),
+        config,
+        null,
+        new DefaultObjectMapper()
+    ));
+    assertEquals(config, decoder.getConfig());
+  }
+
   private ProtoTestEventWrapper.ProtoTestEvent getTestEvent()
   {
     DateTime dateTime = new DateTime(2012, 7, 12, 9, 30, 
ISOChronology.getInstanceUTC());


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to