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.helpers;
018
019import java.lang.reflect.Method;
020import java.net.InetAddress;
021import java.net.NetworkInterface;
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.util.PropertiesUtil;
031
032/**
033 * Generates a unique ID. The generated UUID will be unique for approximately 8,925 years so long as
034 * less than 10,000 IDs are generated per millisecond on the same device (as identified by its MAC address).
035 */
036public final class UUIDUtil {
037    /**
038     * System property that may be used to seed the UUID generation with an integer value.
039     */
040    public static final String UUID_SEQUENCE = "org.apache.logging.log4j.uuidSequence";
041
042    private static final String ASSIGNED_SEQUENCES = "org.apache.logging.log4j.assignedSequences";
043
044    private static AtomicInteger count = new AtomicInteger(0);
045
046    private static final long TYPE1 = 0x1000L;
047
048    private static final byte VARIANT = (byte) 0x80;
049
050    private static final int SEQUENCE_MASK = 0x3FFF;
051
052    private static final long NUM_100NS_INTERVALS_SINCE_UUID_EPOCH = 0x01b21dd213814000L;
053
054    private static long uuidSequence = PropertiesUtil.getProperties().getLongProperty(UUID_SEQUENCE, 0);
055
056    private static long least;
057
058    private static final long LOW_MASK = 0xffffffffL;
059    private static final long MID_MASK = 0xffff00000000L;
060    private static final long HIGH_MASK = 0xfff000000000000L;
061    private static final int NODE_SIZE = 8;
062    private static final int SHIFT_2 = 16;
063    private static final int SHIFT_4 = 32;
064    private static final int SHIFT_6 = 48;
065    private static final int HUNDRED_NANOS_PER_MILLI = 10000;
066
067    static {
068        byte[] mac = null;
069        try {
070            final InetAddress address = InetAddress.getLocalHost();
071            try {
072                NetworkInterface ni = NetworkInterface.getByInetAddress(address);
073                if (ni != null && !ni.isLoopback() && ni.isUp()) {
074                    final Method method = ni.getClass().getMethod("getHardwareAddress");
075                    if (method != null) {
076                        mac = (byte[]) method.invoke(ni);
077                    }
078                }
079
080                if (mac == null) {
081                    final Enumeration<NetworkInterface> enumeration = NetworkInterface.getNetworkInterfaces();
082                    while (enumeration.hasMoreElements() && mac == null) {
083                        ni = enumeration.nextElement();
084                        if (ni != null && ni.isUp() && !ni.isLoopback()) {
085                            final Method method = ni.getClass().getMethod("getHardwareAddress");
086                            if (method != null) {
087                                mac = (byte[]) method.invoke(ni);
088                            }
089                        }
090                    }
091                }
092            } catch (final Exception ex) {
093                ex.printStackTrace();
094                // Ignore exception
095            }
096            if (mac == null || mac.length == 0) {
097                mac = address.getAddress();
098            }
099        } catch (final UnknownHostException e) {
100            // Ignore exception
101        }
102        final Random randomGenerator = new SecureRandom();
103        if (mac == null || mac.length == 0) {
104            mac = new byte[6];
105            randomGenerator.nextBytes(mac);
106        }
107        final int length = mac.length >= 6 ? 6 : mac.length;
108        final int index = mac.length >= 6 ? mac.length - 6 : 0;
109        final byte[] node = new byte[NODE_SIZE];
110        node[0] = VARIANT;
111        node[1] = 0;
112        for (int i = 2; i < NODE_SIZE; ++i) {
113            node[i] = 0;
114        }
115        System.arraycopy(mac, index, node, index + 2, length);
116        final ByteBuffer buf = ByteBuffer.wrap(node);
117        long rand = uuidSequence;
118        final Runtime runtime = Runtime.getRuntime();
119        synchronized (runtime) {
120            String assigned = PropertiesUtil.getProperties().getStringProperty(ASSIGNED_SEQUENCES);
121            long[] sequences;
122            if (assigned == null) {
123                sequences = new long[0];
124            } else {
125                final String[] array = assigned.split(",");
126                sequences = new long[array.length];
127                int i = 0;
128                for (final String value : array) {
129                    sequences[i] = Long.parseLong(value);
130                    ++i;
131                }
132            }
133            if (rand == 0) {
134                rand = randomGenerator.nextLong();
135            }
136            rand &= SEQUENCE_MASK;
137            boolean duplicate;
138            do {
139                duplicate = false;
140                for (final long sequence : sequences) {
141                    if (sequence == rand) {
142                        duplicate = true;
143                        break;
144                    }
145                }
146                if (duplicate) {
147                    rand = (rand + 1) & SEQUENCE_MASK;
148                }
149            } while (duplicate);
150            assigned = assigned == null ? Long.toString(rand) : assigned + "," + Long.toString(rand);
151            System.setProperty(ASSIGNED_SEQUENCES, assigned);
152        }
153
154        least = buf.getLong() | rand << SHIFT_6;
155    }
156
157
158    /* This class cannot be instantiated */
159    private UUIDUtil() {
160    }
161
162    /**
163     * Generates Type 1 UUID. The time contains the number of 100NS intervals that have occurred
164     * since 00:00:00.00 UTC, 10 October 1582. Each UUID on a particular machine is unique to the 100NS interval
165     * until they rollover around 3400 A.D.
166     * <ol>
167     * <li>Digits 1-12 are the lower 48 bits of the number of 100 ns increments since the start of the UUID
168     * epoch.</li>
169     * <li>Digit 13 is the version (with a value of 1).</li>
170     * <li>Digits 14-16 are a sequence number that is incremented each time a UUID is generated.</li>
171     * <li>Digit 17 is the variant (with a value of binary 10) and 10 bits of the sequence number</li>
172     * <li>Digit 18 is final 16 bits of the sequence number.</li>
173     * <li>Digits 19-32 represent the system the application is running on.
174     * </ol>
175     *
176     * @return universally unique identifiers (UUID)
177     */
178    public static UUID getTimeBasedUUID() {
179
180        final long time = ((System.currentTimeMillis() * HUNDRED_NANOS_PER_MILLI) +
181            NUM_100NS_INTERVALS_SINCE_UUID_EPOCH) + (count.incrementAndGet() % HUNDRED_NANOS_PER_MILLI);
182        final long timeLow = (time & LOW_MASK) << SHIFT_4;
183        final long timeMid = (time & MID_MASK) >> SHIFT_2;
184        final long timeHi = (time & HIGH_MASK) >> SHIFT_6;
185        final long most = timeLow | timeMid | TYPE1 | timeHi;
186        return new UUID(most, least);
187    }
188}
189