This is an automated email from the ASF dual-hosted git repository. ifesdjeen pushed a commit to branch dev in repository https://gitbox.apache.org/repos/asf/cassandra-simulator.git
commit c694efb9ba66674c63ac6193e6766b7da41573bd Author: Alex Petrov <[email protected]> AuthorDate: Mon Jul 13 14:26:14 2026 +0000 Reject Simulator outside system classloader buildSchedule() assumes simulator-shared classes come from the system classloader. Fail fast when Simulator is loaded from a different host loader so unsupported classloader setups do not silently split shared static state. --- .../org/apache/cassandra/simulator/Simulator.java | 5 +- .../SimulatorHostClassLoaderReproTest.java | 144 +++++++++++++++++++++ 2 files changed, 148 insertions(+), 1 deletion(-) diff --git a/simulator-core/src/main/java/org/apache/cassandra/simulator/Simulator.java b/simulator-core/src/main/java/org/apache/cassandra/simulator/Simulator.java index feb5d6a..6fb96b1 100644 --- a/simulator-core/src/main/java/org/apache/cassandra/simulator/Simulator.java +++ b/simulator-core/src/main/java/org/apache/cassandra/simulator/Simulator.java @@ -162,7 +162,10 @@ public class Simulator implements AutoCloseable { this.monitorDelayChance = monitorDelayChance; ClassLoader cl = getClass().getClassLoader(); - this.classLoader = cl != null ? cl : ClassLoader.getSystemClassLoader(); + ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); + if (cl != null && cl != systemClassLoader) + throw new IllegalStateException("Simulator must be loaded by the system classloader; found " + cl); + this.classLoader = cl != null ? cl : systemClassLoader; RandomSource.Default rnd = new RandomSource.Default(); rnd.reset(seed); diff --git a/simulator-core/src/test/java/org/apache/cassandra/simulator_test/SimulatorHostClassLoaderReproTest.java b/simulator-core/src/test/java/org/apache/cassandra/simulator_test/SimulatorHostClassLoaderReproTest.java new file mode 100644 index 0000000..a51cbc5 --- /dev/null +++ b/simulator-core/src/test/java/org/apache/cassandra/simulator_test/SimulatorHostClassLoaderReproTest.java @@ -0,0 +1,144 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.cassandra.simulator_test; + +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Verifies that Simulator rejects construction from a non-system host classloader. + * + * Expected behavior: constructing Simulator through a non-system URLClassLoader fails + * immediately with an IllegalStateException. + * + * Failure criterion: invoking Probe.constructSimulator() via a non-system URLClassLoader + * throws IllegalStateException with a message that mentions the system classloader + * requirement. + */ +public class SimulatorHostClassLoaderReproTest +{ + @Test + void simulatorConstructionFromNonSystemLoaderIsRejected() throws Exception + { + try (URLClassLoader loader = new ChildFirstURLClassLoader(runtimeClasspath(), ClassLoader.getPlatformClassLoader())) + { + Class<?> probeClass = Class.forName(Probe.class.getName(), true, loader); + Method probeMethod = probeClass.getMethod("constructSimulator"); + + // TRIGGER: run Simulator from a non-system host classloader. + assertNotSame(Probe.class, probeClass, "probe must be loaded through the custom host loader"); + assertSame(loader, probeClass.getClassLoader(), "probe must come from the custom host loader"); + + // HARNESS: reflectively invoke a child-loaded helper that constructs Simulator. + // ORACLE: this must fail fast with IllegalStateException instead of silently accepting + // an unsupported classloader configuration. + Throwable failure = invokeAndCapture(probeMethod); + assertEquals(IllegalStateException.class, failure.getClass()); + assertTrue(failure.getMessage().contains("Simulator must be loaded by the system classloader"), + "unexpected message: " + failure.getMessage()); + } + } + + public static final class Probe + { + public static void constructSimulator() + { + new org.apache.cassandra.simulator.Simulator(42L).close(); + } + } + + private static Throwable invokeAndCapture(Method probeMethod) throws Exception + { + try + { + probeMethod.invoke(null); + throw new AssertionError("expected simulator construction to fail"); + } + catch (InvocationTargetException e) + { + return e.getCause(); + } + } + + private static final class ChildFirstURLClassLoader extends URLClassLoader + { + private ChildFirstURLClassLoader(URL[] urls, ClassLoader parent) + { + super(urls, parent); + } + + @Override + protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException + { + synchronized (getClassLoadingLock(name)) + { + Class<?> loaded = findLoadedClass(name); + if (loaded == null && shouldLoadChildFirst(name)) + { + try + { + loaded = findClass(name); + } + catch (ClassNotFoundException ignored) + { + } + } + if (loaded == null) + loaded = super.loadClass(name, false); + if (resolve) + resolveClass(loaded); + return loaded; + } + } + + private boolean shouldLoadChildFirst(String name) + { + return name.startsWith("org.apache.cassandra.simulator.") + || name.startsWith("org.apache.cassandra.simulator_test."); + } + } + + private static URL[] runtimeClasspath() + { + return Arrays.stream(System.getProperty("java.class.path").split(File.pathSeparator)) + .map(path -> { + try + { + return new File(path).toURI().toURL(); + } + catch (MalformedURLException e) + { + throw new RuntimeException(e); + } + }) + .toArray(URL[]::new); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
