/***************************************************************************** * * Copyright (C) Zenoss, Inc. 2011, all rights reserved. * * This content is made available according to terms specified in * License.zenoss under the directory where your Zenoss product is installed. * ****************************************************************************/ package org.zenoss.zep.impl; import com.fasterxml.uuid.EthernetAddress; import com.fasterxml.uuid.Generators; import com.fasterxml.uuid.impl.TimeBasedGenerator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.zenoss.zep.UUIDGenerator; import java.net.NetworkInterface; import java.net.SocketException; import java.nio.ByteBuffer; import java.util.Enumeration; import java.util.UUID; /** * Generates time-based UUIDs for use in ZEP using the MAC address of the system. Time-based UUIDs are much better * for use in the database for keys because they are spaced out sequentially and don't fragment the index. */ public class UUIDGeneratorImpl implements UUIDGenerator { private final Logger logger = LoggerFactory.getLogger(UUIDGeneratorImpl.class.getName()); private final TimeBasedGenerator generator; public UUIDGeneratorImpl() throws SocketException { /* Use a generator from a MAC address on the system */ EthernetAddress ethAddress = null; byte[] macAddr = getMacAddress(); if (macAddr != null) { ethAddress = new EthernetAddress(macAddr); } this.generator = Generators.timeBasedGenerator(ethAddress); } /** * The EthernetAddress.fromInterface code returns a random ethernet device each time - we would like to try * and consistently return the same address each time. This method will return the first network address sorted * by name with a valid mac address. * * @return A byte[] mac address. */ private byte[] getMacAddress() { byte[] data = null; try { String addressName = null; final Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces(); while (networkInterfaces.hasMoreElements()) { final NetworkInterface ni = networkInterfaces.nextElement(); if (ni.isLoopback()) { continue; } byte[] hwaddr = ni.getHardwareAddress(); if (hwaddr == null || hwaddr.length != 6) { continue; } if (addressName == null || ni.getName().compareTo(addressName) < 0) { addressName = ni.getName(); data = hwaddr; } } if (addressName != null) { logger.debug("Using network interface {} mac address for UUID generation", addressName); } } catch (SocketException e) { // Fall back to using a randomly generated mac. } if (data == null) { logger.debug("Unable to determine a local mac address for UUID generation - using random address"); } return data; } private static byte[] uuidToBytes(UUID uuid) { final ByteBuffer bb = ByteBuffer.allocate(16); bb.putLong(uuid.getMostSignificantBits()); bb.putLong(uuid.getLeastSignificantBits()); return bb.array(); } @Override public UUID generate() { final UUID uuid = this.generator.generate(); /* * Time-based UUIDs (type 1) are not in ascending order and so don't work well as primary keys. We will * reshuffle the UUIDs to have the timestamp at the end and the "fixed" information (like the MAC address) * at the beginning for less index fragmentation. This means we don't have "valid" UUIDs but they do provide * the desired uniqueness and will not fragment the index like random or time based UUIDs. */ final byte[] original = uuidToBytes(uuid); // UUID = time-low "-" time-mid "-" // time-high-and-version "-" // clock-seq-and-reserved // clock-seq-low "-" node // time-low = 4hexOctet // time-mid = 2hexOctet // time-high-and-version = 2hexOctet // clock-seq-and-reserved = hexOctet // clock-seq-low = hexOctet // node = 6hexOctet final ByteBuffer opt = ByteBuffer.allocate(16); /* Copy mac address to first 6 bytes */ opt.put(original, 10, 6); /* Copy clock seq to next 2 bytes */ opt.put(original, 8, 2); /* Copy time-high-and-version to next 2 bytes */ opt.put(original, 6, 2); /* Copy time-mid to next 4 bytes */ opt.put(original, 4, 2); /* Copy time-low to next 4 bytes */ opt.put(original, 0, 4); opt.flip(); final long mostSig = opt.getLong(); final long leastSig = opt.getLong(); final UUID ret = new UUID(mostSig, leastSig); return ret; } }