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