/** * Copyright 2015-2017 The OpenZipkin 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 zipkin.internal; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.TimeZone; import java.util.concurrent.TimeUnit; public final class Util { public static final Charset UTF_8 = Charset.forName("UTF-8"); static final TimeZone UTC = TimeZone.getTimeZone("UTC"); public static int envOr(String key, int fallback) { return System.getenv(key) != null ? Integer.parseInt(System.getenv(key)) : fallback; } public static String envOr(String key, String fallback) { return System.getenv(key) != null ? System.getenv(key) : fallback; } public static boolean equal(Object a, Object b) { return a == b || (a != null && a.equals(b)); } /** * Copy of {@code com.google.common.base.Preconditions#checkArgument}. */ public static void checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { if (!expression) { throw new IllegalArgumentException(String.format(errorMessageTemplate, errorMessageArgs)); } } /** * Copy of {@code com.google.common.base.Preconditions#checkNotNull}. */ public static <T> T checkNotNull(T reference, String errorMessage) { if (reference == null) { // If either of these parameters is null, the right thing happens anyway throw new NullPointerException(errorMessage); } return reference; } public static <T extends Comparable<? super T>> List<T> sortedList(@Nullable Collection<T> in) { if (in == null || in.isEmpty()) return Collections.emptyList(); if (in.size() == 1) return Collections.singletonList(in.iterator().next()); Object[] array = in.toArray(); Arrays.sort(array); List result = Arrays.asList(array); return Collections.unmodifiableList(result); } /** For bucketed data floored to the day. For example, dependency links. */ public static long midnightUTC(long epochMillis) { Calendar day = Calendar.getInstance(UTC); day.setTimeInMillis(epochMillis); day.set(Calendar.MILLISECOND, 0); day.set(Calendar.SECOND, 0); day.set(Calendar.MINUTE, 0); day.set(Calendar.HOUR_OF_DAY, 0); return day.getTimeInMillis(); } public static List<Date> getDays(long endTs, @Nullable Long lookback) { long to = midnightUTC(endTs); long startMillis = endTs - (lookback != null ? lookback : endTs); long from = startMillis <= 0 ? 0 : midnightUTC(startMillis); // >= 1970 List<Date> days = new ArrayList<>(); for (long time = from; time <= to; time += TimeUnit.DAYS.toMillis(1)) { days.add(new Date(time)); } return days; } /** * Parses a 1 to 32 character lower-hex string with no prefix into an unsigned long, tossing any * bits higher than 64. */ public static long lowerHexToUnsignedLong(String lowerHex) { int length = lowerHex.length(); if (length < 1 || length > 32) throw isntLowerHexLong(lowerHex); // trim off any high bits int beginIndex = length > 16 ? length - 16 : 0; return lowerHexToUnsignedLong(lowerHex, beginIndex); } /** * Parses a 16 character lower-hex string with no prefix into an unsigned long, starting at the * spe index. */ public static long lowerHexToUnsignedLong(String lowerHex, int index) { long result = 0; for (int endIndex = Math.min(index + 16, lowerHex.length()); index < endIndex; index++) { char c = lowerHex.charAt(index); result <<= 4; if (c >= '0' && c <= '9') { result |= c - '0'; } else if (c >= 'a' && c <= 'f') { result |= c - 'a' + 10; } else { throw isntLowerHexLong(lowerHex); } } return result; } static NumberFormatException isntLowerHexLong(String lowerHex) { throw new NumberFormatException( lowerHex + " should be a 1 to 32 character lower-hex string with no prefix"); } /** Returns 16 or 32 character hex string depending on if {@code high} is zero. */ public static String toLowerHex(long high, long low) { char[] result = new char[high != 0 ? 32 : 16]; int pos = 0; if (high != 0) { writeHexLong(result, pos, high); pos += 16; } writeHexLong(result, pos, low); return new String(result); } /** Inspired by {@code okio.Buffer.writeLong} */ public static String toLowerHex(long v) { char[] data = new char[16]; writeHexLong(data, 0, v); return new String(data); } /** Inspired by {@code okio.Buffer.writeLong} */ public static void writeHexLong(char[] data, int pos, long v) { writeHexByte(data, pos + 0, (byte) ((v >>> 56L) & 0xff)); writeHexByte(data, pos + 2, (byte) ((v >>> 48L) & 0xff)); writeHexByte(data, pos + 4, (byte) ((v >>> 40L) & 0xff)); writeHexByte(data, pos + 6, (byte) ((v >>> 32L) & 0xff)); writeHexByte(data, pos + 8, (byte) ((v >>> 24L) & 0xff)); writeHexByte(data, pos + 10, (byte) ((v >>> 16L) & 0xff)); writeHexByte(data, pos + 12, (byte) ((v >>> 8L) & 0xff)); writeHexByte(data, pos + 14, (byte) (v & 0xff)); } // Taken from RxJava throwIfFatal, which was taken from scala public static void propagateIfFatal(Throwable t) { if (t instanceof VirtualMachineError) { throw (VirtualMachineError) t; } else if (t instanceof ThreadDeath) { throw (ThreadDeath) t; } else if (t instanceof LinkageError) { throw (LinkageError) t; } } static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; static void writeHexByte(char[] data, int pos, byte b) { data[pos + 0] = HEX_DIGITS[(b >> 4) & 0xf]; data[pos + 1] = HEX_DIGITS[b & 0xf]; } // throwable ctor not present in JRE 6 static AssertionError assertionError(String message, Throwable cause) { AssertionError error = new AssertionError(message); error.initCause(cause); throw error; } private Util() { } }