• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

evolvedbinary / elemental / 982

29 Apr 2025 08:34PM UTC coverage: 56.409% (+0.007%) from 56.402%
982

push

circleci

adamretter
[feature] Improve README.md badges

28451 of 55847 branches covered (50.94%)

Branch coverage included in aggregate %.

77468 of 131924 relevant lines covered (58.72%)

0.59 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/exist-core/src/main/java/org/exist/management/client/JMXClient.java
1
/*
2
 * Elemental
3
 * Copyright (C) 2024, Evolved Binary Ltd
4
 *
5
 * admin@evolvedbinary.com
6
 * https://www.evolvedbinary.com | https://www.elemental.xyz
7
 *
8
 * Use of this software is governed by the Business Source License 1.1
9
 * included in the LICENSE file and at www.mariadb.com/bsl11.
10
 *
11
 * Change Date: 2028-04-27
12
 *
13
 * On the date above, in accordance with the Business Source License, use
14
 * of this software will be governed by the Apache License, Version 2.0.
15
 *
16
 * Additional Use Grant: Production use of the Licensed Work for a permitted
17
 * purpose. A Permitted Purpose is any purpose other than a Competing Use.
18
 * A Competing Use means making the Software available to others in a commercial
19
 * product or service that: substitutes for the Software; substitutes for any
20
 * other product or service we offer using the Software that exists as of the
21
 * date we make the Software available; or offers the same or substantially
22
 * similar functionality as the Software.
23
 *
24
 * NOTE: Parts of this file contain code from 'The eXist-db Authors'.
25
 *       The original license header is included below.
26
 *
27
 * =====================================================================
28
 *
29
 * eXist-db Open Source Native XML Database
30
 * Copyright (C) 2001 The eXist-db Authors
31
 *
32
 * info@exist-db.org
33
 * http://www.exist-db.org
34
 *
35
 * This library is free software; you can redistribute it and/or
36
 * modify it under the terms of the GNU Lesser General Public
37
 * License as published by the Free Software Foundation; either
38
 * version 2.1 of the License, or (at your option) any later version.
39
 *
40
 * This library is distributed in the hope that it will be useful,
41
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
42
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
43
 * Lesser General Public License for more details.
44
 *
45
 * You should have received a copy of the GNU Lesser General Public
46
 * License along with this library; if not, write to the Free Software
47
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
48
 */
49
package org.exist.management.client;
50

51
import org.exist.start.CompatibleJavaVersionCheck;
52
import org.exist.start.StartException;
53
import org.exist.util.OSUtil;
54
import org.exist.util.SystemExitCodes;
55
import se.softhouse.jargo.Argument;
56
import se.softhouse.jargo.ArgumentException;
57
import se.softhouse.jargo.CommandLineParser;
58
import se.softhouse.jargo.ParsedArguments;
59

60
import javax.management.Attribute;
61
import javax.management.AttributeList;
62
import javax.management.AttributeNotFoundException;
63
import javax.management.InstanceNotFoundException;
64
import javax.management.MBeanException;
65
import javax.management.MBeanServerConnection;
66
import javax.management.MalformedObjectNameException;
67
import javax.management.ObjectName;
68
import javax.management.ReflectionException;
69
import javax.management.openmbean.CompositeData;
70
import javax.management.openmbean.TabularData;
71
import javax.management.remote.JMXConnector;
72
import javax.management.remote.JMXConnectorFactory;
73
import javax.management.remote.JMXServiceURL;
74
import java.io.IOException;
75
import java.util.*;
76

77
import static org.exist.util.ArgumentUtil.getBool;
78
import static se.softhouse.jargo.Arguments.*;
79

80
/**
81
 */
