1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.waarp.common.guid;
22
23 import org.waarp.common.logging.SysErrLogger;
24 import org.waarp.common.utility.StringUtils;
25 import org.waarp.common.utility.SystemPropertyUtil;
26
27 import java.lang.reflect.Method;
28 import java.net.InetAddress;
29 import java.net.NetworkInterface;
30 import java.net.SocketException;
31 import java.net.UnknownHostException;
32 import java.security.AccessController;
33 import java.security.PrivilegedAction;
34 import java.util.Arrays;
35 import java.util.Enumeration;
36 import java.util.LinkedHashMap;
37 import java.util.Map;
38 import java.util.Map.Entry;
39 import java.util.regex.Pattern;
40
41 public final class JvmProcessId {
42
43
44
45 private static final Pattern MACHINE_ID_PATTERN =
46 Pattern.compile("^(?:[0-9a-fA-F][:-]?){6,8}$");
47 private static final int MACHINE_ID_LEN = 6;
48
49
50
51 private static final int MAX_PID = 4194304;
52
53
54
55 static final int JVMPID;
56 private static final Object[] EMPTY_OBJECTS = new Object[0];
57 private static final Class<?>[] EMPTY_CLASSES = new Class<?>[0];
58
59
60
61
62 static byte[] mac;
63 static int macInt;
64 static byte jvmId;
65
66 static {
67 JVMPID = jvmProcessId();
68 mac = macAddress();
69 macInt = macAddressAsInt();
70 jvmId = jvmInstanceId();
71 }
72
73 private JvmProcessId() {
74 }
75
76
77
78
79
80
81 public static byte jvmInstanceId() {
82 final long id = 31L * jvmProcessId() + macAddressAsInt();
83 return (byte) (Long.hashCode(id) & 0xFF);
84 }
85
86
87
88
89 public static int jvmProcessId() {
90
91
92 try {
93 final ClassLoader loader = getSystemClassLoader();
94 String value;
95 value =
96 jvmProcessIdManagementFactory(loader, EMPTY_OBJECTS, EMPTY_CLASSES);
97 final int atIndex = value.indexOf('@');
98 if (atIndex >= 0) {
99 value = value.substring(0, atIndex);
100 }
101 int processId = -1;
102 processId = parseProcessId(processId, value);
103 if (processId < 0 || processId > MAX_PID) {
104 processId = StringUtils.RANDOM.nextInt(MAX_PID + 1);
105 }
106 return processId;
107 } catch (final Throwable e) {
108 SysErrLogger.FAKE_LOGGER.syserr(e);
109 return StringUtils.RANDOM.nextInt(MAX_PID + 1);
110 }
111 }
112
113
114
115
116 public static byte[] macAddress() {
117 try {
118 byte[] machineId = null;
119 final String customMachineId =
120 SystemPropertyUtil.get("org.waarp.machineId");
121 if (customMachineId != null &&
122 MACHINE_ID_PATTERN.matcher(customMachineId).matches()) {
123 machineId = parseMachineId(customMachineId);
124 }
125
126 if (machineId == null) {
127 machineId = defaultMachineId();
128 }
129 return machineId;
130 } catch (final Throwable e) {
131 return StringUtils.getRandom(MACHINE_ID_LEN);
132 }
133 }
134
135
136
137
138 public static int macAddressAsInt() {
139 return (mac[3] & 0xFF) << 24 | (mac[2] & 0xFF) << 16 |
140 (mac[1] & 0xFF) << 8 | mac[0] & 0xFF;
141 }
142
143
144
145
146
147
148
149
150
151 public static synchronized void setMac(final byte[] mac) {
152 if (mac == null) {
153 JvmProcessId.mac = StringUtils.getRandom(MACHINE_ID_LEN);
154 } else {
155 JvmProcessId.mac = Arrays.copyOf(mac, MACHINE_ID_LEN);
156 for (int i = mac.length; i < MACHINE_ID_LEN; i++) {
157 JvmProcessId.mac[i] = (byte) StringUtils.RANDOM.nextInt(256);
158 }
159 }
160 macInt = macAddressAsInt();
161 }
162
163
164
165
166
167 private static int compareAddresses(final byte[] current,
168 final byte[] candidate) {
169 if (candidate == null) {
170 return 1;
171 }
172
173 if (candidate.length < 6) {
174 return 1;
175 }
176
177 boolean onlyZeroAndOne = true;
178 for (final byte b : candidate) {
179 if (b != 0 && b != 1) {
180 onlyZeroAndOne = false;
181 break;
182 }
183 }
184 if (onlyZeroAndOne) {
185 return 1;
186 }
187
188 if ((candidate[0] & 1) != 0) {
189 return 1;
190 }
191
192 if ((current[0] & 2) == 0) {
193 if ((candidate[0] & 2) == 0) {
194
195 return 0;
196 } else {
197
198 return 1;
199 }
200 } else {
201 if ((candidate[0] & 2) == 0) {
202
203 return -1;
204 } else {
205
206 return 0;
207 }
208 }
209 }
210
211
212
213
214
215
216 private static int compareAddresses(final InetAddress current,
217 final InetAddress candidate) {
218 return scoreAddress(current) - scoreAddress(candidate);
219 }
220
221 private static int scoreAddress(final InetAddress addr) {
222 if (addr.isAnyLocalAddress()) {
223 return 0;
224 }
225 if (addr.isMulticastAddress()) {
226 return 1;
227 }
228 if (addr.isLinkLocalAddress()) {
229 return 2;
230 }
231 if (addr.isSiteLocalAddress()) {
232 return 3;
233 }
234
235 return 4;
236 }
237
238
239 private static ClassLoader getSystemClassLoader() {
240 if (System.getSecurityManager() == null) {
241 return ClassLoader.getSystemClassLoader();
242 } else {
243 return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
244 @Override
245 public ClassLoader run() {
246 return ClassLoader.getSystemClassLoader();
247 }
248 });
249 }
250 }
251
252
253
254
255
256
257
258 private static int parseProcessId(final int oldProcessId,
259 final String customProcessId) {
260 int processId = oldProcessId;
261 try {
262 processId = Integer.parseInt(customProcessId);
263 } catch (final NumberFormatException e) {
264
265 }
266 if (processId < 0 || processId > MAX_PID) {
267 processId = -1;
268 }
269 return processId;
270 }
271
272
273
274
275
276
277
278
279 private static String jvmProcessIdManagementFactory(final ClassLoader loader,
280 final Object[] emptyObjects,
281 final Class<?>[] emptyClasses) {
282 String value;
283 try {
284
285
286 final Class<?> mgmtFactoryType =
287 Class.forName("java.lang.management.ManagementFactory", true, loader);
288 final Class<?> runtimeMxBeanType =
289 Class.forName("java.lang.management.RuntimeMXBean", true, loader);
290
291 final Method getRuntimeMXBean =
292 mgmtFactoryType.getMethod("getRuntimeMXBean", emptyClasses);
293 final Object bean = getRuntimeMXBean.invoke(null, emptyObjects);
294 final Method getName =
295 runtimeMxBeanType.getDeclaredMethod("getName", emptyClasses);
296 value = (String) getName.invoke(bean, emptyObjects);
297 } catch (final Exception e) {
298 SysErrLogger.FAKE_LOGGER.syserr(
299 "Unable to get PID, try another way: " + e.getMessage());
300
301 try {
302
303 final Class<?> processType =
304 Class.forName("android.os.Process", true, loader);
305 final Method myPid = processType.getMethod("myPid", emptyClasses);
306 value = myPid.invoke(null, emptyObjects).toString();
307 } catch (final Exception e2) {
308 SysErrLogger.FAKE_LOGGER.syserr(
309 "Unable to get PID: " + e2.getMessage());
310 value = "";
311 }
312 }
313 return value;
314 }
315
316 private static byte[] parseMachineId(String value) {
317
318 value = value.replaceAll("[:-]", "");
319
320 final byte[] machineId = new byte[MACHINE_ID_LEN];
321 for (int i = 0; i < value.length() && i < MACHINE_ID_LEN; i += 2) {
322 machineId[i] = (byte) Integer.parseInt(value.substring(i, i + 2), 16);
323 }
324
325 return machineId;
326 }
327
328 private static byte[] defaultMachineId() {
329
330 final byte[] notFound = { -1 };
331 byte[] bestMacAddr = notFound;
332 InetAddress bestInetAddr;
333 try {
334 bestInetAddr = InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 });
335 } catch (final UnknownHostException e) {
336
337 throw new IllegalArgumentException(e);
338 }
339
340
341 final Map<NetworkInterface, InetAddress> ifaces =
342 new LinkedHashMap<NetworkInterface, InetAddress>();
343 try {
344 for (final Enumeration<NetworkInterface> i =
345 NetworkInterface.getNetworkInterfaces(); i.hasMoreElements(); ) {
346 final NetworkInterface iface = i.nextElement();
347
348 final Enumeration<InetAddress> addrs = iface.getInetAddresses();
349 if (addrs.hasMoreElements()) {
350 final InetAddress a = addrs.nextElement();
351 if (!a.isLoopbackAddress()) {
352 ifaces.put(iface, a);
353 }
354 }
355 }
356 } catch (final SocketException ignored) {
357
358 }
359
360 for (final Entry<NetworkInterface, InetAddress> entry : ifaces.entrySet()) {
361 final NetworkInterface iface = entry.getKey();
362 final InetAddress inetAddr = entry.getValue();
363 if (iface.isVirtual()) {
364 continue;
365 }
366
367 final byte[] macAddr;
368 try {
369 macAddr = iface.getHardwareAddress();
370 } catch (final SocketException e) {
371 continue;
372 }
373
374 boolean replace = false;
375 int res = compareAddresses(bestMacAddr, macAddr);
376 if (res < 0) {
377
378 replace = true;
379 } else if (res == 0) {
380
381 res = compareAddresses(bestInetAddr, inetAddr);
382 if (res < 0) {
383
384 replace = true;
385 } else if (res == 0) {
386
387 if (bestMacAddr.length < macAddr.length) {
388 replace = true;
389 }
390 }
391 }
392
393 if (replace) {
394 bestMacAddr = macAddr;
395 bestInetAddr = inetAddr;
396 }
397 }
398
399 if (bestMacAddr == notFound) {
400 bestMacAddr = StringUtils.getRandom(MACHINE_ID_LEN);
401 }
402 return bestMacAddr;
403 }
404 }