zentol commented on a change in pull request #15039: URL: https://github.com/apache/flink/pull/15039#discussion_r587719680
########## File path: flink-runtime/src/test/java/org/apache/flink/runtime/metrics/util/MetricUtilsTest.java ########## @@ -243,4 +247,84 @@ public MetricGroup addGroup(String name) { METRIC_GROUP_MEMORY, METRIC_GROUP_MANAGED_MEMORY))); } + + // --------------- utility methods and classes --------------- + + /** Intercept loading classes and define them based on found class resources. */ + private static class InterceptingClassLoader extends ClassLoader { + private final Set<String> interceptingClassNames = new HashSet<>(); + private final Map<String, Class<?>> interceptedClasses = new HashMap<>(); + + public InterceptingClassLoader( + Collection<String> interceptingClassNames, ClassLoader parent) { + super(parent); + this.interceptingClassNames.addAll(interceptingClassNames); + } + + @Override + protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { + Class<?> interceptedClass = interceptedClasses.get(name); + if (interceptedClass != null) { + return interceptedClass; + } + if (!interceptingClassNames.contains(name)) { + return super.loadClass(name, resolve); + } + String classFileResourceName = name.replace('.', '/') + ".class"; + try (InputStream inputStream = getResourceAsStream(classFileResourceName)) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + IOUtils.copyBytes(inputStream, outputStream, false); + byte[] bytecode = outputStream.toByteArray(); + Class<?> definedClass = + defineClass( + name, + bytecode, + 0, + bytecode.length, + getClass().getProtectionDomain()); + interceptedClasses.put(name, definedClass); + return definedClass; + } catch (Exception ex) { + throw new ClassNotFoundException(ex.getMessage(), ex); + } + } + } + + /** Define an new class using given class's name and bytecode. */ + @SuppressWarnings("unchecked") + private static <T> Class<T> redefineAsNewClass(Class<T> clazz) throws ClassNotFoundException { + InterceptingClassLoader classLoader = + new InterceptingClassLoader( + Collections.singleton(clazz.getName()), clazz.getClassLoader()); + Class<T> newClass = (Class<T>) classLoader.loadClass(clazz.getName(), true); + Assert.assertNotSame(clazz, newClass); + Assert.assertEquals(clazz.getName(), newClass.getName()); + return newClass; + } + + private static boolean hasMetaspaceMemoryPool() { + return ManagementFactory.getMemoryPoolMXBeans().stream() + .anyMatch(bean -> "Metaspace".equals(bean.getName())); + } + + /** Caller may choose to run multiple times for possible interference with other tests. */ + private void runUntilMetricChanged( + String name, int maxRuns, CheckedSupplier<Object> objectCreator, Gauge<Long> metric) + throws Exception { + maxRuns = Math.min(1, maxRuns); Review comment: ```suggestion maxRuns = Math.max(1, maxRuns); ``` ########## File path: flink-runtime/src/test/java/org/apache/flink/runtime/metrics/util/MetricUtilsTest.java ########## @@ -243,4 +247,84 @@ public MetricGroup addGroup(String name) { METRIC_GROUP_MEMORY, METRIC_GROUP_MANAGED_MEMORY))); } + + // --------------- utility methods and classes --------------- + + /** Intercept loading classes and define them based on found class resources. */ + private static class InterceptingClassLoader extends ClassLoader { + private final Set<String> interceptingClassNames = new HashSet<>(); + private final Map<String, Class<?>> interceptedClasses = new HashMap<>(); + + public InterceptingClassLoader( + Collection<String> interceptingClassNames, ClassLoader parent) { + super(parent); + this.interceptingClassNames.addAll(interceptingClassNames); + } + + @Override + protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { + Class<?> interceptedClass = interceptedClasses.get(name); + if (interceptedClass != null) { + return interceptedClass; + } + if (!interceptingClassNames.contains(name)) { + return super.loadClass(name, resolve); + } + String classFileResourceName = name.replace('.', '/') + ".class"; + try (InputStream inputStream = getResourceAsStream(classFileResourceName)) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + IOUtils.copyBytes(inputStream, outputStream, false); + byte[] bytecode = outputStream.toByteArray(); + Class<?> definedClass = + defineClass( + name, + bytecode, + 0, + bytecode.length, + getClass().getProtectionDomain()); + interceptedClasses.put(name, definedClass); + return definedClass; + } catch (Exception ex) { + throw new ClassNotFoundException(ex.getMessage(), ex); + } + } + } + + /** Define an new class using given class's name and bytecode. */ + @SuppressWarnings("unchecked") + private static <T> Class<T> redefineAsNewClass(Class<T> clazz) throws ClassNotFoundException { + InterceptingClassLoader classLoader = + new InterceptingClassLoader( + Collections.singleton(clazz.getName()), clazz.getClassLoader()); + Class<T> newClass = (Class<T>) classLoader.loadClass(clazz.getName(), true); Review comment: ```suggestion private static Class<?> redefineAsNewClass(Class<?> clazz) throws ClassNotFoundException { ClassLoader classLoader = new ChildFirstClassLoader( ClassLoaderUtils.getClasspathURLs(), clazz.getClassLoader(), new String[] {}, null); Class<?> newClass = classLoader.loadClass(clazz.getName()); ``` This seems to be quite stable without requiring this whole InterceptingClassLoader thing. ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org