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