/* * Copyright © 2014-2015 Cask Data, Inc. * * 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 co.cask.cdap.common.guice; import co.cask.cdap.common.conf.CConfiguration; import co.cask.cdap.common.conf.Constants; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.inject.Inject; import com.google.inject.name.Named; import org.apache.twill.discovery.DiscoveryServiceClient; import org.apache.twill.discovery.ServiceDiscovered; import org.apache.twill.discovery.ZKDiscoveryService; import org.apache.twill.zookeeper.ZKClient; import org.apache.twill.zookeeper.ZKClients; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.TimeUnit; /** * A DiscoveryServiceClient implementation that will namespace correctly for program service discovery. * Otherwise it'll delegate to default one. */ final class ProgramDiscoveryServiceClient implements DiscoveryServiceClient { private static final Logger LOG = LoggerFactory.getLogger(ProgramDiscoveryServiceClient.class); private static final long CACHE_EXPIRES_MINUTES = 1; /** * Defines the set of program types that are discoverable. * TODO: We have program type in app-fabric, but here is common. Worth to refactor Type into one place. */ private enum DiscoverableProgramType { WORKFLOW, WEBAPP, SERVICE; private final String prefix; DiscoverableProgramType() { prefix = name().toLowerCase() + "."; } boolean isPrefixOf(String name) { return name.startsWith(prefix); } } private final ZKClient zkClient; private final DiscoveryServiceClient delegate; private final String twillNamespace; private final LoadingCache<String, DiscoveryServiceClient> clients; @Inject ProgramDiscoveryServiceClient(ZKClient zkClient, CConfiguration configuration, @Named("local.discovery.client") DiscoveryServiceClient delegate) { this.zkClient = zkClient; this.delegate = delegate; this.twillNamespace = configuration.get(Constants.CFG_TWILL_ZK_NAMESPACE); this.clients = CacheBuilder.newBuilder().expireAfterAccess(CACHE_EXPIRES_MINUTES, TimeUnit.MINUTES) .build(createClientLoader()); } @Override public ServiceDiscovered discover(final String name) { for (DiscoverableProgramType type : DiscoverableProgramType.values()) { if (type.isPrefixOf(name)) { return clients.getUnchecked(name).discover(name); } } return delegate.discover(name); } private CacheLoader<String, DiscoveryServiceClient> createClientLoader() { return new CacheLoader<String, DiscoveryServiceClient>() { @Override public DiscoveryServiceClient load(String key) throws Exception { int idx = key.indexOf('.'); // It must be found as checked in the discover method String ns = String.format("%s/%s%s", twillNamespace, key.substring(0, idx), key.substring(idx)); LOG.debug("Create ZKDiscoveryClient for " + ns); return new ZKDiscoveryService(ZKClients.namespace(zkClient, ns)); } }; } }