I am having trouble finding a definitive approach to achieving the following objective.
We have a nice implementation for request scoped operation invocation from client. These operations are expected to be fast and nimble i.e. <10 ms and any heavy lifting is to executed asynchronously independent of operations invoked i.e. in a separate thread or a thread pool.
While executing these threads we have a need to inject request scoped variables into them. Since the request will be done with completed when these threads execute i.e. the thread execution is outside of request scope, we cannot directly inject the request scoped variables.
One solution seems to be ServletScopes.scopeRequest method. To use it effectively we have written a class RequestScopePropagator which effectively copies all the request scoped object references into a map and seeds them during continuation.
I am not convinced if this is the correct approach. Is there a more appropriate way / approach ?
import com.google.inject.Binding;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.Scope;
import com.google.inject.servlet.RequestScoped;
import com.google.inject.servlet.ServletScopes;
import com.google.inject.spi.BindingScopingVisitor;
public class RequestScopePropagator {
private final Map<Key<?>, Provider<?>> requestScopedValues = new HashMap<>
();
Logger logger =
Logger.getLogger(this.getClass().getName());
@Inject
RequestScopePropagator(Injector injector) {
for (Map.Entry<Key<?>, Binding<?>> entry : injector.getAllBindings().entrySet()) {
Key<?> key = entry.getKey();
Binding<?> binding = entry.getValue();
if (binding.acceptScopingVisitor(IS_REQUEST_SCOPED)) {
requestScopedValues.put(key, binding.getProvider());
}
}
}
private final BindingScopingVisitor<Boolean> IS_REQUEST_SCOPED = new BindingScopingVisitor<Boolean>() {
@Override
public Boolean visitScopeAnnotation( Class<? extends Annotation> scopeAnnotation) { return scopeAnnotation == RequestScoped.class; }
@Override
public Boolean visitScope(Scope scope) {
return scope == ServletScopes.REQUEST; }
@Override
public Boolean visitNoScoping() {
return false; }
@Override
public Boolean visitEagerSingleton() {
return false; }
};
public <T> Callable<T> continueRequest(Callable<T> callable) {
Map<Key<?>, Object> seedMap = new HashMap<Key<?>, Object>();
try {
for (Map.Entry<Key<?>, Provider<?>> entry : requestScopedValues.entrySet()) {
if (!entry.getKey().getTypeLiteral().getRawType().isAssignableFrom(HttpServletRequest.class)
&& !entry.getKey().getTypeLiteral().getRawType().isAssignableFrom(HttpServletResponse.class)
&& !entry.getKey().getTypeLiteral().getRawType().isAssignableFrom(Map.class))
seedMap.put(entry.getKey(), entry.getValue().get());
}
} catch (Exception e) {
logger.log(Level.SEVERE, "could not add request cope values in seed map for continuing request scope", e);
throw new AltAppOpsException("could not add request cope values in seed map for continuing request scope");
}
return ServletScopes.scopeRequest(callable, seedMap);
}
}