    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     */
    017    package org.apache.logging.log4j.core.util;
    018
    019    import java.net.InetAddress;
    020    import java.net.NetworkInterface;
    021    import java.net.SocketException;
    022    import java.net.UnknownHostException;
    023    import java.nio.ByteBuffer;
    024    import java.security.SecureRandom;
    025    import java.util.Enumeration;
    026    import java.util.Random;
    027    import java.util.UUID;
    028    import java.util.concurrent.atomic.AtomicInteger;
    029
    030    import org.apache.logging.log4j.Logger;
    031    import org.apache.logging.log4j.status.StatusLogger;
    032    import 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     */
    038    public final class UuidUtil {
    039        private static final Logger LOGGER = StatusLogger.getLogger();
    040        /**
    041         * System property that may be used to seed the UUID generation with an integer value.
    042         */
    043        public static final String UUID_SEQUENCE = "org.apache.logging.log4j.uuidSequence";
    044
    045        private static final String ASSIGNED_SEQUENCES = "org.apache.logging.log4j.assignedSequences";
    046
    047        private static final AtomicInteger count = new AtomicInteger(0);
    048
    049        private static final long TYPE1 = 0x1000L;
    050
    051        private static final byte VARIANT = (byte) 0x80;
    052
    053        private static final int SEQUENCE_MASK = 0x3FFF;
    054
    055        private static final long NUM_100NS_INTERVALS_SINCE_UUID_EPOCH = 0x01b21dd213814000L;
    056
    057        private static final long uuidSequence = PropertiesUtil.getProperties().getLongProperty(UUID_SEQUENCE, 0);
    058
    059        private static final long least;
    060
    061        private static final long LOW_MASK = 0xffffffffL;
    062        private static final long MID_MASK = 0xffff00000000L;
    063        private static final long HIGH_MASK = 0xfff000000000000L;
    064        private static final int NODE_SIZE = 8;
    065        private static final int SHIFT_2 = 16;
    066        private static final int SHIFT_4 = 32;
    067        private static final int SHIFT_6 = 48;
    068        private static final int HUNDRED_NANOS_PER_MILLI = 10000;
    069
    070        static {
    071            byte[] mac = getLocalMacAddress();
    072            final Random randomGenerator = new SecureRandom();
    073            if (mac == null || mac.length == 0) {
    074                mac = new byte[6];
    075                randomGenerator.nextBytes(mac);
    076            }
    077            final int length = mac.length >= 6 ? 6 : mac.length;
    078            final int index = mac.length >= 6 ? mac.length - 6 : 0;
    079            final byte[] node = new byte[NODE_SIZE];
    080            node[0] = VARIANT;
    081            node[1] = 0;
    082            for (int i = 2; i < NODE_SIZE; ++i) {
    083                node[i] = 0;
    084            }
    085            System.arraycopy(mac, index, node, index + 2, length);
    086            final ByteBuffer buf = ByteBuffer.wrap(node);
    087            long rand = uuidSequence;
    088            String assigned = PropertiesUtil.getProperties().getStringProperty(ASSIGNED_SEQUENCES);
    089            long[] sequences;
    090            if (assigned == null) {
    091                sequences = new long[0];
    092            } else {
    093                final String[] array = assigned.split(Patterns.COMMA_SEPARATOR);
    094                sequences = new long[array.length];
    095                int i = 0;
    096                for (final String value : array) {
    097                    sequences[i] = Long.parseLong(value);
    098                    ++i;
    099                }
    100            }
    101            if (rand == 0) {
    102                rand = randomGenerator.nextLong();
    103            }
    104            rand &= SEQUENCE_MASK;
    105            boolean duplicate;
    106            do {
    107                duplicate = false;
    108                for (final long sequence : sequences) {
    109                    if (sequence == rand) {
    110                        duplicate = true;
    111                        break;
    112                    }
    113                }
    114                if (duplicate) {
    115                    rand = (rand + 1) & SEQUENCE_MASK;
    116                }
    117            } while (duplicate);
    118            assigned = assigned == null ? Long.toString(rand) : assigned + ',' + Long.toString(rand);
    119            System.setProperty(ASSIGNED_SEQUENCES, assigned);
    120
    121            least = buf.getLong() | rand << SHIFT_6;
    122        }
    123
    124
    125        /* This class cannot be instantiated */
    126        private UuidUtil() {
    127        }
    128
    129        /**
    130         * Generates Type 1 UUID. The time contains the number of 100NS intervals that have occurred
    131         * since 00:00:00.00 UTC, 10 October 1582. Each UUID on a particular machine is unique to the 100NS interval
    132         * until they rollover around 3400 A.D.
    133         * <ol>
    134         * <li>Digits 1-12 are the lower 48 bits of the number of 100 ns increments since the start of the UUID
    135         * epoch.</li>
    136         * <li>Digit 13 is the version (with a value of 1).</li>
    137         * <li>Digits 14-16 are a sequence number that is incremented each time a UUID is generated.</li>
    138         * <li>Digit 17 is the variant (with a value of binary 10) and 10 bits of the sequence number</li>
    139         * <li>Digit 18 is final 16 bits of the sequence number.</li>
    140         * <li>Digits 19-32 represent the system the application is running on.</li>
    141         * </ol>
    142         *
    143         * @return universally unique identifiers (UUID)
    144         */
    145        public static UUID getTimeBasedUuid() {
    146
    147            final long time = ((System.currentTimeMillis() * HUNDRED_NANOS_PER_MILLI) +
    148                NUM_100NS_INTERVALS_SINCE_UUID_EPOCH) + (count.incrementAndGet() % HUNDRED_NANOS_PER_MILLI);
    149            final long timeLow = (time & LOW_MASK) << SHIFT_4;
    150            final long timeMid = (time & MID_MASK) >> SHIFT_2;
    151            final long timeHi = (time & HIGH_MASK) >> SHIFT_6;
    152            final long most = timeLow | timeMid | TYPE1 | timeHi;
    153            return new UUID(most, least);
    154        }
    155
    156        /**
    157         * Returns the local network interface's MAC address if possible. The local network interface is defined here as
    158         * the {@link java.net.NetworkInterface} that is both up and not a loopback interface.
    159         *
    160         * @return the MAC address of the local network interface or {@code null} if no MAC address could be determined.
    161         * @since 2.1
    162         */
    163        private static byte[] getLocalMacAddress() {
    164            byte[] mac = null;
    165            try {
    166                final InetAddress localHost = InetAddress.getLocalHost();
    167                try {
    168                    final NetworkInterface localInterface = NetworkInterface.getByInetAddress(localHost);
    169                    if (isUpAndNotLoopback(localInterface)) {
    170                        mac = localInterface.getHardwareAddress();
    171                    }
    172                    if (mac == null) {
    173                        final Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
    174                        while (networkInterfaces.hasMoreElements() && mac == null) {
    175                            final NetworkInterface nic = networkInterfaces.nextElement();
    176                            if (isUpAndNotLoopback(nic)) {
    177                                mac = nic.getHardwareAddress();
    178                            }
    179                        }
    180                    }
    181                } catch (final SocketException e) {
    182                    LOGGER.catching(e);
    183                }
    184                if (mac == null || mac.length == 0) {
    185                    mac = localHost.getAddress();
    186                }
    187            } catch (final UnknownHostException ignored) {
    188            }
    189            return mac;
    190        }
    191
    192        private static boolean isUpAndNotLoopback(final NetworkInterface ni) throws SocketException {
    193            return ni != null && !ni.isLoopback() && ni.isUp();
    194        }
    195    }
    196