[JAVA-22519] Created new module libraries-stream and added 5 articles (#15403)
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
package com.baeldung.asm;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||
import static org.objectweb.asm.Opcodes.ACC_STATIC;
|
||||
import static org.objectweb.asm.Opcodes.ASM4;
|
||||
import static org.objectweb.asm.Opcodes.V1_5;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.util.TraceClassVisitor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author baeldung
|
||||
* @param <String>
|
||||
*/
|
||||
public class CustomClassWriter {
|
||||
|
||||
ClassReader reader;
|
||||
ClassWriter writer;
|
||||
AddFieldAdapter addFieldAdapter;
|
||||
AddInterfaceAdapter addInterfaceAdapter;
|
||||
PublicizeMethodAdapter pubMethAdapter;
|
||||
final static String CLASSNAME = "java.lang.Integer";
|
||||
final static String CLONEABLE = "java/lang/Cloneable";
|
||||
|
||||
public CustomClassWriter() {
|
||||
|
||||
try {
|
||||
reader = new ClassReader(CLASSNAME);
|
||||
writer = new ClassWriter(reader, 0);
|
||||
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(CustomClassWriter.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public CustomClassWriter(byte[] contents) {
|
||||
reader = new ClassReader(contents);
|
||||
writer = new ClassWriter(reader, 0);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
CustomClassWriter ccw = new CustomClassWriter();
|
||||
ccw.publicizeMethod();
|
||||
}
|
||||
|
||||
public byte[] addField() {
|
||||
addFieldAdapter = new AddFieldAdapter("aNewBooleanField", org.objectweb.asm.Opcodes.ACC_PUBLIC, writer);
|
||||
reader.accept(addFieldAdapter, 0);
|
||||
return writer.toByteArray();
|
||||
}
|
||||
|
||||
public byte[] publicizeMethod() {
|
||||
pubMethAdapter = new PublicizeMethodAdapter(writer);
|
||||
reader.accept(pubMethAdapter, 0);
|
||||
return writer.toByteArray();
|
||||
}
|
||||
|
||||
public byte[] addInterface() {
|
||||
addInterfaceAdapter = new AddInterfaceAdapter(writer);
|
||||
reader.accept(addInterfaceAdapter, 0);
|
||||
return writer.toByteArray();
|
||||
}
|
||||
|
||||
public class AddInterfaceAdapter extends ClassVisitor {
|
||||
|
||||
public AddInterfaceAdapter(ClassVisitor cv) {
|
||||
super(ASM4, cv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name,
|
||||
String signature, String superName, String[] interfaces) {
|
||||
String[] holding = new String[interfaces.length + 1];
|
||||
holding[holding.length - 1] = CLONEABLE;
|
||||
System.arraycopy(interfaces, 0, holding, 0, interfaces.length);
|
||||
|
||||
cv.visit(V1_5, access, name, signature, superName, holding);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class PublicizeMethodAdapter extends ClassVisitor {
|
||||
|
||||
final Logger logger = Logger.getLogger("PublicizeMethodAdapter");
|
||||
TraceClassVisitor tracer;
|
||||
PrintWriter pw = new PrintWriter(System.out);
|
||||
|
||||
public PublicizeMethodAdapter(ClassVisitor cv) {
|
||||
super(ASM4, cv);
|
||||
this.cv = cv;
|
||||
tracer = new TraceClassVisitor(cv, pw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access,
|
||||
String name,
|
||||
String desc,
|
||||
String signature,
|
||||
String[] exceptions) {
|
||||
|
||||
if (name.equals("toUnsignedString0")) {
|
||||
logger.info("Visiting unsigned method");
|
||||
return tracer.visitMethod(ACC_PUBLIC + ACC_STATIC, name, desc, signature, exceptions);
|
||||
}
|
||||
return tracer.visitMethod(access, name, desc, signature, exceptions);
|
||||
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
tracer.visitEnd();
|
||||
System.out.println(tracer.p.getText());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class AddFieldAdapter extends ClassVisitor {
|
||||
|
||||
String fieldName;
|
||||
int access;
|
||||
boolean isFieldPresent;
|
||||
|
||||
public AddFieldAdapter(String fieldName, int access, ClassVisitor cv) {
|
||||
super(ASM4, cv);
|
||||
this.cv = cv;
|
||||
this.access = access;
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, String name, String desc,
|
||||
String signature, Object value) {
|
||||
if (name.equals(fieldName)) {
|
||||
isFieldPresent = true;
|
||||
}
|
||||
return cv.visitField(access, name, desc, signature, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
if (!isFieldPresent) {
|
||||
FieldVisitor fv = cv.visitField(access, fieldName, Type.BOOLEAN_TYPE.toString(), null, null);
|
||||
if (fv != null) {
|
||||
fv.visitEnd();
|
||||
}
|
||||
}
|
||||
cv.visitEnd();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.baeldung.asm.instrumentation;
|
||||
|
||||
import com.baeldung.asm.CustomClassWriter;
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.IllegalClassFormatException;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.security.ProtectionDomain;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author baeldung
|
||||
*/
|
||||
public class Premain {
|
||||
|
||||
public static void premain(String agentArgs, Instrumentation inst) {
|
||||
inst.addTransformer(new ClassFileTransformer() {
|
||||
|
||||
@Override
|
||||
public byte[] transform(ClassLoader l, String name, Class c,
|
||||
ProtectionDomain d, byte[] b)
|
||||
throws IllegalClassFormatException {
|
||||
|
||||
if (name.equals("java/lang/Integer")) {
|
||||
CustomClassWriter cr = new CustomClassWriter(b);
|
||||
return cr.addField();
|
||||
}
|
||||
return b;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.baeldung.bytebuddy;
|
||||
|
||||
import net.bytebuddy.implementation.bind.annotation.BindingPriority;
|
||||
|
||||
public class Bar {
|
||||
|
||||
@BindingPriority(3)
|
||||
public static String sayHelloBar() {
|
||||
return "Holla in Bar!";
|
||||
}
|
||||
|
||||
@BindingPriority(2)
|
||||
public static String sayBar() {
|
||||
return "bar";
|
||||
}
|
||||
|
||||
public String bar() {
|
||||
return Bar.class.getSimpleName() + " - Bar";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.baeldung.bytebuddy;
|
||||
|
||||
public class Foo {
|
||||
|
||||
public String sayHelloFoo() {
|
||||
return "Hello in Foo!";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.baeldung.javasisst;
|
||||
|
||||
public class Point {
|
||||
public int x = 0;
|
||||
public int y = 0;
|
||||
|
||||
public Point(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public void move(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.baeldung.javasisst;
|
||||
|
||||
public class ThreeDimensionalPoint {
|
||||
public int x = 0;
|
||||
public int y = 0;
|
||||
public int z = 0;
|
||||
|
||||
public ThreeDimensionalPoint(int x, int y, int z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public void move(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.baeldung.bytebuddy;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.agent.ByteBuddyAgent;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
|
||||
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
|
||||
import net.bytebuddy.implementation.FixedValue;
|
||||
import net.bytebuddy.implementation.MethodDelegation;
|
||||
import net.bytebuddy.matcher.ElementMatchers;
|
||||
|
||||
public class ByteBuddyUnitTest {
|
||||
|
||||
@Test
|
||||
public void givenObject_whenToString_thenReturnHelloWorldString() throws InstantiationException, IllegalAccessException {
|
||||
DynamicType.Unloaded unloadedType = new ByteBuddy().subclass(Object.class).method(ElementMatchers.isToString()).intercept(FixedValue.value("Hello World ByteBuddy!")).make();
|
||||
|
||||
Class<?> dynamicType = unloadedType.load(getClass().getClassLoader()).getLoaded();
|
||||
|
||||
assertEquals(dynamicType.newInstance().toString(), "Hello World ByteBuddy!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenFoo_whenRedefined_thenReturnFooRedefined() throws Exception {
|
||||
ByteBuddyAgent.install();
|
||||
new ByteBuddy().redefine(Foo.class).method(named("sayHelloFoo")).intercept(FixedValue.value("Hello Foo Redefined")).make().load(Foo.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
|
||||
Foo f = new Foo();
|
||||
assertEquals(f.sayHelloFoo(), "Hello Foo Redefined");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenSayHelloFoo_whenMethodDelegation_thenSayHelloBar() throws IllegalAccessException, InstantiationException {
|
||||
|
||||
String r = new ByteBuddy().subclass(Foo.class).method(named("sayHelloFoo").and(isDeclaredBy(Foo.class).and(returns(String.class)))).intercept(MethodDelegation.to(Bar.class)).make().load(getClass().getClassLoader()).getLoaded().newInstance()
|
||||
.sayHelloFoo();
|
||||
|
||||
assertEquals(r, Bar.sayHelloBar());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenMethodName_whenDefineMethod_thenCreateMethod() throws Exception {
|
||||
Class<?> type = new ByteBuddy().subclass(Object.class).name("MyClassName").defineMethod("custom", String.class, Modifier.PUBLIC).intercept(MethodDelegation.to(Bar.class)).defineField("x", String.class, Modifier.PUBLIC).make()
|
||||
.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER).getLoaded();
|
||||
|
||||
Method m = type.getDeclaredMethod("custom", null);
|
||||
|
||||
assertEquals(m.invoke(type.newInstance()), Bar.sayHelloBar());
|
||||
assertNotNull(type.getDeclaredField("x"));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.baeldung.javassist;
|
||||
|
||||
import javassist.CannotCompileException;
|
||||
import javassist.ClassPool;
|
||||
import javassist.NotFoundException;
|
||||
import javassist.bytecode.AccessFlag;
|
||||
import javassist.bytecode.BadBytecode;
|
||||
import javassist.bytecode.Bytecode;
|
||||
import javassist.bytecode.ClassFile;
|
||||
import javassist.bytecode.CodeAttribute;
|
||||
import javassist.bytecode.CodeIterator;
|
||||
import javassist.bytecode.FieldInfo;
|
||||
import javassist.bytecode.MethodInfo;
|
||||
import javassist.bytecode.Mnemonic;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class JavasisstUnitTest {
|
||||
@Test
|
||||
public void givenJavasisstAPI_whenConstructClass_thenGenerateAClassFile() throws CannotCompileException, IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||
// given
|
||||
String classNameWithPackage = "com.baeldung.JavassistGeneratedClass";
|
||||
ClassFile cf = new ClassFile(false, classNameWithPackage, null);
|
||||
cf.setInterfaces(new String[] { "java.lang.Cloneable" });
|
||||
|
||||
FieldInfo f = new FieldInfo(cf.getConstPool(), "id", "I");
|
||||
f.setAccessFlags(AccessFlag.PUBLIC);
|
||||
cf.addField(f);
|
||||
|
||||
// when
|
||||
String className = "JavassistGeneratedClass.class";
|
||||
cf.write(new DataOutputStream(new FileOutputStream(className)));
|
||||
|
||||
// then
|
||||
ClassPool classPool = ClassPool.getDefault();
|
||||
Field[] fields = classPool.makeClass(cf).toClass().getFields();
|
||||
assertEquals(fields[0].getName(), "id");
|
||||
|
||||
String classContent = new String(Files.readAllBytes(Paths.get(className)));
|
||||
assertTrue(classContent.contains("java/lang/Cloneable"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenJavaClass_whenLoadAtByJavassist_thenTraversWholeClass() throws NotFoundException, CannotCompileException, BadBytecode {
|
||||
// given
|
||||
ClassPool cp = ClassPool.getDefault();
|
||||
ClassFile cf = cp.get("com.baeldung.javasisst.Point").getClassFile();
|
||||
MethodInfo minfo = cf.getMethod("move");
|
||||
CodeAttribute ca = minfo.getCodeAttribute();
|
||||
CodeIterator ci = ca.iterator();
|
||||
|
||||
// when
|
||||
List<String> operations = new LinkedList<>();
|
||||
while (ci.hasNext()) {
|
||||
int index = ci.next();
|
||||
int op = ci.byteAt(index);
|
||||
operations.add(Mnemonic.OPCODE[op]);
|
||||
}
|
||||
|
||||
// then
|
||||
assertEquals(operations, Arrays.asList("aload_0", "iload_1", "putfield", "aload_0", "iload_2", "putfield", "return"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenTableOfInstructions_whenAddNewInstruction_thenShouldConstructProperSequence() throws NotFoundException, BadBytecode, CannotCompileException, IllegalAccessException, InstantiationException {
|
||||
// given
|
||||
ClassFile cf = ClassPool.getDefault().get("com.baeldung.javasisst.ThreeDimensionalPoint").getClassFile();
|
||||
|
||||
// when
|
||||
FieldInfo f = new FieldInfo(cf.getConstPool(), "id", "I");
|
||||
f.setAccessFlags(AccessFlag.PUBLIC);
|
||||
cf.addField(f);
|
||||
|
||||
ClassPool classPool = ClassPool.getDefault();
|
||||
Field[] fields = classPool.makeClass(cf).toClass().getFields();
|
||||
List<String> fieldsList = Stream.of(fields).map(Field::getName).collect(Collectors.toList());
|
||||
assertTrue(fieldsList.contains("id"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenLoadedClass_whenAddConstructorToClass_shouldCreateClassWithConstructor() throws NotFoundException, CannotCompileException, BadBytecode {
|
||||
// given
|
||||
ClassFile cf = ClassPool.getDefault().get("com.baeldung.javasisst.Point").getClassFile();
|
||||
Bytecode code = new Bytecode(cf.getConstPool());
|
||||
code.addAload(0);
|
||||
code.addInvokespecial("java/lang/Object", MethodInfo.nameInit, "()V");
|
||||
code.addReturn(null);
|
||||
|
||||
// when
|
||||
MethodInfo minfo = new MethodInfo(cf.getConstPool(), MethodInfo.nameInit, "()V");
|
||||
minfo.setCodeAttribute(code.toCodeAttribute());
|
||||
cf.addMethod(minfo);
|
||||
|
||||
// then
|
||||
CodeIterator ci = code.toCodeAttribute().iterator();
|
||||
List<String> operations = new LinkedList<>();
|
||||
while (ci.hasNext()) {
|
||||
int index = ci.next();
|
||||
int op = ci.byteAt(index);
|
||||
operations.add(Mnemonic.OPCODE[op]);
|
||||
}
|
||||
|
||||
assertEquals(operations, Arrays.asList("aload_0", "invokespecial", "return"));
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user