Search code examples
javaspringconcurrencyspring-securityalfresco

Is it/How to provide SecureContext to a Thread from a managed Spring bean


I want to be able to launch the creation of an Alfresco node Asynchronously to prevent a creation bottneck.

For that I'm trying to use ScheduledExecutorService initialized with value 10.

Execution Processed like follow :

from my UploadServlet --> (public) UploadManager#createNodeRef(SOME_PARAMS) --> (private synchronized) UploadManager#createNodeRef(SOME_PARAMS)

Transaction is managed inside UploadServlet

Code :

private synchronized NodeRef createNodeRef(ARGS) {
  //
  // Creation Code using other Alfresco/Spring beans
  //
  return RETURN_VALUE;
}

Async Code (What I'm trying to do)

    private NodeRef makeAsyncNodeCreation(ARGS) {
    final ScheduledFuture<NodeRef> scheduledFuture = nodeCreationExecutor.schedule(new Callable<NodeRef>() {
        @Override
        public NodeRef call() throws Exception {
            // HERE I MAKE THE EFFECTIVE NODE CREATION
            return createNodeRef(filename, type, parentNode, label, kinematic);
        }
    }, MAX_NODE_CREATION_DELAY_IN_SECONDS, SECONDS);

    try {
        return scheduledFuture.get();
    } catch (InterruptedException e) {
        throw new AsyncNodeCreationException(e);
    } catch (ExecutionException e) {
        throw new AsyncNodeCreationException(e);
    }
}

Then the public method

public NodeRef create(ARGS) {
    return makeAsyncNodeCreation(ARGS);
}

The problem

I have the following exception

net.sf.acegisecurity.AuthenticationCredentialsNotFoundException: A valid SecureContext was not provided in the RequestContext

My Question Why SecureContext does no more exist within my async call ?


Solution

  • Because the SecurityContext is hold by SecurityContextHolder in a ThreadLocal Variable (the default strategy) thus the current thread, you need to pass the SecurityContext to the Callable because it's running in a different thread and then inject it to it's own ThreadLocal in SecurityContextHolder.

    I hope this solution applies to Acegi Security :

     // Get the SecurityContext hold by the main thread
     final SecurityContext securityContext = SecurityContextHolder.getContext();
    
     final ScheduledFuture<NodeRef> scheduledFuture = nodeCreationExecutor.schedule(new Callable<NodeRef>() {
    
      @Override
       public NodeRef call() throws Exception {
    
        // Inject the securityContext 
        SecurityContextHolder.setContext(securityContext);
    
        // HERE I MAKE THE EFFECTIVE NODE CREATION
        NodeRef noderef = createNodeRef(filename, type, parentNode, label, kinematic);
    
        // Cleaning...the thread may be recycled
        SecurityContextHolder.setContext(null);
    
        return noderef;
    
     }
    }, MAX_NODE_CREATION_DELAY_IN_SECONDS, SECONDS);