/** * Copyright 2016-2017 Sixt GmbH & Co. Autovermietung KG * 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.sixt.service.framework.configuration.sixt; import com.google.inject.Inject; import com.google.inject.Singleton; import com.sixt.service.configuration.api.ConfigurationOuterClass; import com.sixt.service.framework.OrangeContext; import com.sixt.service.framework.ServiceProperties; import com.sixt.service.framework.annotation.ConfigurationPlugin; import com.sixt.service.framework.configuration.ConfigurationManager; import com.sixt.service.framework.configuration.ConfigurationProvider; import com.sixt.service.framework.rpc.RpcCallException; import com.sixt.service.framework.rpc.RpcClient; import com.sixt.service.framework.rpc.RpcClientFactory; import com.sixt.service.framework.util.Sleeper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; @Singleton @ConfigurationPlugin(name = "sixt") public class SixtConfigurationPlugin implements ConfigurationProvider, Runnable { public final static int LONG_POLL_TIMEOUT = 900_000; //15 minutes private static final Logger logger = LoggerFactory.getLogger(SixtConfigurationPlugin.class); protected ConfigurationManager configManager; protected RpcClientFactory clientFactory; private final ServiceProperties serviceProps; private AtomicInteger lastChangeIndex = new AtomicInteger(0); protected Sleeper sleeper = new Sleeper(); protected final String serviceName; @Inject public SixtConfigurationPlugin(ConfigurationManager cm, RpcClientFactory clientFactory, ServiceProperties serviceProperties) { this.configManager = cm; this.clientFactory = clientFactory; this.serviceProps = serviceProperties; this.serviceName = serviceProperties.getServiceName(); } @Override public void initialize() { Thread t = new Thread(this, "SixtConfigurationPlugin"); t.setDaemon(true); t.start(); } @Override public void run() { while (true) { try { performLongPoll(); } catch (Exception ex) { logger.error("Caught exception performing longPoll", ex); } } } @SuppressWarnings("unchecked") protected void performLongPoll() { ConfigurationOuterClass.Filter configRequest = ConfigurationOuterClass.Filter.newBuilder(). setService(serviceName). setVersion(serviceProps.getServiceVersion()). setInstance(serviceProps.getServiceInstanceId()). setChangeIndex(lastChangeIndex.get()).build(); boolean giveWarning = true; long sleepTime = 1000; ConfigurationOuterClass.ValuesResponse response; while (true) { try { RpcClient<ConfigurationOuterClass.ValuesResponse> client = clientFactory.newClient("com.sixt.service.configuration", "Configuration.GetValues", ConfigurationOuterClass.ValuesResponse.class). withRetries(0).withTimeout(LONG_POLL_TIMEOUT).build(); logger.debug("Calling configuration service, lastChangeIndex = {}", lastChangeIndex.get()); response = client.callSynchronous(configRequest, new OrangeContext()); logger.debug("Got configuration service response"); break; } catch (RpcCallException ex) { if (giveWarning) { logger.info("Getting configuration failed, will retry. {}", ex.toString()); giveWarning = false; } sleeper.sleepNoException(sleepTime); sleepTime = increaseSleepTime(sleepTime); } } if (response != null) { processValues(response.getValuesList()); lastChangeIndex.set(response.getChangeIndex()); } } //pseudo-exponential back-off private long increaseSleepTime(long sleepTime) { if (sleepTime == 1000) { return 4000; } else if (sleepTime == 4000) { return 16000; } else { return 60000; } } private void processValues(List<ConfigurationOuterClass.ValueResponse> valuesList) { Map<String, String> newValues = new HashMap<>(valuesList.size()); for (ConfigurationOuterClass.ValueResponse cv : valuesList) { logger.debug("Got configuration, entry = {}, value = {}", cv.getName(), cv.getBaseValue()); newValues.put(cv.getName(), cv.getBaseValue()); } configManager.processValues(newValues); } }