Fix NPE while loading lookups from empty JDBC source (#16307)

diff --git a/extensions-core/lookups-cached-global/src/main/java/org/apache/druid/server/lookup/namespace/JdbcCacheGenerator.java b/extensions-core/lookups-cached-global/src/main/java/org/apache/druid/server/lookup/namespace/JdbcCacheGenerator.java
index f05ff70..ccf8504 100644
--- a/extensions-core/lookups-cached-global/src/main/java/org/apache/druid/server/lookup/namespace/JdbcCacheGenerator.java
+++ b/extensions-core/lookups-cached-global/src/main/java/org/apache/druid/server/lookup/namespace/JdbcCacheGenerator.java
@@ -204,18 +204,20 @@
     if (tsColumn == null) {
       return null;
     }
-    final Timestamp update = dbi.withHandle(
-        handle -> {
-          final String query = StringUtils.format(
-              "SELECT MAX(%s) FROM %s",
-              tsColumn, table
-          );
-          return handle
-              .createQuery(query)
-              .map(TimestampMapper.FIRST)
-              .first();
-        }
+    final String query = StringUtils.format(
+        "SELECT MAX(%s) FROM %s",
+        tsColumn, table
     );
+    final Timestamp update = dbi.withHandle(
+        handle -> handle
+            .createQuery(query)
+            .map(TimestampMapper.FIRST)
+            .first()
+    );
+    if (update == null) {
+      LOG.info("Lookup table[%s] is empty. No rows returned for the query[%s].", table, query);
+      return null;
+    }
     return update.getTime();
   }
 }
diff --git a/extensions-core/lookups-cached-global/src/test/java/org/apache/druid/server/lookup/namespace/NamespacedExtractorModuleTest.java b/extensions-core/lookups-cached-global/src/test/java/org/apache/druid/server/lookup/namespace/NamespacedExtractorModuleTest.java
index 715da7b..4e04f03 100644
--- a/extensions-core/lookups-cached-global/src/test/java/org/apache/druid/server/lookup/namespace/NamespacedExtractorModuleTest.java
+++ b/extensions-core/lookups-cached-global/src/test/java/org/apache/druid/server/lookup/namespace/NamespacedExtractorModuleTest.java
@@ -121,7 +121,7 @@
     Assert.assertNotNull(version);
     Map<String, String> map = cache.getCache();
     Assert.assertEquals("bar", map.get("foo"));
-    Assert.assertEquals(null, map.get("baz"));
+    Assert.assertNull(map.get("baz"));
   }
 
   @Test
diff --git a/extensions-core/lookups-cached-global/src/test/java/org/apache/druid/server/lookup/namespace/cache/JdbcExtractionNamespaceTest.java b/extensions-core/lookups-cached-global/src/test/java/org/apache/druid/server/lookup/namespace/cache/JdbcExtractionNamespaceTest.java
index cf71a5d..ada8e2e 100644
--- a/extensions-core/lookups-cached-global/src/test/java/org/apache/druid/server/lookup/namespace/cache/JdbcExtractionNamespaceTest.java
+++ b/extensions-core/lookups-cached-global/src/test/java/org/apache/druid/server/lookup/namespace/cache/JdbcExtractionNamespaceTest.java
@@ -391,6 +391,36 @@
   }
 
   @Test(timeout = 60_000L)
+  public void testEmptyTable()
+      throws InterruptedException
+  {
+    // Delete existing rows from table.
+    final Handle handle = derbyConnectorRule.getConnector().getDBI().open();
+    handle.createStatement(
+        StringUtils.format("DELETE FROM %s", TABLE_NAME)
+    ).setQueryTimeout(1).execute();
+
+    final JdbcExtractionNamespace extractionNamespace = new JdbcExtractionNamespace(
+        derbyConnectorRule.getMetadataConnectorConfig(),
+        TABLE_NAME,
+        KEY_NAME,
+        VAL_NAME,
+        tsColumn,
+        null,
+        new Period(0),
+        null,
+        0,
+        null,
+        new JdbcAccessSecurityConfig()
+    );
+    try (CacheScheduler.Entry entry = scheduler.schedule(extractionNamespace)) {
+      CacheSchedulerTest.waitFor(entry);
+      final Map<String, String> map = entry.getCache();
+      Assert.assertTrue(map.isEmpty());
+    }
+  }
+
+  @Test(timeout = 60_000L)
   public void testSkipOld()
       throws InterruptedException
   {