Setting up #FieldProxy field dynamically in Byte Buddy - java

I have to implement an interceptor that can be used for dynamically specified fields regardless of the field name.
On the comment for the answer here
https://stackoverflow.com/a/35113359/11390192
I've read
you can really just use reflection on a #This object. As long as you
cache the Field instances, this has no relevance to performance.
However I doubt the following interceptor implementation is an effecient one (if I understood the comment right).
public static class DynamicFieldInterceptor {
private final String fieldName;
public DynamicFieldInterceptor(String fieldName) {
this.fieldName = fieldName;
}
public void intercept(#This Object thiz) throws NoSuchFieldException, IllegalAccessException {
Field field = thiz.getClass().getDeclaredField(fieldName);
boolean oldAccessible = field.isAccessible();
field.setAccessible(true);
Long fieldValue = (Long)field.get(thiz);
field.set(thiz, fieldValue + 1L); // !< Instead of my business logic
field.setAccessible(oldAccessible);
}
}
I've also tried the following idea: to generate interceptor classes for each field with the different annotations on the #FieldProxy argument. Than use the generated class as an interceptor to the target class.
public interface Metaclass {
void intercept(GetterAndSetter field);
}
public static class MetaclassInterceptor implements Metaclass{
#Override
public void intercept(GetterAndSetter field) {
field.set((Long)field.get() + 1L);
}
}
public static Class<?> annotateInterceptorClass(final String annotation)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
return new ByteBuddy()
.subclass(MetaclassInterceptor.class)
.topLevelType()
.name("ClassForIntercepting_" + annotation + "_Field")
.modifiers(Visibility.PUBLIC, Ownership.STATIC)
.defineMethod("intercept", void.class, Visibility.PUBLIC)
.withParameter(GetterAndSetter.class, "intercept")
.annotateParameter(AnnotationDescription.Builder.ofType(FieldProxy.class)
.define("value", annotation).build())
.intercept(SuperMethodCall.INSTANCE)
.make()
.load(MetaclassInterceptor.class.getClassLoader())
.getLoaded();
}
The class seems to be generated well. The method in the generated class exists and the parameter is annotated with the expected annotation.
However when I tried to use the generated class as an interceptor, I've got an exception.
Class<?> klass = new ByteBuddy()
.subclass(Object.class)
.defineProperty("index0", Long.class, false)
.defineProperty("index1", Long.class, false)
.defineMethod("doSomeActions", void.class, Visibility.PUBLIC)
.intercept(
MethodDelegation
.withDefaultConfiguration()
.withBinders(FieldProxy.Binder.install(GetterAndSetter.class))
// Use dynamically generated interceptor, see abode
.to(annotateInterceptor("index0"))
.andThen(
MethodDelegation
.withDefaultConfiguration()
.withBinders(FieldProxy.Binder.install(GetterAndSetter.class))
// Use dynamically generated interceptor, see abode
.to(annotateInterceptor("index1"))
)
)
.make()
.load(MetaclassInterceptor.class.getClassLoader())
.getLoaded();
Exception in thread "main" java.lang.NoClassDefFoundError: LClassForIntercepting_index0_Field;
at java.base/java.lang.Class.getDeclaredFields0(Native Method)
at java.base/java.lang.Class.privateGetDeclaredFields(Class.java:3062)
at java.base/java.lang.Class.getDeclaredField(Class.java:2410)
at net.bytebuddy.implementation.LoadedTypeInitializer$ForStaticField.onLoad(LoadedTypeInitializer.java:120)
at net.bytebuddy.implementation.LoadedTypeInitializer$Compound.onLoad(LoadedTypeInitializer.java:187)
at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:102)
at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:5662)
at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:5651)
at MainClass4.main(MainClass4.java:107)
Even if I succeeded with dynamic implementation of interceptors, I'd be sure that it's not the perfect way. I think it has to be the possibility to make it in the easier way. Really, #FieldProxy annotation can get the field from both explicitly specified name and bean property if the field name in the annotation is not specified, so I think it is the technical opportunity to map it to any other field.

