001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.util;
018
019import java.net.InetAddress;
020import java.net.NetworkInterface;
021import java.net.SocketException;
022import java.net.UnknownHostException;
023import java.nio.ByteBuffer;
024import java.security.SecureRandom;
025import java.util.Enumeration;
026import java.util.Random;
027import java.util.UUID;
028import java.util.concurrent.atomic.AtomicInteger;
029
030import org.apache.logging.log4j.Logger;
031import org.apache.logging.log4j.status.StatusLogger;
032import org.apache.logging.log4j.util.PropertiesUtil;
033
034/**
035 * Generates a unique ID. The generated UUID will be unique for approximately 8,925 years so long as
036 * less than 10,000 IDs are generated per millisecond on the same device (as identified by its MAC address).
037 */
038public final class UuidUtil {
039    /**
040     * System property that may be used to seed the UUID generation with an integer value.
041     */
042    public static final String UUID_SEQUENCE = "org.apache.logging.log4j.uuidSequence";
043
044    private static final Logger LOGGER = StatusLogger.getLogger();
045
046    private static final String ASSIGNED_SEQUENCES = "org.apache.logging.log4j.assignedSequences";
047
048    private static final AtomicInteger COUNT = new AtomicInteger(0);
049    private static final long TYPE1 = 0x1000L;
050    private static final byte VARIANT = (byte) 0x80;
051    private static final int SEQUENCE_MASK = 0x3FFF;
052    private static final long NUM_100NS_INTERVALS_SINCE_UUID_EPOCH = 0x01b21dd213814000L;
053    private static final long INITIAL_UUID_SEQNO = PropertiesUtil.getProperties().getLongProperty(UUID_SEQUENCE, 0);
054
055    private static final long LEAST;
056
057    private static final long LOW_MASK = 0xffffffffL;
058    private static final long MID_MASK = 0xffff00000000L;
059    private static final long HIGH_MASK = 0xfff000000000000L;
060    private static final int NODE_SIZE = 8;
061    private static final int SHIFT_2 = 16;
062    private static final int SHIFT_4 = 32;
063    private static final int SHIFT_6 = 48;
064    private static final int HUNDRED_NANOS_PER_MILLI = 10000;
065
066    static {
067        byte[] mac = NetUtils.getMacAddress();
068        final Random randomGenerator = new SecureRandom();
069        if (mac == null || mac.length == 0) {
070            mac = new byte[6];
071            randomGenerator.nextBytes(mac);
072        }
073        final int length = mac.length >= 6 ? 6 : mac.length;
074        final int index = mac.length >= 6 ? mac.length - 6 : 0;
075        final byte[] node = new byte[NODE_SIZE];
076        node[0] = VARIANT;
077        node[1] = 0;
078        for (int i = 2; i < NODE_SIZE; ++i) {
079            node[i] = 0;
080        }
081        System.arraycopy(mac, index, node, index + 2, length);
082        final ByteBuffer buf = ByteBuffer.wrap(node);
083        long rand = INITIAL_UUID_SEQNO;
084        String assigned = PropertiesUtil.getProperties().getStringProperty(ASSIGNED_SEQUENCES);
085        long[] sequences;
086        if (assigned == null) {
087            sequences = new long[0];
088        } else {
089            final String[] array = assigned.split(Patterns.COMMA_SEPARATOR);
090            sequences = new long[array.length];
091            int i = 0;
092            for (final String value : array) {
093                sequences[i] = Long.parseLong(value);
094                ++i;
095            }
096        }
097        if (rand == 0) {
098            rand = randomGenerator.nextLong();
099        }
100        rand &= SEQUENCE_MASK;
101        boolean duplicate;
102        do {
103            duplicate = false;
104            for (final long sequence : sequences) {
105                if (sequence == rand) {
106                    duplicate = true;
107                    break;
108                }
109            }
110            if (duplicate) {
111                rand = (rand + 1) & SEQUENCE_MASK;
112            }
113        } while (duplicate);
114        assigned = assigned == null ? Long.toString(rand) : assigned + ',' + Long.toString(rand);
115        System.setProperty(ASSIGNED_SEQUENCES, assigned);
116
117        LEAST = buf.getLong() | rand << SHIFT_6;
118    }
119
120
121    /* This class cannot be instantiated */
122    private UuidUtil() {
123    }
124
125    /**
126     * Generates Type 1 UUID. The time contains the number of 100NS intervals that have occurred
127     * since 00:00:00.00 UTC, 10 October 1582. Each UUID on a particular machine is unique to the 100NS interval
128     * until they rollover around 3400 A.D.
129     * <ol>
130     * <li>Digits 1-12 are the lower 48 bits of the number of 100 ns increments since the start of the UUID
131     * epoch.</li>
132     * <li>Digit 13 is the version (with a value of 1).</li>
133     * <li>Digits 14-16 are a sequence number that is incremented each time a UUID is generated.</li>
134     * <li>Digit 17 is the variant (with a value of binary 10) and 10 bits of the sequence number</li>
135     * <li>Digit 18 is final 16 bits of the sequence number.</li>
136     * <li>Digits 19-32 represent the system the application is running on.</li>
137     * </ol>
138     *
139     * @return universally unique identifiers (UUID)
140     */
141    public static UUID getTimeBasedUuid() {
142
143        final long time = ((System.currentTimeMillis() * HUNDRED_NANOS_PER_MILLI) +
144            NUM_100NS_INTERVALS_SINCE_UUID_EPOCH) + (COUNT.incrementAndGet() % HUNDRED_NANOS_PER_MILLI);
145        final long timeLow = (time & LOW_MASK) << SHIFT_4;
146        final long timeMid = (time & MID_MASK) >> SHIFT_2;
147        final long timeHi = (time & HIGH_MASK) >> SHIFT_6;
148        final long most = timeLow | timeMid | TYPE1 | timeHi;
149        return new UUID(most, LEAST);
150    }
151}
152