Skip to content

Commit

Permalink
#429 Rudimentary CDI support
Browse files Browse the repository at this point in the history
  • Loading branch information
lukas-krecan committed Nov 15, 2022
1 parent bc29ab4 commit 8512a8e
Show file tree
Hide file tree
Showing 13 changed files with 705 additions and 0 deletions.
91 changes: 91 additions & 0 deletions cdi/shedlock-cdi-vintage/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<artifactId>shedlock-parent</artifactId>
<groupId>net.javacrumbs.shedlock</groupId>
<version>5.0.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

<artifactId>shedlock-cdi-vintage</artifactId>
<version>5.0.0-SNAPSHOT</version>


<dependencies>
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-core</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-api</artifactId>
<version>2.0.2</version>
</dependency>

<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>1.3.5</version>
</dependency>

<dependency>
<groupId>org.eclipse.microprofile.config</groupId>
<artifactId>microprofile-config-api</artifactId>
<version>2.0.1</version>
</dependency>

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.ver}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.ver}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.ver}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.ver}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj.ver}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Automatic-Module-Name>
net.javacrumbs.shedlock.cdivintage
</Automatic-Module-Name>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package net.javacrumbs.shedlock.cdi;

import javax.enterprise.util.Nonbinding;
import javax.interceptor.InterceptorBinding;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Inherited
public @interface SchedulerLock {
/**
* Lock name.
*/
@Nonbinding String name();

/**
* How long the lock should be kept in case the machine which obtained the lock died before releasing it.
* This is just a fallback, under normal circumstances the lock is released as soon the tasks finishes. Can be any format
* supported by <a href="https://docs.micronaut.io/latest/guide/config.html#_duration_conversion">Duration Conversion</a>
* <p>
*/
@Nonbinding String lockAtMostFor() default "";

/**
* The lock will be held at least for this period of time. Can be used if you really need to execute the task
* at most once in given period of time. If the duration of the task is shorter than clock difference between nodes, the task can
* be theoretically executed more than once (one node after another). By setting this parameter, you can make sure that the
* lock will be kept at least for given period of time. Can be any format
* supported by <a href="https://docs.micronaut.io/latest/guide/config.html#_duration_conversion">Duration Conversion</a>
*/
@Nonbinding String lockAtLeastFor() default "";
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* Copyright 2009 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.cdi.internal;


import net.javacrumbs.shedlock.cdi.SchedulerLock;
import net.javacrumbs.shedlock.core.ClockProvider;
import net.javacrumbs.shedlock.core.LockConfiguration;

import java.lang.reflect.Method;
import java.time.Duration;
import java.util.Optional;

import static java.util.Objects.requireNonNull;
import static net.javacrumbs.shedlock.cdi.internal.Utils.parseDuration;

class CdiLockConfigurationExtractor {
private final Duration defaultLockAtMostFor;
private final Duration defaultLockAtLeastFor;

CdiLockConfigurationExtractor(Duration defaultLockAtMostFor, Duration defaultLockAtLeastFor) {
this.defaultLockAtMostFor = requireNonNull(defaultLockAtMostFor);
this.defaultLockAtLeastFor = requireNonNull(defaultLockAtLeastFor);
}


Optional<LockConfiguration> getLockConfiguration(Method method) {
Optional<SchedulerLock> annotation = findAnnotation(method);
return annotation.map(this::getLockConfiguration);
}

private LockConfiguration getLockConfiguration(SchedulerLock annotation) {
return new LockConfiguration(
ClockProvider.now(),
getName(annotation),
getLockAtMostFor(annotation),
getLockAtLeastFor(annotation)
);
}

private String getName(SchedulerLock annotation) {
return annotation.name();
}

Duration getLockAtMostFor(SchedulerLock annotation) {
return getValue(
annotation.lockAtMostFor(),
this.defaultLockAtMostFor,
"lockAtMostFor"
);
}

Duration getLockAtLeastFor(SchedulerLock annotation) {
return getValue(
annotation.lockAtLeastFor(),
this.defaultLockAtLeastFor,
"lockAtLeastFor"
);
}

private Duration getValue(String stringValueFromAnnotation, Duration defaultValue, String paramName) {
if (!stringValueFromAnnotation.isEmpty()) {
return parseDuration(stringValueFromAnnotation);
} else {
return defaultValue;
}
}

Optional<SchedulerLock> findAnnotation(Method method) {
return Optional.ofNullable(method.getAnnotation(SchedulerLock.class));
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Copyright 2009 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.cdi.internal;

import net.javacrumbs.shedlock.support.LockException;

class LockingNotSupportedException extends LockException {
LockingNotSupportedException() {
super("Can not lock method returning value (do not know what to return if it's locked)");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package net.javacrumbs.shedlock.cdi.internal;

import net.javacrumbs.shedlock.cdi.SchedulerLock;
import net.javacrumbs.shedlock.core.DefaultLockingTaskExecutor;
import net.javacrumbs.shedlock.core.LockConfiguration;
import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.core.LockingTaskExecutor;
import org.eclipse.microprofile.config.ConfigProvider;

import javax.annotation.Priority;
import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
import java.time.Duration;
import java.util.Objects;
import java.util.Optional;

import static net.javacrumbs.shedlock.cdi.internal.Utils.parseDuration;


@SchedulerLock(name = "?")
@Priority(3001)
@Interceptor
public class SchedulerLockInterceptor {
private final LockingTaskExecutor lockingTaskExecutor;
private final CdiLockConfigurationExtractor lockConfigurationExtractor;

@Inject
public SchedulerLockInterceptor(LockProvider lockProvider) {
this.lockingTaskExecutor = new DefaultLockingTaskExecutor(lockProvider);
String lockAtMostFor = getConfigValue("shedlock.defaults.lock-at-most-for");
String lockAtLeastFor = getConfigValue("shedlock.defaults.lock-at-least-for");
Objects.requireNonNull(lockAtMostFor, "shedlock.defaults.lock-at-most-for parameter is mandatory");
this.lockConfigurationExtractor = new CdiLockConfigurationExtractor(
parseDuration(lockAtMostFor),
lockAtLeastFor != null ? parseDuration(lockAtLeastFor) : Duration.ZERO
);
}

private static String getConfigValue(String propertyName) {
return ConfigProvider.getConfig().getConfigValue(propertyName).getValue();
}

@AroundInvoke
Object lock(InvocationContext context) throws Throwable {
Class<?> returnType = context.getMethod().getReturnType();
if (!void.class.equals(returnType) && !Void.class.equals(returnType)) {
throw new LockingNotSupportedException();
}

Optional<LockConfiguration> lockConfiguration = lockConfigurationExtractor.getLockConfiguration(context.getMethod());
if (lockConfiguration.isPresent()) {
lockingTaskExecutor.executeWithLock((LockingTaskExecutor.Task) context::proceed, lockConfiguration.get());
return null;
} else {
return context.proceed();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package net.javacrumbs.shedlock.cdi.internal;

import java.time.Duration;
import java.time.format.DateTimeParseException;

class Utils {
static Duration parseDuration(String value) {
value = value.trim();
if (value.isEmpty()) {
return null;
}

try {
return Duration.parse(value);
} catch (DateTimeParseException e) {
throw new IllegalArgumentException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" bean-discovery-mode="all" version="2.0">
</beans>
Loading

0 comments on commit 8512a8e

Please sign in to comment.