When you load a class using load(MetaclassInterceptor.class.getClassLoader()), you are creating a new class loader that does not become visible to any other classes on other loaders unless you reuse it.
You can:
a) Combine the two DynamicTypes that are created by the make step and load them together. This way, they will end up in the same class loader.
b) Take the class loader of the first generated class and cast it to an InjectionClassLoader. You will also need to specify the ClassLoadingStrategy.WRAPPER.opened() and use it together with InjectionClassLoader.Strategy.INSTANCE. Note that this will allow anybody with a reference to an instance of your generated class to define classes in the same package.
c) Use ClassLoadingStrategy.Default.INJECTION what defines classes in the original class loader without creating a wrapper. Not that this strategy relies on Unsafe API.

Related

how to read runtime annotations on a field within a class

Imagine an annotation called "MyAnn" with runtime retention, a class MyClass, and an abstract class called MyData. MyClass has a field of type MyData, annotated with MyAnn. Within the instance of MyData, how do see if the annotation MyAnn is present and retrieve its information?
Note - in Java8 I know we can directly annotate the inner class at construction - and that works - but I need this working based on the field annotation.
Thanks for any help!
public MyClass extends MySuperClass() {
#MyAnn(value = "something")
protected MyData mydata;
public void doSomething() {
mydata = new MyData() {};
mydata.initialize();
}
}
public abstract MyData() {
String myValue = null;
public void initialize() {
if (***I am annotated with MyAnn) {
myValue = (***value from annotation MyAnn);
}
}
}
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnn {
String myvalue;
}
MyData cannot directly know whether it has been annotated with MyAnn, because it has no knowledge of whether the current instance is a field or a standalone instance, and if it is a field, then whether the field has or has not been annotated with MyAnn.
So, you will need to somehow obtain and pass the value to initialize(), and you will need to somehow obtain the value at the place where initialize() is called. And from your code, it appears that "something" can be passed as a parameter to initialize(), making the whole thing a lot easier than annotating the field and then checking whether the field is annotated.
But in any case, if you want to check whether a field is annotated, you have to:
obtain the fields of your class with getClass().getDeclaredFields()
loop over the fields; for each field, either
invoke isAnnotationPresent( MyAnn.class ) or
invoke field.getAnnotations(), loop for each annotation, and check whether this annotation instanceof MyAnn.class
Once you have found the annotation, you can get its value with annotation.value();

Proxy in Java with a dynamic interface

