11/*
2+ * Elemental
3+ * Copyright (C) 2024, Evolved Binary Ltd
4+ *
5+ 6+ * https://www.evolvedbinary.com | https://www.elemental.xyz
7+ *
8+ * This library is free software; you can redistribute it and/or
9+ * modify it under the terms of the GNU Lesser General Public
10+ * License as published by the Free Software Foundation; version 2.1.
11+ *
12+ * This library is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+ * Lesser General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU Lesser General Public
18+ * License along with this library; if not, write to the Free Software
19+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20+ *
21+ * NOTE: Parts of this file contain code from 'The eXist-db Authors'.
22+ * The original license header is included below.
23+ *
24+ * =====================================================================
25+ *
226 * eXist-db Open Source Native XML Database
327 * Copyright (C) 2001 The eXist-db Authors
428 *
2347
2448import org .exist .start .CompatibleJavaVersionCheck ;
2549import org .exist .start .StartException ;
50+ import org .exist .util .FileUtils ;
2651import org .exist .util .SystemExitCodes ;
2752import se .softhouse .jargo .Argument ;
2853import se .softhouse .jargo .ArgumentException ;
2954import se .softhouse .jargo .CommandLineParser ;
3055import se .softhouse .jargo .ParsedArguments ;
3156
57+ import javax .annotation .Nullable ;
3258import javax .management .Attribute ;
3359import javax .management .AttributeList ;
3460import javax .management .AttributeNotFoundException ;
3965import javax .management .ObjectName ;
4066import javax .management .ReflectionException ;
4167import javax .management .openmbean .CompositeData ;
68+ import javax .management .openmbean .CompositeDataSupport ;
4269import javax .management .openmbean .TabularData ;
4370import javax .management .remote .JMXConnector ;
4471import javax .management .remote .JMXConnectorFactory ;
@@ -78,9 +105,9 @@ public void memoryStats() {
78105 final CompositeData composite = (CompositeData ) connection .getAttribute (name , "HeapMemoryUsage" );
79106 if (composite != null ) {
80107 echo ("\n MEMORY:" );
81- echo (String .format ("Current heap: %,12d k Committed memory: %,12d k " ,
82- ((Long )composite .get ("used" )) / 1024 , ((Long )composite .get ("committed" )) / 1024 ));
83- echo (String .format ("Max memory: %,12d k " , ((Long )composite .get ("max" )) / 1024 ));
108+ echo (String .format ("Current heap: %12s Committed memory: %12s " ,
109+ FileUtils . humanSize ((Long )composite .get ("used" )), FileUtils . humanSize ((Long )composite .get ("committed" ))));
110+ echo (String .format ("Max memory: %12s " , FileUtils . humanSize ((Long )composite .get ("max" ))));
84111 }
85112 } catch (final Exception e ) {
86113 error (e );
@@ -91,23 +118,24 @@ public void instanceStats() {
91118 try {
92119 echo ("\n INSTANCE:" );
93120 final ObjectName name = new ObjectName ("org.exist.management." + instance + ":type=Database" );
121+ echo (String .format ("%25s: %10s" , "Name" , instance ));
94122 final Long memReserved = (Long ) connection .getAttribute (name , "ReservedMem" );
95- echo (String .format ("%25s: %10d k " , "Reserved memory" , memReserved / 1024 ));
123+ echo (String .format ("%25s: %10s " , "Reserved memory" , FileUtils . humanSize ( memReserved ) ));
96124 final Long memCache = (Long ) connection .getAttribute (name , "CacheMem" );
97- echo (String .format ("%25s: %10d k " , "Cache memory" , memCache / 1024 ));
125+ echo (String .format ("%25s: %10s " , "Cache memory" , FileUtils . humanSize ( memCache ) ));
98126 final Long memCollCache = (Long ) connection .getAttribute (name , "CollectionCacheMem" );
99- echo (String .format ("%25s: %10d k " , "Collection cache memory" , memCollCache / 1024 ));
127+ echo (String .format ("%25s: %10s " , "Collection cache memory" , FileUtils . humanSize ( memCollCache ) ));
100128
101- final String cols [] = { "MaxBrokers" , "AvailableBrokers" , "ActiveBrokers" };
129+ final String [] cols = { "MaxBrokers" , "AvailableBrokers" , "ActiveBrokers" };
102130 echo (String .format ("\n %17s %17s %17s" , cols [0 ], cols [1 ], cols [2 ]));
103131 final AttributeList attrs = connection .getAttributes (name , cols );
104- final Object values [] = getValues (attrs );
105- echo (String .format ("%17d %17d %17d" , values [0 ], values [1 ], values [2 ]));
132+ final Object [] values = getValues (attrs );
133+ echo (String .format ("%17d %17d %17d" , ( Integer ) values [0 ], ( Integer ) values [1 ], ( Integer ) values [2 ]));
106134
107135 final TabularData table = (TabularData ) connection .getAttribute (name , "ActiveBrokersMap" );
108- if (table .size () > 0 ) {
136+ // if (table.size() > 0) {
109137 echo ("\n Currently active threads:" );
110- }
138+ // }
111139
112140 for (Object o : table .values ()) {
113141 final CompositeData data = (CompositeData ) o ;
@@ -121,59 +149,100 @@ public void instanceStats() {
121149 public void cacheStats () {
122150 try {
123151 ObjectName name = new ObjectName ("org.exist.management." + instance + ":type=CacheManager" );
124- String cols [] = { "MaxTotal" , "CurrentSize" };
152+ String [] cols = { "MaxTotal" , "CurrentSize" };
125153 AttributeList attrs = connection .getAttributes (name , cols );
126- Object values [] = getValues (attrs );
154+ Object [] values = getValues (attrs );
127155 echo (String .format ("\n CACHE [%8d pages max. / %8d pages allocated]" , values [0 ], values [1 ]));
128156
129157 final Set <ObjectName > beans = connection .queryNames (new ObjectName ("org.exist.management." + instance + ":type=CacheManager.Cache,*" ), null );
130- cols = new String [] {"Type" , "FileName " , "Size" , "Used" , "Hits" , "Fails" };
158+ cols = new String [] {"Type" , "CacheName " , "Size" , "Used" , "Hits" , "Fails" };
131159 echo (String .format ("%10s %20s %10s %10s %10s %10s" , cols [0 ], cols [1 ], cols [2 ], cols [3 ], cols [4 ], cols [5 ]));
132- for (ObjectName bean : beans ) {
160+ for (final ObjectName bean : beans ) {
133161 name = bean ;
134162 attrs = connection .getAttributes (name , cols );
135163 values = getValues (attrs );
136164 echo (String .format ("%10s %20s %,10d %,10d %,10d %,10d" , values [0 ], values [1 ], values [2 ], values [3 ], values [4 ], values [5 ]));
137165 }
138-
166+
139167 echo ("" );
140- name = new ObjectName ("org.exist.management." + instance + ":type=CollectionCacheManager " );
141- cols = new String [] { "MaxTotal " , "CurrentSize " };
168+ name = new ObjectName ("org.exist.management." + instance + ":type=CollectionCache " );
169+ cols = new String [] { "MaxCacheSize " , "Statistics " };
142170 attrs = connection .getAttributes (name , cols );
143171 values = getValues (attrs );
144- echo (String .format ("Collection Cache: %10d k max / %10d k allocated" ,
145- ((Long )values [0 ] / 1024 ), ((Long )values [1 ] / 1024 )));
172+
173+
174+ echo (String .format ("COLLECTION CACHE: [%10s max]" , FileUtils .humanSize ((Integer )values [0 ])));
175+
176+ cols = new String [] {"Hit Count" , "Miss Count" , "Load Success Count" , "Load Failure Count" , "Total Load Time" , "Eviction Count" , "Eviction Weight" };
177+ echo (String .format ("%10s %20s %20s %20s %20s %20s %20s" , cols [0 ], cols [1 ], cols [2 ], cols [3 ], cols [4 ], cols [5 ], cols [6 ]));
178+ final CompositeDataSupport statistics = (CompositeDataSupport ) values [1 ];
179+ echo (String .format ("%10d %20d %20d %20d %20d %20d %20d" , (Long )statistics .get ("hitCount" ), (Long )statistics .get ("missCount" ), (Long )statistics .get ("loadSuccessCount" ), (Long )statistics .get ("loadFailureCount" ), (Long )statistics .get ("totalLoadTime" ), (Long )statistics .get ("evictionCount" ), (Long )statistics .get ("evictionWeight" )));
180+
146181 } catch (final Exception e ) {
147182 error (e );
148183 }
149184 }
150185
151186 public void lockTable () {
152- echo ("\n List of threads currently waiting for a lock:" );
187+ echo ("\n List of threads attempting to acquire a lock:" );
153188 echo ("-----------------------------------------------" );
154189 try {
155- final TabularData table = (TabularData ) connection .getAttribute (new ObjectName ("org.exist.management:type=LockManager" ), "WaitingThreads" );
156- for (Object o : table .values ()) {
157- final CompositeData data = (CompositeData ) o ;
158- echo ("Thread " + data .get ("waitingThread" ));
159- echo (String .format ("%20s: %s" , "Lock type" , data .get ("lockType" )));
160- echo (String .format ("%20s: %s" , "Lock mode" , data .get ("lockMode" )));
161- echo (String .format ("%20s: %s" , "Lock id" , data .get ("id" )));
162- echo (String .format ("%20s: %s" , "Held by" , Arrays .toString ((String []) data .get ("owner" ))));
163- final String [] readers = (String []) data .get ("waitingForRead" );
164- if (readers .length > 0 ) {
165- echo (String .format ("%20s: %s" , "Wait for read" , Arrays .toString (readers )));
166- }
167- final String [] writers = (String []) data .get ("waitingForWrite" );
168- if (writers .length > 0 ) {
169- echo (String .format ("%20s: %s" , "Wait for write" , Arrays .toString (writers )));
170- }
171- }
190+ final TabularData table = (TabularData ) connection .getAttribute (new ObjectName ("org.exist.management." + instance + ":type=LockTable" ), "Attempting" );
191+ printLockTable (table );
192+ } catch (final MBeanException | AttributeNotFoundException | InstanceNotFoundException | ReflectionException | IOException | MalformedObjectNameException e ) {
193+ error (e );
194+ }
195+
196+ echo ("" );
197+
198+ echo ("\n List of threads holding a lock:" );
199+ echo ("-----------------------------------------------" );
200+ try {
201+ final TabularData table = (TabularData ) connection .getAttribute (new ObjectName ("org.exist.management." + instance + ":type=LockTable" ), "Acquired" );
202+ printLockTable (table );
172203 } catch (final MBeanException | AttributeNotFoundException | InstanceNotFoundException | ReflectionException | IOException | MalformedObjectNameException e ) {
173204 error (e );
174205 }
175206 }
176207
208+ private void printLockTable (final TabularData table ) {
209+ for (final Object tv : table .values ()) {
210+ final CompositeData data = (CompositeData ) tv ;
211+
212+ final String resourceUri = (String ) data .get ("key" );
213+ echo ("URI: " + resourceUri );
214+
215+ final TabularData valueData = (TabularData ) data .get ("value" );
216+ for (final Object vdv : valueData .values ()) {
217+ final CompositeData cvdv = (CompositeData ) vdv ;
218+ final String resourceType = (String ) cvdv .get ("key" );
219+ echo (String .format ("%20s: %s" , "Lock type" , resourceType ));
220+
221+ final TabularData resourceData = (TabularData ) cvdv .get ("value" );
222+
223+ for (final Object rvdv : resourceData .values ()) {
224+ final CompositeData crvdv = (CompositeData ) rvdv ;
225+ final String lockMode = (String ) crvdv .get ("key" );
226+ echo (String .format ("%20s: %s" , "Lock type" , lockMode ));
227+
228+ final TabularData lockData = (TabularData ) crvdv .get ("value" );
229+
230+ for (final Object lrvdv : lockData .values ()) {
231+ final CompositeData clrvdv = (CompositeData ) lrvdv ;
232+ final String owner = (String ) clrvdv .get ("key" );
233+ echo (String .format ("%20s: %s" , "Held by" , owner ));
234+
235+ final CompositeData ownerData = (CompositeData ) clrvdv .get ("value" );
236+
237+ final Integer holdCount = (Integer ) ownerData .get ("count" );
238+ echo (String .format ("%20s: %d" , "Hold count" , holdCount ));
239+ }
240+ }
241+
242+ }
243+ }
244+ }
245+
177246 public void sanityReport () {
178247 echo ("\n Sanity report" );
179248 echo ("-----------------------------------------------" );
@@ -188,12 +257,27 @@ public void sanityReport() {
188257 if (lastCheckStart != null && lastCheckEnd != null )
189258 {echo (String .format ("%22s: %dms" , "Check took" , (lastCheckEnd .getTime () - lastCheckStart .getTime ())));}
190259
191- final TabularData table = (TabularData )
192- connection .getAttribute (name , "Errors" );
193- for (Object o : table .values ()) {
194- final CompositeData data = (CompositeData ) o ;
195- echo (String .format ("%22s: %s" , "Error code" , data .get ("errcode" )));
196- echo (String .format ("%22s: %s" , "Description" , data .get ("description" )));
260+ @ Nullable final Object result = connection .getAttribute (name , "Errors" );
261+ if (result != null ) {
262+ @ Nullable final CompositeData table ;
263+ if (result .getClass ().isArray ()) {
264+ final CompositeData [] tables = ((CompositeData []) result );
265+ if (tables .length > 0 ) {
266+ table = tables [0 ];
267+ } else {
268+ table = null ;
269+ }
270+ } else {
271+ table = (CompositeData ) result ;
272+ }
273+
274+ if (table != null ) {
275+ for (final Object o : table .values ()) {
276+ final CompositeData data = (CompositeData ) o ;
277+ echo (String .format ("%22s: %s" , "Error code" , data .get ("errcode" )));
278+ echo (String .format ("%22s: %s" , "Description" , data .get ("description" )));
279+ }
280+ }
197281 }
198282 } catch (final MBeanException | AttributeNotFoundException | InstanceNotFoundException | ReflectionException | IOException | MalformedObjectNameException e ) {
199283 error (e );
@@ -230,10 +314,10 @@ public void jobReport() {
230314 }
231315 }
232316
233- private Object [] getValues (AttributeList attribs ) {
317+ private Object [] getValues (final AttributeList attribs ) {
234318 final Object [] v = new Object [attribs .size ()];
235319 for (int i = 0 ; i < attribs .size (); i ++) {
236- v [i ] = ((Attribute )attribs .get (i )).getValue ();
320+ v [i ] = ((Attribute ) attribs .get (i )).getValue ();
237321 }
238322 return v ;
239323 }
@@ -256,17 +340,14 @@ private void error(Exception e) {
256340 /* connection arguments */
257341 private static final Argument <String > addressArg = stringArgument ("-a" , "--address" )
258342 .description ("RMI address of the server" )
259- .required ()
260343 .defaultValue ("localhost" )
261344 .build ();
262345 private static final Argument <Integer > portArg = integerArgument ("-p" , "--port" )
263346 .description ("RMI port of the server" )
264- .required ()
265347 .defaultValue (DEFAULT_PORT )
266348 .build ();
267349 private static final Argument <String > instanceArg = stringArgument ("-i" , "--instance" )
268350 .description ("The ID of the database instance to connect to" )
269- .required ()
270351 .defaultValue ("exist" )
271352 .build ();
272353 private static final Argument <Integer > waitArg = integerArgument ("-w" , "--wait" )
0 commit comments