Skip to content

Commit 98a4eb6

Browse files
committed
Use linked resource instead of filesystem
For all metadata files, except the .project (which may be harder to move). This also copies some logic from the filesystem to ProjectsManager. We cannot have the filesystem bundle requiring and referencing the main jdt.ls bundle as this would cause classloading error.
1 parent 0e04fdd commit 98a4eb6

File tree

10 files changed

+159
-87
lines changed

10 files changed

+159
-87
lines changed

org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/MavenProjectImporter.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import org.eclipse.m2e.core.lifecyclemapping.model.PluginExecutionAction;
5555
import org.eclipse.m2e.core.project.IMavenProjectImportResult;
5656
import org.eclipse.m2e.core.project.IProjectConfigurationManager;
57+
import org.eclipse.m2e.core.project.IProjectCreationListener;
5758
import org.eclipse.m2e.core.project.LocalProjectScanner;
5859
import org.eclipse.m2e.core.project.MavenProjectInfo;
5960
import org.eclipse.m2e.core.project.ProjectImportConfiguration;
@@ -208,6 +209,14 @@ public void importToWorkspace(IProgressMonitor monitor) throws CoreException, Op
208209
}
209210
if (!toImport.isEmpty()) {
210211
ProjectImportConfiguration importConfig = new ProjectImportConfiguration();
212+
IProjectCreationListener linkFoldersUponProjectCreation = ProjectsManager.generatesMetadataFilesAtProjectRoot() ? null :
213+
p -> {
214+
try {
215+
ProjectsManager.linkResources(p);
216+
} catch (CoreException ex) {
217+
JavaLanguageServerPlugin.logException(ex);
218+
}
219+
};
211220
if (toImport.size() > artifactIds.size()) {
212221
// Ensure project name is unique when same artifactId
213222
importConfig.setProjectNameTemplate(DUPLICATE_ARTIFACT_TEMPLATE);
@@ -226,7 +235,7 @@ public void importToWorkspace(IProgressMonitor monitor) throws CoreException, Op
226235
while (i++ < MAX_PROJECTS_TO_IMPORT && iter.hasNext()) {
227236
importPartial.add(iter.next());
228237
}
229-
List<IMavenProjectImportResult> result = configurationManager.importProjects(importPartial, importConfig, monitor2.split(MAX_PROJECTS_TO_IMPORT));
238+
List<IMavenProjectImportResult> result = configurationManager.importProjects(importPartial, importConfig, linkFoldersUponProjectCreation, monitor2.split(MAX_PROJECTS_TO_IMPORT));
230239
results.addAll(result);
231240
monitor2.setWorkRemaining(toImport.size() * 2 - it * MAX_PROJECTS_TO_IMPORT);
232241
}
@@ -238,7 +247,7 @@ public void importToWorkspace(IProgressMonitor monitor) throws CoreException, Op
238247
updateProjects(imported, lastWorkspaceStateSaved, monitor2.split(projects.size()));
239248
monitor2.done();
240249
} else {
241-
configurationManager.importProjects(toImport, importConfig, subMonitor.split(75));
250+
configurationManager.importProjects(toImport, importConfig, linkFoldersUponProjectCreation, subMonitor.split(75));
242251
}
243252
}
244253
subMonitor.setWorkRemaining(20);

org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/ProjectsManager.java

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515

1616
import static org.eclipse.jdt.ls.core.internal.JVMConfigurator.configureJVMSettings;
1717

18+
import java.io.ByteArrayInputStream;
1819
import java.io.File;
20+
import java.io.IOException;
1921
import java.net.URI;
2022
import java.util.ArrayList;
2123
import java.util.Arrays;
@@ -29,9 +31,11 @@
2931
import java.util.stream.Stream;
3032

