Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support custom config files #212

Merged
merged 25 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b7ff73c
change config file logic (#194)
dshimo Aug 8, 2023
e025c59
Fix determination of Liberty Workspace for non-default server.xml (#201)
cherylking Aug 23, 2023
6acab22
xmlreader on config portion
dshimo Aug 23, 2023
a7b4a60
Initial changes to watch and process liberty-plugin-config.xml
evie-lau Aug 23, 2023
f59e641
Process and store custom config files
evie-lau Aug 23, 2023
3df2de1
init test, refactor to use LCFM
dshimo Aug 24, 2023
ad35bf3
Initial tests. Small refactor
evie-lau Aug 25, 2023
1c303fe
Fix uri string mismatch. Update key completion to use file-checking m…
evie-lau Aug 28, 2023
146a267
Update comments and todos
evie-lau Aug 29, 2023
41c4c30
Add check for config file path, moved from client-side filewatch filt…
evie-lau Sep 1, 2023
93ff57a
Fix paths for windows
evie-lau Sep 1, 2023
6807f9c
init custom config files list
dshimo Sep 1, 2023
6c55625
Normalize paths. Test still fails on Windows because test file has un…
evie-lau Sep 1, 2023
763c653
Run custom config file test in Unix only
evie-lau Sep 1, 2023
5d4de2b
Modify tests to account for Windows
evie-lau Sep 1, 2023
8bfd08e
Cleanup comments and add copyright
evie-lau Sep 1, 2023
529c6ea
Update filenames to use URI formatted string
evie-lau Sep 5, 2023
a7ae07b
PR comments and cleanup
dshimo Sep 5, 2023
0ac0973
oops
dshimo Sep 5, 2023
07d6414
Minor fixes
evie-lau Sep 5, 2023
5e8b0aa
Fix Windows pathing
evie-lau Sep 5, 2023
95cc901
Fix parse on file change.
evie-lau Sep 5, 2023
1043bbe
Make sure build/test actions are separate. Suppress progress tracking…
evie-lau Sep 6, 2023
c8f4a3a
Add comments, use constants
evie-lau Sep 6, 2023
74620ab
Modify initCustomConfigTest to use a separate test resource folder
evie-lau Sep 6, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ jobs:
java-version: 17
- name: Build LCLS
working-directory: ./liberty-ls
run: ./mvnw clean package
run: ./mvnw clean package -ntp -DskipTests
- name: Test LCLS
working-directory: ./liberty-ls
run: ./mvnw verify
run: ./mvnw verify -ntp
lemminx-liberty:
runs-on: ${{ matrix.os }}
strategy:
Expand All @@ -39,7 +39,7 @@ jobs:
java-version: 17
- name: Build Lemminx Liberty
working-directory: ./lemminx-liberty
run: ./mvnw clean package
run: ./mvnw clean package -ntp
- name: Test Lemminx Liberty
working-directory: ./lemminx-liberty
run: ./mvnw verify
run: ./mvnw verify -ntp
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
/*******************************************************************************
* Copyright (c) 2023 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package io.openliberty.tools.langserver;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import org.eclipse.lsp4j.WorkspaceFolder;

import io.openliberty.tools.langserver.ls.LibertyTextDocument;
import io.openliberty.tools.langserver.utils.XmlReader;

public class LibertyConfigFileManager {
public static final String LIBERTY_PLUGIN_CONFIG_XML = "liberty-plugin-config.xml";
public static final String CUSTOM_SERVER_ENV_XML_TAG = "serverEnv";
public static final String CUSTOM_BOOTSTRAP_PROPERTIES_XML_TAG = "bootstrapPropertiesFile";

private static final String DEFAULT_SERVER_ENV = "src/main/liberty/config/server.env".replace("/", File.separator);
private static final String DEFAULT_BOOTSTRAP_PROPERTIES = "src/main/liberty/config/bootstrap.properties".replace("/", File.separator);


private static Set<String> customServerEnvFiles = new HashSet<String>();
private static Set<String> customBootstrapFiles = new HashSet<String>();

private static final Logger LOGGER = Logger.getLogger(LibertyConfigFileManager.class.getName());

public static void initLibertyConfigFileManager(List<WorkspaceFolder> workspaceFolders) {
if (workspaceFolders == null) {
return;
}
for (WorkspaceFolder folder : workspaceFolders) {
processWorkspaceDir(folder);
}
}

/**
* Given a workspace folder, find and process all Liberty plugin config xmls
*/
public static void processWorkspaceDir(WorkspaceFolder workspaceFolder) {
String workspaceUriString = workspaceFolder.getUri();
String normalizedUriString = workspaceUriString.replace("///", "/");
URI workspaceURI = URI.create(normalizedUriString);
Path workspacePath = Paths.get(workspaceURI);

try {
List<Path> lpcXmlList = findFilesEndsWithInDirectory(workspacePath, LIBERTY_PLUGIN_CONFIG_XML);
for (Path lpcXml : lpcXmlList) {
processLibertyPluginConfigXml(lpcXml.toUri().toString());
}
} catch (IOException e) {
LOGGER.warning("Encountered an IOException on initial custom config processing: " + e.getMessage());
}
// LOGGER.info("Found custom files: server-" + customServerEnvFiles + "; bootstrap-"+customBootstrapFiles);
}

/**
* Given a Liberty plugin config xml, store custom config file paths to memory.
* @param uri - URI-formatted string
*/
public static void processLibertyPluginConfigXml(String uri) {
if (!uri.endsWith(LIBERTY_PLUGIN_CONFIG_XML)) {
return;
}
Map<String, String> customConfigFiles = XmlReader.readTagsFromXml(uri,
cherylking marked this conversation as resolved.
Show resolved Hide resolved
CUSTOM_SERVER_ENV_XML_TAG,
CUSTOM_BOOTSTRAP_PROPERTIES_XML_TAG);
// TODO: handle deletions
// match uri
if (customConfigFiles.containsKey(CUSTOM_SERVER_ENV_XML_TAG)) {
customServerEnvFiles.add(customConfigFiles.get(CUSTOM_SERVER_ENV_XML_TAG));
}
if (customConfigFiles.containsKey(CUSTOM_BOOTSTRAP_PROPERTIES_XML_TAG)) {
customBootstrapFiles.add(customConfigFiles.get(CUSTOM_BOOTSTRAP_PROPERTIES_XML_TAG));
}
}

public static boolean isServerEnvFile(LibertyTextDocument tdi) {
return isServerEnvFile(tdi.getUri());
}

/**
* Checks if file matches one of these conditions:
* - is default server.env file in `src/main/liberty/config`
* - is custom env file specified in liberty-plugin-config.xml (generated from
* build file)
*
* @param uri - normally comes from LibertyTextDocument.getUri() which is a URI formatted string (file:///path/to/file)
* @return
*/
public static boolean isServerEnvFile(String uri) {
String filePath = normalizeFilePath(uri);
return filePath.endsWith(DEFAULT_SERVER_ENV) || customServerEnvFiles.contains(filePath);
}

public static boolean isBootstrapPropertiesFile(LibertyTextDocument tdi) {
return isBootstrapPropertiesFile(tdi.getUri());
}

/**
* Checks if file matches one of these conditions:
* - is default bootstrap.properties file in `src/main/liberty/config`
* - is custom properties file specified in liberty-plugin-config.xml (generated
* from build file)
*
* @param uri - normally comes from LibertyTextDocument.getUri() which is a URI formatted string (file:///path/to/file)
* @return
*/
public static boolean isBootstrapPropertiesFile(String uri) {
String filePath = normalizeFilePath(uri);
return filePath.endsWith(DEFAULT_BOOTSTRAP_PROPERTIES) || customBootstrapFiles.contains(filePath);
}

/**
* Normalize and fix file path, starting from uri-formatted string.
* - Converts to OS-specific filepaths (/ for unix, \ for windows)
* - Handles URL encoding, Windows drive letter discrepancies
* @param uri - URI-formatted string
* @return - OS-specific filepath
*/
public static String normalizeFilePath(String uri) {
// make sure Windows backslashes are replaced with forwardslash for URI.create
String normalizedUriString = uri.replace("\\","/");
String finalPath = null;
if (File.separator.equals("/")) { // unix
Path path = Paths.get(URI.create(normalizedUriString));
finalPath = path.toString();
} else { // windows - URI.create with string instead of URI to handle test paths in Windows. normalize drive letter
String filepath = URI.create(normalizedUriString).getPath();
if (filepath.charAt(0) == '/') {
filepath = filepath.substring(1);
}
Path path = Paths.get(filepath);
finalPath = path.toString();
finalPath = normalizeDriveLetter(finalPath);
/**
* Note - This else case is mostly needed for testing, which use fake paths that don't actually exist.
* - Paths.get(URI) fails, whereas Paths.get(String) works with fake paths.
* - In practice, when URIs are provided by the Liberty Tools IDE client, they are valid paths, where Paths.get(URI) will work
* - just have to normalizeDriveLetter()
*/
}
return finalPath;
}

/**
* Necessary adjustment for Windows URI:
* https://github.com/Microsoft/vscode/issues/42159#issuecomment-360533151
* Notes: LSP uses lowercase drive letters, but Windows file system uses uppercase drive letters
*/
public static String normalizeDriveLetter(String path) {
return path.substring(0, 1).toUpperCase() + path.substring(1);
}

/**
* Search the dir path for all files that end with a name or extension. If none
* are found,
* an empty List is returned.
*
* @param dir Path to search under
* @param nameOrExtension String to match
* @return List<Path> collection of Path that match the given nameOrExtension in
* the specified dir Path.
*/
protected static List<Path> findFilesEndsWithInDirectory(Path dir, String nameOrExtension) throws IOException {
List<Path> matchingFiles = Files.walk(dir)
.filter(p -> (Files.isRegularFile(p) && p.toFile().getName().endsWith(nameOrExtension)))
.collect(Collectors.toList());

return matchingFiles;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2020, 2022 IBM Corporation and others.
* Copyright (c) 2020, 2023 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
Expand All @@ -24,14 +24,11 @@
import org.eclipse.lsp4j.TextDocumentSyncKind;
import org.eclipse.lsp4j.services.LanguageClient;
import org.eclipse.lsp4j.services.LanguageClientAware;
import org.eclipse.lsp4j.services.LanguageServer;
import org.eclipse.lsp4j.services.TextDocumentService;
import org.eclipse.lsp4j.services.WorkspaceService;

import io.openliberty.tools.langserver.common.ParentProcessWatcher.ProcessLanguageServer;

public class LibertyLanguageServer extends AbstractLanguageServer implements ProcessLanguageServer, LanguageClientAware {

public static final String LANGUAGE_ID = "LANGUAGE_ID_LIBERTY";
private static final Logger LOGGER = Logger.getLogger(LibertyLanguageServer.class.getName());

Expand All @@ -57,6 +54,7 @@ public CompletableFuture<InitializeResult> initialize(InitializeParams params) {

ServerCapabilities serverCapabilities = createServerCapabilities();
InitializeResult initializeResult = new InitializeResult(serverCapabilities);
LibertyConfigFileManager.initLibertyConfigFileManager(params.getWorkspaceFolders());
dshimo marked this conversation as resolved.
Show resolved Hide resolved
return CompletableFuture.completedFuture(initializeResult);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2020, 2022 IBM Corporation and others.
* Copyright (c) 2020, 2023 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
Expand All @@ -12,13 +12,17 @@
*******************************************************************************/
package io.openliberty.tools.langserver;

import java.util.logging.Logger;
cherylking marked this conversation as resolved.
Show resolved Hide resolved

import org.eclipse.lsp4j.DidChangeConfigurationParams;
import org.eclipse.lsp4j.DidChangeWatchedFilesParams;
import org.eclipse.lsp4j.FileEvent;
import org.eclipse.lsp4j.services.WorkspaceService;

public class LibertyWorkspaceService implements WorkspaceService {

private final LibertyLanguageServer libertyLanguageServer;
private static final Logger LOGGER = Logger.getLogger(LibertyWorkspaceService.class.getName());

public LibertyWorkspaceService(LibertyLanguageServer libertyls) {
this.libertyLanguageServer = libertyls;
Expand All @@ -31,6 +35,11 @@ public void didChangeConfiguration(DidChangeConfigurationParams params) {

@Override
public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) {
// Do nothing
for (FileEvent change : params.getChanges()) {
String uri = change.getUri();
if (uri.endsWith(LibertyConfigFileManager.LIBERTY_PLUGIN_CONFIG_XML)) {
LibertyConfigFileManager.processLibertyPluginConfigXml(uri);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2022 IBM Corporation and others.
* Copyright (c) 2022, 2023 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
Expand Down Expand Up @@ -29,8 +29,8 @@
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;

import io.openliberty.tools.langserver.LibertyConfigFileManager;
dshimo marked this conversation as resolved.
Show resolved Hide resolved
import io.openliberty.tools.langserver.ls.LibertyTextDocument;
import io.openliberty.tools.langserver.utils.ParserFileHelperUtil;
import io.openliberty.tools.langserver.utils.PropertiesValidationResult;
import io.openliberty.tools.langserver.utils.ServerPropertyValues;

Expand All @@ -42,7 +42,7 @@ public class LibertyPropertiesDiagnosticService {

public Map<String, PropertiesValidationResult> compute(String text, LibertyTextDocument openedDocument) {
Map<String, PropertiesValidationResult> errors = new HashMap<>();
if (ParserFileHelperUtil.isBootstrapPropertiesFile(openedDocument) || ParserFileHelperUtil.isServerEnvFile(openedDocument)) {
if (LibertyConfigFileManager.isBootstrapPropertiesFile(openedDocument) || LibertyConfigFileManager.isServerEnvFile(openedDocument)) {
BufferedReader br = new BufferedReader(new StringReader(text));
String line = null;
int lineNumber = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.eclipse.lsp4j.MarkupContent;
import org.eclipse.lsp4j.Position;

import io.openliberty.tools.langserver.LibertyConfigFileManager;
import io.openliberty.tools.langserver.ls.LibertyTextDocument;
import io.openliberty.tools.langserver.utils.ParserFileHelperUtil;

Expand Down Expand Up @@ -47,7 +48,7 @@ public PropertiesEntryInstance(String entryLine, LibertyTextDocument textDocumen
propertyValueInstanceString = null;
}
// In bootstrap.properties files, ignore whitespaces before and after keys and values
if (ParserFileHelperUtil.isBootstrapPropertiesFile(textDocumentItem)) {
if (LibertyConfigFileManager.isBootstrapPropertiesFile(textDocumentItem)) {
propertyKeyInstanceString = propertyKeyInstanceString.trim();
if (propertyValueInstanceString != null) {
propertyValueInstanceString = propertyValueInstanceString.trim();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.logging.Logger;
import java.util.regex.Pattern;

import io.openliberty.tools.langserver.LibertyConfigFileManager;
import io.openliberty.tools.langserver.ls.LibertyTextDocument;

public class Messages {
Expand Down Expand Up @@ -81,7 +82,6 @@ public static List<String> getMatchingKeys(String query, LibertyTextDocument tex
initializeBundles();
}

String filename = textDocument.getUri();
// remove completion results that don't contain the query string (case-insensitive search)
Predicate<String> filter = s -> {
for (int i = s.length() - query.length(); i >= 0; --i) {
Expand All @@ -90,11 +90,11 @@ public static List<String> getMatchingKeys(String query, LibertyTextDocument tex
}
return true;
};
if (filename.contains("server.env")) { // server.env file
if (LibertyConfigFileManager.isServerEnvFile(textDocument)) { // server.env file
List<String> keys = new ArrayList<String>(serverPropertyKeys);
keys.removeIf(filter);
return keys;
} else if (filename.contains("bootstrap.properties")) { // bootstrap.properties file
} else if (LibertyConfigFileManager.isBootstrapPropertiesFile(textDocument)) { // bootstrap.properties file
List<String> keys = new ArrayList<String>(bootstrapPropertyKeys);
keys.removeIf(filter);
return keys;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2022 IBM Corporation and others.
* Copyright (c) 2022, 2023 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
Expand All @@ -14,7 +14,6 @@
import io.openliberty.tools.langserver.ls.LibertyTextDocument;

public class ParserFileHelperUtil {

public String getLine(LibertyTextDocument textDocumentItem, Position position) {
return getLine(textDocumentItem, position.getLine());
}
Expand All @@ -30,12 +29,4 @@ public String getLine(String text, int line) {
}
return null;
}

dshimo marked this conversation as resolved.
Show resolved Hide resolved
public static boolean isServerEnvFile(LibertyTextDocument tdi) {
return tdi.getUri().endsWith("server.env");
}

public static boolean isBootstrapPropertiesFile(LibertyTextDocument tdi) {
return tdi.getUri().endsWith("bootstrap.properties");
}
}
}
Loading