I'm trying to port some C# code to Java and I'm having trouble with a proxy implementation.
I have this class:
public class Comic
{
public int ComicID;
private List<Volume> _volumes;
public List<Volume> GetVolumes()
{
return _volumes;
}
public void SetVolumes(List<Volume> volumes)
{
_volumes = volumes;
}
}
And I'm trying to add an interceptor to a specific Method call for this entity, but I also need access to it's fields since I'll be using them.
After looking around for how to implement proxies in Java, I got this:
public void Load(Class<?> type)
{
// - type is a Comic.class
ClassLoader appLoader = this.getClass().getClassLoader();
MyProxyHandler proxyHandler = new MyProxyHandler();
Object proxy = Proxy.newProxyInstance(appLoader, new Class[] { ComicInterface.class }, proxyHandler);
}
The problem is, the type is dynamic so I don't know which will be, and I don't want the code to have a requirement of having interfaces of everything, so I looked up on how to build a dynamic interface:
public class InterfaceLoader extends ClassLoader
{
public InterfaceLoader(ClassLoader loader)
{
super(loader);
}
public Class<?> GetInterface(Class<?> type) throws Exception
{
String interfaceName = type.getName() + "$Proxy";
Class<?> interfaceType = null;
interfaceType = findLoadedClass(interfaceName);
if (interfaceType != null) { return interfaceType; }
// - According to the link
byte[] classData = new InterfaceBuilder().BuildInterface(interfaceName, type.getDeclaredMethods());
return defineClass(interfaceName, classBytes, 0, classBytes.length);
}
}
And then I'd use it with the Proxy:
public void Load(Class<?> type)
{
// - type is a Comic.class
ClassLoader appLoader = this.getClass().getClassLoader();
InterfaceLoader loader = new InterfaceLoader(appLoader);
Class<?> dynamicInterface = loader.GetInterface(type);
// - dynamicInterface on debug is "console.Comic$Proxy", seems fine
MyProxyHandler proxyHandler = new MyProxyHandler();
Object proxy = Proxy.newProxyInstance(appLoader, new Class[] { dynamicInterface }, proxyHandler);
}
The exception I get is
java.lang.IllegalArgumentException: interface console.Comic$Proxy is not visible from class loader
I've looked up the exception but only found two solutions, which is to make sure the name doesn't conflict with any type (and I'm sure it doesn't) and to use this.getClass().getClassLoader() instead of type.getClass().getClassLoader() (same error).
What am I doing wrong?
And another issue is, how can I get the "original" object so I can get/set values from fields like ComicID? Using the above proxy method I can intercept methods fine, but I lose access to it's fields.
I've read about it here to get the InvocationHandler, but I don't know how to get the object from the handler and I couldn't find an example with it.
There is only one classLoader that can see your dynamically generated interface, and that is the InterfaceLoader loader. You can probably make the error go away by passing that to newProxyInstance.
But, I'm pretty sure this won't do you any good. None of the java code you are writing is loaded by that class loader, so you won't be able to call through that interface except by reflection.
If you want to use a proxy, then you'll have to make Comic implement an interface, and then use that interface everywhere. In that case, the answer to your next question is that you would pass an instance of the original object to your proxy handler constructor.
If you really want to do this dynamic interception thing, you probably want to look at using ASM (http://asm.ow2.org/) or Javaassist to build a dynamic subclass of Comic that overrides the method you want to intercept, instead of using a proxy.

Get field class in annotations processor

I am writing my first Annotations processor and having trouble with something that seems trivial but I cannot find any information about it.
I have a element annotated with my annotation
#MyAnnotation String property;
When I get this property as a element in my processor I can not seem to get the type of the element in any way. In this case a would want to get a Class or TypeElement instance representing String.
I tried instantiating a class object of the container type with Class.forName() but it threw a ClassNotFoundException. I think this is because I do not have access to the class loader containing the class?
When running your annotation processor, you don't have access to the compiled classes. The point of annotation processing is that it happens pre-compile.
Instead, you need to create an annotation processor that specifically handles your annotation type, then use the mirror API to access the field. For example:
#SupportedAnnotationTypes("com.example.MyAnnotation")
public class CompileTimeAnnotationProcessor extends AbstractProcessor {
#Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
// Only one annotation, so just use annotations.iterator().next();
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(
annotations.iterator().next());
Set<VariableElement> fields = ElementFilter.fieldsIn(elements);
for (VariableElement field : fields) {
TypeMirror fieldType = field.asType();
String fullTypeClassName = fieldType.toString();
// Validate fullTypeClassName
}
return true;
}
}
For the validation, you cannot use any classes which have yet to be compiled (including those that are about to be compiled with the annotation) using something like MyType.class. For these, you must use strings only. That is because annotation processing occurs during a pre-compiling phase known as "source generation", which is what allows you to generate source code before the compiler runs using annotations.
An example validation verifying that the field type is java.lang.String (which is already compiled):
for (VariableElement field : fields) {
TypeMirror fieldType = field.asType();
String fullTypeClassName = fieldType.toString();
if (!String.class.getName().equals(fullTypeClassName)) {
processingEnv.getMessager().printMessage(
Kind.ERROR, "Field type must be java.lang.String", field);
}
}
Resources
Main APT Page
Mirror API Javadocs (Java 7 and older)
Edit: Mirror API Javadocs (Java 8)
Note that the mirror API is now standardized in Java 8 under javax.lang.model and the old API is deprecated. See this blog post for more information. If you've been using the javax classes, then you don't need to worry.
Edit:
I want to get the field type to get annotations on that type. But this does not seem like it will be possible?
Indeed it is possible! This can be done using more methods on the TypeMirror:
if (fieldType.getKind() != TypeKind.DECLARED) {
processingEnv.getMessager().printMessage(
Kind.ERROR, "Field cannot be a generic type.", field);
}
DeclaredType declaredFieldType = (DeclaredType) fieldType;
TypeElement fieldTypeElement = (TypeElement) declaredFieldType.asElement();
From here, you have two choices:
If the annotation you're trying to find is already compiled (i.e. it's from another library) then you can reference the class directly to get the annotation.
If the annotation you're trying to find is not compiled (i.e. it's being compiled in the current call to javac that's running the APT) then you can reference it via AnnotationMirror instances.
Already Compiled
DifferentAnnotation diffAnn = fieldTypeElement.getAnnotation(
DifferentAnnotation.class);
// Process diffAnn
Very straight-forward, this gives you direct access to the annotation itself.
Not Compiled
Note that this solution will work regardless of whether or not the annotation is compiled, it's just not as clean as the code above.
Here are a couple methods I wrote once to extract a certain value from an annotation mirror by its class name:
private static <T> T findAnnotationValue(Element element, String annotationClass,
String valueName, Class<T> expectedType) {
T ret = null;
for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
DeclaredType annotationType = annotationMirror.getAnnotationType();
TypeElement annotationElement = (TypeElement) annotationType
.asElement();
if (annotationElement.getQualifiedName().contentEquals(
annotationClass)) {
ret = extractValue(annotationMirror, valueName, expectedType);
break;
}
}
return ret;
}
private static <T> T extractValue(AnnotationMirror annotationMirror,
String valueName, Class<T> expectedType) {
Map<ExecutableElement, AnnotationValue> elementValues = new HashMap<ExecutableElement, AnnotationValue>(
annotationMirror.getElementValues());
for (Entry<ExecutableElement, AnnotationValue> entry : elementValues
.entrySet()) {
if (entry.getKey().getSimpleName().contentEquals(valueName)) {
Object value = entry.getValue().getValue();
return expectedType.cast(value);
}
}
return null;
}
Let's say that you're looking for the DifferentAnnotation annotation and your source code looks like this:
#DifferentAnnotation(name = "My Class")
public class MyClass {
#MyAnnotation
private String field;
// ...
}
This code will print My Class:
String diffAnnotationName = findAnnotationValue(fieldTypeElement,
"com.example.DifferentAnnotation", "name", String.class);
System.out.println(diffAnnotationName);

