/* * Copyright 2016 KairosDB 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.kairosdb.util; import com.google.common.collect.ImmutableList; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.kairosdb.core.datastore.Sampling; import java.io.*; import java.net.*; import java.util.Collections; import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; public class Util { /** Special thanks to Nadeau software consulting for publishing this code. http://nadeausoftware.com/node/97 @param s string representation of number to parse @return number */ public static long parseLong( final CharSequence s ) { if ( s == null ) throw new NumberFormatException( "Null string" ); // Check for a sign. long num = 0; long sign = -1; final int len = s.length( ); final char ch = s.charAt( 0 ); if ( ch == '-' ) { if ( len == 1 ) throw new NumberFormatException( "Missing digits: " + s ); sign = 1; } else { final int d = ch - '0'; if ( d < 0 || d > 9 ) throw new NumberFormatException( "Malformed: " + s ); num = -d; } // Build the number. final long max = (sign == -1L) ? -Long.MAX_VALUE : Long.MIN_VALUE; final long multmax = max / 10; int i = 1; while ( i < len ) { long d = s.charAt(i++) - '0'; if ( d < 0L || d > 9L ) throw new NumberFormatException( "Malformed: " + s ); if ( num < multmax ) throw new NumberFormatException( "Over/underflow: " + s ); num *= 10; if ( num < (max+d) ) throw new NumberFormatException( "Over/underflow: " + s ); num -= d; } return sign * num; } /** * Returns the host name. First tries to execute "hostname" on the machine. This should work for Linux, Windows, * and Mac. If, for some reason hostname fails, then get the name from InetAddress (which might change depending * on network setup) * * @return hostname */ public static String getHostName() { try { Runtime run = Runtime.getRuntime(); Process process = run.exec("hostname"); try(BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()))) { // Need to read all lines from the stream or the process could hang StringBuilder buffer = new StringBuilder(); String line; while ((line = br.readLine()) != null) buffer.append(line); int returnValue = process.waitFor(); if (returnValue == 0) return buffer.toString(); } } catch (Exception e) { // ignore } try { return InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { return ""; } } public static void packUnsignedLong(long value, DataOutput buffer) throws IOException { /* Encodes a value using the variable-length encoding from <a href="http://code.google.com/apis/protocolbuffers/docs/encoding.html"> Google Protocol Buffers</a>. Zig-zag is not used, so input must not be negative. If values can be negative, use {@link #writeSignedVarLong(long, DataOutput)} instead. This method treats negative input as like a large unsigned value. */ while ((value & ~0x7FL) != 0L) { buffer.writeByte((int) ((value & 0x7F) | 0x80)); value >>>= 7; } buffer.writeByte((int) value); } public static long unpackUnsignedLong(DataInput buffer) throws IOException { int shift = 0; long result = 0; while (shift < 64) { final byte b = buffer.readByte(); result |= (long)(b & 0x7F) << shift; if ((b & 0x80) == 0) { return result; } shift += 7; } throw new IllegalArgumentException("Variable length quantity is too long"); } public static void packLong(long value, DataOutput buffer) throws IOException { // Great trick from http://code.google.com/apis/protocolbuffers/docs/encoding.html#types packUnsignedLong((value << 1) ^ (value >> 63), buffer); } public static long unpackLong(DataInput buffer) throws IOException { long value = unpackUnsignedLong(buffer); return ((value >>> 1) ^ -(value & 1)); } public static InetAddress findPublicIp() { // Check if local host address is a good v4 address InetAddress localAddress = null; try { localAddress = InetAddress.getLocalHost(); if (isGoodV4Address(localAddress)) { return localAddress; } } catch (UnknownHostException ignored) { } if (localAddress == null) { try { localAddress = InetAddress.getByAddress(new byte[]{127, 0, 0, 1}); } catch (UnknownHostException e) { throw new AssertionError("Could not get local ip address"); } } // check all up network interfaces for a good v4 address for (NetworkInterface networkInterface : getGoodNetworkInterfaces()) { for (InetAddress address : Collections.list(networkInterface.getInetAddresses())) { if (isGoodV4Address(address)) { return address; } } } // check all up network interfaces for a good v6 address for (NetworkInterface networkInterface : getGoodNetworkInterfaces()) { for (InetAddress address : Collections.list(networkInterface.getInetAddresses())) { if (isGoodV6Address(address)) { return address; } } } // just return the local host address // it is most likely that this is a disconnected developer machine return localAddress; } /** Returns true if the string contains a number. This means it contains only digits, the minus sign, plus sign and a period. @param s string to test @return true if only contains a number value */ public static boolean isNumber(String s) { checkNotNull(s); if (s.isEmpty()) return false; int start = 0; char firstChar = s.charAt(0); if (firstChar == '+' || firstChar == '-' || firstChar == '.') { start = 1; if (s.length() == 1) return false; } for (int i = start; i < s.length(); i++) { char c = s.charAt(i); if (!Character.isDigit(c) && c != '.') return false; } //noinspection RedundantIfStatement if (s.charAt(s.length() - 1) == '.') return false; // can't have trailing period return true; } private static List<NetworkInterface> getGoodNetworkInterfaces() { ImmutableList.Builder<NetworkInterface> builder = ImmutableList.builder(); try { for (NetworkInterface networkInterface : Collections.list(NetworkInterface.getNetworkInterfaces())) { try { if (!networkInterface.isLoopback() && networkInterface.isUp()) { builder.add(networkInterface); } } catch (Exception ignored) { } } } catch (SocketException ignored) { } return builder.build(); } private static boolean isGoodV4Address(InetAddress address) { return address instanceof Inet4Address && !address.isAnyLocalAddress() && !address.isLoopbackAddress() && !address.isMulticastAddress(); } private static boolean isGoodV6Address(InetAddress address) { return address instanceof Inet6Address && !address.isAnyLocalAddress() && !address.isLoopbackAddress() && !address.isMulticastAddress(); } /** Computes the duration of the sampling (value * unit) starting at timestamp. @param timestamp unix timestamp of the start time. @return the duration of the sampling in millisecond. */ public static long getSamplingDuration(long timestamp, Sampling sampling, DateTimeZone timeZone) { long ret = sampling.getValue(); DateTime dt = new DateTime(timestamp, timeZone); switch (sampling.getUnit()) { case YEARS: ret = new org.joda.time.Duration(dt, dt.plusYears((int) sampling.getValue())).getMillis(); break; case MONTHS: ret = new org.joda.time.Duration(dt, dt.plusMonths((int) sampling.getValue())).getMillis(); break; case WEEKS: ret = new org.joda.time.Duration(dt, dt.plusWeeks((int) sampling.getValue())).getMillis(); break; case DAYS: ret = new org.joda.time.Duration(dt, dt.plusDays((int) sampling.getValue())).getMillis(); break; case HOURS: ret = new org.joda.time.Duration(dt, dt.plusHours((int) sampling.getValue())).getMillis(); break; case MINUTES: ret = new org.joda.time.Duration(dt, dt.plusMinutes((int) sampling.getValue())).getMillis(); break; case SECONDS: ret = new org.joda.time.Duration(dt, dt.plusSeconds((int) sampling.getValue())).getMillis(); break; case MILLISECONDS: ret = sampling.getValue(); break; } return ret; } }