package de.otto.edison.registry.client; import com.ning.http.client.AsyncCompletionHandler; import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.Response; import de.otto.edison.annotations.Beta; import de.otto.edison.registry.configuration.ServiceRegistryProperties; import de.otto.edison.status.configuration.ApplicationInfoProperties; import de.otto.edison.status.domain.ApplicationInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.annotation.PostConstruct; import java.util.concurrent.ScheduledExecutorService; import static java.util.Arrays.stream; import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; import static java.util.concurrent.TimeUnit.MINUTES; import static org.springframework.util.StringUtils.isEmpty; /** * Simple implementation of a RegistryClient using AsyncHttpClient to register the service. * <p> * In order to use this class, a dependency to com.ning:async-http-client:1.9.40 or later must be added to your build. * </p> * * @author Guido Steinacker * @since 1.0.0 */ @Component @ConditionalOnClass(AsyncHttpClient.class) @ConditionalOnBean(AsyncHttpClient.class) @EnableConfigurationProperties(ServiceRegistryProperties.class) @Beta public class AsyncHttpRegistryClient implements RegistryClient { private static final Logger LOG = LoggerFactory.getLogger(AsyncHttpRegistryClient.class); private final ApplicationInfo applicationInfo; private final AsyncHttpClient httpClient; private final ServiceRegistryProperties serviceRegistryProperties; private final ApplicationInfoProperties applicationInfoProperties; private final ScheduledExecutorService scheduledExecutorService = newSingleThreadScheduledExecutor(); private boolean isRunning = false; @Autowired public AsyncHttpRegistryClient(final ApplicationInfo applicationInfo, final AsyncHttpClient httpClient, final ServiceRegistryProperties serviceRegistryProperties, final ApplicationInfoProperties applicationInfoProperties) { this.applicationInfo = applicationInfo; this.httpClient = httpClient; this.serviceRegistryProperties = serviceRegistryProperties; this.applicationInfoProperties = applicationInfoProperties; } @PostConstruct public void postConstruct() { if (serviceRegistryProperties.isEnabled()) { if (validateConfig()) { LOG.info("Scheduling registration at Edison JobTrigger every '{}' minutes.", serviceRegistryProperties.getRefreshAfter()); scheduledExecutorService .scheduleWithFixedDelay(this::registerService, 0, serviceRegistryProperties.getRefreshAfter(), MINUTES); isRunning = true; } else { LOG.warn("==================================================================================="); LOG.warn("ServiceRegistryProperties is enabled, but no service and/or servers are configured"); LOG.warn(serviceRegistryProperties.toString()); LOG.warn("==================================================================================="); } } else { LOG.info("Scheduling registration at Edison JobTrigger disabled!"); } } @Override public void registerService() { stream(serviceRegistryProperties.getServers().split(",")) .filter(server -> !isEmpty(server)) .forEach(discoveryServer -> { try { LOG.debug("Updating registration of service at '{}'", discoveryServer); httpClient .preparePut(discoveryServer + "/environments/" + applicationInfoProperties.getEnvironment() + "/" + applicationInfo.name) .setHeader("Content-Type", "application/vnd.otto.edison.links+json") .setHeader("Accept", "application/vnd.otto.edison.links+json") .setBody( "{\n" + " \"groups\":[\"" + applicationInfoProperties.getGroup() + "\"],\n" + " \"expire\":" + serviceRegistryProperties.getExpireAfter() + ",\n" + " \"links\":[{\n" + " \"rel\":\"http://github.com/otto-de/edison/link-relations/microservice\",\n" + " \"href\" : \"" + serviceRegistryProperties.getService() + "\",\n" + " \"title\":\"" + applicationInfo.name + "\"\n" + " }] \n" + "}" ) .execute(new AsyncCompletionHandler<Integer>() { @Override public Integer onCompleted(final Response response) throws Exception { if (response.getStatusCode() < 300) { LOG.info("Successfully updated registration at " + discoveryServer); } else { LOG.warn("Failed to update registration at '{}': Status='{}' '{}'", discoveryServer, response.getStatusCode(), response.getStatusText()); } return response.getStatusCode(); } @Override public void onThrowable(final Throwable t) { LOG.error("Failed to register at '{}'", discoveryServer, t); } }); } catch (final Exception e) { LOG.error("Error updating registration", e); } }); } private boolean validateConfig() { if (!serviceRegistryProperties.isEnabled()) { return true; } if (isEmpty(serviceRegistryProperties.getServers())) { return false; } if (isEmpty(serviceRegistryProperties.getService())) { return false; } return true; } @Override public boolean isRunning() { return isRunning; } }