/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2010-2011 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc.
*
* OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
*
* OpenNMS(R) is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* OpenNMS(R) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenNMS(R). If not, see:
* http://www.gnu.org/licenses/
*
* For more information contact:
* OpenNMS(R) Licensing <license@opennms.org>
* http://www.opennms.org/
* http://www.opennms.com/
*******************************************************************************/
package org.opennms.features.poller.remote.gwt.client;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.opennms.features.poller.remote.gwt.client.utils.CompareToBuilder;
import org.opennms.features.poller.remote.gwt.client.utils.Interval;
import org.opennms.features.poller.remote.gwt.client.utils.IntervalUtils;
import org.opennms.features.poller.remote.gwt.client.utils.StringUtils;
import com.google.gwt.user.client.rpc.IsSerializable;
/**
* <p>ApplicationDetails class.</p>
*
* @author ranger
* @version $Id: $
* @since 1.8.1
*/
public class ApplicationDetails implements Serializable, IsSerializable {
private static final long serialVersionUID = -3213261172620899769L;
private String m_name;
private ApplicationInfo m_application;
private Map<Integer,GWTLocationMonitor> m_monitors = new HashMap<Integer,GWTLocationMonitor>();
private Map<Integer,GWTMonitoredService> m_services = new HashMap<Integer,GWTMonitoredService>();
private List<GWTLocationSpecificStatus> m_locationSpecificStatuses;
private Date m_startTime;
private Date m_endTime;
private StatusDetails m_statusDetails;
private Map<Integer, Map<Integer, List<GWTServiceOutage>>> m_outages;
/**
* <p>Constructor for ApplicationDetails.</p>
*/
public ApplicationDetails() {
m_name = null;
setLocationSpecificStatuses(null);
setStartTime(null);
setEndTime(null);
}
/**
* <p>Constructor for ApplicationDetails.</p>
*
* @param application a {@link org.opennms.features.poller.remote.gwt.client.ApplicationInfo} object.
* @param from a {@link java.util.Date} object.
* @param to a {@link java.util.Date} object.
* @param monitors a {@link java.util.Collection} object.
* @param statuses a {@link java.util.List} object.
*/
public ApplicationDetails(final ApplicationInfo application, final Date from, final Date to, final Collection<GWTLocationMonitor> monitors, final List<GWTLocationSpecificStatus> statuses) {
m_name = application.getName();
setApplicationInfo(application);
setStartTime(from);
setEndTime(to);
if (monitors != null) {
for (final GWTLocationMonitor monitor : monitors) {
getMonitors().put(monitor.getId(), monitor);
}
}
setLocationSpecificStatuses(statuses);
if (getLocationSpecificStatuses() != null) {
Collections.sort(getLocationSpecificStatuses(), new LocationSpecificStatusComparator());
for (final GWTLocationSpecificStatus status : getLocationSpecificStatuses()) {
final GWTMonitoredService monitoredService = status.getMonitoredService();
m_services.put(monitoredService.getId(), monitoredService);
}
}
}
private Map<Integer, Map<Integer, List<GWTServiceOutage>>> getOutages() {
if (m_outages == null) {
m_outages = getOutagesUncached();
}
return m_outages;
}
private Map<Integer, Map<Integer, List<GWTServiceOutage>>> getOutagesUncached() {
// service id -> location id -> outages
final Map<Integer, Map<Integer, List<GWTServiceOutage>>> outages = new HashMap<Integer, Map<Integer, List<GWTServiceOutage>>>();
if (getLocationSpecificStatuses() == null) {
return outages;
}
for (final GWTLocationSpecificStatus status : getLocationSpecificStatuses()) {
final Integer serviceId = status.getMonitoredService().getId();
final Integer monitorId = status.getLocationMonitor().getId();
GWTServiceOutage lastOutage = null;
Map<Integer, List<GWTServiceOutage>> serviceOutages = outages.get(serviceId);
if (serviceOutages != null) {
List<GWTServiceOutage> monitorOutages = serviceOutages.get(monitorId);
if (monitorOutages != null && monitorOutages.size() > 0) {
lastOutage = monitorOutages.get(monitorOutages.size() - 1);
}
}
if (lastOutage != null && lastOutage.getTo() == null) {
// there's an existing outage, and it's unfinished
if (!status.getPollResult().isDown()) {
// it's back up
lastOutage.setTo(status.getPollResult().getTimestamp());
continue;
}
// otherwise, it's still down... leave the "to" incomplete
} else {
// there's no existing outage
if (status.getPollResult().isDown()) {
// but the service is down on this monitor, start a new outage
lastOutage = new GWTServiceOutage();
lastOutage.setService(status.getMonitoredService());
lastOutage.setMonitor(status.getLocationMonitor());
lastOutage.setFrom(status.getPollResult().getTimestamp());
if (serviceOutages == null) {
serviceOutages = new HashMap<Integer, List<GWTServiceOutage>>();
outages.put(serviceId, serviceOutages);
}
List<GWTServiceOutage> monitorOutages = serviceOutages.get(monitorId);
if (monitorOutages == null) {
monitorOutages = new ArrayList<GWTServiceOutage>();
serviceOutages.put(monitorId, monitorOutages);
}
serviceOutages.get(monitorId).add(lastOutage);
}
}
}
for (final Integer serviceId : outages.keySet()) {
for (final Integer monitorId : outages.get(serviceId).keySet()) {
for (GWTServiceOutage outage : outages.get(serviceId).get(monitorId)) {
if (outage.getFrom() == null) {
outage.setFrom(getStartTime());
}
if (outage.getTo() == null) {
outage.setTo(getEndTime());
}
}
}
}
return outages;
}
/**
* <p>getStatusDetails</p>
*
* @return a {@link org.opennms.features.poller.remote.gwt.client.StatusDetails} object.
*/
public StatusDetails getStatusDetails() {
if (m_statusDetails == null) {
m_statusDetails = getStatusDetailsUncached();
}
return m_statusDetails;
}
private StatusDetails getStatusDetailsUncached() {
return new AppStatusDetailsComputer(getStartTime(), getEndTime(), getMonitors().values(), getApplicationInfo().getServices(), getLocationSpecificStatuses()).compute();
}
/**
* <p>getAvailability</p>
*
* @param service a {@link org.opennms.features.poller.remote.gwt.client.GWTMonitoredService} object.
* @return a {@link java.lang.Double} object.
*/
public Double getAvailability(final GWTMonitoredService service) {
final Set<Interval> serviceOutages = getServiceOutageIntervals(service.getId());
return computeAvailabilityForOutageIntervals(serviceOutages);
}
/**
* <p>getAvailability</p>
*
* @return a {@link java.lang.Double} object.
*/
public Double getAvailability() {
if (getStartTime() == null || getLocationSpecificStatuses() == null) {
return null;
}
// service id -> location id -> outages
final Map<Integer, Map<Integer, List<GWTServiceOutage>>> outages = getOutages();
Set<Interval> serviceOutageIntervals = IntervalUtils.getIntervalSet();
for (final Integer serviceId : outages.keySet()) {
serviceOutageIntervals.addAll(getServiceOutageIntervals(serviceId));
}
return computeAvailabilityForOutageIntervals(IntervalUtils.normalize(serviceOutageIntervals));
}
private Double computeAvailabilityForOutageIntervals(final Set<Interval> intervals) {
Long timeAvailable = 0L;
final Set<Interval> upIntervals = IntervalUtils.invert(getStartTime(), getEndTime(), intervals);
for (final Interval i : upIntervals) {
timeAvailable += (i.getEndMillis() - i.getStartMillis());
}
final Long totalTime = getEndTime().getTime() - getStartTime().getTime();
final double availability = timeAvailable.doubleValue() / totalTime.doubleValue() * 100;
return availability;
}
private Set<Interval> getServiceOutageIntervals(final Integer serviceId) {
final Map<Integer, Map<Integer, List<GWTServiceOutage>>> outages = getOutages();
final Set<Interval> serviceUpIntervals = IntervalUtils.getIntervalSet();
final Map<Integer, List<GWTServiceOutage>> serviceOutages = outages.get(serviceId);
if (serviceOutages != null && serviceOutages.size() != 0) {
for (final GWTLocationMonitor monitor : getMonitors().values()) {
final Integer locationId = monitor.getId();
Set<Interval> locationIntervals = IntervalUtils.getIntervalSet();
if (serviceOutages.containsKey(locationId)) {
for (final GWTServiceOutage outage : serviceOutages.get(locationId)) {
locationIntervals.add(new Interval(outage.getFrom().getTime(), outage.getTo().getTime()));
}
}
locationIntervals = IntervalUtils.invert(getStartTime(), getEndTime(), IntervalUtils.normalize(locationIntervals));
serviceUpIntervals.addAll(locationIntervals);
}
return IntervalUtils.invert(getStartTime(), getEndTime(), IntervalUtils.normalize(serviceUpIntervals));
}
return IntervalUtils.getIntervalSet();
}
/**
* <p>toString</p>
*
* @return a {@link java.lang.String} object.
*/
public String toString() {
return "ApplicationDetails[name=" + m_name + ",range=" + getStartTime() + "-" + getEndTime() + ",statuses=" + getLocationSpecificStatuses() + "]";
}
/**
* <p>getApplicationName</p>
*
* @return a {@link java.lang.String} object.
*/
public String getApplicationName() {
return m_name;
}
/**
* <p>getDetailsAsString</p>
*
* @return a {@link java.lang.String} object.
*/
public String getDetailsAsString() {
// service id -> location id -> outages
final Map<Integer, Map<Integer, List<GWTServiceOutage>>> outages = getOutages();
StringBuilder sb = new StringBuilder();
sb.append("<div id=\"applicationDetails\">\n");
sb.append("<dl class=\"statusContents\">\n");
Set<GWTMonitoredService> services = new TreeSet<GWTMonitoredService>(new Comparator<GWTMonitoredService>() {
public int compare(final GWTMonitoredService a, final GWTMonitoredService b) {
if (a == null) {
if (b == null) return 0;
return 1;
}
if (b == null) return -1;
return new CompareToBuilder()
.append(a.getServiceName(), b.getServiceName())
.append(a.getId(), b.getId())
.toComparison();
}
});
services.addAll(m_services.values());
for (final GWTMonitoredService service : services) {
final Integer serviceId = service.getId();
final double serviceAvailability = getAvailability(service);
String styleName = Status.UNKNOWN.getStyle();
List<GWTLocationMonitor> locationsNotReporting = new ArrayList<GWTLocationMonitor>();
if (serviceAvailability == 100.0) {
styleName = Status.UP.getStyle();
Map<Integer,List<GWTServiceOutage>> serviceOutages = outages.get(serviceId);
if (serviceOutages != null) {
for (final Integer locationId : serviceOutages.keySet()) {
final List<GWTServiceOutage> locationOutages = serviceOutages.get(locationId);
if (locationOutages != null) {
for (final GWTServiceOutage outage : locationOutages) {
if (outage.getTo().equals(getEndTime()) || outage.getTo().after(getEndTime())) {
locationsNotReporting.add(getMonitors().get(locationId));
styleName = Status.MARGINAL.getStyle();
continue;
}
}
}
}
}
} else {
List<Interval> serviceOutageIntervals = new ArrayList<Interval>(getServiceOutageIntervals(serviceId));
final int size = serviceOutageIntervals.size();
if (size > 0) {
if (serviceOutageIntervals.get(size - 1).getEndMillis() == getEndTime().getTime()) {
styleName = Status.DOWN.getStyle();
} else {
styleName = Status.MARGINAL.getStyle();
}
}
styleName = Status.DOWN.getStyle();
}
final List<GWTLocationMonitor> monitors = new ArrayList<GWTLocationMonitor>(getMonitors().values());
Collections.sort(monitors);
sb.append("<dt class=\"").append(styleName).append(" statusDt\">").append(getSummary(service)).append("</dt>\n");
sb.append("<dd class=\"").append(styleName).append(" statusDd\">");
sb.append("Availability: ").append(Double.valueOf(serviceAvailability).intValue()).append("%");
if (locationsNotReporting.size() > 0) {
final List<String> locationString = new ArrayList<String>();
for (final GWTLocationMonitor monitor : locationsNotReporting) {
locationString.add(monitor.getName());
}
sb.append("<br>\n").append("Location");
if (locationsNotReporting.size() > 1) sb.append("s");
sb.append(" with outages: ").append(StringUtils.join(locationString));
}
sb.append("</dd>\n");
}
sb.append("</div>\n");
return sb.toString();
}
private String getSummary(final GWTMonitoredService service) {
final StringBuilder sb = new StringBuilder();
sb.append(service.getServiceName()).append(" (Node ").append(service.getNodeId()).append(")");
if (service.getHostname() != null) {
sb.append("<br>\n").append(service.getHostname());
if (service.getIpAddress() != null && !service.getIpAddress().equals(service.getHostname())) {
sb.append("/").append(service.getIpAddress());
}
}
return sb.toString();
}
/**
* @return the monitors
*/
private Map<Integer,GWTLocationMonitor> getMonitors() {
return m_monitors;
}
/**
* @param locationSpecificStatuses the locationSpecificStatuses to set
*/
private void setLocationSpecificStatuses(List<GWTLocationSpecificStatus> locationSpecificStatuses) {
m_locationSpecificStatuses = locationSpecificStatuses;
}
/**
* @return the locationSpecificStatuses
*/
private List<GWTLocationSpecificStatus> getLocationSpecificStatuses() {
return m_locationSpecificStatuses;
}
/**
* @param application the application to set
*/
private void setApplicationInfo(ApplicationInfo application) {
m_application = application;
}
/**
* <p>getApplicationInfo</p>
*
* @return the application
*/
public ApplicationInfo getApplicationInfo() {
return m_application;
}
/**
* @param statusTo the statusTo to set
*/
private void setEndTime(Date endTime) {
m_endTime = endTime;
}
/**
* @return the statusTo
*/
private Date getEndTime() {
return m_endTime;
}
/**
* @param startTime the startTime to set
*/
private void setStartTime(Date startTime) {
m_startTime = startTime;
}
/**
* @return the startTime
*/
private Date getStartTime() {
return m_startTime;
}
}