1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty.channel;
18
19 import io.netty.util.internal.EmptyArrays;
20 import io.netty.util.internal.MacAddressUtil;
21 import io.netty.util.internal.PlatformDependent;
22 import io.netty.util.internal.SystemPropertyUtil;
23 import io.netty.util.internal.logging.InternalLogger;
24 import io.netty.util.internal.logging.InternalLoggerFactory;
25
26 import java.lang.reflect.Method;
27 import java.util.Arrays;
28 import java.util.concurrent.ThreadLocalRandom;
29 import java.util.concurrent.atomic.AtomicInteger;
30
31 import static io.netty.util.internal.MacAddressUtil.defaultMachineId;
32 import static io.netty.util.internal.MacAddressUtil.parseMAC;
33
34
35
36
37 public final class DefaultChannelId implements ChannelId {
38
39 private static final long serialVersionUID = 809640043754842613L;
40
41 private static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultChannelId.class);
42 private static final byte[] MACHINE_ID;
43 private static final int PROCESS_ID_LEN = 4;
44 private static final int PROCESS_ID;
45 private static final int SEQUENCE_LEN = 4;
46 private static final int TIMESTAMP_LEN = 8;
47 private static final int RANDOM_LEN = 4;
48
49 private static final AtomicInteger nextSequence = new AtomicInteger();
50
51 private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray();
52
53
54
55
56 public static DefaultChannelId newInstance() {
57 return new DefaultChannelId(MACHINE_ID,
58 PROCESS_ID,
59 nextSequence.getAndIncrement(),
60 Long.reverse(System.nanoTime()) ^ System.currentTimeMillis(),
61 ThreadLocalRandom.current().nextInt());
62 }
63
64 static {
65 int processId = -1;
66 String customProcessId = SystemPropertyUtil.get("io.netty.processId");
67 if (customProcessId != null) {
68 try {
69 processId = Integer.parseInt(customProcessId);
70 } catch (NumberFormatException e) {
71
72 }
73
74 if (processId < 0) {
75 processId = -1;
76 logger.warn("-Dio.netty.processId: {} (malformed)", customProcessId);
77 } else if (logger.isDebugEnabled()) {
78 logger.debug("-Dio.netty.processId: {} (user-set)", processId);
79 }
80 }
81
82 if (processId < 0) {
83 processId = defaultProcessId();
84 if (logger.isDebugEnabled()) {
85 logger.debug("-Dio.netty.processId: {} (auto-detected)", processId);
86 }
87 }
88
89 PROCESS_ID = processId;
90
91 byte[] machineId = null;
92 String customMachineId = SystemPropertyUtil.get("io.netty.machineId");
93 if (customMachineId != null) {
94 try {
95 machineId = parseMAC(customMachineId);
96 } catch (Exception e) {
97 logger.warn("-Dio.netty.machineId: {} (malformed)", customMachineId, e);
98 }
99 if (machineId != null) {
100 logger.debug("-Dio.netty.machineId: {} (user-set)", customMachineId);
101 }
102 }
103
104 if (machineId == null) {
105 machineId = defaultMachineId();
106 if (logger.isDebugEnabled()) {
107 logger.debug("-Dio.netty.machineId: {} (auto-detected)", MacAddressUtil.formatAddress(machineId));
108 }
109 }
110
111 MACHINE_ID = machineId;
112 }
113
114 static int processHandlePid(ClassLoader loader) {
115
116 int nilValue = -1;
117 if (PlatformDependent.javaVersion() >= 9) {
118 Long pid;
119 try {
120 Class<?> processHandleImplType = Class.forName("java.lang.ProcessHandle", true, loader);
121 Method processHandleCurrent = processHandleImplType.getMethod("current");
122 Object processHandleInstance = processHandleCurrent.invoke(null);
123 Method processHandlePid = processHandleImplType.getMethod("pid");
124 pid = (Long) processHandlePid.invoke(processHandleInstance);
125 } catch (Exception e) {
126 logger.debug("Could not invoke ProcessHandle.current().pid();", e);
127 return nilValue;
128 }
129 if (pid > Integer.MAX_VALUE || pid < Integer.MIN_VALUE) {
130 throw new IllegalStateException("Current process ID exceeds int range: " + pid);
131 }
132 return pid.intValue();
133 }
134 return nilValue;
135 }
136
137 static int jmxPid(ClassLoader loader) {
138 String value;
139 try {
140
141 Class<?> mgmtFactoryType = Class.forName("java.lang.management.ManagementFactory", true, loader);
142 Class<?> runtimeMxBeanType = Class.forName("java.lang.management.RuntimeMXBean", true, loader);
143
144 Method getRuntimeMXBean = mgmtFactoryType.getMethod("getRuntimeMXBean", EmptyArrays.EMPTY_CLASSES);
145 Object bean = getRuntimeMXBean.invoke(null, EmptyArrays.EMPTY_OBJECTS);
146 Method getName = runtimeMxBeanType.getMethod("getName", EmptyArrays.EMPTY_CLASSES);
147 value = (String) getName.invoke(bean, EmptyArrays.EMPTY_OBJECTS);
148 } catch (Throwable t) {
149 logger.debug("Could not invoke ManagementFactory.getRuntimeMXBean().getName(); Android?", t);
150 try {
151
152 Class<?> processType = Class.forName("android.os.Process", true, loader);
153 Method myPid = processType.getMethod("myPid", EmptyArrays.EMPTY_CLASSES);
154 value = myPid.invoke(null, EmptyArrays.EMPTY_OBJECTS).toString();
155 } catch (Throwable t2) {
156 logger.debug("Could not invoke Process.myPid(); not Android?", t2);
157 value = "";
158 }
159 }
160
161 int atIndex = value.indexOf('@');
162 if (atIndex >= 0) {
163 value = value.substring(0, atIndex);
164 }
165
166 int pid;
167 try {
168 pid = Integer.parseInt(value);
169 } catch (NumberFormatException e) {
170
171 pid = -1;
172 }
173
174 if (pid < 0) {
175 pid = ThreadLocalRandom.current().nextInt();
176 logger.warn("Failed to find the current process ID from '{}'; using a random value: {}", value, pid);
177 }
178
179 return pid;
180 }
181
182 static int defaultProcessId() {
183 ClassLoader loader = PlatformDependent.getClassLoader(DefaultChannelId.class);
184 int processId = processHandlePid(loader);
185 if (processId != -1) {
186 return processId;
187 }
188 return jmxPid(loader);
189 }
190
191 private final byte[] machineId;
192 private final int processId;
193 private final int sequence;
194 private final long timestamp;
195 private final int random;
196 private final int hashCode;
197
198 private transient String shortValue;
199 private transient String longValue;
200
201
202
203
204 DefaultChannelId(final byte[] machineId, final int processId, final int sequence,
205 final long timestamp, final int random) {
206 this.machineId = machineId;
207 this.processId = processId;
208 this.sequence = sequence;
209 this.timestamp = timestamp;
210 this.random = random;
211 hashCode = computeHashCode();
212 }
213
214 private int computeHashCode() {
215 int h = Arrays.hashCode(machineId);
216 h = 31 * h + processId;
217 h = 31 * h + sequence;
218 h = 31 * h + Long.hashCode(timestamp);
219 h = 31 * h + random;
220 return h;
221 }
222
223 @Override
224 public String asShortText() {
225 String shortValue = this.shortValue;
226 if (shortValue == null) {
227 final StringBuilder buf = new StringBuilder(RANDOM_LEN * 2);
228 appendHexInt(buf, random);
229 this.shortValue = shortValue = buf.toString();
230 }
231 return shortValue;
232 }
233
234 @Override
235 public String asLongText() {
236 String longValue = this.longValue;
237 if (longValue == null) {
238 this.longValue = longValue = newLongValue();
239 }
240 return longValue;
241 }
242
243 private String newLongValue() {
244 final int machineIdLen = machineId.length;
245 final StringBuilder buf = new StringBuilder(
246 2 * (machineIdLen + PROCESS_ID_LEN + SEQUENCE_LEN + TIMESTAMP_LEN + RANDOM_LEN) + 4);
247 appendHexBytes(buf, machineId);
248 buf.append('-');
249 appendHexInt(buf, processId);
250 buf.append('-');
251 appendHexInt(buf, sequence);
252 buf.append('-');
253 appendHexLong(buf, timestamp);
254 buf.append('-');
255 appendHexInt(buf, random);
256 return buf.toString();
257 }
258
259 private static void appendHexBytes(StringBuilder buf, byte[] bytes) {
260 for (byte b : bytes) {
261 buf.append(HEX_CHARS[(b & 0xFF) >>> 4]);
262 buf.append(HEX_CHARS[b & 0xF]);
263 }
264 }
265
266 private static void appendHexInt(StringBuilder buf, int value) {
267 for (int i = 28; i >= 0; i -= 4) {
268 buf.append(HEX_CHARS[(value >>> i) & 0xF]);
269 }
270 }
271
272 private static void appendHexLong(StringBuilder buf, long value) {
273 for (int i = 60; i >= 0; i -= 4) {
274 buf.append(HEX_CHARS[(int) ((value >>> i) & 0xF)]);
275 }
276 }
277
278 @Override
279 public int hashCode() {
280 return hashCode;
281 }
282
283 @Override
284 public int compareTo(final ChannelId o) {
285 if (this == o) {
286
287 return 0;
288 }
289 if (o instanceof DefaultChannelId) {
290 final DefaultChannelId other = (DefaultChannelId) o;
291 int cmp = compareBytes(machineId, other.machineId);
292 if (cmp != 0) {
293 return cmp;
294 }
295 cmp = Integer.compareUnsigned(processId, other.processId);
296 if (cmp != 0) {
297 return cmp;
298 }
299 cmp = Integer.compareUnsigned(sequence, other.sequence);
300 if (cmp != 0) {
301 return cmp;
302 }
303 cmp = Long.compareUnsigned(timestamp, other.timestamp);
304 if (cmp != 0) {
305 return cmp;
306 }
307 return Integer.compareUnsigned(random, other.random);
308 }
309
310 return asLongText().compareTo(o.asLongText());
311 }
312
313 private static int compareBytes(byte[] a, byte[] b) {
314 int len1 = a.length;
315 int len2 = b.length;
316 int len = Math.min(len1, len2);
317 for (int k = 0; k < len; k++) {
318 int cmp = (a[k] & 0xFF) - (b[k] & 0xFF);
319 if (cmp != 0) {
320 return cmp;
321 }
322 }
323 return len1 - len2;
324 }
325
326 @Override
327 public boolean equals(Object obj) {
328 if (this == obj) {
329 return true;
330 }
331 if (!(obj instanceof DefaultChannelId)) {
332 return false;
333 }
334 DefaultChannelId other = (DefaultChannelId) obj;
335 return hashCode == other.hashCode
336 && random == other.random
337 && processId == other.processId
338 && sequence == other.sequence
339 && timestamp == other.timestamp
340 && Arrays.equals(machineId, other.machineId);
341 }
342
343 @Override
344 public String toString() {
345 return asShortText();
346 }
347 }