org.apache.camel.component.etcd3.cloud.Etcd3WatchServiceDiscovery Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of camel-etcd3 Show documentation
Show all versions of camel-etcd3 Show documentation
Camel EtcD v3 component based on jetcd
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.camel.component.etcd3.cloud;
import java.nio.charset.Charset;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import io.etcd.jetcd.ByteSequence;
import io.etcd.jetcd.Client;
import io.etcd.jetcd.Watch;
import io.etcd.jetcd.options.WatchOption;
import io.etcd.jetcd.watch.WatchResponse;
import org.apache.camel.cloud.ServiceDefinition;
import org.apache.camel.component.etcd3.Etcd3Configuration;
import org.apache.camel.util.ObjectHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.camel.component.etcd3.Etcd3Helper.toPathPrefix;
/**
* An implementation of a {@link Etcd3ServiceDiscovery} that retrieves all the service definitions from etcd at first
* call, then refresh the list when a change has been detected.
*/
public class Etcd3WatchServiceDiscovery extends Etcd3ServiceDiscovery
implements Watch.Listener {
/**
* The logger.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(Etcd3WatchServiceDiscovery.class);
/**
* The current list of service definitions found.
*/
private volatile List allServices;
/**
* The revision of the last update of the list of service definitions.
*/
private final AtomicLong revision;
/**
* The path prefix of the key-value pairs containing the service definitions.
*/
private final String servicePath;
/**
* The client to access to etcd.
*/
private final Client client;
/**
* The client to watch key-value pairs stored into etcd corresponding to the service definitions.
*/
private final Watch watch;
/**
* The charset to use for the keys.
*/
private final Charset keyCharset;
/**
* The current watcher used to watch the changes of the service definitions.
*/
private final AtomicReference watcher = new AtomicReference<>();
/**
* Construct a {@code Etcd3WatchServiceDiscovery} with the given configuration.
*
* @param configuration the configuration used to set up the service discovery.
*/
public Etcd3WatchServiceDiscovery(Etcd3Configuration configuration) {
super(configuration);
this.revision = new AtomicLong();
this.servicePath = ObjectHelper.notNull(configuration.getServicePath(), "servicePath");
this.client = configuration.createClient();
this.watch = client.getWatchClient();
this.keyCharset = Charset.forName(configuration.getKeyCharset());
}
@Override
protected void doStop() throws Exception {
try {
if (client != null) {
client.close();
}
} finally {
super.doStop();
}
}
@Override
public List getServices(String name) {
List servers = allServices;
if (servers == null) {
lock.lock();
try {
servers = allServices;
if (servers == null) {
servers = reloadServices();
doWatch();
}
} finally {
lock.unlock();
}
}
return servers.stream()
.filter(s -> name.equalsIgnoreCase(s.getName()))
.collect(Collectors.toList());
}
/**
* Gets the list of service definitions from the etcd then update the current list.
*
* @return list of all service definitions that could be found.
*/
private List reloadServices() {
Etcd3GetServicesResponse response = findServices();
revision.getAndUpdate(r -> Math.max(r, response.getRevision() + 1));
this.allServices = response.getServices();
return response.getServices();
}
/**
* If allowed, starts to watch the changes on the service definitions.
*/
private void doWatch() {
if (!isRunAllowed()) {
return;
}
watcher.getAndUpdate(w -> {
if (w != null) {
w.close();
}
return watch.watch(
ByteSequence.from(toPathPrefix(servicePath), keyCharset),
WatchOption.newBuilder().isPrefix(true).withRevision(revision.get()).build(),
this);
});
}
@Override
public void onNext(WatchResponse response) {
// A change has been received let's reload the list
reloadServices();
}
@Override
public void onError(Throwable throwable) {
LOGGER.debug("Cloud not fetch the index, key={}, cause={}", servicePath, throwable);
}
@Override
public void onCompleted() {
doWatch();
}
}