From 19d680bbb769f8ffded6c4c0d9dce36bed82500a Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Wed, 5 Sep 2018 19:02:25 -0400 Subject: [PATCH] PT #160302607: WebFlux RMs JMX beans format + NPE for WebFlux symbols --- .../RequestMappingsParser20.java | 22 +++-- .../app/cli/Boot2xRequestMappingsTest.java | 39 +++++++++ .../WebfluxRouterSymbolProvider.java | 80 +++++++++---------- 3 files changed, 93 insertions(+), 48 deletions(-) create mode 100644 headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/Boot2xRequestMappingsTest.java diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMappingsParser20.java b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMappingsParser20.java index 2a1a7ba15..fb1fbc27f 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMappingsParser20.java +++ b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMappingsParser20.java @@ -26,15 +26,21 @@ public class RequestMappingsParser20 { //Contains 3 different keys now ('dispatcherServlets', 'servletFilters' and 'servlets'. // Each with their own kind of data inside. Looks like 'dispatcherServlets' contains stuff similar to what we // know from Boot 1.x but in slighly different form. We only parse that stuff for now. - JSONObject dispatcherServlets = obj + JSONObject mappings = obj .getJSONObject(contextId) - .getJSONObject("mappings") - .getJSONObject("dispatcherServlets"); - for (String servletId : dispatcherServlets.keySet()) { - JSONArray servlets = dispatcherServlets.getJSONArray(servletId); - for (Object _servlet : servlets) { - JSONObject servlet = (JSONObject)_servlet; - result.add(new Boot20DispatcherServletMapping(servlet)); + .getJSONObject("mappings"); + JSONArray rmArray = null; + if (mappings.has("dispatcherServlets")) { + // Regular Web starter endpoints RMs JMX beans format + rmArray = mappings.getJSONObject("dispatcherServlets").getJSONArray("dispatcherServlet"); + } else if (mappings.has("dispatcherHandlers")) { + // WebFlux endpoints RMs JMX bean format + rmArray = mappings.getJSONObject("dispatcherHandlers").getJSONArray("webHandler"); + } + if (rmArray != null) { + for (Object e : rmArray) { + JSONObject rm = (JSONObject)e; + result.add(new Boot20DispatcherServletMapping(rm)); } } } diff --git a/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/Boot2xRequestMappingsTest.java b/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/Boot2xRequestMappingsTest.java new file mode 100644 index 000000000..450345170 --- /dev/null +++ b/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/Boot2xRequestMappingsTest.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2018 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 static org.junit.Assert.assertEquals; + +import java.util.Collection; + +import org.apache.commons.io.IOUtils; +import org.json.JSONObject; +import org.junit.Test; +import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping; +import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMappingsParser20; + +public class Boot2xRequestMappingsTest { + + @Test + public void testWebRms() throws Exception { + String json = IOUtils.toString(Boot2xRequestMappingsTest.class.getResourceAsStream("/live-rm-beans/rms-boot2-web.json")); + Collection rms = RequestMappingsParser20.parse(new JSONObject(json)); + assertEquals(10, rms.size()); + } + + @Test + public void testWebFluxAnnotationRms() throws Exception { + String json = IOUtils.toString(Boot2xRequestMappingsTest.class.getResourceAsStream("/live-rm-beans/rms-boot2-webflux.json")); + Collection rms = RequestMappingsParser20.parse(new JSONObject(json)); + assertEquals(7, rms.size()); + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouterSymbolProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouterSymbolProvider.java index 5f2b58d8f..b9de30814 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouterSymbolProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouterSymbolProvider.java @@ -41,7 +41,7 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; * @author Martin Lippert */ public class WebfluxRouterSymbolProvider implements SymbolProvider { - + @Override public Collection getSymbols(Annotation node, ITypeBinding typeBinding, Collection metaAnnotations, TextDocument doc) { @@ -73,20 +73,20 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { Block body = methodDeclaration.getBody(); body.accept(new ASTVisitor() { - + @Override public boolean visit(MethodInvocation node) { IMethodBinding methodBinding = node.resolveMethodBinding(); - - if (WebfluxUtils.isRouteMethodInvocation(methodBinding)) { + + if (methodBinding != null && WebfluxUtils.isRouteMethodInvocation(methodBinding)) { extractMappingSymbol(node, doc, result); } - + return super.visit(node); } - + }); - + return result; } @@ -95,17 +95,17 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { WebfluxRouteElement[] httpMethods = extractMethods(node, doc); WebfluxRouteElement[] contentTypes = extractContentTypes(node, doc); WebfluxRouteElement[] acceptTypes = extractAcceptTypes(node, doc); - + int methodNameStart = node.getName().getStartPosition(); int invocationStart = node.getStartPosition(); - + StringBuilder pathBuilder = new StringBuilder(); for (WebfluxRouteElement pathElement : pathElements) { pathBuilder.insert(0, pathElement.getElement()); } - + String path = pathBuilder.toString(); - + if (path.length() > 0) { try { @@ -125,14 +125,14 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { private WebfluxElementsInformation extractElementsInformation(WebfluxRouteElement[] path, WebfluxRouteElement[] methods, WebfluxRouteElement[] contentTypes, WebfluxRouteElement[] acceptTypes) { List allRanges = new ArrayList<>(); - + WebfluxRouteElement[][] allElements = new WebfluxRouteElement[][] {path, methods, contentTypes, acceptTypes}; for (int i = 0; i < allElements.length; i++) { for (int j = 0; j < allElements[i].length; j++) { allRanges.add(allElements[i][j].getElementRange()); } } - + return new WebfluxElementsInformation((Range[]) allRanges.toArray(new Range[allRanges.size()])); } @@ -144,13 +144,13 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { ((ASTNode)argument).accept(pathFinder); } } - + List path = pathFinder.getPath(); - + extractNestedValue(routerInvocation, path, (methodInvocation) -> { IMethodBinding methodBinding = methodInvocation.resolveMethodBinding(); String methodName = methodBinding.getName(); - + try { if (WebfluxUtils.REQUEST_PREDICATE_PATH_METHOD.equals(methodName)) { StringLiteral stringLiteral = WebfluxUtils.extractStringLiteralArgument(methodInvocation); @@ -165,10 +165,10 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { } return null; }); - + return (WebfluxRouteElement[]) path.toArray(new WebfluxRouteElement[path.size()]); } - + private WebfluxRouteElement[] extractMethods(MethodInvocation routerInvocation, TextDocument doc) { WebfluxMethodFinder methodFinder = new WebfluxMethodFinder(routerInvocation, doc); List arguments = routerInvocation.arguments(); @@ -179,11 +179,11 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { } final List methods = methodFinder.getMethods(); - + extractNestedValue(routerInvocation, methods, (methodInvocation) -> { IMethodBinding methodBinding = methodInvocation.resolveMethodBinding(); String methodName = methodBinding.getName(); - + try { if (WebfluxUtils.REQUEST_PREDICATE_METHOD_METHOD.equals(methodName)) { QualifiedName qualifiedName = WebfluxUtils.extractQualifiedNameArgument(methodInvocation); @@ -199,10 +199,10 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { return null; }); - + return (WebfluxRouteElement[]) methods.toArray(new WebfluxRouteElement[methods.size()]); } - + private WebfluxRouteElement[] extractAcceptTypes(MethodInvocation routerInvocation, TextDocument doc) { WebfluxAcceptTypeFinder typeFinder = new WebfluxAcceptTypeFinder(doc); List arguments = routerInvocation.arguments(); @@ -211,13 +211,13 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { ((ASTNode)argument).accept(typeFinder); } } - + final List acceptTypes = typeFinder.getAcceptTypes(); - + extractNestedValue(routerInvocation, acceptTypes, (methodInvocation) -> { IMethodBinding methodBinding = methodInvocation.resolveMethodBinding(); String methodName = methodBinding.getName(); - + try { if (WebfluxUtils.REQUEST_PREDICATE_ACCEPT_TYPE_METHOD.equals(methodName)) { SimpleName nameArgument = WebfluxUtils.extractSimpleNameArgument(methodInvocation); @@ -233,10 +233,10 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { return null; }); - + return (WebfluxRouteElement[]) acceptTypes.toArray(new WebfluxRouteElement[acceptTypes.size()]); } - + private WebfluxRouteElement[] extractContentTypes(MethodInvocation routerInvocation, TextDocument doc) { WebfluxContentTypeFinder contentTypeFinder = new WebfluxContentTypeFinder(doc); List arguments = routerInvocation.arguments(); @@ -245,13 +245,13 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { ((ASTNode)argument).accept(contentTypeFinder); } } - + final List contentTypes = contentTypeFinder.getContentTypes(); - + extractNestedValue(routerInvocation, contentTypes, (methodInvocation) -> { IMethodBinding methodBinding = methodInvocation.resolveMethodBinding(); String methodName = methodBinding.getName(); - + try { if (WebfluxUtils.REQUEST_PREDICATE_CONTENT_TYPE_METHOD.equals(methodName)) { SimpleName nameArgument = WebfluxUtils.extractSimpleNameArgument(methodInvocation); @@ -267,10 +267,10 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { return null; }); - + return (WebfluxRouteElement[]) contentTypes.toArray(new WebfluxRouteElement[contentTypes.size()]); } - + private void extractNestedValue(ASTNode node, Collection values, Function extractor) { if (node == null || node instanceof TypeDeclaration) { return; @@ -279,7 +279,7 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { if (node instanceof MethodInvocation) { MethodInvocation methodInvocation = (MethodInvocation) node; IMethodBinding methodBinding = methodInvocation.resolveMethodBinding(); - + if (WebfluxUtils.ROUTER_FUNCTIONS_TYPE.equals(methodBinding.getDeclaringClass().getBinaryName())) { String name = methodBinding.getName(); if (WebfluxUtils.REQUEST_PREDICATE_NEST_METHOD.equals(name)) { @@ -296,7 +296,7 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { } } } - + extractNestedValue(node.getParent(), values, extractor); } @@ -314,26 +314,26 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { if (methodBinding != null && methodBinding.getDeclaringClass() != null && methodBinding.getMethodDeclaration() != null) { String handlerClass = methodBinding.getDeclaringClass().getBinaryName(); if (handlerClass != null) handlerClass = handlerClass.trim(); - + String handlerMethod = methodBinding.getMethodDeclaration().toString(); if (handlerMethod != null) handlerMethod = handlerMethod.trim(); - + return new WebfluxHandlerInformation(handlerClass, handlerMethod, path, getElementStrings(httpMethods), getElementStrings(contentTypes), getElementStrings(acceptTypes)); } } } } - + return null; } - + private String[] getElementStrings(WebfluxRouteElement[] routeElements) { List result = new ArrayList<>(); - + for (int i = 0; i < routeElements.length; i++) { result.add(routeElements[i].getElement()); } - + return (String[]) result.toArray(new String[result.size()]); }