I have some code that works fine without the @Async annotation but seems to fail with a NPE inside the reference to the @Autowired DAO when @Async is present on the method.
Here's the definition of the @Autowired properties
@Component
public class DocumentManager implements IDocumentManager {
private Log logger = LogFactory.getLog(this.getClass());
private OfficeManager officeManager = null;
@Autowired
private IConverterService converterService;
@Autowired
private IApplicationProperties applicationProperties;
@Autowired
private ILockDAO lockDAO;
@Autowired
private IFileAttachmentDAO fileAttachmentDAO;
And here is the method whic is failing (in the same class).
@Async
public void convertAttachment(Integer id) {
// Now because we are running asynchronously it is likely that the caller
// to the Create/Update still has a lockUID on this record.
// We will need our own lockUID before we can update the PDF property
Long retryInterval = 5000L; // 5 seconds
Integer retryCount = 5;
Integer attempts = 0;
String lockUID = null;
while (attempts < retryCount && UtilityClass.isNullOrEmpty(lockUID)) {
attempts++;
// Seems to go wrong here debugger shows
ValidationResult result = lockDAO.create(EntityTypeEnum.FILE_ATTACHMENT, id);
lockUID = lockDAO.getLockUIDFromResult(result);
if (UtilityClass.isNullOrEmpty(lockUID)) {
try {
Thread.sleep(retryInterval);
} catch (InterruptedException e) {
// http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html
Thre开发者_Python百科ad.currentThread().interrupt();
}
}
}
if (!UtilityClass.isNullOrEmpty(lockUID)) {
Here is a snippet from the stack trace:
Thread [myExecutor-1] (Suspended)
InvocationTargetException.<init>(Throwable) line: 54
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25
Method.invoke(Object, Object...) line: 597
AopUtils.invokeJoinpointUsingReflection(Object, Method, Object[]) line: 309
ReflectiveMethodInvocation.invokeJoinpoint() line: 183
ReflectiveMethodInvocation.proceed() line: 150
AsyncExecutionInterceptor$1.call() line: 80
FutureTask$Sync.innerRun() line: 303
FutureTask<V>.run() line: 138
ThreadPoolExecutor$Worker.runTask(Runnable) line: 886
ThreadPoolExecutor$Worker.run() line: 908
Thread.run() line: 619
Prompted by comments to dig further I did just that and to my shame I discovered the following.
1) The program is not actually crashing or throwing an exception.
2) The Autowired lockDAO code is being called and this in turn tries to retrieve the currently logged in user id using the code below, n.b. I am using Spring Security for authentication and Authorisation.
@Override
public User getCurrentUser()
{
String name = FlexContext.getUserPrincipal().getName();
User user = userDAO.findByName(name);
return user;
}
3) So it is in the code above that I discovered that FlexContext.getUserPrincipal() is returning a null which then causes an InvocationTargetException. Although for some reason I do not understand nothing regarding this InvocationTargetException is written to the log or console.
So it appears that because the method is being called @Async then all the methods of FlexContext return a null after the non Async method that spawns the Async thread has returned.
So how can this be resolved. My locking methods were fetching the user details based on the call to FlexContext.getUserPrinciple. I had initially thought that the problem would be resolved by telling Spring to propagate the security context to child threads as explained here: Inherit Spring Security Context in Child Threads, but that did not improve the situation.
The solution was simpler than I thought, having implemented the changes to propagate the Security Context to child threads I just needed to change my getCurrentUser() method to:
public User getCurrentUser() {
return (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
Answering at this point of time will be old though, just going through so following check could be helpful if @EnableAsync in there in your @Configuration. Also make sure method 'convertAttachment' invocation should be outside in some other bean.
精彩评论