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 }