Skip to content

Commit 9cd6859

Browse files
committed
Start JMX heartbeat immediately where feasible, initialize persisted JMX connections in one batch on VisualVM startup.
1 parent a32037a commit 9cd6859

File tree

1 file changed

+70
-48
lines changed

1 file changed

+70
-48
lines changed

visualvm/jmx/src/org/graalvm/visualvm/jmx/impl/JmxApplicationProvider.java

Lines changed: 70 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -217,10 +217,19 @@ public JmxApplication createJmxApplication(String connectionString, String displ
217217
throw new JmxApplicationException(e.getMessage(), e.getCause());
218218
}
219219
}
220-
220+
221221
private JmxApplication addJmxApplication(boolean newApp, JMXServiceURL serviceURL,
222222
String connectionName, String displayName, String suggestedName, String hostName,
223-
EnvironmentProvider provider, Storage storage, String allowsInsecure, boolean lazyCheck) throws JMXException {
223+
EnvironmentProvider provider, Storage storage, String allowsInsecure, boolean lazy) throws JMXException {
224+
225+
if (lazy) return addLazyJmxApplication(newApp, serviceURL, connectionName, displayName, suggestedName,
226+
hostName, provider, storage, allowsInsecure, true);
227+
else throw new RuntimeException("Only lazy JMX connections currently implemented!"); // NOI18N
228+
}
229+
230+
private JmxApplication addLazyJmxApplication(boolean newApp, JMXServiceURL serviceURL, String connectionName,
231+
String displayName, String suggestedName, String hostName, EnvironmentProvider provider,
232+
Storage storage, String allowsInsecure, boolean scheduleHeartbeat) throws JMXException {
224233

225234
// Resolve JMXServiceURL, finish if not resolved
226235
if (serviceURL == null) {
@@ -300,8 +309,8 @@ private JmxApplication addJmxApplication(boolean newApp, JMXServiceURL serviceUR
300309
// Setup whether the SSL connection is required or not
301310
application.getStorage().setCustomProperty(PROPERTY_RETRY_WITHOUT_SSL, allowsInsecure);
302311

303-
// NOTE: 'lazyCheck' currently always true!
304-
312+
// // NOTE: 'lazyCheck' currently always true!
313+
//
305314
// // Connect to the JMX agent
306315
// JmxModel model = lazyCheck ? null : JmxModelFactory.getJmxModelFor(application);
307316
//
@@ -337,21 +346,26 @@ private JmxApplication addJmxApplication(boolean newApp, JMXServiceURL serviceUR
337346

338347
// if (model == null || model.getConnectionState() != ConnectionState.CONNECTED) {
339348
synchronized (unavailableApps) { unavailableApps.add(application); }
340-
scheduleHeartbeat();
349+
if (scheduleHeartbeat) scheduleHeartbeat();
341350
// }
342351

343352
return application;
344353
}
345354

346355

347-
private static final int HEARTBEAT_POLL = 5000;
348-
private static final int HEARTBEAT_THREADS = 10;
356+
private static final int HEARTBEAT_DELAY = 100;
357+
private static final int HEARTBEAT_POLL_DELAY = 5000;
358+
private static final int HEARTBEAT_MAX_THREADS = 10;
349359
private final Set<JmxApplication> unavailableApps = new HashSet();
350360

351361
private volatile boolean heartbeatRunning;
352362
private volatile boolean anotherHeartbeatPending;
353363

354364
private void scheduleHeartbeat() {
365+
scheduleHeartbeatImpl(true);
366+
}
367+
368+
private void scheduleHeartbeatImpl(boolean immediately) {
355369
if (heartbeatRunning) {
356370
anotherHeartbeatPending = true;
357371
return;
@@ -373,53 +387,55 @@ private void scheduleHeartbeat() {
373387
}
374388

375389
final AtomicInteger counter = new AtomicInteger(apps.size());
376-
final RequestProcessor processor = new RequestProcessor("JMX Heartbeat Processor", Math.min(counter.intValue(), HEARTBEAT_THREADS)); // NOI18N
377-
// System.err.println(">>> Heartbeat for " + counter + " targets at " + LocalTime.now());
390+
RequestProcessor processor = new RequestProcessor("JMX Heartbeat Processor", Math.min(counter.intValue(), HEARTBEAT_MAX_THREADS)); // NOI18N
391+
// System.err.println(">>> Heartbeat for " + counter + " targets at " + java.time.LocalTime.now());
378392
for (final JmxApplication app : apps) {
379393
processor.post(new Runnable() {
380394
@Override
381395
public void run() {
382-
JmxModel model = new JmxModelProvider().createModelFor(app);
383-
if (model == null || model.getConnectionState() != ConnectionState.CONNECTED) {
384-
synchronized (unavailableApps) { unavailableApps.add(app); }
385-
} else {
386-
app.setStateImpl(Stateful.STATE_AVAILABLE);
387-
388-
app.jmxModel = JmxModelFactory.getJmxModelFor(app);
389-
app.jvm = JvmFactory.getJVMFor(app);
390-
391-
app.jmxModel.addPropertyChangeListener(new PropertyChangeListener() {
392-
public void propertyChange(PropertyChangeEvent evt) {
393-
if (evt.getNewValue() == ConnectionState.CONNECTED) {
394-
app.setStateImpl(Stateful.STATE_AVAILABLE);
395-
} else {
396-
app.setStateImpl(Stateful.STATE_UNAVAILABLE);
397-
if (!app.isRemoved()) {
398-
synchronized (unavailableApps) { unavailableApps.add(app); }
399-
scheduleHeartbeat();
396+
try {
397+
JmxModel model = new JmxModelProvider().createModelFor(app);
398+
if (model == null || model.getConnectionState() != ConnectionState.CONNECTED) {
399+
synchronized (unavailableApps) { unavailableApps.add(app); }
400+
} else {
401+
app.setStateImpl(Stateful.STATE_AVAILABLE);
402+
403+
app.jmxModel = JmxModelFactory.getJmxModelFor(app);
404+
app.jvm = JvmFactory.getJVMFor(app);
405+
406+
app.jmxModel.addPropertyChangeListener(new PropertyChangeListener() {
407+
public void propertyChange(PropertyChangeEvent evt) {
408+
if (evt.getNewValue() == ConnectionState.CONNECTED) {
409+
app.setStateImpl(Stateful.STATE_AVAILABLE);
410+
} else {
411+
app.setStateImpl(Stateful.STATE_UNAVAILABLE);
412+
if (!app.isRemoved()) {
413+
synchronized (unavailableApps) { unavailableApps.add(app); }
414+
scheduleHeartbeatImpl(true);
415+
}
416+
// TODO: remove listener from model once not needed!
400417
}
401-
// TODO: remove listener from model once not needed!
418+
}
419+
});
420+
}
421+
} finally {
422+
if (counter.decrementAndGet() == 0) {
423+
heartbeatRunning = false; // done
424+
425+
if (!anotherHeartbeatPending) {
426+
anotherHeartbeatPending = false;
427+
synchronized (unavailableApps) {
428+
Iterator<JmxApplication> appsI = unavailableApps.iterator();
429+
while (appsI.hasNext()) if (appsI.next().isRemoved()) appsI.remove();
430+
if (unavailableApps.isEmpty()) return; // not pending and no apps to check, return
402431
}
403432
}
404-
});
405-
}
406-
407-
if (counter.decrementAndGet() == 0) {
408-
heartbeatRunning = false; // done
409-
410-
if (!anotherHeartbeatPending) {
411-
anotherHeartbeatPending = false;
412-
synchronized (unavailableApps) {
413-
Iterator<JmxApplication> appsI = unavailableApps.iterator();
414-
while (appsI.hasNext()) if (appsI.next().isRemoved()) appsI.remove();
415-
if (unavailableApps.isEmpty()) return; // not pending and no apps to check, return
416-
}
433+
434+
if (!heartbeatRunning) scheduleHeartbeatImpl(false); // start again if needed and not running yet
417435
}
418-
419-
if (!heartbeatRunning) scheduleHeartbeat(); // start again if needed and not running yet
420436
}
421437
}
422-
}, HEARTBEAT_POLL);
438+
}, immediately ? HEARTBEAT_DELAY : HEARTBEAT_POLL_DELAY);
423439
}
424440
}
425441

@@ -513,7 +529,8 @@ public synchronized void dataChanged(DataChangeEvent<Host> event) {
513529
for (Host host : hosts) {
514530
String hostName = host.getHostName();
515531
Set<Storage> storageSet = persistedApplications.get(hostName);
516-
if (storageSet != null) {
532+
int storageSetSize = storageSet == null ? 0 : storageSet.size();
533+
if (storageSetSize > 0) {
517534
persistedApplications.remove(hostName);
518535

519536
String[] keys = new String[] {
@@ -524,10 +541,13 @@ public synchronized void dataChanged(DataChangeEvent<Host> event) {
524541
PROPERTY_ENV_PROVIDER_ID,
525542
PROPERTY_RETRY_WITHOUT_SSL
526543
};
544+
545+
final AtomicInteger counter = new AtomicInteger(storageSetSize);
546+
RequestProcessor processor = new RequestProcessor("JMX Persistence Processor", Math.min(counter.intValue(), HEARTBEAT_MAX_THREADS)); // NOI18N
527547

528548
for (final Storage storage : storageSet) {
529549
final String[] values = storage.getCustomProperties(keys);
530-
new RequestProcessor().post(new Runnable() {
550+
processor.post(new Runnable() {
531551
public void run() {
532552
try {
533553
String epid = values[4];
@@ -539,8 +559,8 @@ public void run() {
539559
EnvironmentProvider ep = epid == null ? null :
540560
JmxConnectionSupportImpl.
541561
getProvider(epid);
542-
addJmxApplication(false, null, values[0], values[2], values[3],
543-
values[1], ep, storage, values[5], true);
562+
addLazyJmxApplication(false, null, values[0], values[2], values[3],
563+
values[1], ep, storage, values[5], false);
544564
} catch (final JMXException e) {
545565
if (e.isConfig()) {
546566
DialogDisplayer.getDefault().notifyLater(
@@ -553,6 +573,8 @@ public void run() {
553573
failedAppsN.add(name);
554574
failedAppsS.add(storage);
555575
}
576+
} finally {
577+
if (counter.decrementAndGet() == 0) scheduleHeartbeat();
556578
}
557579
synchronized (persistedAppsCount) {
558580
persistedAppsCount[0]--;

0 commit comments

Comments
 (0)