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

Feature/websocket Поддержка работы через websocket #2567

Merged
merged 12 commits into from
Oct 28, 2022

Conversation

salexdv
Copy link
Contributor

@salexdv salexdv commented Feb 24, 2022

Описание

Поддержка общения в BSL Language Server через websocket

Связанные задачи

#1427

Чеклист

Общие

  • Ветка PR обновлена из develop
  • Отладочные, закомментированные и прочие, не имеющие смысла участки кода удалены
  • Изменения покрыты тестами
  • Обязательные действия перед коммитом выполнены (запускал команду gradlew precommit)

Дополнительно

Хотелось бы иметь возможность общаться BSL Language Server через websocket. Например, это нужно для редактора на основе monaco editor, работающего внутри 1С через webkit
bsl

Сложности, недоработки, вопросы

С разработкой на Java был мало знаком, до того как решил добавить указанную функциональность, отсюда есть несколько сложностей, которые мешают полноценной работе сервера через вебсокет.

  1. Сервер всегда использует конфигурацию по умолчанию. Я так понимаю, что взаимодействие с конфигурацией завязано на аннотацию @RequiredArgsConstructor и поле класса private final LanguageServerConfiguration. При обработке команды в классе WebsocketStartCommand никаких проблем с чтением и обновлением конфигурации из указанного файла не возникает. Однако, не ясно, как конфигурацию можно передать в BSLLSWebSocketEndpoint для дальнейшей передачи в BSLLanguageServer. Можете подсказать, как решить эту проблему?
  2. Как правильно тестировать работу через вебсокеты, какие тесты желательно реализовать?

@salexdv salexdv changed the title Feature/websocket Feature/websocket Поддержка работы через websocket Feb 24, 2022
@nixel2007
Copy link
Member

ну нихрена себе!

спасибо, заценим.

@nixel2007 nixel2007 requested review from nixel2007, theshadowco, otymko and asosnoviy and removed request for nixel2007 February 24, 2022 16:21
footer = "@|green Copyright(c) 2018-2020|@")
@Component
@RequiredArgsConstructor
public class WebsocketStartCommand implements Callable<Integer> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@salexdv в рамках реализации LanguageServerStartCommand общение через веб-сокет не реализовать? Это ведь только транспорт, верно?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Наверно, это можно назвать транспортом. Делал по аналогии с LanguageServerStartCommand

private final LanguageServerConfiguration configuration;
private final WebSocketRunner launcher;

public Integer call() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@salexdv нужны тесты

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тесты добавил. Проверяется запуск сервера и возможность подключения к нему. Таких проверок будет достаточно?

@otymko
Copy link
Member

otymko commented Feb 27, 2022

Описание

Поддержка общения в BSL Language Server через websocket

Связанные задачи

#1427

Чеклист

Общие

  • Ветка PR обновлена из develop
  • Отладочные, закомментированные и прочие, не имеющие смысла участки кода удалены
  • Изменения покрыты тестами
  • Обязательные действия перед коммитом выполнены (запускал команду gradlew precommit)

Дополнительно

Хотелось бы иметь возможность общаться BSL Language Server через websocket. Например, это нужно для редактора на основе monaco editor, работающего внутри 1С через webkit bsl

Сложности, недоработки, вопросы

С разработкой на Java был мало знаком, до того как решил добавить указанную функциональность, отсюда есть несколько сложностей, которые мешают полноценной работе сервера через вебсокет.

  1. Сервер всегда использует конфигурацию по умолчанию. Я так понимаю, что взаимодействие с конфигурацией завязано на аннотацию @RequiredArgsConstructor и поле класса private final LanguageServerConfiguration. При обработке команды в классе WebsocketStartCommand никаких проблем с чтением и обновлением конфигурации из указанного файла не возникает. Однако, не ясно, как конфигурацию можно передать в BSLLSWebSocketEndpoint для дальнейшей передачи в BSLLanguageServer. Можете подсказать, как решить эту проблему?
  2. Как правильно тестировать работу через вебсокеты, какие тесты желательно реализовать?
  1. попробую на днях найти примеры тестирования lsp и вебсокета, вроде такие тесты были в lsp4j.

@salexdv
Copy link
Contributor Author

salexdv commented Mar 1, 2022

Прилагаю "клиенты" для тестирования test_client.zip

С простым текстом модуля index_successful.html всё хорошо, а вот index_fail.html падает вот здесь и причины падения пока непонятны.

salexdv added a commit to salexdv/bsl-language-server that referenced this pull request Mar 1, 2022
@nixel2007
Copy link
Member

