Skip to content

Commit 465d988

Browse files
Remind users to upgrade old (<21) Java and EOL Spring Boot/Framework versions (#901)
1 parent 852589a commit 465d988

14 files changed

+598
-1
lines changed

package.json

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,16 @@
288288
"command": "java.view.package.renameFile",
289289
"title": "%contributes.commands.java.view.package.renameFile%",
290290
"category": "Java"
291+
},
292+
{
293+
"command": "_java.view.modernizeJavaProject",
294+
"title": "%contributes.commands.java.view.modernizeJavaProject%",
295+
"category": "Java"
296+
},
297+
{
298+
"command": "_java.upgradeWithCopilot",
299+
"title": "%contributes.commands.java.upgradeWithCopilot%",
300+
"category": "Java"
291301
}
292302
],
293303
"configuration": {
@@ -323,6 +333,11 @@
323333
"description": "%configuration.java.dependency.packagePresentation%",
324334
"default": "flat"
325335
},
336+
"java.dependency.enableDependencyCheckup": {
337+
"type": "boolean",
338+
"description": "%configuration.java.dependency.enableDependencyCheckup%",
339+
"default": true
340+
},
326341
"java.project.exportJar.targetPath": {
327342
"type": "string",
328343
"anyOf": [
@@ -550,6 +565,14 @@
550565
{
551566
"command": "_java.project.create.from.javaprojectexplorer",
552567
"when": "false"
568+
},
569+
{
570+
"command": "_java.view.modernizeJavaProject",
571+
"when": "false"
572+
},
573+
{
574+
"command": "_java.upgradeWithCopilot",
575+
"when": "false"
553576
}
554577
],
555578
"explorer/context": [
@@ -568,6 +591,11 @@
568591
"when": "explorerResourceIsFolder",
569592
"group": "1_javaactions@30"
570593
},
594+
{
595+
"command": "_java.view.modernizeJavaProject",
596+
"when": "explorerResourceIsFolder && java:serverMode && isModernizationExtensionInstalled",
597+
"group": "1_javaactions@40"
598+
},
571599
{
572600
"command": "java.view.package.revealInProjectExplorer",
573601
"when": "resourceFilename =~ /(.*\\.gradle)|(.*\\.gradle\\.kts)|(pom\\.xml)$/ && java:serverMode == Standard",
@@ -1092,4 +1120,4 @@
10921120
"vscode-extension-telemetry-wrapper": "^0.14.0",
10931121
"vscode-tas-client": "^0.1.75"
10941122
}
1095-
}
1123+
}

package.nls.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"contributes.commands.java.view.package.copyRelativeFilePath": "Copy Relative Path",
2525
"contributes.commands.java.view.package.new": "New...",
2626
"contributes.commands.java.view.package.newJavaClass": "Class...",
27+
"contributes.commands.java.view.modernizeJavaProject": "Modernize Java project",
2728
"contributes.commands.java.view.package.newJavaInterface": "Interface...",
2829
"contributes.commands.java.view.package.newJavaEnum": "Enum...",
2930
"contributes.commands.java.view.package.newJavaRecord": "Record...",
@@ -38,11 +39,13 @@
3839
"contributes.commands.java.view.fileExplorer.newPackage": "New Java Package...",
3940
"contributes.submenus.javaProject.new": "New",
4041
"contributes.commands.java.view.menus.file.newJavaClass": "New Java File",
42+
"contributes.commands.java.upgradeWithCopilot": "Upgrade dependencies",
4143
"configuration.java.dependency.showMembers": "Show the members in the explorer",
4244
"configuration.java.dependency.syncWithFolderExplorer": "Link Java Projects Explorer with the active editor",
4345
"configuration.java.dependency.autoRefresh": "Synchronize Java Projects explorer with changes",
4446
"configuration.java.dependency.refreshDelay": "The delay time (ms) the auto refresh is invoked when changes are detected",
4547
"configuration.java.dependency.packagePresentation": "Package presentation mode: flat or hierarchical",
48+
"configuration.java.dependency.enableDependencyCheckup": "Show reminders when your Java runtimes or dependencies need an upgrade.",
4649
"configuration.java.project.explorer.showNonJavaResources": "When enabled, the explorer shows non-Java resources.",
4750
"configuration.java.project.exportJar.targetPath.customization": "The output path of the exported jar. Leave it empty if you want to manually pick the output location.",
4851
"configuration.java.project.exportJar.targetPath.workspaceFolder": "Export the jar file into the workspace folder. Its name is the same as the folder's.",

