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.helpers;
018    
019    import org.apache.logging.log4j.util.PropertiesUtil;
020    
021    import java.lang.reflect.Method;
022    import java.net.InetAddress;
023    import java.net.NetworkInterface;
024    import java.net.UnknownHostException;
025    import java.nio.ByteBuffer;
026    import java.security.SecureRandom;
027    import java.util.Enumeration;
028    import java.util.Random;
029    import java.util.UUID;
030    import java.util.concurrent.atomic.AtomicInteger;
031    
032    /**
033     * Generates a unique id. The generated UUID will be unique for approximately 8,925 years so long as
034     * less than 4095 ids are generated per millisecond on the same device (as identified by its MAC adddress).
035     */
036    public 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                        }
144                    }
145                    if (duplicate) {
146                        rand = (rand + 1) & SEQUENCE_MASK;
147                    }
148                } while (duplicate);
149                assigned = assigned == null ? Long.toString(rand) : assigned + "," + Long.toString(rand);
150                System.setProperty(ASSIGNED_SEQUENCES, assigned);
151            }
152    
153            least = buf.getLong() | rand << SHIFT_6;
154        }
155    
156    
157        /* This class cannot be instantiated */
158        private UUIDUtil() {
159        }
160    
161        /**
162         * Generates Type 1 UUID. The time contains the number of 100NS intervals that have occurred
163         * since 00:00:00.00 UTC, 10 October 1582. Each UUID on a particular machine is unique to the 100NS interval
164         * until they rollover around 3400 A.D.
165         * <ol>
166         * <li>Digits 1-12 are the lower 48 bits of the number of 100 ns increments since the start of the UUID
167         * epoch.</li>
168         * <li>Digit 13 is the version (with a value of 1).</li>
169         * <li>Digits 14-16 are a sequence number that is incremented each time a UUID is generated.</li>
170         * <li>Digit 17 is the variant (with a value of binary 10) and 10 bits of the sequence number</li>
171         * <li>Digit 18 is final 16 bits of the sequence number.</li>
172         * <li>Digits 19-32 represent the system the application is running on.
173         * </ol>
174         *
175         * @return universally unique identifiers (UUID)
176         */
177        public static UUID getTimeBasedUUID() {
178    
179            final long time = ((System.currentTimeMillis() * HUNDRED_NANOS_PER_MILLI) +
180                NUM_100NS_INTERVALS_SINCE_UUID_EPOCH) + (count.incrementAndGet() % HUNDRED_NANOS_PER_MILLI);
181            final long timeLow = (time & LOW_MASK) << SHIFT_4;
182            final long timeMid = (time & MID_MASK) >> SHIFT_2;
183            final long timeHi = (time & HIGH_MASK) >> SHIFT_6;
184            final long most = timeLow | timeMid | TYPE1 | timeHi;
185            return new UUID(most, least);
186        }
187    }
188