1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 package org.spf4j.concurrent;
33
34 import com.google.common.io.BaseEncoding;
35 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
36 import java.net.NetworkInterface;
37 import java.net.SocketException;
38 import java.nio.ByteBuffer;
39 import java.security.NoSuchAlgorithmException;
40 import java.security.SecureRandom;
41 import java.util.Enumeration;
42 import java.util.function.Supplier;
43 import java.util.logging.Level;
44 import java.util.logging.Logger;
45 import javax.annotation.ParametersAreNonnullByDefault;
46 import org.spf4j.base.AppendableUtils;
47 import org.spf4j.os.ProcessUtil;
48
49
50
51
52
53
54
55
56 @ParametersAreNonnullByDefault
57 public final class UIDGenerator implements Supplier<CharSequence> {
58
59 private final Sequence sequence;
60
61 private final StringBuilder base;
62
63 private final int maxSize;
64
65 public UIDGenerator(final Sequence sequence) {
66 this(sequence, 0);
67 }
68
69 public UIDGenerator(final Sequence sequence, final String prefix) {
70 this(sequence, BaseEncoding.base64Url(), 0, '.', prefix);
71 }
72
73 public UIDGenerator(final Sequence sequence, final long customEpoch) {
74 this(sequence, BaseEncoding.base64Url(), customEpoch, '.', "");
75 }
76
77 public UIDGenerator(final Sequence sequence, final String prefix, final long customEpoch) {
78 this(sequence, BaseEncoding.base64Url(), customEpoch, '.', prefix);
79 }
80
81
82
83
84
85
86
87 @SuppressFBWarnings("STT_TOSTRING_STORED_IN_FIELD")
88 public UIDGenerator(final Sequence sequence, final BaseEncoding baseEncoding,
89 final long customEpoch, final char separator, final String prefix) {
90 this.sequence = sequence;
91 StringBuilder sb = generateIdBase(prefix, baseEncoding, separator, customEpoch);
92 base = sb;
93 maxSize = base.length() + 16;
94 }
95
96 public static StringBuilder generateIdBase(final String prefix,
97 final char separator) {
98 return generateIdBase(prefix, BaseEncoding.base64Url(), separator, 1509741164184L);
99 }
100
101 public static StringBuilder generateIdBase(final String prefix,
102 final char separator,
103 final long customEpoch) {
104 return generateIdBase(prefix, BaseEncoding.base64Url(), separator, customEpoch);
105 }
106
107 @SuppressFBWarnings("PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS")
108 public static StringBuilder generateIdBase(final String prefix,
109 final BaseEncoding baseEncoding, final char separator,
110 final long customEpoch) {
111 StringBuilder sb = new StringBuilder(16 + prefix.length());
112 sb.append(prefix);
113
114 byte[] intfMac;
115 try {
116 Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
117 if (networkInterfaces != null && networkInterfaces.hasMoreElements()) {
118 do {
119 intfMac = networkInterfaces.nextElement().getHardwareAddress();
120 } while ((intfMac == null || intfMac.length == 0) && networkInterfaces.hasMoreElements());
121 if (intfMac == null) {
122 Logger.getLogger(UIDGenerator.class.getName()).warning(
123 "Unable to get interface MAC address for ID generation");
124 try {
125 intfMac = ByteBuffer.allocate(Long.BYTES).putLong(SecureRandom.getInstanceStrong().nextLong()).array();
126 } catch (NoSuchAlgorithmException ex) {
127 throw new IllegalStateException(ex);
128 }
129 }
130 } else {
131 Logger.getLogger(UIDGenerator.class.getName()).warning(
132 "Unable to get interface MAC address for ID generation");
133 try {
134 intfMac = ByteBuffer.allocate(Long.BYTES).putLong(SecureRandom.getInstanceStrong().nextLong()).array();
135 } catch (NoSuchAlgorithmException ex) {
136 throw new IllegalStateException(ex);
137 }
138 }
139 } catch (SocketException ex) {
140 Logger.getLogger(UIDGenerator.class.getName()).log(Level.WARNING,
141 "Unable to get interface MAC address for ID generation", ex);
142 try {
143 intfMac = ByteBuffer.allocate(Long.BYTES).putLong(SecureRandom.getInstanceStrong().nextLong()).array();
144 } catch (NoSuchAlgorithmException ex2) {
145 ex2.addSuppressed(ex);
146 throw new IllegalStateException(ex2);
147 }
148 }
149 sb.append(baseEncoding.encode(intfMac)).append(separator);
150
151 AppendableUtils.appendUnsignedString(sb, ProcessUtil.getPid(), 5);
152 sb.append(separator);
153 AppendableUtils.appendUnsignedString(sb, (System.currentTimeMillis() - customEpoch) / 1000, 5);
154 sb.append(separator);
155 return sb;
156 }
157
158 public int getMaxSize() {
159 return maxSize;
160 }
161
162 public CharSequence next() {
163 StringBuilder result = new StringBuilder(maxSize);
164 result.append(base);
165 AppendableUtils.appendUnsignedString(result, sequence.next(), 5);
166 return result;
167 }
168
169 @Override
170 public String toString() {
171 return "UIDGenerator{" + "sequence=" + sequence + ", base=" + base + ", maxSize=" + maxSize + '}';
172 }
173
174 @Override
175 public CharSequence get() {
176 return next();
177 }
178
179 }