Skip to content

Commit 508b9a5

Browse files
authored
feat: Report messages when exporting jar (#499)
* report message
1 parent ca75d11 commit 508b9a5

File tree

8 files changed

+101
-51
lines changed

8 files changed

+101
-51
lines changed

jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java

Lines changed: 54 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,16 @@
2929
import java.util.zip.ZipFile;
3030

3131
import org.apache.commons.io.FilenameUtils;
32+
import org.apache.commons.lang3.StringUtils;
3233
import org.eclipse.core.resources.IFile;
3334
import org.eclipse.core.resources.IProject;
3435
import org.eclipse.core.resources.IWorkspaceRoot;
3536
import org.eclipse.core.resources.ResourcesPlugin;
3637
import org.eclipse.core.runtime.CoreException;
3738
import org.eclipse.core.runtime.IPath;
3839
import org.eclipse.core.runtime.IProgressMonitor;
40+
import org.eclipse.core.runtime.IStatus;
41+
import org.eclipse.core.runtime.MultiStatus;
3942
import org.eclipse.core.runtime.NullProgressMonitor;
4043
import org.eclipse.core.runtime.Path;
4144
import org.eclipse.jdt.core.IJavaElement;
@@ -67,6 +70,8 @@
6770

6871
public final class ProjectCommand {
6972

73+
private static String COMMAND_EXPORT_JAR_REPORT = "java.view.package.exportJarReport";
74+
7075
private static class MainClassInfo {
7176
public String name;
7277
public String path;
@@ -83,23 +88,6 @@ private static class Classpath {
8388
public boolean isArtifact;
8489
}
8590

86-
private static class ExportResult {
87-
public boolean result;
88-
public String message;
89-
public String log;
90-
91-
ExportResult(boolean result) {
92-
this.result = result;
93-
this.log = "";
94-
}
95-
96-
ExportResult(boolean result, String message) {
97-
this.result = result;
98-
this.message = message;
99-
this.log = "";
100-
}
101-
}
102-
10391
private static final Gson gson = new GsonBuilder().registerTypeAdapterFactory(new CollectionTypeAdapter.Factory())
10492
.registerTypeAdapterFactory(new EnumTypeAdapter.Factory()).create();
10593

@@ -151,16 +139,14 @@ private static IWorkspaceRoot getWorkspaceRoot() {
151139
return ResourcesPlugin.getWorkspace().getRoot();
152140
}
153141

154-
public static ExportResult exportJar(List<Object> arguments, IProgressMonitor monitor) {
155-
if (arguments.size() < 3) {
156-
return new ExportResult(false, "Invalid export Arguments");
157-
}
158-
if (monitor.isCanceled()) {
159-
return new ExportResult(false, "User cancelled");
142+
public static boolean exportJar(List<Object> arguments, IProgressMonitor monitor) {
143+
if (arguments.size() < 4) {
144+
return false;
160145
}
161146
String mainClass = gson.fromJson(gson.toJson(arguments.get(0)), String.class);
162147
Classpath[] classpaths = gson.fromJson(gson.toJson(arguments.get(1)), Classpath[].class);
163148
String destination = gson.fromJson(gson.toJson(arguments.get(2)), String.class);
149+
String taskLabel = gson.fromJson(gson.toJson(arguments.get(3)), String.class);
164150
Manifest manifest = new Manifest();
165151
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
166152
if (mainClass.length() > 0) {
@@ -170,24 +156,39 @@ public static ExportResult exportJar(List<Object> arguments, IProgressMonitor mo
170156
Set<String> directories = new HashSet<>();
171157
for (Classpath classpath : classpaths) {
172158
if (monitor.isCanceled()) {
173-
return new ExportResult(false, "User cancelled");
159+
return false;
174160
}
175161
if (classpath.isArtifact) {
176-
writeArchive(new ZipFile(classpath.source), /* areDirectoryEntriesIncluded = */true,
177-
/* isCompressed = */true, target, directories, monitor);
162+
MultiStatus resultStatus = writeArchive(new ZipFile(classpath.source),
163+
/* areDirectoryEntriesIncluded = */true, /* isCompressed = */true, target, directories, monitor);
164+
int severity = resultStatus.getSeverity();
165+
if (severity == IStatus.OK) {
166+
java.nio.file.Path path = java.nio.file.Paths.get(classpath.source);
167+
reportExportJarMessage(taskLabel, IStatus.OK, "Successfully added the file to the exported jar: " + path.getFileName().toString());
168+
continue;
169+
}
170+
if (resultStatus.isMultiStatus()) {
171+
for (IStatus childStatus : resultStatus.getChildren()) {
172+
reportExportJarMessage(taskLabel, severity, childStatus.getMessage());
173+
}
174+
} else {
175+
reportExportJarMessage(taskLabel, severity, resultStatus.getMessage());
176+
}
178177
} else {
179178
try {
180179
writeFile(new File(classpath.source), new Path(classpath.destination), /* areDirectoryEntriesIncluded = */true,
181180
/* isCompressed = */true, target, directories);
181+
reportExportJarMessage(taskLabel, IStatus.OK, "Successfully added the file to the exported jar: " + classpath.destination);
182182
} catch (CoreException e) {
183-
// TODO: Collect reports
183+
reportExportJarMessage(taskLabel, IStatus.ERROR, e.getMessage());
184184
}
185185
}
186186
}
187187
} catch (IOException e) {
188-
return new ExportResult(false, e.getMessage());
188+
reportExportJarMessage(taskLabel, IStatus.ERROR, e.getMessage());
189+
return false;
189190
}
190-
return new ExportResult(true);
191+
return true;
191192
}
192193

193194
public static List<MainClassInfo> getMainClasses(List<Object> arguments, IProgressMonitor monitor) throws Exception {
@@ -253,4 +254,28 @@ public static String getModuleName(IJavaProject project) {
253254
}
254255
}
255256

257+
private static void reportExportJarMessage(String taskLabel, int severity, String message) {
258+
if (StringUtils.isNotBlank(message) && StringUtils.isNotBlank(taskLabel)) {
259+
String readableSeverity = getSeverityString(severity);
260+
JavaLanguageServerPlugin.getInstance().getClientConnection().executeClientCommand(COMMAND_EXPORT_JAR_REPORT,
261+
taskLabel, "[" + readableSeverity + "] " + message);
262+
}
263+
}
264+
265+
private static String getSeverityString(int severity) {
266+
switch (severity) {
267+
case IStatus.INFO:
268+
return "INFO";
269+
case IStatus.WARNING:
270+
return "WARNING";
271+
case IStatus.ERROR:
272+
return "ERROR";
273+
case IStatus.CANCEL:
274+
return "CANCEL";
275+
case IStatus.OK:
276+
return "OK";
277+
default:
278+
return "UNKNOWN STATUS";
279+
}
280+
}
256281
}

src/commands.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export namespace Commands {
3030

3131
export const VIEW_PACKAGE_EXPORT_JAR = "java.view.package.exportJar";
3232

33+
export const EXPORT_JAR_REPORT = "java.view.package.exportJarReport";
34+
3335
export const VIEW_PACKAGE_NEW_JAVA_CLASS = "java.view.package.newJavaClass";
3436

3537
export const VIEW_PACKAGE_NEW_JAVA_PACKAGE = "java.view.package.newPackage";

src/exportJarSteps/ExportJarTaskProvider.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import { lstat } from "fs-extra";
55
import * as globby from "globby";
66
import * as _ from "lodash";
7-
import { platform } from "os";
7+
import { EOL, platform } from "os";
88
import { dirname, extname, isAbsolute, join, relative } from "path";
99
import {
1010
CustomExecution, Event, EventEmitter, Pseudoterminal, Task, TaskDefinition,
@@ -44,6 +44,7 @@ export async function executeExportJarTask(node?: INodeData): Promise<void> {
4444
isExportingJar = true;
4545
const stepMetadata: IStepMetadata = {
4646
entry: node,
47+
taskLabel: "exportjar:default",
4748
steps: [],
4849
projectList: [],
4950
elements: [],
@@ -55,7 +56,7 @@ export async function executeExportJarTask(node?: INodeData): Promise<void> {
5556
throw new Error(ExportJarMessages.stepErrorMessage(ExportJarMessages.StepAction.FINDEXECUTOR, ExportJarStep.ResolveJavaProject));
5657
}
5758
await resolveJavaProjectExecutor.execute(stepMetadata);
58-
tasks.executeTask(ExportJarTaskProvider.getTask(stepMetadata));
59+
tasks.executeTask(ExportJarTaskProvider.getDefaultTask(stepMetadata));
5960
} catch (err) {
6061
if (err) {
6162
failMessage(`${err}`);
@@ -68,13 +69,13 @@ export class ExportJarTaskProvider implements TaskProvider {
6869

6970
public static exportJarType: string = "java";
7071

71-
public static getTask(stepMetadata: IStepMetadata): Task {
72+
public static getDefaultTask(stepMetadata: IStepMetadata): Task {
7273
if (!stepMetadata.workspaceFolder) {
7374
throw new Error(ExportJarMessages.fieldUndefinedMessage(ExportJarMessages.Field.WORKSPACEFOLDER, ExportJarStep.ResolveTask));
7475
}
7576
const defaultDefinition: IExportJarTaskDefinition = {
7677
type: ExportJarTaskProvider.exportJarType,
77-
label: `${ExportJarTaskProvider.exportJarType}: exportjar:default`,
78+
label: "exportjar:default",
7879
targetPath: Settings.getExportJarTargetPath(),
7980
elements: [],
8081
mainClass: undefined,
@@ -93,9 +94,10 @@ export class ExportJarTaskProvider implements TaskProvider {
9394
const definition: IExportJarTaskDefinition = <IExportJarTaskDefinition>task.definition;
9495
const folder: WorkspaceFolder = <WorkspaceFolder>task.scope;
9596
const resolvedTask: Task = new Task(definition, folder, task.name, ExportJarTaskProvider.exportJarType,
96-
new CustomExecution(async (resolvedDefinition: TaskDefinition): Promise<Pseudoterminal> => {
97+
new CustomExecution(async (resolvedDefinition: IExportJarTaskDefinition): Promise<Pseudoterminal> => {
9798
const stepMetadata: IStepMetadata = {
9899
entry: undefined,
100+
taskLabel: resolvedDefinition.label || `exportjar:${folder.name}`,
99101
workspaceFolder: folder,
100102
projectList: await Jdtls.getProjects(folder.uri.toString()),
101103
steps: [],
@@ -134,15 +136,15 @@ export class ExportJarTaskProvider implements TaskProvider {
134136
const mainClasses: IMainClassInfo[] = await Jdtls.getMainClasses(folder.uri.toString());
135137
const defaultDefinition: IExportJarTaskDefinition = {
136138
type: ExportJarTaskProvider.exportJarType,
137-
label: `${ExportJarTaskProvider.exportJarType}: exportjar:${folder.name}`,
138139
mainClass: (mainClasses.length === 1) ? mainClasses[0].name : undefined,
139140
targetPath: Settings.getExportJarTargetPath(),
140141
elements: elementList,
141142
};
142-
const defaultTask: Task = new Task(defaultDefinition, folder, `exportjar:${folder.name}`,
143-
ExportJarTaskProvider.exportJarType, new CustomExecution(async (resolvedDefinition: TaskDefinition): Promise<Pseudoterminal> => {
143+
const defaultTask: Task = new Task(defaultDefinition, folder, `exportjar:${folder.name}`, ExportJarTaskProvider.exportJarType,
144+
new CustomExecution(async (resolvedDefinition: IExportJarTaskDefinition): Promise<Pseudoterminal> => {
144145
const stepMetadata: IStepMetadata = {
145146
entry: undefined,
147+
taskLabel: resolvedDefinition.label || `exportjar:${folder.name}`,
146148
workspaceFolder: folder,
147149
projectList: await Jdtls.getProjects(folder.uri.toString()),
148150
steps: [],
@@ -170,11 +172,16 @@ class ExportJarTaskTerminal implements Pseudoterminal {
170172

171173
constructor(exportJarTaskDefinition: IExportJarTaskDefinition, stepMetadata: IStepMetadata) {
172174
this.stepMetadata = stepMetadata;
175+
this.stepMetadata.taskLabel = exportJarTaskDefinition.label || "";
173176
this.stepMetadata.mainClass = exportJarTaskDefinition.mainClass;
174177
this.stepMetadata.outputPath = exportJarTaskDefinition.targetPath;
175178
this.stepMetadata.elements = exportJarTaskDefinition.elements || [];
176179
}
177180

181+
public handleInput(data: string): void {
182+
this.writeEmitter.fire(data + EOL);
183+
}
184+
178185
public async open(_initialDimensions: TerminalDimensions | undefined): Promise<void> {
179186
let exportResult: boolean | undefined;
180187
try {

src/exportJarSteps/GenerateJarExecutor.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { Jdtls } from "../java/jdtls";
1111
import { INodeData } from "../java/nodeData";
1212
import { IExportJarStepExecutor } from "./IExportJarStepExecutor";
1313
import { IClasspath, IStepMetadata } from "./IStepMetadata";
14-
import { createPickBox, ExportJarMessages, ExportJarStep, ExportJarTargets, getExtensionApi, toPosixPath } from "./utility";
14+
import { createPickBox, ExportJarMessages, ExportJarStep, ExportJarTargets, getExtensionApi, revealTerminal, toPosixPath } from "./utility";
1515

1616
export class GenerateJarExecutor implements IExportJarStepExecutor {
1717

@@ -82,12 +82,14 @@ export class GenerateJarExecutor implements IExportJarStepExecutor {
8282
if (_.isEmpty(classpaths)) {
8383
return reject(new Error(ExportJarMessages.CLASSPATHS_EMPTY));
8484
}
85-
const exportResult: IExportResult | undefined = await Jdtls.exportJar(basename(mainClass), classpaths, destPath, token);
86-
if (exportResult?.result === true) {
85+
revealTerminal(stepMetadata.taskLabel);
86+
const exportResult: boolean | undefined = await Jdtls.exportJar(basename(mainClass),
87+
classpaths, destPath, stepMetadata.taskLabel, token);
88+
if (exportResult === true) {
8789
stepMetadata.outputPath = destPath;
8890
return resolve(true);
8991
} else {
90-
return reject(new Error("Export jar failed." + exportResult?.message));
92+
return reject(new Error("Export jar failed."));
9193
}
9294
});
9395
});
@@ -249,9 +251,3 @@ interface IJarQuickPickItem extends QuickPickItem {
249251
path: string;
250252
type: string;
251253
}
252-
253-
export interface IExportResult {
254-
result: boolean;
255-
message: string;
256-
log?: string;
257-
}

src/exportJarSteps/IStepMetadata.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { ExportJarStep } from "./utility";
77

88
export interface IStepMetadata {
99
entry?: INodeData;
10+
taskLabel: string;
1011
workspaceFolder?: WorkspaceFolder;
1112
mainClass?: string;
1213
outputPath?: string;

src/exportJarSteps/utility.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,19 @@ export async function getExtensionApi(): Promise<any> {
153153
}
154154
return extensionApi;
155155
}
156+
157+
export function showExportJarReport(taskLabel: string, message: string) {
158+
const terminals = window.terminals;
159+
const presenterTerminals = terminals.filter((terminal) => terminal.name.indexOf(taskLabel) >= 0);
160+
if (presenterTerminals.length > 0) {
161+
presenterTerminals[presenterTerminals.length - 1].sendText(message);
162+
}
163+
}
164+
165+
export function revealTerminal(terminalName: string) {
166+
const terminals = window.terminals;
167+
const presenterTerminals = terminals.filter((terminal) => terminal.name.indexOf(terminalName) >= 0);
168+
if (presenterTerminals.length > 0) {
169+
presenterTerminals[presenterTerminals.length - 1].show();
170+
}
171+
}

src/java/jdtls.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
import { CancellationToken, commands } from "vscode";
55
import { Commands, executeJavaLanguageServerCommand } from "../commands";
6-
import { IExportResult } from "../exportJarSteps/GenerateJarExecutor";
76
import { IClasspath } from "../exportJarSteps/IStepMetadata";
87
import { IMainClassInfo } from "../exportJarSteps/ResolveMainClassExecutor";
98
import { INodeData } from "./nodeData";
@@ -17,7 +16,7 @@ export namespace Jdtls {
1716
return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_PROJECT_REFRESH_LIB_SERVER, params);
1817
}
1918

20-
export async function getPackageData(params: {[key: string]: any}): Promise<INodeData[]> {
19+
export async function getPackageData(params: { [key: string]: any }): Promise<INodeData[]> {
2120
return await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_GETPACKAGEDATA, params) || [];
2221
}
2322

@@ -30,9 +29,9 @@ export namespace Jdtls {
3029
}
3130

3231
export async function exportJar(mainClass: string, classpaths: IClasspath[],
33-
destination: string, token: CancellationToken): Promise<IExportResult | undefined> {
32+
destination: string, taskLabel: string, token: CancellationToken): Promise<boolean | undefined> {
3433
return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_PROJECT_GENERATEJAR,
35-
mainClass, classpaths, destination, token);
34+
mainClass, classpaths, destination, taskLabel, token);
3635
}
3736

3837
export enum CompileWorkspaceStatus {

src/views/dependencyDataProvider.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { contextManager } from "../../extension.bundle";
1111
import { Commands } from "../commands";
1212
import { Context } from "../constants";
1313
import { executeExportJarTask } from "../exportJarSteps/ExportJarTaskProvider";
14+
import { showExportJarReport } from "../exportJarSteps/utility";
1415
import { Jdtls } from "../java/jdtls";
1516
import { INodeData, NodeKind } from "../java/nodeData";
1617
import { languageServerApiManager } from "../languageServerApi/languageServerApiManager";
@@ -35,6 +36,9 @@ export class DependencyDataProvider implements TreeDataProvider<ExplorerNode> {
3536
constructor(public readonly context: ExtensionContext) {
3637
context.subscriptions.push(commands.registerCommand(Commands.VIEW_PACKAGE_REFRESH, (debounce?: boolean, element?: ExplorerNode) =>
3738
this.refreshWithLog(debounce, element)));
39+
context.subscriptions.push(commands.registerCommand(Commands.EXPORT_JAR_REPORT, (taskLabel: string, message: string) => {
40+
showExportJarReport(taskLabel, message);
41+
}));
3842
context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_EXPORT_JAR, async (node: INodeData) => {
3943
executeExportJarTask(node);
4044
}));

0 commit comments

Comments
 (0)