diff --git a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/BasicCache.java b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/BasicCache.java index b7b3c0a8c0..22eaf1448b 100644 --- a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/BasicCache.java +++ b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/BasicCache.java @@ -26,12 +26,14 @@ public interface BasicCache { /** Returns the value stored in the cache, or null if not present. */ - @Nullable - V get(@NonNull K key); + @Nullable V get(@NonNull K key); /** Stores the value into the cache, replacing an existing mapping if present. */ void put(@NonNull K key, @NonNull V value); + /** Removes the entry from the cache, if present. */ + void remove(@NonNull K key); + /** Invalidates all entries from the cache. */ void clear(); diff --git a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/PutRemoveBenchmark.java b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/PutRemoveBenchmark.java new file mode 100644 index 0000000000..40f5971d52 --- /dev/null +++ b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/PutRemoveBenchmark.java @@ -0,0 +1,108 @@ +/* + * Copyright 2014 Ben Manes. All Rights Reserved. + * + * 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 com.github.benmanes.caffeine.cache; + +import java.util.Random; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Group; +import org.openjdk.jmh.annotations.GroupThreads; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; + +import site.ycsb.generator.NumberGenerator; +import site.ycsb.generator.ScrambledZipfianGenerator; + +/** + * A benchmark that loosely evaluates the put and removal performance of the cache. The cache is + * written using a Zipf distribution of keys is used to incur lock contention on popular entries. + *

+ * The performance of this benchmark is expected to be unrealistic due to removing entries. A cache + * that optimistically skips locking when the entry is absent receives a benefit, but sacrifices + * linearizability with a computation. In real-world usages the rate of explicit writes is + * relatively rare compared to reads. Thus, this benchmark is only for diagnosing performance + * concerns and should not be used to compare implementations. + *

+ *

{@code
+ *   ./gradlew jmh -PincludePattern=PutRemoveBenchmark
+ * }
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@State(Scope.Group) +public class PutRemoveBenchmark { + private static final int SIZE = (2 << 14); + private static final int MASK = SIZE - 1; + private static final int ITEMS = SIZE / 3; + + @Param({ + "Caffeine", + "LinkedHashMap_Lru", + "ConcurrentHashMap", + "ConcurrentLinkedHashMap", + "Guava", + "Cache2k", + "Ehcache3", + }) + CacheType cacheType; + + BasicCache cache; + Integer[] ints; + + @State(Scope.Thread) + public static class ThreadState { + static final Random random = new Random(); + int index = random.nextInt(); + } + + @Setup + public void setup() { + ints = new Integer[SIZE]; + cache = cacheType.create(2 * SIZE); + + // Enforce full initialization of internal structures + for (int i = 0; i < 2 * SIZE; i++) { + cache.put(i, Boolean.TRUE); + } + cache.clear(); + + // Populate with a realistic access distribution + NumberGenerator generator = new ScrambledZipfianGenerator(ITEMS); + for (int i = 0; i < SIZE; i++) { + ints[i] = generator.nextValue().intValue(); + cache.put(ints[i], Boolean.TRUE); + } + } + + @TearDown(Level.Iteration) + public void tearDown() { + cache.cleanUp(); + } + + @Benchmark @Group @GroupThreads(4) + public void put(ThreadState threadState) { + cache.put(ints[threadState.index++ & MASK], Boolean.TRUE); + } + + @Benchmark @Group @GroupThreads(4) + public void remove(ThreadState threadState) { + cache.remove(ints[threadState.index++ & MASK]); + } +} diff --git a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/Cache2k.java b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/Cache2k.java index 05838108f5..1a291b8c98 100644 --- a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/Cache2k.java +++ b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/Cache2k.java @@ -28,7 +28,7 @@ public final class Cache2k implements BasicCache { @SuppressWarnings("unchecked") public Cache2k(int maximumSize) { - cache = (Cache) Cache2kBuilder.forUnknownTypes() + cache = Cache2kBuilder.forUnknownTypes() .entryCapacity(maximumSize) .eternal(true) .build(); @@ -44,6 +44,11 @@ public void put(K key, V value) { cache.put(key, value); } + @Override + public void remove(K key) { + cache.remove(key); + } + @Override public void clear() { cache.clear(); diff --git a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/CaffeineCache.java b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/CaffeineCache.java index aab8ab9c3d..b8e8fe60ec 100644 --- a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/CaffeineCache.java +++ b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/CaffeineCache.java @@ -46,6 +46,11 @@ public void put(K key, V value) { map.put(key, value); } + @Override + public void remove(K key) { + map.remove(key); + } + @Override public void clear() { cache.invalidateAll(); diff --git a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/Collision.java b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/Collision.java index 36205fa36f..75ccaf11dc 100644 --- a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/Collision.java +++ b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/Collision.java @@ -43,6 +43,11 @@ public void put(K key, V value) { cache.putReplace(key, value); } + @Override + public void remove(K key) { + cache.remove(key); + } + @Override public void clear() { cache.clear(); diff --git a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/ConcurrentMapCache.java b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/ConcurrentMapCache.java index 11395ac064..0004592a4d 100644 --- a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/ConcurrentMapCache.java +++ b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/ConcurrentMapCache.java @@ -41,6 +41,11 @@ public void put(K key, V value) { map.put(key, value); } + @Override + public void remove(K key) { + map.remove(key); + } + @Override public void clear() { map.clear(); diff --git a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/Ehcache3.java b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/Ehcache3.java index d7aa1535b1..2ca3b7d400 100644 --- a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/Ehcache3.java +++ b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/Ehcache3.java @@ -50,6 +50,11 @@ public void put(K key, V value) { cache.put(key, value); } + @Override + public void remove(K key) { + cache.remove(key); + } + @Override public void clear() { cache.clear(); diff --git a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/ElasticSearchCache.java b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/ElasticSearchCache.java index 67e57df3fa..9d4df3f13d 100644 --- a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/ElasticSearchCache.java +++ b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/ElasticSearchCache.java @@ -42,6 +42,11 @@ public void put(K key, V value) { cache.put(key, value); } + @Override + public void remove(K key) { + cache.invalidate(key); + } + @Override public void clear() { cache.invalidateAll(); diff --git a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/ExpiringMapCache.java b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/ExpiringMapCache.java index 962536f42f..90149ad95e 100644 --- a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/ExpiringMapCache.java +++ b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/ExpiringMapCache.java @@ -43,6 +43,11 @@ public void put(K key, V value) { cache.put(key, value); } + @Override + public void remove(K key) { + cache.remove(key); + } + @Override public void clear() { cache.clear(); diff --git a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/GuavaCache.java b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/GuavaCache.java index fab8c176ca..25055e29b1 100644 --- a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/GuavaCache.java +++ b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/GuavaCache.java @@ -50,6 +50,11 @@ public void put(K key, V value) { cache.put(key, value); } + @Override + public void remove(K key) { + cache.invalidate(key); + } + @Override public void clear() { cache.invalidateAll(); diff --git a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/LinkedHashMapCache.java b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/LinkedHashMapCache.java index 0e022a298a..245b79ad60 100644 --- a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/LinkedHashMapCache.java +++ b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/LinkedHashMapCache.java @@ -44,6 +44,13 @@ public void put(K key, V value) { } } + @Override + public void remove(K key) { + synchronized (map) { + map.remove(key); + } + } + @Override public void clear() { synchronized (map) { diff --git a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/TCache.java b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/TCache.java index d6ba1358d8..2500cbab86 100644 --- a/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/TCache.java +++ b/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/impl/TCache.java @@ -47,6 +47,11 @@ public void put(K key, V value) { cache.put(key, value); } + @Override + public void remove(K key) { + cache.remove(key); + } + @Override public void clear() { cache.clear(); diff --git a/caffeine/src/main/java/com/github/benmanes/caffeine/cache/BoundedLocalCache.java b/caffeine/src/main/java/com/github/benmanes/caffeine/cache/BoundedLocalCache.java index c5d03e1ce1..de3d2d9aa2 100644 --- a/caffeine/src/main/java/com/github/benmanes/caffeine/cache/BoundedLocalCache.java +++ b/caffeine/src/main/java/com/github/benmanes/caffeine/cache/BoundedLocalCache.java @@ -2088,56 +2088,6 @@ public Map getAllPresent(Iterable keys) { @Override public @Nullable V remove(Object key) { - return hasWriter() - ? removeWithWriter(key) - : removeNoWriter(key); - } - - /** - * Removes the mapping for a key without notifying the writer. - * - * @param key key whose mapping is to be removed - * @return the removed value or null if no mapping was found - */ - @Nullable V removeNoWriter(Object key) { - Node node = data.remove(nodeFactory.newLookupKey(key)); - if (node == null) { - return null; - } - - V oldValue; - synchronized (node) { - oldValue = node.getValue(); - if (node.isAlive()) { - node.retire(); - } - } - - RemovalCause cause; - if (oldValue == null) { - cause = RemovalCause.COLLECTED; - } else if (hasExpired(node, expirationTicker().read())) { - cause = RemovalCause.EXPIRED; - } else { - cause = RemovalCause.EXPLICIT; - } - - if (hasRemovalListener()) { - @SuppressWarnings("unchecked") - K castKey = (K) key; - notifyRemoval(castKey, oldValue, cause); - } - afterWrite(new RemovalTask(node)); - return (cause == RemovalCause.EXPLICIT) ? oldValue : null; - } - - /** - * Removes the mapping for a key after notifying the writer. - * - * @param key key whose mapping is to be removed - * @return the removed value or null if no mapping was found - */ - @Nullable V removeWithWriter(Object key) { @SuppressWarnings("unchecked") K castKey = (K) key; @SuppressWarnings({"unchecked", "rawtypes"}) diff --git a/caffeine/src/main/java/com/github/benmanes/caffeine/cache/Cache.java b/caffeine/src/main/java/com/github/benmanes/caffeine/cache/Cache.java index dfd6355716..c6da398095 100644 --- a/caffeine/src/main/java/com/github/benmanes/caffeine/cache/Cache.java +++ b/caffeine/src/main/java/com/github/benmanes/caffeine/cache/Cache.java @@ -167,7 +167,7 @@ default Map getAll(@NonNull Iterable keys, * @param keys the keys whose associated values are to be removed * @throws NullPointerException if the specified collection is null or contains a null element */ - void invalidateAll(@NonNull Iterable keys); + void invalidateAll(@NonNull Iterable<@NonNull ?> keys); /** * Discards all entries in the cache. The behavior of this operation is undefined for an entry diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/issues/Issue412Test.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/issues/Issue412Test.java new file mode 100644 index 0000000000..ae850a6c30 --- /dev/null +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/issues/Issue412Test.java @@ -0,0 +1,103 @@ +/* + * Copyright 2020 Ben Manes. All Rights Reserved. + * + * 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 com.github.benmanes.caffeine.cache.issues; + +import static com.github.benmanes.caffeine.testing.ConcurrentTestHarness.DAEMON_FACTORY; +import static com.github.benmanes.caffeine.testing.ConcurrentTestHarness.timeTasks; +import static com.google.common.collect.ImmutableMultiset.toImmutableMultiset; +import static com.google.common.util.concurrent.MoreExecutors.shutdownAndAwaitTermination; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.not; + +import java.time.Duration; +import java.util.Random; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.RemovalCause; +import com.github.benmanes.caffeine.cache.testing.RemovalListeners; +import com.github.benmanes.caffeine.cache.testing.RemovalListeners.ConsumingRemovalListener; +import com.github.benmanes.caffeine.cache.testing.RemovalNotification; +import com.google.common.collect.Multiset; + +import site.ycsb.generator.NumberGenerator; +import site.ycsb.generator.ScrambledZipfianGenerator; + +/** + * Issue #412: Incorrect removal cause when expiration races with removal + * + * @author ben.manes@gmail.com (Ben Manes) + */ +@Test(groups = "isolated") +public final class Issue412Test { + private static final int NUM_THREADS = 5; + + private ConsumingRemovalListener listener; + private Cache cache; + private ExecutorService executor; + private Integer[] ints; + private Random random; + + @BeforeMethod + public void before() { + executor = Executors.newCachedThreadPool(DAEMON_FACTORY); + listener = RemovalListeners.consuming(); + cache = Caffeine.newBuilder() + .expireAfterWrite(Duration.ofNanos(10)) + .removalListener(listener) + .executor(executor) + .build(); + ints = generateSequence(); + random = new Random(); + } + + @Test + public void expire_remove() { + timeTasks(NUM_THREADS, this::addRemoveAndExpire); + shutdownAndAwaitTermination(executor, 1, TimeUnit.MINUTES); + + Multiset causes = listener.evicted().stream() + .map(RemovalNotification::getCause) + .collect(toImmutableMultiset()); + assertThat(causes, not(hasItem(RemovalCause.COLLECTED))); + } + + private void addRemoveAndExpire() { + int mask = (ints.length - 1); + int index = random.nextInt(); + for (int i = 0; i < (10 * ints.length); i++) { + Integer key = ints[index++ & mask]; + cache.put(key, Boolean.TRUE); + cache.invalidate(key); + } + } + + private static Integer[] generateSequence() { + Integer[] ints = new Integer[2 << 14]; + NumberGenerator generator = new ScrambledZipfianGenerator(ints.length / 3); + for (int i = 0; i < ints.length; i++) { + ints[i] = generator.nextValue().intValue(); + } + return ints; + } +} diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/RemovalListeners.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/RemovalListeners.java index 8792104be4..c95b9e4c40 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/RemovalListeners.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/RemovalListeners.java @@ -19,6 +19,7 @@ import java.io.Serializable; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.RejectedExecutionException; @@ -35,7 +36,7 @@ public final class RemovalListeners { private RemovalListeners() {} /** A removal listener that stores the notifications for inspection. */ - public static RemovalListener consuming() { + public static ConsumingRemovalListener consuming() { return new ConsumingRemovalListener<>(); } @@ -78,11 +79,11 @@ public static final class ConsumingRemovalListener private final List> evicted; public ConsumingRemovalListener() { - this.evicted = new ArrayList<>(); + this.evicted = Collections.synchronizedList(new ArrayList<>()); } @Override - public synchronized void onRemoval(K key, V value, RemovalCause cause) { + public void onRemoval(K key, V value, RemovalCause cause) { validate(key, value, cause); evicted.add(new RemovalNotification<>(key, value, cause)); } diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/testing/ConcurrentTestHarness.java b/caffeine/src/test/java/com/github/benmanes/caffeine/testing/ConcurrentTestHarness.java index bd390e4b41..e10d801076 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/testing/ConcurrentTestHarness.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/testing/ConcurrentTestHarness.java @@ -43,7 +43,7 @@ * @author ben.manes@gmail.com (Ben Manes) */ public final class ConcurrentTestHarness { - private static final ThreadFactory DAEMON_FACTORY = new ThreadFactoryBuilder() + public static final ThreadFactory DAEMON_FACTORY = new ThreadFactoryBuilder() .setPriority(Thread.MIN_PRIORITY).setDaemon(true).build(); public static final ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor(DAEMON_FACTORY); diff --git a/checksum.xml b/checksum.xml index 080278fa0a..08b15e14d2 100644 --- a/checksum.xml +++ b/checksum.xml @@ -26,6 +26,7 @@ + @@ -101,6 +102,7 @@ + @@ -140,9 +142,11 @@ + + @@ -241,6 +245,9 @@ D046D70ABBD137039F917CCD3786F5B3FCABA0FBAC3AE1C7597A77FA84BC75466F48A40DA53ADA6C45F05F77AF952D5EA82A9B0F873C53DF894DE0E15DB6D8AA + + 559ECA3B9FC21BE76ECAAA9E0073DC2014BA50DF2A14AD8919F358C1D7761E2C492EB09DAE1099A308C2F8C0A14635AD4583FF26B7868A782DE6A16D26F7EC6C + B3BFAD07E6A3D4D73CBCE802D8614CF4AC84E589166D243D41028DC077F84C027DF4D514F145360405F37DA73A8F2E7B65D90877A9EE1151174D2440530F9051 diff --git a/config/pmd/rulesSets.xml b/config/pmd/rulesSets.xml index 5651914da2..4f10af3af5 100644 --- a/config/pmd/rulesSets.xml +++ b/config/pmd/rulesSets.xml @@ -62,6 +62,7 @@ + diff --git a/gradle/codeQuality.gradle b/gradle/codeQuality.gradle index 3130b181e1..2cf09b8700 100644 --- a/gradle/codeQuality.gradle +++ b/gradle/codeQuality.gradle @@ -180,8 +180,6 @@ afterEvaluate { tasks.findAll { it.name.startsWith('spotbugs') }.each { it.enabled = System.properties.containsKey('spotbugs') it.group = 'SpotBugs' - it.reports.xml.enabled = false - it.reports.html.enabled = true } } diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index bf88130658..7a73e1a8ca 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -27,23 +27,23 @@ ext { versions = [ akka: '2.6.4', cache2k: '1.3.1.Alpha', - checkerFramework: '3.2.0', + checkerFramework: '3.3.0', collision: '0.3.3', commonsCompress: '1.20', - commonsLang3: '3.9', + commonsLang3: '3.10', commonsMath3: '3.6.1', concurrentlinkedhashmap: '1.4.2', config: '1.4.0', ehcache3: '3.8.1', errorprone: '2.3.4', errorproneJavac: '9+181-r4173-1', - elasticSearch: '7.6.1', + elasticSearch: '7.6.2', expiringMap: '0.5.9', fastfilter: 'bf0b02297f', fastutil: '8.3.1', flipTables: '1.1.0', - guava: '28.2-jre', - jackrabbit: '1.24.0', + guava: '29.0-jre', + jackrabbit: '1.26.0', jamm: '0.3.3', javaObjectLayout: '0.10', javapoet: '1.12.1', @@ -69,14 +69,14 @@ ext { jctools: '3.0.0', junit: '4.13', mockito: '3.3.3', - paxExam: '4.13.1', + paxExam: '4.13.3', testng: '7.2.0', truth: '0.24', ] pluginVersions = [ apt: '0.21', - bnd: '5.0.0', - checkstyle: '8.30', + bnd: '5.0.1', + checkstyle: '8.31', coveralls: '2.8.4', coverity: '1.0.10', errorprone: '1.1.1', @@ -84,12 +84,12 @@ ext { jmh: '0.5.0', jmhReport: '0.9.0', nullaway: '1.0.1', - pmd: '6.22.0', + pmd: '6.23.0', semanticVersioning: '1.1.0', shadow: '5.2.0', sonarqube: '2.8.0.1969', - spotbugs: '4.0.0-RC1', - spotbugsPlugin: '3.0.0', + spotbugs: '4.0.2', + spotbugsPlugin: '4.0.5', stats: '0.2.2', versions: '0.28.0', ] @@ -188,10 +188,7 @@ ext { semanticVersioning: "io.ehdev:gradle-semantic-versioning:${pluginVersions.semanticVersioning}", shadow: "com.github.jengelman.gradle.plugins:shadow:${pluginVersions.shadow}", sonarqube: "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:${pluginVersions.sonarqube}", - spotbugs: [ - "com.github.spotbugs:spotbugs-gradle-plugin:${pluginVersions.spotbugsPlugin}", - libraries.guava // https://github.com/spotbugs/spotbugs-gradle-plugin/issues/119 - ], + spotbugs: "gradle.plugin.com.github.spotbugs.snom:spotbugs-gradle-plugin:${pluginVersions.spotbugsPlugin}", stats: "org.kordamp.gradle:stats-gradle-plugin:${pluginVersions.stats}", versions: "com.github.ben-manes:gradle-versions-plugin:${pluginVersions.versions}", ] diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 17bc87bdbb..29b89d4399 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-rc-4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME