All Downloads are FREE. Search and download functionalities are using the official Maven repository.

co.cask.cdap.common.app.RunIds Maven / Gradle / Ivy

There is a newer version: 5.1.2
Show newest version
/*
 * Copyright © 2015 Cask Data, Inc.
 *
 * 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 co.cask.cdap.common.app;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
import com.google.common.primitives.Longs;
import org.apache.twill.api.RunId;

import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Generates an unique ID for a running program using type 1 and variant 2 time-based {@link UUID}.
 * This implements time-based UUID generation algorithm described in
 * A Universally Unique IDentifier (UUID) URN Namespace
 * with the following modifications:
 * 
    *
  • It does not share state with other instances of time-based UUID generators on a given machine. So it is * recommended not to run more than one instance of this on a machine to guarantee uniqueness of UUIDs generated.
  • *
  • The timestamp embedded in the UUID is only valid up to millisecond precision. This is because this * implementation uses a counter value in the remaining bits to ensure unique UUIDs are generated when invoked * multiple times at the same millisecond (up to 10000 times).
  • *
*/ public final class RunIds { private static final Random RANDOM = new Random(); // Number of 100ns intervals since 15 October 1582 00:00:000000000 till UNIX epoch private static final long NUM_100NS_INTERVALS_SINCE_UUID_EPOCH = 0x01b21dd213814000L; // Multiplier to convert millisecond into 100ns private static final long HUNDRED_NANO_MULTIPLIER = 10000; private static final AtomicLong COUNTER = new AtomicLong(); /** * @return UUID based on current time. If called repeatedly within the same millisecond, this is * guaranteed to generate at least 10000 unique UUIDs for the millisecond. */ public static RunId generate() { return new RunIdImpl(generateUUIDForTime(System.currentTimeMillis())); } /** * Converts string representation of run id into {@link RunId}l */ public static RunId fromString(String id) { return new RunIdImpl(UUID.fromString(id)); } /** * Returns time-based UUID for given time. This method is recommended to be used only for testing. * @param timeMillis time since epoch * @return time-based UUID. */ @VisibleForTesting public static RunId generate(long timeMillis) { return new RunIdImpl(generateUUIDForTime(timeMillis)); } /** * @return time from the UUID if it is a time-based UUID, -1 otherwise. */ public static long getTime(RunId runId, TimeUnit timeUnit) { UUID uuid = UUID.fromString(runId.getId()); if (uuid.version() == 1 && uuid.variant() == 2) { long timeInMilliseconds = (uuid.timestamp() - NUM_100NS_INTERVALS_SINCE_UUID_EPOCH) / HUNDRED_NANO_MULTIPLIER; return timeUnit.convert(timeInMilliseconds, TimeUnit.MILLISECONDS); } return -1; } @VisibleForTesting static UUID generateUUIDForTime(long timeInMillis) { // Use system time in milliseconds to generate time in 100ns. // Use COUNTER to ensure unique time gets generated for the same millisecond (up to HUNDRED_NANO_MULTIPLIER) // Hence the time is valid only for millisecond precision, event though it represents 100ns precision. long ts = timeInMillis * HUNDRED_NANO_MULTIPLIER + COUNTER.incrementAndGet() % HUNDRED_NANO_MULTIPLIER; long time = ts + NUM_100NS_INTERVALS_SINCE_UUID_EPOCH; long timeLow = time & 0xffffffffL; long timeMid = time & 0xffff00000000L; long timeHi = time & 0xfff000000000000L; long upperLong = (timeLow << 32) | (timeMid >> 16) | (1 << 12) | (timeHi >> 48); // Random clock ID int clockId = RANDOM.nextInt() & 0x3FFF; long nodeId; try { Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); NetworkInterface networkInterface = null; while (interfaces.hasMoreElements()) { networkInterface = interfaces.nextElement(); if (!networkInterface.isLoopback()) { break; } } byte[] mac = networkInterface == null ? null : networkInterface.getHardwareAddress(); if (mac == null) { nodeId = (RANDOM.nextLong() & 0xFFFFFFL) | 0x100000L; } else { nodeId = Longs.fromBytes((byte) 0, (byte) 0, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } } catch (SocketException e) { // Generate random node ID nodeId = RANDOM.nextLong() & 0xFFFFFFL | 0x100000L; } long lowerLong = ((long) clockId | 0x8000) << 48 | nodeId; return new java.util.UUID(upperLong, lowerLong); } private static class RunIdImpl implements RunId { private final UUID id; public RunIdImpl(UUID id) { this.id = id; } @Override public String getId() { return id.toString(); } @Override public String toString() { return id.toString(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } RunIdImpl that = (RunIdImpl) o; return Objects.equal(this.id, that.id); } @Override public int hashCode() { return id.hashCode(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy