Skip to content

Commit 062a0ab

Browse files
committed
GH-1666: trigger re-validation of controller classes when property files change
1 parent 980ebf3 commit 062a0ab

File tree

8 files changed

+177
-50
lines changed

8 files changed

+177
-50
lines changed

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import java.io.File;
1414
import java.io.IOException;
1515
import java.net.URI;
16-
import java.net.URISyntaxException;
1716
import java.nio.file.Files;
1817
import java.nio.file.Path;
1918
import java.util.ArrayDeque;
@@ -469,7 +468,7 @@ public CompletableFuture<Void> createDocuments(String[] docURIs) {
469468
List<String> docs = projectMapping.get(project);
470469

471470
try {
472-
DocumentDescriptor[] updatedDocs = docs.stream().map(doc -> createUpdatedDoc(doc)).toArray(DocumentDescriptor[]::new);
471+
DocumentDescriptor[] updatedDocs = docs.stream().map(doc -> DocumentDescriptor.createFromUri(doc)).toArray(DocumentDescriptor[]::new);
473472
futures.add(updateItems(project, updatedDocs, indexer));
474473
}
475474
catch (Exception e) {
@@ -525,7 +524,7 @@ public CompletableFuture<Void> updateDocument(String docURI, String content, Str
525524
Optional<IJavaProject> maybeProject = projectFinder().find(new TextDocumentIdentifier(docURI));
526525
if (maybeProject.isPresent()) {
527526
try {
528-
DocumentDescriptor updatedDoc = createUpdatedDoc(docURI);
527+
DocumentDescriptor updatedDoc = DocumentDescriptor.createFromUri(docURI);
529528
futures.add(updateItem(maybeProject.get(), updatedDoc, content, indexer));
530529
}
531530
catch (Exception e) {
@@ -557,7 +556,7 @@ public CompletableFuture<Void> updateDocuments(String[] docURIs, String reason)
557556
List<String> docs = projectMapping.get(project);
558557

559558
try {
560-
DocumentDescriptor[] updatedDocs = docs.stream().map(doc -> createUpdatedDoc(doc)).toArray(DocumentDescriptor[]::new);
559+
DocumentDescriptor[] updatedDocs = docs.stream().map(doc -> DocumentDescriptor.createFromUri(doc)).toArray(DocumentDescriptor[]::new);
561560
futures.add(updateItems(project, updatedDocs, indexer));
562561
}
563562
catch (Exception e) {
@@ -635,16 +634,6 @@ private String[] getDocumentsInterestingForIndexer(SpringIndexer indexer, String
635634
return Arrays.stream(docURIs).filter(docURI -> indexer.isInterestedIn(docURI)).toArray(String[]::new);
636635
}
637636

638-
private DocumentDescriptor createUpdatedDoc(String docURI) throws RuntimeException {
639-
try {
640-
File file = new File(new URI(docURI));
641-
long lastModified = file.lastModified();
642-
return new DocumentDescriptor(UriUtil.toUri(file).toASCIIString(), lastModified);
643-
} catch (URISyntaxException e) {
644-
throw new RuntimeException(e);
645-
}
646-
}
647-
648637
public CompletableFuture<Void> deleteDocument(String deletedDocURI) {
649638
return deleteDocuments(new String[] {deletedDocURI});
650639
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/JdtAstReconciler.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
package org.springframework.ide.vscode.boot.java.reconcilers;
1212

1313
import java.net.URI;
14+
import java.util.List;
1415

1516
import org.eclipse.jdt.core.dom.ASTVisitor;
1617
import org.eclipse.jdt.core.dom.CompilationUnit;
@@ -33,4 +34,8 @@ public interface JdtAstReconciler {
3334

3435
ASTVisitor createVisitor(IJavaProject project, URI docURI, CompilationUnit cu, ReconcilingContext context);
3536

37+
default List<String> identifyFilesToReconcile(IJavaProject project, List<String> changedPropertyFiles) {
38+
return List.of();
39+
};
40+
3641
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/JdtReconciler.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import java.net.URI;
1414
import java.util.ArrayList;
15+
import java.util.Arrays;
1516
import java.util.Collection;
1617
import java.util.Collections;
1718
import java.util.List;
@@ -155,6 +156,12 @@ public Map<IDocument, Collection<ReconcileProblem>> reconcile(IJavaProject proje
155156
return Collections.emptyMap();
156157
}
157158

159+
public List<String> identifyFilesToReconcile(IJavaProject project, List<String> changedPropertyFiles) {
160+
return Arrays.stream(this.reconcilers)
161+
.flatMap(reconciler -> reconciler.identifyFilesToReconcile(project, changedPropertyFiles).stream())
162+
.toList();
163+
}
164+
158165
private List<JdtAstReconciler> getApplicableReconcilers(IJavaProject project) {
159166
return this.applicableReconcilersCache.computeIfAbsent(project.getElementName(), (name) -> {
160167
return computeApplicableReconcilers(project);
@@ -200,5 +207,4 @@ public long getStatsCounter() {
200207
return stats_counter;
201208
}
202209

203-
204210
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/WebApiVersioningReconciler.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.net.URI;
1616
import java.util.Arrays;
1717
import java.util.List;
18+
import java.util.stream.Stream;
1819

1920
import org.eclipse.jdt.core.dom.ASTVisitor;
2021
import org.eclipse.jdt.core.dom.CompilationUnit;
@@ -108,8 +109,7 @@ public boolean visit(TypeDeclaration type) {
108109
return super.visit(type);
109110
}
110111

111-
Arrays.stream(springIndex.getBeansOfProject(project.getElementName()))
112-
.filter(bean -> isAnnotatedWith(bean, Annotations.CONTROLLER))
112+
identifyBeansToReconcile(project)
113113
.map(bean -> UriUtil.toFileString(bean.getLocation().getUri()))
114114
.forEach(file -> context.markForAffetcedFilesIndexing(file));
115115

@@ -118,6 +118,18 @@ public boolean visit(TypeDeclaration type) {
118118
};
119119
}
120120

121+
@Override
122+
public List<String> identifyFilesToReconcile(IJavaProject project, List<String> changedPropertyFiles) {
123+
return identifyBeansToReconcile(project)
124+
.map(bean -> bean.getLocation().getUri())
125+
.toList();
126+
}
127+
128+
private Stream<Bean> identifyBeansToReconcile(IJavaProject project) {
129+
return Arrays.stream(springIndex.getBeansOfProject(project.getElementName()))
130+
.filter(bean -> isAnnotatedWith(bean, Annotations.CONTROLLER));
131+
}
132+
121133
private boolean isApiVersioningConfigured(IJavaProject project, ReconcilingContext context) {
122134
List<WebConfigIndexElement> javaWebConfigs = springIndex.getNodesOfType(project.getElementName(), WebConfigIndexElement.class);
123135
List<WebConfigIndexElement> propertiesWebConfigs = context.getReconcilingIndex().getWebConfigProperties(project);
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2020 Pivotal, Inc.
2+
* Copyright (c) 2020, 2025 Pivotal, Inc.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -10,23 +10,63 @@
1010
*******************************************************************************/
1111
package org.springframework.ide.vscode.boot.java.utils;
1212

13+
import java.io.File;
14+
import java.net.URI;
15+
import java.net.URISyntaxException;
16+
import java.util.List;
17+
18+
import org.springframework.ide.vscode.commons.util.UriUtil;
19+
1320
public class DocumentDescriptor {
1421

1522
private final String docURI;
23+
private final String file;
24+
1625
private final long lastModified;
1726

18-
public DocumentDescriptor(String docURI, long lastModified) {
19-
super();
27+
private DocumentDescriptor(String file, String docURI, long lastModified) {
28+
this.file = file;
2029
this.docURI = docURI;
2130
this.lastModified = lastModified;
2231
}
2332

33+
public String getFile() {
34+
return file;
35+
}
36+
2437
public String getDocURI() {
2538
return docURI;
2639
}
27-
40+
2841
public long getLastModified() {
2942
return lastModified;
3043
}
44+
45+
public static DocumentDescriptor createFromFile(String fileName) {
46+
File file = new File(fileName);
47+
long lastModified = file.lastModified();
48+
return new DocumentDescriptor(fileName, UriUtil.toUri(file).toASCIIString(), lastModified);
49+
}
50+
51+
public static DocumentDescriptor createFromFile(String fileName, long lastModified) {
52+
File file = new File(fileName);
53+
return new DocumentDescriptor(fileName, UriUtil.toUri(file).toASCIIString(), lastModified);
54+
}
55+
56+
public static DocumentDescriptor createFromUri(String docUri) {
57+
try {
58+
File file = new File(new URI(docUri));
59+
long lastModified = file.lastModified();
60+
return new DocumentDescriptor(file.getAbsolutePath(), docUri, lastModified);
61+
} catch (URISyntaxException e) {
62+
throw new RuntimeException(e);
63+
}
64+
}
65+
66+
public static List<DocumentDescriptor> createFromUris(List<String> docUris) {
67+
return docUris.stream()
68+
.map(docUri -> createFromUri(docUri))
69+
.toList();
70+
}
3171

3272
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,13 @@ public SpringIndexerJavaDependencyTracker getDependencyTracker() {
142142

143143
@Override
144144
public String[] getFileWatchPatterns() {
145-
return new String[] {"**/*.java"};
145+
return new String[] {"**/*.java", "**/*.properties", "**/*.yaml", "**/*.yml"};
146146
}
147147

148148
@Override
149149
public boolean isInterestedIn(String resource) {
150-
return resource.endsWith(".java");
150+
return resource.endsWith(".java") ||
151+
(resource.contains("application") && (resource.endsWith(".properties") || resource.endsWith(".yml") || resource.endsWith(".yaml")));
151152
}
152153

153154
@Override
@@ -193,6 +194,11 @@ public void updateFile(IJavaProject project, DocumentDescriptor updatedDoc, Stri
193194

194195
@Override
195196
public void updateFiles(IJavaProject project, DocumentDescriptor[] updatedDocs) throws Exception {
197+
updateJavaFiles(project, updatedDocs);
198+
updatePropertyFiles(project, updatedDocs);
199+
}
200+
201+
private void updateJavaFiles(IJavaProject project, DocumentDescriptor[] updatedDocs) throws Exception {
196202
if (updatedDocs != null) {
197203
DocumentDescriptor[] docs = filterDocuments(project, updatedDocs);
198204

@@ -205,6 +211,26 @@ public void updateFiles(IJavaProject project, DocumentDescriptor[] updatedDocs)
205211
}
206212
}
207213

214+
private void updatePropertyFiles(IJavaProject project, DocumentDescriptor[] updatedDocs) {
215+
List<String> changedPropertyFiles = Arrays.stream(updatedDocs)
216+
.filter(doc -> doc.getDocURI().contains("application"))
217+
.filter(doc -> doc.getDocURI().endsWith(".properties") || doc.getDocURI().endsWith(".yml") || doc.getDocURI().endsWith(".yaml"))
218+
.map(doc -> doc.getDocURI())
219+
.toList();
220+
221+
if (changedPropertyFiles.size() > 0) {
222+
List<String> filesToReconcile = reconciler.identifyFilesToReconcile(project, changedPropertyFiles);
223+
List<DocumentDescriptor> filesWithTimestamps = DocumentDescriptor.createFromUris(filesToReconcile);
224+
225+
try {
226+
reconcileWithCompleteIndex(project, filesWithTimestamps);
227+
}
228+
catch (Exception e) {
229+
log.error("error reconcling java source as reaction to property file change", e);
230+
}
231+
}
232+
}
233+
208234
@Override
209235
public void removeFiles(IJavaProject project, String[] docURIs) throws Exception {
210236

@@ -324,7 +350,9 @@ private DocumentDescriptor[] filterDocuments(IJavaProject project, DocumentDescr
324350
IndexCacheKey indexCacheKey = getCacheKey(project, INDEX_KEY);
325351
IndexCacheKey diagnosticsCacheKey = getCacheKey(project, DIAGNOSTICS_KEY);
326352

327-
return Arrays.stream(updatedDocs).filter(doc -> shouldProcessDocument(project, doc.getDocURI()))
353+
return Arrays.stream(updatedDocs)
354+
.filter(doc -> doc.getDocURI().endsWith(".java"))
355+
.filter(doc -> shouldProcessDocument(project, doc.getDocURI()))
328356
.filter(doc -> isCacheOutdated(symbolsCacheKey, doc.getDocURI(), doc.getLastModified())
329357
|| isCacheOutdated(indexCacheKey, doc.getDocURI(), doc.getLastModified())
330358
|| isCacheOutdated(diagnosticsCacheKey, doc.getDocURI(), doc.getLastModified()))
@@ -356,8 +384,7 @@ private boolean isCacheOutdated(IndexCacheKey cacheKey, String docURI, long modi
356384
private void scanFiles(IJavaProject project, DocumentDescriptor[] docs) throws Exception {
357385
Set<String> scannedFiles = new HashSet<>();
358386
for (int i = 0; i < docs.length; i++) {
359-
String file = UriUtil.toFileString(docs[i].getDocURI());
360-
scannedFiles.add(file);
387+
scannedFiles.add(docs[i].getFile());
361388
}
362389

363390
ScanFilesInternallyResult result = scanFilesInternally(project, docs);
@@ -439,9 +466,7 @@ private ScanFilesInternallyResult scanFilesInternally(IJavaProject project, Docu
439466
for (int i = 0; i < docs.length; i++) {
440467
updatedDocs.put(docs[i].getDocURI(), docs[i]);
441468

442-
String file = UriUtil.toFileString(docs[i].getDocURI());
443-
444-
javaFiles[i] = file;
469+
javaFiles[i] = docs[i].getFile();
445470
lastModified[i] = docs[i].getLastModified();
446471
}
447472

@@ -533,12 +558,9 @@ private void scanAffectedFiles(IJavaProject project, Set<String> changedTypes, S
533558
}
534559

535560
if (!filesToScan.isEmpty()) {
536-
DocumentDescriptor[] docsToScan = filesToScan.stream().map(file -> {
537-
File realFile = new File(file);
538-
String docURI = UriUtil.toUri(realFile).toASCIIString();
539-
long lastModified = realFile.lastModified();
540-
return new DocumentDescriptor(docURI, lastModified);
541-
}).toArray(DocumentDescriptor[]::new);
561+
DocumentDescriptor[] docsToScan = filesToScan.stream()
562+
.map(file -> DocumentDescriptor.createFromFile(file))
563+
.toArray(DocumentDescriptor[]::new);
542564

543565
for (DocumentDescriptor docToScan : docsToScan) {
544566
this.symbolHandler.removeSymbols(project, docToScan.getDocURI());
@@ -655,22 +677,23 @@ public void acceptAST(String sourceFilePath, CompilationUnit cu) {
655677
}
656678
}
657679

658-
private void reconcileWithCompleteIndex(IJavaProject project, Map<String, Long> markedForReconcilingWithCompleteIndex) throws Exception {
659-
if (markedForReconcilingWithCompleteIndex.isEmpty()) {
680+
private void reconcileWithCompleteIndex(IJavaProject project, List<DocumentDescriptor> filesWithTimestamps) throws Exception {
681+
if (filesWithTimestamps.isEmpty()) {
660682
return;
661683
}
662684

663685
boolean ignoreMethodBodies = false;
664686

665-
String[] javaFiles = markedForReconcilingWithCompleteIndex.keySet().toArray(String[]::new);
666-
667-
log.info("additional reconciling with complete index triggere for: " + Arrays.toString(javaFiles));
687+
String[] javaFiles = new String[filesWithTimestamps.size()];
688+
long[] modificationTimestamps = new long[filesWithTimestamps.size()];
668689

669-
long[] modificationTimestamps = new long[javaFiles.length];
670-
for (int i = 0; i < javaFiles.length; i++) {
671-
modificationTimestamps[i] = markedForReconcilingWithCompleteIndex.get(javaFiles[i]);
690+
for (int i = 0; i < filesWithTimestamps.size(); i++) {
691+
javaFiles[i] = filesWithTimestamps.get(i).getFile();
692+
modificationTimestamps[i] = filesWithTimestamps.get(i).getLastModified();
672693
}
673694

695+
log.info("additional reconciling with complete index triggered for: " + Arrays.toString(javaFiles));
696+
674697
// reconcile
675698
SpringIndexerJavaScanResult reconcilingResult = new SpringIndexerJavaScanResult(project, javaFiles);
676699
ReconcilingIndex reconcilingIndex = new ReconcilingIndex();
@@ -1164,7 +1187,7 @@ private void addTestsJavaSourcesToIndex() {
11641187
File file = path.toFile();
11651188
URI docUri = UriUtil.toUri(file);
11661189
String content = FileUtils.readFileToString(file, Charset.defaultCharset());
1167-
scanFile(project, new DocumentDescriptor(docUri.toASCIIString(), file.lastModified()), content);
1190+
scanFile(project, DocumentDescriptor.createFromUri(docUri.toASCIIString()), content);
11681191
}
11691192
} catch (Exception e) {
11701193
log.error("{}", e);

0 commit comments

Comments
 (0)