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

smiklosovic pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git

commit d75d6032811c3f83813fb21209fe8ed3be53b2ea
Merge: f989d96b4b 15a6bac06d
Author: Stefan Miklosovic <[email protected]>
AuthorDate: Mon Jul 20 20:46:35 2026 +0200

    Merge branch 'cassandra-6.0' into trunk

 CHANGES.txt                                        |   1 +
 src/java/org/apache/cassandra/auth/AuthConfig.java |  19 ++--
 .../cassandra/auth/MutualTlsAuthenticator.java     |   3 +-
 .../auth/MutualTlsInternodeAuthenticator.java      |   3 +-
 .../cassandra/config/DatabaseDescriptor.java       |  15 +--
 .../cassandra/config/ParameterizedClass.java       |  24 ++++-
 src/java/org/apache/cassandra/db/StorageHook.java  |   4 +-
 .../db/guardrails/GuardrailsConfigProvider.java    |   2 +-
 .../cassandra/db/guardrails/ValueGenerator.java    |   7 +-
 .../cassandra/db/guardrails/ValueValidator.java    |   5 +-
 .../apache/cassandra/db/marshal/TypeParser.java    |  19 +++-
 .../cassandra/diag/DiagnosticEventPersistence.java |   5 +-
 .../cassandra/index/SecondaryIndexManager.java     |   7 +-
 .../cassandra/index/sasi/conf/IndexMode.java       |  49 +++++----
 .../locator/AbstractReplicationStrategy.java       |  10 +-
 .../repair/autorepair/AutoRepairConfig.java        |   5 +-
 .../apache/cassandra/schema/CompactionParams.java  |  10 +-
 .../apache/cassandra/schema/CompressionParams.java |  13 ++-
 .../org/apache/cassandra/schema/IndexMetadata.java |   4 +-
 .../apache/cassandra/schema/MemtableParams.java    |   8 +-
 .../apache/cassandra/schema/ReplicationParams.java |   4 +-
 .../cassandra/security/AbstractCryptoProvider.java |   2 +-
 .../apache/cassandra/security/CipherFactory.java   |   8 +-
 .../org/apache/cassandra/service/CacheService.java |   8 +-
 .../org/apache/cassandra/service/ClientState.java  |   2 +-
 .../service/DiskErrorsHandlerService.java          |   2 +-
 .../cassandra/service/accord/AccordService.java    |   4 +-
 .../org/apache/cassandra/streaming/StreamHook.java |   2 +-
 .../cassandra/tcm/extensions/ExtensionKey.java     |   7 +-
 .../org/apache/cassandra/tools/nodetool/Sjk.java   |   3 +-
 src/java/org/apache/cassandra/tracing/Tracing.java |   2 +-
 .../apache/cassandra/triggers/TriggerExecutor.java |  26 ++++-
 src/java/org/apache/cassandra/utils/Clock.java     |   2 +-
 .../org/apache/cassandra/utils/FBUtilities.java    | 112 ++++++++++++++++++---
 .../org/apache/cassandra/utils/JMXServerUtils.java |   2 +-
 .../org/apache/cassandra/utils/MBeanWrapper.java   |   2 +-
 .../org/apache/cassandra/utils/MonotonicClock.java |   5 +-
 .../org/apache/cassandra/auth/AuthConfigTest.java  |  73 ++++++++++++++
 .../cassandra/config/ClassLoadingSearchProbe.java  |  37 +++++++
 .../cassandra/config/DatabaseDescriptorTest.java   |  22 ++++
 .../cassandra/config/ParameterizedClassTest.java   |  48 +++++++++
 .../guardrails/GuardrailsConfigProviderTest.java   |  14 +++
 .../db/guardrails/ValueGeneratorTest.java          |  18 ++++
 .../db/guardrails/ValueValidatorTest.java          |  18 ++++
 .../cassandra/db/marshal/TypeParserTest.java       |  14 +++
 .../diag/ClassLoadingTestDiagnosticEvent.java      |  44 ++++++++
 .../diag/DiagnosticEventPersistenceTest.java       |  71 +++++++++++++
 .../cassandra/index/SecondaryIndexManagerTest.java |  15 +++
 .../cassandra/index/sasi/conf/IndexModeTest.java   |  53 +++++++++-
 .../repair/autorepair/AutoRepairConfigTest.java    |  17 ++++
 .../cassandra/schema/CompactionParamsTest.java     |  44 ++++++++
 .../cassandra/schema/CompressionParamsTest.java    |  17 ++++
 .../apache/cassandra/schema/IndexMetadataTest.java |  31 ++++++
 .../schema/MemtableFactoryInvalidFieldType.java    |  34 +++++++
 .../schema/MemtableFactoryInvalidReturnType.java   |  39 +++++++
 .../cassandra/schema/MemtableParamsTest.java       |  44 ++++++++
 .../cassandra/schema/ReplicationParamsTest.java    |  87 ++++++++++++++++
 .../cassandra/security/CipherFactoryTest.java      |  22 ++++
 .../cassandra/security/CryptoProviderTest.java     |   2 +-
 .../cassandra/triggers/TriggerExecutorTest.java    |  26 +++++
 .../cassandra/utils/ClassLoadingSearchProbe.java   |  33 ++++++
 .../ClassLoadingTestNonAssignable.java}            |  23 ++---
 .../cassandra/utils/ClassLoadingTestSupport.java   |  47 +++++++++
 .../apache/cassandra/utils/FBUtilitiesTest.java    |  84 ++++++++++++++++
 .../org/apache/cassandra/tools/LoaderOptions.java  |  14 +--
 .../stress/settings/OptionReplication.java         |   7 +-
 .../cassandra/stress/settings/SettingsMode.java    |   8 +-
 67 files changed, 1263 insertions(+), 149 deletions(-)

