change detection is now able to re-connect to already stopped application

This commit is contained in:
Martin Lippert
2018-06-08 18:23:49 +02:00
parent e157343daa
commit 71bebf307a
5 changed files with 134 additions and 37 deletions

View File

@@ -160,12 +160,7 @@ public class SpringBootApp {
public String getJavaCommand() throws IOException {
Properties props = this.vm.getSystemProperties();
if (props.contains("sun.java.command")) {
return props.getProperty("sun.java.command");
}
else {
return null;
}
return (String) props.get("sun.java.command");
}
public boolean containsSystemProperty(Object key) throws IOException {

View File

@@ -13,6 +13,7 @@ package org.springframework.ide.vscode.boot.java.utils;
import java.util.ArrayList;
import java.util.List;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
/**
@@ -20,12 +21,19 @@ import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
*/
public class Change {
private final SpringBootApp runningApp;
private List<LiveBean> newBeans;
private List<LiveBean> deletedBeans;
public Change() {
public Change(SpringBootApp runningApp) {
this.runningApp = runningApp;
}
public SpringBootApp getRunningApp() {
return runningApp;
}
public List<LiveBean> getNewBeans() {
return newBeans;
}
@@ -49,5 +57,5 @@ public class Change {
newBeans.add(bean);
}
}

View File

@@ -10,8 +10,12 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.utils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
@@ -26,26 +30,97 @@ public class ChangeDetectionHistory {
this.changeHistory = new HashMap<>();
}
public Change checkForChanges(SpringBootApp app) {
String virtualID = getVirtualAppID(app);
public Change[] checkForChanges(SpringBootApp[] runningApps) {
List<Change> result = null;
if (!changeHistory.containsKey(virtualID)) {
ChangeHistory appHistory = new ChangeHistory(virtualID);
changeHistory.put(virtualID, appHistory);
for (SpringBootApp runningApp : runningApps) {
String virtualID = getVirtualAppID(runningApp);
if (changeHistory.containsKey(virtualID)) {
// the standard case
ChangeHistory appHistory = changeHistory.get(virtualID);
appHistory.updateProcess(runningApp);
Change change = appHistory.checkForUpdates();
if (change != null) {
if (result == null) {
result = new ArrayList<>();
}
result.add(change);
}
}
else {
String oldAppID = getOldApp(runningApp, runningApps);
if (oldAppID != null) {
ChangeHistory oldHistory = changeHistory.remove(oldAppID);
oldHistory.updateProcess(runningApp);
changeHistory.put(virtualID, oldHistory);
Change change = oldHistory.checkForUpdates();
if (change != null) {
if (result == null) {
result = new ArrayList<>();
}
result.add(change);
}
}
else {
ChangeHistory newHistory = new ChangeHistory();
newHistory.updateProcess(runningApp);
changeHistory.put(virtualID, newHistory);
newHistory.checkForUpdates();
}
}
}
ChangeHistory appHistory = changeHistory.get(virtualID);
appHistory.updateProcess(app);
Change result = appHistory.checkForUpdates();
return result;
if (result != null) {
return (Change[]) result.toArray(new Change[result.size()]);
}
else {
return null;
}
}
private String getVirtualAppID(SpringBootApp app) {
// TODO: this needs a lot more work to make this a real VIRTUAL ID which detects the same app
// running across different app restarts, so the process ID is not the best way to do this (just
// a temporary solution)
return app.getProcessID();
}
private String getOldApp(SpringBootApp app, SpringBootApp[] allApps) {
Set<String> histories = this.changeHistory.keySet();
try {
String commandLine = app.getJavaCommand();
String[] classpath = app.getClasspath();
for (String oldProcessID : histories) {
if (this.changeHistory.get(oldProcessID).matchesProcess(commandLine, classpath)
&& processNotRunningAnymore(oldProcessID, allApps)) {
return oldProcessID;
}
}
}
catch (IOException e) {
e.printStackTrace();
}
return null;
}
private boolean processNotRunningAnymore(String oldProcessID, SpringBootApp[] allApps) {
try {
for (SpringBootApp app : allApps) {
String id = getVirtualAppID(app);
if (id != null && id.equals(oldProcessID)) {
return false;
}
}
}
catch (Exception e) {
e.printStackTrace();
}
return true;
}
}

View File

@@ -10,7 +10,9 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.utils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -27,23 +29,38 @@ public class ChangeHistory {
private static final List<LiveBean> EMPTY_BEANS_LIST = new ArrayList<>(0);
private final String virtualAppID;
private SpringBootApp runningProcess;
private SpringBootApp associatedProcess;
private String associatedProcessCommand;
private String[] associatedProcessClasspath;
private LiveBeansModel lastBeans;
public ChangeHistory(String virtualAppID) {
this.virtualAppID = virtualAppID;
public ChangeHistory() {
}
public void updateProcess(SpringBootApp app) {
this.runningProcess = app;
if (this.associatedProcess != app) {
this.associatedProcess = app;
try {
this.associatedProcessCommand = app.getJavaCommand();
this.associatedProcessClasspath = app.getClasspath();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
public boolean matchesProcess(String commandLine, String[] classpath) {
return this.associatedProcessCommand != null && this.associatedProcessCommand.equals(commandLine)
&& this.associatedProcessClasspath != null && Arrays.deepEquals(this.associatedProcessClasspath, classpath);
}
public Change checkForUpdates() {
Change result = null;
LiveBeansModel currentBeans = this.runningProcess.getBeans();
LiveBeansModel currentBeans = this.associatedProcess.getBeans();
if (!currentBeans.isEmpty()) {
if (lastBeans == null) {
@@ -87,7 +104,7 @@ public class ChangeHistory {
if (!contains(currentBeans, bean)) {
if (result == null) {
result = new Change();
result = new Change(associatedProcess);
}
result.addDeletedBean(bean);
@@ -98,7 +115,7 @@ public class ChangeHistory {
if (!contains(previousBeans, bean)) {
if (result == null) {
result = new Change();
result = new Change(associatedProcess);
}
result.addNewBean(bean);
@@ -111,7 +128,7 @@ public class ChangeHistory {
private boolean contains(List<LiveBean> beans, LiveBean bean) {
for (LiveBean beansFromList : beans) {
if (StringUtils.equals(beansFromList.getId(), bean.getId())
&& StringUtils.equals(beansFromList.getType(), bean.getType())
&& StringUtils.equals(beansFromList.getType(true), bean.getType(true))
&& StringUtils.equals(beansFromList.getResource(), bean.getResource())) {
return true;
}

View File

@@ -135,6 +135,12 @@ public class SpringLiveChangeDetectionWatchdog {
if (changeDetectionEnabled) {
try {
SpringBootApp[] runningBootApps = runningAppProvider.getAllRunningSpringApps().toArray(new SpringBootApp[0]);
Change[] changes = changeHistory.checkForChanges(runningBootApps);
if (changes != null && changes.length > 0) {
for (Change change : changes) {
publishDetectedChange(change);
}
}
for (SpringBootApp app : runningBootApps) {
updateApp(app);
}
@@ -145,16 +151,12 @@ public class SpringLiveChangeDetectionWatchdog {
}
private void updateApp(SpringBootApp app) {
Change change = changeHistory.checkForChanges(app);
if (change != null) {
publishDetectedChange(change, app);
}
}
private void publishDetectedChange(Change change, SpringBootApp app) {
private void publishDetectedChange(Change change) {
Map<String, List<Diagnostic>> diagnostics = new HashMap<>();
IJavaProject[] projects = findProjectsFor(app);
IJavaProject[] projects = findProjectsFor(change.getRunningApp());
List<LiveBean> deletedBeans = change.getDeletedBeans();
if (deletedBeans != null) {