View Javadoc
1   /*
2    * Copyright 2013 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
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   * The default {@link ChannelId} implementation.
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       * Returns a new {@link DefaultChannelId} instance.
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                  // Malformed input.
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             // Invoke java.lang.management.ManagementFactory.getRuntimeMXBean().getName()
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                 // Invoke android.os.Process.myPid()
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             // value did not contain an integer.
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         // machineId
166         System.arraycopy(MACHINE_ID, 0, data, i, MACHINE_ID.length);
167         i += MACHINE_ID.length;
168 
169         // processId
170         i = writeInt(i, PROCESS_ID);
171 
172         // sequence
173         i = writeInt(i, nextSequence.getAndIncrement());
174 
175         // timestamp (kind of)
176         i = writeLong(i, Long.reverse(System.nanoTime()) ^ System.currentTimeMillis());
177 
178         // random
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             // short circuit
252             return 0;
253         }
254         if (o instanceof DefaultChannelId) {
255             // lexicographic comparison
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                     // treat these as unsigned bytes for comparison
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 }