Use guava cache to better manage apps from getAllRunning(Spring|Java)Apps

This commit is contained in:
Kris De Volder
2017-11-01 14:26:32 -07:00
parent f8d4e0ca8f
commit fb3af5c1e3
7 changed files with 87 additions and 56 deletions

View File

@@ -314,7 +314,7 @@ jobs:
trigger: true
- get: maven-cache
- task: build-boot-java-vsix-snapshot
attempts: 3 #Because its a bit flaky with the maven bits
#attempts: 3 #Because its a bit flaky with the maven bits
file: sts4/concourse/tasks/build-vsix.yml
params:
extension_id: vscode-boot-java

View File

@@ -18,17 +18,8 @@ import com.google.common.collect.ImmutableList;
public interface RunningAppProvider {
public static final RunningAppProvider DEFAULT = new RunningAppProvider() {
@Override public Collection<SpringBootApp> getAllRunningSpringApps() throws Exception {
return SpringBootApp.getAllRunningSpringApps().values();
}
};
public static final RunningAppProvider NULL = new RunningAppProvider() {
@Override public Collection<SpringBootApp> getAllRunningSpringApps() throws Exception {
return ImmutableList.of();
}
};
public static final RunningAppProvider DEFAULT = SpringBootApp::getAllRunningSpringApps;
public static final RunningAppProvider NULL = () -> ImmutableList.of();
Collection<SpringBootApp> getAllRunningSpringApps() throws Exception;
}

View File

