Rework SpringBootApp cache

This commit is contained in:
Kris De Volder
2017-11-02 10:46:16 -07:00
parent 0e2c0f15ea
commit 2a51701d41
2 changed files with 71 additions and 63 deletions

View File

@@ -12,6 +12,7 @@ package org.springframework.ide.vscode.commons.boot.app.cli;
import java.io.File;
import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
@@ -60,64 +61,22 @@ public class SpringBootApp {
private VirtualMachine vm;
private VirtualMachineDescriptor vmd;
private Supplier<String> jmxConnect;
private static final Cache<VirtualMachineDescriptor, String> pidToJmxConnectUrl = CacheBuilder.newBuilder().build();
private static Callable<Collection<SpringBootApp>> cached(Callable<Collection<SpringBootApp>> provider) {
LoadingCache<Object, CompletableFuture<Collection<SpringBootApp>>> cache = CacheBuilder.newBuilder()
.expireAfterWrite(500, TimeUnit.MILLISECONDS)
.removalListener(removalNotification -> {
@SuppressWarnings("unchecked")
CompletableFuture<Collection<SpringBootApp>> removed = (CompletableFuture<Collection<SpringBootApp>>) removalNotification.getValue();
if (!removed.isCompletedExceptionally()) {
try {
Collection<SpringBootApp> apps = removed.get();
for (SpringBootApp springBootApp : apps) {
springBootApp.dispose();
}
} catch (Exception e) {
Log.log(e);
}
}
})
.build(new CacheLoader<Object, CompletableFuture<Collection<SpringBootApp>>>() {
@Override public CompletableFuture<Collection<SpringBootApp>> load(Object key) {
try {
return CompletableFuture.completedFuture(provider.call());
} catch (Throwable e) {
return Futures.error(e);
}
}
});
return () -> {
Object key = SpringBootApp.class; //This key really doesn't matter, as long as we use the same non-null object each time.
return cache.get(key).get();
};
}
private static Collection<SpringBootApp> fetchRunningJavaApps() throws Exception {
List<VirtualMachineDescriptor> list = VirtualMachine.list();
ImmutableList.Builder<SpringBootApp> apps = ImmutableList.builder();
for (VirtualMachineDescriptor vmd : list) {
apps.add(new SpringBootApp(vmd));
private final Supplier<String> jmxConnect = Suppliers.memoize(() -> {
try {
return vm.startLocalManagementAgent();
} catch (Exception e) {
Log.log(e);
return null;
}
});
// Invalidate part of pidToJmxConnectUrl cache to remove old processes
Set<VirtualMachineDescriptor> oldProcesses = new HashSet<>(pidToJmxConnectUrl.asMap().keySet());
oldProcesses.removeAll(list);
pidToJmxConnectUrl.invalidateAll(oldProcesses);
return apps.build();
}
private static SpringBootAppCache cache = new SpringBootAppCache();
public static Collection<SpringBootApp> getAllRunningJavaApps() throws Exception {
return cachedJavaAppsGetter.call();
return cache.getAllRunningJavaApps();
}
private static Callable<Collection<SpringBootApp>> cachedJavaAppsGetter = cached(SpringBootApp::fetchRunningJavaApps);
/**
* @return Map that contains the boot apps, mapping the process ID -> boot app accessor object
*/
@@ -128,7 +87,6 @@ public class SpringBootApp {
public SpringBootApp(VirtualMachineDescriptor vmd) throws Exception {
this.vmd = vmd;
this.vm = VirtualMachine.attach(vmd);
this.jmxConnect = Suppliers.memoize(() -> getJmxConnectUrl());
System.err.println("SpringBootApp created: "+this);
}
@@ -140,17 +98,6 @@ public class SpringBootApp {
return vmd.displayName();
}
private String getJmxConnectUrl() {
try {
return pidToJmxConnectUrl.get(vmd, () -> {
return vm.startLocalManagementAgent();
});
} catch (ExecutionException e) {
Log.log(e);
return null;
}
}
public String getHost() throws Exception {
JMXServiceURL serviceUrl = new JMXServiceURL(jmxConnect.get());
return serviceUrl.getHost();

View File

@@ -0,0 +1,61 @@
/*******************************************************************************
* Copyright (c) 2017 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli;
import java.time.Duration;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import org.springframework.ide.vscode.commons.util.Log;
import com.google.common.collect.ImmutableMap;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;
public class SpringBootAppCache {
private static final Duration EXPIRE_AFTER = Duration.ofMillis(500); //Limits rate at which we refresh list of apps
private long nextRefreshAfter = Long.MIN_VALUE;
private ImmutableMap<VirtualMachineDescriptor, SpringBootApp> apps = ImmutableMap.of();
public synchronized Collection<SpringBootApp> getAllRunningJavaApps() {
if (System.currentTimeMillis()>=nextRefreshAfter) {
refresh();
}
return apps.values();
}
private void refresh() {
List<VirtualMachineDescriptor> currentVms = VirtualMachine.list();
HashSet<VirtualMachineDescriptor> oldVms = new HashSet<>(apps.keySet());
ImmutableMap.Builder<VirtualMachineDescriptor, SpringBootApp> newApps = ImmutableMap.builder();
for (VirtualMachineDescriptor vm : currentVms) {
oldVms.remove(vm);
SpringBootApp existingApp = apps.get(vm);
if (existingApp!=null) {
newApps.put(vm, existingApp);
} else {
try {
newApps.put(vm, new SpringBootApp(vm));
} catch (Exception e) {
Log.log(e);
}
}
}
for (VirtualMachineDescriptor oldVm : oldVms) {
apps.get(oldVm).dispose();
}
apps = newApps.build();
nextRefreshAfter = System.currentTimeMillis() + EXPIRE_AFTER.toMillis();
}
}