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]
