From dfe0c513311db1e41853c8647acba0b2afd3711e Mon Sep 17 00:00:00 2001 From: aboyko Date: Mon, 8 May 2023 18:57:45 -0400 Subject: [PATCH] Beans model extension API for VSCode --- .../commons/protocol/spring/BeansParams.java | 25 ++++++++++++++ .../protocol/spring/SpringModelService.java | 4 ++- .../vscode/boot/app/SpringSymbolIndex.java | 4 ++- .../test/SpringModelServiceTest.java | 9 +++-- .../vscode-spring-boot/lib/api.d.ts | 34 ++++++++++++++++++- .../vscode-spring-boot/lib/apiManager.ts | 29 ++++++++++++++-- .../vscode-spring-boot/lib/notification.ts | 4 +++ 7 files changed, 102 insertions(+), 7 deletions(-) create mode 100644 headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/BeansParams.java diff --git a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/BeansParams.java b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/BeansParams.java new file mode 100644 index 000000000..f37c3f14d --- /dev/null +++ b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/BeansParams.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2023 VMware, 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 + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VMware, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.protocol.spring; + +public class BeansParams { + + private String projectName; + + public String getProjectName() { + return projectName; + } + + public void setProjectName(String projectName) { + this.projectName = projectName; + } + +} diff --git a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/SpringModelService.java b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/SpringModelService.java index 3c73e7d4e..df893cf0a 100644 --- a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/SpringModelService.java +++ b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/SpringModelService.java @@ -14,11 +14,13 @@ import java.util.List; import java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; +@JsonSegment("spring") public interface SpringModelService { @JsonRequest - default CompletableFuture> beans(String project) { + default CompletableFuture> beans(BeansParams params) { throw new UnsupportedOperationException(); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java index 6cfa94615..7a6dd4635 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java @@ -70,6 +70,7 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguage import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; import org.springframework.ide.vscode.commons.languageserver.util.SimpleWorkspaceService; import org.springframework.ide.vscode.commons.protocol.spring.Bean; +import org.springframework.ide.vscode.commons.protocol.spring.BeansParams; import org.springframework.ide.vscode.commons.protocol.spring.SpringModelService; import org.springframework.ide.vscode.commons.util.Futures; import org.springframework.ide.vscode.commons.util.StringUtil; @@ -706,7 +707,8 @@ public class SpringSymbolIndex implements InitializingBean, SpringModelService { } @Override - public CompletableFuture> beans(String projectName) { + public CompletableFuture> beans(BeansParams params) { + String projectName = params.getProjectName(); CompletableFuture latestTask = this.latestScheduledTaskByProject.get(projectName); if (latestTask != null) { diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metamodel/test/SpringModelServiceTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metamodel/test/SpringModelServiceTest.java index 4e72c0e15..ff4902b46 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metamodel/test/SpringModelServiceTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metamodel/test/SpringModelServiceTest.java @@ -30,6 +30,7 @@ import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest; import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.protocol.spring.Bean; +import org.springframework.ide.vscode.commons.protocol.spring.BeansParams; import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness; import org.springframework.ide.vscode.project.harness.ProjectsHarness; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -65,7 +66,9 @@ public class SpringModelServiceTest { @Test void testSpringModelServiceBeansForNonExistingProject() throws Exception { - CompletableFuture> result = indexer.beans("random-project"); + BeansParams params = new BeansParams(); + params.setProjectName("random-project"); + CompletableFuture> result = indexer.beans(params); List list = result.get(); assertNull(list); @@ -73,7 +76,9 @@ public class SpringModelServiceTest { @Test void testBeansNameAndTypeFromBeanAnnotatedMethod() throws Exception { - CompletableFuture> result = indexer.beans("test-spring-indexing"); + BeansParams params = new BeansParams(); + params.setProjectName("test-spring-indexing"); + CompletableFuture> result = indexer.beans(params); List beans = result.get(5, TimeUnit.SECONDS); diff --git a/vscode-extensions/vscode-spring-boot/lib/api.d.ts b/vscode-extensions/vscode-spring-boot/lib/api.d.ts index c7921bf53..c2c9fef24 100644 --- a/vscode-extensions/vscode-spring-boot/lib/api.d.ts +++ b/vscode-extensions/vscode-spring-boot/lib/api.d.ts @@ -1,6 +1,7 @@ import { Event } from "vscode"; import { LanguageClient } from "vscode-languageclient/node"; import { LiveProcess } from "./notification"; +import {Location} from "vscode-languageclient"; export interface ExtensionAPI { readonly client: LanguageClient; @@ -55,7 +56,16 @@ export interface ExtensionAPI { * * Returns a list of processKeys. */ - readonly listConnectedProcesses: () => Promise + readonly listConnectedProcesses: () => Promise; + + /** + * Spring Model capable of computing beans and other static spring related information + * + * Returns Spring Model object. + */ + readonly getSpringModel: () => SpringModel; + + readonly onSpringModelUpdated: Event; } interface LiveProcessDataQuery { @@ -82,4 +92,26 @@ interface MetricsQuery extends LiveProcessDataQuery { endpoint: "metrics"; metricName: string; tags?: string; +} + +interface Bean { + readonly name: string; + readonly type: string; + readonly location: Location; + readonly injectionPoints: InjectionPoint[]; + readonly supertypes: string[]; +} + +interface InjectionPoint { + readonly name: string; + readonly type: string; + readonly location: Location; +} + +interface SpringModel { + readonly beans: (params: BeansParams) => Promise; +} + +interface BeansParams { + projectName: string; } \ No newline at end of file diff --git a/vscode-extensions/vscode-spring-boot/lib/apiManager.ts b/vscode-extensions/vscode-spring-boot/lib/apiManager.ts index 922f96fbd..7702b86cc 100644 --- a/vscode-extensions/vscode-spring-boot/lib/apiManager.ts +++ b/vscode-extensions/vscode-spring-boot/lib/apiManager.ts @@ -1,7 +1,17 @@ import { commands, Uri } from "vscode"; import { Emitter, LanguageClient } from "vscode-languageclient/node"; -import { ExtensionAPI } from "./api"; -import { LiveProcess, LiveProcessConnectedNotification, LiveProcessDisconnectedNotification, LiveProcessUpdatedNotification, LiveProcessGcPausesMetricsUpdatedNotification, LiveProcessMemoryMetricsUpdatedNotification } from "./notification"; +import {Bean, BeansParams, ExtensionAPI, SpringModel} from "./api"; +import { + LiveProcess, + LiveProcessConnectedNotification, + LiveProcessDisconnectedNotification, + LiveProcessUpdatedNotification, + LiveProcessGcPausesMetricsUpdatedNotification, + LiveProcessMemoryMetricsUpdatedNotification, + SpringModelUpdatedNotification +} from "./notification"; +import VSCode from "vscode"; +import {RequestType} from "vscode-languageclient"; export class ApiManager { public api: ExtensionAPI; @@ -10,6 +20,7 @@ export class ApiManager { private onDidLiveProcessUpdateEmitter: Emitter = new Emitter(); private onDidLiveProcessGcPausesMetricsUpdateEmitter: Emitter = new Emitter(); private onDidLiveProcessMemoryMetricsUpdateEmitter: Emitter = new Emitter(); + private onSpringModelUpdateEmitter: Emitter = new Emitter(); public constructor(client: LanguageClient) { const onDidLiveProcessConnect = this.onDidLiveProcessConnectEmitter.event; @@ -17,6 +28,7 @@ export class ApiManager { const onDidLiveProcessUpdate = this.onDidLiveProcessUpdateEmitter.event; const onDidLiveProcessGcPausesMetricsUpdate = this.onDidLiveProcessGcPausesMetricsUpdateEmitter.event; const onDidLiveProcessMemoryMetricsUpdate = this.onDidLiveProcessMemoryMetricsUpdateEmitter.event; + const onSpringModelUpdated = this.onSpringModelUpdateEmitter.event; const COMMAND_LIVEDATA_GET = "sts/livedata/get"; const getLiveProcessData = async (query) => { @@ -49,6 +61,17 @@ export class ApiManager { client.onNotification(LiveProcessGcPausesMetricsUpdatedNotification.type, (process: LiveProcess) => this.onDidLiveProcessGcPausesMetricsUpdateEmitter.fire(process)); client.onNotification(LiveProcessMemoryMetricsUpdatedNotification.type, (process: LiveProcess) => this.onDidLiveProcessMemoryMetricsUpdateEmitter.fire(process)); + client.onNotification(SpringModelUpdatedNotification.type, () => this.onSpringModelUpdateEmitter.fire()); + + const beansRequestType = new RequestType('spring/beans'); + const beans = (params: BeansParams) => { + return client.sendRequest(beansRequestType, params); + } + + const getSpringModel = () => ({ + beans + }) + this.api = { client, onDidLiveProcessConnect, @@ -56,11 +79,13 @@ export class ApiManager { onDidLiveProcessUpdate, onDidLiveProcessMemoryMetricsUpdate, onDidLiveProcessGcPausesMetricsUpdate, + onSpringModelUpdated, getLiveProcessData, refreshLiveProcessData, getLiveProcessMetricsData, refreshLiveProcessMetricsData, listConnectedProcesses, + getSpringModel }; } } diff --git a/vscode-extensions/vscode-spring-boot/lib/notification.ts b/vscode-extensions/vscode-spring-boot/lib/notification.ts index b006e73f2..3194d8363 100644 --- a/vscode-extensions/vscode-spring-boot/lib/notification.ts +++ b/vscode-extensions/vscode-spring-boot/lib/notification.ts @@ -36,4 +36,8 @@ export namespace LiveProcessGcPausesMetricsUpdatedNotification { export namespace LiveProcessMemoryMetricsUpdatedNotification { export const type = new NotificationType('sts/liveprocess/memory/metrics/updated'); +} + +export namespace SpringModelUpdatedNotification { + export const type = new NotificationType('spring/model/updated'); } \ No newline at end of file