blob: 02fffb753fc25c349a8888da42ccd8778da69df5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.zookeeper;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;
import org.apache.zookeeper.common.StringUtils;
/**
* a class that represents the stats associated with quotas
*/
public class StatsTrack {
private static final String countStr = "count";
private static final String countHardLimitStr = "countHardLimit";
private static final String byteStr = "bytes";
private static final String byteHardLimitStr = "byteHardLimit";
private final Map<String, Long> stats = new HashMap<>();
private static final Pattern PAIRS_SEPARATOR = Pattern.compile("[,;]+");
/**
* a default constructor for
* stats
*/
public StatsTrack() {
this("");
}
/**
*
* @param stat the byte[] stat to be initialized with
*/
public StatsTrack(byte[] stat) {
this(new String(stat, StandardCharsets.UTF_8));
}
/**
* the stat string should be of the form key1str=long,key2str=long,..
* where either , or ; are valid separators
* uninitialized values are returned as -1
* @param stat the stat string to be initialized with
*/
public StatsTrack(String stat) {
this.stats.clear();
if (stat == null || stat.length() == 0) {
return;
}
String[] keyValuePairs = PAIRS_SEPARATOR.split(stat);
for (String keyValuePair : keyValuePairs) {
String[] kv = keyValuePair.split("=");
this.stats.put(kv[0], Long.parseLong(StringUtils.isEmpty(kv[1]) ? "-1" : kv[1]));
}
}
/**
* get the count of nodes allowed as part of quota
*
* @return the count as part of this string
*/
public long getCount() {
return getValue(countStr);
}
/**
* set the count for this stat tracker.
*
* @param count
* the count to set with
*/
public void setCount(long count) {
setValue(countStr, count);
}
/**
* get the count of nodes allowed as part of quota (hard limit)
*
* @return the count as part of this string
*/
public long getCountHardLimit() {
return getValue(countHardLimitStr);
}
/**
* set the count hard limit
*
* @param count the count limit to set
*/
public void setCountHardLimit(long count) {
setValue(countHardLimitStr, count);
}
/**
* get the count of bytes allowed as part of quota
*
* @return the bytes as part of this string
*/
public long getBytes() {
return getValue(byteStr);
}
/**
* set the bytes for this stat tracker.
*
* @param bytes
* the bytes to set with
*/
public void setBytes(long bytes) {
setValue(byteStr, bytes);
}
/**
* get the count of bytes allowed as part of quota (hard limit)
*
* @return the bytes as part of this string
*/
public long getByteHardLimit() {
return getValue(byteHardLimitStr);
}
/**
* set the byte hard limit
*
* @param bytes the byte limit to set
*/
public void setByteHardLimit(long bytes) {
setValue(byteHardLimitStr, bytes);
}
/**
* get helper to lookup a given key
*
* @param key the key to lookup
* @return key's value or -1 if it doesn't exist
*/
private long getValue(String key) {
Long val = this.stats.get(key);
return val == null ? -1 : val.longValue();
}
/**
* set helper to set the value for the specified key
*
* @param key the key to set
* @param value the value to set
*/
private void setValue(String key, long value) {
this.stats.put(key, value);
}
/*
* returns the string that maps to this stat tracking.
*
* Builds a string of the form
* "count=4,bytes=5=;countHardLimit=10;byteHardLimit=10"
*
* This string is slightly hacky to preserve compatibility with 3.4.3 and
* older parser. In particular, count must be first, bytes must be second,
* all new fields must use a separator that is not a "," (so, ";"), and the
* seemingly spurious "=" after the bytes field is essential to allowing
* it to be parseable by the old parsing code.
*/
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
ArrayList<String> keys = new ArrayList<>(stats.keySet());
// Special handling for count=,byte= to enforce them coming first
// for backwards compatibility
keys.remove(countStr);
keys.remove(byteStr);
buf.append(countStr);
buf.append("=");
buf.append(getCount());
buf.append(",");
buf.append(byteStr);
buf.append("=");
buf.append(getBytes());
if (!keys.isEmpty()) {
// Add extra = to trick old parsing code so it will ignore new flags
buf.append("=");
Collections.sort(keys);
for (String key : keys) {
buf.append(";");
buf.append(key);
buf.append("=");
buf.append(stats.get(key));
}
}
return buf.toString();
}
public byte[] getStatsBytes() {
return toString().getBytes(StandardCharsets.UTF_8);
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final StatsTrack that = (StatsTrack) o;
return Objects.equals(stats, that.stats);
}
@Override
public int hashCode() {
return Objects.hash(stats);
}
}