Fixed - host name containing underscore cause NPE. Illegal reflective access by org.redisson.misc.URIBuilder warning removed #2150 #2029

pull/2160/head^2
Nikita Koksharov 6 years ago
parent b2fc908615
commit 0ce487f08d

@ -15,7 +15,6 @@
*/
package org.redisson;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@ -37,7 +36,7 @@ import org.redisson.connection.ConnectionListener;
import org.redisson.connection.ConnectionManager;
import org.redisson.connection.MasterSlaveEntry;
import org.redisson.connection.RedisClientEntry;
import org.redisson.misc.URIBuilder;
import org.redisson.misc.RedisURI;
/**
*
@ -56,15 +55,15 @@ public class RedisNodes<N extends Node> implements NodesGroup<N> {
@Override
public N getNode(String address) {
Collection<MasterSlaveEntry> entries = connectionManager.getEntrySet();
URI addr = URIBuilder.create(address);
RedisURI addr = new RedisURI(address);
for (MasterSlaveEntry masterSlaveEntry : entries) {
if (masterSlaveEntry.getAllEntries().isEmpty()
&& URIBuilder.compare(masterSlaveEntry.getClient().getAddr(), addr)) {
&& RedisURI.compare(masterSlaveEntry.getClient().getAddr(), addr)) {
return (N) new RedisClientEntry(masterSlaveEntry.getClient(), connectionManager.getCommandExecutor(), NodeType.MASTER);
}
for (ClientConnectionsEntry entry : masterSlaveEntry.getAllEntries()) {
if (URIBuilder.compare(entry.getClient().getAddr(), addr)
if (RedisURI.compare(entry.getClient().getAddr(), addr)
&& entry.getFreezeReason() != FreezeReason.MANAGER) {
return (N) new RedisClientEntry(entry.getClient(), connectionManager.getCommandExecutor(), entry.getNodeType());
}

@ -17,7 +17,6 @@ package org.redisson.client;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.UnknownHostException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@ -28,6 +27,7 @@ import org.redisson.api.RFuture;
import org.redisson.client.handler.RedisChannelInitializer;
import org.redisson.client.handler.RedisChannelInitializer.Type;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedisURI;
import org.redisson.misc.RedissonPromise;
import io.netty.bootstrap.Bootstrap;
@ -63,7 +63,7 @@ public final class RedisClient {
private final AtomicReference<RFuture<InetSocketAddress>> resolvedAddrFuture = new AtomicReference<RFuture<InetSocketAddress>>();
private final Bootstrap bootstrap;
private final Bootstrap pubSubBootstrap;
private final URI uri;
private final RedisURI uri;
private InetSocketAddress resolvedAddr;
private final ChannelGroup channels;

@ -20,7 +20,7 @@ import java.net.URI;
import java.util.concurrent.ExecutorService;
import org.redisson.config.SslProvider;
import org.redisson.misc.URIBuilder;
import org.redisson.misc.RedisURI;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.socket.SocketChannel;
@ -35,7 +35,7 @@ import io.netty.util.Timer;
*/
public class RedisClientConfig {
private URI address;
private RedisURI address;
private InetSocketAddress addr;
private Timer timer;
@ -105,23 +105,23 @@ public class RedisClientConfig {
}
public RedisClientConfig setAddress(String host, int port) {
this.address = URIBuilder.create("redis://" + host + ":" + port);
this.address = new RedisURI("redis://" + host + ":" + port);
return this;
}
public RedisClientConfig setAddress(String address) {
this.address = URIBuilder.create(address);
this.address = new RedisURI(address);
return this;
}
public RedisClientConfig setAddress(InetSocketAddress addr, URI address) {
public RedisClientConfig setAddress(InetSocketAddress addr, RedisURI address) {
this.addr = addr;
this.address = address;
return this;
}
public RedisClientConfig setAddress(URI address) {
public RedisClientConfig setAddress(RedisURI address) {
this.address = address;
return this;
}
public URI getAddress() {
public RedisURI getAddress() {
return address;
}
public InetSocketAddress getAddr() {

@ -15,9 +15,7 @@
*/
package org.redisson.client;
import java.net.URI;
import org.redisson.misc.URIBuilder;
import org.redisson.misc.RedisURI;
/**
*
@ -29,18 +27,18 @@ public class RedisRedirectException extends RedisException {
private static final long serialVersionUID = 181505625075250011L;
private final int slot;
private final URI url;
private final RedisURI url;
public RedisRedirectException(int slot, String url) {
this.slot = slot;
this.url = URIBuilder.create("redis://" + url);
this.url = new RedisURI("redis://" + url);
}
public int getSlot() {
return slot;
}
public URI getUrl() {
public RedisURI getUrl() {
return url;
}

@ -16,7 +16,6 @@
package org.redisson.cluster;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
@ -36,6 +35,7 @@ import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import org.redisson.api.NodeType;
import org.redisson.api.RFuture;
@ -58,8 +58,8 @@ import org.redisson.connection.MasterSlaveConnectionManager;
import org.redisson.connection.MasterSlaveEntry;
import org.redisson.connection.SingleEntry;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedisURI;
import org.redisson.misc.RedissonPromise;
import org.redisson.misc.URIBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -82,7 +82,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
private ScheduledFuture<?> monitorFuture;
private volatile URI lastClusterNode;
private volatile RedisURI lastClusterNode;
private RedisStrictCommand<List<ClusterNodeInfo>> clusterNodesCommand;
@ -103,7 +103,8 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
Throwable lastException = null;
List<String> failedMasters = new ArrayList<String>();
for (URI addr : cfg.getNodeAddresses()) {
for (String address : cfg.getNodeAddresses()) {
RedisURI addr = new RedisURI(address);
RFuture<RedisConnection> connectionFuture = connectToNode(cfg, addr, null, addr.getHost());
try {
RedisConnection connection = connectionFuture.syncUninterruptibly().getNow();
@ -180,7 +181,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
}
@Override
protected RedisClientConfig createRedisConfig(NodeType type, URI address, int timeout, int commandTimeout, String sslHostname) {
protected RedisClientConfig createRedisConfig(NodeType type, RedisURI address, int timeout, int commandTimeout, String sslHostname) {
RedisClientConfig result = super.createRedisConfig(type, address, timeout, commandTimeout, sslHostname);
result.setReadOnly(type == NodeType.SLAVE && config.getReadMode() != ReadMode.MASTER);
return result;
@ -226,14 +227,15 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
}
MasterSlaveServersConfig config = create(cfg);
config.setMasterAddress(partition.getMasterAddress());
config.setMasterAddress(partition.getMasterAddress().toString());
MasterSlaveEntry e;
List<RFuture<Void>> futures = new ArrayList<RFuture<Void>>();
if (config.checkSkipSlavesInit()) {
e = new SingleEntry(ClusterConnectionManager.this, config);
} else {
config.setSlaveAddresses(partition.getSlaveAddresses());
Set<String> slaveAddresses = partition.getSlaveAddresses().stream().map(r -> r.toString()).collect(Collectors.toSet());
config.setSlaveAddresses(slaveAddresses);
e = new MasterSlaveEntry(ClusterConnectionManager.this, config);
@ -247,7 +249,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
}
}
RFuture<RedisClient> f = e.setupMasterEntry(config.getMasterAddress());
RFuture<RedisClient> f = e.setupMasterEntry(new RedisURI(config.getMasterAddress()));
RPromise<Void> initFuture = new RedissonPromise<Void>();
futures.add(initFuture);
f.onComplete((res, ex3) -> {
@ -275,12 +277,13 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
return result;
}
private void scheduleClusterChangeCheck(ClusterServersConfig cfg, Iterator<URI> iterator) {
private void scheduleClusterChangeCheck(ClusterServersConfig cfg, Iterator<RedisURI> iterator) {
monitorFuture = group.schedule(new Runnable() {
@Override
public void run() {
if (configEndpointHostName != null) {
URI uri = cfg.getNodeAddresses().iterator().next();
String address = cfg.getNodeAddresses().iterator().next();
RedisURI uri = new RedisURI(address);
AddressResolver<InetSocketAddress> resolver = resolverGroup.getResolver(getGroup().next());
Future<List<InetSocketAddress>> allNodes = resolver.resolveAll(InetSocketAddress.createUnresolved(uri.getHost(), uri.getPort()));
allNodes.addListener(new FutureListener<List<InetSocketAddress>>() {
@ -288,34 +291,34 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
public void operationComplete(Future<List<InetSocketAddress>> future) throws Exception {
AtomicReference<Throwable> lastException = new AtomicReference<Throwable>(future.cause());
if (!future.isSuccess()) {
checkClusterState(cfg, Collections.<URI>emptyList().iterator(), lastException);
checkClusterState(cfg, Collections.<RedisURI>emptyList().iterator(), lastException);
return;
}
List<URI> nodes = new ArrayList<URI>();
List<RedisURI> nodes = new ArrayList<>();
for (InetSocketAddress addr : future.getNow()) {
URI node = URIBuilder.create(uri.getScheme() + "://" + addr.getAddress().getHostAddress() + ":" + addr.getPort());
URI address = applyNatMap(node);
RedisURI node = new RedisURI(uri.getScheme() + "://" + addr.getAddress().getHostAddress() + ":" + addr.getPort());
RedisURI address = applyNatMap(node);
nodes.add(address);
}
Iterator<URI> nodesIterator = nodes.iterator();
Iterator<RedisURI> nodesIterator = nodes.iterator();
checkClusterState(cfg, nodesIterator, lastException);
}
});
} else {
AtomicReference<Throwable> lastException = new AtomicReference<Throwable>();
Iterator<URI> nodesIterator = iterator;
Iterator<RedisURI> nodesIterator = iterator;
if (nodesIterator == null) {
List<URI> nodes = new ArrayList<URI>();
List<URI> slaves = new ArrayList<URI>();
List<RedisURI> nodes = new ArrayList<>();
List<RedisURI> slaves = new ArrayList<>();
for (ClusterPartition partition : getLastPartitions()) {
if (!partition.isMasterFail()) {
nodes.add(partition.getMasterAddress());
}
Set<URI> partitionSlaves = new HashSet<URI>(partition.getSlaveAddresses());
Set<RedisURI> partitionSlaves = new HashSet<>(partition.getSlaveAddresses());
partitionSlaves.removeAll(partition.getFailedSlaveAddresses());
slaves.addAll(partitionSlaves);
}
@ -332,7 +335,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
}, cfg.getScanInterval(), TimeUnit.MILLISECONDS);
}
private void checkClusterState(ClusterServersConfig cfg, Iterator<URI> iterator, AtomicReference<Throwable> lastException) {
private void checkClusterState(ClusterServersConfig cfg, Iterator<RedisURI> iterator, AtomicReference<Throwable> lastException) {
if (!iterator.hasNext()) {
if (lastException.get() != null) {
log.error("Can't update cluster state", lastException.get());
@ -343,7 +346,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
if (!getShutdownLatch().acquire()) {
return;
}
URI uri = iterator.next();
RedisURI uri = iterator.next();
RFuture<RedisConnection> connectionFuture = connectToNode(cfg, uri, null, configEndpointHostName);
connectionFuture.onComplete((connection, e) -> {
if (e != null) {
@ -358,7 +361,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
}
private void updateClusterState(ClusterServersConfig cfg, RedisConnection connection,
Iterator<URI> iterator, URI uri, AtomicReference<Throwable> lastException) {
Iterator<RedisURI> iterator, RedisURI uri, AtomicReference<Throwable> lastException) {
RFuture<List<ClusterNodeInfo>> future = connection.async(clusterNodesCommand);
future.onComplete((nodes, e) -> {
if (e != null) {
@ -402,7 +405,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
MasterSlaveEntry entry = getEntry(currentPart.slots().nextSetBit(0));
// should be invoked first in order to remove stale failedSlaveAddresses
Set<URI> addedSlaves = addRemoveSlaves(entry, currentPart, newPart);
Set<RedisURI> addedSlaves = addRemoveSlaves(entry, currentPart, newPart);
// Do some slaves have changed state from failed to alive?
upDownSlaves(entry, currentPart, newPart, addedSlaves);
@ -411,20 +414,20 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
}
}
private void upDownSlaves(MasterSlaveEntry entry, ClusterPartition currentPart, ClusterPartition newPart, Set<URI> addedSlaves) {
Set<URI> aliveSlaves = new HashSet<URI>(currentPart.getFailedSlaveAddresses());
private void upDownSlaves(MasterSlaveEntry entry, ClusterPartition currentPart, ClusterPartition newPart, Set<RedisURI> addedSlaves) {
Set<RedisURI> aliveSlaves = new HashSet<>(currentPart.getFailedSlaveAddresses());
aliveSlaves.removeAll(addedSlaves);
aliveSlaves.removeAll(newPart.getFailedSlaveAddresses());
for (URI uri : aliveSlaves) {
for (RedisURI uri : aliveSlaves) {
currentPart.removeFailedSlaveAddress(uri);
if (entry.hasSlave(uri) && entry.slaveUp(uri, FreezeReason.MANAGER)) {
log.info("slave: {} has up for slot ranges: {}", uri, currentPart.getSlotRanges());
}
}
Set<URI> failedSlaves = new HashSet<URI>(newPart.getFailedSlaveAddresses());
Set<RedisURI> failedSlaves = new HashSet<>(newPart.getFailedSlaveAddresses());
failedSlaves.removeAll(currentPart.getFailedSlaveAddresses());
for (URI uri : failedSlaves) {
for (RedisURI uri : failedSlaves) {
currentPart.addFailedSlaveAddress(uri);
if (entry.slaveDown(uri, FreezeReason.MANAGER)) {
log.warn("slave: {} has down for slot ranges: {}", uri, currentPart.getSlotRanges());
@ -432,11 +435,11 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
}
}
private Set<URI> addRemoveSlaves(MasterSlaveEntry entry, ClusterPartition currentPart, ClusterPartition newPart) {
Set<URI> removedSlaves = new HashSet<URI>(currentPart.getSlaveAddresses());
private Set<RedisURI> addRemoveSlaves(MasterSlaveEntry entry, ClusterPartition currentPart, ClusterPartition newPart) {
Set<RedisURI> removedSlaves = new HashSet<>(currentPart.getSlaveAddresses());
removedSlaves.removeAll(newPart.getSlaveAddresses());
for (URI uri : removedSlaves) {
for (RedisURI uri : removedSlaves) {
currentPart.removeSlaveAddress(uri);
if (entry.slaveDown(uri, FreezeReason.MANAGER)) {
@ -444,9 +447,9 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
}
}
Set<URI> addedSlaves = new HashSet<URI>(newPart.getSlaveAddresses());
Set<RedisURI> addedSlaves = new HashSet<>(newPart.getSlaveAddresses());
addedSlaves.removeAll(currentPart.getSlaveAddresses());
for (URI uri : addedSlaves) {
for (RedisURI uri : addedSlaves) {
RFuture<Void> future = entry.addSlave(uri);
future.onComplete((res, ex) -> {
if (ex != null) {
@ -499,8 +502,8 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
ClusterPartition newMasterPart = find(newPartitions, slot);
// does partition has a new master?
if (!newMasterPart.getMasterAddress().equals(currentPart.getMasterAddress())) {
URI newUri = newMasterPart.getMasterAddress();
URI oldUri = currentPart.getMasterAddress();
RedisURI newUri = newMasterPart.getMasterAddress();
RedisURI oldUri = currentPart.getMasterAddress();
RFuture<RedisClient> future = changeMaster(slot, newUri);
future.onComplete((res, e) -> {
@ -693,10 +696,10 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
return result;
}
public URI applyNatMap(URI address) {
public RedisURI applyNatMap(RedisURI address) {
String mappedAddress = natMap.get(address.getHost() + ":" + address.getPort());
if (mappedAddress != null) {
return URIBuilder.create(address.getScheme() + "://" + mappedAddress);
return new RedisURI(address.getScheme() + "://" + mappedAddress);
}
return address;
}
@ -718,7 +721,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
ClusterPartition partition = getPartition(partitions, id);
URI address = applyNatMap(clusterNodeInfo.getAddress());
RedisURI address = applyNatMap(clusterNodeInfo.getAddress());
if (clusterNodeInfo.containsFlag(Flag.SLAVE)) {
slavePartition.setParent(partition);
@ -751,10 +754,10 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
if (cp.getParent() != null && cp.getParent().getType() == Type.MASTER) {
ClusterPartition parent = cp.getParent();
for (URI addr : cp.getSlaveAddresses()) {
for (RedisURI addr : cp.getSlaveAddresses()) {
parent.addSlaveAddress(addr);
}
for (URI addr : cp.getFailedSlaveAddresses()) {
for (RedisURI addr : cp.getFailedSlaveAddresses()) {
parent.addFailedSlaveAddress(addr);
}
}
@ -787,7 +790,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
}
@Override
public URI getLastClusterNode() {
public RedisURI getLastClusterNode() {
return lastClusterNode;
}

@ -15,11 +15,11 @@
*/
package org.redisson.cluster;
import java.net.URI;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import org.redisson.misc.URIBuilder;
import org.redisson.misc.RedisURI;
/**
*
@ -33,7 +33,7 @@ public class ClusterNodeInfo {
private final String nodeInfo;
private String nodeId;
private URI address;
private RedisURI address;
private final Set<Flag> flags = EnumSet.noneOf(Flag.class);
private String slaveOf;
@ -50,11 +50,11 @@ public class ClusterNodeInfo {
this.nodeId = nodeId;
}
public URI getAddress() {
public RedisURI getAddress() {
return address;
}
public void setAddress(String address) {
this.address = URIBuilder.create(address);
this.address = new RedisURI(address);
}
public void addSlotRange(ClusterSlotRange range) {

@ -15,12 +15,13 @@
*/
package org.redisson.cluster;
import java.net.URI;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.redisson.misc.RedisURI;
/**
*
* @author Nikita Koksharov
@ -34,9 +35,9 @@ public class ClusterPartition {
private final String nodeId;
private boolean masterFail;
private URI masterAddress;
private final Set<URI> slaveAddresses = new HashSet<URI>();
private final Set<URI> failedSlaves = new HashSet<URI>();
private RedisURI masterAddress;
private final Set<RedisURI> slaveAddresses = new HashSet<>();
private final Set<RedisURI> failedSlaves = new HashSet<>();
private final BitSet slots = new BitSet();
private final Set<ClusterSlotRange> slotRanges = new HashSet<ClusterSlotRange>();
@ -123,30 +124,30 @@ public class ClusterPartition {
return slots.cardinality();
}
public URI getMasterAddress() {
public RedisURI getMasterAddress() {
return masterAddress;
}
public void setMasterAddress(URI masterAddress) {
public void setMasterAddress(RedisURI masterAddress) {
this.masterAddress = masterAddress;
}
public void addFailedSlaveAddress(URI address) {
public void addFailedSlaveAddress(RedisURI address) {
failedSlaves.add(address);
}
public Set<URI> getFailedSlaveAddresses() {
public Set<RedisURI> getFailedSlaveAddresses() {
return Collections.unmodifiableSet(failedSlaves);
}
public void removeFailedSlaveAddress(URI uri) {
public void removeFailedSlaveAddress(RedisURI uri) {
failedSlaves.remove(uri);
}
public void addSlaveAddress(URI address) {
public void addSlaveAddress(RedisURI address) {
slaveAddresses.add(address);
}
public Set<URI> getSlaveAddresses() {
public Set<RedisURI> getSlaveAddresses() {
return Collections.unmodifiableSet(slaveAddresses);
}
public void removeSlaveAddress(URI uri) {
public void removeSlaveAddress(RedisURI uri) {
slaveAddresses.remove(uri);
failedSlaves.remove(uri);
}

@ -15,15 +15,13 @@
*/
package org.redisson.config;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.redisson.misc.URIBuilder;
/**
*
* @author Nikita Koksharov
@ -36,7 +34,7 @@ public class ClusterServersConfig extends BaseMasterSlaveServersConfig<ClusterSe
/**
* Redis cluster node urls list
*/
private List<URI> nodeAddresses = new ArrayList<URI>();
private List<String> nodeAddresses = new ArrayList<>();
/**
* Redis cluster scan interval in milliseconds
@ -60,15 +58,13 @@ public class ClusterServersConfig extends BaseMasterSlaveServersConfig<ClusterSe
* @return config
*/
public ClusterServersConfig addNodeAddress(String... addresses) {
for (String address : addresses) {
nodeAddresses.add(URIBuilder.create(address));
}
nodeAddresses.addAll(Arrays.asList(addresses));
return this;
}
public List<URI> getNodeAddresses() {
public List<String> getNodeAddresses() {
return nodeAddresses;
}
void setNodeAddresses(List<URI> nodeAddresses) {
void setNodeAddresses(List<String> nodeAddresses) {
this.nodeAddresses = nodeAddresses;
}

@ -28,7 +28,6 @@ import org.redisson.connection.AddressResolverGroupFactory;
import org.redisson.connection.ConnectionManager;
import org.redisson.connection.DnsAddressResolverGroupFactory;
import org.redisson.connection.ReplicatedConnectionManager;
import org.redisson.misc.URIBuilder;
import io.netty.channel.EventLoopGroup;
@ -95,10 +94,6 @@ public class Config {
public Config() {
}
static {
URIBuilder.patchUriObject();
}
public Config(Config oldConf) {
setExecutor(oldConf.getExecutor());

@ -21,9 +21,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
import java.net.URL;
import java.util.List;
import java.util.Scanner;
import java.util.UUID;
import java.util.regex.Matcher;
@ -42,7 +40,6 @@ import org.redisson.connection.SingleConnectionManager;
import org.redisson.connection.balancer.LoadBalancer;
import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
@ -68,38 +65,6 @@ public class ConfigSupport {
}
public abstract static class SingleSeverConfigMixIn {
@JsonProperty
List<URI> address;
@JsonIgnore
abstract SingleServerConfig setAddress(String address);
@JsonIgnore
abstract URI getAddress();
@JsonIgnore
abstract void setAddress(URI address);
}
public abstract static class MasterSlaveServersConfigMixIn {
@JsonProperty
List<URI> masterAddress;
@JsonIgnore
abstract MasterSlaveServersConfig setMasterAddress(String masterAddress);
@JsonIgnore
abstract URI getMasterAddress();
@JsonIgnore
abstract void setMasterAddress(URI masterAddress);
}
@JsonIgnoreProperties({"clusterConfig", "sentinelConfig"})
public static class ConfigMixIn {
@ -264,8 +229,6 @@ public class ConfigSupport {
private ObjectMapper createMapper(JsonFactory mapping, ClassLoader classLoader) {
ObjectMapper mapper = new ObjectMapper(mapping);
mapper.addMixIn(MasterSlaveServersConfig.class, MasterSlaveServersConfigMixIn.class);
mapper.addMixIn(SingleServerConfig.class, SingleSeverConfigMixIn.class);
mapper.addMixIn(Config.class, ConfigMixIn.class);
mapper.addMixIn(ReferenceCodecProvider.class, ClassMixIn.class);
mapper.addMixIn(AddressResolverGroupFactory.class, ClassMixIn.class);

@ -15,10 +15,9 @@
*/
package org.redisson.config;
import java.net.URI;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.redisson.misc.URIBuilder;
/**
*
@ -30,12 +29,12 @@ public class MasterSlaveServersConfig extends BaseMasterSlaveServersConfig<Maste
/**
* Redis slave servers addresses
*/
private Set<URI> slaveAddresses = new HashSet<URI>();
private Set<String> slaveAddresses = new HashSet<String>();
/**
* Redis master server address
*/
private URI masterAddress;
private String masterAddress;
/**
* Database index used for Redis connection
@ -60,22 +59,11 @@ public class MasterSlaveServersConfig extends BaseMasterSlaveServersConfig<Maste
* @return config
*/
public MasterSlaveServersConfig setMasterAddress(String masterAddress) {
if (masterAddress != null) {
this.masterAddress = URIBuilder.create(masterAddress);
}
this.masterAddress = masterAddress;
return this;
}
public URI getMasterAddress() {
if (masterAddress != null) {
return masterAddress;
}
return null;
}
public MasterSlaveServersConfig setMasterAddress(URI masterAddress) {
if (masterAddress != null) {
this.masterAddress = masterAddress;
}
return this;
public String getMasterAddress() {
return masterAddress;
}
/**
@ -85,19 +73,17 @@ public class MasterSlaveServersConfig extends BaseMasterSlaveServersConfig<Maste
* @return config
*/
public MasterSlaveServersConfig addSlaveAddress(String... addresses) {
for (String address : addresses) {
slaveAddresses.add(URIBuilder.create(address));
}
slaveAddresses.addAll(Arrays.asList(addresses));
return this;
}
public MasterSlaveServersConfig addSlaveAddress(URI slaveAddress) {
public MasterSlaveServersConfig addSlaveAddress(String slaveAddress) {
slaveAddresses.add(slaveAddress);
return this;
}
public Set<URI> getSlaveAddresses() {
public Set<String> getSlaveAddresses() {
return slaveAddresses;
}
public void setSlaveAddresses(Set<URI> readAddresses) {
public void setSlaveAddresses(Set<String> readAddresses) {
this.slaveAddresses = readAddresses;
}

@ -15,10 +15,9 @@
*/
package org.redisson.config;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.redisson.misc.URIBuilder;
/**
* Configuration for an Azure Redis Cache or AWS ElastiCache servers.
@ -32,7 +31,7 @@ public class ReplicatedServersConfig extends BaseMasterSlaveServersConfig<Replic
/**
* Replication group node urls list
*/
private List<URI> nodeAddresses = new ArrayList<URI>();
private List<String> nodeAddresses = new ArrayList<>();
/**
* Replication group scan interval in milliseconds
@ -61,15 +60,13 @@ public class ReplicatedServersConfig extends BaseMasterSlaveServersConfig<Replic
* @return config
*/
public ReplicatedServersConfig addNodeAddress(String... addresses) {
for (String address : addresses) {
nodeAddresses.add(URIBuilder.create(address));
}
nodeAddresses.addAll(Arrays.asList(addresses));
return this;
}
public List<URI> getNodeAddresses() {
public List<String> getNodeAddresses() {
return nodeAddresses;
}
void setNodeAddresses(List<URI> nodeAddresses) {
void setNodeAddresses(List<String> nodeAddresses) {
this.nodeAddresses = nodeAddresses;
}

@ -15,15 +15,13 @@
*/
package org.redisson.config;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.redisson.misc.URIBuilder;
/**
*
* @author Nikita Koksharov
@ -31,7 +29,7 @@ import org.redisson.misc.URIBuilder;
*/
public class SentinelServersConfig extends BaseMasterSlaveServersConfig<SentinelServersConfig> {
private List<URI> sentinelAddresses = new ArrayList<URI>();
private List<String> sentinelAddresses = new ArrayList<>();
private Map<String, String> natMap = Collections.emptyMap();
@ -80,15 +78,13 @@ public class SentinelServersConfig extends BaseMasterSlaveServersConfig<Sentinel
* @return config
*/
public SentinelServersConfig addSentinelAddress(String... addresses) {
for (String address : addresses) {
sentinelAddresses.add(URIBuilder.create(address));
}
sentinelAddresses.addAll(Arrays.asList(addresses));
return this;
}
public List<URI> getSentinelAddresses() {
public List<String> getSentinelAddresses() {
return sentinelAddresses;
}
void setSentinelAddresses(List<URI> sentinelAddresses) {
void setSentinelAddresses(List<String> sentinelAddresses) {
this.sentinelAddresses = sentinelAddresses;
}

@ -15,9 +15,6 @@
*/
package org.redisson.config;
import java.net.URI;
import org.redisson.misc.URIBuilder;
/**
*
* @author Nikita Koksharov
@ -29,7 +26,7 @@ public class SingleServerConfig extends BaseConfig<SingleServerConfig> {
* Redis server address
*
*/
private URI address;
private String address;
/**
* Minimum idle subscription connection amount
@ -116,20 +113,12 @@ public class SingleServerConfig extends BaseConfig<SingleServerConfig> {
*/
public SingleServerConfig setAddress(String address) {
if (address != null) {
this.address = URIBuilder.create(address);
this.address = address;
}
return this;
}
public URI getAddress() {
if (address != null) {
return address;
}
return null;
}
void setAddress(URI address) {
if (address != null) {
this.address = address;
}
public String getAddress() {
return address;
}
/**

@ -16,7 +16,6 @@
package org.redisson.connection;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.Collection;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
@ -32,6 +31,7 @@ import org.redisson.command.CommandSyncService;
import org.redisson.config.Config;
import org.redisson.config.MasterSlaveServersConfig;
import org.redisson.misc.InfinitySemaphoreLatch;
import org.redisson.misc.RedisURI;
import org.redisson.pubsub.PublishSubscribeService;
import io.netty.channel.EventLoopGroup;
@ -46,7 +46,7 @@ import io.netty.util.concurrent.Future;
*/
public interface ConnectionManager {
URI applyNatMap(URI address);
RedisURI applyNatMap(RedisURI address);
UUID getId();
@ -56,7 +56,7 @@ public interface ConnectionManager {
ExecutorService getExecutor();
URI getLastClusterNode();
RedisURI getLastClusterNode();
Config getCfg();
@ -92,11 +92,11 @@ public interface ConnectionManager {
RFuture<RedisConnection> connectionWriteOp(NodeSource source, RedisCommand<?> command);
RedisClient createClient(NodeType type, URI address, int timeout, int commandTimeout, String sslHostname);
RedisClient createClient(NodeType type, RedisURI address, int timeout, int commandTimeout, String sslHostname);
RedisClient createClient(NodeType type, InetSocketAddress address, URI uri, String sslHostname);
RedisClient createClient(NodeType type, InetSocketAddress address, RedisURI uri, String sslHostname);
RedisClient createClient(NodeType type, URI address, String sslHostname);
RedisClient createClient(NodeType type, RedisURI address, String sslHostname);
MasterSlaveEntry getEntry(RedisClient redisClient);

@ -16,7 +16,6 @@
package org.redisson.connection;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@ -27,6 +26,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.redisson.api.RFuture;
import org.redisson.client.RedisClient;
import org.redisson.connection.ClientConnectionsEntry.FreezeReason;
import org.redisson.misc.RedisURI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -48,19 +48,19 @@ public class DNSMonitor {
private final AddressResolver<InetSocketAddress> resolver;
private final ConnectionManager connectionManager;
private final Map<URI, InetSocketAddress> masters = new HashMap<URI, InetSocketAddress>();
private final Map<URI, InetSocketAddress> slaves = new HashMap<URI, InetSocketAddress>();
private final Map<RedisURI, InetSocketAddress> masters = new HashMap<>();
private final Map<RedisURI, InetSocketAddress> slaves = new HashMap<>();
private ScheduledFuture<?> dnsMonitorFuture;
private long dnsMonitoringInterval;
public DNSMonitor(ConnectionManager connectionManager, RedisClient masterHost, Collection<URI> slaveHosts, long dnsMonitoringInterval, AddressResolverGroup<InetSocketAddress> resolverGroup) {
public DNSMonitor(ConnectionManager connectionManager, RedisClient masterHost, Collection<RedisURI> slaveHosts, long dnsMonitoringInterval, AddressResolverGroup<InetSocketAddress> resolverGroup) {
this.resolver = resolverGroup.getResolver(connectionManager.getGroup().next());
masterHost.resolveAddr().syncUninterruptibly();
masters.put(masterHost.getConfig().getAddress(), masterHost.getAddr());
for (URI host : slaveHosts) {
for (RedisURI host : slaveHosts) {
Future<InetSocketAddress> resolveFuture = resolver.resolve(InetSocketAddress.createUnresolved(host.getHost(), host.getPort()));
resolveFuture.syncUninterruptibly();
slaves.put(host, resolveFuture.getNow());
@ -97,7 +97,7 @@ public class DNSMonitor {
}
private void monitorMasters(AtomicInteger counter) {
for (Entry<URI, InetSocketAddress> entry : masters.entrySet()) {
for (Entry<RedisURI, InetSocketAddress> entry : masters.entrySet()) {
Future<InetSocketAddress> resolveFuture = resolver.resolve(InetSocketAddress.createUnresolved(entry.getKey().getHost(), entry.getKey().getPort()));
resolveFuture.addListener(new FutureListener<InetSocketAddress>() {
@Override
@ -134,7 +134,7 @@ public class DNSMonitor {
}
private void monitorSlaves(AtomicInteger counter) {
for (Entry<URI, InetSocketAddress> entry : slaves.entrySet()) {
for (Entry<RedisURI, InetSocketAddress> entry : slaves.entrySet()) {
Future<InetSocketAddress> resolveFuture = resolver.resolve(InetSocketAddress.createUnresolved(entry.getKey().getHost(), entry.getKey().getPort()));
resolveFuture.addListener(new FutureListener<InetSocketAddress>() {
@Override

@ -16,18 +16,19 @@
package org.redisson.connection;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.stream.Collectors;
import org.redisson.Version;
import org.redisson.api.NodeType;
@ -48,8 +49,8 @@ import org.redisson.config.TransportMode;
import org.redisson.misc.CountableListener;
import org.redisson.misc.InfinitySemaphoreLatch;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedisURI;
import org.redisson.misc.RedissonPromise;
import org.redisson.misc.URIBuilder;
import org.redisson.pubsub.PublishSubscribeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -241,7 +242,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
}
}
protected RFuture<RedisConnection> connectToNode(BaseMasterSlaveServersConfig<?> cfg, URI addr, RedisClient client, String sslHostname) {
protected RFuture<RedisConnection> connectToNode(BaseMasterSlaveServersConfig<?> cfg, RedisURI addr, RedisClient client, String sslHostname) {
final Object key;
if (client != null) {
key = client;
@ -342,7 +343,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
} else {
entry = createMasterSlaveEntry(config);
}
RFuture<RedisClient> f = entry.setupMasterEntry(config.getMasterAddress());
RFuture<RedisClient> f = entry.setupMasterEntry(new RedisURI(config.getMasterAddress()));
f.syncUninterruptibly();
for (int slot = singleSlotRange.getStartSlot(); slot < singleSlotRange.getEndSlot() + 1; slot++) {
@ -358,15 +359,16 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
protected void startDNSMonitoring(RedisClient masterHost) {
if (config.getDnsMonitoringInterval() != -1) {
Set<RedisURI> slaveAddresses = config.getSlaveAddresses().stream().map(r -> new RedisURI(r)).collect(Collectors.toSet());
dnsMonitor = new DNSMonitor(this, masterHost,
config.getSlaveAddresses(), config.getDnsMonitoringInterval(), resolverGroup);
slaveAddresses, config.getDnsMonitoringInterval(), resolverGroup);
dnsMonitor.start();
}
}
protected MasterSlaveEntry createMasterSlaveEntry(MasterSlaveServersConfig config) {
MasterSlaveEntry entry = new MasterSlaveEntry(this, config);
List<RFuture<Void>> fs = entry.initSlaveBalancer(java.util.Collections.<URI>emptySet());
List<RFuture<Void>> fs = entry.initSlaveBalancer(java.util.Collections.<RedisURI>emptySet());
for (RFuture<Void> future : fs) {
future.syncUninterruptibly();
}
@ -412,31 +414,31 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
}
@Override
public RedisClient createClient(NodeType type, URI address, String sslHostname) {
public RedisClient createClient(NodeType type, RedisURI address, String sslHostname) {
RedisClient client = createClient(type, address, config.getConnectTimeout(), config.getTimeout(), sslHostname);
return client;
}
@Override
public RedisClient createClient(NodeType type, InetSocketAddress address, URI uri, String sslHostname) {
public RedisClient createClient(NodeType type, InetSocketAddress address, RedisURI uri, String sslHostname) {
RedisClient client = createClient(type, address, uri, config.getConnectTimeout(), config.getTimeout(), sslHostname);
return client;
}
@Override
public RedisClient createClient(NodeType type, URI address, int timeout, int commandTimeout, String sslHostname) {
public RedisClient createClient(NodeType type, RedisURI address, int timeout, int commandTimeout, String sslHostname) {
RedisClientConfig redisConfig = createRedisConfig(type, address, timeout, commandTimeout, sslHostname);
return RedisClient.create(redisConfig);
}
private RedisClient createClient(NodeType type, InetSocketAddress address, URI uri, int timeout, int commandTimeout, String sslHostname) {
private RedisClient createClient(NodeType type, InetSocketAddress address, RedisURI uri, int timeout, int commandTimeout, String sslHostname) {
RedisClientConfig redisConfig = createRedisConfig(type, null, timeout, commandTimeout, sslHostname);
redisConfig.setAddress(address, uri);
return RedisClient.create(redisConfig);
}
protected RedisClientConfig createRedisConfig(NodeType type, URI address, int timeout, int commandTimeout, String sslHostname) {
protected RedisClientConfig createRedisConfig(NodeType type, RedisURI address, int timeout, int commandTimeout, String sslHostname) {
RedisClientConfig redisConfig = new RedisClientConfig();
redisConfig.setAddress(address)
.setTimer(timer)
@ -489,9 +491,9 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
return null;
}
private MasterSlaveEntry getEntry(URI addr) {
private MasterSlaveEntry getEntry(RedisURI addr) {
for (MasterSlaveEntry entry : client2entry.values()) {
if (URIBuilder.compare(entry.getClient().getAddr(), addr)) {
if (RedisURI.compare(entry.getClient().getAddr(), addr)) {
return entry;
}
if (entry.hasSlave(addr)) {
@ -521,7 +523,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
return slot2entry.get(slot);
}
protected final RFuture<RedisClient> changeMaster(int slot, URI address) {
protected final RFuture<RedisClient> changeMaster(int slot, RedisURI address) {
final MasterSlaveEntry entry = getEntry(slot);
final RedisClient oldClient = entry.getClient();
RFuture<RedisClient> future = entry.changeMaster(address);
@ -559,7 +561,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
}
// fix for https://github.com/redisson/redisson/issues/1548
if (source.getRedirect() != null
&& !URIBuilder.compare(entry.getClient().getAddr(), source.getAddr())
&& !RedisURI.compare(entry.getClient().getAddr(), source.getAddr())
&& entry.hasSlave(source.getAddr())) {
return entry.redirectedConnectionWriteOp(command, source.getAddr());
}
@ -714,12 +716,12 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
return executor;
}
public URI getLastClusterNode() {
public RedisURI getLastClusterNode() {
return null;
}
@Override
public URI applyNatMap(URI address) {
public RedisURI applyNatMap(RedisURI address) {
return address;
}
}

@ -16,7 +16,6 @@
package org.redisson.connection;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
@ -40,9 +39,9 @@ import org.redisson.connection.pool.MasterConnectionPool;
import org.redisson.connection.pool.MasterPubSubConnectionPool;
import org.redisson.misc.CountableListener;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedisURI;
import org.redisson.misc.RedissonPromise;
import org.redisson.misc.TransferListener;
import org.redisson.misc.URIBuilder;
import org.redisson.pubsub.PubSubConnectionEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -92,28 +91,29 @@ public class MasterSlaveEntry {
return config;
}
public List<RFuture<Void>> initSlaveBalancer(Collection<URI> disconnectedNodes) {
public List<RFuture<Void>> initSlaveBalancer(Collection<RedisURI> disconnectedNodes) {
boolean freezeMasterAsSlave = !config.getSlaveAddresses().isEmpty()
&& !config.checkSkipSlavesInit()
&& disconnectedNodes.size() < config.getSlaveAddresses().size();
List<RFuture<Void>> result = new LinkedList<RFuture<Void>>();
RFuture<Void> f = addSlave(config.getMasterAddress(), freezeMasterAsSlave, NodeType.MASTER);
RFuture<Void> f = addSlave(new RedisURI(config.getMasterAddress()), freezeMasterAsSlave, NodeType.MASTER);
result.add(f);
for (URI address : config.getSlaveAddresses()) {
f = addSlave(address, disconnectedNodes.contains(address), NodeType.SLAVE);
for (String address : config.getSlaveAddresses()) {
RedisURI uri = new RedisURI(address);
f = addSlave(uri, disconnectedNodes.contains(uri), NodeType.SLAVE);
result.add(f);
}
return result;
}
public RFuture<RedisClient> setupMasterEntry(InetSocketAddress address, URI uri) {
public RFuture<RedisClient> setupMasterEntry(InetSocketAddress address, RedisURI uri) {
RedisClient client = connectionManager.createClient(NodeType.MASTER, address, uri, sslHostname);
return setupMasterEntry(client);
}
public RFuture<RedisClient> setupMasterEntry(URI address) {
public RFuture<RedisClient> setupMasterEntry(RedisURI address) {
RedisClient client = connectionManager.createClient(NodeType.MASTER, address, sslHostname);
return setupMasterEntry(client);
}
@ -173,7 +173,7 @@ public class MasterSlaveEntry {
return slaveDown(entry);
}
public boolean slaveDown(URI address, FreezeReason freezeReason) {
public boolean slaveDown(RedisURI address, FreezeReason freezeReason) {
ClientConnectionsEntry entry = slaveBalancer.freeze(address, freezeReason);
if (entry == null) {
return false;
@ -276,7 +276,7 @@ public class MasterSlaveEntry {
return slaveBalancer.contains(addr);
}
public boolean hasSlave(URI addr) {
public boolean hasSlave(RedisURI addr) {
return slaveBalancer.contains(addr);
}
@ -284,11 +284,11 @@ public class MasterSlaveEntry {
return slaveBalancer.getAvailableClients();
}
public RFuture<Void> addSlave(URI address) {
public RFuture<Void> addSlave(RedisURI address) {
return addSlave(address, false, NodeType.SLAVE);
}
public RFuture<Void> addSlave(InetSocketAddress address, URI uri) {
public RFuture<Void> addSlave(InetSocketAddress address, RedisURI uri) {
return addSlave(address, uri, false, NodeType.SLAVE);
}
@ -323,12 +323,12 @@ public class MasterSlaveEntry {
return result;
}
private RFuture<Void> addSlave(InetSocketAddress address, URI uri, boolean freezed, NodeType nodeType) {
private RFuture<Void> addSlave(InetSocketAddress address, RedisURI uri, boolean freezed, NodeType nodeType) {
RedisClient client = connectionManager.createClient(NodeType.SLAVE, address, uri, sslHostname);
return addSlave(client, freezed, nodeType);
}
private RFuture<Void> addSlave(URI address, boolean freezed, NodeType nodeType) {
private RFuture<Void> addSlave(RedisURI address, boolean freezed, NodeType nodeType) {
RedisClient client = connectionManager.createClient(nodeType, address, sslHostname);
return addSlave(client, freezed, nodeType);
}
@ -361,11 +361,11 @@ public class MasterSlaveEntry {
return true;
}
public boolean isSlaveUnfreezed(URI address) {
public boolean isSlaveUnfreezed(RedisURI address) {
return slaveBalancer.isUnfreezed(address);
}
public boolean slaveUp(URI address, FreezeReason freezeReason) {
public boolean slaveUp(RedisURI address, FreezeReason freezeReason) {
if (!slaveBalancer.unfreeze(address, freezeReason)) {
return false;
}
@ -373,7 +373,7 @@ public class MasterSlaveEntry {
InetSocketAddress addr = masterEntry.getClient().getAddr();
// exclude master from slaves
if (!config.checkSkipSlavesInit()
&& !URIBuilder.compare(addr, address)) {
&& !RedisURI.compare(addr, address)) {
if (slaveDown(addr, FreezeReason.SYSTEM)) {
log.info("master {} excluded from slaves", addr);
}
@ -406,21 +406,21 @@ public class MasterSlaveEntry {
* @param address of Redis
* @return client
*/
public RFuture<RedisClient> changeMaster(URI address) {
public RFuture<RedisClient> changeMaster(RedisURI address) {
ClientConnectionsEntry oldMaster = masterEntry;
RFuture<RedisClient> future = setupMasterEntry(address);
changeMaster(address, oldMaster, future);
return future;
}
public void changeMaster(InetSocketAddress address, URI uri) {
public void changeMaster(InetSocketAddress address, RedisURI uri) {
ClientConnectionsEntry oldMaster = masterEntry;
RFuture<RedisClient> future = setupMasterEntry(address, uri);
changeMaster(uri, oldMaster, future);
}
private void changeMaster(URI address, ClientConnectionsEntry oldMaster,
private void changeMaster(RedisURI address, ClientConnectionsEntry oldMaster,
RFuture<RedisClient> future) {
future.onComplete((newMasterClient, e) -> {
if (e != null) {
@ -471,7 +471,7 @@ public class MasterSlaveEntry {
return writeConnectionPool.get(command);
}
public RFuture<RedisConnection> redirectedConnectionWriteOp(RedisCommand<?> command, URI addr) {
public RFuture<RedisConnection> redirectedConnectionWriteOp(RedisCommand<?> command, RedisURI addr) {
return slaveBalancer.getConnection(command, addr);
}
@ -482,7 +482,7 @@ public class MasterSlaveEntry {
return slaveBalancer.nextConnection(command);
}
public RFuture<RedisConnection> connectionReadOp(RedisCommand<?> command, URI addr) {
public RFuture<RedisConnection> connectionReadOp(RedisCommand<?> command, RedisURI addr) {
return slaveBalancer.getConnection(command, addr);
}

@ -15,9 +15,8 @@
*/
package org.redisson.connection;
import java.net.URI;
import org.redisson.client.RedisClient;
import org.redisson.misc.RedisURI;
/**
*
@ -29,7 +28,7 @@ public class NodeSource {
public enum Redirect {MOVED, ASK}
private Integer slot;
private URI addr;
private RedisURI addr;
private RedisClient redisClient;
private Redirect redirect;
private MasterSlaveEntry entry;
@ -52,7 +51,7 @@ public class NodeSource {
this.redisClient = redisClient;
}
public NodeSource(Integer slot, URI addr, Redirect redirect) {
public NodeSource(Integer slot, RedisURI addr, Redirect redirect) {
this.slot = slot;
this.addr = addr;
this.redirect = redirect;
@ -74,7 +73,7 @@ public class NodeSource {
return redisClient;
}
public URI getAddr() {
public RedisURI getAddr() {
return addr;
}

@ -15,7 +15,6 @@
*/
package org.redisson.connection;
import java.net.URI;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@ -34,6 +33,7 @@ import org.redisson.config.MasterSlaveServersConfig;
import org.redisson.config.ReadMode;
import org.redisson.config.ReplicatedServersConfig;
import org.redisson.connection.ClientConnectionsEntry.FreezeReason;
import org.redisson.misc.RedisURI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -53,7 +53,7 @@ public class ReplicatedConnectionManager extends MasterSlaveConnectionManager {
private final Logger log = LoggerFactory.getLogger(getClass());
private AtomicReference<URI> currentMaster = new AtomicReference<URI>();
private AtomicReference<RedisURI> currentMaster = new AtomicReference<>();
private ScheduledFuture<?> monitorFuture;
@ -68,7 +68,8 @@ public class ReplicatedConnectionManager extends MasterSlaveConnectionManager {
this.config = create(cfg);
initTimer(this.config);
for (URI addr : cfg.getNodeAddresses()) {
for (String address : cfg.getNodeAddresses()) {
RedisURI addr = new RedisURI(address);
RFuture<RedisConnection> connectionFuture = connectToNode(cfg, addr, null, addr.getHost());
connectionFuture.awaitUninterruptibly();
RedisConnection connection = connectionFuture.getNow();
@ -84,10 +85,10 @@ public class ReplicatedConnectionManager extends MasterSlaveConnectionManager {
}
currentMaster.set(addr);
log.info("{} is the master", addr);
this.config.setMasterAddress(addr);
this.config.setMasterAddress(addr.toString());
} else {
log.info("{} is a slave", addr);
this.config.addSlaveAddress(addr);
this.config.addSlaveAddress(addr.toString());
}
}
@ -123,11 +124,12 @@ public class ReplicatedConnectionManager extends MasterSlaveConnectionManager {
return;
}
URI master = currentMaster.get();
RedisURI master = currentMaster.get();
log.debug("Current master: {}", master);
AtomicInteger count = new AtomicInteger(cfg.getNodeAddresses().size());
for (URI addr : cfg.getNodeAddresses()) {
for (String address : cfg.getNodeAddresses()) {
RedisURI addr = new RedisURI(address);
RFuture<RedisConnection> connectionFuture = connectToNode(cfg, addr, null, addr.getHost());
connectionFuture.onComplete((connection, exc) -> {
if (exc != null) {
@ -180,7 +182,7 @@ public class ReplicatedConnectionManager extends MasterSlaveConnectionManager {
}, cfg.getScanInterval(), TimeUnit.MILLISECONDS);
}
private void slaveUp(URI uri) {
private void slaveUp(RedisURI uri) {
MasterSlaveEntry entry = getEntry(singleSlotRange.getStartSlot());
if (entry.slaveUp(uri, FreezeReason.MANAGER)) {
log.info("slave: {} has up", uri);

@ -16,7 +16,6 @@
package org.redisson.connection;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -52,8 +51,8 @@ import org.redisson.config.SentinelServersConfig;
import org.redisson.connection.ClientConnectionsEntry.FreezeReason;
import org.redisson.misc.CountableListener;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedisURI;
import org.redisson.misc.RedissonPromise;
import org.redisson.misc.URIBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -72,11 +71,11 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
private final Logger log = LoggerFactory.getLogger(getClass());
private final Set<URI> sentinelHosts = new HashSet<>();
private final ConcurrentMap<URI, RedisClient> sentinels = new ConcurrentHashMap<>();
private final Set<RedisURI> sentinelHosts = new HashSet<>();
private final ConcurrentMap<RedisURI, RedisClient> sentinels = new ConcurrentHashMap<>();
private final AtomicReference<String> currentMaster = new AtomicReference<>();
private final Set<URI> disconnectedSlaves = new HashSet<>();
private final Set<RedisURI> disconnectedSlaves = new HashSet<>();
private ScheduledFuture<?> monitorFuture;
private AddressResolver<InetSocketAddress> sentinelResolver;
@ -101,7 +100,8 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
this.sentinelResolver = resolverGroup.getResolver(getGroup().next());
for (URI addr : cfg.getSentinelAddresses()) {
for (String address : cfg.getSentinelAddresses()) {
RedisURI addr = new RedisURI(address);
RedisClient client = createClient(NodeType.SENTINEL, addr, this.config.getConnectTimeout(), this.config.getRetryInterval() * this.config.getRetryAttempts(), null);
try {
RedisConnection c = client.connect();
@ -117,7 +117,8 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
}
}
for (URI addr : cfg.getSentinelAddresses()) {
for (String address : cfg.getSentinelAddresses()) {
RedisURI addr = new RedisURI(address);
if (NetUtil.createByteArrayFromIpAddressString(addr.getHost()) == null && !addr.getHost().equals("localhost")) {
sentinelHosts.add(convert(addr.getHost(), "" + addr.getPort()));
}
@ -156,7 +157,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
log.info("slave: {} added", host);
if (flags.contains("s_down") || flags.contains("disconnected")) {
URI uri = URIBuilder.create(host);
RedisURI uri = new RedisURI(host);
disconnectedSlaves.add(uri);
log.warn("slave: {} is down", host);
}
@ -172,12 +173,12 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
String ip = map.get("ip");
String port = map.get("port");
URI sentinelAddr = convert(ip, port);
RedisURI sentinelAddr = convert(ip, port);
RFuture<Void> future = registerSentinel(sentinelAddr, this.config);
connectionFutures.add(future);
}
URI currentAddr = convert(client.getAddr().getAddress().getHostAddress(), "" + client.getAddr().getPort());
RedisURI currentAddr = convert(client.getAddr().getAddress().getHostAddress(), "" + client.getAddr().getPort());
RFuture<Void> f = registerSentinel(currentAddr, this.config);
connectionFutures.add(f);
@ -221,7 +222,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
}
@Override
protected RedisClientConfig createRedisConfig(NodeType type, URI address, int timeout, int commandTimeout,
protected RedisClientConfig createRedisConfig(NodeType type, RedisURI address, int timeout, int commandTimeout,
String sslHostname) {
RedisClientConfig result = super.createRedisConfig(type, address, timeout, commandTimeout, sslHostname);
if (type == NodeType.SENTINEL && !usePassword) {
@ -244,7 +245,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
}
};
for (URI host : sentinelHosts) {
for (RedisURI host : sentinelHosts) {
Future<List<InetSocketAddress>> allNodes = sentinelResolver.resolveAll(InetSocketAddress.createUnresolved(host.getHost(), host.getPort()));
allNodes.addListener(new FutureListener<List<InetSocketAddress>>() {
@Override
@ -254,11 +255,11 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
return;
}
Set<URI> newUris = future.getNow().stream()
Set<RedisURI> newUris = future.getNow().stream()
.map(addr -> convert(addr.getAddress().getHostAddress(), "" + addr.getPort()))
.collect(Collectors.toSet());
for (URI uri : newUris) {
for (RedisURI uri : newUris) {
if (!sentinels.containsKey(uri)) {
registerSentinel(uri, getConfig());
}
@ -348,7 +349,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
String newMaster = createAddress(master.get(0), master.get(1));
if (!newMaster.equals(current)
&& currentMaster.compareAndSet(current, newMaster)) {
RFuture<RedisClient> changeFuture = changeMaster(singleSlotRange.getStartSlot(), URIBuilder.create(newMaster));
RFuture<RedisClient> changeFuture = changeMaster(singleSlotRange.getStartSlot(), new RedisURI(newMaster));
changeFuture.onComplete((res, ex) -> {
if (ex != null) {
currentMaster.compareAndSet(newMaster, current);
@ -432,7 +433,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
return;
}
Set<URI> newUris = list.stream().filter(m -> {
Set<RedisURI> newUris = list.stream().filter(m -> {
String flags = m.get("flags");
if (!m.isEmpty() && !flags.contains("disconnected") && !flags.contains("s_down")) {
return true;
@ -445,7 +446,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
}).collect(Collectors.toSet());
InetSocketAddress addr = connection.getRedisClient().getAddr();
URI currentAddr = convert(addr.getAddress().getHostAddress(), "" + addr.getPort());
RedisURI currentAddr = convert(addr.getAddress().getHostAddress(), "" + addr.getPort());
newUris.add(currentAddr);
updateSentinels(newUris);
@ -453,17 +454,17 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
sentinelsFuture.onComplete(commonListener);
}
private void updateSentinels(Set<URI> newUris) {
Set<URI> currentUris = new HashSet<>(SentinelConnectionManager.this.sentinels.keySet());
Set<URI> addedUris = new HashSet<>(newUris);
private void updateSentinels(Set<RedisURI> newUris) {
Set<RedisURI> currentUris = new HashSet<>(SentinelConnectionManager.this.sentinels.keySet());
Set<RedisURI> addedUris = new HashSet<>(newUris);
addedUris.removeAll(currentUris);
for (URI uri : addedUris) {
for (RedisURI uri : addedUris) {
registerSentinel(uri, getConfig());
}
currentUris.removeAll(newUris);
for (URI uri : currentUris) {
for (RedisURI uri : currentUris) {
RedisClient sentinel = SentinelConnectionManager.this.sentinels.remove(uri);
if (sentinel != null) {
sentinel.shutdownAsync();
@ -495,7 +496,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
return entry;
}
private RFuture<Void> registerSentinel(URI addr, MasterSlaveServersConfig c) {
private RFuture<Void> registerSentinel(RedisURI addr, MasterSlaveServersConfig c) {
RedisClient sentinel = sentinels.get(addr);
if (sentinel != null) {
return RedissonPromise.newSucceededFuture(null);
@ -521,9 +522,9 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
RPromise<Void> result = new RedissonPromise<Void>();
// to avoid addition twice
MasterSlaveEntry entry = getEntry(singleSlotRange.getStartSlot());
URI uri = convert(ip, port);
RedisURI uri = convert(ip, port);
if (!entry.hasSlave(uri) && !config.checkSkipSlavesInit()) {
RFuture<Void> future = entry.addSlave(URIBuilder.create(addr));
RFuture<Void> future = entry.addSlave(new RedisURI(addr));
future.onComplete((res, e) -> {
if (e != null) {
result.tryFailure(e);
@ -546,9 +547,9 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
return result;
}
private URI convert(String ip, String port) {
private RedisURI convert(String ip, String port) {
String addr = createAddress(ip, port);
URI uri = URIBuilder.create(addr);
RedisURI uri = new RedisURI(addr);
return uri;
}
@ -557,7 +558,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
log.warn("slave: {}:{} has down", ip, port);
} else {
MasterSlaveEntry entry = getEntry(singleSlotRange.getStartSlot());
URI uri = convert(ip, port);
RedisURI uri = convert(ip, port);
if (entry.slaveDown(uri, FreezeReason.MANAGER)) {
log.warn("slave: {}:{} has down", ip, port);
}
@ -581,7 +582,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
return;
}
URI uri = convert(ip, port);
RedisURI uri = convert(ip, port);
if (getEntry(singleSlotRange.getStartSlot()).slaveUp(uri, FreezeReason.MANAGER)) {
String slaveAddr = ip + ":" + port;
log.info("slave: {} has up", slaveAddr);

@ -15,12 +15,11 @@
*/
package org.redisson.connection;
import java.net.URI;
import org.redisson.api.RFuture;
import org.redisson.client.RedisConnection;
import org.redisson.client.protocol.RedisCommand;
import org.redisson.config.MasterSlaveServersConfig;
import org.redisson.misc.RedisURI;
/**
*
@ -34,7 +33,7 @@ public class SingleEntry extends MasterSlaveEntry {
}
@Override
public RFuture<RedisConnection> connectionReadOp(RedisCommand<?> command, URI addr) {
public RFuture<RedisConnection> connectionReadOp(RedisCommand<?> command, RedisURI addr) {
return super.connectionWriteOp(command);
}

@ -16,7 +16,6 @@
package org.redisson.connection.balancer;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
@ -39,8 +38,8 @@ import org.redisson.connection.pool.PubSubConnectionPool;
import org.redisson.connection.pool.SlaveConnectionPool;
import org.redisson.misc.CountableListener;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedisURI;
import org.redisson.misc.RedissonPromise;
import org.redisson.misc.URIBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -107,7 +106,7 @@ public class LoadBalancerManager {
return count;
}
public boolean unfreeze(URI address, FreezeReason freezeReason) {
public boolean unfreeze(RedisURI address, FreezeReason freezeReason) {
ClientConnectionsEntry entry = getEntry(address);
if (entry == null) {
throw new IllegalStateException("Can't find " + address + " in slaves!");
@ -145,7 +144,7 @@ public class LoadBalancerManager {
return false;
}
public ClientConnectionsEntry freeze(URI address, FreezeReason freezeReason) {
public ClientConnectionsEntry freeze(RedisURI address, FreezeReason freezeReason) {
ClientConnectionsEntry connectionEntry = getEntry(address);
return freeze(connectionEntry, freezeReason);
}
@ -190,12 +189,12 @@ public class LoadBalancerManager {
return getEntry(addr) != null;
}
public boolean isUnfreezed(URI addr) {
public boolean isUnfreezed(RedisURI addr) {
ClientConnectionsEntry entry = getEntry(addr);
return !entry.isFreezed();
}
public boolean contains(URI addr) {
public boolean contains(RedisURI addr) {
return getEntry(addr) != null;
}
@ -203,10 +202,10 @@ public class LoadBalancerManager {
return getEntry(redisClient) != null;
}
private ClientConnectionsEntry getEntry(URI addr) {
private ClientConnectionsEntry getEntry(RedisURI addr) {
for (ClientConnectionsEntry entry : client2Entry.values()) {
InetSocketAddress entryAddr = entry.getClient().getAddr();
if (URIBuilder.compare(entryAddr, addr)) {
if (RedisURI.compare(entryAddr, addr)) {
return entry;
}
}
@ -227,7 +226,7 @@ public class LoadBalancerManager {
return client2Entry.get(redisClient);
}
public RFuture<RedisConnection> getConnection(RedisCommand<?> command, URI addr) {
public RFuture<RedisConnection> getConnection(RedisCommand<?> command, RedisURI addr) {
ClientConnectionsEntry entry = getEntry(addr);
if (entry != null) {
return slaveConnectionPool.get(command, entry);

@ -16,7 +16,6 @@
package org.redisson.connection.balancer;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@ -29,7 +28,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.redisson.connection.ClientConnectionsEntry;
import org.redisson.misc.URIBuilder;
import org.redisson.misc.RedisURI;
/**
* Weighted Round Robin balancer.
@ -77,7 +76,7 @@ public class WeightedRoundRobinBalancer implements LoadBalancer {
*/
public WeightedRoundRobinBalancer(Map<String, Integer> weights, int defaultWeight) {
for (Entry<String, Integer> entry : weights.entrySet()) {
URI uri = URIBuilder.create(entry.getKey());
RedisURI uri = new RedisURI(entry.getKey());
InetSocketAddress addr = new InetSocketAddress(uri.getHost(), uri.getPort());
if (entry.getValue() <= 0) {
throw new IllegalArgumentException("Weight can't be less than or equal zero");

@ -0,0 +1,127 @@
/**
* Copyright (c) 2013-2019 Nikita Koksharov
*
* 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 org.redisson.misc;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.URL;
/**
*
* @author Nikita Koksharov
*
*/
public class RedisURI {
private final boolean ssl;
private final String host;
private final int port;
public RedisURI(String uri) {
if (!uri.startsWith("redis://")
&& !uri.startsWith("rediss://")) {
throw new IllegalArgumentException("Redis url should start with redis:// or rediss:// (for SSL connection)");
}
String urlHost = uri.replaceFirst("redis://", "http://").replaceFirst("rediss://", "http://");
String ipV6Host = uri.substring(uri.indexOf("://")+3, uri.lastIndexOf(":"));
if (ipV6Host.contains(":")) {
urlHost = urlHost.replace(ipV6Host, "[" + ipV6Host + "]");
}
try {
URL url = new URL(urlHost);
host = url.getHost();
port = url.getPort();
ssl = uri.startsWith("rediss://");
} catch (MalformedURLException e) {
throw new IllegalArgumentException(e);
}
}
public String getScheme() {
if (ssl) {
return "rediss";
}
return "redis";
}
public boolean isSsl() {
return ssl;
}
public String getHost() {
return host;
}
public int getPort() {
return port;
}
private static String trimIpv6Brackets(String host) {
if (host.startsWith("[") && host.endsWith("]")) {
return host.substring(1, host.length() - 1);
}
return host;
}
public static boolean compare(InetSocketAddress entryAddr, RedisURI addr) {
if (((entryAddr.getHostName() != null && entryAddr.getHostName().equals(trimIpv6Brackets(addr.getHost())))
|| entryAddr.getAddress().getHostAddress().equals(trimIpv6Brackets(addr.getHost())))
&& entryAddr.getPort() == addr.getPort()) {
return true;
}
return false;
}
@Override
@SuppressWarnings("AvoidInlineConditionals")
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((host == null) ? 0 : host.hashCode());
result = prime * result + port;
result = prime * result + (ssl ? 1231 : 1237);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
RedisURI other = (RedisURI) obj;
if (host == null) {
if (other.host != null)
return false;
} else if (!host.equals(other.host))
return false;
if (port != other.port)
return false;
if (ssl != other.ssl)
return false;
return true;
}
@Override
public String toString() {
return getScheme() + "://" + host + ":" + port;
}
}

@ -1,90 +0,0 @@
/**
* Copyright (c) 2013-2019 Nikita Koksharov
*
* 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 org.redisson.misc;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.InetSocketAddress;
import java.net.URI;
/**
*
* @author Rui Gu (https://github.com/jackygurui)
*/
public class URIBuilder {
public static URI create(String uri) {
if (!uri.startsWith("redis://")
&& !uri.startsWith("rediss://")) {
throw new IllegalArgumentException("Redis url should start with redis:// or rediss:// (for SSL connection)");
}
URI u = URI.create(uri);
// Let's assuming most of the time it is OK.
if (u.getHost() != null) {
return u;
}
String s = uri.substring(0, uri.lastIndexOf(":")).replaceFirst("redis://", "").replaceFirst("rediss://", "");
// Assuming this is an IPv6 format, other situations will be handled by
// Netty at a later stage.
return URI.create(uri.replace(s, "[" + s + "]"));
}
public static void patchUriObject() {
try {
patchUriField(35184372088832L, "L_DASH");
patchUriField(2147483648L, "H_DASH");
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
private static void patchUriField(Long maskValue, String fieldName)
throws IOException {
try {
Field field = URI.class.getDeclaredField(fieldName);
Field modifiers = Field.class.getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.setAccessible(true);
field.setLong(null, maskValue);
} catch (NoSuchFieldException e) {
// skip for Android platform
} catch (Exception e) {
throw new IOException(e);
}
}
private static String trimIpv6Brackets(String host) {
if (host.startsWith("[") && host.endsWith("]")) {
return host.substring(1, host.length() - 1);
}
return host;
}
public static boolean compare(InetSocketAddress entryAddr, URI addr) {
if (((entryAddr.getHostName() != null && entryAddr.getHostName().equals(trimIpv6Brackets(addr.getHost())))
|| entryAddr.getAddress().getHostAddress().equals(trimIpv6Brackets(addr.getHost())))
&& entryAddr.getPort() == addr.getPort()) {
return true;
}
return false;
}
}
Loading…
Cancel
Save