result = fileManager.getAllBuffers();
- return result;
+ return fileManager.getAllBuffers();
}
}
-
/**
* Compile and load using a specific class loader and writer. The
* compilation result is cached against the loader for future calls.
@@ -241,7 +247,7 @@ public Class> loadFromJava(@NotNull ClassLoader classLoader,
MyJavaFileManager fileManager = fileManagerMap.get(classLoader);
if (fileManager == null) {
- StandardJavaFileManager standardJavaFileManager = s_compiler.getStandardFileManager(null, null, null);
+ StandardJavaFileManager standardJavaFileManager = currentCompiler().getStandardFileManager(null, null, null);
fileManager = getFileManager(standardJavaFileManager);
fileManagerMap.put(classLoader, fileManager);
}
diff --git a/src/main/java/net/openhft/compiler/CompilerUtils.java b/src/main/java/net/openhft/compiler/CompilerUtils.java
index 0dd4c6b..8e9e2ac 100644
--- a/src/main/java/net/openhft/compiler/CompilerUtils.java
+++ b/src/main/java/net/openhft/compiler/CompilerUtils.java
@@ -19,7 +19,10 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Objects;
@@ -41,10 +44,10 @@ public enum CompilerUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(CompilerUtils.class);
private static final Method DEFINE_CLASS_METHOD;
- private static final Charset UTF_8 = Charset.forName("UTF-8");
+ private static final Charset UTF_8 = StandardCharsets.UTF_8;
private static final String JAVA_CLASS_PATH = "java.class.path";
- static JavaCompiler s_compiler;
- static StandardJavaFileManager s_standardJavaFileManager;
+ static volatile JavaCompiler s_compiler;
+ static volatile StandardJavaFileManager s_standardJavaFileManager;
/*
* Use sun.misc.Unsafe to gain access to ClassLoader.defineClass. This allows
@@ -79,22 +82,36 @@ private static boolean isDebug() {
}
/**
- * Reinitialises the cached {@link JavaCompiler}. This method is not thread-safe
- * and callers must serialise access if used outside static initialisation.
+ * Reinitialises the cached {@link JavaCompiler}. This method synchronises
+ * on a dedicated lock to avoid racy lazy initialisation of static fields.
*
* @throws AssertionError if the compiler classes cannot be loaded.
*/
private static void reset() {
- s_compiler = ToolProvider.getSystemJavaCompiler();
- if (s_compiler == null) {
- try {
- Class> javacTool = Class.forName("com.sun.tools.javac.api.JavacTool");
- Method create = javacTool.getMethod("create");
- s_compiler = (JavaCompiler) create.invoke(null);
- } catch (Exception e) {
- throw new AssertionError(e);
+ synchronized (CompilerUtils.class) {
+ JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+ if (compiler == null) {
+ try {
+ Class> javacTool = Class.forName("com.sun.tools.javac.api.JavacTool");
+ Method create = javacTool.getMethod("create");
+ compiler = (JavaCompiler) create.invoke(null);
+ } catch (Exception e) {
+ throw new AssertionError(e);
+ }
}
+ s_compiler = compiler;
+ // Invalidate any cached file manager tied to the previous compiler.
+ s_standardJavaFileManager = null;
+ }
+ }
+
+ static JavaCompiler currentCompiler() {
+ JavaCompiler compiler = s_compiler;
+ if (compiler != null) {
+ return compiler;
}
+ reset();
+ return s_compiler;
}
/**
@@ -225,11 +242,7 @@ private static String readText(@NotNull String resourceName) throws IOException
@NotNull
private static String decodeUTF8(@NotNull byte[] bytes) {
- try {
- return new String(bytes, UTF_8.name());
- } catch (UnsupportedEncodingException e) {
- throw new AssertionError(e);
- }
+ return new String(bytes, UTF_8);
}
@Nullable
@@ -240,17 +253,13 @@ private static byte[] readBytes(@NotNull File file) {
if (len > Runtime.getRuntime().totalMemory() / 10)
throw new IllegalStateException("Attempted to read large file " + file + " was " + len + " bytes.");
byte[] bytes = new byte[(int) len];
- DataInputStream dis = null;
- try {
- dis = new DataInputStream(new FileInputStream(file));
+ try (DataInputStream dis = new DataInputStream(Files.newInputStream(file.toPath()))) {
dis.readFully(bytes);
+ return bytes;
} catch (IOException e) {
- close(dis);
LOGGER.warn("Unable to read {}", file, e);
throw new IllegalStateException("Unable to read file " + file, e);
}
-
- return bytes;
}
private static void close(@Nullable Closeable closeable) {
@@ -284,11 +293,7 @@ public static boolean writeText(@NotNull File file, @NotNull String text) {
*/
@NotNull
private static byte[] encodeUTF8(@NotNull String text) {
- try {
- return text.getBytes(UTF_8.name());
- } catch (UnsupportedEncodingException e) {
- throw new AssertionError(e);
- }
+ return text.getBytes(UTF_8);
}
/**
@@ -310,7 +315,9 @@ public static boolean writeBytes(@NotNull File file, @NotNull byte[] bytes) {
if (Arrays.equals(bytes, bytes2))
return false;
bak = new File(parentDir, file.getName() + ".bak");
- file.renameTo(bak);
+ if (!file.renameTo(bak)) {
+ LOGGER.debug("Unable to rename {} to backup {}", file, bak);
+ }
}
FileOutputStream fos = null;
@@ -318,13 +325,16 @@ public static boolean writeBytes(@NotNull File file, @NotNull byte[] bytes) {
fos = new FileOutputStream(file);
fos.write(bytes);
} catch (IOException e) {
- close(fos);
LOGGER.warn("Unable to write {} as {}", file, decodeUTF8(bytes), e);
- file.delete();
- if (bak != null)
- bak.renameTo(file);
+ if (file.exists() && !file.delete()) {
+ LOGGER.debug("Unable to delete {}", file);
+ }
+ if (bak != null && bak.exists() && !bak.renameTo(file)) {
+ LOGGER.debug("Unable to restore backup {} to {}", bak, file);
+ }
throw new IllegalStateException("Unable to write " + file, e);
} finally {
+ close(fos);
if (bak != null && bak.exists() && file.exists()) {
if (!bak.delete()) {
LOGGER.debug("Unable to delete backup {}", bak);
diff --git a/src/main/java/net/openhft/compiler/JavaSourceFromString.java b/src/main/java/net/openhft/compiler/JavaSourceFromString.java
index 49cf37f..9ced010 100644
--- a/src/main/java/net/openhft/compiler/JavaSourceFromString.java
+++ b/src/main/java/net/openhft/compiler/JavaSourceFromString.java
@@ -8,11 +8,11 @@
import javax.tools.SimpleJavaFileObject;
import java.net.URI;
-/*
- * An internal SimpleJavaFileObject implementation representing Java source
- * code provided as a String, allowing the Java compiler to read source
- * directly from memory. Example URI: string:///com/example/Hello.java. The
- * contents are expected to be UTF-8.
+/**
+ * {@link javax.tools.JavaFileObject} backed by a String of source code.
+ *
+ * Allows the JDK compiler to consume in-memory source via a synthetic URI such as
+ * {@code string:///com/example/Hello.java}, avoiding the need for temporary files.
*/
class JavaSourceFromString extends SimpleJavaFileObject {
/**
diff --git a/src/main/java/net/openhft/compiler/MyJavaFileManager.java b/src/main/java/net/openhft/compiler/MyJavaFileManager.java
index ba788b6..9141697 100644
--- a/src/main/java/net/openhft/compiler/MyJavaFileManager.java
+++ b/src/main/java/net/openhft/compiler/MyJavaFileManager.java
@@ -31,7 +31,7 @@
*/
public class MyJavaFileManager implements JavaFileManager {
private static final Logger LOG = LoggerFactory.getLogger(MyJavaFileManager.class);
- private final static Unsafe unsafe;
+ private static final Unsafe unsafe;
private static final long OVERRIDE_OFFSET;
// Unsafe sets AccessibleObject.override for speed and JDK-9+ compatibility
@@ -211,11 +211,11 @@ public Map getAllBuffers() {
} catch (InterruptedException t) {
Thread.currentThread().interrupt();
- LOG.warn("Interrupted while waiting for compilation result [class=" + e.getKey() + "]");
+ LOG.warn("Interrupted while waiting for compilation result [class={}]", e.getKey());
break;
} catch (ExecutionException | TimeoutException t) {
- LOG.warn("Failed to wait for compilation result [class=" + e.getKey() + "]", t);
+ LOG.warn("Failed to wait for compilation result [class={}]", e.getKey(), t);
continue;
}
diff --git a/src/main/java/net/openhft/compiler/package-info.java b/src/main/java/net/openhft/compiler/package-info.java
new file mode 100644
index 0000000..7e6d821
--- /dev/null
+++ b/src/main/java/net/openhft/compiler/package-info.java
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2013-2025 chronicle.software; SPDX-License-Identifier: Apache-2.0
+ */
+/**
+ * Runtime Java compilation utilities based on the standard {@code javax.tools} API.
+ *
+ * Classes here compile source strings and byte arrays on the fly, cache
+ * generated classes, and provide convenience wrappers for loading the results at
+ * runtime.
+ */
+package net.openhft.compiler;
diff --git a/src/test/java/eg/FooBarTee.java b/src/test/java/eg/FooBarTee.java
index 65123e1..6a40af2 100644
--- a/src/test/java/eg/FooBarTee.java
+++ b/src/test/java/eg/FooBarTee.java
@@ -8,24 +8,19 @@
import eg.components.TeeImpl;
public class FooBarTee {
- private final String name;
- private final TeeImpl tee;
- private final BarImpl bar;
- private final BarImpl copy;
- public Foo foo;
+ public final Foo foo;
public FooBarTee(String name) {
- this.name = name;
- tee = new TeeImpl("test");
+ TeeImpl tee = new TeeImpl("test");
- bar = new BarImpl(tee, 55);
+ BarImpl bar = new BarImpl(tee, 55);
- copy = new BarImpl(tee, 555);
+ BarImpl copy = new BarImpl(tee, 555);
// ${generatedDate}
// Build scripts replace the token with the current date.
- foo = new Foo(bar, copy, "generated test ${generatedDate}", 5);
+ foo = new Foo(bar, copy, "generated test ${generatedDate}", name.length());
}
public void start() {
diff --git a/src/test/java/eg/components/BarImpl.java b/src/test/java/eg/components/BarImpl.java
index 40e34a0..c004ea6 100644
--- a/src/test/java/eg/components/BarImpl.java
+++ b/src/test/java/eg/components/BarImpl.java
@@ -6,7 +6,6 @@
/**
* Sample implementation used for tests.
*/
-
public class BarImpl implements Bar {
private final int i;
private final Tee tee;
diff --git a/src/test/java/eg/components/Foo.java b/src/test/java/eg/components/Foo.java
index e379e5d..5ad4a92 100644
--- a/src/test/java/eg/components/Foo.java
+++ b/src/test/java/eg/components/Foo.java
@@ -3,15 +3,16 @@
*/
package eg.components;
-@SuppressWarnings("QuestionableName")
/**
* Simple data holder used to demonstrate dynamic compilation.
*/
+@SuppressWarnings("QuestionableName")
public class Foo {
- private final Bar bar;
- private final Bar copy;
+ @SuppressWarnings("unused")
+ public final Bar bar;
+ @SuppressWarnings("unused")
+ public final Bar copy;
public final String s;
- private final int i;
/**
* Creates a new instance.
@@ -25,6 +26,5 @@ public Foo(Bar bar, Bar copy, String s, int i) {
this.bar = bar;
this.copy = copy;
this.s = s;
- this.i = i;
}
}
diff --git a/src/test/java/mytest/RuntimeCompileTest.java b/src/test/java/mytest/RuntimeCompileTest.java
index 22b9f0f..13ed6f1 100644
--- a/src/test/java/mytest/RuntimeCompileTest.java
+++ b/src/test/java/mytest/RuntimeCompileTest.java
@@ -9,6 +9,8 @@
import java.net.URL;
import java.net.URLClassLoader;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
@@ -22,7 +24,7 @@
import static org.junit.Assert.fail;
public class RuntimeCompileTest {
- private static String code = "package mytest;\n" +
+ private static final String code = "package mytest;\n" +
"public class Test implements IntConsumer {\n" +
" public void accept(int num) {\n" +
" if ((byte) num != num)\n" +
@@ -32,7 +34,8 @@ public class RuntimeCompileTest {
@Test
public void outOfBounds() throws Exception {
- ClassLoader cl = new URLClassLoader(new URL[0]);
+ ClassLoader cl = AccessController.doPrivileged(
+ (PrivilegedAction) () -> new URLClassLoader(new URL[0]));
Class> aClass = CompilerUtils.CACHED_COMPILER.
loadFromJava(cl, "mytest.Test", code);
IntConsumer consumer = (IntConsumer) aClass.getDeclaredConstructor().newInstance();
@@ -41,6 +44,7 @@ public void outOfBounds() throws Exception {
consumer.accept(128); // no ok
fail();
} catch (IllegalArgumentException expected) {
+ assertEquals("Unexpected exception type", IllegalArgumentException.class, expected.getClass());
}
}
@@ -55,10 +59,7 @@ public void testMultiThread() throws Exception {
" called.incrementAndGet();\n" +
" }\n");
for (int j=0; j<1_000; j++) {
- largeClass.append(" public void accept"+j+"(int num) {\n" +
- " if ((byte) num != num)\n" +
- " throw new IllegalArgumentException();\n" +
- " }\n");
+ largeClass.append(" public void accept").append(j).append("(int num) {\n").append(" if ((byte) num != num)\n").append(" throw new IllegalArgumentException();\n").append(" }\n");
}
largeClass.append("}\n");
final String code2 = largeClass.toString();
diff --git a/src/test/java/net/openhft/compiler/AiRuntimeGuardrailsTest.java b/src/test/java/net/openhft/compiler/AiRuntimeGuardrailsTest.java
index 6606fa5..06fdd95 100644
--- a/src/test/java/net/openhft/compiler/AiRuntimeGuardrailsTest.java
+++ b/src/test/java/net/openhft/compiler/AiRuntimeGuardrailsTest.java
@@ -72,7 +72,7 @@ public void successfulCompilationRecordsMetrics() throws Exception {
new CachedCompilerInvoker()
);
- Class> clazz = pipeline.compile("agent-B", "OkClass",
+ final Class> clazz = pipeline.compile("agent-B", "OkClass",
"public class OkClass { public int add(int a, int b) { return a + b; } }");
assertEquals("agent-B should see exactly one attempt", 1, telemetry.compileAttempts("agent-B"));
@@ -100,8 +100,8 @@ public void cacheHitDoesNotRecompileButRecordsMetric() throws Exception {
);
String source = "public class CacheCandidate { public String id() { return \"ok\"; } }";
- Class> first = pipeline.compile("agent-C", "CacheCandidate", source);
- Class> second = pipeline.compile("agent-C", "CacheCandidate", source);
+ final Class> first = pipeline.compile("agent-C", "CacheCandidate", source);
+ final Class> second = pipeline.compile("agent-C", "CacheCandidate", source);
assertEquals("Underlying compiler should only run once thanks to caching", 1, rawCompileCount.get());
assertEquals(2, telemetry.compileAttempts("agent-C"));
@@ -109,7 +109,7 @@ public void cacheHitDoesNotRecompileButRecordsMetric() throws Exception {
assertEquals(1, telemetry.successes("agent-C"));
assertEquals(0, telemetry.compileFailures("agent-C"));
assertEquals("Cache hit count should be tracked", 1, telemetry.cacheHits("agent-C"));
- assertTrue(first == second);
+ assertSame(first, second);
}
@Test
@@ -198,6 +198,7 @@ private interface SourceValidator {
}
private static final class ValidationException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
ValidationException(String message) {
super(message);
}
diff --git a/src/test/java/net/openhft/compiler/CachedCompilerAdditionalTest.java b/src/test/java/net/openhft/compiler/CachedCompilerAdditionalTest.java
index 535bc8d..e9889d6 100644
--- a/src/test/java/net/openhft/compiler/CachedCompilerAdditionalTest.java
+++ b/src/test/java/net/openhft/compiler/CachedCompilerAdditionalTest.java
@@ -19,6 +19,8 @@
import java.util.Comparator;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import static org.junit.Assert.*;
@@ -60,8 +62,9 @@ public void compileFromJavaReturnsEmptyMapOnFailure() throws Exception {
@Test
public void updateFileManagerForClassLoaderInvokesConsumer() throws Exception {
CachedCompiler compiler = new CachedCompiler(null, null);
- ClassLoader loader = new ClassLoader() {
- };
+ ClassLoader loader = AccessController.doPrivileged(
+ (PrivilegedAction) () -> new ClassLoader() {
+ });
compiler.loadFromJava(loader, "coverage.UpdateTarget", "package coverage; public class UpdateTarget {}");
AtomicBoolean invoked = new AtomicBoolean(false);
@@ -73,9 +76,11 @@ public void updateFileManagerForClassLoaderInvokesConsumer() throws Exception {
public void updateFileManagerNoOpWhenClassLoaderUnknown() {
CachedCompiler compiler = new CachedCompiler(null, null);
AtomicBoolean invoked = new AtomicBoolean(false);
- compiler.updateFileManagerForClassLoader(new ClassLoader() {
- }, fm -> invoked.set(true));
- assertTrue("Consumer should not be invoked when manager missing", !invoked.get());
+ ClassLoader loader = AccessController.doPrivileged(
+ (PrivilegedAction) () -> new ClassLoader() {
+ });
+ compiler.updateFileManagerForClassLoader(loader, fm -> invoked.set(true));
+ assertFalse("Consumer should not be invoked when manager missing", invoked.get());
}
@Test
@@ -86,8 +91,9 @@ public void closeClosesAllManagedFileManagers() throws Exception {
AtomicBoolean closed = new AtomicBoolean(false);
cachedCompiler.setFileManagerOverride(standard -> new TrackingFileManager(standard, closed));
- ClassLoader loader = new ClassLoader() {
- };
+ ClassLoader loader = AccessController.doPrivileged(
+ (PrivilegedAction) () -> new ClassLoader() {
+ });
cachedCompiler.loadFromJava(loader, "coverage.CloseTarget", "package coverage; public class CloseTarget {}");
cachedCompiler.close();
assertTrue("Close should propagate to file managers", closed.get());
@@ -153,8 +159,9 @@ public void writesSourceAndClassFilesWhenDirectoriesProvided() throws Exception
String className = "coverage.FileOutput";
String versionOne = "package coverage; public class FileOutput { public String value() { return \"v1\"; } }";
- ClassLoader loaderOne = new ClassLoader() {
- };
+ ClassLoader loaderOne = AccessController.doPrivileged(
+ (PrivilegedAction) () -> new ClassLoader() {
+ });
firstPass.loadFromJava(loaderOne, className, versionOne);
firstPass.close();
@@ -166,16 +173,17 @@ public void writesSourceAndClassFilesWhenDirectoriesProvided() throws Exception
CachedCompiler secondPass = new CachedCompiler(sourceDir.toFile(), classDir.toFile());
String versionTwo = "package coverage; public class FileOutput { public String value() { return \"v2\"; } }";
- ClassLoader loaderTwo = new ClassLoader() {
- };
+ ClassLoader loaderTwo = AccessController.doPrivileged(
+ (PrivilegedAction) () -> new ClassLoader() {
+ });
secondPass.loadFromJava(loaderTwo, className, versionTwo);
secondPass.close();
byte[] updatedBytes = Files.readAllBytes(classFile);
- assertTrue("Updating the source should change emitted bytecode", !Arrays.equals(firstBytes, updatedBytes));
+ assertFalse("Updating the source should change emitted bytecode", Arrays.equals(firstBytes, updatedBytes));
Path backupFile = classDir.resolve("coverage/FileOutput.class.bak");
- assertTrue("Backup should be cleaned up", !Files.exists(backupFile));
+ assertFalse("Backup should be cleaned up", Files.exists(backupFile));
} finally {
deleteRecursively(classDir);
deleteRecursively(sourceDir);
diff --git a/src/test/java/net/openhft/compiler/CompilerTest.java b/src/test/java/net/openhft/compiler/CompilerTest.java
index 011434e..1cbbfcd 100644
--- a/src/test/java/net/openhft/compiler/CompilerTest.java
+++ b/src/test/java/net/openhft/compiler/CompilerTest.java
@@ -9,12 +9,14 @@
import org.junit.Test;
import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
-import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
public class CompilerTest extends TestCase {
@@ -40,9 +42,9 @@ public void test_compiler() throws Throwable {
// CompilerUtils.setDebug(true);
// added so the test passes in Maven.
CompilerUtils.addClassPath("target/test-classes");
-// ClassLoader loader = CompilerTest.class.getClassLoader();
-// URLClassLoader urlClassLoader = new URLClassLoader(((URLClassLoader)loader).getURLs(), null);
-// Class fooBarTee1 = urlClassLoader.loadClass("eg.FooBarTee");
+ // ClassLoader loader = CompilerTest.class.getClassLoader();
+ // URLClassLoader urlClassLoader = new URLClassLoader(((URLClassLoader)loader).getURLs(), null);
+ // Class fooBarTee1 = urlClassLoader.loadClass("eg.FooBarTee");
// this writes the file to disk only when debugging is enabled.
CachedCompiler cc = CompilerUtils.DEBUGGING ?
@@ -103,9 +105,9 @@ public void test_fromFile()
try {
System.setOut(new PrintStream(new OutputStream() {
@Override
- public void write(int b) throws IOException {
+ public void write(int b) {
}
- }));
+ }, true, StandardCharsets.UTF_8.name()));
final Constructor stringConstructor = clazz.getConstructor(String.class);
long start = 0;
for (int i = -RUNS / 10; i < RUNS; i++) {
@@ -134,16 +136,16 @@ public void test_settingPrintStreamWithCompilerErrors() throws Exception {
try {
System.setOut(new PrintStream(new OutputStream() {
@Override
- public void write(int b) throws IOException {
+ public void write(int b) {
usedSysOut.set(true);
}
- }));
+ }, true, StandardCharsets.UTF_8.name()));
System.setErr(new PrintStream(new OutputStream() {
@Override
- public void write(int b) throws IOException {
+ public void write(int b) {
usedSysErr.set(true);
}
- }));
+ }, true, StandardCharsets.UTF_8.name()));
CompilerUtils.CACHED_COMPILER.loadFromJava(
getClass().getClassLoader(), "TestClass", "clazz TestClass {}",
@@ -163,7 +165,7 @@ public void write(int b) throws IOException {
"TestClass.java:1: error", "clazz TestClass {}");
for (String expectedError : expectedInErrorFromCompiler) {
- String errorMessage = String.format("Does not contain expected '%s' in:\n%s", expectedError, writer.toString());
+ String errorMessage = String.format("Does not contain expected '%s' in:%n%s", expectedError, writer);
assertTrue(errorMessage, writer.toString().contains(expectedError));
}
}
@@ -182,13 +184,13 @@ public void test_settingPrintStreamWithNoErrors() throws Exception {
public void write(int b) {
usedSysOut.set(true);
}
- }));
+ }, true, StandardCharsets.UTF_8.name()));
System.setErr(new PrintStream(new OutputStream() {
@Override
- public void write(int b) throws IOException {
+ public void write(int b) {
usedSysErr.set(true);
}
- }));
+ }, true, StandardCharsets.UTF_8.name()));
CompilerUtils.CACHED_COMPILER.loadFromJava(
getClass().getClassLoader(), "TestClass", "class TestClass {}",
@@ -214,16 +216,16 @@ public void test_settingPrintStreamWithWarnings() throws Exception {
try {
System.setOut(new PrintStream(new OutputStream() {
@Override
- public void write(int b) throws IOException {
+ public void write(int b) {
usedSysOut.set(true);
}
- }));
+ }, true, StandardCharsets.UTF_8.name()));
System.setErr(new PrintStream(new OutputStream() {
@Override
- public void write(int b) throws IOException {
+ public void write(int b) {
usedSysErr.set(true);
}
- }));
+ }, true, StandardCharsets.UTF_8.name()));
CompilerUtils.CACHED_COMPILER.loadFromJava(
getClass().getClassLoader(), "TestClass",
@@ -242,7 +244,7 @@ public void write(int b) throws IOException {
public void test_compilerErrorsDoNotBreakNextCompilations() throws Exception {
// quieten the compiler output
- PrintWriter quietWriter = new PrintWriter(new StringWriter());
+ PrintWriter quietWriter = new PrintWriter(new OutputStreamWriter(new ByteArrayOutputStream(), StandardCharsets.UTF_8));
// cause a compiler error
try {
@@ -258,7 +260,7 @@ public void test_compilerErrorsDoNotBreakNextCompilations() throws Exception {
getClass().getClassLoader(), "S", "class S {" +
"public static final String s = \"ok\";}");
- Callable callable = (Callable)
+ java.util.concurrent.Callable> callable = (java.util.concurrent.Callable>)
CompilerUtils.CACHED_COMPILER.loadFromJava(
getClass().getClassLoader(), "OtherClass",
"import java.util.concurrent.Callable; " +
@@ -274,10 +276,11 @@ public void test_compilerErrorsDoNotBreakNextCompilations() throws Exception {
@Test
public void testNewCompiler() throws Exception {
for (int i = 1; i <= 3; i++) {
- ClassLoader classLoader = new ClassLoader() {
- };
+ ClassLoader classLoader = AccessController.doPrivileged(
+ (PrivilegedAction) () -> new ClassLoader() {
+ });
CachedCompiler cc = new CachedCompiler(null, null);
- Class> a = cc.loadFromJava(classLoader, "A", "public class A { static int i = " + i + "; }");
+ cc.loadFromJava(classLoader, "A", "public class A { static int i = " + i + "; }");
Class> b = cc.loadFromJava(classLoader, "B", "public class B implements net.openhft.compiler.MyIntSupplier { public int get() { return A.i; } }");
MyIntSupplier bi = (MyIntSupplier) b.getDeclaredConstructor().newInstance();
assertEquals(i, bi.get());
diff --git a/src/test/java/net/openhft/compiler/CompilerUtilsIoTest.java b/src/test/java/net/openhft/compiler/CompilerUtilsIoTest.java
index dde885f..48587b6 100644
--- a/src/test/java/net/openhft/compiler/CompilerUtilsIoTest.java
+++ b/src/test/java/net/openhft/compiler/CompilerUtilsIoTest.java
@@ -12,6 +12,8 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -31,7 +33,7 @@ public void writeTextDetectsNoChangeAndReadBytesMatches() throws Exception {
assertTrue("First write should report changes", written);
boolean unchanged = CompilerUtils.writeText(file, "hello");
- assertTrue("Repeat write with identical content should be treated as unchanged", !unchanged);
+ assertFalse("Repeat write with identical content should be treated as unchanged", unchanged);
boolean changed = CompilerUtils.writeText(file, "different");
assertTrue("Modified content should trigger a rewrite", changed);
@@ -109,7 +111,7 @@ public void defineClassLoadsCompiledBytes() throws Exception {
public void addClassPathHandlesMissingDirectory() {
Path nonExisting = Paths.get("not-existing-" + System.nanoTime());
boolean result = CompilerUtils.addClassPath(nonExisting.toString());
- assertTrue("Missing directories should return false", !result);
+ assertFalse("Missing directories should return false", result);
}
@Test
@@ -136,7 +138,7 @@ public void readTextInlineShortcutAndReadBytesMissing() throws Exception {
Method readBytes = CompilerUtils.class.getDeclaredMethod("readBytes", File.class);
readBytes.setAccessible(true);
Object missing = readBytes.invoke(null, new File("definitely-missing-" + System.nanoTime()));
- assertEquals(null, missing);
+ assertNull(missing);
Path tempFile = Files.createTempFile("compiler-utils-bytes", ".bin");
Files.write(tempFile, "bytes".getBytes(StandardCharsets.UTF_8));
@@ -165,13 +167,13 @@ public void getInputStreamSupportsInlineContent() throws Exception {
Method method = CompilerUtils.class.getDeclaredMethod("getInputStream", String.class);
method.setAccessible(true);
try (InputStream is = (InputStream) method.invoke(null, "=inline-data")) {
- String value = new String(readFully(is));
+ String value = new String(readFully(is), StandardCharsets.UTF_8);
assertEquals("inline-data", value);
}
Path tempFile = Files.createTempFile("compiler-utils-stream", ".txt");
Files.write(tempFile, "file-data".getBytes(StandardCharsets.UTF_8));
try (InputStream is = (InputStream) method.invoke(null, tempFile.toString())) {
- String value = new String(readFully(is));
+ String value = new String(readFully(is), StandardCharsets.UTF_8);
assertEquals("file-data", value);
}
}
@@ -190,15 +192,16 @@ public void getInputStreamUsesSlashFallback() throws Exception {
Method method = CompilerUtils.class.getDeclaredMethod("getInputStream", String.class);
method.setAccessible(true);
ClassLoader original = Thread.currentThread().getContextClassLoader();
- ClassLoader loader = new ClassLoader(original) {
+ ClassLoader loader = AccessController.doPrivileged(
+ (PrivilegedAction) () -> new ClassLoader(original) {
@Override
public InputStream getResourceAsStream(String name) {
- if ("/fallback-resource".equals(name)) {
- return new ByteArrayInputStream("fallback".getBytes(StandardCharsets.UTF_8));
+ if ("/fallback-resource".equals(name)) {
+ return new ByteArrayInputStream("fallback".getBytes(StandardCharsets.UTF_8));
+ }
+ return null;
}
- return null;
- }
- };
+ });
Thread.currentThread().setContextClassLoader(loader);
try (InputStream is = (InputStream) method.invoke(null, "fallback-resource")) {
assertEquals("fallback", new String(readFully(is), StandardCharsets.UTF_8));
diff --git a/src/test/java/net/openhft/compiler/MyIntSupplier.java b/src/test/java/net/openhft/compiler/MyIntSupplier.java
index dbfa63c..8011ffb 100644
--- a/src/test/java/net/openhft/compiler/MyIntSupplier.java
+++ b/src/test/java/net/openhft/compiler/MyIntSupplier.java
@@ -7,5 +7,5 @@
* Created by Peter Lawrey on 31/12/2016.
*/
public interface MyIntSupplier {
- public int get();
+ int get();
}
diff --git a/src/test/java/net/openhft/compiler/MyJavaFileManagerTest.java b/src/test/java/net/openhft/compiler/MyJavaFileManagerTest.java
index 693519f..cedb04b 100644
--- a/src/test/java/net/openhft/compiler/MyJavaFileManagerTest.java
+++ b/src/test/java/net/openhft/compiler/MyJavaFileManagerTest.java
@@ -6,7 +6,6 @@
import org.junit.Test;
import javax.tools.FileObject;
-import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
@@ -61,6 +60,7 @@ public void bufferedClassReturnedFromInput() throws IOException {
}
}
+ @SuppressWarnings("rawtypes")
@Test
public void getJavaFileForInputDelegatesWhenBufferMissing() throws Exception {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
@@ -98,7 +98,7 @@ public InputStream openInputStream() {
JavaFileObject result = manager.getJavaFileForInput(StandardLocation.CLASS_OUTPUT,
"example.KindMismatch", JavaFileObject.Kind.SOURCE);
assertTrue("Delegate should be consulted when buffer missing", delegated.get());
- assertTrue("Result should match delegate outcome", result == expected);
+ assertSame("Result should match delegate outcome", result, expected);
}
}
@@ -140,7 +140,7 @@ public void listLocationsForModulesAndInferModuleNameDeferToDelegate() throws IO
Iterable> locations =
manager.listLocationsForModules(modulesLocation);
for (Set ignored : locations) {
- // no-op
+ assertNotNull("Module location set should not be null", ignored);
}
} catch (UnsupportedOperationException ignored) {
// Delegate does not expose module support on this JDK.