3133
import org.apache.commons.lang3.StringUtils;
34+
import org.eclipse.core.internal.preferences.EclipsePreferences;
3235
import org.eclipse.core.internal.resources.CharsetManager;
3336
import org.eclipse.core.internal.resources.Workspace;
3437
import org.eclipse.core.resources.FileInfoMatcherDescription;
38+
import org.eclipse.core.resources.IFile;
3539
import org.eclipse.core.resources.IFolder;
3640
import org.eclipse.core.resources.IMarker;
3741
import org.eclipse.core.resources.IProject;
@@ -52,6 +56,7 @@
5256
import org.eclipse.core.runtime.IProgressMonitor;
5357
import org.eclipse.core.runtime.IStatus;
5458
import org.eclipse.core.runtime.MultiStatus;
59+
import org.eclipse.core.runtime.NullProgressMonitor;
5560
import org.eclipse.core.runtime.OperationCanceledException;
5661
import org.eclipse.core.runtime.Platform;
5762
import org.eclipse.core.runtime.Status;
@@ -343,6 +348,45 @@ public static IProject createJavaProject(IProject project, IProgressMonitor moni
343348
return createJavaProject(project, null, "src", "bin", monitor);
344349
}
345350

351+
/*
352+
* ⚠ These value is duplicated in ProjectsManager as both bundles must remain independent,
353+
* but the same dir should be used for .project or .settings/.classpath.
354+
* So when updating one, think about updating the other.
355+
*/
356+
public static final String GENERATES_METADATA_FILES_AT_PROJECT_ROOT = "java.import.generatesMetadataFilesAtProjectRoot";
357+
public static final IPath METADATA_FOLDER_PATH = ResourcesPlugin.getWorkspace().getRoot().getLocation().append(".projects");
358+
359+
/**
360+
* Check whether the metadata files needs to be generated at project root.
361+
*/
362+
public static boolean generatesMetadataFilesAtProjectRoot() {
363+
String property = System.getProperty(GENERATES_METADATA_FILES_AT_PROJECT_ROOT);
364+
if (property == null) {
365+
return true;
366+
}
367+
return Boolean.parseBoolean(property);
368+
}
369+
370+
/**
371+
* Get the redirected path of the input path. The path will be redirected to
372+
* the workspace's metadata folder ({@link JLSFsUtils#METADATA_FOLDER_PATH}).
373+
* @param projectName name of the project.
374+
* @param path path needs to be redirected.
375+
* @return the redirected path.
376+
*/
377+
public static IPath getMetaDataFilePath(String projectName, IPath path) {
378+
if (path.segmentCount() == 1) {
379+
return METADATA_FOLDER_PATH.append(projectName).append(path);
380+
}
381+
382+
String lastSegment = path.lastSegment();
383+
if (IProjectDescription.DESCRIPTION_FILE_NAME.equals(lastSegment)) {
384+
return METADATA_FOLDER_PATH.append(projectName).append(lastSegment);
385+
}
386+
387+
return null;
388+
}
389+
346390
public static IProject createJavaProject(IProject project, IPath projectLocation, String src, String bin, IProgressMonitor monitor) throws CoreException, OperationCanceledException {
347391
if (project.exists()) {
348392
return project;
@@ -355,6 +399,9 @@ public static IProject createJavaProject(IProject project, IPath projectLocation
355399
}
356400
project.create(description, monitor);
357401
project.open(monitor);
402+
if (!generatesMetadataFilesAtProjectRoot() && !DEFAULT_PROJECT_NAME.equals(project.getName())) {
403+
linkResources(project);
404+
}
358405

359406
//Turn into Java project
360407
description = project.getDescription();
@@ -396,6 +443,43 @@ public static IProject createJavaProject(IProject project, IPath projectLocation
396443
return project;
397444
}
398445

446+
public static void linkResources(IProject project) throws CoreException {
447+
IFile classpathFile = project.getFile(IJavaProject.CLASSPATH_FILE_NAME);
448+
if (!classpathFile.exists()) {
449+
File diskFile = getMetaDataFilePath(classpathFile.getProject().getName(), classpathFile.getProjectRelativePath()).toFile();
450+
if (!diskFile.exists()) {
451+
try {
452+
diskFile.getParentFile().mkdirs();
453+
diskFile.createNewFile();
454+
} catch (IOException ex) {
455+
throw new CoreException(Status.error(diskFile + " cannot be created", ex)); //$NON-NLS-1$
456+
}
457+
}
458+
classpathFile.createLink(diskFile.toURI(), IResource.NONE, new NullProgressMonitor());
459+
classpathFile.setContents(new ByteArrayInputStream("<classpath/>".getBytes()), 0, null);
460+
}
461+
IFile factorypathFile = project.getFile(".factorypath");
462+
if (!factorypathFile.exists()) {
463+
File diskFile = getMetaDataFilePath(classpathFile.getProject().getName(), factorypathFile.getProjectRelativePath()).toFile();
464+
if (!diskFile.exists()) {
465+
try {
466+
diskFile.getParentFile().mkdirs();
467+
diskFile.createNewFile();
468+
} catch (IOException ex) {
469+
throw new CoreException(Status.error(diskFile + " cannot be created", ex)); //$NON-NLS-1$
470+
}
471+
}
472+
factorypathFile.createLink(diskFile.toURI(), IResource.ALLOW_MISSING_LOCAL, new NullProgressMonitor());
473+
factorypathFile.setContents(new ByteArrayInputStream("<factorypath/>".getBytes()), 0, null);
474+
}
475+
IFolder settingsFolder = project.getFolder(EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME);
476+
if (!settingsFolder.exists()) {
477+
File diskFolder = getMetaDataFilePath(classpathFile.getProject().getName(), settingsFolder.getProjectRelativePath()).toFile();
478+
diskFolder.mkdirs();
479+
settingsFolder.createLink(diskFolder.toURI(), IResource.ALLOW_MISSING_LOCAL, new NullProgressMonitor());
480+
}
481+
}
482+
399483
@Override
400484
public Job updateProject(IProject project, boolean force) {
401485
if (project == null || ProjectUtils.isInternalBuildSupport(BuildSupportManager.find(project).orElse(null))) {
@@ -426,6 +510,7 @@ public IStatus runInWorkspace(IProgressMonitor monitor) {
426510
updateEncoding(monitor);
427511
project.deleteMarkers(BUILD_FILE_MARKER_TYPE, false, IResource.DEPTH_ONE);
428512
long elapsed = System.currentTimeMillis() - start;
513+
replaceLinkedMetadataWithLocal(project);
429514
JavaLanguageServerPlugin.logInfo("Updated " + projectName + " in " + elapsed + " ms");
430515
} catch (Exception e) {
431516
String msg = "Error updating " + projectName;
@@ -625,5 +710,12 @@ public void reportProjectsStatus() {
625710
JavaLanguageServerPlugin.sendStatus(ServiceStatus.ProjectStatus, "OK");
626711
}
627712
}
713+
714+
private void replaceLinkedMetadataWithLocal(IProject p) throws CoreException {
715+
if (new File(p.getLocation().toFile(), IJavaProject.CLASSPATH_FILE_NAME).exists() &&
716+
p.getFile(IJavaProject.CLASSPATH_FILE_NAME).isLinked()) {
717+
p.getFile(IJavaProject.CLASSPATH_FILE_NAME).delete(false, false, null);
718+
}
719+
}
628720

629721
}

org.eclipse.jdt.ls.filesystem/src/org/eclipse/jdt/ls/core/internal/filesystem/JLSFile.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.eclipse.core.filesystem.IFileStore;
2222
import org.eclipse.core.internal.filesystem.local.LocalFile;
23+
import org.eclipse.core.resources.IProjectDescription;
2324
import org.eclipse.core.runtime.IPath;
2425
import org.eclipse.core.runtime.IProgressMonitor;
2526
import org.eclipse.core.runtime.Path;
@@ -56,11 +57,9 @@ public String[] childNames(int options, IProgressMonitor monitor) {
5657
}
5758

5859
Set<String> childNameSet = new LinkedHashSet<>(Arrays.asList(childNames));
59-
for (String fileName : JLSFsUtils.METADATA_NAMES) {
60-
if (!childNameSet.contains(fileName) &&
61-
JLSFsUtils.METADATA_FOLDER_PATH.append(projectName).append(fileName).toFile().exists()) {
62-
childNameSet.add(fileName);
63-
}
60+
if (!childNameSet.contains(IProjectDescription.DESCRIPTION_FILE_NAME) &&
61+
JLSFsUtils.METADATA_FOLDER_PATH.append(projectName).append(IProjectDescription.DESCRIPTION_FILE_NAME).toFile().exists()) {
62+
childNameSet.add(IProjectDescription.DESCRIPTION_FILE_NAME);
6463
}
6564

6665
return childNameSet.toArray(String[]::new);

org.eclipse.jdt.ls.filesystem/src/org/eclipse/jdt/ls/core/internal/filesystem/JLSFileSystem.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@
2323

2424
/**
2525
* JDT.LS's own implementation of file system to handle the 'file' scheme uri.
26-
* The purpose of this implementation is to allow the project metadata files (.project, .classpath, .settings/)
27-
* can be persisted out of the project root.
26+
* The purpose of this implementation is to allow the .project files can be
27+
* persisted out of the project root, since for the moment using linked resources
28+
* seem not possible.
2829
*/
2930
public class JLSFileSystem extends LocalFileSystem {
3031

org.eclipse.jdt.ls.filesystem/src/org/eclipse/jdt/ls/core/internal/filesystem/JLSFsUtils.java

Lines changed: 15 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -13,43 +13,34 @@
1313

1414
package org.eclipse.jdt.ls.core.internal.filesystem;
1515

16-
import java.util.Arrays;
17-
import java.util.HashSet;
1816
import java.util.Objects;
19-
import java.util.Set;
2017

21-
import org.eclipse.core.internal.preferences.EclipsePreferences;
2218
import org.eclipse.core.resources.IContainer;
2319
import org.eclipse.core.resources.IProject;
2420
import org.eclipse.core.resources.IProjectDescription;
2521
import org.eclipse.core.resources.ResourcesPlugin;
2622
import org.eclipse.core.runtime.IPath;
27-
import org.eclipse.jdt.core.IJavaProject;
2823

2924
/**
3025
* Utilities of the file system implementation.
3126
*/
3227
public class JLSFsUtils {
3328
private JLSFsUtils() {}
3429

35-
static final IPath METADATA_FOLDER_PATH = ResourcesPlugin.getPlugin().getStateLocation().append(".projects");
36-
3730
/**
38-
* The system property key to specify the file system mode.
39-
*/
40-
static final String GENERATES_METADATA_FILES_AT_PROJECT_ROOT = "java.import.generatesMetadataFilesAtProjectRoot";
41-
42-
static final String FACTORY_PATH = ".factorypath";
43-
31+
* ⚠ This value is duplicated in ProjectsManager as both bundles must remain independent,
32+
* but the same dir should be used for .project or .settings/.classpath.
33+
* So when updating one, think about updating the other.
34+
**/
35+
static final IPath METADATA_FOLDER_PATH = ResourcesPlugin.getWorkspace().getRoot().getLocation().append(".projects");;
4436
/**
45-
* The metadata files
37+
* The system property key to specify the file system mode.
38+
*
39+
* This value is duplicated in ProjectsManager as both bundles must remain independent,
40+
* but the same dir should be used for .project or .settings/.classpath.
41+
* So when updating one, think about updating the other.
4642
*/
47-
static final Set<String> METADATA_NAMES = new HashSet<>(Arrays.asList(
48-
IProjectDescription.DESCRIPTION_FILE_NAME,
49-
EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME,
50-
IJavaProject.CLASSPATH_FILE_NAME,
51-
FACTORY_PATH
52-
));
43+
static final String GENERATES_METADATA_FILES_AT_PROJECT_ROOT = "java.import.generatesMetadataFilesAtProjectRoot";
5344

5445
/**
5546
* Determine whether the resource should be stored in workspace's metadata folder.
@@ -77,9 +68,6 @@ static boolean shouldStoreInMetadataArea(IPath location) {
7768
// do not redirect if the file already exists on disk
7869
if (location.toFile().exists()) {
7970
return false;
80-
} else if (location.lastSegment().endsWith(EclipsePreferences.PREFS_FILE_EXTENSION)) {
81-
location = location.removeLastSegments(1);
82-
return !location.toFile().exists();
8371
}
8472

8573
return true;
@@ -91,23 +79,9 @@ static boolean shouldStoreInMetadataArea(IPath location) {
9179
* @return whether the given location points to a metadata file.
9280
*/
9381
static boolean isProjectMetadataFile(IPath location) {
94-
if (location == null || location.segmentCount() < 2) {
95-
return false;
96-
}
97-
98-
if (location.lastSegment().endsWith(EclipsePreferences.PREFS_FILE_EXTENSION)) {
99-
location = location.removeLastSegments(1);
100-
}
101-
102-
if (location.segmentCount() < 2) {
103-
return false;
104-
}
105-
106-
if (!METADATA_NAMES.contains(location.lastSegment())) {
107-
return false;
108-
}
109-
110-
return true;
82+
return location != null && //
83+
location.segmentCount() != 2 && //
84+
IProjectDescription.DESCRIPTION_FILE_NAME.equals(location.lastSegment());
11185
}
11286

11387
/**
@@ -116,9 +90,6 @@ static boolean isProjectMetadataFile(IPath location) {
11690
* @param filePath the file path.
11791
*/
11892
static IPath getContainerPath(IPath filePath) {
119-
if (filePath.lastSegment().endsWith(EclipsePreferences.PREFS_FILE_EXTENSION)) {
120-
filePath = filePath.removeLastSegments(1);
121-
}
12293
return filePath.removeLastSegments(1);
12394
}
12495

@@ -152,12 +123,8 @@ static IPath getMetaDataFilePath(String projectName, IPath path) {
152123
}
153124

154125
String lastSegment = path.lastSegment();
155-
if (METADATA_NAMES.contains(lastSegment)) {
126+
if (IProjectDescription.DESCRIPTION_FILE_NAME.equals(lastSegment)) {
156127
return METADATA_FOLDER_PATH.append(projectName).append(lastSegment);
157-
} else if (lastSegment.endsWith(EclipsePreferences.PREFS_FILE_EXTENSION)) {
158-
return METADATA_FOLDER_PATH.append(projectName)
159-
.append(EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME)
160-
.append(lastSegment);
161128
}
162129

163130
return null;

org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/EclipseProjectMetadataFileTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import org.eclipse.jdt.core.IJavaProject;
3232
import org.eclipse.jdt.ls.core.internal.ProjectUtils;
3333
import org.eclipse.jdt.ls.core.internal.managers.AbstractProjectsManagerBasedTest;
34+
import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager;
35+
import org.eclipse.jdt.ls.core.internal.managers.ProjectsManagerTest;
3436
import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager.CHANGE_TYPE;
3537
import org.junit.Before;
3638
import org.junit.Test;
@@ -51,7 +53,7 @@ public static Collection<String> data(){
5153

5254
@Before
5355
public void setUp() {
54-
System.setProperty(JLSFsUtils.GENERATES_METADATA_FILES_AT_PROJECT_ROOT, fsMode);
56+
System.setProperty(ProjectsManager.GENERATES_METADATA_FILES_AT_PROJECT_ROOT, fsMode);
5557
}
5658

5759
@Test

0 commit comments

Comments
 (0)