1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty5.channel;
18
19 import io.netty5.buffer.BufferUtil;
20 import io.netty5.util.internal.MacAddressUtil;
21 import io.netty5.util.internal.SystemPropertyUtil;
22 import io.netty5.util.internal.logging.InternalLogger;
23 import io.netty5.util.internal.logging.InternalLoggerFactory;
24
25 import java.util.Arrays;
26 import java.util.concurrent.ThreadLocalRandom;
27 import java.util.concurrent.atomic.AtomicInteger;
28
29 import static io.netty5.util.internal.MacAddressUtil.defaultMachineId;
30 import static io.netty5.util.internal.MacAddressUtil.parseMAC;
31
32
33
34
35 public final class DefaultChannelId implements ChannelId {
36
37 private static final long serialVersionUID = 3884076183504074063L;
38
39 private static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultChannelId.class);
40 private static final byte[] MACHINE_ID;
41 private static final int PROCESS_ID_LEN = 4;
42 private static final int PROCESS_ID;
43 private static final int SEQUENCE_LEN = 4;
44 private static final int TIMESTAMP_LEN = 8;
45 private static final int RANDOM_LEN = 4;
46
47 private static final AtomicInteger nextSequence = new AtomicInteger();
48
49
50
51
52 public static DefaultChannelId newInstance() {
53 return new DefaultChannelId();
54 }
55
56 static {
57 int processId = -1;
58 String customProcessId = SystemPropertyUtil.get("io.netty5.processId");
59 if (customProcessId != null) {
60 try {
61 processId = Integer.parseInt(customProcessId);
62 } catch (NumberFormatException e) {
63
64 }
65
66 if (processId < 0) {
67 processId = -1;
68 logger.warn("-Dio.netty5.processId: {} (malformed)", customProcessId);
69 } else if (logger.isDebugEnabled()) {
70 logger.debug("-Dio.netty5.processId: {} (user-set)", processId);
71 }
72 }
73
74 if (processId < 0) {
75 processId = defaultProcessId();
76 if (logger.isDebugEnabled()) {
77 logger.debug("-Dio.netty5.processId: {} (auto-detected)", processId);
78 }
79 }
80
81 PROCESS_ID = processId;
82
83 byte[] machineId = null;
84 String customMachineId = SystemPropertyUtil.get("io.netty5.machineId");
85 if (customMachineId != null) {
86 try {
87 machineId = parseMAC(customMachineId);
88 } catch (Exception e) {
89 logger.warn("-Dio.netty5.machineId: {} (malformed)", customMachineId, e);
90 }
91 if (machineId != null) {
92 logger.debug("-Dio.netty5.machineId: {} (user-set)", customMachineId);
93 }
94 }
95
96 if (machineId == null) {
97 machineId = defaultMachineId();
98 if (logger.isDebugEnabled()) {
99 logger.debug("-Dio.netty5.machineId: {} (auto-detected)", MacAddressUtil.formatAddress(machineId));
100 }
101 }
102
103 MACHINE_ID = machineId;
104 }
105
106 private static int defaultProcessId() {
107 long pid = ProcessHandle.current().pid();
108 if (pid > Integer.MAX_VALUE || pid < Integer.MIN_VALUE) {
109 logger.warn("Current process ID exceeds int range: " + pid);
110 pid = ThreadLocalRandom.current().nextInt();
111 }
112 return (int) pid;
113 }
114
115 private final byte[] data;
116 private final int hashCode;
117
118 private transient String shortValue;
119 private transient String longValue;
120
121 private DefaultChannelId() {
122 data = new byte[MACHINE_ID.length + PROCESS_ID_LEN + SEQUENCE_LEN + TIMESTAMP_LEN + RANDOM_LEN];
123 int i = 0;
124
125
126 System.arraycopy(MACHINE_ID, 0, data, i, MACHINE_ID.length);
127 i += MACHINE_ID.length;
128
129
130 i = writeInt(i, PROCESS_ID);
131
132
133 i = writeInt(i, nextSequence.getAndIncrement());
134
135
136 i = writeLong(i, Long.reverse(System.nanoTime()) ^ System.currentTimeMillis());
137
138
139 int random = ThreadLocalRandom.current().nextInt();
140 i = writeInt(i, random);
141 assert i == data.length;
142
143 hashCode = Arrays.hashCode(data);
144 }
145
146 private int writeInt(int i, int value) {
147 data[i ++] = (byte) (value >>> 24);
148 data[i ++] = (byte) (value >>> 16);
149 data[i ++] = (byte) (value >>> 8);
150 data[i ++] = (byte) value;
151 return i;
152 }
153
154 private int writeLong(int i, long value) {
155 data[i ++] = (byte) (value >>> 56);
156 data[i ++] = (byte) (value >>> 48);
157 data[i ++] = (byte) (value >>> 40);
158 data[i ++] = (byte) (value >>> 32);
159 data[i ++] = (byte) (value >>> 24);
160 data[i ++] = (byte) (value >>> 16);
161 data[i ++] = (byte) (value >>> 8);
162 data[i ++] = (byte) value;
163 return i;
164 }
165
166 @Override
167 public String asShortText() {
168 String shortValue = this.shortValue;
169 if (shortValue == null) {
170 this.shortValue = shortValue = BufferUtil.hexDump(data, data.length - RANDOM_LEN, RANDOM_LEN);
171 }
172 return shortValue;
173 }
174
175 @Override
176 public String asLongText() {
177 String longValue = this.longValue;
178 if (longValue == null) {
179 this.longValue = longValue = newLongValue();
180 }
181 return longValue;
182 }
183
184 private String newLongValue() {
185 StringBuilder buf = new StringBuilder(2 * data.length + 5);
186 int i = 0;
187 i = appendHexDumpField(buf, i, MACHINE_ID.length);
188 i = appendHexDumpField(buf, i, PROCESS_ID_LEN);
189 i = appendHexDumpField(buf, i, SEQUENCE_LEN);
190 i = appendHexDumpField(buf, i, TIMESTAMP_LEN);
191 i = appendHexDumpField(buf, i, RANDOM_LEN);
192 assert i == data.length;
193 return buf.substring(0, buf.length() - 1);
194 }
195
196 private int appendHexDumpField(StringBuilder buf, int i, int length) {
197 buf.append(BufferUtil.hexDump(data, i, length));
198 buf.append('-');
199 i += length;
200 return i;
201 }
202
203 @Override
204 public int hashCode() {
205 return hashCode;
206 }
207
208 @Override
209 public int compareTo(final ChannelId o) {
210 if (this == o) {
211
212 return 0;
213 }
214 if (o instanceof DefaultChannelId) {
215
216 final byte[] otherData = ((DefaultChannelId) o).data;
217 int len1 = data.length;
218 int len2 = otherData.length;
219 int len = Math.min(len1, len2);
220
221 for (int k = 0; k < len; k++) {
222 byte x = data[k];
223 byte y = otherData[k];
224 if (x != y) {
225
226 return (x & 0xff) - (y & 0xff);
227 }
228 }
229 return len1 - len2;
230 }
231
232 return asLongText().compareTo(o.asLongText());
233 }
234
235 @Override
236 public boolean equals(Object obj) {
237 if (this == obj) {
238 return true;
239 }
240 if (!(obj instanceof DefaultChannelId)) {
241 return false;
242 }
243 DefaultChannelId other = (DefaultChannelId) obj;
244 return hashCode == other.hashCode && Arrays.equals(data, other.data);
245 }
246
247 @Override
248 public String toString() {
249 return asShortText();
250 }
251 }