Skip to content

Commit 0ac7638

Browse files
authored
Merge pull request #1671 from 1c-syntax/feature/force-close-on-parent-process-crash
2 parents 4a75dbb + af198ab commit 0ac7638

File tree

6 files changed

+273
-0
lines changed

6 files changed

+273
-0
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* This file is a part of BSL Language Server.
3+
*
4+
* Copyright © 2018-2021
5+
* Alexey Sosnoviy <labotamy@gmail.com>, Nikita Gryzlov <nixel2007@gmail.com> and contributors
6+
*
7+
* SPDX-License-Identifier: LGPL-3.0-or-later
8+
*
9+
* BSL Language Server is free software; you can redistribute it and/or
10+
* modify it under the terms of the GNU Lesser General Public
11+
* License as published by the Free Software Foundation; either
12+
* version 3.0 of the License, or (at your option) any later version.
13+
*
14+
* BSL Language Server is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17+
* Lesser General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Lesser General Public
20+
* License along with BSL Language Server.
21+
*/
22+
package com.github._1c_syntax.bsl.languageserver;
23+
24+
import com.github._1c_syntax.bsl.languageserver.events.LanguageServerInitializeRequestReceivedEvent;
25+
import lombok.RequiredArgsConstructor;
26+
import lombok.extern.slf4j.Slf4j;
27+
import org.eclipse.lsp4j.services.LanguageServer;
28+
import org.springframework.context.event.EventListener;
29+
import org.springframework.scheduling.annotation.Scheduled;
30+
import org.springframework.stereotype.Component;
31+
32+
/**
33+
* Наблюдатель за жизнью родительского процесса, запустившего Language Server.
34+
*/
35+
@Component
36+
@Slf4j
37+
@RequiredArgsConstructor
38+
public class ParentProcessWatcher {
39+
40+
private final LanguageServer languageServer;
41+
private long parentProcessId;
42+
43+
/**
44+
* Обработчик события {@link LanguageServerInitializeRequestReceivedEvent}.
45+
* <p>
46+
* Анализирует параметры запроса и подготавливает данные для слежения за родительским процессом.
47+
*
48+
* @param event Событие
49+
*/
50+
@EventListener
51+
public void handleEvent(LanguageServerInitializeRequestReceivedEvent event) {
52+
var processId = event.getParams().getProcessId();
53+
if (processId == null) {
54+
return;
55+
}
56+
parentProcessId = processId;
57+
}
58+
59+
/**
60+
* Фоновая процедура, отслеживающая родительский процесс.
61+
*/
62+
@Scheduled(fixedDelay = 30000L)
63+
public void watch() {
64+
if (parentProcessId == 0) {
65+
return;
66+
}
67+
68+
boolean processIsAlive = ProcessHandle.of(parentProcessId)
69+
.map(ProcessHandle::isAlive)
70+
.orElse(false);
71+
72+
if (!processIsAlive) {
73+
LOGGER.error("Parent process with pid {} is not found. Closing application...", parentProcessId);
74+
languageServer.exit();
75+
}
76+
}
77+
}

src/main/java/com/github/_1c_syntax/bsl/languageserver/aop/EventPublisherAspect.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,14 @@
2525
import com.github._1c_syntax.bsl.languageserver.configuration.events.LanguageServerConfigurationChangedEvent;
2626
import com.github._1c_syntax.bsl.languageserver.context.DocumentContext;
2727
import com.github._1c_syntax.bsl.languageserver.context.events.DocumentContextContentChangedEvent;
28+
import com.github._1c_syntax.bsl.languageserver.events.LanguageServerInitializeRequestReceivedEvent;
2829
import lombok.NoArgsConstructor;
2930
import lombok.extern.slf4j.Slf4j;
3031
import org.aspectj.lang.JoinPoint;
3132
import org.aspectj.lang.annotation.AfterReturning;
3233
import org.aspectj.lang.annotation.Aspect;
34+
import org.eclipse.lsp4j.InitializeParams;
35+
import org.eclipse.lsp4j.services.LanguageServer;
3336
import org.springframework.context.ApplicationEvent;
3437
import org.springframework.context.ApplicationEventPublisher;
3538
import org.springframework.context.ApplicationEventPublisherAware;
@@ -71,6 +74,15 @@ public void documentContextRebuild(JoinPoint joinPoint) {
7174
publishEvent(new DocumentContextContentChangedEvent((DocumentContext) joinPoint.getThis()));
7275
}
7376

