Une façon de se débarrasser de ces impressions non désirées de façon permanente serait d'utiliser la manipulation de bytecode pour supprimer les instructions d'impression de la bibliothèque gênante. Cela peut être fait par exemple en utilisant ASM (ou l'un des autres cadres de niveau supérieur et plus facile à utiliser AOP).
Vous pouvez le faire au moment de l'exécution ou en tant qu'opération unique de réécriture des fichiers de classe de la bibliothèque. Reportez-vous à la documentation d'ASM pour savoir comment. Voici une preuve de concept. Qu'est-ce qu'il fait, c'est qu'il remplace toutes les références à System.out
avec une référence à un PrintStream
qui ne fait rien.
D'abord les tests. Ils utilisent certaines classes d'utilitaires de my project pour aider à tester les transformations bytecode (le tester nécessite de créer un chargeur de classes personnalisé et d'appliquer les transformations bytecode à la bonne classe, mais pas aux autres classes).
package net.orfjackal.dimdwarf.aop;
import net.orfjackal.dimdwarf.aop.conf.*;
import org.junit.*;
import org.objectweb.asm.*;
import org.objectweb.asm.util.CheckClassAdapter;
import java.io.*;
import java.lang.instrument.ClassFileTransformer;
import java.lang.reflect.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
public class RemoveCallsToSystemOutTest {
private PrintStream originalOut;
private ByteArrayOutputStream collectedOut;
@Before
public void collectSystemOut() {
originalOut = System.out;
collectedOut = new ByteArrayOutputStream();
System.setOut(new PrintStream(collectedOut));
}
@After
public void restoreOriginalSystemOut() {
System.setOut(originalOut);
}
@Test
public void the_target_class_prints_when_not_manipulated() throws Exception {
String safetyCheck = callPrintSomething(TroublesomePrinter.class);
assertThat(safetyCheck, is("it did execute"));
assertThat(collectedOut.size(), is(greaterThan(0)));
}
@Test
public void the_target_class_does_not_print_when_it_has_been_manipulated() throws Exception {
String safetyCheck = callPrintSomething(instrumentClass(TroublesomePrinter.class));
assertThat(safetyCheck, is("it did execute"));
assertThat(collectedOut.size(), is(0));
}
private static String callPrintSomething(Class<?> clazz) throws Exception {
Method m = clazz.getMethod("printSomething");
m.setAccessible(true);
return (String) m.invoke(null);
}
private static Class<?> instrumentClass(Class<?> cls) throws ClassNotFoundException {
ClassFileTransformer transformer = new AbstractTransformationChain() {
protected ClassVisitor getAdapters(ClassVisitor cv) {
cv = new CheckClassAdapter(cv);
cv = new RemoveCallsToSystemOut(cv);
return cv;
}
};
ClassLoader loader = new TransformationTestClassLoader(cls.getPackage().getName() + ".*", transformer);
return loader.loadClass(cls.getName());
}
}
class TroublesomePrinter {
public static String printSomething() {
System.out.println("something");
return "it did execute";
}
}
Et puis l'implémentation. Veuillez noter que vous ne devez pas utiliser ce code sans le comprendre au préalable. Ne pas program by coincidence.
class SilentSystem {
public static final PrintStream out = new PrintStream(new OutputStream() {
public void write(int b) {
}
});
}
class RemoveCallsToSystemOut extends ClassAdapter {
public RemoveCallsToSystemOut(ClassVisitor cv) {
super(cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
return new MyMethodAdapter(super.visitMethod(access, name, desc, signature, exceptions));
}
private static class MyMethodAdapter extends MethodAdapter {
public MyMethodAdapter(MethodVisitor mv) {
super(mv);
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
if (opcode == Opcodes.GETSTATIC
&& owner.equals("java/lang/System")
&& name.equals("out")
&& desc.equals("Ljava/io/PrintStream;")) {
super.visitFieldInsn(opcode, "net/orfjackal/dimdwarf/aop/SilentSystem", name, desc);
} else {
super.visitFieldInsn(opcode, owner, name, desc);
}
}
}
}
Alors juste le vider dans un fichier aléatoire? Et plus tard supprimer le fichier? Cela semble un peu inutile, n'est-il pas préférable de le vider dans un tampon en mémoire? – Oak
@Oak Si les messages ne vous intéressent pas, que diriez-vous d'étendre PrintStream et d'ignorer quoi que ce soit (implémentations vides)? – stacker
Sous-classer avec une implémentation vide est en fait la meilleure idée à ce jour - très simple, assez court avec une classe anonyme. – Oak