src/commands.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ export namespace Commands {
4242

4343
export const VIEW_PACKAGE_NEW_JAVA_CLASS = "java.view.package.newJavaClass";
4444

45+
export const VIEW_MODERNIZE_JAVA_PROJECT = "_java.view.modernizeJavaProject";
46+
4547
export const VIEW_PACKAGE_NEW_JAVA_INTERFACE = "java.view.package.newJavaInterface";
4648

4749
export const VIEW_PACKAGE_NEW_JAVA_ENUM = "java.view.package.newJavaEnum";
@@ -132,6 +134,8 @@ export namespace Commands {
132134

133135
export const JAVA_PROJECT_CHECK_IMPORT_STATUS = "java.project.checkImportStatus";
134136

137+
export const JAVA_UPGRADE_WITH_COPILOT = "_java.upgradeWithCopilot";
138+
135139
/**
136140
* Commands from Visual Studio Code
137141
*/
@@ -156,6 +160,11 @@ export namespace Commands {
156160

157161
export const BUILD_PROJECT = "java.project.build";
158162

163+
/**
164+
* Commands from Java Upgrade Tool
165+
*/
166+
export const GOTO_AGENT_MODE = "javaupgrade.gotoAgentMode";
167+
159168
/**
160169
* Get the project settings
161170
*/

src/constants.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ export namespace Explorer {
3333

3434
export namespace ExtensionName {
3535
export const JAVA_LANGUAGE_SUPPORT: string = "redhat.java";
36+
export const APP_MODERNIZATION_FOR_JAVA = "vscjava.migrate-java-to-azure";
37+
export const APP_MODERNIZATION_UPGRADE_FOR_JAVA = "vscjava.vscode-java-upgrade";
38+
}
39+
40+
export namespace Upgrade {
41+
export const PACKAGE_ID_FOR_JAVA_RUNTIME = "java:*";
3642
}
3743

3844
/**

src/extension.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ import { DiagnosticProvider } from "./tasks/buildArtifact/migration/DiagnosticPr
2020
import { setContextForDeprecatedTasks, updateExportTaskType } from "./tasks/buildArtifact/migration/utils";
2121
import { CodeActionProvider } from "./tasks/buildArtifact/migration/CodeActionProvider";
2222
import { newJavaFile } from "./explorerCommands/new";
23+
import upgradeManager from "./upgrade/upgradeManager";
2324

2425
export async function activate(context: ExtensionContext): Promise<void> {
2526
contextManager.initialize(context);
27+
upgradeManager.initialize(context);
2628
await initializeFromJsonFile(context.asAbsolutePath("./package.json"));
2729
await initExpService(context);
2830
await instrumentOperation("activation", activateExtension)(context);

src/settings.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ export class Settings {
108108
return workspace.getConfiguration("java.dependency").get("refreshDelay", 2000);
109109
}
110110

111+
public static getEnableDependencyCheckup() {
112+
return workspace.getConfiguration("java.dependency").get("enableDependencyCheckup", true);
113+
}
114+
111115
public static getExportJarTargetPath(): string {
112116
// tslint:disable-next-line: no-invalid-template-strings
113117
return workspace.getConfiguration("java.project.exportJar").get<string>("targetPath", "${workspaceFolder}/${workspaceFolderBasename}.jar");

src/syncHandler.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { DataNode } from "./views/dataNode";
1313
import { ExplorerNode } from "./views/explorerNode";
1414
import { explorerNodeCache } from "./views/nodeCache/explorerNodeCache";
1515
import { Jdtls } from "./java/jdtls";
16+
import upgradeManager from "./upgrade/upgradeManager";
1617

1718
const ENABLE_AUTO_REFRESH: string = "java.view.package.enableAutoRefresh";
1819
const DISABLE_AUTO_REFRESH: string = "java.view.package.disableAutoRefresh";
@@ -46,6 +47,7 @@ class SyncHandler implements Disposable {
4647

4748
this.disposables.push(workspace.onDidChangeWorkspaceFolders(() => {
4849
this.refresh();
50+
upgradeManager.scan();
4951
}));
5052

5153
try {

src/upgrade/assessmentManager.ts

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
import * as semver from 'semver';
5+
import { Jdtls } from "../java/jdtls";
6+
import { NodeKind, type INodeData } from "../java/nodeData";
7+
import { type DependencyCheckItem, UpgradeReason, type UpgradeIssue } from "./type";
8+
import { DEPENDENCY_JAVA_RUNTIME } from "./dependency.metadata";
9+
import { Upgrade } from '../constants';
10+
import { buildPackageId } from './utility';
11+
import metadataManager from './metadataManager';
12+
import { sendInfo } from 'vscode-extension-telemetry-wrapper';
13+
14+
function getJavaIssues(data: INodeData): UpgradeIssue[] {
15+
const javaVersion = data.metaData?.MaxSourceVersion as number | undefined;
16+
const { name, supportedVersion } = DEPENDENCY_JAVA_RUNTIME;
17+
if (!javaVersion) {
18+
return [];
19+
}
20+
const currentSemVer = semver.coerce(javaVersion);
21+
if (currentSemVer && !semver.satisfies(currentSemVer, supportedVersion)) {
22+
return [{
23+
...DEPENDENCY_JAVA_RUNTIME,
24+
packageId: Upgrade.PACKAGE_ID_FOR_JAVA_RUNTIME,
25+
packageDisplayName: name,
26+
currentVersion: String(javaVersion),
27+
}];
28+
}
29+
30+
return [];
31+
}
32+
33+
function getUpgradeForDependency(versionString: string, supportedVersionDefinition: DependencyCheckItem, packageId: string): UpgradeIssue | null {
34+
const { reason } = supportedVersionDefinition;
35+
switch (reason) {
36+
case UpgradeReason.DEPRECATED: {
37+
return {
38+
...supportedVersionDefinition,
39+
packageDisplayName: supportedVersionDefinition.name,
40+
reason,
41+
currentVersion: versionString,
42+
packageId,
43+
};
44+
}
45+
case UpgradeReason.END_OF_LIFE: {
46+
const currentSemVer = semver.coerce(versionString);
47+
if (currentSemVer && !semver.satisfies(currentSemVer, supportedVersionDefinition.supportedVersion)) {
48+
return {
49+
...supportedVersionDefinition,
50+
packageDisplayName: supportedVersionDefinition.name,
51+
reason,
52+
currentVersion: versionString,
53+
packageId,
54+
};
55+
}
56+
}
57+
}
58+
59+
return null;
60+
}
61+
62+
function getDependencyIssue(data: INodeData): UpgradeIssue | null {
63+
const versionString = data.metaData?.["maven.version"];
64+
const groupId = data.metaData?.["maven.groupId"];
65+
const artifactId = data.metaData?.["maven.artifactId"];
66+
const packageId = buildPackageId(groupId, artifactId);
67+
const supportedVersionDefinition = metadataManager.getMetadataById(packageId);
68+
if (!versionString || !groupId || !supportedVersionDefinition) {
69+
return null;
70+
}
71+
72+
return getUpgradeForDependency(versionString, supportedVersionDefinition, packageId);
73+
}
74+
75+
async function getDependencyIssues(projectNode: INodeData): Promise<UpgradeIssue[]> {
76+
const projectStructureData = await Jdtls.getPackageData({ kind: NodeKind.Project, projectUri: projectNode.uri });
77+
const packageContainerIssues = await Promise.allSettled(
78+
projectStructureData
79+
.filter(x => x.kind === NodeKind.Container)
80+
.map(async (packageContainer) => {
81+
const packages = await Jdtls.getPackageData({
82+
kind: NodeKind.Container,
83+
projectUri: projectNode.uri,
84+
path: packageContainer.path,
85+
});
86+
87+
return packages.map(getDependencyIssue).filter((x): x is UpgradeIssue => Boolean(x));
88+
})
89+
);
90+
91+
return packageContainerIssues
92+
.map(x => {
93+
if (x.status === "fulfilled") {
94+
return x.value;
95+
}
96+
97+
sendInfo("", {
98+
operationName: "java.dependency.assessmentManager.getDependencyIssues",
99+
});
100+
return [];
101+
})
102+
.reduce((a, b) => [...a, ...b]);
103+
}
104+
105+
async function getProjectIssues(projectNode: INodeData): Promise<UpgradeIssue[]> {
106+
const issues: UpgradeIssue[] = [];
107+
issues.push(...getJavaIssues(projectNode));
108+
issues.push(...(await getDependencyIssues(projectNode)));
109+
return issues;
110+
}
111+
112+
async function getWorkspaceIssues(workspaceFolderUri: string): Promise<UpgradeIssue[]> {
113+
const projects = await Jdtls.getProjects(workspaceFolderUri);
114+
const projectsIssues = await Promise.allSettled(projects.map(async (projectNode) => {
115+
const issues = await getProjectIssues(projectNode);
116+
sendInfo("", {
117+
operationName: "java.dependency.assessmentManager.getWorkspaceIssues",
118+
issuesFoundForPackageId: JSON.stringify(issues.map(x => `${x.packageId}:${x.currentVersion}`)),
119+
});
120+
return issues;
121+
}));
122+
123+
const workspaceIssues = projectsIssues.map(x => {
124+
if (x.status === "fulfilled") {
125+
return x.value;
126+
}
127+
128+
sendInfo("", {
129+
operationName: "java.dependency.assessmentManager.getWorkspaceIssues",
130+
});
131+
return [];
132+
}).reduce((a, b) => [...a, ...b]);
133+
134+
return workspaceIssues;
135+
}
136+
137+
export default {
138+
getWorkspaceIssues,
139+
};

0 commit comments

Comments
 (0)