diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java index 73aa40c62..6a2b23c38 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java @@ -13,6 +13,7 @@ package org.springframework.ide.vscode.boot.java.requestmapping; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import org.eclipse.jdt.core.dom.ASTNode; @@ -31,6 +32,7 @@ import org.json.JSONObject; import org.springframework.ide.vscode.boot.java.handlers.HoverProvider; import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; import org.springframework.ide.vscode.commons.util.BadLocationException; +import org.springframework.ide.vscode.commons.util.Log; import org.springframework.ide.vscode.commons.util.text.TextDocument; /** @@ -65,12 +67,13 @@ public class RequestMappingHoverProvider implements HoverProvider { private CompletableFuture provideHover(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { try { - JSONObject[] mappings = getRequestMappingsFromProcesses(runningApps); + + Optional val = getRequestMappingsForAnnotation(annotation, runningApps); List> hoverContent = new ArrayList<>(); - for (int i = 0; i < mappings.length; i++) { - addHoverContent(mappings[i], hoverContent, annotation); + if (val.isPresent()) { + addHoverContent(val.get(), hoverContent, annotation); } Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength()); @@ -88,13 +91,60 @@ public class RequestMappingHoverProvider implements HoverProvider { return null; } - private void addHoverContent(JSONObject jsonObject, List> hoverContent, Annotation annotation) { - Iterator keys = jsonObject.keys(); + private Optional getRequestMappingsForAnnotation(Annotation annotation, + SpringBootApp[] runningApps) { + + try { + SpringBootApp foundApp = null; + JSONObject requestMappings = null; + for (SpringBootApp app : runningApps) { + String mappings = app.getRequestMappings(); + if (doesMatch(annotation, mappings)) { + requestMappings = new JSONObject(mappings); + foundApp = app; + break; + } + } + if (foundApp != null && requestMappings != null) { + return Optional.of(new AppMappings(requestMappings, foundApp)); + } + } + catch (Exception e) { + Log.log(e); + } + + return Optional.empty(); + + + } + + private void addHoverContent(AppMappings appMappings, List> hoverContent, Annotation annotation) throws Exception { + Iterator keys = appMappings.mappings.keys(); while (keys.hasNext()) { String key = keys.next(); if (doesMatch(annotation, key)) { - hoverContent.add(Either.forLeft(key + " - [url of the mapping](http://localhost:8080/path)")); - hoverContent.add(Either.forLeft(jsonObject.get(key).toString())); + String path = UrlUtil.extractPath(key); + String port = appMappings.app.getPort(); + String host = appMappings.app.getHost(); + + String url = UrlUtil.createUrl(host, port, path); + StringBuilder builder = new StringBuilder(); + + if (url != null) { + builder.append("Navigate to mapping: "); + builder.append("["); + builder.append(path); + builder.append("]"); + builder.append("("); + builder.append(url); + builder.append(")"); + } else { + builder.append("Unable to resolve URL for path mapping: " + key); + } + + hoverContent.add(Either.forLeft(builder.toString())); + +// hoverContent.add(Either.forLeft(" PORT : " + port)); } } } @@ -147,4 +197,16 @@ public class RequestMappingHoverProvider implements HoverProvider { return result.toArray(new JSONObject[result.size()]); } + static class AppMappings { + + public final JSONObject mappings; + public final SpringBootApp app; + + public AppMappings(JSONObject mapping, SpringBootApp app) { + this.mappings = mapping; + this.app = app; + } + + } + } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/UrlUtil.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/UrlUtil.java new file mode 100644 index 000000000..27d60fc42 --- /dev/null +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/UrlUtil.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * 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.boot.java.requestmapping; + +import java.util.regex.Pattern; +import java.util.stream.Stream; + +public class UrlUtil { + + + public static Stream processOrPaths(String pathExp) { + if (pathExp.contains("||")) { + String[] paths = pathExp.split(Pattern.quote("||")); + return Stream.of(paths).map(String::trim); + } else { + return Stream.of(pathExp); + } + } + + + public static String extractPath(String key) { + if (key.startsWith("{[")) { //Case 2 (see above) + //An almost json string. Unfortunately not really json so we can't + //use org.json or jackson Mapper to properly parse this. + int start = 2; //right after first '[' + int end = key.indexOf(']'); + if (end>=2) { + return key.substring(start, end); + } + } + //Case 1, or some unanticipated stuff. + //Assume the key is the path, which is right for Case 1 + // and probably more useful than null for 'unanticipated stuff'. + return key; + } + + /** + * Creates http URL string based on host, port and path + * @param host + * @param port + * @param path + * @return the resultant URL + */ + public static String createUrl(String host, String port, String path) { + if (path==null) { + path = ""; + } + if (host!=null) { + if (port != null) { + if (!path.startsWith("/")) { + path = "/" +path; + } + return "http://"+host+":"+port+path; + } + } + return null; + } + +} diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootApp.java b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootApp.java index 64d944988..3b454b76e 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootApp.java +++ b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootApp.java @@ -316,4 +316,17 @@ public class SpringBootApp { return null; } + public String getHost() throws Exception { + String jmxConnect = this.vm.startLocalManagementAgent(); + + JMXConnector jmxConnector = null; + try { + JMXServiceURL serviceUrl = new JMXServiceURL(jmxConnect); + return serviceUrl.getHost(); + } + finally { + if (jmxConnector != null) jmxConnector.close(); + } + } + }