Specify which fields are (not) serialized in ObjectOutputStream without using transient or serialPersistentFields

Is there any way to tell an ObjectOutputStream which fields of a serializable class should be serialized without using the keyword transient and without defining an serialPersistentFields-array?
Background: I need to use annotations to define which members of a class should be serialized (or better: not be serialized). The involved classes must implement the interface Serializable, but NOT Externalizable, so I don't want to implement the serialization/deserialization algorithm for each object but rather just use annotations for it. I can not use the transient keyword, because the annotation requires some further checks to determine whether a field should be serialized or not. These checks have to be done by the ObjectOutputStream (or in my own subclass of ObjectOutputStream). I also cannot define a serialPersistentFields-array in each class, because as explained previously, at compilation time it is not defined which fields should be serialized.
So the only thing that should be notet in the affected class is the annotation at field-level (#Target(ElementType.FIELD)).
I've tried quite a lot of approaches in the last few days, but haven't found one which is working:
The ObjectOutputStream has a method writeObjectOverride(Object) which can be used to define an own implementation of the serialization-process when extending ObjectOutputStream. This only works if the ObjectOutputStream is initialized with the no-argument-constructor because otherwise writeObjectOverride is never invoked. But this approach requires me to implement the whole serialization-process by myself and I don't want to do this, as it is quite complex and already implemented by the default ObjectOutputStream. I am looking for a way to just modify the default serialization implementation.
Another approach was extending ObjectOutputStream again and overriding writeObjectOverride(Object) (after calling enableReplaceObject(true)). In this method, I tried using some kind of SerializationProxy (see What is the Serialization Proxy Pattern?) to encapsulate the serialized object in a proxy which defines a List of Fields which should be serialized. But this approach also fails as writeObjectOverride then is also called for the List of fields (List<SerializedField> fields) in the Proxy resulting in an infinite loop.
Example:
public class AnnotationAwareObjectOutputStream extends ObjectOutputStream {
public AnnotationAwareObjectOutputStream(OutputStream out)
throws IOException {
super(out);
enableReplaceObject(true);
}
#Override
protected Object replaceObject(Object obj) throws IOException {
try {
return new SerializableProxy(obj);
} catch (Exception e) {
return new IOException(e);
}
}
private class SerializableProxy implements Serializable {
private Class<?> clazz;
private List<SerializedField> fields = new LinkedList<SerializedField>();
private SerializableProxy(Object obj) throws IllegalArgumentException,
IllegalAccessException {
clazz = obj.getClass();
for (Field field : getInheritedFields(obj.getClass())) {
// add all fields which don't have an DontSerialize-Annotation
if (!field.isAnnotationPresent(DontSerialize.class))
fields.add(new SerializedField(field.getType(), field
.get(obj)));
}
}
public Object readResolve() {
// TODO: reconstruct object of type clazz and set fields using
// reflection
return null;
}
}
private class SerializedField {
private Class<?> type;
private Object value;
public SerializedField(Class<?> type, Object value) {
this.type = type;
this.value = value;
}
}
/** return all fields including superclass-fields */
public static List<Field> getInheritedFields(Class<?> type) {
List<Field> fields = new ArrayList<Field>();
for (Class<?> c = type; c != null; c = c.getSuperclass()) {
fields.addAll(Arrays.asList(c.getDeclaredFields()));
}
return fields;
}
}
// I just use the annotation DontSerialize in this example for simlicity.
// Later on I want to parametrize the annotation and do some further checks
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface DontSerialize {
}
When I found out that it is possible to modify modifiers at runtime (see Change private static final field using Java reflection) I tried to set the transient-Modifier at runtime if the corresponding annotation was set.
Unfortunately this also does not work, because the approach used in the previous link seems to work only on static fields.
When trying it with non-static fields it runs without an exception but is not persisted because is looks like Field.class.getDeclaredField(...) returns new instances of the affected fields every time it is called:
public void setTransientTest() throws SecurityException,
NoSuchFieldException, IllegalArgumentException,
IllegalAccessException {
Class<MyClass> clazz = MyClass.class;
// anyField is defined as "private String anyField"
Field field = clazz.getDeclaredField("anyField");
System.out.println("1. is "
+ (Modifier.isTransient(field.getModifiers()) ? "" : "NOT ")
+ "transient");
Field modifiersField = Field.class.getDeclaredField("modifiers");
boolean wasAccessible = modifiersField.isAccessible();
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() | Modifier.TRANSIENT);
modifiersField.setAccessible(wasAccessible);
System.out.println("2. is "
+ (Modifier.isTransient(field.getModifiers()) ? "" : "NOT ")
+ "transient");
Field field2 = clazz.getDeclaredField("anyField");
System.out.println("3. is "
+ (Modifier.isTransient(field2.getModifiers()) ? "" : "NOT ")
+ "transient");
}
The output is:
1. is NOT transient
2. is transient
3. is NOT transient
So after calling getDeclaredField again (Field field2 = clazz.getDeclaredField("anyField");) it already lost the transient modifier.
Next approach:
Extend ObjectOutputStream and override ObjectOutputStream.PutField putFields() and define an own PutField-implementation. PutField lets you specify which (additional) fields are serialized but unfortunately the interface only has a lot of methodes of the form put(String name, <type> val) and when implementing these I cannot associate the method calls with the class field it is invoked from. For instance when serializing a field declared as private String test = "foo" the method put("test", "foo") is invoked, but I cannot associate the value of name (which is test) with the class containing the field test because no reference to the containing class is available and therefore it is impossible to read the annotation noted for the field test.
I also tried a few other approaches but as already mentioned I was not able to successfully serialize all fields except the ones with the annotation DontSerialize present.
One thing I also came across were ByteCode manipulators. Maybe it is possible with these but I have a requirement for not using any external tools - it needs to be pure Java (1.5 or 1.6).
Sorry for this really long post but I just wanted to show what I already tried and am hoping that someone can help me.
Thanks in advance.
I would reconsider if "Serialization" is really the thing you want to do. Given that the Serialization rules depends on some logic defined at runtime, the Deserialization process will be a nightmare to write.
Interesting problem, though.
Without rewriting much of Java Serialization, you will need to rewrite the bytecode. At runtime this can be done with Java Agents, but can also be done to class files during the build.