77+
@AfterReturning("Pointcuts.isLanguageServer() && Pointcuts.isInitializeCall() && args(initializeParams)")
78+
public void languageServerInitialize(JoinPoint joinPoint, InitializeParams initializeParams) {
79+
var event = new LanguageServerInitializeRequestReceivedEvent(
80+
(LanguageServer) joinPoint.getThis(),
81+
initializeParams
82+
);
83+
publishEvent(event);
84+
}
85+
7486
private void publishEvent(ApplicationEvent event) {
7587
if (!active) {
7688
LOGGER.warn("Trying to send event in not active event publisher.");

src/main/java/com/github/_1c_syntax/bsl/languageserver/aop/Pointcuts.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.github._1c_syntax.bsl.languageserver.context.ServerContext;
2727
import com.github._1c_syntax.bsl.languageserver.diagnostics.BSLDiagnostic;
2828
import org.aspectj.lang.annotation.Pointcut;
29+
import org.eclipse.lsp4j.services.LanguageServer;
2930

3031
/**
3132
* Сборник общих Pointcut для AOP-слоя.
@@ -37,61 +38,86 @@ public class Pointcuts {
3738
*/
3839
@Pointcut("within(com.github._1c_syntax.bsl.languageserver..*)")
3940
public void isBSLLanguageServerScope() {
41+
// no-op
4042
}
4143

4244
/**
4345
* Это обращение к классу {@link LanguageServerConfiguration}.
4446
*/
4547
@Pointcut("within(com.github._1c_syntax.bsl.languageserver.configuration.LanguageServerConfiguration)")
4648
public void isLanguageServerConfiguration() {
49+
// no-op
4750
}
4851

4952
/**
5053
* Это обращение к классу {@link DocumentContext}.
5154
*/
5255
@Pointcut("within(com.github._1c_syntax.bsl.languageserver.context.DocumentContext)")
5356
public void isDocumentContext() {
57+
// no-op
58+
}
59+
60+
/**
61+
* Это обращение к реализации {@link LanguageServer}.
62+
*/
63+
@Pointcut("within(org.eclipse.lsp4j.services.LanguageServer+)")
64+
public void isLanguageServer() {
65+
// no-op
5466
}
5567

5668
/**
5769
* Это обращение к классу {@link ServerContext}.
5870
*/
5971
@Pointcut("within(com.github._1c_syntax.bsl.languageserver.context.ServerContext)")
6072
public void isServerContext() {
73+
// no-op
6174
}
6275

6376
/**
6477
* Это обращение к реализации интерфейса {@link BSLDiagnostic}.
6578
*/
6679
@Pointcut("within(com.github._1c_syntax.bsl.languageserver.diagnostics.BSLDiagnostic+)")
6780
public void isBSLDiagnostic() {
81+
// no-op
6882
}
6983

7084
/**
7185
* Это вызов метода rebuild.
7286
*/
7387
@Pointcut("isBSLLanguageServerScope() && execution(* rebuild(..))")
7488
public void isRebuildCall() {
89+
// no-op
7590
}
7691

7792
/**
7893
* Это вызов метода update.
7994
*/
8095
@Pointcut("isBSLLanguageServerScope() && execution(* update(..))")
8196
public void isUpdateCall() {
97+
// no-op
8298
}
8399

84100
/**
85101
* Это вызов метода reset.
86102
*/
87103
@Pointcut("isBSLLanguageServerScope() && execution(* reset(..))")
88104
public void isResetCall() {
105+
// no-op
89106
}
90107

91108
/**
92109
* Это вызов метода getDiagnostics.
93110
*/
94111
@Pointcut("isBSLLanguageServerScope() && execution(* getDiagnostics(..))")
95112
public void isGetDiagnosticsCall() {
113+
// no-op
114+
}
115+
116+
/**
117+
* Это вызов метода initialize.
118+
*/
119+
@Pointcut("isBSLLanguageServerScope() && execution(* initialize(..))")
120+
public void isInitializeCall() {
121+
// no-op
96122
}
97123
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* This file is a part of BSL Language Server.
3+
*
4+
* Copyright © 2018-2021
5+
* Alexey Sosnoviy <labotamy@gmail.com>, Nikita Gryzlov <nixel2007@gmail.com> and contributors
6+
*
7+
* SPDX-License-Identifier: LGPL-3.0-or-later
8+
*
9+
* BSL Language Server is free software; you can redistribute it and/or
10+
* modify it under the terms of the GNU Lesser General Public
11+
* License as published by the Free Software Foundation; either
12+
* version 3.0 of the License, or (at your option) any later version.
13+
*
14+
* BSL Language Server is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17+
* Lesser General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Lesser General Public
20+
* License along with BSL Language Server.
21+
*/
22+
package com.github._1c_syntax.bsl.languageserver.events;
23+
24+
import lombok.Getter;
25+
import org.eclipse.lsp4j.InitializeParams;
26+
import org.eclipse.lsp4j.services.LanguageServer;
27+
import org.springframework.context.ApplicationEvent;
28+
29+
/**
30+
* Описание события получения языковым сервером запроса initialize.
31+
* <p>
32+
* В качестве источника события содержит ссылку на {@link LanguageServer}.
33+
*/
34+
public class LanguageServerInitializeRequestReceivedEvent extends ApplicationEvent {
35+
36+
private static final long serialVersionUID = 7153531865051478056L;
37+
38+
/**
39+
* Параметры вызванного запроса initialize.
40+
*/
41+
@Getter
42+
private final transient InitializeParams params;
43+
44+
public LanguageServerInitializeRequestReceivedEvent(LanguageServer source, InitializeParams params) {
45+
super(source);
46+
this.params = params;
47+
}
48+
49+
@Override
50+
public LanguageServer getSource() {
51+
return (LanguageServer) super.getSource();
52+
}
53+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* This file is a part of BSL Language Server.
3+
*
4+
* Copyright © 2018-2021
5+
* Alexey Sosnoviy <labotamy@gmail.com>, Nikita Gryzlov <nixel2007@gmail.com> and contributors
6+
*
7+
* SPDX-License-Identifier: LGPL-3.0-or-later
8+
*
9+
* BSL Language Server is free software; you can redistribute it and/or
10+
* modify it under the terms of the GNU Lesser General Public
11+
* License as published by the Free Software Foundation; either
12+
* version 3.0 of the License, or (at your option) any later version.
13+
*
14+
* BSL Language Server is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17+
* Lesser General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Lesser General Public
20+
* License along with BSL Language Server.
21+
*/
22+
/**
23+
* События пакета com.github._1c_syntax.bsl.languageserver.
24+
*/
25+
@ParametersAreNonnullByDefault
26+
package com.github._1c_syntax.bsl.languageserver.events;
27+
28+
import javax.annotation.ParametersAreNonnullByDefault;
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* This file is a part of BSL Language Server.
3+
*
4+
* Copyright © 2018-2021
5+
* Alexey Sosnoviy <labotamy@gmail.com>, Nikita Gryzlov <nixel2007@gmail.com> and contributors
6+
*
7+
* SPDX-License-Identifier: LGPL-3.0-or-later
8+
*
9+
* BSL Language Server is free software; you can redistribute it and/or
10+
* modify it under the terms of the GNU Lesser General Public
11+
* License as published by the Free Software Foundation; either
12+
* version 3.0 of the License, or (at your option) any later version.
13+
*
14+
* BSL Language Server is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17+
* Lesser General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Lesser General Public
20+
* License along with BSL Language Server.
21+
*/
22+
package com.github._1c_syntax.bsl.languageserver;
23+
24+
import com.github._1c_syntax.bsl.languageserver.events.LanguageServerInitializeRequestReceivedEvent;
25+
import org.eclipse.lsp4j.InitializeParams;
26+
import org.eclipse.lsp4j.services.LanguageServer;
27+
import org.junit.jupiter.api.Test;
28+
import org.mockito.InjectMocks;
29+
import org.mockito.Mock;
30+
import org.springframework.boot.test.context.SpringBootTest;
31+
32+
import static org.mockito.Mockito.never;
33+
import static org.mockito.Mockito.times;
34+
import static org.mockito.Mockito.verify;
35+
36+
@SpringBootTest
37+
class ParentProcessWatcherTest {
38+
39+
@InjectMocks
40+
private ParentProcessWatcher parentProcessWatcher;
41+
42+
@Mock
43+
private LanguageServer languageServer;
44+
45+
@Test
46+
void testParentProcessIsDead() {
47+
// given
48+
var params = new InitializeParams();
49+
params.setProcessId(-1);
50+
51+
var event = new LanguageServerInitializeRequestReceivedEvent(languageServer, params);
52+
parentProcessWatcher.handleEvent(event);
53+
54+
// when
55+
parentProcessWatcher.watch();
56+
57+
// then
58+
verify(languageServer, times(1)).exit();
59+
}
60+
61+
@Test
62+
void testParentProcessIsAlive() {
63+
// given
64+
var params = new InitializeParams();
65+
params.setProcessId((int) ProcessHandle.current().pid());
66+
67+
var event = new LanguageServerInitializeRequestReceivedEvent(languageServer, params);
68+
parentProcessWatcher.handleEvent(event);
69+
70+
// when
71+
parentProcessWatcher.watch();
72+
73+
// then
74+
verify(languageServer, never()).exit();
75+
}
76+
77+
}

0 commit comments

Comments
 (0)