1212import  org .elasticsearch .common .settings .Settings ;
1313import  org .elasticsearch .common .util .concurrent .EsExecutors ;
1414
15+ import  java .io .IOException ;
16+ import  java .nio .file .Files ;
17+ import  java .nio .file .Path ;
1518import  java .util .List ;
1619import  java .util .Map ;
17- import  java .util .stream .Collectors ;
1820import  java .util .stream .Stream ;
1921
2022final  class  SystemJvmOptions  {
2123
2224    static  List <String > systemJvmOptions (Settings  nodeSettings , final  Map <String , String > sysprops ) {
2325        String  distroType  = sysprops .get ("es.distribution.type" );
2426        boolean  isHotspot  = sysprops .getOrDefault ("sun.management.compiler" , "" ).contains ("HotSpot" );
25- 
26-         return  Stream .concat (
27+          boolean   useEntitlements  =  Boolean . parseBoolean ( sysprops . getOrDefault ( "es.entitlements.enabled" ,  "false" )); 
28+         return  Stream .of (
2729            Stream .of (
2830                /* 
2931                 * Cache ttl in seconds for positive DNS lookups noting that this overrides the JDK security property 
@@ -35,8 +37,6 @@ static List<String> systemJvmOptions(Settings nodeSettings, final Map<String, St
3537                 * networkaddress.cache.negative ttl; set to -1 to cache forever. 
3638                 */ 
3739                "-Des.networkaddress.cache.negative.ttl=10" ,
38-                 // Allow to set the security manager. 
39-                 "-Djava.security.manager=allow" ,
4040                // pre-touch JVM emory pages during initialization 
4141                "-XX:+AlwaysPreTouch" ,
4242                // explicitly set the stack size 
@@ -61,15 +61,17 @@ static List<String> systemJvmOptions(Settings nodeSettings, final Map<String, St
6161                "-Dlog4j2.disable.jmx=true" ,
6262                "-Dlog4j2.formatMsgNoLookups=true" ,
6363                "-Djava.locale.providers=CLDR" ,
64-                 maybeEnableNativeAccess (),
65-                 maybeOverrideDockerCgroup (distroType ),
66-                 maybeSetActiveProcessorCount (nodeSettings ),
67-                 setReplayFile (distroType , isHotspot ),
6864                // Pass through distribution type 
6965                "-Des.distribution.type="  + distroType 
7066            ),
71-             maybeWorkaroundG1Bug ()
72-         ).filter (e  -> e .isEmpty () == false ).collect (Collectors .toList ());
67+             maybeEnableNativeAccess (),
68+             maybeOverrideDockerCgroup (distroType ),
69+             maybeSetActiveProcessorCount (nodeSettings ),
70+             maybeSetReplayFile (distroType , isHotspot ),
71+             maybeWorkaroundG1Bug (),
72+             maybeAllowSecurityManager (),
73+             maybeAttachEntitlementAgent (useEntitlements )
74+         ).flatMap (s  -> s ).toList ();
7375    }
7476
7577    /* 
@@ -86,42 +88,42 @@ static List<String> systemJvmOptions(Settings nodeSettings, final Map<String, St
8688     * that cgroup statistics are available for the container this process 
8789     * will run in. 
8890     */ 
89-     private  static  String  maybeOverrideDockerCgroup (String  distroType ) {
91+     private  static  Stream < String >  maybeOverrideDockerCgroup (String  distroType ) {
9092        if  ("docker" .equals (distroType )) {
91-             return  "-Des.cgroups.hierarchy.override=/" ;
93+             return  Stream . of ( "-Des.cgroups.hierarchy.override=/" ) ;
9294        }
93-         return  "" ;
95+         return  Stream . empty () ;
9496    }
9597
96-     private  static  String   setReplayFile (String  distroType , boolean  isHotspot ) {
98+     private  static  Stream < String >  maybeSetReplayFile (String  distroType , boolean  isHotspot ) {
9799        if  (isHotspot  == false ) {
98100            // the replay file option is only guaranteed for hotspot vms 
99-             return  "" ;
101+             return  Stream . empty () ;
100102        }
101103        String  replayDir  = "logs" ;
102104        if  ("rpm" .equals (distroType ) || "deb" .equals (distroType )) {
103105            replayDir  = "/var/log/elasticsearch" ;
104106        }
105-         return  "-XX:ReplayDataFile="  + replayDir  + "/replay_pid%p.log" ;
107+         return  Stream . of ( "-XX:ReplayDataFile="  + replayDir  + "/replay_pid%p.log" ) ;
106108    }
107109
108110    /* 
109111     * node.processors determines thread pool sizes for Elasticsearch. When it 
110112     * is set, we need to also tell the JVM to respect a different value 
111113     */ 
112-     private  static  String  maybeSetActiveProcessorCount (Settings  nodeSettings ) {
114+     private  static  Stream < String >  maybeSetActiveProcessorCount (Settings  nodeSettings ) {
113115        if  (EsExecutors .NODE_PROCESSORS_SETTING .exists (nodeSettings )) {
114116            int  allocated  = EsExecutors .allocatedProcessors (nodeSettings );
115-             return  "-XX:ActiveProcessorCount="  + allocated ;
117+             return  Stream . of ( "-XX:ActiveProcessorCount="  + allocated ) ;
116118        }
117-         return  "" ;
119+         return  Stream . empty () ;
118120    }
119121
120-     private  static  String  maybeEnableNativeAccess () {
122+     private  static  Stream < String >  maybeEnableNativeAccess () {
121123        if  (Runtime .version ().feature () >= 21 ) {
122-             return  "--enable-native-access=org.elasticsearch.nativeaccess,org.apache.lucene.core" ;
124+             return  Stream . of ( "--enable-native-access=org.elasticsearch.nativeaccess,org.apache.lucene.core" ) ;
123125        }
124-         return  "" ;
126+         return  Stream . empty () ;
125127    }
126128
127129    /* 
@@ -134,4 +136,37 @@ private static Stream<String> maybeWorkaroundG1Bug() {
134136        }
135137        return  Stream .of ();
136138    }
139+ 
140+     private  static  Stream <String > maybeAllowSecurityManager () {
141+         // Will become conditional on useEntitlements once entitlements can run without SM 
142+         return  Stream .of ("-Djava.security.manager=allow" );
143+     }
144+ 
145+     private  static  Stream <String > maybeAttachEntitlementAgent (boolean  useEntitlements ) {
146+         if  (useEntitlements  == false ) {
147+             return  Stream .empty ();
148+         }
149+ 
150+         Path  dir  = Path .of ("lib" , "entitlement-bridge" );
151+         if  (Files .exists (dir ) == false ) {
152+             throw  new  IllegalStateException ("Directory for entitlement bridge jar does not exist: "  + dir );
153+         }
154+         String  bridgeJar ;
155+         try  (var  s  = Files .list (dir )) {
156+             var  candidates  = s .limit (2 ).toList ();
157+             if  (candidates .size () != 1 ) {
158+                 throw  new  IllegalStateException ("Expected one jar in "  + dir  + "; found "  + candidates .size ());
159+             }
160+             bridgeJar  = candidates .get (0 ).toString ();
161+         } catch  (IOException  e ) {
162+             throw  new  IllegalStateException ("Failed to list entitlement jars in: "  + dir , e );
163+         }
164+         return  Stream .of (
165+             "-Des.entitlements.enabled=true" ,
166+             "-XX:+EnableDynamicAgentLoading" ,
167+             "-Djdk.attach.allowAttachSelf=true" ,
168+             "--patch-module=java.base="  + bridgeJar ,
169+             "--add-exports=java.base/org.elasticsearch.entitlement.bridge=org.elasticsearch.entitlement" 
170+         );
171+     }
137172}
0 commit comments