package nl.topicus.onderwijs.dashboard.modules.topicus; import static nl.topicus.onderwijs.dashboard.modules.topicus.RetrieverUtils.getStatuspage; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.TimeUnit; import net.htmlparser.jericho.Element; import net.htmlparser.jericho.Source; import nl.topicus.onderwijs.dashboard.config.ISettings; import nl.topicus.onderwijs.dashboard.datasources.ApplicationVersion; import nl.topicus.onderwijs.dashboard.datasources.AverageRequestTime; import nl.topicus.onderwijs.dashboard.datasources.NumberOfServers; import nl.topicus.onderwijs.dashboard.datasources.NumberOfServersOffline; import nl.topicus.onderwijs.dashboard.datasources.NumberOfUsers; import nl.topicus.onderwijs.dashboard.datasources.NumberOfUsersPerServer; import nl.topicus.onderwijs.dashboard.datasources.RequestsPerMinute; import nl.topicus.onderwijs.dashboard.datasources.ServerAlerts; import nl.topicus.onderwijs.dashboard.datasources.ServerStatus; import nl.topicus.onderwijs.dashboard.datasources.Uptime; import nl.topicus.onderwijs.dashboard.datatypes.Alert; import nl.topicus.onderwijs.dashboard.datatypes.DotColor; import nl.topicus.onderwijs.dashboard.keys.Key; import nl.topicus.onderwijs.dashboard.modules.AbstractService; import nl.topicus.onderwijs.dashboard.modules.DashboardRepository; import nl.topicus.onderwijs.dashboard.modules.ServiceConfiguration; import org.apache.wicket.util.time.Duration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service @ServiceConfiguration(interval = 30, unit = TimeUnit.SECONDS) public class ParnassysStatusRetriever extends AbstractService implements TopicusApplicationStatusProvider { private static final Logger log = LoggerFactory .getLogger(ParnassysStatusRetriever.class); private HashMap<Key, TopicusApplicationStatus> statusses = new HashMap<Key, TopicusApplicationStatus>(); private Map<String, Alert> oldAlerts = new HashMap<String, Alert>(); @Autowired public ParnassysStatusRetriever(ISettings settings) { super(settings); } @Override public void onConfigure(DashboardRepository repository) { Map<Key, Map<String, ?>> serviceSettings = getSettings() .getServiceSettings(ParnassysStatusRetriever.class); for (Key project : serviceSettings.keySet()) { repository.addDataSource(project, RequestsPerMinute.class, new RequestsPerMinuteImpl(project, this)); repository.addDataSource(project, AverageRequestTime.class, new AverageRequestTimeImpl(project, this)); repository.addDataSource(project, NumberOfUsers.class, new NumberOfUsersImpl(project, this)); repository.addDataSource(project, NumberOfServers.class, new NumberOfServersImpl(project, this)); repository.addDataSource(project, NumberOfServersOffline.class, new NumberOfServersOfflineImpl(project, this)); repository.addDataSource(project, Uptime.class, new UptimeImpl( project, this)); repository.addDataSource(project, ApplicationVersion.class, new ApplicationVersionImpl(project, this)); repository.addDataSource(project, ServerStatus.class, new ServerStatusImpl(project, this)); repository.addDataSource(project, ServerAlerts.class, new AlertsImpl(project, this)); repository.addDataSource(project, NumberOfUsersPerServer.class, new NumberOfUsersPerServerImpl(project, this)); } } @Override @SuppressWarnings("unchecked") public void refreshData() { HashMap<Key, TopicusApplicationStatus> newStatusses = new HashMap<Key, TopicusApplicationStatus>(); Map<Key, Map<String, ?>> serviceSettings = getSettings() .getServiceSettings(ParnassysStatusRetriever.class); for (Map.Entry<Key, Map<String, ?>> configEntry : serviceSettings .entrySet()) { Key project = configEntry.getKey(); Map<String, String> urls = (Map<String, String>) configEntry .getValue().get("urls"); TopicusApplicationStatus status = getProjectData(project, urls); newStatusses.put(project, status); } statusses = newStatusses; } private TopicusApplicationStatus getProjectData(Key project, Map<String, String> urls) { TopicusApplicationStatus status = new TopicusApplicationStatus(); if (urls == null || urls.isEmpty()) { return status; } List<Alert> alerts = new ArrayList<Alert>(); for (Entry<String, String> statusUrlEntry : urls.entrySet()) { String statusCode = statusUrlEntry.getKey(); String statusUrl = statusUrlEntry.getValue(); TopicusServerStatus server = new TopicusServerStatus(statusCode, statusUrl); status.addServer(server); Alert oldAlert = oldAlerts.get(statusUrl); try { StatusPageResponse statuspage = getStatuspage(statusUrl); if (!statuspage.isOk()) { server.setServerStatus(DotColor.RED); Alert alert = new Alert(oldAlert, DotColor.RED, project, "Server " + statusCode + " offline with HTTP code " + statuspage.getHttpStatusCode()); oldAlerts.put(statusUrl, alert); alerts.add(alert); continue; } String page = statuspage.getPageContent(); Source source = new Source(page); source.fullSequentialParse(); List<Element> tableHeaders = source.getAllElements("class", "main_label", true); for (Element tableHeader : tableHeaders) { String contents = tableHeader.getContent().toString(); if ("Actieve sessies:".equals(contents)) { fetchNumberOfUsers(server, tableHeader); } else if ("Gestart op:".equals(contents)) { fetchStartTijd(server, tableHeader); } else if ("Gemiddelde requesttijd:".equals(contents)) { fetchAvgRequestTime(server, tableHeader); } else if ("Requests per minuut:".equals(contents)) { fetchRequestsPerMinute(server, tableHeader); } } server.setServerStatus(DotColor.GREEN); oldAlerts.put(statusUrl, null); } catch (Exception e) { server.setServerStatus(DotColor.YELLOW); Alert alert = new Alert(oldAlert, DotColor.YELLOW, project, e.getMessage()); oldAlerts.put(statusUrl, alert); alerts.add(alert); log.warn("Could not retrieve status for '" + statusUrl + "': " + e.getClass().getSimpleName() + " - " + e.getLocalizedMessage()); } } status.setAlerts(alerts); log.info("Application status: {}->{}", project, status); return status; } private void fetchNumberOfUsers(TopicusServerStatus server, Element tableHeader) { Element sessiesCell = tableHeader.getParentElement().getContent() .getChildElements().get(1); String tdContents = sessiesCell.getTextExtractor().toString(); Integer numberOfUsersOnServer = Integer.valueOf(tdContents); server.setNumberOfUsers(numberOfUsersOnServer); } private void fetchStartTijd(TopicusServerStatus server, Element tableHeader) { Element starttijdCell = tableHeader.getParentElement().getContent() .getChildElements().get(1); String starttijdText = starttijdCell.getTextExtractor().toString(); SimpleDateFormat sdf = new SimpleDateFormat("E MMM dd HH:mm:ss Z yyyy"); try { Date starttime = sdf.parse(starttijdText); Date now = new Date(); server.setUptime(Duration.milliseconds( now.getTime() - starttime.getTime()).getMilliseconds()); } catch (ParseException e) { log.error("Unable to parse starttime " + starttijdText + " according to format " + sdf.toPattern(), e); } } private void fetchAvgRequestTime(TopicusServerStatus server, Element tableHeader) { Element sessiesCell = tableHeader.getParentElement().getContent() .getChildElements().get(1); String tdContents = sessiesCell.getTextExtractor().toString(); int space = tdContents.indexOf(' '); if (space > -1) { tdContents = tdContents.substring(0, space); } int avgRequestTime = Integer.parseInt(tdContents); server.setAverageRequestDuration(avgRequestTime); } private void fetchRequestsPerMinute(TopicusServerStatus server, Element tableHeader) { Element sessiesCell = tableHeader.getParentElement().getContent() .getChildElements().get(1); String tdContents = sessiesCell.getTextExtractor().toString(); Integer requestsPerMinute = Integer.valueOf(tdContents); server.setRequestsPerMinute(requestsPerMinute); } @Override public TopicusApplicationStatus getStatus(Key project) { return statusses.get(project); } }