org.spf4j.concurrent.UIDGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.spf4j.concurrent.UIDGenerator.java

Source

/*
 * Copyright (c) 2001, Zoltan Farkas All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package org.spf4j.concurrent;

import com.google.common.io.BaseEncoding;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;

/**
 * Unique ID Generator Based on the assumptions:
 * 1. host MAC address is used. (each network interface has a Unique ID) (encoded with provided encoder)
 * 2. process id is used + current epoch seconds. it is assumed the PID is not recycled within a second.
 * 3. A process sequence is used. UIDs will cycle after Long.MaxValue is reached.
 *
 * @author zoly
 */
@ParametersAreNonnullByDefault
public final class UIDGenerator {

    private final Sequence sequence;

    private final StringBuilder base;

    private final int maxSize;

    private final int baseLength;

    public UIDGenerator(final Sequence sequence) {
        this(sequence, 0);
    }

    public UIDGenerator(final Sequence sequence, final String prefix) {
        this(sequence, BaseEncoding.base64Url(), 0, '.', prefix);
    }

    public UIDGenerator(final Sequence sequence, final long customEpoch) {
        this(sequence, BaseEncoding.base64Url(), customEpoch, '.', "");
    }

    /**
     * Construct a UID Generator
     * @param sequence
     * @param baseEncoding - if null MAC address based ID will not be included.
     */
    public UIDGenerator(final Sequence sequence, @Nullable final BaseEncoding baseEncoding, final long customEpoch,
            final char separator, final String prefix) {
        this.sequence = sequence;
        StringBuilder sb = new StringBuilder(16 + prefix.length());
        sb.append(prefix);
        if (baseEncoding != null) {
            byte[] intfMac;
            try {
                Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
                if (networkInterfaces != null && networkInterfaces.hasMoreElements()) {
                    do {
                        intfMac = networkInterfaces.nextElement().getHardwareAddress();
                    } while ((intfMac == null || intfMac.length == 0) && networkInterfaces.hasMoreElements());
                    if (intfMac == null) {
                        intfMac = new byte[] { 0 };
                    }
                } else {
                    intfMac = new byte[] { 0 };
                }
            } catch (SocketException ex) {
                throw new RuntimeException(ex);
            }
            sb.append(baseEncoding.encode(intfMac)).append(separator);
        }
        appendUnsignedString(sb, org.spf4j.base.Runtime.PID, 5);
        sb.append(separator);
        appendUnsignedString(sb, (System.currentTimeMillis() - customEpoch) / 1000, 5);
        sb.append(separator);
        base = sb;
        baseLength = base.length();
        maxSize = baseLength + 16;
    }

    public int getMaxSize() {
        return maxSize;
    }

    public CharSequence next() {
        StringBuilder result = new StringBuilder(maxSize);
        result.append(base);
        appendUnsignedString(result, sequence.next(), 5);
        return result;
    }

    private static final char[] DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
            'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
            'z' };

    private static final ThreadLocal<char[]> BUFF = new ThreadLocal<char[]>() {

        @Override
        @SuppressFBWarnings("SUA_SUSPICIOUS_UNINITIALIZED_ARRAY")
        protected char[] initialValue() {
            return new char[64];
        }

    };

    private static void appendUnsignedString(final StringBuilder sb, final long nr, final int shift) {
        long i = nr;
        char[] buf = BUFF.get();
        int charPos = 64;
        int radix = 1 << shift;
        long mask = radix - 1;
        do {
            buf[--charPos] = DIGITS[(int) (i & mask)];
            i >>>= shift;
        } while (i != 0);
        sb.append(buf, charPos, (64 - charPos));
    }

}