Is there any way to implement AOP logging to public method of class that implements Runnable
and ran by ExecutorService?
Thread class
@Component
@Scope("prototype")
public class FileProcessor implements Runnable {
private final LinkedBlockingQueue<File> filesQueue;
private final GiftCertificateMapper certificateMapper;
private final File errorFolder;
private static final ReentrantLock LOCK = new ReentrantLock();
private static final Logger LOGGER = LoggerFactory.getLogger(FileProcessor.class);
public FileProcessor(LinkedBlockingQueue<File> filesQueue, GiftCertificateMapper certificateMapper,
File errorFolder) {
this.filesQueue = filesQueue;
this.certificateMapper = certificateMapper;
this.errorFolder = errorFolder;
}
@Override
public void run() {
File file = null;
try {
while ((file = filesQueue.poll(100, TimeUnit.MILLISECONDS)) != null) {
processFile(file);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOGGER.warn("File thread was interrupted");
} catch (IOException e) {
LOGGER.error("Error processing file {} \n{}", file.getAbsolutePath(), e);
}
}
public void processFile(File file) throws IOException {
if (file != null) {
try {
ObjectMapper objectMapper = new ObjectMapper();
List<GiftCertificate> certificates = Arrays.asList(objectMapper.readValue(file, GiftCertificate[].class));
certificateMapper.insertList(certificates);
file.delete();
} catch (JsonParseException | UnrecognizedPropertyException | InvalidFormatException | DataIntegrityViolationException e) {
moveFileToErrorFolder(file);
}
}
}
private void moveFileToErrorFolder(File file) throws IOException {
try {
LOCK.lock();
Files.move(Paths.get(file.getAbsolutePath()), getPathForMovingFile(file), StandardCopyOption.ATOMIC_MOVE);
} finally {
LOCK.unlock();
}
}
private Path getPathForMovingFile(File fileForMove) {
File fileList[] = errorFolder.listFiles();
int filesWithSameNameCounter = 0;
if (fileList != null && fileList.length > 0) {
for (File file : fileList) {
if (file.getName().contains(fileForMove.getName())) {
filesWithSameNameCounter++;
}
}
}
return filesWithSameNameCounter > 0 ?
Paths.get(errorFolder.getAbsolutePath(), "(" + filesWithSameNameCounter + ")" + fileForMove.getName()) :
Paths.get(errorFolder.getAbsolutePath(), fileForMove.getName());
}
}
Aspect
@Aspect
@Component
@ConditionalOnProperty(
value = "file-processing.logging.enabled",
havingValue = "true",
matchIfMissing = true)
public class FileProcessingLoggingAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(FileProcessingLoggingAspect.class);
@Pointcut("execution(* com.epam.esm.processor.FileProcessor.processFile(java.io.File))")
public void processFilePointcut() {
}
@Around("processFilePointcut()")
public Object logFileProcessing(ProceedingJoinPoint joinPoint) throws Throwable {
// File file = (File) joinPoint.getArgs()[0];
// long time = System.currentTimeMillis();
Object object = joinPoint.proceed();
// long resultTime = System.currentTimeMillis() - time;
LOGGER.info("Processing of file took milliseconds");
return object;
}
}
In Spring AOP , internal method calls cannot be intercepted.
In the code shared , even though the method processFile()
is public , it gets called from run()
. This is a self reference / internal method call , which cannot be intercepted.
Details can be read in the documentation
Due to the proxy-based nature of Spring’s AOP framework, calls within the target object are, by definition, not intercepted. For JDK proxies, only public interface method calls on the proxy can be intercepted
A pointcut expression to intercept all external method calls to a class implementing Runnable
would be as follows
@Around("this(java.lang.Runnable) && within(com.epam.esm.processor..*)")
public Object logFileProcessing(ProceedingJoinPoint pjp) throws Throwable {
try {
return pjp.proceed();
} finally {
//log
System.out.println("****Logged");
}
}
Scoping designator within()
limits the scope to apply the advice.
The point cut @Pointcut("execution(* com.epam.esm.processor.FileProcessor.processFile(java.io.File))")
is valid and would work if an external method call happens to it.
Hope this helps.