4

I have a java class in a 3rd party lib with a private member which is assigned at class instantiation.

public class CacheLookupUtil extends AbstractCacheLookupUtil<InvocationContext> {
  @Inject
  private BeanManagerUtil beanManagerUtil;

  private CacheKeyGenerator defaultCacheKeyGenerator = new DefaultCacheKeyGenerator();
  private CacheResolverFactory defaultCacheResolverFactory = new DefaultCacheResolverFactory();

  ...
  ...
}

My problem is that the assignment of defaultCacheResolverFactory is causing an exception due to the wrong constructor having been chosen.

If I try to subclass CacheLookupUtil, this assignment is still done in the parent class, so I'm no further ahead.

Is there any mechanism I can use in Java reflection that would allow me to construct/instantiate the object, but prevent the assignment of defaultCacheResolverFactory, and allowing me to set the value via reflection?

I know this is an ugly solution, but to be honest, I cannot visualize any other way to proceed.

Eric B.
  • 23,425
  • 50
  • 169
  • 316

2 Answers2

2

Is DefaultCacheResolverFactory part of your libraries jar?

  1. If not I would guess that this is a version problem.

  2. Otherwise you should lookout for a bugfix version of your library or open a ticket.

  3. Last but not least you could use AspectJ Load-Time Weaving to manipulate the bytecode at class loading time. But this requires that you always start your code with Load-Time Weaving. See Load-Time Weaving.

So I personally would prefer option 1 or 2.

Community
  • 1
  • 1
PowerStat
  • 3,757
  • 8
  • 32
  • 57
  • Looks like I'm stuck to using AJ. But I'm not sure what kind of pointcut I need to put on something like this. If I put a pointcut against the set() method of the field, it will intercept any setters. If I put a pointcut on the constructor of the CacheLookupUtil class, I'm not sure how that affects the initialization of the private field - will the `DefaultCacheResolverFactory()` still be called? – Eric B. Aug 07 '19 at 14:22
  • I think this would answer your question better as when I wrote it myself: https://stackoverflow.com/a/17536744/3021395 – PowerStat Aug 07 '19 at 15:30
  • I'm still not sure which pointcut to use to be able to replace the initialization of a field. Would it have to be the `initialization(*.new(..))` pointcut? But then how do I prevent the original class from executing the actual initialization? Or do I have to combine `initialization(*.new(..))` and the `set( CacheResolverFactory defaultCacheResolverFactory)`? Or can I use an around pointcut against the initialization & set to prevent that field from being set and replace the object myself? – Eric B. Aug 07 '19 at 16:01
  • I think this requires an in deep understanding of object instantiation like described in: https://stackoverflow.com/a/23094875/3021395 After this you could be lucky that the `defaultCacheResolverFactory` is not final, so that you could initialize it a second time within the constructor. Last but not least some more information: https://stackoverflow.com/a/15571384/3021395 – PowerStat Aug 07 '19 at 18:31
  • Please also keep in mind that you need an exact Signature to only match the constructor of `CacheLookupUtil` - so don't use an asterisk. – PowerStat Aug 07 '19 at 18:40
2

Check the version of library that contains CacheLookupUtil (I understand that its a thirdparty class). For example, let it be jar-A.

Then check the version of jar that contains DefaultCacheResolverFactory. If its also jar-A, this effectively means that this library doesn't work at this version, so you should upgrade. If its in some jar-B, then check pom.xml of jar-A itself, what version of dependency on jar-B is required, probably you override the version of this jar.

Then adjust the version so that jar-A's expectations from the version of jar-B will match :)

For me its the best solution.

Now as for dirty tricks. One trick can be to create your own copy of CacheLookupUtil and put it into the same package, depending on class-loaders policy (you haven't specified in which environment do you run, so I assume plain java) it might load first and effectively "substitute" CacheLookupUtil from the jar.

Of course the same can be done with DefaultCacheResolverFactory (so that you could fix the no-op constructor there)

If you believe its a real bug, an another option to consider is to fork from the "buggy" library and create your own version of it with a fix. Of course you better make the developers of the original library to fix this bug so that eventually you could get back to the official version, in the world of open source, sometimes solutions like this work as long as the licencing permits doing so.

If it doesn't help, then Byte Code manipulation is the only way to fix as @PowerStat already mentioned. this I believe, Java Agent, class loading patching, AspectJ, and so forth. Hopefully you won't get there only because of this issue :)

Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
  • Unfortunately, it's the design of the library (org.jsr107.ri:cache-annotations-ri-cdi:1.1.1). The `CacheLookupUtil` class is designed that way, and even the latest version is still implemented like that. I was hoping I could play some tricks with Reflection, but cannot find anyway to do it without AspectJ. I managed to hack around it by creating my own class in my own package, then shading the entire lib into my own package structure, but it is an extremely ugly solution that I don't particularly like. – Eric B. Aug 07 '19 at 14:19
  • Reflection doesnt allow changing the structure of the class. Only to query the structure and set values to fields... – Mark Bramnik Aug 07 '19 at 14:27