82
public class JMXClient {
83

84
    private MBeanServerConnection connection;
85
    private String instance;
86

87
    public JMXClient(String instanceName) {
×
88
        this.instance = instanceName;
×
89
    }
×
90

91
    public void connect(String address,int port) throws IOException {
92
        final JMXServiceURL url =
×
93
                new JMXServiceURL("service:jmx:rmi:///jndi/rmi://"+address+":" + port + "/jmxrmi");
×
94
        final Map<String, String[]> env = new HashMap<>();
×
95
        final String[] creds = {"guest", "guest"};
×
96
        env.put(JMXConnector.CREDENTIALS, creds);
×
97

98
        final JMXConnector jmxc = JMXConnectorFactory.connect(url, env);
×
99
        connection = jmxc.getMBeanServerConnection();
×
100
        echo("Connected to MBean server.");
×
101
    }
×
102

103
    public void memoryStats() {
104
        try {
105
            final ObjectName name = new ObjectName("java.lang:type=Memory");
×
106
            final CompositeData composite = (CompositeData) connection.getAttribute(name, "HeapMemoryUsage");
×
107
            if (composite != null) {
×
108
                echo("\nMEMORY:");
×
109
                echo(String.format("Current heap: %,12d k        Committed memory:  %,12d k",
×
110
                        ((Long)composite.get("used")) / 1024, ((Long)composite.get("committed")) / 1024));
×
111
                echo(String.format("Max memory:   %,12d k", ((Long)composite.get("max")) / 1024));
×
112
            }
113
        } catch (final Exception e) {
×
114
            error(e);
×
115
        }
116
    }
×
117

118
    public void instanceStats() {
119
        try {
120
            echo("\nINSTANCE:");
×
121
            final ObjectName name = new ObjectName("org.exist.management." + instance + ":type=Database");
×
122
            final Long memReserved = (Long) connection.getAttribute(name, "ReservedMem");
×
123
            echo(String.format("%25s: %10d k", "Reserved memory", memReserved / 1024));
×
124
            final Long memCache = (Long) connection.getAttribute(name, "CacheMem");
×
125
            echo(String.format("%25s: %10d k", "Cache memory", memCache / 1024));
×
126
            final Long memCollCache = (Long) connection.getAttribute(name, "CollectionCacheMem");
×
127
            echo(String.format("%25s: %10d k", "Collection cache memory", memCollCache / 1024));
×
128

129
            final String cols[] = { "MaxBrokers", "AvailableBrokers", "ActiveBrokers" };
×
130
            echo(String.format("\n%17s %17s %17s", cols[0], cols[1], cols[2]));
×
131
            final AttributeList attrs = connection.getAttributes(name, cols);
×
132
            final Object values[] = getValues(attrs);
×
133
            echo(String.format("%17d %17d %17d", values[0], values[1], values[2]));
×
134

135
            final TabularData table = (TabularData) connection.getAttribute(name, "ActiveBrokersMap");
×
136
            if (table.size() > 0) {
×
137
                echo("\nCurrently active threads:");
×
138
            }
139

140
            for (Object o : table.values()) {
×
141
                final CompositeData data = (CompositeData) o;
×
142
                echo(String.format("\t%20s: %3d", data.get("owner"), data.get("referenceCount")));
×
143
            }
144
        } catch (final Exception e) {
×
145
            error(e);
×
146
        }
147
    }
×
148

149
    public void cacheStats() {
150
        try {
151
            ObjectName name = new ObjectName("org.exist.management." + instance + ":type=CacheManager");
×
152
            String cols[] = { "MaxTotal", "CurrentSize" };
×
153
            AttributeList attrs = connection.getAttributes(name, cols);
×
154
            Object values[] = getValues(attrs);
×
155
            echo(String.format("\nCACHE [%8d pages max. / %8d pages allocated]", values[0], values[1]));
×
156

157
            final Set<ObjectName> beans = connection.queryNames(new ObjectName("org.exist.management." + instance + ":type=CacheManager.Cache,*"), null);
×
158
            cols = new String[] {"Type", "FileName", "Size", "Used", "Hits", "Fails"};
×
159
            echo(String.format("%10s %20s %10s %10s %10s %10s", cols[0], cols[1], cols[2], cols[3], cols[4], cols[5]));
×
160
            for (ObjectName bean : beans) {
×
161
                name = bean;
×
162
                attrs = connection.getAttributes(name, cols);
×
163
                values = getValues(attrs);
×
164
                echo(String.format("%10s %20s %,10d %,10d %,10d %,10d", values[0], values[1], values[2], values[3], values[4], values[5]));
×
165
            }
166
            
167
            echo("");
×
168
           name = new ObjectName("org.exist.management." + instance + ":type=CollectionCacheManager");
×
169
            cols = new String[] { "MaxTotal", "CurrentSize" };
×
170
            attrs = connection.getAttributes(name, cols);
×
171
            values = getValues(attrs);
×
172
           echo(String.format("Collection Cache: %10d k max / %10d k allocated",
×
173
               ((Long)values[0] / 1024), ((Long)values[1] / 1024)));
×
174
        } catch (final Exception e) {
×
175
            error(e);
×
176
        }
177
    }
×
178

179
    public void lockTable() {
180
        echo("\nList of threads currently waiting for a lock:");
×
181
        echo("-----------------------------------------------");
×
182
        try {
183
            final TabularData table = (TabularData) connection.getAttribute(new ObjectName("org.exist.management:type=LockManager"), "WaitingThreads");
×
184
            for (Object o : table.values()) {
×
185
                final CompositeData data = (CompositeData) o;
×
186
                echo("Thread " + data.get("waitingThread"));
×
187
                echo(String.format("%20s: %s", "Lock type", data.get("lockType")));
×
188
                echo(String.format("%20s: %s", "Lock mode", data.get("lockMode")));
×
189
                echo(String.format("%20s: %s", "Lock id", data.get("id")));
×
190
                echo(String.format("%20s: %s", "Held by", Arrays.toString((String[]) data.get("owner"))));
×
191
                final String[] readers = (String[]) data.get("waitingForRead");
×
192
                if (readers.length > 0) {
×
193
                    echo(String.format("%20s: %s", "Wait for read", Arrays.toString(readers)));
×
194
                }
195
                final String[] writers = (String[]) data.get("waitingForWrite");
×
196
                if (writers.length > 0) {
×
197
                    echo(String.format("%20s: %s", "Wait for write", Arrays.toString(writers)));
×
198
                }
199
            }
200
        } catch (final MBeanException | AttributeNotFoundException | InstanceNotFoundException | ReflectionException | IOException | MalformedObjectNameException e) {
×
201
            error(e);
×
202
        }
203
    }
×
204

205
    public void sanityReport() {
206
        echo("\nSanity report");
×
207
        echo("-----------------------------------------------");
×
208
        try {
209
            final ObjectName name = new ObjectName("org.exist.management." + instance + ".tasks:type=SanityReport");
×
210
            final String status = (String) connection.getAttribute(name, "Status");
×
211
            final Date lastCheckStart = (Date) connection.getAttribute(name, "LastCheckStart");
×
212
            final Date lastCheckEnd = (Date) connection.getAttribute(name, "LastCheckEnd");
×
213
            echo(String.format("%22s: %s", "Status", status));
×
214
            echo(String.format("%22s: %s", "Last check start", lastCheckStart));
×
215
            echo(String.format("%22s: %s", "Last check end", lastCheckEnd));
×
216
            if (lastCheckStart != null && lastCheckEnd != null)
×
217
                {echo(String.format("%22s: %dms", "Check took", (lastCheckEnd.getTime() - lastCheckStart.getTime())));}
×
218

219
            final TabularData table = (TabularData)
×
220
                    connection.getAttribute(name, "Errors");
×
221
            for (Object o : table.values()) {
×
222
                final CompositeData data = (CompositeData) o;
×
223
                echo(String.format("%22s: %s", "Error code", data.get("errcode")));
×
224
                echo(String.format("%22s: %s", "Description", data.get("description")));
×
225
            }
226
        } catch (final MBeanException | AttributeNotFoundException | InstanceNotFoundException | ReflectionException | IOException | MalformedObjectNameException e) {
×
227
            error(e);
×
228
        }
229
    }
×
230

231
    public void jobReport() {
232
        echo("\nRunning jobs report");
×
233
        echo("-----------------------------------------------");
×
234
        try {
235
            final ObjectName name = new ObjectName("org.exist.management." + instance + ":type=ProcessReport");
×
236

237
            TabularData table = (TabularData)
×
238
                    connection.getAttribute(name, "RunningJobs");
×
239
            String[] cols = new String[] { "ID", "Action", "Info" };
×
240
            echo(String.format("%15s %30s %30s", cols[0], cols[1], cols[2]));
×
241
            for (Object value : table.values()) {
×
242
                final CompositeData data = (CompositeData) value;
×
243
                echo(String.format("%15s %30s %30s", data.get("id"), data.get("action"), data.get("info")));
×
244
            }
245

246
            echo("\nRunning queries");
×
247
            echo("-----------------------------------------------");
×
248
            table = (TabularData)
×
249
                    connection.getAttribute(name, "RunningQueries");
×
250
            cols = new String[] { "ID", "Type", "Key", "Terminating" };
×
251
            echo(String.format("%10s %10s %30s %s", cols[0], cols[1], cols[2], cols[3]));
×
252
            for (Object o : table.values()) {
×
253
                final CompositeData data = (CompositeData) o;
×
254
                echo(String.format("%15s %15s %30s %6s", data.get("id"), data.get("sourceType"), data.get("sourceKey"), data.get("terminating")));
×
255
            }
256
        } catch (final MBeanException | AttributeNotFoundException | InstanceNotFoundException | ReflectionException | IOException | MalformedObjectNameException e) {
×
257
            error(e);
×
258
        }
259
    }
×
260

261
    private Object[] getValues(AttributeList attribs) {
262
        final Object[] v = new Object[attribs.size()];
×
263
        for (int i = 0; i < attribs.size(); i++) {
×
264
            v[i] = ((Attribute)attribs.get(i)).getValue();
×
265
        }
266
        return v;
×
267
    }
268

269
    private void echo(String msg) {
270
        System.out.println(msg);
×
271
    }
×
272
    
273
    private void error(Exception e) {
274
        System.err.println("ERROR: " + e.getMessage());
×
275
        e.printStackTrace();
×
276
    }
×
277

278
    private static final int DEFAULT_PORT = 1099;
279
    private static final int DEFAULT_WAIT_TIME = 0;
280

281
    /* general arguments */
282
    private static final Argument<?> helpArg = helpArgument("-h", "--help");
×
283

284
    /* connection arguments */
285
    private static final Argument<String> addressArg = stringArgument("-a", "--address")
×
286
            .description("RMI address of the server")
×
287
            .defaultValue("localhost")
×
288
            .build();
×
289
    private static final Argument<Integer> portArg = integerArgument("-p", "--port")
×
290
            .description("RMI port of the server")
×
291
            .defaultValue(DEFAULT_PORT)
×
292
            .build();
×
293
    private static final Argument<String> instanceArg = stringArgument("-i", "--instance")
×
294
            .description("The ID of the database instance to connect to")
×
295
            .defaultValue("exist")
×
296
            .build();
×
297
    private static final Argument<Integer> waitArg = integerArgument("-w", "--wait")
×
298
            .description("while displaying server statistics: keep retrieving statistics, but wait the specified number of seconds between calls.")
×
299
            .defaultValue(DEFAULT_WAIT_TIME)
×
300
            .build();
×
301

302
    /* display mode options */
303
    private static final Argument<Boolean> cacheDisplayArg = optionArgument("-c", "--cache")
×
304
            .description("displays server statistics on cache and memory usage.")
×
305
            .defaultValue(false)
×
306
            .build();
×
307
    private static final Argument<Boolean> locksDisplayArg = optionArgument("-l", "--locks")
×
308
            .description("lock manager: display locking information on all threads currently waiting for a lock on a resource or collection. Useful to debug deadlocks. During normal operation, the list will usually be empty (means: no blocked threads).")
×
309
            .defaultValue(false)
×
310
            .build();
×
311

312
    /* display info options */
313
    private static final Argument<Boolean> dbInfoArg = optionArgument("-d", "--db")
×
314
            .description("display general info about the db instance.")
×
315
            .defaultValue(false)
×
316
            .build();
×
317
    private static final Argument<Boolean> memoryInfoArg = optionArgument("-m", "--memory")
×
318
            .description("display info on free and total memory. Can be combined with other parameters.")
×
319
            .defaultValue(false)
×
320
            .build();
×
321
    private static final Argument<Boolean> sanityCheckInfoArg = optionArgument("-s", "--report")
×
322
            .description("retrieve sanity check report from the db")
×
323
            .defaultValue(false)
×
324
            .build();
×
325
    private static final Argument<Boolean> jobsInfoArg = optionArgument("-j", "--jobs")
×
326
            .description("retrieve sanity check report from the db")
×
327
            .defaultValue(false)
×
328
            .build();
×
329

330
    private enum Mode {
×
331
        STATS,
×
332
        LOCKS
×
333
    }
334

335
    @SuppressWarnings("unchecked")
336
        public static void main(final String[] args) {
337
        try {
338
            CompatibleJavaVersionCheck.checkForCompatibleJavaVersion();
×
339

340
            final ParsedArguments arguments = CommandLineParser
×
341
                    .withArguments(addressArg, portArg, instanceArg, waitArg)
×
342
                    .andArguments(cacheDisplayArg, locksDisplayArg)
×
343
                    .andArguments(dbInfoArg, memoryInfoArg, sanityCheckInfoArg, jobsInfoArg)
×
344
                    .andArguments(helpArg)
×
345
                    .programName("jmxclient" + (OSUtil.IS_WINDOWS ? ".bat" : ".sh"))
×
346
                    .parse(args);
×
347

348
            process(arguments);
×
349
        } catch (final StartException e) {
×
350
            if (e.getMessage() != null && !e.getMessage().isEmpty()) {
×
351
                System.err.println(e.getMessage());
×
352
            }
353
            System.exit(e.getErrorCode());
×
354
        } catch (final ArgumentException e) {
×
355
            System.out.println(e.getMessageAndUsage());
×
356
            System.exit(SystemExitCodes.INVALID_ARGUMENT_EXIT_CODE);
×
357
        }
358

359
    }
×
360

361
    private static void process(final ParsedArguments arguments) {
362
        final String address = arguments.get(addressArg);
×
363
        final int port = Optional.ofNullable(arguments.get(portArg)).orElse(DEFAULT_PORT);
×
364
        final String dbInstance = arguments.get(instanceArg);
×
365
        final long waitTime = Optional.ofNullable(arguments.get(waitArg)).orElse(DEFAULT_WAIT_TIME);
×
366

367
        Mode mode = Mode.STATS;
×
368
        if(getBool(arguments, cacheDisplayArg)) {
×
369
            mode = Mode.STATS;
×
370
        }
371
        if(getBool(arguments, locksDisplayArg)) {
×
372
            mode = Mode.LOCKS;
×
373
        }
374

375
        final boolean displayInstance = getBool(arguments, dbInfoArg);
×
376
        final boolean displayMem = getBool(arguments, memoryInfoArg);
×
377
        final boolean displayReport = getBool(arguments, sanityCheckInfoArg);
×
378
        final boolean jobReport = getBool(arguments, jobsInfoArg);
×
379

380
        try {
381
            final JMXClient stats = new JMXClient(dbInstance);
×
382
            stats.connect(address,port);
×
383
            stats.memoryStats();
×
384
            while (true) {
385
                switch (mode) {
×
386
                    case STATS :
387
                        stats.cacheStats();
×
388
                        break;
×
389
                    case LOCKS :
390
                        stats.lockTable();
×
391
                        break;
392
                }
393
                if (displayInstance) {stats.instanceStats();}
×
394
                if (displayMem) {stats.memoryStats();}
×
395
                if (displayReport) {stats.sanityReport();}
×
396
                if (jobReport) {stats.jobReport();}
×
397
                if (waitTime > 0) {
×
398
                    synchronized (stats) {
×
399
                        try {
400
                            stats.wait(waitTime);
×
401
                        } catch (final InterruptedException e) {
×
402
                            System.err.println("INTERRUPTED: " + e.getMessage());
×
403
                        }
404
                    }
405
                } else
406
                    {return;}
×
407
            }
408
        } catch (final IOException e) {
×
409
            e.printStackTrace(); 
×
410
        } 
411
    }
×
412
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc