Coverage Report - com.allanbank.mongodb.bson.element.ObjectId
 
Classes in this File Line Coverage Branch Coverage Complexity
ObjectId
87%
94/107
100%
22/22
1.733
 
 1  
 /*
 2  
  * #%L
 3  
  * ObjectId.java - mongodb-async-driver - Allanbank Consulting, Inc.
 4  
  * %%
 5  
  * Copyright (C) 2011 - 2014 Allanbank Consulting, Inc.
 6  
  * %%
 7  
  * Licensed under the Apache License, Version 2.0 (the "License");
 8  
  * you may not use this file except in compliance with the License.
 9  
  * You may obtain a copy of the License at
 10  
  * 
 11  
  *      http://www.apache.org/licenses/LICENSE-2.0
 12  
  * 
 13  
  * Unless required by applicable law or agreed to in writing, software
 14  
  * distributed under the License is distributed on an "AS IS" BASIS,
 15  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 16  
  * See the License for the specific language governing permissions and
 17  
  * limitations under the License.
 18  
  * #L%
 19  
  */
 20  
 package com.allanbank.mongodb.bson.element;
 21  
 
 22  
 import java.io.Serializable;
 23  
 import java.lang.management.ManagementFactory;
 24  
 import java.lang.management.RuntimeMXBean;
 25  
 import java.net.InetAddress;
 26  
 import java.net.NetworkInterface;
 27  
 import java.security.MessageDigest;
 28  
 import java.security.SecureRandom;
 29  
 import java.util.Enumeration;
 30  
 import java.util.concurrent.TimeUnit;
 31  
 import java.util.concurrent.atomic.AtomicLong;
 32  
 
 33  
 import com.allanbank.mongodb.bson.io.EndianUtils;
 34  
 import com.allanbank.mongodb.util.IOUtils;
 35  
 
 36  
 /**
 37  
  * An Object Id.
 38  
  * 
 39  
  * @api.yes This class is part of the driver's API. Public and protected members
 40  
  *          will be deprecated for at least 1 non-bugfix release (version
 41  
  *          numbers are <major>.<minor>.<bugfix>) before being
 42  
  *          removed or modified.
 43  
  * @copyright 2011-2013, Allanbank Consulting, Inc., All Rights Reserved
 44  
  */
 45  0
 public class ObjectId implements Serializable, Comparable<ObjectId> {
 46  
 
 47  
     /** The current process's machine id. */
 48  
     public static final long MACHINE_ID;
 49  
 
 50  
     /** The counter to add to the machine id. */
 51  
     private static final AtomicLong COUNTER;
 52  
 
 53  
     /** Serialization version for the class. */
 54  
     private static final long serialVersionUID = -3035334151717895487L;
 55  
 
 56  
     static {
 57  1
         long value = 0;
 58  1
         final SecureRandom rand = new SecureRandom();
 59  
         try {
 60  1
             boolean foundIface = true;
 61  1
             final MessageDigest md5 = MessageDigest.getInstance("MD5");
 62  
 
 63  
             try {
 64  1
                 final Enumeration<NetworkInterface> ifaces = NetworkInterface
 65  
                         .getNetworkInterfaces();
 66  5
                 while (ifaces.hasMoreElements()) {
 67  
                     try {
 68  4
                         final NetworkInterface iface = ifaces.nextElement();
 69  
 
 70  4
                         if (!iface.isLoopback()) {
 71  3
                             md5.update(iface.getHardwareAddress());
 72  3
                             foundIface = true;
 73  
                         }
 74  
                     }
 75  0
                     catch (final Throwable tryAnotherIface) {
 76  
                         // Noting to do. Try the next one.
 77  0
                         tryAnotherIface.hashCode(); // PMD - Shhhh.
 78  4
                     }
 79  
                 }
 80  
             }
 81  0
             catch (final Throwable tryTheHostName) {
 82  
                 // Nothing to do here. Fall through.
 83  0
                 tryTheHostName.hashCode(); // PMD - Shhhh.
 84  1
             }
 85  
 
 86  1
             if (!foundIface) {
 87  0
                 md5.update(InetAddress.getLocalHost().getHostName()
 88  
                         .getBytes("UTF8"));
 89  
             }
 90  
 
 91  1
             final byte[] hash = md5.digest();
 92  1
             value += (hash[0] & 0xFF);
 93  1
             value <<= Byte.SIZE;
 94  1
             value += (hash[1] & 0xFF);
 95  1
             value <<= Byte.SIZE;
 96  1
             value += (hash[2] & 0xFF);
 97  1
             value <<= Byte.SIZE;
 98  
         }
 99  0
         catch (final Throwable t) {
 100  
             // Degenerate to a random machine id.
 101  0
             for (int i = 0; i < 3; ++i) {
 102  0
                 value += rand.nextInt(256);
 103  0
                 value <<= Byte.SIZE;
 104  
             }
 105  1
         }
 106  
 
 107  
         // Try and find the process id from the runtime.
 108  
         int processId;
 109  
         try {
 110  1
             final RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
 111  1
             final String processName = runtime.getName();
 112  1
             final int atLoc = processName.indexOf('@');
 113  1
             if (atLoc >= 0) {
 114  1
                 final String pidString = processName.substring(0, atLoc);
 115  1
                 processId = Integer.parseInt(pidString);
 116  1
             }
 117  
             else {
 118  
                 // Degenerate to a random process id.
 119  0
                 processId = rand.nextInt();
 120  
             }
 121  
 
 122  
         }
 123  0
         catch (final Throwable t) {
 124  
             // Degenerate to a random process id.
 125  0
             processId = rand.nextInt();
 126  1
         }
 127  
 
 128  1
         value += ((processId >> Byte.SIZE) & 0xFF);
 129  1
         value <<= Byte.SIZE;
 130  1
         value += (processId & 0xFF);
 131  
 
 132  1
         MACHINE_ID = (value << 24);
 133  1
         COUNTER = new AtomicLong(rand.nextLong() & 0xFFFFFFL);
 134  1
     }
 135  
 
 136  
     /**
 137  
      * Generates the current timestamp value. This is the number of
 138  
      * <b>seconds</b> since the Unix Epoch.
 139  
      * 
 140  
      * @return The unique object id value.
 141  
      */
 142  
     private static int now() {
 143  201459
         return (int) TimeUnit.MILLISECONDS
 144  
                 .toSeconds(System.currentTimeMillis());
 145  
     }
 146  
 
 147  
     /**
 148  
      * Generates the current timestamp value. This is the number of
 149  
      * <b>seconds</b> since the Unix Epoch.
 150  
      * 
 151  
      * @return The unique object id value.
 152  
      */
 153  
     private static long processId() {
 154  201459
         return MACHINE_ID + (COUNTER.incrementAndGet() & 0xFFFFFFL);
 155  
     }
 156  
 
 157  
     /** The BSON Object Id's machine identifier. */
 158  
     private final long myMachineId;
 159  
 
 160  
     /** The BSON ObjectId's timestamp. */
 161  
     private final int myTimestamp;
 162  
 
 163  
     /**
 164  
      * Constructs a new {@link ObjectId}.
 165  
      */
 166  
     public ObjectId() {
 167  201459
         this(now(), processId());
 168  201459
     }
 169  
 
 170  
     /**
 171  
      * Constructs a new {@link ObjectId}.
 172  
      * 
 173  
      * @param timestamp
 174  
      *            The BSON Object Id timestamp.
 175  
      * @param machineId
 176  
      *            The BSON Object Id machine id.
 177  
      */
 178  201559
     public ObjectId(final int timestamp, final long machineId) {
 179  201559
         myTimestamp = timestamp;
 180  201559
         myMachineId = machineId;
 181  201559
     }
 182  
 
 183  
     /**
 184  
      * Constructs a new {@link ObjectId}.
 185  
      * 
 186  
      * @param hexBytes
 187  
      *            The hex encoded byte value.
 188  
      * @throws IllegalArgumentException
 189  
      *             If the hex encoded string is not 24 characters.
 190  
      */
 191  15
     public ObjectId(final String hexBytes) throws IllegalArgumentException {
 192  
 
 193  15
         if (hexBytes.length() != 24) {
 194  2
             throw new IllegalArgumentException(
 195  
                     "Invalid ObjectId value.  Must be a 24 character hex string.");
 196  
         }
 197  
 
 198  13
         final byte[] bytes = IOUtils.hexToBytes(hexBytes);
 199  12
         int timestamp = 0;
 200  60
         for (int i = 0; i < 4; ++i) {
 201  48
             int value = (bytes[i] & 0xFF);
 202  48
             value <<= (Byte.SIZE * i);
 203  48
             timestamp += value;
 204  
         }
 205  
 
 206  12
         long machineId = 0;
 207  108
         for (int i = 4; i < 12; ++i) {
 208  96
             long value = (bytes[i] & 0xFF);
 209  96
             value <<= (Byte.SIZE * (i - 4));
 210  96
             machineId += value;
 211  
         }
 212  
 
 213  12
         myTimestamp = EndianUtils.swap(timestamp);
 214  12
         myMachineId = EndianUtils.swap(machineId);
 215  12
     }
 216  
 
 217  
     /**
 218  
      * {@inheritDoc}
 219  
      * <p>
 220  
      * Overridden to compare the object ids based on the tuple (timestamp,
 221  
      * machineId).
 222  
      * </p>
 223  
      */
 224  
     @Override
 225  
     public int compareTo(final ObjectId other) {
 226  11
         int result = myTimestamp - other.myTimestamp;
 227  11
         if (result == 0) {
 228  9
             result = (myMachineId < other.myMachineId) ? -1
 229  
                     : ((myMachineId == other.myMachineId) ? 0 : 1);
 230  
         }
 231  11
         return result;
 232  
     }
 233  
 
 234  
     /**
 235  
      * Determines if the passed object is of this same type as this object and
 236  
      * if so that its fields are equal.
 237  
      * 
 238  
      * @param object
 239  
      *            The object to compare to.
 240  
      * 
 241  
      * @see java.lang.Object#equals(java.lang.Object)
 242  
      */
 243  
     @Override
 244  
     public boolean equals(final Object object) {
 245  7502
         boolean result = false;
 246  7502
         if (this == object) {
 247  1348
             result = true;
 248  
         }
 249  6154
         else if ((object != null) && (getClass() == object.getClass())) {
 250  6094
             final ObjectId other = (ObjectId) object;
 251  
 
 252  6094
             result = (myMachineId == other.myMachineId)
 253  
                     && (myTimestamp == other.myTimestamp);
 254  
         }
 255  7502
         return result;
 256  
     }
 257  
 
 258  
     /**
 259  
      * The low 3 byte value of the machine id.
 260  
      * 
 261  
      * @return The low 3 byte value of the machine id.
 262  
      */
 263  
     public int getCounterField() {
 264  1
         return (int) (myMachineId & 0xFFFFFFL);
 265  
     }
 266  
 
 267  
     /**
 268  
      * The lower 8 bytes of the object id. This is the machine identifier field
 269  
      * and counter.
 270  
      * 
 271  
      * @return The lower 8 bytes of the object id.
 272  
      */
 273  
     public long getMachineId() {
 274  2476
         return myMachineId;
 275  
     }
 276  
 
 277  
     /**
 278  
      * The upper 3 bytes in the machine id.
 279  
      * 
 280  
      * @return The upper 3 bytes of the machine id.
 281  
      */
 282  
     public int getMachineIdentifier() {
 283  1
         return (int) ((myMachineId >> 40) & 0xFFFFFFL);
 284  
     }
 285  
 
 286  
     /**
 287  
      * Middle 2 byte process id field from the machine id.
 288  
      * 
 289  
      * @return The middle 2 byte process id field from the machine id.
 290  
      */
 291  
     public int getPidField() {
 292  1
         return (int) ((myMachineId >> 24) & 0xFFFFL);
 293  
     }
 294  
 
 295  
     /**
 296  
      * The upper 4 bytes of the object id. This is the <b>seconds</b> since the
 297  
      * UNIX Epoch.
 298  
      * 
 299  
      * @return The upper 4 bytes of the object id.
 300  
      */
 301  
     public int getTimestamp() {
 302  2476
         return myTimestamp;
 303  
     }
 304  
 
 305  
     /**
 306  
      * Computes a reasonable hash code.
 307  
      * 
 308  
      * @return The hash code value.
 309  
      */
 310  
     @Override
 311  
     public int hashCode() {
 312  1566924
         int result = 0;
 313  1566924
         result += (int) ((myMachineId >> 32) & 0xFFFFFFFF);
 314  1566924
         result += (int) (myMachineId & 0xFFFFFFFF);
 315  1566924
         result += myTimestamp;
 316  1566924
         return result;
 317  
     }
 318  
 
 319  
     /**
 320  
      * Returns the HEX string form of the ObjectId.
 321  
      * 
 322  
      * @return The HEX string form of the ObjectId.
 323  
      */
 324  
     public String toHexString() {
 325  384
         final StringBuilder builder = new StringBuilder();
 326  384
         String hex = Integer.toHexString(myTimestamp);
 327  384
         builder.append("00000000".substring(hex.length()));
 328  384
         builder.append(hex);
 329  
 
 330  384
         hex = Long.toHexString(myMachineId);
 331  384
         builder.append("0000000000000000".substring(hex.length()));
 332  384
         builder.append(hex);
 333  384
         return builder.toString();
 334  
     }
 335  
 
 336  
     /**
 337  
      * String form of the object.
 338  
      * 
 339  
      * @return A human readable form of the object.
 340  
      * 
 341  
      * @see Object#toString()
 342  
      */
 343  
     @Override
 344  
     public String toString() {
 345  384
         final StringBuilder builder = new StringBuilder();
 346  
 
 347  384
         builder.append("ObjectId(");
 348  384
         builder.append(toHexString());
 349  384
         builder.append(")");
 350  
 
 351  384
         return builder.toString();
 352  
     }
 353  
 }