1 package com.jt.boot.utils; 2 3 import com.google.common.base.Objects; 4 5 import java.net.NetworkInterface; 6 import java.nio.ByteBuffer; 7 import java.util.Date; 8 import java.util.Enumeration; 9 import java.util.Random; 10 import java.util.concurrent.atomic.AtomicInteger; 11 import java.util.logging.Level; 12 import java.util.logging.Logger; 13 14 /** 15 * <p>A globally unique identifier for objects.</p> 16 * <p/> 17 * <p>Consists of 12 bytes, divided as follows:</p> 18 * <table border="1"> 19 * <caption>ObjectID layout</caption> 20 * <tr> 21 * <td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td><td>10</td><td>11</td> 22 * </tr> 23 * <tr> 24 * <td colspan="4">time</td><td colspan="3">machine</td> <td colspan="2">pid</td><td colspan="3">inc</td> 25 * </tr> 26 * </table> 27 * <p/> 28 * <p>Instances of this class are immutable.</p> 29 */ 30 public class ObjectId implements Comparable<ObjectId>, java.io.Serializable { 31 32 private final int _time; 33 private final int _machine; 34 private final int _inc; 35 private boolean _new; 36 private static final int _genmachine; 37 38 private static AtomicInteger _nextInc = new AtomicInteger((new Random()).nextInt()); 39 40 private static final long serialVersionUID = -4415279469780082174L; 41 42 private static final Logger LOGGER = Logger.getLogger("org.bson.ObjectId"); 43 44 /** 45 * Create a new object id. 46 */ 47 public ObjectId() { 48 _time = (int) (System.currentTimeMillis() / 1000); 49 _machine = _genmachine; 50 _inc = _nextInc.getAndIncrement(); 51 _new = true; 52 } 53 54 public static String id() { 55 return get().toHexString(); 56 } 57 58 /** 59 * Gets a new object id. 60 * 61 * @return the new id 62 */ 63 public static ObjectId get() { 64 return new ObjectId(); 65 } 66 67 /** 68 * Checks if a string could be an {@code ObjectId}. 69 * 70 * @param s a potential ObjectId as a String. 71 * @return whether the string could be an object id 72 * @throws IllegalArgumentException if hexString is null 73 */ 74 public static boolean isValid(String s) { 75 if (s == null) 76 return false; 77 78 final int len = s.length(); 79 if (len != 24) 80 return false; 81 82 for (int i = 0; i < len; i++) { 83 char c = s.charAt(i); 84 if (c >= '0' && c <= '9') 85 continue; 86 if (c >= 'a' && c <= 'f') 87 continue; 88 if (c >= 'A' && c <= 'F') 89 continue; 90 91 return false; 92 } 93 94 return true; 95 } 96 97 98 /** 99 * Converts this instance into a 24-byte hexadecimal string representation. 100 * 101 * @return a string representation of the ObjectId in hexadecimal format 102 */ 103 public String toHexString() { 104 final StringBuilder buf = new StringBuilder(24); 105 for (final byte b : toByteArray()) { 106 buf.append(String.format("%02x", b & 0xff)); 107 } 108 return buf.toString(); 109 } 110 111 /** 112 * Convert to a byte array. Note that the numbers are stored in big-endian order. 113 * 114 * @return the byte array 115 */ 116 public byte[] toByteArray() { 117 byte b[] = new byte[12]; 118 ByteBuffer bb = ByteBuffer.wrap(b); 119 // by default BB is big endian like we need 120 bb.putInt(_time); 121 bb.putInt(_machine); 122 bb.putInt(_inc); 123 return b; 124 } 125 126 private int _compareUnsigned(int i, int j) { 127 long li = 0xFFFFFFFFL; 128 li = i & li; 129 long lj = 0xFFFFFFFFL; 130 lj = j & lj; 131 long diff = li - lj; 132 if (diff < Integer.MIN_VALUE) 133 return Integer.MIN_VALUE; 134 if (diff > Integer.MAX_VALUE) 135 return Integer.MAX_VALUE; 136 return (int) diff; 137 } 138 139 public int compareTo(ObjectId id) { 140 if (id == null) 141 return -1; 142 143 int x = _compareUnsigned(_time, id._time); 144 if (x != 0) 145 return x; 146 147 x = _compareUnsigned(_machine, id._machine); 148 if (x != 0) 149 return x; 150 151 return _compareUnsigned(_inc, id._inc); 152 } 153 154 /** 155 * Gets the timestamp (number of seconds since the Unix epoch). 156 * 157 * @return the timestamp 158 */ 159 public int getTimestamp() { 160 return _time; 161 } 162 163 /** 164 * Gets the timestamp as a {@code Date} instance. 165 * 166 * @return the Date 167 */ 168 public Date getDate() { 169 return new Date(_time * 1000L); 170 } 171 172 173 /** 174 * Gets the current value of the auto-incrementing counter. 175 * 176 * @return the current counter value. 177 */ 178 public static int getCurrentCounter() { 179 return _nextInc.get(); 180 } 181 182 183 static { 184 185 try { 186 // build a 2-byte machine piece based on NICs info 187 int machinePiece; 188 { 189 try { 190 StringBuilder sb = new StringBuilder(); 191 Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces(); 192 while (e.hasMoreElements()) { 193 NetworkInterface ni = e.nextElement(); 194 sb.append(ni.toString()); 195 } 196 machinePiece = sb.toString().hashCode() << 16; 197 } catch (Throwable e) { 198 // exception sometimes happens with IBM JVM, use random 199 LOGGER.log(Level.WARNING, e.getMessage(), e); 200 machinePiece = (new Random().nextInt()) << 16; 201 } 202 LOGGER.fine("machine piece post: " + Integer.toHexString(machinePiece)); 203 } 204 205 // add a 2 byte process piece. It must represent not only the JVM but the class loader. 206 // Since static var belong to class loader there could be collisions otherwise 207 final int processPiece; 208 { 209 int processId = new Random().nextInt(); 210 try { 211 processId = java.lang.management.ManagementFactory.getRuntimeMXBean().getName().hashCode(); 212 } catch (Throwable t) { 213 } 214 215 ClassLoader loader = ObjectId.class.getClassLoader(); 216 int loaderId = loader != null ? System.identityHashCode(loader) : 0; 217 218 StringBuilder sb = new StringBuilder(); 219 sb.append(Integer.toHexString(processId)); 220 sb.append(Integer.toHexString(loaderId)); 221 processPiece = sb.toString().hashCode() & 0xFFFF; 222 LOGGER.fine("process piece: " + Integer.toHexString(processPiece)); 223 } 224 225 _genmachine = machinePiece | processPiece; 226 LOGGER.fine("machine : " + Integer.toHexString(_genmachine)); 227 } catch (Exception e) { 228 throw new RuntimeException(e); 229 } 230 231 } 232 233 @Override 234 public boolean equals(Object o) { 235 if (this == o) return true; 236 if (o == null || getClass() != o.getClass()) return false; 237 238 ObjectId that = (ObjectId) o; 239 240 return Objects.equal(this.serialVersionUID, that.serialVersionUID) && 241 Objects.equal(this.LOGGER, that.LOGGER) && 242 Objects.equal(this._time, that._time) && 243 Objects.equal(this._machine, that._machine) && 244 Objects.equal(this._inc, that._inc) && 245 Objects.equal(this._new, that._new) && 246 Objects.equal(this._nextInc, that._nextInc) && 247 Objects.equal(this._genmachine, that._genmachine); 248 } 249 250 @Override 251 public int hashCode() { 252 return Objects.hashCode(serialVersionUID, LOGGER, _time, _machine, _inc, _new, 253 _nextInc, _genmachine); 254 } 255 256 public static void main(String[] args) { 257 System.out.println(new ObjectId().toHexString()); 258 System.out.println(new ObjectId().toHexString()); 259 System.out.println(new ObjectId().toHexString()); 260 } 261 }