1
+ component implements = " IStrategy" singleton {
2
+
3
+ property name = " murmur3" inject = " java:com.sangupta.murmur.Murmur3" ;
4
+
5
+ public boolean function isEnabled (
6
+ required struct parameters ,
7
+ required struct context
8
+ ) {
9
+ param arguments .parameters .stickiness = " default" ;
10
+ var stickinessId = resolveStickiness ( arguments .parameters .stickiness , arguments .context );
11
+ if ( ! len ( stickinessId ) ) {
12
+ return false ;
13
+ }
14
+ param arguments .parameters .rollout = 100 ;
15
+ var percentage = getPercentage ( arguments .parameters .rollout );
16
+ param arguments .parameters .groupId = " " ;
17
+ var norm = getNormalizedNumber ( stickinessId , arguments .parameters .groupId )
18
+ return norm <= percentage ;
19
+ }
20
+
21
+ private string function resolveStickiness ( required string stickinessKey , required struct context ) {
22
+ switch ( arguments .stickinessKey ) {
23
+ case " userId" :
24
+ return arguments .context .userId ;
25
+ case " sessionId" :
26
+ return arguments .context .sessionId
27
+ case " random" :
28
+ return randRange ( 1 , 100 );
29
+ case " default" :
30
+ default :
31
+ if ( arguments .context .keyExists ( " userId" ) && len ( arguments .context .userId ) ) {
32
+ return arguments .context .userId ;
33
+ }
34
+ if ( arguments .context .keyExists ( " sessionId" ) && len ( arguments .context .sessionId ) ) {
35
+ return arguments .context .sessionId ;
36
+ }
37
+ return randRange ( 1 , 100 );
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Takes a numeric string value and converts it to a integer between 0 and 100.
43
+ *
44
+ * returns 0 if the string is not numeric.
45
+ *
46
+ * @param percentage - A numeric string value
47
+ * @return a integer between 0 and 100
48
+ */
49
+ private numeric function getPercentage ( required any percentage ) {
50
+ return clamp ( 0 , isNumeric ( arguments .percentage ) ? arguments .percentage : 0 , 100 );
51
+ }
52
+
53
+ private numeric function clamp ( required numeric low , required numeric actual , required numeric high ) {
54
+ if ( arguments .actual < arguments .low ) {
55
+ return arguments .low ;
56
+ }
57
+ if ( arguments .actual > arguments .high ) {
58
+ return arguments .high ;
59
+ }
60
+ return arguments .actual ;
61
+ }
62
+
63
+ private numeric function getNormalizedNumber ( required string identifier , required string groupId , numeric no rmalizer = 100 ) {
64
+ var value = getStringBytes ( " #arguments .groupId #:#arguments .identifier #" );
65
+ var hash = calculateHash ( value );
66
+ return normalizeHash ( hash , normalizer );
67
+ }
68
+
69
+ private array function getStringBytes ( required string identifier ) {
70
+ return createObject ( " java" , " java.lang.String" ).init ( arguments .identifier ).getBytes ();
71
+ }
72
+
73
+ private any function calculateHash ( required array bytes ) {
74
+ var hash = variables .murmur3 .hash_x86_32 ( arguments .bytes , len ( arguments .bytes ), 0 );
75
+ return createObject ( " java" , " java.math.BigInteger" ).valueOf ( hash );
76
+ }
77
+
78
+ private numeric function normalizeHash ( required any hash , required numeric no rmalizer ) {
79
+ var normalizerBigInt = createObject ( " java" , " java.math.BigInteger" ).init ( normalizer )
80
+ return arguments .hash .remainder ( normalizerBigInt ) + 1 ;
81
+ }
82
+
83
+ }
0 commit comments