/* * Copyright 2013-2014 the original author or authors. * * 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 de.codecentric.boot.admin.registry; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.http.ResponseEntity; import de.codecentric.boot.admin.event.ClientApplicationStatusChangedEvent; import de.codecentric.boot.admin.model.Application; import de.codecentric.boot.admin.model.Info; import de.codecentric.boot.admin.model.StatusInfo; import de.codecentric.boot.admin.registry.store.ApplicationStore; import de.codecentric.boot.admin.web.client.ApplicationOperations; /** * The StatusUpdater is responsible for updating the status of all or a single application querying * the healthUrl. * * @author Johannes Edmeier */ public class StatusUpdater implements ApplicationEventPublisherAware { private static final Logger LOGGER = LoggerFactory.getLogger(StatusUpdater.class); private final ApplicationStore store; private final ApplicationOperations applicationOps; private ApplicationEventPublisher publisher; private long statusLifetime = 10_000L; public StatusUpdater(ApplicationStore store, ApplicationOperations applicationOps) { this.store = store; this.applicationOps = applicationOps; } public void updateStatusForAllApplications() { long now = System.currentTimeMillis(); for (Application application : store.findAll()) { if (now - statusLifetime > application.getStatusInfo().getTimestamp()) { updateStatus(application); } } } public void updateStatus(Application application) { StatusInfo oldStatus = application.getStatusInfo(); StatusInfo newStatus = queryStatus(application); boolean statusChanged = !newStatus.equals(oldStatus); Application.Builder builder = Application.copyOf(application).withStatusInfo(newStatus); if (statusChanged && !newStatus.isOffline() && !newStatus.isUnknown()) { builder.withInfo(queryInfo(application)); } Application newState = builder.build(); store.save(newState); if (statusChanged) { publisher.publishEvent( new ClientApplicationStatusChangedEvent(newState, oldStatus, newStatus)); } } protected Info queryInfo(Application application) { try { ResponseEntity<Map<String, Serializable>> response = applicationOps .getInfo(application); if (response.getStatusCode().is2xxSuccessful() && response.hasBody()) { return convertInfo(response); } else { LOGGER.info("Couldn't retrieve info for {}: {} - {}", application, response.getStatusCode(), response.getBody()); return Info.empty(); } } catch (Exception ex) { LOGGER.warn("Couldn't retrieve info for {}", application, ex); return convertInfo(ex); } } protected Info convertInfo(ResponseEntity<Map<String, Serializable>> response) { return Info.from(response.getBody()); } protected Info convertInfo(Exception ex) { return Info.empty(); } protected StatusInfo queryStatus(Application application) { LOGGER.trace("Updating status for {}", application); try { return convertStatusInfo(applicationOps.getHealth(application)); } catch (Exception ex) { if ("OFFLINE".equals(application.getStatusInfo().getStatus())) { LOGGER.debug("Couldn't retrieve status for {}", application, ex); } else { LOGGER.info("Couldn't retrieve status for {}", application, ex); } return convertStatusInfo(ex); } } protected StatusInfo convertStatusInfo(ResponseEntity<Map<String, Serializable>> response) { if (response.hasBody() && response.getBody().get("status") instanceof String) { return StatusInfo.valueOf((String) response.getBody().get("status"), response.getBody()); } if (response.getStatusCode().is2xxSuccessful()) { return StatusInfo.ofUp(); } Map<String, Serializable> details = new HashMap<>(); details.put("status", response.getStatusCodeValue()); details.put("error", response.getStatusCode().getReasonPhrase()); if (response.hasBody()) { details.putAll(response.getBody()); } return StatusInfo.ofDown(details); } protected StatusInfo convertStatusInfo(Exception ex) { Map<String, Serializable> details = new HashMap<>(); details.put("message", ex.getMessage()); details.put("exception", ex.getClass().getName()); return StatusInfo.ofOffline(details); } public void setStatusLifetime(long statusLifetime) { this.statusLifetime = statusLifetime; } public long getStatusLifetime() { return statusLifetime; } @Override public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { this.publisher = publisher; } }