/*
* Copyright 2017 NAVER Corp.
*
* 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 com.navercorp.pinpoint.web.vo.timeline.inspector;
import com.navercorp.pinpoint.web.filter.agent.AgentEventFilter;
import com.navercorp.pinpoint.web.vo.AgentEvent;
import com.navercorp.pinpoint.web.vo.Range;
import org.springframework.util.Assert;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* @author HyunGil Jeong
*/
public class AgentEventTimelineBuilder {
private static final int DEFAULT_NUM_TIMESLOTS = 50;
private final long timelineStartTimestamp;
private final long timelineEndTimestamp;
private final long timeslotSize;
private final int numTimeslots;
private List<AgentEvent> agentEvents = Collections.emptyList();
private List<AgentEventFilter> filters = new ArrayList<>();
public AgentEventTimelineBuilder(Range range) {
this(range, DEFAULT_NUM_TIMESLOTS);
}
public AgentEventTimelineBuilder(Range range, int numTimeslots) {
Assert.notNull(range, "range must not be null");
Assert.isTrue(range.getRange() > 0, "timeline must have range greater than 0");
Assert.isTrue(numTimeslots > 0, "numTimeslots must be greater than 0");
this.timelineStartTimestamp = range.getFrom();
this.timelineEndTimestamp = range.getTo();
int adjustedNumTimeslots = numTimeslots;
if (range.getRange() < adjustedNumTimeslots) {
adjustedNumTimeslots = (int) range.getRange();
}
this.timeslotSize = (timelineEndTimestamp - timelineStartTimestamp) / adjustedNumTimeslots;
this.numTimeslots = adjustedNumTimeslots;
}
public AgentEventTimelineBuilder from(List<AgentEvent> agentEvents) {
if (agentEvents != null) {
this.agentEvents = agentEvents;
}
return this;
}
public AgentEventTimelineBuilder addFilter(AgentEventFilter filter) {
if (filter != null) {
filters.add(filter);
}
return this;
}
public AgentEventTimeline build() {
List<AgentEvent> filteredAgentEvents = new ArrayList<>();
for (AgentEvent agentEvent : agentEvents) {
if (filterAgentEvent(agentEvent) == AgentEventFilter.ACCEPT) {
filteredAgentEvents.add(agentEvent);
}
}
return new AgentEventTimeline(createTimelineSegments(filteredAgentEvents));
}
private boolean filterAgentEvent(AgentEvent agentEvent) {
for (AgentEventFilter filter : filters) {
if (!filter.accept(agentEvent)) {
return AgentEventFilter.REJECT;
}
}
return AgentEventFilter.ACCEPT;
}
private List<AgentEventTimelineSegment> createTimelineSegments(List<AgentEvent> agentEvents) {
if (agentEvents.isEmpty()) {
return Collections.emptyList();
}
Map<Long, List<AgentEvent>> timeWindowEventsMap = createTimeslotIndexMap(agentEvents);
List<AgentEventTimelineSegment> timelineSegments = new ArrayList<>();
for (Map.Entry<Long, List<AgentEvent>> e : timeWindowEventsMap.entrySet()) {
AgentEventTimelineSegment segment = createSegment(e.getKey(), e.getValue());
if (segment != null) {
timelineSegments.add(segment);
}
}
return timelineSegments;
}
private Map<Long, List<AgentEvent>> createTimeslotIndexMap(List<AgentEvent> agentEvents) {
Map<Long, List<AgentEvent>> timeslotIndexMap = new TreeMap<>();
for (AgentEvent agentEvent : agentEvents) {
long timeslotIndex = getTimeslotIndex(agentEvent.getEventTimestamp());
List<AgentEvent> timeslotAgentEvents = timeslotIndexMap.get(timeslotIndex);
if (timeslotAgentEvents == null) {
timeslotAgentEvents = new ArrayList<>();
timeslotIndexMap.put(timeslotIndex, timeslotAgentEvents);
}
timeslotAgentEvents.add(agentEvent);
}
return timeslotIndexMap;
}
private AgentEventTimelineSegment createSegment(long timeslotIndex, List<AgentEvent> agentEvents) {
// timeslotIndex guaranteed to be greater than 0 and less than numTimeslots
long segmentStartTimestamp = timelineStartTimestamp + (timeslotIndex * timeslotSize);
long segmentEndTimestamp = segmentStartTimestamp + timeslotSize;
if (timeslotIndex >= (numTimeslots - 1)) {
segmentEndTimestamp = timelineEndTimestamp;
}
AgentEventMarker agentEventMarker = createAgentEventMarker(agentEvents);
if (agentEventMarker.getTotalCount() == 0) {
return null;
}
AgentEventTimelineSegment segment = new AgentEventTimelineSegment();
segment.setStartTimestamp(segmentStartTimestamp);
segment.setEndTimestamp(segmentEndTimestamp);
segment.setValue(agentEventMarker);
return segment;
}
private long getTimeslotIndex(long timestamp) {
long diff = timestamp - timelineStartTimestamp;
long index = diff / timeslotSize;
if (index < 0) {
index = 0;
}
if (index >= numTimeslots) {
index = numTimeslots - 1;
}
return index;
}
private AgentEventMarker createAgentEventMarker(List<AgentEvent> agentEvents) {
AgentEventMarker agentEventMarker = new AgentEventMarker();
for (AgentEvent agentEvent : agentEvents) {
agentEventMarker.addAgentEvent(agentEvent);
}
return agentEventMarker;
}
}