diff --cc CHANGES.txt
index 14b5748afa,2bbecfca5a..2449f51844
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -56,23 -50,6 +56,24 @@@ Merged from 6.0
   * Differentiate between legitimate cases where the first entry is the same 
as the last entry and empty bounds in SSTableCursorWriter#addIndexBlock() 
(CASSANDRA-21255)
   * Introduce minimum_threshold for data resurrection startup check 
(CASSANDRA-21293)
   * Synchronously publish changes to local gossip state following metadata 
updates (CASSANDRA-21239)
 +Merged from 5.0:
 + * Support Python 3.12 and 3.13 in cqlsh (CASSANDRA-20997)
 + * Make synchronization on VectorMemoryIndex inserts more granular 
(CASSANDRA-21160)
 + * Fix RequestFailureReason serializer and nits in a few others 
(CASSANDRA-21437)
 + * Remove golang dependency in gen-doc and replace with python implementation 
(CASSANDRA-21432)
 + * Use estimated compressed size for tables to check if there is enough free 
space for a compaction (CASSANDRA-21245)
 + * Fix failing select on system_views.settings for non-string keys 
(CASSANDRA-21348)
 +Merged from 4.0:
++ * Verify extension type before initializing reflectively-loaded classes 
(CASSANDRA-21525)
 + * Rename conflicting nodetool import --copy-data short option from -p to -cd 
(CASSANDRA-20214)
 + * Fix PasswordObfuscator failing to obfuscate certain passwords 
(CASSANDRA-21113)
 + * Fix negative memtable allocator ownership when an update is shadowed by an 
existing row deletion (CASSANDRA-21469)
 + * Consider first token of SSTable when calculating SSTable intersection in 
LeveledScanner  (CASSANDRA-21369)
 + * Remove inFlightEcho entry on ECHO_REQ failure (CASSANDRA-21428)
 + * Validate snapshot names (CASSANDRA-21389)
 +
 +
 +6.0-alpha2
   * Change default for cassandra.set_sep_thread_name to false to reduce CPU 
usage (CASSANDRA-21089)
   * Avoid permission checks for masked columns when the table doesn't have any 
(CASSANDRA-21299)
   * Reduce allocations and array copies due to buffer resizing in 
LocalDataResponse during row serialization (CASSANDRA-21285)
diff --cc src/java/org/apache/cassandra/auth/AuthConfig.java
index 86e1f626ff,a9257f1905..52182afe31
--- a/src/java/org/apache/cassandra/auth/AuthConfig.java
+++ b/src/java/org/apache/cassandra/auth/AuthConfig.java
@@@ -109,7 -110,7 +110,9 @@@ public final class AuthConfi
  
          // network authorizer
  
-         INetworkAuthorizer networkAuthorizer = 
authInstantiate(conf.network_authorizer, AllowAllNetworkAuthorizer.class);
 -        INetworkAuthorizer networkAuthorizer = 
authInstantiate(conf.network_authorizer, INetworkAuthorizer.class, 
AllowAllNetworkAuthorizer.class);
++        INetworkAuthorizer networkAuthorizer = 
authInstantiate(conf.network_authorizer,
++                                                               
INetworkAuthorizer.class,
++                                                               
AllowAllNetworkAuthorizer.class);
  
          if (networkAuthorizer.requireAuthorization() && 
!authenticator.requireAuthentication())
          {
@@@ -120,7 -121,7 +123,9 @@@
  
          // cidr authorizer
  
-         ICIDRAuthorizer cidrAuthorizer = 
authInstantiate(conf.cidr_authorizer, AllowAllCIDRAuthorizer.class);
 -        ICIDRAuthorizer cidrAuthorizer = 
authInstantiate(conf.cidr_authorizer, ICIDRAuthorizer.class, 
AllowAllCIDRAuthorizer.class);
++        ICIDRAuthorizer cidrAuthorizer = authInstantiate(conf.cidr_authorizer,
++                                                         
ICIDRAuthorizer.class,
++                                                         
AllowAllCIDRAuthorizer.class);
  
          if (cidrAuthorizer.requireAuthorization() && 
!authenticator.requireAuthentication())
          {
diff --cc src/java/org/apache/cassandra/db/StorageHook.java
index f5fdec6a56,0abd0f05ed..56938b441f
--- a/src/java/org/apache/cassandra/db/StorageHook.java
+++ b/src/java/org/apache/cassandra/db/StorageHook.java
@@@ -89,4 -89,4 +89,4 @@@ public interface StorageHoo
              }
          };
      }
--}
++}
diff --cc src/java/org/apache/cassandra/db/guardrails/ValueGenerator.java
index c01e18f679,d1c844c120..0a11734034
--- a/src/java/org/apache/cassandra/db/guardrails/ValueGenerator.java
+++ b/src/java/org/apache/cassandra/db/guardrails/ValueGenerator.java
@@@ -165,4 -168,4 +168,4 @@@ public abstract class ValueGenerator<VA
                                                      className, message), ex);
          }
      }
--}
++}
diff --cc src/java/org/apache/cassandra/db/marshal/TypeParser.java
index b2dc0202bc,e9195bea90..d3a20fc7ca
--- a/src/java/org/apache/cassandra/db/marshal/TypeParser.java
+++ b/src/java/org/apache/cassandra/db/marshal/TypeParser.java
@@@ -463,10 -462,22 +462,9 @@@ public class TypeParse
          }
      }
  
 -    private static Class<? extends AbstractType<?>> 
getAbstractTypeClass(String compareWith) throws ConfigurationException
 -    {
 -        String className = compareWith.contains(".") ? compareWith : 
"org.apache.cassandra.db.marshal." + compareWith;
 -        // Defer class initialization until after confirming this is an 
AbstractType. The static instance field
 -        // access or getInstance(TypeParser) invocation below performs the 
initialization for valid types.
 -        @SuppressWarnings("unchecked")
 -        Class<? extends AbstractType<?>> typeClass =
 -            (Class<? extends AbstractType<?>>) 
FBUtilities.classForNameWithoutInitialization(className,
 -                                                                              
               "abstract-type",
 -                                                                              
               AbstractType.class);
 -        return typeClass;
 -    }
 -
      private static AbstractType<?> getAbstractType(String compareWith, 
TypeParser parser) throws SyntaxException, ConfigurationException
      {
-         String className = compareWith.contains(".") ? compareWith : 
"org.apache.cassandra.db.marshal." + compareWith;
-         Class<? extends AbstractType<?>> typeClass = 
FBUtilities.<AbstractType<?>>classForName(className, "abstract-type");
+         Class<? extends AbstractType<?>> typeClass = 
getAbstractTypeClass(compareWith);
          if (PseudoUtf8Type.class.isAssignableFrom(typeClass))
          {
              if (StorageService.instance.isDaemonSetupCompleted())
@@@ -491,6 -502,6 +489,19 @@@
          }
      }
  
++    private static Class<? extends AbstractType<?>> 
getAbstractTypeClass(String compareWith) throws ConfigurationException
++    {
++        String className = compareWith.contains(".") ? compareWith : 
"org.apache.cassandra.db.marshal." + compareWith;
++        // Defer class initialization until after confirming this is an 
AbstractType. The static instance field
++        // access or getInstance(TypeParser) invocation below performs the 
initialization for valid types.
++        @SuppressWarnings("unchecked")
++        Class<? extends AbstractType<?>> typeClass =
++            (Class<? extends AbstractType<?>>) 
FBUtilities.classForNameWithoutInitialization(className,
++                                                                              
               "abstract-type",
++                                                                              
               AbstractType.class);
++        return typeClass;
++    }
++
      private static AbstractType<?> getRawAbstractType(Class<? extends 
AbstractType<?>> typeClass) throws ConfigurationException
      {
          try
diff --cc src/java/org/apache/cassandra/diag/DiagnosticEventPersistence.java
index 81820382f3,ccd04cef3f..b1e8df8783
--- a/src/java/org/apache/cassandra/diag/DiagnosticEventPersistence.java
+++ b/src/java/org/apache/cassandra/diag/DiagnosticEventPersistence.java
@@@ -135,7 -136,8 +136,7 @@@ public final class DiagnosticEventPersi
          if (!eventClazz.startsWith("org.apache.cassandra."))
              throw new RuntimeException("Not a Cassandra event class: " + 
eventClazz);
  
-         Class<DiagnosticEvent> clazz = (Class<DiagnosticEvent>) 
Class.forName(eventClazz);
 -        // Load without initialization so the type can be verified before the 
class's static initializer runs.
+         Class<?> clazz = Class.forName(eventClazz, false, 
DiagnosticEventPersistence.class.getClassLoader());
  
          if (!(DiagnosticEvent.class.isAssignableFrom(clazz)))
              throw new InvalidClassException("Event class must be of type 
DiagnosticEvent");
diff --cc src/java/org/apache/cassandra/index/sasi/conf/IndexMode.java
index c827b2b820,2c6204c910..339a2cae5d
--- a/src/java/org/apache/cassandra/index/sasi/conf/IndexMode.java
+++ b/src/java/org/apache/cassandra/index/sasi/conf/IndexMode.java
@@@ -99,16 -100,9 +100,9 @@@ public class IndexMod
          // validate that a valid analyzer class was provided if specified
          if (indexOptions.containsKey(INDEX_ANALYZER_CLASS_OPTION))
          {
-             Class<?> analyzerClass;
-             try
-             {
-                 analyzerClass = 
Class.forName(indexOptions.get(INDEX_ANALYZER_CLASS_OPTION));
-             }
-             catch (ClassNotFoundException e)
-             {
-                 throw new ConfigurationException(String.format("Invalid 
analyzer class option specified [%s]",
-                                                                
indexOptions.get(INDEX_ANALYZER_CLASS_OPTION)));
-             }
+             Class<? extends AbstractAnalyzer> analyzerClass = 
FBUtilities.classForNameWithoutInitialization(indexOptions.get(INDEX_ANALYZER_CLASS_OPTION),
 -                                                                              
                             "analyzer",
 -                                                                              
                             AbstractAnalyzer.class);
++                                                                              
                              "analyzer",
++                                                                              
                              AbstractAnalyzer.class);
  
              AbstractAnalyzer analyzer;
              try
diff --cc src/java/org/apache/cassandra/tcm/extensions/ExtensionKey.java
index 976e327951,e1736d13d3..2165fceddd
--- a/src/java/org/apache/cassandra/tcm/extensions/ExtensionKey.java
+++ b/src/java/org/apache/cassandra/tcm/extensions/ExtensionKey.java
@@@ -68,4 -70,4 +70,3 @@@ public class ExtensionKey<V, K extends 
          }
      }
  }
