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:
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 ingetOrCreate?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?