1 /***********************************************************************
2  * Copyright (c) 2013-2024 Commonwealth Computer Research, Inc.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Apache License, Version 2.0
5  * which accompanies this distribution and is available at
6  * http://www.opensource.org/licenses/apache2.0.php.
7  ***********************************************************************/
8 
9 package org.locationtech.geomesa.utils.uuid
10 
11 import org.geotools.api.feature.simple.{SimpleFeature, SimpleFeatureType}
12 import org.locationtech.geomesa.utils.index.ByteArrays
13 
14 import java.util.UUID
15 
16 /**
17  * Creates feature id based on current system time.
18  */
19 class IngestTimeFeatureIdGenerator extends FeatureIdGenerator {
20   override def createId(sft: SimpleFeatureType, sf: SimpleFeature): String =
21     TimeSortedUuidGenerator.createUuid().toString
22 }
23 
24 /**
25  * UUID generator that creates UUIDs that sort by creation time (useful for accumulo).
26  *
27  * Uses variant 2 (IETF) and version 4 (for random UUIDs, although it's not totally random).
28  * See https://en.wikipedia.org/wiki/Universally_unique_identifier#Variants_and_versions
29  *
30  * Using a version 1 (time based) UUID doesn't ensure uniqueness when running in different processes on
31  * the same machine (at least not without some complicated distributed locking), as MAC address
32  * (or IP address) is the unique factor.
33  */
34 object TimeSortedUuidGenerator extends RandomLsbUuidGenerator {
35 
36   /**
37    * Creates a UUID where the first 8 bytes are based on the current time and the second 8 bytes are
38    * based on a random number. This should provide uniqueness along with sorting by date.
39    *
40    * Doesn't support negative time values.
41    */
42   def createUuid(time: Long = System.currentTimeMillis()): UUID = {
43     val mostSigBits = timeBytes(time)
44     val leastSigBits = createRandomLsb()
45     new UUID(mostSigBits, leastSigBits)
46   }
47 
48   /**
49    * Creates the time based part of the uuid.
50    */
51   private def timeBytes(time: Long): Long = {
52     val array = getTempByteArray
53 
54     // write the time in a sorted fashion
55     // we drop the 4 most significant bits as we need 4 bits extra for the version
56     // this shouldn't matter as we use sys time, so we don't need to worry about negative numbers
57     array(0) = (time >> 52).asInstanceOf[Byte]
58     array(1) = (time >> 44).asInstanceOf[Byte]
59     array(2) = (time >> 36).asInstanceOf[Byte]
60     array(3) = (time >> 28).asInstanceOf[Byte]
61     array(4) = (time >> 20).asInstanceOf[Byte]
62     array(5) = (time >> 12).asInstanceOf[Byte]
63     array(6) = ((time >> 8) & 0x0f).asInstanceOf[Byte]  // the 0x0f clears the version bits
64     array(7) = time.asInstanceOf[Byte]
65 
66     // set the version number for the UUID
67     setVersion(array)
68 
69     ByteArrays.readLong(array)
70   }
71 }
Line Stmt Id Pos Tree Symbol Tests Code
21 16701 868 - 913 Apply java.util.UUID.toString TimeSortedUuidGenerator.createUuid(TimeSortedUuidGenerator.createUuid$default$1).toString()
43 16702 1847 - 1862 Apply org.locationtech.geomesa.utils.uuid.TimeSortedUuidGenerator.timeBytes TimeSortedUuidGenerator.this.timeBytes(time)
44 16703 1886 - 1903 Apply org.locationtech.geomesa.utils.uuid.RandomLsbUuidGenerator.createRandomLsb TimeSortedUuidGenerator.this.createRandomLsb()
45 16704 1908 - 1943 Apply java.util.UUID.<init> new java.util.UUID(mostSigBits, leastSigBits)
52 16705 2069 - 2085 Select org.locationtech.geomesa.utils.uuid.Version4UuidGenerator.getTempByteArray TimeSortedUuidGenerator.this.getTempByteArray
57 16706 2320 - 2321 Literal <nosymbol> 0
57 16707 2334 - 2336 Literal <nosymbol> 52
57 16708 2325 - 2356 TypeApply scala.Any.asInstanceOf time.>>(52).asInstanceOf[Byte]
57 16709 2314 - 2356 Apply scala.Array.update array.update(0, time.>>(52).asInstanceOf[Byte])
58 16710 2367 - 2368 Literal <nosymbol> 1
58 16711 2381 - 2383 Literal <nosymbol> 44
58 16712 2372 - 2403 TypeApply scala.Any.asInstanceOf time.>>(44).asInstanceOf[Byte]
58 16713 2361 - 2403 Apply scala.Array.update array.update(1, time.>>(44).asInstanceOf[Byte])
59 16714 2414 - 2415 Literal <nosymbol> 2
59 16715 2428 - 2430 Literal <nosymbol> 36
59 16716 2419 - 2450 TypeApply scala.Any.asInstanceOf time.>>(36).asInstanceOf[Byte]
59 16717 2408 - 2450 Apply scala.Array.update array.update(2, time.>>(36).asInstanceOf[Byte])
60 16718 2461 - 2462 Literal <nosymbol> 3
60 16719 2475 - 2477 Literal <nosymbol> 28
60 16720 2466 - 2497 TypeApply scala.Any.asInstanceOf time.>>(28).asInstanceOf[Byte]
60 16721 2455 - 2497 Apply scala.Array.update array.update(3, time.>>(28).asInstanceOf[Byte])
61 16722 2508 - 2509 Literal <nosymbol> 4
61 16723 2522 - 2524 Literal <nosymbol> 20
61 16724 2513 - 2544 TypeApply scala.Any.asInstanceOf time.>>(20).asInstanceOf[Byte]
61 16725 2502 - 2544 Apply scala.Array.update array.update(4, time.>>(20).asInstanceOf[Byte])
62 16726 2555 - 2556 Literal <nosymbol> 5
62 16727 2569 - 2571 Literal <nosymbol> 12
62 16728 2560 - 2591 TypeApply scala.Any.asInstanceOf time.>>(12).asInstanceOf[Byte]
62 16729 2549 - 2591 Apply scala.Array.update array.update(5, time.>>(12).asInstanceOf[Byte])
63 16730 2602 - 2603 Literal <nosymbol> 6
63 16731 2617 - 2618 Literal <nosymbol> 8
63 16732 2622 - 2626 Literal <nosymbol> 15
63 16733 2607 - 2646 TypeApply scala.Any.asInstanceOf time.>>(8).&(15).asInstanceOf[Byte]
63 16734 2596 - 2646 Apply scala.Array.update array.update(6, time.>>(8).&(15).asInstanceOf[Byte])
64 16735 2694 - 2695 Literal <nosymbol> 7
64 16736 2699 - 2722 TypeApply scala.Any.asInstanceOf time.asInstanceOf[Byte]
64 16737 2688 - 2722 Apply scala.Array.update array.update(7, time.asInstanceOf[Byte])
67 16738 2771 - 2788 Apply org.locationtech.geomesa.utils.uuid.Version4UuidGenerator.setVersion TimeSortedUuidGenerator.this.setVersion(array)
69 16739 2794 - 2820 Apply org.locationtech.geomesa.utils.index.ByteArrays.readLong org.locationtech.geomesa.utils.index.ByteArrays.readLong(array, org.locationtech.geomesa.utils.index.ByteArrays.readLong$default$2)