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.HTMLElementName;
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.LastServerCheckTime;
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 CobraStatusRetriever extends AbstractService implements
TopicusApplicationStatusProvider {
private static final Logger log = LoggerFactory
.getLogger(CobraStatusRetriever.class);
private Map<Key, TopicusApplicationStatus> statusses = new HashMap<Key, TopicusApplicationStatus>();
private Map<String, Alert> oldAlerts = new HashMap<String, Alert>();
@Autowired
public CobraStatusRetriever(ISettings settings) {
super(settings);
}
@Override
public TopicusApplicationStatus getStatus(Key project) {
return statusses.get(project);
}
@Override
public void onConfigure(DashboardRepository repository) {
Map<Key, Map<String, ?>> serviceSettings = getSettings()
.getServiceSettings(CobraStatusRetriever.class);
for (Key project : serviceSettings.keySet()) {
repository.addDataSource(project, NumberOfUsers.class,
new NumberOfUsersImpl(project, this));
repository.addDataSource(project, AverageRequestTime.class,
new AverageRequestTimeImpl(project, this));
repository.addDataSource(project, RequestsPerMinute.class,
new RequestsPerMinuteImpl(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));
repository.addDataSource(project, LastServerCheckTime.class,
new LastServerCheckTimeImpl(project, this));
}
}
@Override
@SuppressWarnings("unchecked")
public void refreshData() {
HashMap<Key, TopicusApplicationStatus> newStatusses = new HashMap<Key, TopicusApplicationStatus>();
Map<Key, Map<String, ?>> serviceSettings = getSettings()
.getServiceSettings(CobraStatusRetriever.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(HTMLElementName.TH);
for (Element tableHeader : tableHeaders) {
String contents = tableHeader.getContent().toString();
if ("Applicatie".equals(contents)) {
fetchApplicationVersion(server, tableHeader);
} else if ("Sessions/Requests".equals(contents)) {
fetchSessionAndRequestData(server, tableHeader);
} else if ("Wicket Sessions/Requests".equals(contents)) {
fetchSessionAndRequestData(server, tableHeader);
} else if ("Sessies/Requests".equals(contents)) {
fetchSessionAndRequestData(server, tableHeader);
} else if ("Wicket Sessies/Requests".equals(contents)) {
fetchSessionAndRequestData(server, tableHeader);
}
}
List<Element> tdHeaders = source
.getAllElements(HTMLElementName.TD);
for (Element td : tdHeaders) {
String contents = td.getContent().toString();
if ("Starttijd".equals(contents)) {
getStartTime(server, td);
}
}
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 fetchSessionAndRequestData(TopicusServerStatus server,
Element tableHeader) {
List<Element> tableRows = tableHeader.getParentElement()
.getParentElement().getAllElements(HTMLElementName.TR);
for (Element curRow : tableRows) {
Element nameColumn = curRow.getFirstElement("class", "name_column",
true);
if (nameColumn == null)
continue;
String name = nameColumn.getTextExtractor().toString();
String value = curRow
.getFirstElement("class", "value_column", true)
.getTextExtractor().toString();
if ("Live sessies".equals(name) || "Live Sessions".equals(name)) {
try {
server.setNumberOfUsers(Integer.parseInt(value));
} catch (NumberFormatException e) {
log.error("Cannot parse number of users: " + value);
}
} else if ("Gem. request duur".equals(name)) {
try {
int space = value.indexOf(' ');
if (space > -1) {
value = value.substring(0, space);
}
server.setAverageRequestDuration(Integer.parseInt(value));
} catch (NumberFormatException e) {
log.error("Cannot parse avg request duration: " + value);
}
} else if ("Requests per minuut".equals(name)) {
try {
server.setRequestsPerMinute(Integer.parseInt(value));
} catch (NumberFormatException e) {
log.error("Cannot parse req per minute: " + value);
}
}
}
server.setTime(new Date());
}
private void fetchApplicationVersion(TopicusServerStatus server,
Element tableHeader) {
Element versieCell = tableHeader.getParentElement().getParentElement()
.getContent().getFirstElement("class", "value_column", true);
server.setVersion(versieCell.getContent().getTextExtractor().toString());
}
/*
* <tr><td class="name_column">Starttijd</td><td
* class="value_column"><span>10-10-2010 03:51:22</span></td></tr>
*/
private void getStartTime(TopicusServerStatus server, Element td) {
Element starttijdCell = td.getParentElement().getFirstElement("class",
"value_column", true);
String starttijdText = starttijdCell.getTextExtractor().toString();
SimpleDateFormat sdf1 = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
SimpleDateFormat sdf2 = new SimpleDateFormat("dd-MM-yyyy hh:mm");
Date starttime = null;
try {
starttime = sdf1.parse(starttijdText);
} catch (ParseException e) {
try {
starttime = sdf2.parse(starttijdText);
} catch (ParseException e2) {
log.error("Unable to parse starttime " + starttijdText
+ " according to format " + sdf1.toPattern() + " and"
+ " " + sdf1.toPattern(), e);
}
}
if (starttime != null) {
Date now = new Date();
server.setUptime(Duration.milliseconds(
now.getTime() - starttime.getTime()).getMilliseconds());
}
}
}