/*
* Copyright 2016 the original author or authors.
*
* 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 org.glowroot.common.model;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.primitives.Doubles;
import org.glowroot.wire.api.model.AggregateOuterClass.Aggregate;
public class ServiceCallCollector {
private final Map<String, Map<String, MutableServiceCall>> serviceCalls = Maps.newHashMap();
private final int limit;
private final int maxMultiplierWhileBuilding;
// this is only used by UI
private long lastCaptureTime;
public ServiceCallCollector(int limit, int maxMultiplierWhileBuilding) {
this.limit = limit;
this.maxMultiplierWhileBuilding = maxMultiplierWhileBuilding;
}
public void updateLastCaptureTime(long captureTime) {
lastCaptureTime = Math.max(lastCaptureTime, captureTime);
}
public long getLastCaptureTime() {
return lastCaptureTime;
}
public List<Aggregate.ServiceCallsByType> toProto() {
if (serviceCalls.isEmpty()) {
return ImmutableList.of();
}
List<Aggregate.ServiceCallsByType> serviceCallsByType = Lists.newArrayList();
for (Entry<String, Map<String, MutableServiceCall>> entry : serviceCalls.entrySet()) {
List<Aggregate.ServiceCall> serviceCalls =
Lists.newArrayListWithCapacity(entry.getValue().values().size());
for (MutableServiceCall serviceCall : entry.getValue().values()) {
serviceCalls.add(serviceCall.toProto());
}
if (serviceCalls.size() > limit) {
order(serviceCalls);
serviceCalls = serviceCalls.subList(0, limit);
}
serviceCallsByType.add(Aggregate.ServiceCallsByType.newBuilder()
.setType(entry.getKey())
.addAllServiceCall(serviceCalls)
.build());
}
return serviceCallsByType;
}
public void mergeServiceCalls(List<Aggregate.ServiceCallsByType> toBeMergedServiceCalls)
throws IOException {
for (Aggregate.ServiceCallsByType toBeMergedServiceCallsByType : toBeMergedServiceCalls) {
String type = toBeMergedServiceCallsByType.getType();
Map<String, MutableServiceCall> serviceCallsForType = serviceCalls.get(type);
if (serviceCallsForType == null) {
serviceCallsForType = Maps.newHashMap();
serviceCalls.put(type, serviceCallsForType);
}
for (Aggregate.ServiceCall serviceCall : toBeMergedServiceCallsByType
.getServiceCallList()) {
mergeServiceCall(serviceCall, serviceCallsForType);
}
}
}
public void mergeServiceCall(String type, String text, double totalDurationNanos,
long executionCount) {
Map<String, MutableServiceCall> serviceCallsForType = serviceCalls.get(type);
if (serviceCallsForType == null) {
serviceCallsForType = Maps.newHashMap();
serviceCalls.put(type, serviceCallsForType);
}
mergeServiceCall(text, totalDurationNanos, executionCount, serviceCallsForType);
}
private void mergeServiceCall(Aggregate.ServiceCall serviceCall,
Map<String, MutableServiceCall> serviceCallsForType) {
MutableServiceCall aggregateServiceCall = serviceCallsForType.get(serviceCall.getText());
if (aggregateServiceCall == null) {
if (maxMultiplierWhileBuilding != 0
&& serviceCallsForType.size() >= limit * maxMultiplierWhileBuilding) {
return;
}
aggregateServiceCall = new MutableServiceCall(serviceCall.getText());
serviceCallsForType.put(serviceCall.getText(), aggregateServiceCall);
}
aggregateServiceCall.addToTotalDurationNanos(serviceCall.getTotalDurationNanos());
aggregateServiceCall.addToExecutionCount(serviceCall.getExecutionCount());
}
private void mergeServiceCall(String text, double totalDurationNanos, long executionCount,
Map<String, MutableServiceCall> serviceCallsForType) {
MutableServiceCall aggregateServiceCall = serviceCallsForType.get(text);
if (aggregateServiceCall == null) {
if (maxMultiplierWhileBuilding != 0
&& serviceCallsForType.size() >= limit * maxMultiplierWhileBuilding) {
return;
}
aggregateServiceCall = new MutableServiceCall(text);
serviceCallsForType.put(text, aggregateServiceCall);
}
aggregateServiceCall.addToTotalDurationNanos(totalDurationNanos);
aggregateServiceCall.addToExecutionCount(executionCount);
}
private void order(List<Aggregate.ServiceCall> serviceCalls) {
// reverse sort by total
Collections.sort(serviceCalls, new Comparator<Aggregate.ServiceCall>() {
@Override
public int compare(Aggregate.ServiceCall left, Aggregate.ServiceCall right) {
return Doubles.compare(right.getTotalDurationNanos(), left.getTotalDurationNanos());
}
});
}
}