001/*
002 * (C) Copyright 2018 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     bdelbosc
018 */
019package org.nuxeo.lib.stream.log;
020
021import static java.lang.Math.max;
022
023import java.io.IOException;
024import java.util.List;
025import java.util.Objects;
026import java.util.stream.Collectors;
027
028import com.fasterxml.jackson.databind.JsonNode;
029import com.fasterxml.jackson.databind.ObjectMapper;
030
031/**
032 * Extends LogLag with lower and upper timestamps to express lag as a latency.
033 *
034 * @since 10.1
035 */
036public class Latency {
037    protected final LogLag lag;
038
039    protected final long lower;
040
041    protected final long upper;
042
043    protected final String key;
044
045    public Latency(long lower, long upper, LogLag lag, String key) {
046        Objects.requireNonNull(lag);
047        this.lower = lower;
048        this.upper = upper;
049        this.lag = lag;
050        this.key = key;
051    }
052
053    public static Latency noLatency(long upper, LogLag lag) {
054        Objects.requireNonNull(lag);
055        if (lag.lag() != 0) {
056            throw new IllegalArgumentException("Lag found: " + lag);
057        }
058        return new Latency(0, upper, lag, null);
059    }
060
061    public static Latency fromJson(String json) {
062        ObjectMapper mapper = new ObjectMapper();
063        try {
064            JsonNode obj = mapper.readTree(json);
065            long lower = obj.get("low").asLong();
066            long upper = obj.get("up").asLong();
067            long lag = obj.get("lag").asLong();
068            String key = obj.get("key") == null ? null : obj.get("key").asText();
069            return new Latency(lower, upper, LogLag.of(lag), key);
070        } catch (IOException e) {
071            throw new IllegalArgumentException("Invalid json: " + json, e);
072        }
073    }
074
075    public static Latency of(List<Latency> latencies) {
076        LogLag lag = LogLag.of(latencies.stream().map(Latency::lag).collect(Collectors.toList()));
077        final long[] start = { Long.MAX_VALUE };
078        final long[] end = { 0 };
079        final String[] key = { "" };
080        latencies.forEach(item -> {
081            if (item.lower > 0 && item.lower < start[0]) {
082                start[0] = item.lower;
083                key[0] = item.key;
084            }
085            end[0] = max(end[0], item.upper);
086        });
087        return new Latency(start[0] == Long.MAX_VALUE ? 0 : start[0], end[0], lag, key[0]);
088    }
089
090    /**
091     * Returns the latency expressed in millisecond.
092     */
093    public long latency() {
094        return lag.lag() > 0 ? upper - lower : 0;
095    }
096
097    /**
098     * Returns the lower timestamp.
099     */
100    public long lower() {
101        return lower;
102    }
103
104    /**
105     * Returns the upper timestamp.
106     */
107    public long upper() {
108        return upper;
109    }
110
111    public LogLag lag() {
112        return lag;
113    }
114
115    /**
116     * Returns the key associated with the lower timestamp.
117     */
118    public String key() {
119        return key;
120    }
121
122    @Override
123    public String toString() {
124        return "Latency{" + "lat=" + latency() + ", lower=" + lower + ", upper=" + upper + ", key=" + key + ", lag="
125                + lag + '}';
126    }
127
128    public String asJson() {
129        return String.format("{\"lat\":\"%s\",\"low\":\"%s\",\"up\":\"%s\",\"lag\":\"%s\"%s}", latency(), lower, upper,
130                lag.lag, key == null ? "" : ",\"key\":\"" + key + "\"");
131    }
132}