/* * Copyright (c) 2008-2014 MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.bson.types; import java.net.NetworkInterface; import java.nio.ByteBuffer; import java.util.Date; import java.util.Enumeration; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; /** *

A globally unique identifier for objects.

* *

Consists of 12 bytes, divided as follows:

* * * * * * * * *
ObjectID layout
01234567891011
timemachine pidinc
* *

Instances of this class are immutable.

* * @mongodb.driver.manual core/object-id ObjectId */ public class ObjectId implements Comparable , java.io.Serializable { private static final long serialVersionUID = -4415279469780082174L; static final Logger LOGGER = Logger.getLogger( "org.bson.ObjectId" ); /** * Gets a new object id. * * @return the new id */ public static ObjectId get(){ return new ObjectId(); } /** *

Creates an ObjectId using time, machine and inc values. The Java driver used to create all ObjectIds this way, but it does not * match the ObjectId specification, which requires four values, not * three. This major release of the Java driver conforms to the specification, but still supports clients that are relying on the * behavior of the previous major release by providing this explicit factory method that takes three parameters instead of four.

* *

Ordinary users of the driver will not need this method. It's only for those that have written there own BSON decoders.

* *

NOTE: This will not break any application that use ObjectIds. The 12-byte representation will be round-trippable from old to new * driver releases.

* * @param time time in seconds * @param machine machine ID * @param inc incremental value * @return a new {@code ObjectId} created from the given values * @since 2.12.0 */ public static ObjectId createFromLegacyFormat(final int time, final int machine, final int inc) { return new ObjectId(time, machine, inc); } /** * Checks if a string could be an {@code ObjectId}. * * @param s a potential ObjectId as a String. * @return whether the string could be an object id * @throws IllegalArgumentException if hexString is null */ public static boolean isValid( String s ){ if ( s == null ) return false; final int len = s.length(); if ( len != 24 ) return false; for ( int i=0; i= '0' && c <= '9' ) continue; if ( c >= 'a' && c <= 'f' ) continue; if ( c >= 'A' && c <= 'F' ) continue; return false; } return true; } /** * Turn an object into an {@code ObjectId}, if possible. Strings will be converted into {@code ObjectId}s, if possible, and * {@code ObjectId}s will be cast and returned. Passing in {@code null} returns {@code null}. * * @param o the object to convert * @return an {@code ObjectId} if it can be massaged, null otherwise * @deprecated This method is NOT a part of public API and will be dropped in 3.x versions. */ @Deprecated public static ObjectId massageToObjectId( Object o ){ if ( o == null ) return null; if ( o instanceof ObjectId ) return (ObjectId)o; if ( o instanceof String ){ String s = o.toString(); if ( isValid( s ) ) return new ObjectId( s ); } return null; } /** * Constructs a new instance using the given date. * * @param time the date */ public ObjectId(Date time) { this(time, _genmachine, _nextInc.getAndIncrement()); } /** * Constructs a new instances using the given date and counter. * * @param time the date * @param inc the counter * @throws IllegalArgumentException if the high order byte of counter is not zero */ public ObjectId(Date time, int inc) { this(time, _genmachine, inc); } /** * Constructs an ObjectId using time, machine and inc values. The Java driver has done it this way for a long time, but it does not * match the ObjectId specification, which requires four values, not * three. The next major release of the Java driver will conform to this specification, but will still need to support clients that are * relying on the current behavior. To that end, this constructor is now deprecated in favor of the more explicit factory method * ObjectId#createFromLegacyFormat(int, int, int)}, and in the next major release this constructor will be removed. * * @param time the date * @param machine the machine identifier * @param inc the counter * @see ObjectId#createFromLegacyFormat(int, int, int) * @deprecated {@code ObjectId}'s constructed this way do not conform to the * ObjectId specification. Please * use {@link org.bson.types.ObjectId#ObjectId(byte[])} or {@link ObjectId#createFromLegacyFormat(int, int, int)} instead. */ @Deprecated public ObjectId( Date time , int machine , int inc ){ _time = (int)(time.getTime() / 1000); _machine = machine; _inc = inc; _new = false; } /** Creates a new instance from a string. * @param s the string to convert * @throws IllegalArgumentException if the string is not a valid id */ public ObjectId( String s ){ this( s , false ); } /** * Constructs a new instance of {@code ObjectId} from a string. * @param s the string representation of ObjectId. Can contains only [0-9]|[a-f]|[A-F] characters. * @param babble if {@code true} - convert to 'babble' objectId format * * @deprecated 'babble' format is deprecated. Please use {@link #ObjectId(String)} instead. */ @Deprecated public ObjectId( String s , boolean babble ){ if ( ! isValid( s ) ) throw new IllegalArgumentException( "invalid ObjectId [" + s + "]" ); if ( babble ) s = babbleToMongod( s ); byte b[] = new byte[12]; for ( int i=0; iObjectId specification, * which requires four values, not three. The next major release of the Java driver will conform to this specification, * but we will still need to support clients that are relying on the current behavior. To that end, * this constructor is now deprecated in favor of the more explicit factory method ObjectId#createFromLegacyFormat(int, int, int)}, * and in the next major release this constructor will be removed. * * @param time time in seconds * @param machine machine ID * @param inc incremental value * @see ObjectId#createFromLegacyFormat(int, int, int) * @deprecated {@code ObjectId}'s constructed this way do not conform to * the ObjectId specification. * Please use {@link org.bson.types.ObjectId#ObjectId(byte[])} or * {@link ObjectId#createFromLegacyFormat(int, int, int)} instead. */ @Deprecated public ObjectId(int time, int machine, int inc) { _time = time; _machine = machine; _inc = inc; _new = false; } /** * Create a new object id. */ public ObjectId(){ _time = (int) (System.currentTimeMillis() / 1000); _machine = _genmachine; _inc = _nextInc.getAndIncrement(); _new = true; } @Override public int hashCode(){ int x = _time; x += ( _machine * 111 ); x += ( _inc * 17 ); return x; } @Override public boolean equals( Object o ){ if ( this == o ) return true; ObjectId other = massageToObjectId( o ); if ( other == null ) return false; return _time == other._time && _machine == other._machine && _inc == other._inc; } /** * @deprecated 'babble' format is deprecated and will be removed in 3.0. Please use {@link #toHexString()} instead. */ @SuppressWarnings("JavaDoc") @Deprecated public String toStringBabble(){ return babbleToMongod( toStringMongod() ); } /** * Converts this instance into a 24-byte hexadecimal string representation. * * @return a string representation of the ObjectId in hexadecimal format */ public String toHexString() { final StringBuilder buf = new StringBuilder(24); for (final byte b : toByteArray()) { buf.append(String.format("%02x", b & 0xff)); } return buf.toString(); } /** * @return a string representation of the ObjectId in hexadecimal format * @see ObjectId#toHexString() * @deprecated use {@link #toHexString()} */ @Deprecated public String toStringMongod(){ byte b[] = toByteArray(); StringBuilder buf = new StringBuilder(24); for ( int i=0; i=0; i-- ) buf.append( _pos( b , i ) ); for ( int i=11; i>=8; i-- ) buf.append( _pos( b , i ) ); return buf.toString(); } public String toString(){ return toStringMongod(); } int _compareUnsigned( int i , int j ){ long li = 0xFFFFFFFFL; li = i & li; long lj = 0xFFFFFFFFL; lj = j & lj; long diff = li - lj; if (diff < Integer.MIN_VALUE) return Integer.MIN_VALUE; if (diff > Integer.MAX_VALUE) return Integer.MAX_VALUE; return (int) diff; } public int compareTo( ObjectId id ){ if ( id == null ) return -1; int x = _compareUnsigned( _time , id._time ); if ( x != 0 ) return x; x = _compareUnsigned( _machine , id._machine ); if ( x != 0 ) return x; return _compareUnsigned( _inc , id._inc ); } /** * Gets the timestamp (number of seconds since the Unix epoch). * * @return the timestamp */ public int getTimestamp() { return _time; } /** * Gets the timestamp as a {@code Date} instance. * * @return the Date */ public Date getDate() { return new Date(_time * 1000L); } /** * Gets the time of this instance, in milliseconds. * * @deprecated Use #getDate instead * @return the time component of this ID in milliseconds */ @Deprecated public long getTime(){ return _time * 1000L; } /** * Gets the time of this ID, in seconds. * * @deprecated Use #getTimestamp instead * @return the time component of this ID in seconds */ @Deprecated public int getTimeSecond() { return _time; } /** * Gets the counter. * * @return the counter * @deprecated Please use the {@link #toByteArray()} instead. */ @Deprecated public int getInc() { return _inc; } /** * Gets the timestamp. * * @return the timestamp * @deprecated Please use {@link #getTimestamp()} ()} instead. */ @Deprecated public int _time(){ return _time; } /** * Gets the machine identifier. * * @return the machine identifier * @see #createFromLegacyFormat(int, int, int) * @deprecated Please use {@code #toByteArray()} instead. */ @Deprecated public int getMachine() { return _machine; } /** * Gets the machine identifier. * * @return the machine identifier * @see #createFromLegacyFormat(int, int, int) * @deprecated Please use {@link #toByteArray()} instead. */ @Deprecated public int _machine(){ return _machine; } /** * Gets the counter. * * @return the counter * @see #createFromLegacyFormat(int, int, int) * @deprecated Please use {@link #toByteArray()} instead. */ @Deprecated public int _inc(){ return _inc; } /** * @deprecated 'new' flag breaks the immutability of the {@code ObjectId} class * and will be dropped in 3.x versions of the driver */ @Deprecated public boolean isNew() { return _new; } /** * @deprecated 'new' flag breaks the immutability of the {@code ObjectId} class * and will be dropped in 3.x versions of the driver */ @Deprecated public void notNew(){ _new = false; } /** * Gets the machine identifier. * * @return the machine identifier * @see #createFromLegacyFormat(int, int, int) * @deprecated */ @Deprecated public static int getGenMachineId() { return _genmachine; } /** * Gets the current value of the auto-incrementing counter. * * @return the current counter value. */ public static int getCurrentCounter() { return _nextInc.get(); } /** * Gets the current value of the auto-incrementing counter. * * @deprecated Please use {@link #getCurrentCounter()} instead. */ @Deprecated public static int getCurrentInc() { return _nextInc.get(); } final int _time; final int _machine; final int _inc; boolean _new; /** * @deprecated This method is NOT a part of public API and will be dropped in 3.x versions. */ @Deprecated public static int _flip( int x ){ int z = 0; z |= ( ( x << 24 ) & 0xFF000000 ); z |= ( ( x << 8 ) & 0x00FF0000 ); z |= ( ( x >> 8 ) & 0x0000FF00 ); z |= ( ( x >> 24 ) & 0x000000FF ); return z; } private static AtomicInteger _nextInc = new AtomicInteger( (new java.util.Random()).nextInt() ); private static final int _genmachine; static { try { // build a 2-byte machine piece based on NICs info int machinePiece; { try { StringBuilder sb = new StringBuilder(); Enumeration e = NetworkInterface.getNetworkInterfaces(); while ( e.hasMoreElements() ){ NetworkInterface ni = e.nextElement(); sb.append( ni.toString() ); } machinePiece = sb.toString().hashCode() << 16; } catch (Throwable e) { // exception sometimes happens with IBM JVM, use random LOGGER.log(Level.WARNING, e.getMessage(), e); machinePiece = (new Random().nextInt()) << 16; } LOGGER.fine( "machine piece post: " + Integer.toHexString( machinePiece ) ); } // add a 2 byte process piece. It must represent not only the JVM but the class loader. // Since static var belong to class loader there could be collisions otherwise final int processPiece; { int processId = new java.util.Random().nextInt(); try { processId = java.lang.management.ManagementFactory.getRuntimeMXBean().getName().hashCode(); } catch ( Throwable t ){ } ClassLoader loader = ObjectId.class.getClassLoader(); int loaderId = loader != null ? System.identityHashCode(loader) : 0; StringBuilder sb = new StringBuilder(); sb.append(Integer.toHexString(processId)); sb.append(Integer.toHexString(loaderId)); processPiece = sb.toString().hashCode() & 0xFFFF; LOGGER.fine( "process piece: " + Integer.toHexString( processPiece ) ); } _genmachine = machinePiece | processPiece; LOGGER.fine( "machine : " + Integer.toHexString( _genmachine ) ); } catch ( Exception e ){ throw new RuntimeException( e ); } } }