/** * Copyright 2015 Flipkart Internet Pvt. Ltd. * * 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 com.flipkart.ranger.serviceprovider; import com.flipkart.ranger.healthcheck.HealthChecker; import com.flipkart.ranger.healthcheck.Healthcheck; import com.flipkart.ranger.healthservice.ServiceHealthAggregator; import com.flipkart.ranger.model.Serializer; import com.flipkart.ranger.model.ServiceNode; import com.github.rholder.retry.BlockStrategies; import com.github.rholder.retry.Retryer; import com.github.rholder.retry.RetryerBuilder; import com.github.rholder.retry.StopStrategies; import com.github.rholder.retry.WaitStrategies; import org.apache.curator.framework.CuratorFramework; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; public class ServiceProvider<T> { private static final Logger logger = LoggerFactory.getLogger(ServiceProvider.class); private String serviceName; private Serializer<T> serializer; private CuratorFramework curatorFramework; private ServiceNode<T> serviceNode; private List<Healthcheck> healthchecks; private final int healthUpdateInterval; private final int staleUpdateThreshold; private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); private ScheduledFuture<?> future; private ServiceHealthAggregator serviceHealthAggregator; public ServiceProvider(String serviceName, Serializer<T> serializer, CuratorFramework curatorFramework, ServiceNode<T> serviceNode, List<Healthcheck> healthchecks, int healthUpdateInterval, int staleUpdateThreshold, ServiceHealthAggregator serviceHealthAggregator) { this.serviceName = serviceName; this.serializer = serializer; this.curatorFramework = curatorFramework; this.serviceNode = serviceNode; this.healthchecks = healthchecks; this.healthUpdateInterval = healthUpdateInterval; this.staleUpdateThreshold = staleUpdateThreshold; this.serviceHealthAggregator = serviceHealthAggregator; } public void updateState(ServiceNode<T> serviceNode) throws Exception { final String path = String.format("/%s/%s", serviceName, serviceNode.representation()); if(null == curatorFramework.checkExists().forPath(path)) { createPath(); } curatorFramework.setData().forPath( path, serializer.serialize(serviceNode)); } public void start() throws Exception { serviceHealthAggregator.start(); curatorFramework.blockUntilConnected(); curatorFramework.createContainers(String.format("/%s", serviceName)); logger.debug("Connected to zookeeper for {}", serviceName); createPath(); logger.debug("Set initial node data on zookeeper for {}", serviceName); future = executorService.scheduleWithFixedDelay(new HealthChecker<>(healthchecks, this), 0, healthUpdateInterval, TimeUnit.MILLISECONDS); } public void stop() throws Exception { serviceHealthAggregator.stop(); if(null != future) { future.cancel(true); } curatorFramework.close(); } public ServiceNode<T> getServiceNode() { return serviceNode; } public int getStaleUpdateThreshold() { return staleUpdateThreshold; } private void createPath() throws Exception { Retryer<Void> retryer = RetryerBuilder.<Void>newBuilder() .retryIfExceptionOfType(KeeperException.NodeExistsException.class) //Ephemeral node still exists .withWaitStrategy(WaitStrategies.fixedWait(1, TimeUnit.SECONDS)) .withBlockStrategy(BlockStrategies.threadSleepStrategy()) .withStopStrategy(StopStrategies.neverStop()) .build(); try { retryer.call(() -> { curatorFramework.create().withMode(CreateMode.EPHEMERAL).forPath( String.format("/%s/%s", serviceName, serviceNode.representation()), serializer.serialize(serviceNode)); return null; }); } catch (Exception e) { final String message = String.format("Could not create node for %s after 60 retries (1 min). " + "This service will not be discoverable. Retry after some time.", serviceName); logger.error(message, e); throw new Exception(message, e); } } }