Reputation: 3630
I am trying to generate a very simple code with Byte Buddy
.
I have a POJO class where some fields are annotated with @SecureAttribute
, For such fields I would like to override getter implementation and redirect the call to a SecurityService.getSecureValue()
implementation.
Original class:
public class Properties {
@SecureAttribute
protected String password;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Desired Proxy:
public class PropertiesProxy {
private SecurityService securityService;
public void setSecurityService(SecurityService var1) {
this.securityService = var1;
}
public SecurityService getSecurityService() {
return this.securityService;
}
@Override
public String getPassword() {
return securityService.getSecureValue(password);
}
}
Emitting a field was easy but overriding a method becomes complicated. I have found a number of samples relative to my task which I try to apply but do not seem to get the required result.
So my major question is: how do I trace and debug the code generator? First thing I've learned was to print the class to file:
DynamicType.Unloaded<?> unloadedType = byteBuddy.make();
unloadedType.saveIn(new File("d:/temp/bytebuddy"));
This gives me an output where the extra field was added but not a glance of the getter override (disassembled from .class file):
public class PropertiesImpl$ByteBuddy$OLlyZYNY extends PropertiesImpl {
private SecurityService securityService;
public void setSecurityService(SecurityService var1) {
this.securityService = var1;
}
public SecurityService getSecurityService() {
return this.securityService;
}
public PropertiesImpl$ByteBuddy$OLlyZYNY() {
}
}
Here I do not exactly understand how to look for the error. Does it mean that I used totally wrong method implementation and Byte Buddy
simply skipped it? Or am I wrong with ElementMatchers? Is there some trace or whatever that will give me a clue how to fix my code?
Current implementation:
private Class<?> wrapProperties() throws IOException {
DynamicType.Builder<?> byteBuddy = new ByteBuddy()
.subclass(PropertiesImpl.class)
.defineProperty("securityService", SecurityService.class);
Arrays.stream(PropertiesImpl.class.getDeclaredFields())
.filter(item -> item.getAnnotation(SecureAttribute.class) != null)
.forEach(item -> byteBuddy
.method(ElementMatchers.named(getGetterBeanName(item)))
.intercept(new GetterWrapperImplementation(item)));
DynamicType.Unloaded<?> unloadedType = byteBuddy.make();
unloadedType.saveIn(new File("d:/temp/bytebuddy"));
Class<?> wrapperClass = unloadedType.load(PropertiesImpl.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
return wrapperClass;
}
public static class GetterWrapperImplementation implements Implementation {
public static final TypeDescription SS_TYPE;
public static final MethodDescription SS_GET_SECURE_VALUE;
private final Field filed;
static {
try {
SS_TYPE = new TypeDescription.ForLoadedType(SecurityService.class);
SS_GET_SECURE_VALUE = new MethodDescription.ForLoadedMethod(SecurityService.class.getDeclaredMethod("getSecureValue", String.class));
}
catch (final NoSuchMethodException | SecurityException e) {
throw new RuntimeException(e);
}
}
public GetterWrapperImplementation(Field filed) {
this.filed = filed;
}
@Override
public InstrumentedType prepare(final InstrumentedType instrumentedType) {
return instrumentedType;
}
@Override
public ByteCodeAppender appender(final Target implementationTarget) {
final TypeDescription thisType = implementationTarget.getInstrumentedType();
return new ByteCodeAppender.Simple(Arrays.asList(
TypeCreation.of(SS_TYPE),
// get securityService field
MethodVariableAccess.loadThis(),
FieldAccess.forField(thisType.getDeclaredFields()
.filter(ElementMatchers.named("securityService"))
.getOnly()
).read(),
// get secure field
MethodVariableAccess.loadThis(),
FieldAccess.forField(thisType.getDeclaredFields()
.filter(ElementMatchers.named(filed.getName()))
.getOnly()
).read(),
MethodInvocation.invoke(SS_GET_SECURE_VALUE),
MethodReturn.of(TypeDescription.STRING)
));
}
}
What I know for the fact is that breakpoints inside ByteCodeAppender appender(final Target implementationTarget)
do not get hit, but again not sure how to interpret this.
Thanks.
Upvotes: 2
Views: 1070
Reputation: 43997
The Byte Buddy DSL is immutable. This means that you always have to call:
builder = builder.method(...).intercept(...);
Your forEach does not do what you expect for this reason.
As for your implementation, you can just use MethodCall on a field and define the other field as an argument.
Upvotes: 2