@@ -22,6 +22,9 @@ import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanServerConnection;
@@ -32,13 +35,17 @@ import javax.management.remote.JMXServiceURL;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping;
import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMappingImpl1;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
import org.springframework.ide.vscode.commons.util.CollectorUtil;
import org.springframework.ide.vscode.commons.util.Futures;
import org.springframework.ide.vscode.commons.util.Log;
import org.springframework.ide.vscode.commons.util.StringUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;
@@ -51,44 +58,65 @@ public class SpringBootApp {
private VirtualMachine vm;
private VirtualMachineDescriptor vmd;
/**
* @return Map that contains the boot apps, mapping the process ID -> boot app accessor object
*/
public static Map<String, SpringBootApp> getAllRunningJavaApps() throws Exception {
Map<String, SpringBootApp> result = new HashMap<>();
List<VirtualMachineDescriptor> list = VirtualMachine.list();
for (VirtualMachineDescriptor vmd : list) {
SpringBootApp app = new SpringBootApp(vmd);
result.put(app.getProcessID(), app);
}
return result;
}
/**
* @return Map that contains the boot apps, mapping the process ID -> boot app accessor object
*/
public static Map<String, SpringBootApp> getAllRunningSpringApps() throws Exception {
Map<String, SpringBootApp> result = new HashMap<>();
List<VirtualMachineDescriptor> list = VirtualMachine.list();
for (VirtualMachineDescriptor vmd : list) {
try {
SpringBootApp app = new SpringBootApp(vmd);
if (app.isSpringBootApp()) {
result.put(app.getProcessID(), app);
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);
}
}
catch (Exception e) {
System.err.println("cannot attach to app: " + vmd.id());
})
.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();
};
}
return result;
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));
}
return apps.build();
}
public static Collection<SpringBootApp> getAllRunningJavaApps() throws Exception {
return cachedJavaAppsGetter.call();
}
private static Callable<Collection<SpringBootApp>> cachedJavaAppsGetter = cached(SpringBootApp::fetchRunningJavaApps);
/**
* @return Map that contains the boot apps, mapping the process ID -> boot app accessor object
*/
public static Collection<SpringBootApp> getAllRunningSpringApps() throws Exception {
return getAllRunningJavaApps().stream().filter(SpringBootApp::isSpringBootApp).collect(CollectorUtil.toImmutableList());
}
public SpringBootApp(VirtualMachineDescriptor vmd) throws Exception {
this.vmd = vmd;
this.vm = VirtualMachine.attach(vmd);
System.err.println("SpringBootApp created: "+this);
}
public String getProcessID() {
@@ -105,7 +133,7 @@ public class SpringBootApp {
return serviceUrl.getHost();
}
public boolean isSpringBootApp() throws Exception {
public boolean isSpringBootApp() {
return !containsSystemProperty("sts4.languageserver.name")
&& (
isSpringBootAppClasspath() ||
@@ -435,7 +463,6 @@ public class SpringBootApp {
_profiles = env.opt("profiles"); //Boot 1.5
}
if (_profiles instanceof JSONArray) {
@SuppressWarnings("unchecked")
JSONArray profiles = (JSONArray) _profiles;
ImmutableList.Builder<String> list = ImmutableList.builder();
for (Object object : profiles) {
@@ -452,5 +479,19 @@ public class SpringBootApp {
return null;
}
public void dispose() {
if (vm!=null) {
System.err.println("SpringBootApp disposed: "+this);
try {
vm.detach();
} catch (Exception e) {
}
vm = null;
}
if (vmd!=null) {
vmd = null;
}
}
}

View File

@@ -10,17 +10,16 @@
*******************************************************************************/
package org.springframework.ide.vscode.commons.boot.app.cli;
import java.util.Map;
import java.util.Collection;
/**
* @author Martin Lippert
*/
@SuppressWarnings("restriction")
public class SpringBootAppCLI {
public static void main(String[] args) throws Exception {
Map<String, SpringBootApp> allRunningJavaApps = SpringBootApp.getAllRunningJavaApps();
for (SpringBootApp app : allRunningJavaApps.values()) {
Collection<SpringBootApp> allRunningJavaApps = SpringBootApp.getAllRunningJavaApps();
for (SpringBootApp app : allRunningJavaApps) {
if (app.isSpringBootApp()) {
printBootAppDetails(app);
}

View File

@@ -90,7 +90,7 @@ public class SpringBootAppTest {
private SpringBootApp getAppContaining(String nameFragment) {
try {
return SpringBootApp.getAllRunningJavaApps().values().stream().filter(app -> app.getProcessName().contains(nameFragment)).findAny().get();
return SpringBootApp.getAllRunningJavaApps().stream().filter(app -> app.getProcessName().contains(nameFragment)).findAny().get();
} catch (Exception e) {
throw ExceptionUtil.unchecked(e);
}
@@ -107,17 +107,17 @@ public class SpringBootAppTest {
}
@Test public void getAllJavaApps() throws Exception {
Map<String, SpringBootApp> allApps = SpringBootApp.getAllRunningJavaApps();
Collection<SpringBootApp> allApps = SpringBootApp.getAllRunningJavaApps();
for (String appName : appNames) {
Optional<SpringBootApp> myProcess = allApps.values().stream().filter(app -> app.getProcessName().contains(appName)).findAny();
Optional<SpringBootApp> myProcess = allApps.stream().filter(app -> app.getProcessName().contains(appName)).findAny();
assertTrue(appName, myProcess.isPresent());
}
}
@Test public void getAllBootApps() throws Exception {
Map<String, SpringBootApp> allApps = SpringBootApp.getAllRunningSpringApps();
Collection<SpringBootApp> allApps = SpringBootApp.getAllRunningSpringApps();
for (String appName : appNames) {
Optional<SpringBootApp> myProcess = allApps.values().stream().filter(app -> app.getProcessName().contains(appName)).findAny();
Optional<SpringBootApp> myProcess = allApps.stream().filter(app -> app.getProcessName().contains(appName)).findAny();
assertTrue(myProcess.isPresent());
}
}

View File

@@ -37,7 +37,7 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
<version>${guava-version}</version>
</dependency>
<dependency>
<groupId>javax.inject</groupId>

View File

@@ -22,7 +22,7 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
<version>${guava-version}</version>
</dependency>
<!-- ANTLR Parser Runtime -->
<dependency>