/** * Copyright 2009-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.javacrumbs.shedlock.spring.aop; import net.javacrumbs.shedlock.core.LockConfiguration; import net.javacrumbs.shedlock.core.LockingTaskExecutor; import net.javacrumbs.shedlock.core.LockingTaskExecutor.TaskResult; import net.javacrumbs.shedlock.spring.annotation.SchedulerLock; import org.aopalliance.aop.Advice; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import net.javacrumbs.shedlock.support.annotation.NonNull; import org.springframework.aop.Pointcut; import org.springframework.aop.support.AbstractPointcutAdvisor; import org.springframework.aop.support.ComposablePointcut; import org.springframework.aop.support.annotation.AnnotationMatchingPointcut; import java.lang.annotation.Annotation; import java.util.Optional; class MethodProxyScheduledLockAdvisor extends AbstractPointcutAdvisor { private final Pointcut pointcut = new ComposablePointcut(methodPointcutFor(net.javacrumbs.shedlock.core.SchedulerLock.class)) .union(methodPointcutFor(SchedulerLock.class)); private final Advice advice; MethodProxyScheduledLockAdvisor(SpringLockConfigurationExtractor lockConfigurationExtractor, LockingTaskExecutor lockingTaskExecutor) { this.advice = new LockingInterceptor(lockConfigurationExtractor, lockingTaskExecutor); } @NonNull private static AnnotationMatchingPointcut methodPointcutFor(Class<? extends Annotation> methodAnnotationType) { return new AnnotationMatchingPointcut( null, methodAnnotationType, true ); } /** * Get the Pointcut that drives this advisor. */ @NonNull @Override public Pointcut getPointcut() { return pointcut; } @NonNull @Override public Advice getAdvice() { return advice; } private static class LockingInterceptor implements MethodInterceptor { private final SpringLockConfigurationExtractor lockConfigurationExtractor; private final LockingTaskExecutor lockingTaskExecutor; LockingInterceptor(SpringLockConfigurationExtractor lockConfigurationExtractor, LockingTaskExecutor lockingTaskExecutor) { this.lockConfigurationExtractor = lockConfigurationExtractor; this.lockingTaskExecutor = lockingTaskExecutor; } @Override public Object invoke(MethodInvocation invocation) throws Throwable { Class<?> returnType = invocation.getMethod().getReturnType(); if (returnType.isPrimitive() && !void.class.equals(returnType)) { throw new LockingNotSupportedException("Can not lock method returning primitive value"); } LockConfiguration lockConfiguration = lockConfigurationExtractor.getLockConfiguration(invocation.getThis(), invocation.getMethod()).get(); TaskResult<Object> result = lockingTaskExecutor.executeWithLock(invocation::proceed, lockConfiguration); if (Optional.class.equals(returnType)) { return toOptional(result); } else { return result.getResult(); } } private static Object toOptional(TaskResult<Object> result) { if (result.wasExecuted()) { return result.getResult(); } else { return Optional.empty(); } } } }