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.helpers;
18  
19  import java.lang.reflect.Method;
20  import java.net.InetAddress;
21  import java.net.NetworkInterface;
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.util.PropertiesUtil;
31  
32  /**
33   * Generates a unique ID. The generated UUID will be unique for approximately 8,925 years so long as
34   * less than 10,000 IDs are generated per millisecond on the same device (as identified by its MAC address).
35   */
36  public final class UUIDUtil {
37      /**
38       * System property that may be used to seed the UUID generation with an integer value.
39       */
40      public static final String UUID_SEQUENCE = "org.apache.logging.log4j.uuidSequence";
41  
42      private static final String ASSIGNED_SEQUENCES = "org.apache.logging.log4j.assignedSequences";
43  
44      private static AtomicInteger count = new AtomicInteger(0);
45  
46      private static final long TYPE1 = 0x1000L;
47  
48      private static final byte VARIANT = (byte) 0x80;
49  
50      private static final int SEQUENCE_MASK = 0x3FFF;
51  
52      private static final long NUM_100NS_INTERVALS_SINCE_UUID_EPOCH = 0x01b21dd213814000L;
53  
54      private static long uuidSequence = PropertiesUtil.getProperties().getLongProperty(UUID_SEQUENCE, 0);
55  
56      private static long least;
57  
58      private static final long LOW_MASK = 0xffffffffL;
59      private static final long MID_MASK = 0xffff00000000L;
60      private static final long HIGH_MASK = 0xfff000000000000L;
61      private static final int NODE_SIZE = 8;
62      private static final int SHIFT_2 = 16;
63      private static final int SHIFT_4 = 32;
64      private static final int SHIFT_6 = 48;
65      private static final int HUNDRED_NANOS_PER_MILLI = 10000;
66  
67      static {
68          byte[] mac = null;
69          try {
70              final InetAddress address = InetAddress.getLocalHost();
71              try {
72                  NetworkInterface ni = NetworkInterface.getByInetAddress(address);
73                  if (ni != null && !ni.isLoopback() && ni.isUp()) {
74                      final Method method = ni.getClass().getMethod("getHardwareAddress");
75                      if (method != null) {
76                          mac = (byte[]) method.invoke(ni);
77                      }
78                  }
79  
80                  if (mac == null) {
81                      final Enumeration<NetworkInterface> enumeration = NetworkInterface.getNetworkInterfaces();
82                      while (enumeration.hasMoreElements() && mac == null) {
83                          ni = enumeration.nextElement();
84                          if (ni != null && ni.isUp() && !ni.isLoopback()) {
85                              final Method method = ni.getClass().getMethod("getHardwareAddress");
86                              if (method != null) {
87                                  mac = (byte[]) method.invoke(ni);
88                              }
89                          }
90                      }
91                  }
92              } catch (final Exception ex) {
93                  ex.printStackTrace();
94                  // Ignore exception
95              }
96              if (mac == null || mac.length == 0) {
97                  mac = address.getAddress();
98              }
99          } 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