View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.util;
18  
19  import java.net.InetAddress;
20  import java.net.NetworkInterface;
21  import java.net.SocketException;
22  import java.net.UnknownHostException;
23  import java.nio.ByteBuffer;
24  import java.security.SecureRandom;
25  import java.util.Enumeration;
26  import java.util.Random;
27  import java.util.UUID;
28  import java.util.concurrent.atomic.AtomicInteger;
29  
30  import org.apache.logging.log4j.Logger;
31  import org.apache.logging.log4j.status.StatusLogger;
32  import org.apache.logging.log4j.util.PropertiesUtil;
33  
34  /**
35   * Generates a unique ID. The generated UUID will be unique for approximately 8,925 years so long as
36   * less than 10,000 IDs are generated per millisecond on the same device (as identified by its MAC address).
37   */
38  public final class UuidUtil {
39      private static final Logger LOGGER = StatusLogger.getLogger();
40      /**
41       * System property that may be used to seed the UUID generation with an integer value.
42       */
43      public static final String UUID_SEQUENCE = "org.apache.logging.log4j.uuidSequence";
44  
45      private static final String ASSIGNED_SEQUENCES = "org.apache.logging.log4j.assignedSequences";
46  
47      private static final AtomicInteger count = new AtomicInteger(0);
48  
49      private static final long TYPE1 = 0x1000L;
50  
51      private static final byte VARIANT = (byte) 0x80;
52  
53      private static final int SEQUENCE_MASK = 0x3FFF;
54  
55      private static final long NUM_100NS_INTERVALS_SINCE_UUID_EPOCH = 0x01b21dd213814000L;
56  
57      private static final long uuidSequence = PropertiesUtil.getProperties().getLongProperty(UUID_SEQUENCE, 0);
58  
59      private static final long least;
60  
61      private static final long LOW_MASK = 0xffffffffL;
62      private static final long MID_MASK = 0xffff00000000L;
63      private static final long HIGH_MASK = 0xfff000000000000L;
64      private static final int NODE_SIZE = 8;
65      private static final int SHIFT_2 = 16;
66      private static final int SHIFT_4 = 32;
67      private static final int SHIFT_6 = 48;
68      private static final int HUNDRED_NANOS_PER_MILLI = 10000;
69  
70      static {
71          byte[] mac = getLocalMacAddress();
72          final Random randomGenerator = new SecureRandom();
73          if (mac == null || mac.length == 0) {
74              mac = new byte[6];
75              randomGenerator.nextBytes(mac);
76          }
77          final int length = mac.length >= 6 ? 6 : mac.length;
78          final int index = mac.length >= 6 ? mac.length - 6 : 0;
79          final byte[] node = new byte[NODE_SIZE];
80          node[0] = VARIANT;
81          node[1] = 0;
82          for (int i = 2; i < NODE_SIZE; ++i) {
83              node[i] = 0;
84          }
85          System.arraycopy(mac, index, node, index + 2, length);
86          final ByteBuffer buf = ByteBuffer.wrap(node);
87          long rand = uuidSequence;
88          String assigned = PropertiesUtil.getProperties().getStringProperty(ASSIGNED_SEQUENCES);
89          long[] sequences;
90          if (assigned == null) {
91              sequences = new long[0];
92          } else {
93              final String[] array = assigned.split(Patterns.COMMA_SEPARATOR);
94              sequences = new long[array.length];
95              int i = 0;
96              for (final String value : array) {
97                  sequences[i] = Long.parseLong(value);
98                  ++i;
99              }
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