--
diff --cc src/java/org/apache/cassandra/utils/FBUtilities.java
index a0ec48390c,f71947e153..8bf13f9ea5
--- a/src/java/org/apache/cassandra/utils/FBUtilities.java
+++ b/src/java/org/apache/cassandra/utils/FBUtilities.java
@@@ -734,27 -739,9 +740,30 @@@ public class FBUtilitie
          }
      }
  
 +    public static AbstractCompressionProvider newCompressionProvider(String 
className) throws ConfigurationException
 +    {
 +        try
 +        {
 +            if (!className.contains("."))
 +                className = "org.apache.cassandra.io.compress." + className;
 +
-             Class<?> compressionProviderClass = 
FBUtilities.classForName(className, "compression service provider");
-             return (AbstractCompressionProvider) 
compressionProviderClass.getConstructor().newInstance();
++            Class<? extends AbstractCompressionProvider> 
compressionProviderClass =
++                FBUtilities.classForNameWithoutInitialization(className, 
"compression service provider", AbstractCompressionProvider.class);
++            return compressionProviderClass.getConstructor().newInstance();
 +        }
 +        catch (ConfigurationException e)
 +        {
 +            throw e;
 +        }
 +        catch (Exception e)
 +        {
 +            throw new ConfigurationException(String.format("Unable to create 
an instance of the compression service provider for %s", className), e);
 +        }
 +    }
 +
      /**
+      * Loads and initializes a class.
+      *
       * @return The Class for the given name.
       * @param classname Fully qualified classname.
       * @param readable Descriptive noun for the role the class plays.
@@@ -1496,4 -1562,4 +1584,4 @@@
          });
          return adapter;
      }
--}
++}
diff --cc test/unit/org/apache/cassandra/config/DatabaseDescriptorTest.java
index 5796c4032f,c54d727ea8..6f80a3dc6c
--- a/test/unit/org/apache/cassandra/config/DatabaseDescriptorTest.java
+++ b/test/unit/org/apache/cassandra/config/DatabaseDescriptorTest.java
@@@ -133,6 -155,6 +136,25 @@@ public class DatabaseDescriptorTes
          }
      }
  
++    @Test
++    public void testCreateEndpointSnitchWrongTypeRejectedWithoutInitializing()
++    {
++        
ClassLoadingTestSupport.assertNotInitialized(ClassLoadingTestNonAssignable.class);
++
++        assertThatThrownBy(() -> 
DatabaseDescriptor.createEndpointSnitch(ClassLoadingTestNonAssignable.class.getName()))
++        .isInstanceOf(ConfigurationException.class)
++        .hasMessageContaining("must extend or implement " + 
IEndpointSnitch.class.getName());
++
++        
assertThat(ClassLoadingTestSupport.wasInitialized(ClassLoadingTestNonAssignable.class)).isFalse();
++    }
++
++    @Test
++    public void testCreateEndpointSnitchValidClassResolves()
++    {
++        IEndpointSnitch snitch = 
DatabaseDescriptor.createEndpointSnitch("SimpleSnitch");
++        assertThat(snitch).isNotNull();
++    }
++
      @Test
      public void testRpcInterface()
      {
diff --cc test/unit/org/apache/cassandra/config/ParameterizedClassTest.java
index 732812aefd,1ceaa734fa..4794d3de80
--- a/test/unit/org/apache/cassandra/config/ParameterizedClassTest.java
+++ b/test/unit/org/apache/cassandra/config/ParameterizedClassTest.java
@@@ -71,31 -74,76 +74,76 @@@ public class ParameterizedClassTes
          assertNotNull(instance);
      }
  
 -    @Test
 -    public void testNewInstanceWithValidConstructorsFavorsMapConstructor()
 -    {
 -        ParameterizedClass parameterizedClass = new 
ParameterizedClass(ParameterizedClassExample.class.getName());
 -        ParameterizedClassExample instance = 
ParameterizedClass.newInstance(parameterizedClass, null);
 -        assertNotNull(instance);
 -    }
 -
 -    @Test
 -    public void 
testNewInstanceWithValidConstructorsUsingNullParamtersFavorsMapConstructor()
 -    {
 -        ParameterizedClass parameterizedClass = new 
ParameterizedClass(ParameterizedClassExample.class.getName());
 -        parameterizedClass.parameters = null;
 -
 -        ParameterizedClassExample instance = 
ParameterizedClass.newInstance(parameterizedClass, null);
 -        assertNotNull(instance);
 -    }
 -
 -    @Test
 -    public void 
testNewInstanceWithConstructorExceptionPreservesOriginalFailure()
 -    {
 -        assertThatThrownBy(() -> ParameterizedClass.newInstance(new 
ParameterizedClass(ParameterizedClassExample.class.getName(),
 -                                                                              
         Map.of("fail", "true")), null))
 -        .hasMessageStartingWith("Failed to instantiate class")
 -        .hasMessageContaining("Simulated failure")
 -        .isInstanceOf(ConfigurationException.class);
 -    }
 -
+     @Test
+     public void testTypedNewInstanceRejectsWithoutInitializing()
+     {
+         
ClassLoadingTestSupport.assertNotInitialized(ClassLoadingTestNonAssignable.class);
+         ParameterizedClass parameterizedClass = new 
ParameterizedClass(ClassLoadingTestNonAssignable.class.getName());
+ 
+         assertThatThrownBy(() -> 
ParameterizedClass.newInstance(parameterizedClass, null, Runnable.class))
+         .hasMessageContaining("must extend or implement " + 
Runnable.class.getName())
+         .isInstanceOf(ConfigurationException.class);
+         
assertThat(ClassLoadingTestSupport.wasInitialized(ClassLoadingTestNonAssignable.class)).isFalse();
+     }
+ 
+     @Test
+     public void testTwoArgNewInstanceDoesNotInitializeAtLoadTime()
+     {
+         // The 2-arg overload performs no type check, but it must still load 
without running <clinit>.
+         // ClassLoadingTestNonAssignable has no usable constructor, so 
instantiation fails -- but only after the
+         // class has been loaded; loading must not have run its static 
initializer.
+         
ClassLoadingTestSupport.assertNotInitialized(ClassLoadingTestNonAssignable.class);
+         ParameterizedClass parameterizedClass = new 
ParameterizedClass(ClassLoadingTestNonAssignable.class.getName());
+ 
+         assertThatThrownBy(() -> 
ParameterizedClass.newInstance(parameterizedClass, null))
+         .isInstanceOf(ConfigurationException.class);
+         
assertThat(ClassLoadingTestSupport.wasInitialized(ClassLoadingTestNonAssignable.class)).isFalse();
+     }
+ 
+     @Test
+     public void 
testNewInstanceSearchPackageFallThroughDoesNotAbortOnWrongType()
+     {
+         // org.apache.cassandra.utils.ClassLoadingSearchProbe (wrong type, 
not a Runnable) resolves under the earlier
+         // search package, while 
org.apache.cassandra.config.ClassLoadingSearchProbe (correct type, a Runnable)
+         // resolves under the later one. A wrong-type match under the earlier 
package must not abort the search.
+         
ClassLoadingTestSupport.assertNotInitialized(org.apache.cassandra.utils.ClassLoadingSearchProbe.class);
+         ParameterizedClass parameterizedClass = new 
ParameterizedClass("ClassLoadingSearchProbe");
+ 
+         Runnable instance = ParameterizedClass.newInstance(parameterizedClass,
+                                                            
List.of("org.apache.cassandra.utils",
+                                                                    
"org.apache.cassandra.config"),
+                                                            Runnable.class);
+         assertNotNull(instance);
+         
assertThat(instance).isInstanceOf(org.apache.cassandra.config.ClassLoadingSearchProbe.class);
+         // The wrong-type class that was probed under the earlier package 
must not have been initialized.
+         
assertThat(ClassLoadingTestSupport.wasInitialized(org.apache.cassandra.utils.ClassLoadingSearchProbe.class)).isFalse();
+     }
++
 +    @Test
 +    public void testNewInstanceWithValidConstructorsFavorsMapConstructor()
 +    {
 +        ParameterizedClass parameterizedClass = new 
ParameterizedClass(ParameterizedClassExample.class.getName());
 +        ParameterizedClassExample instance = 
ParameterizedClass.newInstance(parameterizedClass, null);
 +        assertNotNull(instance);
 +    }
 +
 +    @Test
 +    public void 
testNewInstanceWithValidConstructorsUsingNullParamtersFavorsMapConstructor()
 +    {
 +        ParameterizedClass parameterizedClass = new 
ParameterizedClass(ParameterizedClassExample.class.getName());
 +        parameterizedClass.parameters = null;
 +
 +        ParameterizedClassExample instance = 
ParameterizedClass.newInstance(parameterizedClass, null);
 +        assertNotNull(instance);
 +    }
 +
 +    @Test
 +    public void 
testNewInstanceWithConstructorExceptionPreservesOriginalFailure()
 +    {
 +        assertThatThrownBy(() -> ParameterizedClass.newInstance(new 
ParameterizedClass(ParameterizedClassExample.class.getName(),
 +                                                                              
         Map.of("fail", "true")), null))
 +        .hasMessageStartingWith("Failed to instantiate class")
 +        .hasMessageContaining("Simulated failure")
 +        .isInstanceOf(ConfigurationException.class);
 +    }
  }
diff --cc test/unit/org/apache/cassandra/db/marshal/TypeParserTest.java
index e9b2e3c764,1ad9f203f1..fa50c1d02a
--- a/test/unit/org/apache/cassandra/db/marshal/TypeParserTest.java
+++ b/test/unit/org/apache/cassandra/db/marshal/TypeParserTest.java
@@@ -102,6 -116,6 +105,17 @@@ public class TypeParserTes
          catch (SyntaxException e) {}
      }
  
++    @Test
++    public void testRejectsNonAbstractTypeWithoutInitializing() throws 
SyntaxException
++    {
++        
ClassLoadingTestSupport.assertNotInitialized(ClassLoadingTestNonAssignable.class);
++        assertThatThrownBy(() -> 
TypeParser.parse(ClassLoadingTestNonAssignable.class.getName()))
++        .isInstanceOf(ConfigurationException.class)
++        .hasMessageContaining("must extend or implement " + 
AbstractType.class.getName());
++
++        
assertFalse(ClassLoadingTestSupport.wasInitialized(ClassLoadingTestNonAssignable.class));
++    }
++
      @Test
      public void testParsePartitionerOrder() throws ConfigurationException, 
SyntaxException
      {
diff --cc 
test/unit/org/apache/cassandra/repair/autorepair/AutoRepairConfigTest.java
index 188e0da30f,2b00cc7c33..4dbdf24efb
--- a/test/unit/org/apache/cassandra/repair/autorepair/AutoRepairConfigTest.java
+++ b/test/unit/org/apache/cassandra/repair/autorepair/AutoRepairConfigTest.java
@@@ -68,6 -72,6 +72,19 @@@ public class AutoRepairConfigTest exten
          AutoRepair.SLEEP_IF_REPAIR_FINISHES_QUICKLY = new 
DurationSpec.IntSecondsBound("0s");
      }
  
++    @Test
++    public void testTokenRangeSplitterWrongTypeRejectedWithoutInitializing()
++    {
++        
ClassLoadingTestSupport.assertNotInitialized(ClassLoadingTestNonAssignable.class);
++
++        ParameterizedClass pc = new 
ParameterizedClass(ClassLoadingTestNonAssignable.class.getName(), 
Collections.emptyMap());
++        assertThatThrownBy(() -> 
AutoRepairConfig.newAutoRepairTokenRangeSplitter(repairType, pc))
++        .isInstanceOf(ConfigurationException.class)
++        .hasStackTraceContaining("must extend or implement " + 
IAutoRepairTokenRangeSplitter.class.getName());
++
++        
assertThat(ClassLoadingTestSupport.wasInitialized(ClassLoadingTestNonAssignable.class)).isFalse();
++    }
++
      @Test
      public void autoRepairConfigDefaultsAreNotNull()
      {
diff --cc test/unit/org/apache/cassandra/schema/CompressionParamsTest.java
index 05b98e86aa,da39194a39..e4a696e663
--- a/test/unit/org/apache/cassandra/schema/CompressionParamsTest.java
+++ b/test/unit/org/apache/cassandra/schema/CompressionParamsTest.java
@@@ -30,10 -24,13 +30,14 @@@ import org.junit.BeforeClass
  import org.junit.Test;
  
  import org.apache.cassandra.config.DatabaseDescriptor;
+ import org.apache.cassandra.exceptions.ConfigurationException;
 +import org.apache.cassandra.io.compress.BufferType;
  import org.apache.cassandra.io.compress.ICompressor;
+ import org.apache.cassandra.utils.ClassLoadingTestNonAssignable;
+ import org.apache.cassandra.utils.ClassLoadingTestSupport;
  
  import static org.assertj.core.api.Assertions.assertThat;
+ import static org.assertj.core.api.Assertions.assertThatThrownBy;
  
  public class CompressionParamsTest
  {
@@@ -82,66 -79,16 +86,79 @@@
          .isFalse();
      }
  
 +    @Test
 +    public void testNonBuiltInCompressorClassResolvesViaDefaultProvider()
 +    {
 +        // A 3rd-party / user-supplied compressor class is not in 
CompressorType, so the registry
 +        // has no entry for it. CompressionParams.createCompressor must fall 
back to the default
 +        // provider, which reflectively invokes the class's static 
create(Map) factory - otherwise
 +        // every existing pluggable compressor outside 
org.apache.cassandra.io.compress NPEs.
 +        Map<String, String> opts = ImmutableMap.of("class", 
CustomTestCompressor.class.getName(),
 +                                                   "chunk_length_in_kb", "8");
 +        CompressionParams params = CompressionParams.fromMap(opts);
 +
 +        
assertThat(params.getSstableCompressor()).isInstanceOf(CustomTestCompressor.class);
 +        assertThat(params.klass()).isEqualTo(CustomTestCompressor.class);
 +    }
 +
+     @Test
+     public void testRejectsNonCompressorWithoutInitializing()
+     {
+         
ClassLoadingTestSupport.assertNotInitialized(ClassLoadingTestNonAssignable.class);
+ 
+         assertThatThrownBy(() -> 
CompressionParams.fromMap(Collections.singletonMap(CompressionParams.CLASS,
+                                                                               
      ClassLoadingTestNonAssignable.class.getName())))
+         .isInstanceOf(ConfigurationException.class)
+         .hasMessageContaining("must extend or implement " + 
ICompressor.class.getName());
+ 
+         
assertThat(ClassLoadingTestSupport.wasInitialized(ClassLoadingTestNonAssignable.class)).isFalse();
+     }
++
 +    public static class CustomTestCompressor implements ICompressor
 +    {
 +        public static CustomTestCompressor create(Map<String, String> options)
 +        {
 +            return new CustomTestCompressor();
 +        }
 +
 +        @Override
 +        public int initialCompressedBufferLength(int chunkLength)
 +        {
 +            return chunkLength;
 +        }
 +
 +        @Override
 +        public int uncompress(byte[] in, int io, int il, byte[] out, int oo) 
throws IOException
 +        {
 +            return 0;
 +        }
 +
 +        @Override
 +        public void compress(ByteBuffer in, ByteBuffer out) throws IOException
 +        {
 +        }
 +
 +        @Override
 +        public void uncompress(ByteBuffer in, ByteBuffer out) throws 
IOException
 +        {
 +        }
 +
 +        @Override
 +        public BufferType preferredBufferType()
 +        {
 +            return BufferType.OFF_HEAP;
 +        }
 +
 +        @Override
 +        public boolean supports(BufferType bufferType)
 +        {
 +            return true;
 +        }
 +
 +        @Override
 +        public Set<String> supportedOptions()
 +        {
 +            return Collections.emptySet();
 +        }
 +    }
  }
diff --cc test/unit/org/apache/cassandra/schema/MemtableParamsTest.java
index 75094ef5d1,1e8c85feb8..0e21b050d9
--- a/test/unit/org/apache/cassandra/schema/MemtableParamsTest.java
+++ b/test/unit/org/apache/cassandra/schema/MemtableParamsTest.java
@@@ -52,6 -56,46 +56,46 @@@ public class MemtableParamsTes
          assertEquals(ImmutableMap.of("default", DEFAULT), map);
      }
  
+     @Test
+     public void testInvalidFactoryMethodDoesNotInitializeClass() throws 
Exception
+     {
+         
ClassLoadingTestSupport.assertNotInitialized(MemtableFactoryInvalidReturnType.class);
+ 
+         assertThatThrownBy(() -> 
getMemtableFactory(MemtableFactoryInvalidReturnType.class))
+         .isInstanceOf(ConfigurationException.class)
+         .hasStackTraceContaining("must return");
+ 
+         
assertThat(ClassLoadingTestSupport.wasInitialized(MemtableFactoryInvalidReturnType.class)).isFalse();
+     }
+ 
+     @Test
+     public void testInvalidFactoryFieldDoesNotInitializeClass() throws 
Exception
+     {
+         
ClassLoadingTestSupport.assertNotInitialized(MemtableFactoryInvalidFieldType.class);
+ 
+         assertThatThrownBy(() -> 
getMemtableFactory(MemtableFactoryInvalidFieldType.class))
+         .isInstanceOf(ConfigurationException.class)
+         .hasStackTraceContaining("must be of type");
+ 
+         
assertThat(ClassLoadingTestSupport.wasInitialized(MemtableFactoryInvalidFieldType.class)).isFalse();
+     }
+ 
+     private static void getMemtableFactory(Class<?> memtableClass) throws 
Exception
+     {
+         Method method = 
MemtableParams.class.getDeclaredMethod("getMemtableFactory", 
ParameterizedClass.class);
+         method.setAccessible(true);
+         try
+         {
 -            method.invoke(null, new 
ParameterizedClass(memtableClass.getName(), null));
++            method.invoke(null, new 
ParameterizedClass(memtableClass.getName()));
+         }
+         catch (InvocationTargetException e)
+         {
+             if (e.getCause() instanceof ConfigurationException)
+                 throw (ConfigurationException) e.getCause();
+             throw e;
+         }
+     }
+ 
      @Test
      public void testDefaultRemapped()
      {
diff --cc test/unit/org/apache/cassandra/security/CipherFactoryTest.java
index cb12d58558,ac36250479..4115af0575
--- a/test/unit/org/apache/cassandra/security/CipherFactoryTest.java
+++ b/test/unit/org/apache/cassandra/security/CipherFactoryTest.java
@@@ -121,6 -143,6 +126,23 @@@ public class CipherFactoryTes
          Assert.assertFalse(c1 == c2);
      }
  
++    @Test
++    public void keyProviderWrongTypeRejectedWithoutInitializing()
++    {
++        
ClassLoadingTestSupport.assertNotInitialized(ClassLoadingTestNonAssignable.class);
++
++        TransparentDataEncryptionOptions options = 
EncryptionContextGenerator.createEncryptionOptions();
++        options.key_provider.class_name = 
ClassLoadingTestNonAssignable.class.getName();
++
++        // CipherFactory wraps the load failure; the cause is the type-check 
ConfigurationException
++        assertThatThrownBy(() -> new CipherFactory(options))
++        .isInstanceOf(RuntimeException.class)
++        .hasCauseInstanceOf(ConfigurationException.class)
++        .hasStackTraceContaining("must extend or implement " + 
KeyProvider.class.getName());
++
++        
assertThat(ClassLoadingTestSupport.wasInitialized(ClassLoadingTestNonAssignable.class)).isFalse();
++    }
++
      @Test(expected = AssertionError.class)
      public void getDecryptor_NullIv() throws IOException
      {
diff --cc test/unit/org/apache/cassandra/triggers/TriggerExecutorTest.java
index be1c617a7b,8068df4913..b7430d2d06
--- a/test/unit/org/apache/cassandra/triggers/TriggerExecutorTest.java
+++ b/test/unit/org/apache/cassandra/triggers/TriggerExecutorTest.java
@@@ -114,6 -116,6 +116,18 @@@ public class TriggerExecutorTes
          .withMessageContaining("Trigger class NotExistedTriggerClass couldn't 
be found.");
      }
  
++    @Test
++    public void nonTriggerClassRejectedWithoutInitializing()
++    {
++        ClassLoadingTestSupport.assertNotInitialized(NonTrigger.class);
++
++        assertThatExceptionOfType(ConfigurationException.class)
++        .isThrownBy(() -> 
TriggerExecutor.instance.loadTriggerClass(NonTrigger.class.getName()))
++        .withMessageContaining("must extend or implement " + 
ITrigger.class.getName());
++
++        
assertThat(ClassLoadingTestSupport.wasInitialized(NonTrigger.class)).isFalse();
++    }
++
      @Test
      public void noTriggerMutations() throws ConfigurationException, 
InvalidRequestException
      {
@@@ -326,6 -328,6 +340,18 @@@
          }
      }
  
++    public static class NonTrigger
++    {
++        static
++        {
++            ClassLoadingTestSupport.markInitialized(NonTrigger.class);
++        }
++
++        public NonTrigger()
++        {
++        }
++    }
++
      public static class SameKeySameCfTrigger implements ITrigger
      {
          public Collection<Mutation> augment(Partition partition)
diff --cc test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
index c9451730f3,64df0a0541..00e94230a0
--- a/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
+++ b/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
@@@ -59,9 -61,14 +60,14 @@@ import org.apache.cassandra.dht.LocalPa
  import org.apache.cassandra.dht.Murmur3Partitioner;
  import org.apache.cassandra.dht.OrderPreservingPartitioner;
  import org.apache.cassandra.dht.RandomPartitioner;
+ import org.apache.cassandra.exceptions.ConfigurationException;
++import org.apache.cassandra.io.compress.AbstractCompressionProvider;
+ import org.apache.cassandra.security.AbstractCryptoProvider;
+ import org.apache.cassandra.security.ISslContextFactory;
  
  import static org.assertj.core.api.Assertions.assertThat;
+ import static org.assertj.core.api.Assertions.assertThatThrownBy;
  import static org.junit.Assert.assertEquals;
 -import static org.junit.Assert.assertFalse;
  import static org.junit.Assert.fail;
  
  public class FBUtilitiesTest
@@@ -69,6 -76,73 +75,84 @@@
  
      public static final Logger LOGGER = 
LoggerFactory.getLogger(FBUtilitiesTest.class);
  
+     @Test
+     public void testTypedClassForNameRejectsWithoutInitializing()
+     {
+         
ClassLoadingTestSupport.assertNotInitialized(ClassLoadingTestNonAssignable.class);
+         assertThatThrownBy(() -> 
FBUtilities.classForNameWithoutInitialization(ClassLoadingTestNonAssignable.class.getName(),
 "test class", Runnable.class))
+         .isInstanceOf(ConfigurationException.class)
+         .hasMessageContaining("must extend or implement " + 
Runnable.class.getName());
+ 
 -        
assertFalse(ClassLoadingTestSupport.wasInitialized(ClassLoadingTestNonAssignable.class));
++        
assertThat(ClassLoadingTestSupport.wasInitialized(ClassLoadingTestNonAssignable.class)).isFalse();
+     }
+ 
+     @Test
+     public void testTypedConstructRejectsWithoutInitializing()
+     {
+         
ClassLoadingTestSupport.assertNotInitialized(ClassLoadingTestNonAssignable.class);
+         assertThatThrownBy(() -> 
FBUtilities.construct(ClassLoadingTestNonAssignable.class.getName(), "test 
class", Runnable.class))
+         .isInstanceOf(ConfigurationException.class)
+         .hasMessageContaining("must extend or implement " + 
Runnable.class.getName());
+ 
 -        
assertFalse(ClassLoadingTestSupport.wasInitialized(ClassLoadingTestNonAssignable.class));
++        
assertThat(ClassLoadingTestSupport.wasInitialized(ClassLoadingTestNonAssignable.class)).isFalse();
+     }
+ 
+     @Test
+     public void testTypedInstanceOrConstructRejectsWithoutInitializing()
+     {
+         
ClassLoadingTestSupport.assertNotInitialized(ClassLoadingTestNonAssignable.class);
+         assertThatThrownBy(() -> 
FBUtilities.instanceOrConstruct(ClassLoadingTestNonAssignable.class.getName(), 
"test class", Runnable.class))
+         .isInstanceOf(ConfigurationException.class)
+         .hasMessageContaining("must extend or implement " + 
Runnable.class.getName());
+ 
 -        
assertFalse(ClassLoadingTestSupport.wasInitialized(ClassLoadingTestNonAssignable.class));
++        
assertThat(ClassLoadingTestSupport.wasInitialized(ClassLoadingTestNonAssignable.class)).isFalse();
+     }
+ 
+     @Test
+     public void testNewAuditLoggerRejectsWrongTypeWithoutInitializing()
+     {
+         
ClassLoadingTestSupport.assertNotInitialized(ClassLoadingTestNonAssignable.class);
 -        assertThatThrownBy(() -> 
FBUtilities.newAuditLogger(ClassLoadingTestNonAssignable.class.getName(), 
Collections.emptyMap()))
++        assertThatThrownBy(() -> 
FBUtilities.newAuditLogger(ClassLoadingTestNonAssignable.class.getName(), 
Map.of()))
+         .isInstanceOf(ConfigurationException.class)
+         .hasRootCauseInstanceOf(ConfigurationException.class)
+         .hasStackTraceContaining("must extend or implement " + 
IAuditLogger.class.getName());
+ 
 -        
assertFalse(ClassLoadingTestSupport.wasInitialized(ClassLoadingTestNonAssignable.class));
++        
assertThat(ClassLoadingTestSupport.wasInitialized(ClassLoadingTestNonAssignable.class)).isFalse();
+     }
+ 
+     @Test
+     public void testNewSslContextFactoryRejectsWrongTypeWithoutInitializing()
+     {
+         
ClassLoadingTestSupport.assertNotInitialized(ClassLoadingTestNonAssignable.class);
 -        assertThatThrownBy(() -> 
FBUtilities.newSslContextFactory(ClassLoadingTestNonAssignable.class.getName(), 
Collections.emptyMap()))
++        assertThatThrownBy(() -> 
FBUtilities.newSslContextFactory(ClassLoadingTestNonAssignable.class.getName(), 
Map.of()))
+         .isInstanceOf(ConfigurationException.class)
+         .hasStackTraceContaining("must extend or implement " + 
ISslContextFactory.class.getName());
+ 
 -        
assertFalse(ClassLoadingTestSupport.wasInitialized(ClassLoadingTestNonAssignable.class));
++        
assertThat(ClassLoadingTestSupport.wasInitialized(ClassLoadingTestNonAssignable.class)).isFalse();
+     }
+ 
+     @Test
+     public void testNewCryptoProviderRejectsWrongTypeWithoutInitializing()
+     {
+         
ClassLoadingTestSupport.assertNotInitialized(ClassLoadingTestNonAssignable.class);
 -        assertThatThrownBy(() -> 
FBUtilities.newCryptoProvider(ClassLoadingTestNonAssignable.class.getName(), 
Collections.emptyMap()))
++        assertThatThrownBy(() -> 
FBUtilities.newCryptoProvider(ClassLoadingTestNonAssignable.class.getName(), 
Map.of()))
+         .isInstanceOf(ConfigurationException.class)
+         .hasMessageContaining("must extend or implement " + 
AbstractCryptoProvider.class.getName());
+ 
 -        
assertFalse(ClassLoadingTestSupport.wasInitialized(ClassLoadingTestNonAssignable.class));
++        
assertThat(ClassLoadingTestSupport.wasInitialized(ClassLoadingTestNonAssignable.class)).isFalse();
++    }
++
++    @Test
++    public void 
testNewCompressionProviderRejectsWrongTypeWithoutInitializing()
++    {
++        
ClassLoadingTestSupport.assertNotInitialized(ClassLoadingTestNonAssignable.class);
++        assertThatThrownBy(() -> 
FBUtilities.newCompressionProvider(ClassLoadingTestNonAssignable.class.getName()))
++        .isInstanceOf(ConfigurationException.class)
++        .hasStackTraceContaining("must extend or implement " + 
AbstractCompressionProvider.class.getName());
++
++        
assertThat(ClassLoadingTestSupport.wasInitialized(ClassLoadingTestNonAssignable.class)).isFalse();
+     }
+ 
      @Test
      public void testCompareByteSubArrays()
      {


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


Reply via email to