Java: Lazy object storage with auto key from stack trace

27 views Asked by At

I implemented a mechanism for lazily calculated objects, just with a ConcurrentMap of ConcurrentMaps as an EJB Singleton:

@Singleton
public class LazyObjectStore {
    
    private final ConcurrentMap<String, ConcurrentMap<Integer, Object>> lazyObjects = new ConcurrentHashMap<>();
    
    @SuppressWarnings("unchecked")
    public <T> T getOrCreate(String key, Supplier<T> supplier) {
        // create the holder
        lazyObjects.computeIfAbsent(key, i -> new ConcurrentHashMap<>());
        ConcurrentMap<Integer, Object> holder = lazyObjects.get(key);
        // we use the integer 0 as the key in our holder map, so we use computeIfAbsent
        // to call the supplier at most once
        holder.computeIfAbsent(0, i -> supplier.get());
        return (T)holder.get(0);
    }
}

Now I can use my lazy Object store in my other EJBs, for example:

public class MyServiceBean {
   
    @Inject private LazyObjectStore objectStore;

    public void soSomething() {
        // some code before
        // ...
        MyComplexCalculatedResult calculationResult = objectStore.getOrCreate(
                "b58be0e0-b26f-4775-a002-f285f42177f2", // just use a generated UUID here
                () -> performComplexCalculation());
        // some code after
        // ...
    }
}

So this complex calculation in performComplexCalculation() will be done only in the first call of my doSomething().

It works good, but there is still the problem that I need an unique id for each place where I want to use my LazyObjectStore. So the question is: Can I use the Java Stack Trace to find the place where I called getOrCreate() and then use this stack trace element as the key.

So I modified my map for the lazyObjects:

private final ConcurrentMap<StackTraceElement, ConcurrentMap<Integer, Object>> lazyObjects = new ConcurrentHashMap<>();

And now in my getOrCreate(...) method I determine the StackTraceElement in a "bogus" manner:

public <T> T getOrCreate(Supplier<T> supplier) {
    StackTraceElement key = StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE)
            .walk(stackstream -> stackstream
                .filter(stackFrame -> (!stackFrame.getClassName().startsWith(LazyObjectStore.this.getClass().getCanonicalName())))
                        .filter(stackFrame -> stackFrame.getClassName().startsWith("de."))
                        .map(StackFrame::toStackTraceElement)
                        .findFirst()
                        .get());
}

I can use this idiom to find the point of the code where I call the getOrCreate-Method of my object store; and I can omit the UUID key:

    public void soSomething() {
        // some code before
        // ...
        MyComplexCalculatedResult calculationResult = objectStore.getOrCreate(
                () -> performComplexCalculation());
        // some code after
        // ...
    }

This works on my code because I develop for a german institution, so my code will always be in packages starting with "de.".

My questions are:

  1. Is there a better solution to find the point in the code? Especially in EJB context where there are a lot of proxy calls between the objectStore.getOrCreate()-call and the first line of code in getOrCreate?

  2. Will there be a condition where my code fails? I am currently using this during integration test but I don't dare me to use this in production code. Where are the caveats?

0

There are 0 answers