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      /**
40       * System property that may be used to seed the UUID generation with an integer value.
41       */
42      public static final String UUID_SEQUENCE = "org.apache.logging.log4j.uuidSequence";
43  
44      private static final Logger LOGGER = StatusLogger.getLogger();
45  
46      private static final String ASSIGNED_SEQUENCES = "org.apache.logging.log4j.assignedSequences";
47  
48      private static final AtomicInteger COUNT = new AtomicInteger(0);
49      private static final long TYPE1 = 0x1000L;
50      private static final byte VARIANT = (byte) 0x80;
51      private static final int SEQUENCE_MASK = 0x3FFF;
52      private static final long NUM_100NS_INTERVALS_SINCE_UUID_EPOCH = 0x01b21dd213814000L;
53      private static final long INITIAL_UUID_SEQNO = PropertiesUtil.getProperties().getLongProperty(UUID_SEQUENCE, 0);
54  
55      private static final long LEAST;
56  
57      private static final long LOW_MASK = 0xffffffffL;
58      private static final long MID_MASK = 0xffff00000000L;
59      private static final long HIGH_MASK = 0xfff000000000000L;
60      private static final int NODE_SIZE = 8;
61      private static final int SHIFT_2 = 16;
62      private static final int SHIFT_4 = 32;
63      private static final int SHIFT_6 = 48;
64      private static final int HUNDRED_NANOS_PER_MILLI = 10000;
65  
66      static {
67          byte[] mac = NetUtils.getMacAddress();
68          final Random randomGenerator = new SecureRandom();
69          if (mac == null || mac.length == 0) {
70              mac = new byte[6];
71              randomGenerator.nextBytes(mac);
72          }
73          final int length = mac.length >= 6 ? 6 : mac.length;
74          final int index = mac.length >= 6 ? mac.length - 6 : 0;
75          final byte[] node = new byte[NODE_SIZE];
76          node[0] = VARIANT;
77          node[1] = 0;
78          for (int i = 2; i < NODE_SIZE; ++i) {
79              node[i] = 0;
80          }
81          System.arraycopy(mac, index, node, index + 2, length);
82          final ByteBuffer buf = ByteBuffer.wrap(node);
83          long rand = INITIAL_UUID_SEQNO;
84          String assigned = PropertiesUtil.getProperties().getStringProperty(ASSIGNED_SEQUENCES);
85          long[] sequences;
86          if (assigned == null) {
87              sequences = new long[0];
88          } else {
89              final String[] array = assigned.split(Patterns.COMMA_SEPARATOR);
90              sequences = new long[array.length];
91              int i = 0;
92              for (final String value : array) {
93                  sequences[i] = Long.parseLong(value);
94                  ++i;
95              }
96          }
97          if (rand == 0) {
98              rand = randomGenerator.nextLong();
99          }
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