Skip to content

Commit

Permalink
null check before getting from a ConcurrentHashMap.
Browse files Browse the repository at this point in the history
#7248: java.lang.NullPointerException: Cannot invoke "Object.hashCode()" because "key" is null
  • Loading branch information
wangmingliang-ms committed Jan 31, 2023
1 parent f8b7cb8 commit 3909ce2
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@
package org.wso2.lsp4intellij;

import com.intellij.AppTopics;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginInstaller;
import com.intellij.ide.plugins.PluginStateListener;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ApplicationComponent;
Expand All @@ -32,7 +29,6 @@
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.microsoft.intellij.CommonConst;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
Expand Down Expand Up @@ -62,11 +58,11 @@

public class IntellijLanguageClient implements ApplicationComponent, Disposable {

private static Logger LOG = Logger.getInstance(IntellijLanguageClient.class);
private static final Logger LOG = Logger.getInstance(IntellijLanguageClient.class);
private static final Map<Pair<String, String>, LanguageServerWrapper> extToLanguageWrapper = new ConcurrentHashMap<>();
private static Map<String, Set<LanguageServerWrapper>> projectToLanguageWrappers = new ConcurrentHashMap<>();
private static Map<Pair<String, String>, LanguageServerDefinition> extToServerDefinition = new ConcurrentHashMap<>();
private static Map<String, LSPExtensionManager> extToExtManager = new ConcurrentHashMap<>();
private static final Map<String, Set<LanguageServerWrapper>> projectToLanguageWrappers = new ConcurrentHashMap<>();
private static final Map<Pair<String, String>, LanguageServerDefinition> extToServerDefinition = new ConcurrentHashMap<>();
private static final Map<String, LSPExtensionManager> extToExtManager = new ConcurrentHashMap<>();
private static final Predicate<LanguageServerWrapper> RUNNING = (s) -> s.getStatus() != ServerStatus.STOPPED;

@Override
Expand All @@ -87,7 +83,7 @@ public void initComponent() {
Runtime.getRuntime().addShutdownHook(new Thread(() -> projectToLanguageWrappers.values().stream()
.flatMap(Collection::stream).filter(RUNNING).forEach(s -> s.stop(true))));
LOG.info("Intellij Language Client initialized successfully");
} catch (Exception e) {
} catch (final Exception e) {
LOG.warn("Fatal error occurred when initializing Intellij language client.", e);
}
}
Expand All @@ -96,7 +92,7 @@ public void initComponent() {
* Use it to initialize the server connection for the given project (useful if no editor is launched)
*/
public void initProjectConnections(@NotNull Project project) {
String projectStr = FileUtils.projectToUri(project);
final String projectStr = FileUtils.projectToUri(project);
// find serverdefinition keys for this project and try to start a wrapper
extToServerDefinition.entrySet().stream().filter(e -> e.getKey().getRight().equals(projectStr)).forEach(entry -> {
updateLanguageWrapperContainers(project, entry.getKey(), entry.getValue()).start();
Expand Down Expand Up @@ -153,7 +149,7 @@ public static void addExtensionManager(@NotNull String ext, @NotNull LSPExtensio
* @return All instantiated ServerWrappers
*/
public static Set<LanguageServerWrapper> getAllServerWrappersFor(String projectUri) {
Set<LanguageServerWrapper> allWrappers = new HashSet<>();
final Set<LanguageServerWrapper> allWrappers = new HashSet<>();
extToLanguageWrapper.forEach((stringStringPair, languageServerWrapper) -> {
if (FileUtils.projectToUri(languageServerWrapper.getProject()).equals(projectUri)) {
allWrappers.add(languageServerWrapper);
Expand All @@ -166,10 +162,7 @@ public static Set<LanguageServerWrapper> getAllServerWrappersFor(String projectU
* @return All registered LSP protocol extension managers.
*/
public static LSPExtensionManager getExtensionManagerFor(String fileExt) {
if (extToExtManager.containsKey(fileExt)) {
return extToExtManager.get(fileExt);
}
return null;
return Optional.ofNullable(fileExt).map(extToExtManager::get).orElse(null);
}

/**
Expand All @@ -187,18 +180,18 @@ public static boolean isExtensionSupported(VirtualFile virtualFile) {
* @param editor the editor
*/
public static void editorOpened(Editor editor) {
VirtualFile file = FileDocumentManager.getInstance().getFile(editor.getDocument());
final VirtualFile file = FileDocumentManager.getInstance().getFile(editor.getDocument());
if (!FileUtils.isFileSupported(file)) {
LOG.debug("Handling open on a editor which host a LightVirtual/Null file");
return;
}

Project project = editor.getProject();
final Project project = editor.getProject();
if (project == null) {
LOG.debug("Opened an unsupported editor, which does not have an attached project.");
return;
}
String projectUri = FileUtils.projectToUri(project);
final String projectUri = FileUtils.projectToUri(project);
if (projectUri == null) {
LOG.warn("File for editor " + editor.getDocument().getText() + " is null");
return;
Expand All @@ -215,7 +208,7 @@ public static void editorOpened(Editor editor) {
LanguageServerDefinition serverDefinition = extToServerDefinition.get(new ImmutablePair<>(ext, projectUri));
if (serverDefinition == null) {
// Fallback to file name pattern matching, where the map key is a regex.
Optional<Pair<String, String>> keyForFile = extToServerDefinition.keySet().stream().
final Optional<Pair<String, String>> keyForFile = extToServerDefinition.keySet().stream().
filter(keyPair -> fileName.matches(keyPair.getLeft()) && keyPair.getRight().equals(projectUri))
.findFirst();
if (keyForFile.isPresent()) {
Expand All @@ -232,7 +225,7 @@ public static void editorOpened(Editor editor) {
}
if (serverDefinition == null) {
// Fallback to file name pattern matching, where the map key is a regex.
Optional<Pair<String, String>> keyForFile = extToServerDefinition.keySet().stream().
final Optional<Pair<String, String>> keyForFile = extToServerDefinition.keySet().stream().
filter(keyPair -> fileName.matches(keyPair.getLeft()) && keyPair.getRight().isEmpty())
.findFirst();
if (keyForFile.isPresent()) {
Expand All @@ -247,31 +240,31 @@ public static void editorOpened(Editor editor) {
return;
}
// Update project mapping for language servers.
LanguageServerWrapper wrapper = updateLanguageWrapperContainers(project, new ImmutablePair<>(ext, projectUri), serverDefinition);
final LanguageServerWrapper wrapper = updateLanguageWrapperContainers(project, new ImmutablePair<>(ext, projectUri), serverDefinition);

LOG.info("Adding file " + fileName);
wrapper.connect(editor);
});
}

private static synchronized LanguageServerWrapper updateLanguageWrapperContainers(Project project, final Pair<String, String> key, LanguageServerDefinition serverDefinition) {
String projectUri = FileUtils.projectToUri(project);
LanguageServerWrapper wrapper = extToLanguageWrapper.get(key);
String ext = key.getLeft();
final String projectUri = FileUtils.projectToUri(project);
LanguageServerWrapper wrapper = Optional.ofNullable(key).map(extToLanguageWrapper::get).orElse(null);
final String ext = Optional.ofNullable(key).map(Pair::getLeft).orElse(null);
if (wrapper == null) {
LOG.info("Instantiating wrapper for " + ext + " : " + projectUri);
if (extToExtManager.get(ext) != null) {
if (Objects.nonNull(ext) && extToExtManager.get(ext) != null) {
wrapper = new LanguageServerWrapper(serverDefinition, project, extToExtManager.get(ext));
} else {
wrapper = new LanguageServerWrapper(serverDefinition, project);
}
String[] exts = serverDefinition.ext.split(LanguageServerDefinition.SPLIT_CHAR);
for (String ex : exts) {
final String[] exts = serverDefinition.ext.split(LanguageServerDefinition.SPLIT_CHAR);
for (final String ex : exts) {
extToLanguageWrapper.put(new ImmutablePair<>(ex, projectUri), wrapper);
}

Set<LanguageServerWrapper> wrappers = projectToLanguageWrappers
.computeIfAbsent(projectUri, k -> new HashSet<>());
final Set<LanguageServerWrapper> wrappers = Optional.ofNullable(projectUri).map(u -> projectToLanguageWrappers
.computeIfAbsent(u, k -> new HashSet<>())).orElse(new HashSet<>());
wrappers.add(wrapper);

} else {
Expand All @@ -287,14 +280,14 @@ private static synchronized LanguageServerWrapper updateLanguageWrapperContainer
* @param editor the editor.
*/
public static void editorClosed(Editor editor) {
VirtualFile file = FileUtils.virtualFileFromEditor(editor);
final VirtualFile file = FileUtils.virtualFileFromEditor(editor);
if (!FileUtils.isFileSupported(file)) {
LOG.debug("Handling close on a editor which host a LightVirtual/Null file");
return;
}

pool(() -> {
LanguageServerWrapper serverWrapper = LanguageServerWrapper.forEditor(editor);
final LanguageServerWrapper serverWrapper = LanguageServerWrapper.forEditor(editor);
if (serverWrapper != null) {
LOG.info("Disconnecting " + FileUtils.editorToURIString(editor));
serverWrapper.disconnect(editor);
Expand Down Expand Up @@ -336,15 +329,15 @@ public static void setTimeouts(Map<Timeouts, Integer> newTimeouts) {
*/
@SuppressWarnings("unused")
public static void setTimeout(Timeouts timeout, int value) {
Map<Timeouts, Integer> newTimeout = new HashMap<>();
final Map<Timeouts, Integer> newTimeout = new HashMap<>();
newTimeout.put(timeout, value);
setTimeouts(newTimeout);
}

public static void removeWrapper(LanguageServerWrapper wrapper) {
if (wrapper.getProject() != null) {
String[] extensions = wrapper.getServerDefinition().ext.split(LanguageServerDefinition.SPLIT_CHAR);
for (String ext : extensions) {
final String[] extensions = wrapper.getServerDefinition().ext.split(LanguageServerDefinition.SPLIT_CHAR);
for (final String ext : extensions) {
extToLanguageWrapper.remove(new MutablePair<>(ext, FileUtils.pathToUri(
new File(wrapper.getProjectRootPath()).getAbsolutePath())));
}
Expand Down Expand Up @@ -384,9 +377,9 @@ public void dispose() {
}

private static void processDefinition(LanguageServerDefinition definition, String projectUri) {
String[] extensions = definition.ext.split(LanguageServerDefinition.SPLIT_CHAR);
for (String ext : extensions) {
Pair<String, String> keyPair = new ImmutablePair<>(ext, projectUri);
final String[] extensions = definition.ext.split(LanguageServerDefinition.SPLIT_CHAR);
for (final String ext : extensions) {
final Pair<String, String> keyPair = new ImmutablePair<>(ext, projectUri);
if (extToServerDefinition.get(keyPair) == null) {
extToServerDefinition.put(keyPair, definition);
LOG.info("Added server definition for " + ext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public DefaultLanguageClient(@NotNull ClientContext context) {

@Override
public CompletableFuture<ApplyWorkspaceEditResponse> applyEdit(ApplyWorkspaceEditParams params) {
boolean response = WorkspaceEditHandler.applyEdit(params.getEdit(), "LSP edits");
final boolean response = WorkspaceEditHandler.applyEdit(params.getEdit(), "LSP edits");
return CompletableFuture.supplyAsync(() -> new ApplyWorkspaceEditResponse(response));
}

Expand All @@ -78,23 +78,22 @@ public CompletableFuture<List<WorkspaceFolder>> workspaceFolders() {
@Override
public CompletableFuture<Void> registerCapability(RegistrationParams params) {
return CompletableFuture.runAsync(() -> params.getRegistrations().forEach(r -> {
String id = r.getId();
Optional<DynamicRegistrationMethods> method = DynamicRegistrationMethods.forName(r.getMethod());
final String id = r.getId();
final Optional<DynamicRegistrationMethods> method = DynamicRegistrationMethods.forName(r.getMethod());
method.ifPresent(dynamicRegistrationMethods -> registrations.put(id, Pair.of(dynamicRegistrationMethods, r.getRegisterOptions())));

}));
}

@Override
public CompletableFuture<Void> unregisterCapability(UnregistrationParams params) {
return CompletableFuture.runAsync(() -> params.getUnregisterations().forEach((Unregistration r) -> {
String id = r.getId();
Optional<DynamicRegistrationMethods> method = DynamicRegistrationMethods.forName(r.getMethod());
final String id = r.getId();
final Optional<DynamicRegistrationMethods> method = DynamicRegistrationMethods.forName(r.getMethod());
if (registrations.containsKey(id)) {
registrations.remove(id);
} else {
Map<Pair<DynamicRegistrationMethods, Object>, String> inverted = new HashMap<>();
for (Map.Entry<String, Pair<DynamicRegistrationMethods, Object>> entry : registrations.entrySet()) {
final Map<Pair<DynamicRegistrationMethods, Object>, String> inverted = new HashMap<>();
for (final Map.Entry<String, Pair<DynamicRegistrationMethods, Object>> entry : registrations.entrySet()) {
inverted.put(entry.getValue(), entry.getKey());
}
if (method.isPresent() && inverted.containsKey(method.get())) {
Expand All @@ -115,22 +114,22 @@ public void telemetryEvent(Object o) {

@Override
public void publishDiagnostics(PublishDiagnosticsParams publishDiagnosticsParams) {
String uri = FileUtils.sanitizeURI(publishDiagnosticsParams.getUri());
List<Diagnostic> diagnostics = publishDiagnosticsParams.getDiagnostics();
Set<EditorEventManager> managers = Optional.ofNullable(EditorEventManagerBase.managersForUri(uri)).orElse(Collections.emptySet());
for (EditorEventManager manager : managers) {
final String uri = FileUtils.sanitizeURI(publishDiagnosticsParams.getUri());
final List<Diagnostic> diagnostics = publishDiagnosticsParams.getDiagnostics();
final Set<EditorEventManager> managers = Optional.ofNullable(EditorEventManagerBase.managersForUri(uri)).orElse(Collections.emptySet());
for (final EditorEventManager manager : managers) {
manager.diagnostics(diagnostics);
}
}

@Override
public void showMessage(MessageParams messageParams) {
String title = "Language Server message";
String message = messageParams.getMessage();
final String title = "Language Server Message";
final String message = messageParams.getMessage();

if (isModal) {
ApplicationUtils.invokeLater(() -> {
MessageType msgType = messageParams.getType();
final MessageType msgType = messageParams.getType();
switch (msgType) {
case Error:
Messages.showErrorDialog(message, title);
Expand All @@ -148,7 +147,7 @@ public void showMessage(MessageParams messageParams) {
}
});
} else {
NotificationType type = getNotificationType(messageParams.getType());
final NotificationType type = getNotificationType(messageParams.getType());
final Notification notification = new Notification(
"lsp", messageParams.getType().toString(), messageParams.getMessage(), type);
notification.notify(context.getProject());
Expand All @@ -157,20 +156,20 @@ public void showMessage(MessageParams messageParams) {

@Override
public CompletableFuture<MessageActionItem> showMessageRequest(ShowMessageRequestParams showMessageRequestParams) {
List<MessageActionItem> actions = showMessageRequestParams.getActions();
String title = "Language Server " + showMessageRequestParams.getType().toString();
String message = showMessageRequestParams.getMessage();
MessageType msgType = showMessageRequestParams.getType();
final List<MessageActionItem> actions = showMessageRequestParams.getActions();
final String title = "Language Server " + showMessageRequestParams.getType().toString();
final String message = showMessageRequestParams.getMessage();
final MessageType msgType = showMessageRequestParams.getType();

String[] options = new String[actions == null ? 0 : actions.size()];
final String[] options = new String[actions == null ? 0 : actions.size()];
for (int i = 0, size = options.length; i < size; i++) {
options[i] = actions.get(i).getTitle();
}

int exitCode;
FutureTask<Integer> task;
final FutureTask<Integer> task;
if (isModal) {
Icon icon;
final Icon icon;
switch (msgType) {
case Error:
icon = UIUtil.getErrorIcon();
Expand All @@ -194,7 +193,7 @@ public CompletableFuture<MessageActionItem> showMessageRequest(ShowMessageReques

try {
exitCode = task.get();
} catch (InterruptedException | ExecutionException e) {
} catch (final InterruptedException | ExecutionException e) {
LOG.warn(e.getMessage());
exitCode = -1;
}
Expand All @@ -204,7 +203,7 @@ public CompletableFuture<MessageActionItem> showMessageRequest(ShowMessageReques
final Notification notification = STICKY_NOTIFICATION_GROUP.createNotification(title, null, message, getNotificationType(msgType));
final CompletableFuture<Integer> integerCompletableFuture = new CompletableFuture<>();
for (int i = 0, optionsSize = options.length; i < optionsSize; i++) {
int finalI = i;
final int finalI = i;
notification.addAction(new NotificationAction(options[i]) {
@Override
public boolean isDumbAware() {
Expand All @@ -227,7 +226,7 @@ public void actionPerformed(@NotNull AnActionEvent e, @NotNull Notification noti

try {
exitCode = integerCompletableFuture.get();
} catch (InterruptedException | ExecutionException e) {
} catch (final InterruptedException | ExecutionException e) {
LOG.warn(e.getMessage());
exitCode = -1;
}
Expand All @@ -250,8 +249,8 @@ protected NotificationType getNotificationType(@NotNull MessageType messageType)

@Override
public void logMessage(MessageParams messageParams) {
String message = messageParams.getMessage();
MessageType msgType = messageParams.getType();
final String message = messageParams.getMessage();
final MessageType msgType = messageParams.getType();

switch (msgType) {
case Error:
Expand Down
Loading

0 comments on commit 3909ce2

Please sign in to comment.