How to read the value of a private field from a different class in Java?

I have a poorly designed class in a 3rd-party JAR and I need to access one of its private fields. For example,
why should I need to choose private field is it necessary?
class IWasDesignedPoorly {
private Hashtable stuffIWant;
}
IWasDesignedPoorly obj = ...;
How can I use reflection to get the value of stuffIWant?
In order to access private fields, you need to get them from the class's declared fields and then make them accessible:
Field f = obj.getClass().getDeclaredField("stuffIWant"); //NoSuchFieldException
f.setAccessible(true);
Hashtable iWantThis = (Hashtable) f.get(obj); //IllegalAccessException
EDIT: as has been commented by aperkins, both accessing the field, setting it as accessible and retrieving the value can throw Exceptions, although the only checked exceptions you need to be mindful of are commented above.
The NoSuchFieldException would be thrown if you asked for a field by a name which did not correspond to a declared field.
obj.getClass().getDeclaredField("misspelled"); //will throw NoSuchFieldException
The IllegalAccessException would be thrown if the field was not accessible (for example, if it is private and has not been made accessible via missing out the f.setAccessible(true) line.
The RuntimeExceptions which may be thrown are either SecurityExceptions (if the JVM's SecurityManager will not allow you to change a field's accessibility), or IllegalArgumentExceptions, if you try and access the field on an object not of the field's class's type:
f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type
Try FieldUtils from Apache commons-lang3:
FieldUtils.readField(object, fieldName, true);
P.S. In my opinion, reflection is evil.
Reflection isn't the only way to resolve your issue (which is to access the private functionality/behaviour of a class/component)
An alternative solution is to extract the class from the .jar, decompile it using (say) Jode or Jad, change the field (or add an accessor), and recompile it against the original .jar. Then put the new .class ahead of the .jar in the classpath, or reinsert it in the .jar. (the jar utility allows you to extract and reinsert to an existing .jar)
As noted below, this resolves the wider issue of accessing/changing private state rather than simply accessing/changing a field.
This requires the .jar not to be signed, of course.
One other option that hasn't been mentioned yet: use Groovy. Groovy allows you to access private instance variables as a side effect of the design of the language. Whether or not you have a getter for the field, you can just use
def obj = new IWasDesignedPoorly()
def hashTable = obj.getStuffIWant()
Using the Reflection in Java you can access all the private/public fields and methods of one class to another .But as per the Oracle documentation in the section drawbacks they recommended that :
"Since reflection allows code to perform operations that would be illegal in non-reflective code, such as accessing private fields and methods, the use of reflection can result in unexpected side-effects, which may render code dysfunctional and may destroy portability. Reflective code breaks abstractions and therefore may change behavior with upgrades of the platform"
here is following code snapts to demonstrate basic concepts of Reflection
Reflection1.java
public class Reflection1{
private int i = 10;
public void methoda()
{
System.out.println("method1");
}
public void methodb()
{
System.out.println("method2");
}
public void methodc()
{
System.out.println("method3");
}
}
Reflection2.java
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Reflection2{
public static void main(String ar[]) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
{
Method[] mthd = Reflection1.class.getMethods(); // for axis the methods
Field[] fld = Reflection1.class.getDeclaredFields(); // for axis the fields
// Loop for get all the methods in class
for(Method mthd1:mthd)
{
System.out.println("method :"+mthd1.getName());
System.out.println("parametes :"+mthd1.getReturnType());
}
// Loop for get all the Field in class
for(Field fld1:fld)
{
fld1.setAccessible(true);
System.out.println("field :"+fld1.getName());
System.out.println("type :"+fld1.getType());
System.out.println("value :"+fld1.getInt(new Reflaction1()));
}
}
}
Hope it will help.
As oxbow_lakes mentions, you can use reflection to get around the access restrictions (assuming your SecurityManager will let you).
That said, if this class is so badly designed that it makes you resort to such hackery, maybe you should look for an alternative. Sure this little hack might be saving you a few hours now, but how much will it cost you down the road?
Java 9 introduced Variable Handles. You can access a private field of a class using them.
The code for your example will look like following:
var lookup = MethodHandles.lookup();
var handle = MethodHandles
.privateLookupIn(IWasDesignedPoorly.class, lookup)
.findVarHandle(IWasDesignedPoorly.class, "stuffIWant", Hashtable.class);
var value = handle.get(obj);
It is also advisable to use Lookup and VarHandle objects as static final fields.
Use the Soot Java Optimization framework to directly modify the bytecode.
http://www.sable.mcgill.ca/soot/
Soot is completely written in Java and works with new Java versions.
If using Spring:
In a testing context, ReflectionTestUtils provides some handy tools that can help out here with minimal effort. It's described as being "for use in unit and integration testing scenarios".
In a non-testing context, there is also a similar class named ReflectionUtils but this is described as "Only intended for internal use" - see this answer for a good interpretation of what this means.
To address the example in the original post:
Hashtable iWantThis = (Hashtable)ReflectionTestUtils.getField(obj, "stuffIWant");
You need to do the following:
private static Field getField(Class<?> cls, String fieldName) {
for (Class<?> c = cls; c != null; c = c.getSuperclass()) {
try {
final Field field = c.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (final NoSuchFieldException e) {
// Try parent
} catch (Exception e) {
throw new IllegalArgumentException(
"Cannot access field " + cls.getName() + "." + fieldName, e);
}
}
throw new IllegalArgumentException(
"Cannot find field " + cls.getName() + "." + fieldName);
}
You can use jOOR for that.
class Foo {
private final String value = "ABC";
}
class Bar {
private final Foo foo = new Foo();
public String value() {
return org.joor.Reflect
.on(this.foo)
.field("value")
.get();
}
}
class BarTest {
#Test
void accessPrivateField() {
Assertions.assertEquals(new Bar().value(), "ABC");
}
}
Just an additional note about reflection: I have observed in some special cases, when several classes with the same name exist in different packages, that reflection as used in the top answer may fail to pick the correct class from the object. So if you know what is the package.class of the object, then it's better to access its private field values as follows:
org.deeplearning4j.nn.layers.BaseOutputLayer ll = (org.deeplearning4j.nn.layers.BaseOutputLayer) model.getLayer(0);
Field f = Class.forName("org.deeplearning4j.nn.layers.BaseOutputLayer").getDeclaredField("solver");
f.setAccessible(true);
Solver s = (Solver) f.get(ll);
(This is the example class that was not working for me)
It is quite easy with the tool XrayInterface. Just define the missing getters/setters, e.g.
interface BetterDesigned {
Hashtable getStuffIWant(); //is mapped by convention to stuffIWant
}
and xray your poor designed project:
IWasDesignedPoorly obj = new IWasDesignedPoorly();
BetterDesigned better = ...;
System.out.println(better.getStuffIWant());
Internally this relies on reflection.
Try to go around the problem for the case, the calass of which you want to set/get data is one of your own classes.
Just create a public setter(Field f, Object value) and public Object getter(Field f) for that. You can even do some securoty check on your own inside theses member functions. E.g. for the setter:
class myClassName {
private String aString;
public set(Field field, Object value) {
// (A) do some checkings here for security
// (B) set the value
field.set(this, value);
}
}
Of course, now you have to find out the java.lang.reflect.Field for sString prior to setting of field value.
I do use this technique in a generic ResultSet-to-and-from-model-mapper.

Categories