/* * Copyright 2016 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * * 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.kie.server.router.client; import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import org.kie.server.api.KieServerConstants; import org.kie.server.api.model.KieContainerResource; import org.kie.server.api.model.KieContainerResourceFilter; import org.kie.server.api.model.KieContainerResourceList; import org.kie.server.api.model.KieContainerStatusFilter; import org.kie.server.api.model.ReleaseIdFilter; import org.kie.server.api.model.ServiceResponse; import org.kie.server.common.rest.KieServerHttpRequest; import org.kie.server.common.rest.KieServerHttpRequestException; import org.kie.server.common.rest.KieServerHttpResponse; import org.kie.server.services.api.KieContainerInstance; import org.kie.server.services.api.KieServer; import org.kie.server.services.api.KieServerEventListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class KieServerRouterEventListener implements KieServerEventListener { private static final Logger logger = LoggerFactory.getLogger(KieServerRouterEventListener.class); private static final String ROUTER_ADD_URL = "/admin/add"; private static final String ROUTER_REMOVE_URL = "/admin/remove"; private String serverId = System.getProperty(KieServerConstants.KIE_SERVER_ID); private String serverURL = System.getProperty(KieServerConstants.KIE_SERVER_LOCATION); private String routerURL = System.getProperty(KieServerConstants.KIE_SERVER_ROUTER); private int failedAttemptsInterval = Integer.parseInt(System.getProperty(KieServerConstants.KIE_SERVER_ROUTER_ATTEMPT_INTERVAL, "10")); private KieContainerResourceFilter activeOnly = new KieContainerResourceFilter(ReleaseIdFilter.ACCEPT_ALL, KieContainerStatusFilter.parseFromNullableString("STARTED")); private ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); private RouterConnectionObserver observer = new RouterConnectionObserver(); private String CONTAINER_JSON = "\"containerId\" : \"{0}\"," + "\"alias\" : \"{1}\"," + "\"serverUrl\" : \"{2}\"," + "\"serverId\" : \"{3}\"," + "\"releaseId\" : \"{4}\""; public KieServerRouterEventListener() { } public KieServerRouterEventListener(String serverId, String serverURL, String routerURL, int failedAttemptsInterval) { this.serverId = serverId; this.serverURL = serverURL; this.routerURL = routerURL; this.failedAttemptsInterval = failedAttemptsInterval; } @Override public void beforeServerStarted(KieServer kieServer) { } @Override public void afterServerStarted(KieServer kieServer) { } @Override public void beforeServerStopped(KieServer kieServer) { if (routerURL == null) { logger.debug("KieServer router url not given, skipping"); return; } ServiceResponse<KieContainerResourceList> containers = kieServer.listContainers(activeOnly); containers.getResult().getContainers().forEach(ci -> { routers().forEach(url -> { String alias = getContainerAlias(ci); String containerIdPayload = "{" + MessageFormat.format(CONTAINER_JSON, ci.getContainerId(), alias, serverURL, serverId, ci.getReleaseId().toExternalForm()) + "}"; boolean success = send(url + ROUTER_REMOVE_URL, ci.getContainerId(), containerIdPayload, false, false); if (success) { logger.info("Removed '{}' as server location for container id '{}'", serverURL, ci.getContainerId()); } }); }); } @Override public void afterServerStopped(KieServer kieServer) { close(); } @Override public void beforeContainerStarted(KieServer kieServer, KieContainerInstance containerInstance) { } @Override public void afterContainerStarted(KieServer kieServer, KieContainerInstance containerInstance) { if (routerURL == null) { logger.debug("KieServer router url not given, skipping"); return; } routers().forEach(url -> { String alias = getContainerAlias(containerInstance.getResource()); String containerIdPayload = "{" + MessageFormat.format(CONTAINER_JSON, containerInstance.getContainerId(), alias, serverURL, serverId, containerInstance.getResource().getReleaseId().toExternalForm()) + "}"; boolean success = send(url + ROUTER_ADD_URL, containerInstance.getContainerId(), containerIdPayload, true, true); if (success) { logger.info("Added '{}' as server location for container id '{}'", serverURL, containerInstance.getContainerId()); } }); } @Override public void beforeContainerStopped(KieServer kieServer, KieContainerInstance containerInstance) { } @Override public void afterContainerStopped(KieServer kieServer, KieContainerInstance containerInstance) { if (routerURL == null) { logger.debug("KieServer router url not given, skipping"); return; } routers().forEach(url -> { String alias = getContainerAlias(containerInstance.getResource()); String containerIdPayload = "{" + MessageFormat.format(CONTAINER_JSON, containerInstance.getContainerId(), alias, serverURL, serverId, containerInstance.getResource().getReleaseId().toExternalForm()) + "}"; boolean success = send(url + ROUTER_REMOVE_URL, containerInstance.getContainerId(), containerIdPayload, false, true); if (success) { logger.info("Removed '{}' as server location for container id '{}'", serverURL, containerInstance.getContainerId()); } }); } protected boolean send(String url, String containerId, String payload, boolean add, boolean retry) { try { KieServerHttpRequest httpRequest = KieServerHttpRequest.newRequest(url) .followRedirects(true) .contentType("application/json") .accept("application/json") .timeout(5000) .body(payload) .post(); KieServerHttpResponse response = httpRequest.response(); int responseCode = response.code(); logger.debug("Response for url {} is {}", httpRequest.getUrl(), responseCode); if (responseCode > 201) { throw new KieServerHttpRequestException("Connection error " + responseCode); } observer.onSuccess(url); return true; } catch (KieServerHttpRequestException ioe) { logger.debug("Send to router failed", ioe); if (retry) { executorService.schedule(() -> { boolean success = send(url, containerId, payload, add, true); if (success) { if (add) { logger.info("Added '{}' as server location for container '{}'", serverURL, containerId); } else { logger.info("Removed '{}' as server location for container '{}'", serverURL, containerId); } } }, failedAttemptsInterval, TimeUnit.SECONDS); logger.warn("Failed at sending request to router at {} due to {}. Next attempt is scheduled to fire in {} seconds", url, findCause(ioe).getMessage(), failedAttemptsInterval); } else { logger.warn("Failed at sending request to router at {} due to {}.", url, findCause(ioe).getMessage()); } observer.onFailure(url); return false; } catch (Exception e) { logger.warn("Failed at sending request to router at {} due to {}", url, findCause(e).getMessage()); logger.debug("Send to router failed", e); return false; } } protected String getContainerAlias(KieContainerResource containerInstance) { String alias = containerInstance.getContainerAlias(); if (alias == null || alias.isEmpty()) { alias = containerInstance.getReleaseId().getArtifactId(); } return alias; } protected List<String> routers() { ArrayList<String> list = new ArrayList<>(); String[] routerUrls = routerURL.split(","); for (String routerUrl : routerUrls) { routerUrl = routerUrl.trim(); if (routerUrl.endsWith("/")) { routerUrl = routerUrl.substring(0, routerUrl.length()-1); } list.add(routerUrl); } return list; } protected Throwable findCause(Exception e) { Throwable found = e; while (found.getCause() != null) { found = found.getCause(); } return found; } public RouterConnectionObserver getObserver() { return observer; } public void setObserver(RouterConnectionObserver observer) { this.observer = observer; } public static class RouterConnectionObserver { public void onSuccess(String url) { } public void onFailure(String url) { } } public void close() { logger.debug("About to shutdown internal executor service to handle failed attempts when connecting to kie server router..."); this.executorService.shutdownNow(); logger.debug("Internal executor service to handle failed attempts when connecting to kie server router stopped successfully"); } }