@salexdv а есть стак-трейс и trace.log?

@salexdv salexdv closed this Mar 2, 2022
@salexdv salexdv reopened this Mar 2, 2022
@salexdv
Copy link
Contributor Author

salexdv commented Mar 2, 2022

@salexdv а есть стак-трейс и trace.log?

Да. trace.log

@nixel2007
Copy link
Member

@qtLex тут трейс лог по переменным в индексе. глянешь?

@qtLex
Copy link
Contributor

qtLex commented Mar 12, 2022

@nixel2007 Гляну.

@qtLex
Copy link
Contributor

qtLex commented Mar 12, 2022

@qtLex тут трейс лог по переменным в индексе. глянешь?

Вот, что удалось накопать #2577.

@nixel2007
Copy link
Member

Влил, спасибо!


@Override
protected void configure(Builder<LanguageClient> builder) {
builder.setLocalService(BSLLSBinding.getApplicationContext().getBean(BSLLanguageServer.class));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Использование BSLLSBinding чем-то обосновано? Обычное внедрение зависимостей не работало?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Через обычное внедрение зависимостей у меня не получилось сделать. Скорее всего, я что-то делал не так, но клиент у меня возвращал вот такое

BSL_Unsupported

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Хм. Можем попробовать еще раз? на класс повесить аннотацию @RequiredArgsConstructor, и объявить поле private final BSLLanguageServer languageServer

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Пробовал так сделать. Сервер стартует, но клиент не соединяется с ним. Вернее соединение устанавливается, клиент вызывает метод initialize и отваливается. Затем заново попытка создать соединение и так по кругу.

chrome_4SK2fxQiUx
BSL_Unsupported_0


@Override
protected void connect(Collection<Object> localServices, LanguageClient remoteProxy) {
LanguageClientHolder clientHolder = BSLLSBinding.getApplicationContext().getBean(LanguageClientHolder.class);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Более правильно это делать через внедрение List

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не очень понимаю, о чём речь. Можешь подробнее написать или подсказать, куда посмотреть, что почитать?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

добавить поле private final List<LanguageClientAware> languageClientAwares;

и обойти их в цикле, вызвав у каждого connect. Как пример - LanguageServerStartCommand

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Попробовал так:

@RequiredArgsConstructor
public class BSLLSWebSocketEndpoint extends WebSocketEndpoint<LanguageClient> {
    private final List<LanguageClientAware> languageClientAwares;
    @Override
    protected void connect(Collection<Object> localServices, LanguageClient remoteProxy) {
        languageClientAwares.forEach(languageClientAware -> languageClientAware.connect(remoteProxy));
    }
}

Поведение у клиента становится таким же, как выше описывал для @RequiredArgsConstructor и private final BSLLanguageServer languageServer


String contextPath = "/";
var server = new Server(hostname, port, contextPath, null, BSLLSWebSocketServerConfigProvider.class);
Runtime.getRuntime().addShutdownHook(new Thread(server::stop, "bsl-websocket-server-shutdown-hook"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

может быть здесь подойдет использование родного спрингового @PreDestroy вместо shudown hook целиком JVM?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addShutdownHook убрал

} catch (Exception e) {
LOGGER.error("Cannot start websocket server.", e);
} finally {
server.stop();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

не совсем понимаю, зачем stop и тут и в preshutdown hook

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Этот блок тоже убрал


try {
server.start();
Thread.currentThread().join();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

поясни, плз, зачем здесь join?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Скорее всего напишу полную фигню т.к. опыта разработки на java мне явно не хватает. Тут приостанавливаем выполнение текущего потока, чтобы иметь возможность обработать InterruptedException и там остановить websocket-сервер. Долго сидел с этим, но другого способа нормально работать, в том числе тесты, не нашел. Если не обработать прерывание потока, тогда ws-сервер при окончании текста продолжает висеть на порту и следующий тест выдает Address already in use: bind

@Slf4j
@Component
@RequiredArgsConstructor
public class WebSocketRunner {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Предлагаю немного привести API в соответствие с LSP. Переименовать класс в WebSocketLauncher, а основной метод в startListening.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Может быть вообще сделать его implements Launcher

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тогда и внедрять его можно будет по какому-нибудь условию прямо в команду запуска в режиме LSP. Покурю этот момент

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Переименовал класс в WebSocketLauncher, а основной метод в startListening

build.gradle.kts Outdated Show resolved Hide resolved
@nixel2007
Copy link
Member

nixel2007 commented Jul 20, 2022 via email

@salexdv
Copy link
Contributor Author

salexdv commented Jul 21, 2022

Нужно врубать отладочные логи и глядеть... Тестовый клиент ты скидывал, да?

Да, выше тут выкладывал

@salexdv
Copy link
Contributor Author

salexdv commented Jul 21, 2022

Была у меня такая проблема:

  1. Сервер всегда использует конфигурацию по умолчанию. Я так понимаю, что взаимодействие с конфигурацией завязано на аннотацию @RequiredArgsConstructor и поле класса private final LanguageServerConfiguration. При обработке команды в классе WebsocketStartCommand никаких проблем с чтением и обновлением конфигурации из указанного файла не возникает. Однако, не ясно, как конфигурацию можно передать в BSLLSWebSocketEndpoint для дальнейшей передачи в BSLLanguageServer. Можете подсказать, как решить эту проблему?

Проблему решил, но не уверен, что сделал это правильно. Решение такое:

// Класс WebsocketCommand
LanguageServerConfiguration configuration = BSLLSBinding.getLanguageServerConfiguration();
configuration.update(configurationFile);

Изначально была аннотация @RequiredArgsConstructor и поле private final LanguageServerConfiguration configuration;, но в классе BSLLanguageServer всё равно оказывалась конфигурация по умолчанию, не смотря на передачу имени файла конфигурации при запуске.

@nixel2007
Copy link
Member

Однако, не ясно, как конфигурацию можно передать в BSLLSWebSocketEndpoint

Точно так же, через private final поле. Это класс-синглтон, все классы, которые запрашивают себе конфигурацию, получат один и тот же её инстанс. Соответственно если ты в команде сделаешь configuration.update, то она обновится во всех классах сразу.

Вообще единственное назначение BSLLSBinding - инициализация bsl ls вне спринг контекста, как например в сонар-плагине. Здесь же запуск из cli, поэтому все должно работать штатно, через внедрение зависимостей

@salexdv
Copy link
Contributor Author

salexdv commented Jul 21, 2022

Точно так же, через private final поле. Это класс-синглтон, все классы, которые запрашивают себе конфигурацию, получат один и тот же её инстанс. Соответственно если ты в команде сделаешь configuration.update, то она обновится во всех классах сразу.

Это понятно. Изначально по этому пути и пошёл, но не работает.

nixel2007 pushed a commit to salexdv/bsl-language-server that referenced this pull request Oct 26, 2022
nixel2007 pushed a commit to salexdv/bsl-language-server that referenced this pull request Oct 26, 2022
@nixel2007
Copy link
Member

@salexdv тэкс, я таки осилил перевод веб-сокетов на ядро спринга. просьба протестировать.

@asosnoviy @otymko присоединяйтесь к веселью.

P.S. Из того что обнаружил - в "тест-клиенте" довольно старая версия монако/лэнгклиента и нет свежих фич. плюс клиент не шлет workspace в initialize, из-за этого не подхватываются сорцы из конфиг файла. но это вроде бы все решаемо.

@@ -1,4 +1,4 @@
spring.main.web-application-type=none
server.port=8025
Copy link
Member

@otymko otymko Oct 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это бы сразу указать в доке.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В джавадоке указано, в маркдаун еще перенести надо


@Component
@RequiredArgsConstructor
public class BSLLSWebSocketEndpoint extends WebSocketEndpoint<LanguageClient> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Жабадок спасет мир=)


@Configuration
@EnableWebSocket
public class WebSocketConfiguration {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Жабадок спасет мир

@salexdv
Copy link
Contributor Author

salexdv commented Oct 26, 2022

P.S. Из того что обнаружил - в "тест-клиенте" довольно старая версия монако/лэнгклиента и нет свежих фич. плюс клиент не шлет workspace в initialize, из-за этого не подхватываются сорцы из конфиг файла. но это вроде бы все решаемо.

Используется версия monaco 0.20.0. Более новые версии отказываются запускаться внутри 1С. Для использования в вебе вполне можно использовать последнюю.

@nixel2007
Copy link
Member

@salexdv поменял эндпоинт с /bsl-language-server на /lsp. Порт по умолчанию 8025. И порт и путь можно переопределить из командой строки.

@nixel2007 nixel2007 merged commit 2e7c1a4 into 1c-syntax:develop Oct 28, 2022
@sonarcloud
Copy link

sonarcloud bot commented Oct 28, 2022

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
No Duplication information No Duplication information

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants