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