GH-10083: Implement Nullability in ZK module
Related to: https://github.com/spring-projects/spring-integration/issues/10083 * Add `@org.jspecify.annotations.NullMarked` to all the packages in the ZK module * Fix all the respective Nullability failures in the ZK module * Fix message in assert for `candidate` in the `LeaderInitiatorFactoryBean.afterPropertiesSet()` * Fix warning for `serialVersionUID` in the `ZookeeperLockRegistry`
This commit is contained in:
committed by
Glenn Renfro
parent
07778e1dad
commit
2c3c0d375b
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2024 the original author or authors.
|
||||
* Copyright 2015-2025 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.
|
||||
@@ -115,9 +115,7 @@ public class CuratorFrameworkFactoryBean implements FactoryBean<CuratorFramework
|
||||
this.lifecycleLock.lock();
|
||||
try {
|
||||
if (!this.running) {
|
||||
if (this.client != null) {
|
||||
this.client.start();
|
||||
}
|
||||
this.client.start();
|
||||
this.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2023 the original author or authors.
|
||||
* Copyright 2015-2025 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.
|
||||
@@ -19,6 +19,7 @@ package org.springframework.integration.zookeeper.config;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.curator.framework.CuratorFramework;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
@@ -31,6 +32,7 @@ import org.springframework.integration.leader.event.DefaultLeaderEventPublisher;
|
||||
import org.springframework.integration.leader.event.LeaderEventPublisher;
|
||||
import org.springframework.integration.zookeeper.leader.LeaderInitiator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Creates a {@link LeaderInitiator}.
|
||||
@@ -44,21 +46,22 @@ import org.springframework.util.Assert;
|
||||
public class LeaderInitiatorFactoryBean
|
||||
implements FactoryBean<LeaderInitiator>, SmartLifecycle, InitializingBean, ApplicationEventPublisherAware {
|
||||
|
||||
private CuratorFramework client;
|
||||
private @Nullable CuratorFramework client;
|
||||
|
||||
private Candidate candidate;
|
||||
private @Nullable Candidate candidate;
|
||||
|
||||
private String path;
|
||||
private @Nullable String path;
|
||||
|
||||
private LeaderInitiator leaderInitiator;
|
||||
private @Nullable LeaderInitiator leaderInitiator;
|
||||
|
||||
private boolean autoStartup = true;
|
||||
|
||||
private int phase = Integer.MAX_VALUE - 1000; // NOSONAR magic number
|
||||
|
||||
@SuppressWarnings("NullAway.Init")
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
private LeaderEventPublisher leaderEventPublisher;
|
||||
private @Nullable LeaderEventPublisher leaderEventPublisher;
|
||||
|
||||
public LeaderInitiatorFactoryBean() {
|
||||
}
|
||||
@@ -164,21 +167,24 @@ public class LeaderInitiatorFactoryBean
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
if (this.leaderInitiator == null) {
|
||||
this.leaderInitiator = new LeaderInitiator(this.client, this.candidate, this.path);
|
||||
Assert.notNull(this.client, "The 'CuratorFramework' must be provided.");
|
||||
Assert.notNull(this.candidate, "The 'candidate' must be provided.");
|
||||
this.leaderInitiator =
|
||||
StringUtils.hasText(this.path)
|
||||
? new LeaderInitiator(this.client, this.candidate, this.path)
|
||||
: new LeaderInitiator(this.client, this.candidate);
|
||||
this.leaderInitiator.setPhase(this.phase);
|
||||
this.leaderInitiator.setAutoStartup(this.autoStartup);
|
||||
if (this.leaderEventPublisher != null) {
|
||||
this.leaderInitiator.setLeaderEventPublisher(this.leaderEventPublisher);
|
||||
}
|
||||
else if (this.applicationEventPublisher != null) {
|
||||
this.leaderInitiator.setLeaderEventPublisher(
|
||||
new DefaultLeaderEventPublisher(this.applicationEventPublisher));
|
||||
LeaderEventPublisher leaderEventPublisherToSet = this.leaderEventPublisher;
|
||||
if (leaderEventPublisherToSet == null) {
|
||||
leaderEventPublisherToSet = new DefaultLeaderEventPublisher(this.applicationEventPublisher);
|
||||
}
|
||||
this.leaderInitiator.setLeaderEventPublisher(leaderEventPublisherToSet);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LeaderInitiator getObject() {
|
||||
public @Nullable LeaderInitiator getObject() {
|
||||
return this.leaderInitiator;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/**
|
||||
* Provides classes related to configuration.
|
||||
*/
|
||||
@org.jspecify.annotations.NullMarked
|
||||
package org.springframework.integration.zookeeper.config;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/**
|
||||
* Base package for zookeeper configuration.
|
||||
*/
|
||||
@org.jspecify.annotations.NullMarked
|
||||
package org.springframework.integration.zookeeper.config.xml;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2023 the original author or authors.
|
||||
* Copyright 2014-2025 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.
|
||||
@@ -28,12 +28,12 @@ import org.apache.curator.framework.imps.CuratorFrameworkState;
|
||||
import org.apache.curator.framework.recipes.leader.LeaderSelector;
|
||||
import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter;
|
||||
import org.apache.curator.framework.recipes.leader.Participant;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.integration.leader.Candidate;
|
||||
import org.springframework.integration.leader.Context;
|
||||
import org.springframework.integration.leader.event.LeaderEventPublisher;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Bootstrap leadership {@link Candidate candidates}
|
||||
@@ -79,7 +79,7 @@ public class LeaderInitiator implements SmartLifecycle {
|
||||
/**
|
||||
* Leader event publisher if set
|
||||
*/
|
||||
private LeaderEventPublisher leaderEventPublisher;
|
||||
private @Nullable LeaderEventPublisher leaderEventPublisher;
|
||||
|
||||
/**
|
||||
* @see SmartLifecycle
|
||||
@@ -94,7 +94,7 @@ public class LeaderInitiator implements SmartLifecycle {
|
||||
/**
|
||||
* Curator utility for selecting leaders.
|
||||
*/
|
||||
private volatile LeaderSelector leaderSelector;
|
||||
private volatile @Nullable LeaderSelector leaderSelector;
|
||||
|
||||
/**
|
||||
* Flag that indicates whether the leadership election for
|
||||
@@ -167,15 +167,17 @@ public class LeaderInitiator implements SmartLifecycle {
|
||||
if (!this.running) {
|
||||
if (this.client.getState() != CuratorFrameworkState.STARTED) {
|
||||
// we want to do curator start here because it needs to
|
||||
// be started before leader selector and it gets a little
|
||||
// be started before leader selector, and it gets a little
|
||||
// complicated to control ordering via beans so that
|
||||
// curator is fully started.
|
||||
this.client.start();
|
||||
}
|
||||
this.leaderSelector = new LeaderSelector(this.client, buildLeaderPath(), new LeaderListener());
|
||||
this.leaderSelector.setId(this.candidate.getId());
|
||||
this.leaderSelector.autoRequeue();
|
||||
this.leaderSelector.start();
|
||||
LeaderSelector leaderSelectorToSet =
|
||||
new LeaderSelector(this.client, buildLeaderPath(), new LeaderListener());
|
||||
leaderSelectorToSet.setId(this.candidate.getId());
|
||||
leaderSelectorToSet.autoRequeue();
|
||||
leaderSelectorToSet.start();
|
||||
this.leaderSelector = leaderSelectorToSet;
|
||||
|
||||
this.running = true;
|
||||
LOGGER.debug("Started LeaderInitiator");
|
||||
@@ -195,7 +197,10 @@ public class LeaderInitiator implements SmartLifecycle {
|
||||
this.lifecycleMonitor.lock();
|
||||
try {
|
||||
if (this.running) {
|
||||
this.leaderSelector.close();
|
||||
LeaderSelector leaderSelectorToClose = this.leaderSelector;
|
||||
if (leaderSelectorToClose != null) {
|
||||
leaderSelectorToClose.close();
|
||||
}
|
||||
this.running = false;
|
||||
LOGGER.debug("Stopped LeaderInitiator");
|
||||
}
|
||||
@@ -229,7 +234,7 @@ public class LeaderInitiator implements SmartLifecycle {
|
||||
* @return the ZooKeeper path used for leadership election by Curator
|
||||
*/
|
||||
private String buildLeaderPath() {
|
||||
String ns = StringUtils.hasText(this.namespace) ? this.namespace : DEFAULT_NAMESPACE;
|
||||
String ns = this.namespace;
|
||||
if (ns.charAt(0) != '/') {
|
||||
ns = '/' + ns;
|
||||
}
|
||||
@@ -294,12 +299,16 @@ public class LeaderInitiator implements SmartLifecycle {
|
||||
|
||||
@Override
|
||||
public boolean isLeader() {
|
||||
return LeaderInitiator.this.leaderSelector.hasLeadership();
|
||||
LeaderSelector leaderSelectorToCheck = LeaderInitiator.this.leaderSelector;
|
||||
return leaderSelectorToCheck != null && leaderSelectorToCheck.hasLeadership();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void yield() {
|
||||
LeaderInitiator.this.leaderSelector.interruptLeadership();
|
||||
LeaderSelector leaderSelectorToInterrupt = LeaderInitiator.this.leaderSelector;
|
||||
if (leaderSelectorToInterrupt != null) {
|
||||
leaderSelectorToInterrupt.interruptLeadership();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -312,13 +321,17 @@ public class LeaderInitiator implements SmartLifecycle {
|
||||
* @return the leader.
|
||||
* @since 6.0.3
|
||||
*/
|
||||
public Participant getLeader() {
|
||||
try {
|
||||
return LeaderInitiator.this.leaderSelector.getLeader();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
public @Nullable Participant getLeader() {
|
||||
LeaderSelector leaderSelectorToUse = LeaderInitiator.this.leaderSelector;
|
||||
if (leaderSelectorToUse != null) {
|
||||
try {
|
||||
return leaderSelectorToUse.getLeader();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -327,12 +340,16 @@ public class LeaderInitiator implements SmartLifecycle {
|
||||
* @since 6.0.3
|
||||
*/
|
||||
public Collection<Participant> getParticipants() {
|
||||
try {
|
||||
return LeaderInitiator.this.leaderSelector.getParticipants();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
LeaderSelector leaderSelectorToUse = LeaderInitiator.this.leaderSelector;
|
||||
if (leaderSelectorToUse != null) {
|
||||
try {
|
||||
return leaderSelectorToUse.getParticipants();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -352,12 +369,7 @@ public class LeaderInitiator implements SmartLifecycle {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRole() {
|
||||
return LeaderInitiator.this.candidate.getRole();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Participant getLeader() {
|
||||
public @Nullable Participant getLeader() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/**
|
||||
* Temporary package until s-c-c-zookeeper is released.
|
||||
*/
|
||||
@org.jspecify.annotations.NullMarked
|
||||
package org.springframework.integration.zookeeper.leader;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2023 the original author or authors.
|
||||
* Copyright 2015-2025 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.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.integration.zookeeper.lock;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@@ -63,7 +64,10 @@ public class ZookeeperLockRegistry implements ExpirableLockRegistry, DisposableB
|
||||
private final Lock locksLock = new ReentrantLock();
|
||||
|
||||
private final Map<String, ZkLock> locks =
|
||||
new LinkedHashMap<String, ZkLock>(16, 0.75F, true) {
|
||||
new LinkedHashMap<>(16, 0.75F, true) {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 7092378879531819061L;
|
||||
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Entry<String, ZkLock> eldest) {
|
||||
@@ -334,9 +338,7 @@ public class ZookeeperLockRegistry implements ExpirableLockRegistry, DisposableB
|
||||
}
|
||||
}
|
||||
catch (@SuppressWarnings("unused") TimeoutException e) {
|
||||
if (future != null) {
|
||||
future.cancel(true);
|
||||
}
|
||||
future.cancel(true);
|
||||
return false;
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/**
|
||||
* Provides classes related to locking.
|
||||
*/
|
||||
@org.jspecify.annotations.NullMarked
|
||||
package org.springframework.integration.zookeeper.lock;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2024 the original author or authors.
|
||||
* Copyright 2015-2025 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.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.integration.zookeeper.metadata;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
@@ -31,6 +32,7 @@ import org.apache.curator.framework.recipes.cache.CuratorCacheListener;
|
||||
import org.apache.curator.utils.CloseableUtils;
|
||||
import org.apache.zookeeper.KeeperException;
|
||||
import org.apache.zookeeper.data.Stat;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.integration.metadata.ListenableMetadataStore;
|
||||
@@ -67,7 +69,7 @@ public class ZookeeperMetadataStore implements ListenableMetadataStore, SmartLif
|
||||
|
||||
private String encoding = StandardCharsets.UTF_8.name();
|
||||
|
||||
private CuratorCache cache;
|
||||
private @Nullable CuratorCache cache;
|
||||
|
||||
private boolean autoStartup = true;
|
||||
|
||||
@@ -79,7 +81,7 @@ public class ZookeeperMetadataStore implements ListenableMetadataStore, SmartLif
|
||||
|
||||
public ZookeeperMetadataStore(CuratorFramework client) {
|
||||
Assert.notNull(client, "Client cannot be null");
|
||||
this.client = client; // NOSONAR
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,7 +117,7 @@ public class ZookeeperMetadataStore implements ListenableMetadataStore, SmartLif
|
||||
}
|
||||
|
||||
@Override
|
||||
public String putIfAbsent(String key, String value) {
|
||||
public @Nullable String putIfAbsent(String key, String value) {
|
||||
this.lock.lock();
|
||||
try {
|
||||
Assert.notNull(key, KEY_MUST_NOT_BE_NULL);
|
||||
@@ -207,12 +209,13 @@ public class ZookeeperMetadataStore implements ListenableMetadataStore, SmartLif
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(String key) {
|
||||
public @Nullable String get(String key) {
|
||||
this.lock.lock();
|
||||
try {
|
||||
Assert.notNull(key, KEY_MUST_NOT_BE_NULL);
|
||||
Assert.state(isRunning(), "ZookeeperMetadataStore has to be started before using.");
|
||||
return this.cache.get(getPath(key))
|
||||
return Objects.requireNonNull(this.cache)
|
||||
.get(getPath(key))
|
||||
.map(currentData -> {
|
||||
// our version is more recent than the cache
|
||||
if (this.updateMap.containsKey(key) &&
|
||||
@@ -240,7 +243,7 @@ public class ZookeeperMetadataStore implements ListenableMetadataStore, SmartLif
|
||||
}
|
||||
|
||||
@Override
|
||||
public String remove(String key) {
|
||||
public @Nullable String remove(String key) {
|
||||
this.lock.lock();
|
||||
try {
|
||||
Assert.notNull(key, KEY_MUST_NOT_BE_NULL);
|
||||
@@ -276,7 +279,7 @@ public class ZookeeperMetadataStore implements ListenableMetadataStore, SmartLif
|
||||
}
|
||||
|
||||
public String getPath(String key) {
|
||||
return "".equals(key) ? this.root : this.root + '/' + key;
|
||||
return key.isEmpty() ? this.root : this.root + '/' + key;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -344,7 +347,7 @@ public class ZookeeperMetadataStore implements ListenableMetadataStore, SmartLif
|
||||
return path.replace(this.root + '/', "");
|
||||
}
|
||||
|
||||
private record LocalChildData(String value, int version) {
|
||||
private record LocalChildData(@Nullable String value, int version) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -2,4 +2,5 @@
|
||||
* Provides classes supporting the Zookeeper-based
|
||||
* {@link org.springframework.integration.metadata.ListenableMetadataStore}
|
||||
*/
|
||||
@org.jspecify.annotations.NullMarked
|
||||
package org.springframework.integration.zookeeper.metadata;
|
||||
|
||||
Reference in New Issue
Block a user