Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Weird NullPointerException when accessing final field

I'm not sure how to start this. I have ridiculous NullPointerException in a place where it should not be. I don't expect much from the answers since the situation looks like an anomaly, but the question and the answer (if I finally find one) may be useful in education purposes. If it won't be, I'll probably delete the question.

Caused by: java.lang.NullPointerException
        at com.ah.dao.hbase.Snapshotable.lock(Snapshotable.java:17)
        at com.ah.pipeline.dump.DumpController.dump(DumpController.java:78)

07: public abstract class Snapshotable {
08:    private final AtomicBoolean readonly = new AtomicBoolean(false);
09:
10:    abstract public TableSuit getTableInfo();
11:
12:    public boolean locked() {
13:       return readonly.get();
14:    }
15:
16:    public final void lock() {
17:        readonly.set(true); <-- happens here
18:    }
19:
20:    public final void release() {
21:        readonly.set(false);
22:    }
23: }

At first readonly variable was not final, so I though it could be an unsafe publication effect, but now I have no ideas. There are no reflection tricks with these variable in our code, but some methods of this class descendants are proxied with aspectj.


Update with aop details
@Service
public class HDao extends Snapshotable {

   @PerformanceMonitoring
   public void save(PatchEvent patchEvent) {
       if (locked()) {
            throw new DumpException(tableName);
       }

@Aspect
@Component
public class PMAdvice {

   @Around(value = "@annotation(performanceMonitoring)", argNames = "jp, p")
   public Object saveEvent(ProceedingJoinPoint jp, PerformanceMonitoring p) throws Throwable {
      // basic stuff
like image 808
AdamSkywalker Avatar asked Nov 09 '22 14:11

AdamSkywalker


1 Answers

I could reproduce that with a small program. The problem indeed was related to AOP. With the help of debugging I found that final methods do not work with AspectJ proxying.

It turned out that proxying in AspectJ is done by runtime subclassing. So it creates a wrapper subclass which delegates all methods to proxied objects. It works fine for non final methods.

simplified example:

class WrapperProxy extends MyClass {
    private MyClass delegate = new MyClass();

    @Override
    public void run() {
       delegate.run();
    }
}

This technique has a problem with final methods since they can not be overriden in subclasses. AspectJ handles it in a strange way: it creates another delegate instance of MyClass and redirects all final method calls to it. This delegate is not even initialised and in my case it had null final fields.

To fix my particular problem I just removed a final keyword from methods.

like image 112
AdamSkywalker Avatar answered Nov 14 '22 23:11

AdamSkywalker