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.buffer.ByteBufUtil;
20 import io.netty.util.internal.EmptyArrays;
21 import io.netty.util.internal.MacAddressUtil;
22 import io.netty.util.internal.PlatformDependent;
23 import io.netty.util.internal.SystemPropertyUtil;
24 import io.netty.util.internal.logging.InternalLogger;
25 import io.netty.util.internal.logging.InternalLoggerFactory;
26
27 import java.lang.reflect.Method;
28 import java.util.Arrays;
29 import java.util.concurrent.ThreadLocalRandom;
30 import java.util.concurrent.atomic.AtomicInteger;
31
32 import static io.netty.util.internal.MacAddressUtil.defaultMachineId;
33 import static io.netty.util.internal.MacAddressUtil.parseMAC;
34 import static io.netty.util.internal.PlatformDependent.BIG_ENDIAN_NATIVE_ORDER;
35
36
37
38
39 public final class DefaultChannelId implements ChannelId {
40
41 private static final long serialVersionUID = 3884076183504074063L;
42
43 private static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultChannelId.class);
44 private static final byte[] MACHINE_ID;
45 private static final int PROCESS_ID_LEN = 4;
46 private static final int PROCESS_ID;
47 private static final int SEQUENCE_LEN = 4;
48 private static final int TIMESTAMP_LEN = 8;
49 private static final int RANDOM_LEN = 4;
50
51 private static final AtomicInteger nextSequence = new AtomicInteger();
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[] data;
192 private final int hashCode;
193
194 private transient String shortValue;
195 private transient String longValue;
196
197
198
199
200 DefaultChannelId(final byte[] machineId, final int processId, final int sequence,
201 final long timestamp, final int random) {
202 final byte[] data = new byte[machineId.length + PROCESS_ID_LEN + SEQUENCE_LEN + TIMESTAMP_LEN + RANDOM_LEN];
203 int i = 0;
204
205
206 System.arraycopy(machineId, 0, data, i, machineId.length);
207 i += machineId.length;
208
209
210 writeInt(data, i, processId);
211 i += Integer.BYTES;
212
213
214 writeInt(data, i, sequence);
215 i += Integer.BYTES;
216
217
218 writeLong(data, i, timestamp);
219 i += Long.BYTES;
220
221
222 writeInt(data, i, random);
223 i += Integer.BYTES;
224 assert i == data.length;
225
226 this.data = data;
227 hashCode = Arrays.hashCode(data);
228 }
229
230 private static void writeInt(byte[] data, int i, int value) {
231 if (PlatformDependent.isUnaligned()) {
232 PlatformDependent.putInt(data, i, BIG_ENDIAN_NATIVE_ORDER ? value : Integer.reverseBytes(value));
233 return;
234 }
235 data[i] = (byte) (value >>> 24);
236 data[i + 1] = (byte) (value >>> 16);
237 data[i + 2] = (byte) (value >>> 8);
238 data[i + 3] = (byte) value;
239 }
240
241 private static void writeLong(byte[] data, int i, long value) {
242 if (PlatformDependent.isUnaligned()) {
243 PlatformDependent.putLong(data, i, BIG_ENDIAN_NATIVE_ORDER ? value : Long.reverseBytes(value));
244 return;
245 }
246 data[i] = (byte) (value >>> 56);
247 data[i + 1] = (byte) (value >>> 48);
248 data[i + 2] = (byte) (value >>> 40);
249 data[i + 3] = (byte) (value >>> 32);
250 data[i + 4] = (byte) (value >>> 24);
251 data[i + 5] = (byte) (value >>> 16);
252 data[i + 6] = (byte) (value >>> 8);
253 data[i + 7] = (byte) value;
254 }
255
256 @Override
257 public String asShortText() {
258 String shortValue = this.shortValue;
259 if (shortValue == null) {
260 this.shortValue = shortValue = ByteBufUtil.hexDump(data, data.length - RANDOM_LEN, RANDOM_LEN);
261 }
262 return shortValue;
263 }
264
265 @Override
266 public String asLongText() {
267 String longValue = this.longValue;
268 if (longValue == null) {
269 this.longValue = longValue = newLongValue();
270 }
271 return longValue;
272 }
273
274 private String newLongValue() {
275 final StringBuilder buf = new StringBuilder(2 * data.length + 5);
276 final int machineIdLen = data.length - PROCESS_ID_LEN - SEQUENCE_LEN - TIMESTAMP_LEN - RANDOM_LEN;
277 int i = 0;
278 i = appendHexDumpField(buf, i, machineIdLen);
279 i = appendHexDumpField(buf, i, PROCESS_ID_LEN);
280 i = appendHexDumpField(buf, i, SEQUENCE_LEN);
281 i = appendHexDumpField(buf, i, TIMESTAMP_LEN);
282 i = appendHexDumpField(buf, i, RANDOM_LEN);
283 assert i == data.length;
284 return buf.substring(0, buf.length() - 1);
285 }
286
287 private int appendHexDumpField(StringBuilder buf, int i, int length) {
288 buf.append(ByteBufUtil.hexDump(data, i, length));
289 buf.append('-');
290 i += length;
291 return i;
292 }
293
294 @Override
295 public int hashCode() {
296 return hashCode;
297 }
298
299 @Override
300 public int compareTo(final ChannelId o) {
301 if (this == o) {
302
303 return 0;
304 }
305 if (o instanceof DefaultChannelId) {
306
307 final byte[] otherData = ((DefaultChannelId) o).data;
308 int len1 = data.length;
309 int len2 = otherData.length;
310 int len = Math.min(len1, len2);
311
312 for (int k = 0; k < len; k++) {
313 byte x = data[k];
314 byte y = otherData[k];
315 if (x != y) {
316
317 return (x & 0xff) - (y & 0xff);
318 }
319 }
320 return len1 - len2;
321 }
322
323 return asLongText().compareTo(o.asLongText());
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 && Arrays.equals(data, other.data);
336 }
337
338 @Override
339 public String toString() {
340 return asShortText();
341 }
342 }