/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.plugin.languageserver.ide.registry; import com.google.gwt.core.client.Callback; import com.google.inject.Inject; import com.google.inject.Singleton; import com.google.web.bindery.event.shared.EventBus; import org.eclipse.che.api.languageserver.shared.ProjectExtensionKey; import org.eclipse.che.api.languageserver.shared.event.LanguageServerInitializeEvent; import org.eclipse.che.api.languageserver.shared.model.ExtendedInitializeResult; import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; import org.eclipse.che.api.promises.client.Operation; import org.eclipse.che.api.promises.client.OperationException; import org.eclipse.che.api.promises.client.Promise; import org.eclipse.che.api.promises.client.callback.CallbackPromiseHelper; import org.eclipse.che.api.promises.client.js.Promises; import org.eclipse.che.ide.api.machine.events.WsAgentStateEvent; import org.eclipse.che.ide.api.machine.events.WsAgentStateHandler; import org.eclipse.che.ide.api.notification.NotificationManager; import org.eclipse.che.ide.api.notification.StatusNotification; import org.eclipse.che.ide.rest.DtoUnmarshallerFactory; import org.eclipse.che.ide.util.loging.Log; import org.eclipse.che.ide.websocket.MessageBus; import org.eclipse.che.ide.websocket.MessageBusProvider; import org.eclipse.che.ide.websocket.WebSocketException; import org.eclipse.che.ide.websocket.rest.SubscriptionHandler; import org.eclipse.che.ide.websocket.rest.Unmarshallable; import org.eclipse.che.plugin.languageserver.ide.service.LanguageServerRegistryServiceClient; import org.eclipse.lsp4j.ServerCapabilities; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.eclipse.che.api.languageserver.shared.ProjectExtensionKey.createProjectKey; /** * @author Anatoliy Bazko */ @Singleton public class LanguageServerRegistry { private final EventBus eventBus; private final LanguageServerRegistryServiceClient client; private final Map<ProjectExtensionKey, ExtendedInitializeResult> projectToInitResult; private final Map<ProjectExtensionKey, Callback<ExtendedInitializeResult, Throwable>> callbackMap; @Inject public LanguageServerRegistry(EventBus eventBus, LanguageServerRegistryServiceClient client) { this.eventBus = eventBus; this.client = client; this.projectToInitResult = new HashMap<>(); this.callbackMap = new HashMap<>(); } /** * Registers language server description and capabilities. */ protected void register(String projectPath, LanguageDescription languageDescription, ServerCapabilities capabilities) { ExtendedInitializeResult initializeResult = new ExtendedInitializeResult(projectPath, capabilities, languageDescription); for (String ext : languageDescription.getFileExtensions()) { ProjectExtensionKey key = createProjectKey(projectPath, ext); projectToInitResult.put(key, initializeResult); if (callbackMap.containsKey(key)) { Callback<ExtendedInitializeResult, Throwable> callback = callbackMap.remove(key); callback.onSuccess(initializeResult); } } } public Promise<ExtendedInitializeResult> getOrInitializeServer(String projectPath, String ext, String filePath) { final ProjectExtensionKey key = createProjectKey(projectPath, ext); if (projectToInitResult.containsKey(key)) { return Promises.resolve(projectToInitResult.get(key)); } else { //call initialize service client.initializeServer(filePath); //wait for response return CallbackPromiseHelper.createFromCallback(new CallbackPromiseHelper.Call<ExtendedInitializeResult, Throwable>() { @Override public void makeCall(Callback<ExtendedInitializeResult, Throwable> callback) { callbackMap.put(key, callback); } }); } } @Inject protected void registerAllServers() { eventBus.addHandler(WsAgentStateEvent.TYPE, new WsAgentStateHandler() { @Override public void onWsAgentStarted(WsAgentStateEvent event) { Promise<List<ExtendedInitializeResult>> registeredLanguages = client.getRegisteredLanguages(); registeredLanguages.then(new Operation<List<ExtendedInitializeResult>>() { @Override public void apply(List<ExtendedInitializeResult> initialResults) throws OperationException { for (ExtendedInitializeResult initializeResultDTO : initialResults) { for (LanguageDescription languageDescription : initializeResultDTO.getSupportedLanguages()) { register(initializeResultDTO.getProject(), languageDescription, initializeResultDTO.getCapabilities()); } } } }); } @Override public void onWsAgentStopped(WsAgentStateEvent event) { } }); } @Inject protected void subscribeToInitializeEvent(final DtoUnmarshallerFactory unmarshallerFactory, final MessageBusProvider messageBusProvider, final NotificationManager notificationManager, final EventBus eventBus) { eventBus.addHandler(WsAgentStateEvent.TYPE, new WsAgentStateHandler() { @Override public void onWsAgentStarted(WsAgentStateEvent event) { MessageBus messageBus = messageBusProvider.getMachineMessageBus(); Unmarshallable<LanguageServerInitializeEvent> unmarshaller = unmarshallerFactory.newWSUnmarshaller(LanguageServerInitializeEvent.class); try { messageBus.subscribe("languageserver", new SubscriptionHandler<LanguageServerInitializeEvent>(unmarshaller) { @Override protected void onMessageReceived(LanguageServerInitializeEvent initializeEvent) { register(initializeEvent.getProjectPath(), initializeEvent.getSupportedLanguages(), initializeEvent.getServerCapabilities()); } @Override protected void onErrorReceived(Throwable exception) { notificationManager.notify(exception.getMessage(), StatusNotification.Status.FAIL, StatusNotification.DisplayMode.NOT_EMERGE_MODE); } }); } catch (WebSocketException exception) { Log.error(getClass(), exception); } } @Override public void onWsAgentStopped(WsAgentStateEvent event) { projectToInitResult.clear(); } }); } }