/*
* RHQ Management Platform
* Copyright (C) 2005-2008 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation, and/or the GNU Lesser
* General Public License, version 2.1, also as published by the Free
* Software Foundation.
*
* This program 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 and the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.rhq.core.domain.event.transfer;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.jetbrains.annotations.NotNull;
import org.rhq.core.domain.event.Event;
import org.rhq.core.domain.event.EventSeverity;
import org.rhq.core.domain.event.EventSource;
/**
* A report of resource {@link Event}s that the Plugin Container periodically sends to the Server. The report contains
* all Events that have occurred since the last time a report was successfully sent.
*
* Each report has a limit on the number of events they can hold. There are actually two limits: first,
* each event source has a limit on the number of events they can have and second, the report has a total
* number of events it can hold at a maximum (i.e. the sum of all events for all event sources cannot
* exceed this total maximum).
*
* This report maintains a counter of the number of events that have been dropped due to hitting these
* maximum limits. If you want this report to get additional warning events added to them to indicate
* if these maximum limits were exceeded, call {@link #addLimitWarningEvents()}.
*
* @author Ian Springer
* @author John Mazzitelli
*/
public class EventReport implements Serializable {
private static final long serialVersionUID = 2L;
private final int maxEventsPerSource;
private final int maxEventsPerReport;
private Map<EventSource, Set<Event>> events = new HashMap<EventSource, Set<Event>>();
// these are transient because the are only used when building up the report.
// after the report has been completed and to be sent over the wire, they are no longer needed.
// eventsDropped=number of events that were dropped due to limits being reached
// totalEventsInReport=the total number of events in the report (sum of all events for all event sources)
// addedLimitWarningEvents=true when one or more limit warning events were added to this report
private transient Map<EventSource, Integer> eventsDropped;
private transient int totalEventsInReport = 0;
private transient boolean addedLimitWarningEvents = false;
public EventReport(int maxEventsPerSource, int maxEventsPerReport) {
this.maxEventsPerSource = maxEventsPerSource;
this.maxEventsPerReport = maxEventsPerReport;
}
/**
* Adds the given <code>event</code> to this report. If this report is too full,
* the event will be silently rejected (i.e. an exception will not be thrown, but the
* event will not be sent to the server).
*
* @param event the {@link Event} to be added
* @param eventSource the source of the Event to be added
*/
public void addEvent(@NotNull Event event, @NotNull EventSource eventSource) {
if (this.totalEventsInReport < this.maxEventsPerReport) {
Set<Event> eventSet = getEventsForEventSource(eventSource);
if (eventSet.size() < this.maxEventsPerSource) {
eventSet.add(event);
this.totalEventsInReport++;
} else {
// this event source has maxed out its allowed number of events for this report
droppedEvent(eventSource);
}
} else {
// this event report has maxed out its total allowed number of events.
droppedEvent(eventSource);
}
return;
}
private void droppedEvent(EventSource eventSource) {
if (this.eventsDropped == null) {
this.eventsDropped = new HashMap<EventSource, Integer>();
}
Integer droppedCount = this.eventsDropped.get(eventSource);
if (droppedCount == null) {
droppedCount = Integer.valueOf(1);
} else {
droppedCount = Integer.valueOf(droppedCount.intValue() + 1);
}
this.eventsDropped.put(eventSource, droppedCount);
return;
}
private Set<Event> getEventsForEventSource(EventSource eventSource) {
Set<Event> eventSet = this.events.get(eventSource);
if (eventSet == null) {
eventSet = new LinkedHashSet<Event>();
this.events.put(eventSource, eventSet);
}
return eventSet;
}
/**
* Returns the Events contained in this report; the Events are in a map keyed off Event sources.
*
* @return the Events contained in this report
*/
@NotNull
public Map<EventSource, Set<Event>> getEvents() {
return this.events;
}
/**
* This method will check to see if any maximum limits were exceeded and if so adds
* warning events to indicate that the limits were breached.
*
* The plugin container should call this method before it wants to send the report
* over the wire to the server side. This method should be called whenever you are done
* adding events to the report and you want to then process the events.
*
* This method will only add warning events once. If this method adds at least one
* warning event to the report, further calls to this method will be a no-op
* (i.e. the method will do nothing and return immediately). Therefore, it is recommended
* you call this method only after the report is done and you are ready to send the
* report to the server for further processing.
*
* @return true if some events in teh current report were dropped due to limits
*/
public boolean addLimitWarningEvents() {
if (this.addedLimitWarningEvents || this.eventsDropped == null) {
return false; // we already added them or there is nothing to add
}
Event warningEvent;
String warningMessage;
long now = System.currentTimeMillis();
for (Map.Entry<EventSource, Integer> entry : this.eventsDropped.entrySet()) {
EventSource eventSource = entry.getKey();
Integer droppedCount = entry.getValue();
Set<Event> eventSet = getEventsForEventSource(eventSource);
if (eventSet.size() >= this.maxEventsPerSource) {
// this event source hit its individual limit
warningMessage = "Event Report Limit Reached: reached the maximum allowed events ["
+ this.maxEventsPerSource + "] for this event source - dropped [" + droppedCount + "] events";
} else {
// this report reached its total limit, even though this event source did not hit its individual limit
warningMessage = "Event Report Limit Reached: reached total maximum allowed events ["
+ this.maxEventsPerReport + "] - dropped [" + droppedCount + "] events";
}
warningEvent = new Event(eventSource.getEventDefinition().getName(), eventSource.getLocation(), now,
EventSeverity.WARN, warningMessage, eventSource);
eventSet.add(warningEvent);
this.addedLimitWarningEvents = true;
}
return this.eventsDropped.size() > 0;
}
public Map<EventSource, Integer> getDroppedEvents() {
return Collections.unmodifiableMap(eventsDropped);
}
public int getMaxEventsPerSource() {
return this.maxEventsPerSource;
}
public int getMaxEventsPerReport() {
return this.maxEventsPerReport;
}
@Override
public String toString() {
return this.getClass().getName().substring(this.getClass().getName().lastIndexOf(".") + 1) + "[" + this.events
+ "]";
}
}