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