Skip to content

Commit d0326b3

Browse files
CsCherrYYjdneo
andauthored
Specific target folder when exporting jar and enhancement of jdtls.ext (#342)
Co-authored-by: Sheng Chen <[email protected]>
1 parent ba72d5d commit d0326b3

File tree

15 files changed

+318
-123
lines changed

15 files changed

+318
-123
lines changed

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

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666

6767
public final class ProjectCommand {
6868

69-
public static class MainClassInfo {
69+
private static class MainClassInfo {
7070

7171
public String name;
7272

@@ -78,10 +78,25 @@ public MainClassInfo(String name, String path) {
7878
}
7979
}
8080

81-
private static final Gson gson = new GsonBuilder()
82-
.registerTypeAdapterFactory(new CollectionTypeAdapter.Factory())
83-
.registerTypeAdapterFactory(new EnumTypeAdapter.Factory())
84-
.create();
81+
private static class exportResult {
82+
public boolean result;
83+
public String message;
84+
public String log;
85+
86+
exportResult(boolean result) {
87+
this.result = result;
88+
this.log = "";
89+
}
90+
91+
exportResult(boolean result, String message) {
92+
this.result = result;
93+
this.message = message;
94+
this.log = "";
95+
}
96+
}
97+
98+
private static final Gson gson = new GsonBuilder().registerTypeAdapterFactory(new CollectionTypeAdapter.Factory())
99+
.registerTypeAdapterFactory(new EnumTypeAdapter.Factory()).create();
85100

86101
public static List<PackageNode> listProjects(List<Object> arguments, IProgressMonitor monitor) {
87102
String workspaceUri = (String) arguments.get(0);
@@ -95,9 +110,11 @@ public static List<PackageNode> listProjects(List<Object> arguments, IProgressMo
95110
if (!project.isAccessible() || !ProjectUtils.isJavaProject(project)) {
96111
continue;
97112
}
98-
// Ignore all the projects that's not contained in the workspace folder, except for the invisible project.
113+
// Ignore all the projects that's not contained in the workspace folder, except
114+
// for the invisible project.
99115
// This check is needed in multi-root scenario.
100-
if ((!ResourceUtils.isContainedIn(project.getLocation(), paths) && !Objects.equals(project.getName(), invisibleProjectName))) {
116+
if ((!ResourceUtils.isContainedIn(project.getLocation(), paths)
117+
&& !Objects.equals(project.getName(), invisibleProjectName))) {
101118
continue;
102119
}
103120
PackageNode projectNode = PackageNode.createNodeForProject(JavaCore.create(project));
@@ -115,7 +132,8 @@ public static boolean refreshLibraries(List<Object> arguments, IProgressMonitor
115132
String projectName = ProjectUtils.getWorkspaceInvisibleProjectName(workspacePath);
116133
IProject project = getWorkspaceRoot().getProject(projectName);
117134
try {
118-
ReferencedLibraries libraries = JavaLanguageServerPlugin.getPreferencesManager().getPreferences().getReferencedLibraries();
135+
ReferencedLibraries libraries = JavaLanguageServerPlugin.getPreferencesManager().getPreferences()
136+
.getReferencedLibraries();
119137
UpdateClasspathJob.getInstance().updateClasspath(JavaCore.create(project), libraries);
120138
return true;
121139
} catch (Exception e) {
@@ -128,48 +146,47 @@ private static IWorkspaceRoot getWorkspaceRoot() {
128146
return ResourcesPlugin.getWorkspace().getRoot();
129147
}
130148

131-
public static boolean exportJar(List<Object> arguments, IProgressMonitor monitor) {
149+
public static exportResult exportJar(List<Object> arguments, IProgressMonitor monitor) {
132150
if (arguments.size() < 3) {
133-
return false;
151+
return new exportResult(false, "Invalid export Arguments");
134152
}
135153
String mainMethod = gson.fromJson(gson.toJson(arguments.get(0)), String.class);
136154
String[] classpaths = gson.fromJson(gson.toJson(arguments.get(1)), String[].class);
137155
String destination = gson.fromJson(gson.toJson(arguments.get(2)), String.class);
138156
Manifest manifest = new Manifest();
139157
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
140158
if (mainMethod.length() > 0) {
141-
manifest.getMainAttributes().put(Attributes.Name.MAIN_CLASS,mainMethod);
159+
manifest.getMainAttributes().put(Attributes.Name.MAIN_CLASS, mainMethod);
142160
}
143161
try (JarOutputStream target = new JarOutputStream(new FileOutputStream(destination), manifest)) {
144-
Set<String> fDirectories = new HashSet<>();
162+
Set<String> directories = new HashSet<>();
145163
for (String classpath : classpaths) {
146164
if (classpath != null) {
147-
if(classpath.endsWith(".jar")) {
165+
if (classpath.endsWith(".jar")) {
148166
ZipFile zip = new ZipFile(classpath);
149-
writeArchive(zip, true, true, target, fDirectories, monitor);
150-
}
151-
else {
167+
writeArchive(zip, true, true, target, directories, monitor);
168+
} else {
152169
File folder = new File(classpath);
153-
writeFileRecursively(folder, target, fDirectories, folder.getAbsolutePath().length() + 1);
170+
writeFileRecursively(folder, target, directories, folder.getAbsolutePath().length() + 1);
154171
}
155172
}
156173
}
157174
} catch (Exception e) {
158-
return false;
175+
return new exportResult(false, e.getMessage());
159176
}
160-
return true;
177+
return new exportResult(true);
161178
}
162179

163-
private static void writeFileRecursively(File folder, JarOutputStream fJarOutputStream, Set<String> fDirectories, int len) {
180+
private static void writeFileRecursively(File folder, JarOutputStream jarOutputStream, Set<String> directories,
181+
int len) {
164182
File[] files = folder.listFiles();
165183
for (File file : files) {
166184
if (file.isDirectory()) {
167-
writeFileRecursively(file, fJarOutputStream, fDirectories, len);
185+
writeFileRecursively(file, jarOutputStream, directories, len);
168186
} else if (file.isFile()) {
169187
try {
170-
writeFile(file, new Path(file.getAbsolutePath().substring(len)), true, true, fJarOutputStream, fDirectories);
171-
}
172-
catch (Exception e) {
188+
writeFile(file, new Path(file.getAbsolutePath().substring(len)), true, true, jarOutputStream, directories);
189+
} catch (Exception e) {
173190
// do nothing
174191
}
175192
}
@@ -219,8 +236,8 @@ public void acceptSearchMatch(SearchMatch match) {
219236
};
220237
SearchEngine searchEngine = new SearchEngine();
221238
try {
222-
searchEngine.search(pattern, new SearchParticipant[] {SearchEngine.getDefaultSearchParticipant()},
223-
scope, requestor, new NullProgressMonitor());
239+
searchEngine.search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, scope,
240+
requestor, new NullProgressMonitor());
224241
} catch (CoreException e) {
225242
// ignore
226243
}

package-lock.json

Lines changed: 6 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,15 @@
178178
],
179179
"description": "%configuration.java.dependency.packagePresentation%",
180180
"default": "flat"
181+
},
182+
"java.project.exportJar.targetPath": {
183+
"type": "string",
184+
"enum": [
185+
"${workspaceFolder}/${workspaceFolderBasename}.jar",
186+
"askUser"
187+
],
188+
"description": "%configuration.java.project.exportJar.targetPath%",
189+
"default": "${workspaceFolder}/${workspaceFolderBasename}.jar"
181190
}
182191
}
183192
},

package.nls.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@
2222
"configuration.java.dependency.syncWithFolderExplorer": "Synchronize Java Projects explorer selection with folder explorer",
2323
"configuration.java.dependency.autoRefresh": "Synchronize Java Projects explorer with changes",
2424
"configuration.java.dependency.refreshDelay": "The delay time (ms) the auto refresh is invoked when changes are detected",
25-
"configuration.java.dependency.packagePresentation": "Package presentation mode: flat or hierarchical"
25+
"configuration.java.dependency.packagePresentation": "Package presentation mode: flat or hierarchical",
26+
"configuration.java.project.exportJar.targetPath": "The default output path of export jar"
2627
}

package.nls.zh.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@
2222
"configuration.java.dependency.syncWithFolderExplorer": "在 Java 项目管理器中同步关联当前打开的文件",
2323
"configuration.java.dependency.autoRefresh": "在 Java 项目管理器中自动同步修改",
2424
"configuration.java.dependency.refreshDelay": "控制 Java 项目管理器刷新的延迟时间 (毫秒)",
25-
"configuration.java.dependency.packagePresentation": "Java 包显示方式: 平行显示或者分层显示"
25+
"configuration.java.dependency.packagePresentation": "Java 包显示方式: 平行显示或者分层显示",
26+
"configuration.java.project.exportJar.targetPath": "导出 Jar 文件的默认路径"
2627
}

src/exportJarFileCommand.ts

Lines changed: 23 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

4-
import { EOL, platform } from "os";
5-
import { commands, Uri, window } from "vscode";
6-
import { sendOperationError } from "vscode-extension-telemetry-wrapper";
4+
import { tasks } from "vscode";
75
import { buildWorkspace } from "./build";
6+
import { ExportJarTaskProvider } from "./exportJarSteps/ExportJarTaskProvider";
87
import { GenerateJarExecutor } from "./exportJarSteps/GenerateJarExecutor";
98
import { IExportJarStepExecutor } from "./exportJarSteps/IExportJarStepExecutor";
109
import { IStepMetadata } from "./exportJarSteps/IStepMetadata";
1110
import { ResolveJavaProjectExecutor } from "./exportJarSteps/ResolveJavaProjectExecutor";
1211
import { ResolveMainMethodExecutor } from "./exportJarSteps/ResolveMainMethodExecutor";
12+
import { failMessage, successMessage } from "./exportJarSteps/utility";
1313
import { isStandardServerReady } from "./extension";
1414
import { INodeData } from "./java/nodeData";
1515

@@ -20,74 +20,45 @@ export enum ExportJarStep {
2020
Finish = "FINISH",
2121
}
2222

23-
let isExportingJar: boolean = false;
2423
const stepMap: Map<ExportJarStep, IExportJarStepExecutor> = new Map<ExportJarStep, IExportJarStepExecutor>([
2524
[ExportJarStep.ResolveJavaProject, new ResolveJavaProjectExecutor()],
2625
[ExportJarStep.ResolveMainMethod, new ResolveMainMethodExecutor()],
2726
[ExportJarStep.GenerateJar, new GenerateJarExecutor()],
2827
]);
2928

30-
export async function createJarFile(node?: INodeData) {
31-
if (!isStandardServerReady() || isExportingJar) {
29+
let isExportingJar: boolean = false;
30+
31+
export async function executeExportJarTask(node?: INodeData): Promise<void> {
32+
if (!isStandardServerReady() || isExportingJar || await buildWorkspace() === false) {
3233
return;
3334
}
35+
const stepMetadata: IStepMetadata = {
36+
entry: node,
37+
steps: [],
38+
};
39+
tasks.executeTask(ExportJarTaskProvider.getTask(stepMetadata));
40+
return;
41+
}
42+
43+
export async function createJarFile(stepMetadata: IStepMetadata) {
3444
isExportingJar = true;
45+
let step: ExportJarStep = ExportJarStep.ResolveJavaProject;
3546
return new Promise<string>(async (resolve, reject) => {
36-
if (await buildWorkspace() === false) {
37-
return reject();
38-
}
39-
let step: ExportJarStep = ExportJarStep.ResolveJavaProject;
40-
let stepMetadata: IStepMetadata = {
41-
entry: node,
42-
elements: [],
43-
steps: [],
44-
};
4547
while (step !== ExportJarStep.Finish) {
4648
try {
47-
const executor: IExportJarStepExecutor = stepMap.get(step);
48-
if (executor === undefined) {
49-
// Unpredictable error, return to the initialization
50-
step = ExportJarStep.ResolveJavaProject;
51-
stepMetadata = {
52-
entry: node,
53-
elements: [],
54-
steps: [],
55-
};
56-
} else {
57-
step = await executor.execute(stepMetadata);
58-
}
49+
step = await stepMap.get(step).execute(stepMetadata);
5950
} catch (err) {
60-
return err ? reject(`${err}`) : reject();
51+
return reject(err);
6152
}
6253
}
6354
return resolve(stepMetadata.outputPath);
6455
}).then((message) => {
65-
successMessage(message);
6656
isExportingJar = false;
57+
successMessage(message);
6758
}, (err) => {
68-
failMessage(err);
6959
isExportingJar = false;
60+
if (err) {
61+
failMessage(`${err}`);
62+
}
7063
});
7164
}
72-
73-
function failMessage(message: string) {
74-
sendOperationError("", "Export Jar", new Error(message));
75-
window.showErrorMessage(message, "Done");
76-
}
77-
78-
function successMessage(outputFileName: string) {
79-
let openInExplorer: string;
80-
if (platform() === "win32") {
81-
openInExplorer = "Reveal in File Explorer";
82-
} else if (platform() === "darwin") {
83-
openInExplorer = "Reveal in Finder";
84-
} else {
85-
openInExplorer = "Open Containing Folder";
86-
}
87-
window.showInformationMessage("Successfully exported jar to" + EOL + outputFileName,
88-
openInExplorer, "Done").then((messageResult) => {
89-
if (messageResult === openInExplorer) {
90-
commands.executeCommand("revealFileInOS", Uri.file(outputFileName));
91-
}
92-
});
93-
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
import {
5+
CustomExecution, Event, EventEmitter, Pseudoterminal, Task, TaskDefinition,
6+
TaskProvider, TaskRevealKind, TaskScope, TerminalDimensions,
7+
} from "vscode";
8+
import { createJarFile } from "../exportJarFileCommand";
9+
import { Settings } from "../settings";
10+
import { IStepMetadata } from "./IStepMetadata";
11+
12+
export class ExportJarTaskProvider implements TaskProvider {
13+
14+
public static exportJarType: string = "java";
15+
16+
public static getTask(stepMetadata: IStepMetadata): Task {
17+
const targetPathSetting: string = Settings.getExportJarTargetPath();
18+
const defaultDefinition: IExportJarTaskDefinition = {
19+
type: ExportJarTaskProvider.exportJarType,
20+
targetPath: targetPathSetting,
21+
elements: [],
22+
mainMethod: undefined,
23+
};
24+
const task: Task = new Task(defaultDefinition, TaskScope.Workspace, "exportjar:default", ExportJarTaskProvider.exportJarType,
25+
new CustomExecution(async (resolvedDefinition: TaskDefinition): Promise<Pseudoterminal> => {
26+
return new ExportJarTaskTerminal(resolvedDefinition, stepMetadata);
27+
}));
28+
task.presentationOptions.reveal = TaskRevealKind.Never;
29+
return task;
30+
}
31+
32+
public async resolveTask(_task: Task): Promise<Task> {
33+
return _task;
34+
}
35+
36+
public async provideTasks(): Promise<Task[]> {
37+
return [];
38+
}
39+
40+
}
41+
42+
class ExportJarTaskTerminal implements Pseudoterminal {
43+
44+
public writeEmitter = new EventEmitter<string>();
45+
public closeEmitter = new EventEmitter<void>();
46+
47+
public onDidWrite: Event<string> = this.writeEmitter.event;
48+
public onDidClose?: Event<void> = this.closeEmitter.event;
49+
50+
private stepMetadata: IStepMetadata;
51+
52+
constructor(exportJarTaskDefinition: IExportJarTaskDefinition, stepMetadata: IStepMetadata) {
53+
this.stepMetadata = stepMetadata;
54+
this.stepMetadata.mainMethod = exportJarTaskDefinition.mainMethod;
55+
this.stepMetadata.outputPath = exportJarTaskDefinition.targetPath;
56+
this.stepMetadata.elements = exportJarTaskDefinition.elements;
57+
}
58+
59+
public async open(initialDimensions: TerminalDimensions | undefined): Promise<void> {
60+
await createJarFile(this.stepMetadata);
61+
this.closeEmitter.fire();
62+
}
63+
64+
public close(): void {
65+
66+
}
67+
}
68+
69+
interface IExportJarTaskDefinition extends TaskDefinition {
70+
elements?: string[];
71+
mainMethod?: string;
72+
targetPath?: string;
73+
}

0 commit comments

Comments
 (0)