/** * Copyright 2015-2016 Red Hat, Inc, and individual contributors. * * 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.wildfly.swarm.topology.consul.runtime; import java.math.BigInteger; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import com.orbitz.consul.CatalogClient; import com.orbitz.consul.HealthClient; import com.orbitz.consul.cache.ServiceHealthCache; import com.orbitz.consul.model.ConsulResponse; import com.orbitz.consul.option.CatalogOptions; import com.orbitz.consul.option.ImmutableCatalogOptions; import com.orbitz.consul.option.ImmutableQueryOptions; import com.orbitz.consul.option.QueryOptions; import org.jboss.msc.inject.Injector; import org.jboss.msc.service.Service; import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.StartContext; import org.jboss.msc.service.StartException; import org.jboss.msc.service.StopContext; import org.jboss.msc.value.InjectedValue; import org.wildfly.swarm.topology.consul.ConsulTopologyMessages; import org.wildfly.swarm.topology.runtime.TopologyManager; /** * Catalog-watching service. * * This service regularly performs a blocking-wait poll of the catalog of all * services in order to avoid having to know a-priori which services are of * interest to the application. * * Any discovered service is setup with a related health-cache listener in * order to maintain the full topology. * * @author John Hovell * @author Bob McWhirter */ public class CatalogWatcher implements Service<CatalogWatcher>, Runnable { public static final ServiceName SERVICE_NAME = ConsulService.SERVICE_NAME.append("catalog-watcher"); public Injector<CatalogClient> getCatalogClientInjector() { return this.catalogClientInjector; } public Injector<HealthClient> getHealthClientInjector() { return this.healthClientInjector; } public Injector<TopologyManager> getTopologyManagerInjector() { return this.topologyManagerInjector; } @Override public void start(StartContext startContext) throws StartException { this.thread = new Thread(this); this.thread.start(); } @Override public void stop(StopContext stopContext) { this.thread.interrupt(); this.watchers.entrySet().forEach(e -> { try { e.getValue().stop(); } catch (Exception ex) { ConsulTopologyMessages.MESSAGES.errorStoppingCatalogWatcher(e.getKey(), ex); } }); } @Override public CatalogWatcher getValue() throws IllegalStateException, IllegalArgumentException { return null; } @Override public void run() { CatalogClient client = this.catalogClientInjector.getValue(); BigInteger index = null; while (true) { QueryOptions options = QueryOptions.BLANK; if (index != null) { options = ImmutableQueryOptions.builder() .wait("60s") .index(index) .build(); } ConsulResponse<Map<String, List<String>>> services = client.getServices(options); index = services.getIndex(); Map<String, List<String>> response = services.getResponse(); response.keySet().forEach(e -> { setupWatcher(e); }); try { Thread.sleep(1000); } catch (InterruptedException e) { break; } } } private void setupWatcher(String serviceName) { if (watchers.containsKey(serviceName)) { return; } CatalogOptions options = ImmutableCatalogOptions.builder() .build(); ServiceHealthCache healthCache = ServiceHealthCache.newCache( this.healthClientInjector.getValue(), serviceName, true, options, 5 ); try { healthCache.addListener(new ServiceCacheListener(serviceName, this.topologyManagerInjector.getValue())); healthCache.start(); healthCache.awaitInitialized(1, TimeUnit.SECONDS); this.watchers.put(serviceName, healthCache); } catch (Exception e) { ConsulTopologyMessages.MESSAGES.errorSettingUpCatalogWatcher(serviceName, e); } } private InjectedValue<CatalogClient> catalogClientInjector = new InjectedValue<>(); private InjectedValue<HealthClient> healthClientInjector = new InjectedValue<>(); private InjectedValue<TopologyManager> topologyManagerInjector = new InjectedValue<>(); private Thread thread; private Map<String, ServiceHealthCache> watchers = new HashMap<>(); }