diff options
Diffstat (limited to 'language-web/src/main/java/tools')
16 files changed, 165 insertions, 103 deletions
diff --git a/language-web/src/main/java/tools/refinery/language/web/ProblemWebModule.java b/language-web/src/main/java/tools/refinery/language/web/ProblemWebModule.java index 799a9c64..04f38414 100644 --- a/language-web/src/main/java/tools/refinery/language/web/ProblemWebModule.java +++ b/language-web/src/main/java/tools/refinery/language/web/ProblemWebModule.java | |||
@@ -3,9 +3,15 @@ | |||
3 | */ | 3 | */ |
4 | package tools.refinery.language.web; | 4 | package tools.refinery.language.web; |
5 | 5 | ||
6 | import org.eclipse.xtext.web.server.XtextServiceDispatcher; | ||
7 | |||
8 | import tools.refinery.language.web.xtext.server.NoPrecomputedServicesXtextServiceDispatcher; | ||
6 | 9 | ||
7 | /** | 10 | /** |
8 | * Use this class to register additional components to be used within the web application. | 11 | * Use this class to register additional components to be used within the web application. |
9 | */ | 12 | */ |
10 | public class ProblemWebModule extends AbstractProblemWebModule { | 13 | public class ProblemWebModule extends AbstractProblemWebModule { |
14 | public Class<? extends XtextServiceDispatcher> bindXtextServiceDispatcher() { | ||
15 | return NoPrecomputedServicesXtextServiceDispatcher.class; | ||
16 | } | ||
11 | } | 17 | } |
diff --git a/language-web/src/main/java/tools/refinery/language/web/ProblemWebSocketServlet.java b/language-web/src/main/java/tools/refinery/language/web/ProblemWebSocketServlet.java index 9ffd6557..df67b521 100644 --- a/language-web/src/main/java/tools/refinery/language/web/ProblemWebSocketServlet.java +++ b/language-web/src/main/java/tools/refinery/language/web/ProblemWebSocketServlet.java | |||
@@ -3,7 +3,7 @@ package tools.refinery.language.web; | |||
3 | import org.eclipse.xtext.util.DisposableRegistry; | 3 | import org.eclipse.xtext.util.DisposableRegistry; |
4 | 4 | ||
5 | import jakarta.servlet.ServletException; | 5 | import jakarta.servlet.ServletException; |
6 | import tools.refinery.language.web.xtext.XtextWebSocketServlet; | 6 | import tools.refinery.language.web.xtext.servlet.XtextWebSocketServlet; |
7 | 7 | ||
8 | public class ProblemWebSocketServlet extends XtextWebSocketServlet { | 8 | public class ProblemWebSocketServlet extends XtextWebSocketServlet { |
9 | 9 | ||
diff --git a/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java b/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java index f6311070..a71d8e93 100644 --- a/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java +++ b/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java | |||
@@ -23,7 +23,7 @@ import org.slf4j.LoggerFactory; | |||
23 | 23 | ||
24 | import jakarta.servlet.DispatcherType; | 24 | import jakarta.servlet.DispatcherType; |
25 | import jakarta.servlet.SessionTrackingMode; | 25 | import jakarta.servlet.SessionTrackingMode; |
26 | import tools.refinery.language.web.xtext.XtextWebSocketServlet; | 26 | import tools.refinery.language.web.xtext.servlet.XtextWebSocketServlet; |
27 | 27 | ||
28 | public class ServerLauncher { | 28 | public class ServerLauncher { |
29 | public static final String DEFAULT_LISTEN_ADDRESS = "localhost"; | 29 | public static final String DEFAULT_LISTEN_ADDRESS = "localhost"; |
diff --git a/language-web/src/main/java/tools/refinery/language/web/xtext/server/NoPrecomputedServicesXtextServiceDispatcher.java b/language-web/src/main/java/tools/refinery/language/web/xtext/server/NoPrecomputedServicesXtextServiceDispatcher.java new file mode 100644 index 00000000..6660d6ac --- /dev/null +++ b/language-web/src/main/java/tools/refinery/language/web/xtext/server/NoPrecomputedServicesXtextServiceDispatcher.java | |||
@@ -0,0 +1,16 @@ | |||
1 | package tools.refinery.language.web.xtext.server; | ||
2 | |||
3 | import org.eclipse.xtext.web.server.XtextServiceDispatcher; | ||
4 | import org.eclipse.xtext.web.server.model.PrecomputedServiceRegistry; | ||
5 | |||
6 | import com.google.inject.Singleton; | ||
7 | |||
8 | @Singleton | ||
9 | public class NoPrecomputedServicesXtextServiceDispatcher extends XtextServiceDispatcher { | ||
10 | @Override | ||
11 | protected void registerPreComputedServices(PrecomputedServiceRegistry registry) { | ||
12 | // Do not register any precomputed services, because we will always send | ||
13 | // requests for any pre-computation in the same websocket message as the | ||
14 | // document update request. | ||
15 | } | ||
16 | } | ||
diff --git a/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/ResponseHandler.java b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/ResponseHandler.java new file mode 100644 index 00000000..b1fcbc8b --- /dev/null +++ b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/ResponseHandler.java | |||
@@ -0,0 +1,8 @@ | |||
1 | package tools.refinery.language.web.xtext.servlet; | ||
2 | |||
3 | import java.io.IOException; | ||
4 | |||
5 | @FunctionalInterface | ||
6 | public interface ResponseHandler { | ||
7 | void onResponse(XtextWebSocketResponse response) throws IOException; | ||
8 | } | ||
diff --git a/language-web/src/main/java/tools/refinery/language/web/xtext/SimpleServiceContext.java b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/SimpleServiceContext.java index 8036b749..945d5db0 100644 --- a/language-web/src/main/java/tools/refinery/language/web/xtext/SimpleServiceContext.java +++ b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/SimpleServiceContext.java | |||
@@ -1,4 +1,4 @@ | |||
1 | package tools.refinery.language.web.xtext; | 1 | package tools.refinery.language.web.xtext.servlet; |
2 | 2 | ||
3 | import java.util.Map; | 3 | import java.util.Map; |
4 | import java.util.Set; | 4 | import java.util.Set; |
@@ -8,6 +8,7 @@ import org.eclipse.xtext.web.server.ISession; | |||
8 | import org.eclipse.xtext.web.server.InvalidRequestException; | 8 | import org.eclipse.xtext.web.server.InvalidRequestException; |
9 | 9 | ||
10 | import com.google.common.collect.ImmutableMap; | 10 | import com.google.common.collect.ImmutableMap; |
11 | import com.google.common.collect.ImmutableSet; | ||
11 | 12 | ||
12 | record SimpleServiceContext(ISession session, Map<String, String> parameters) implements IServiceContext { | 13 | record SimpleServiceContext(ISession session, Map<String, String> parameters) implements IServiceContext { |
13 | 14 | ||
@@ -17,13 +18,9 @@ record SimpleServiceContext(ISession session, Map<String, String> parameters) im | |||
17 | 18 | ||
18 | public static final String STATE_ID_PARAMETER = "requiredStateId"; | 19 | public static final String STATE_ID_PARAMETER = "requiredStateId"; |
19 | 20 | ||
20 | public SimpleServiceContext(ISession session, XtextWebSocketRequest request, String stateId, int index) { | ||
21 | this(session, addPerTransactionData(request, stateId, request.getRequestData().get(index))); | ||
22 | } | ||
23 | |||
24 | @Override | 21 | @Override |
25 | public Set<String> getParameterKeys() { | 22 | public Set<String> getParameterKeys() { |
26 | return parameters.keySet(); | 23 | return ImmutableSet.copyOf(parameters.keySet()); |
27 | } | 24 | } |
28 | 25 | ||
29 | @Override | 26 | @Override |
@@ -36,8 +33,9 @@ record SimpleServiceContext(ISession session, Map<String, String> parameters) im | |||
36 | return session; | 33 | return session; |
37 | } | 34 | } |
38 | 35 | ||
39 | private static Map<String, String> addPerTransactionData(XtextWebSocketRequest request, String stateId, | 36 | public static IServiceContext ofTransaction(ISession session, XtextWebSocketRequest request, String stateId, |
40 | Map<String, String> parameters) { | 37 | int index) { |
38 | var parameters = request.getRequestData().get(index); | ||
41 | checkParameters(parameters, RESOURCE_NAME_PARAMETER); | 39 | checkParameters(parameters, RESOURCE_NAME_PARAMETER); |
42 | checkParameters(parameters, CONTENT_TYPE_PARAMETER); | 40 | checkParameters(parameters, CONTENT_TYPE_PARAMETER); |
43 | checkParameters(parameters, STATE_ID_PARAMETER); | 41 | checkParameters(parameters, STATE_ID_PARAMETER); |
@@ -52,7 +50,8 @@ record SimpleServiceContext(ISession session, Map<String, String> parameters) im | |||
52 | if (stateId != null) { | 50 | if (stateId != null) { |
53 | builder.put(STATE_ID_PARAMETER, stateId); | 51 | builder.put(STATE_ID_PARAMETER, stateId); |
54 | } | 52 | } |
55 | return builder.build(); | 53 | var allParameters = builder.build(); |
54 | return new SimpleServiceContext(session, allParameters); | ||
56 | } | 55 | } |
57 | 56 | ||
58 | private static void checkParameters(Map<String, String> parameters, String perTransactionParameter) { | 57 | private static void checkParameters(Map<String, String> parameters, String perTransactionParameter) { |
diff --git a/language-web/src/main/java/tools/refinery/language/web/xtext/SimpleSession.java b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/SimpleSession.java index 691077d0..09c055a2 100644 --- a/language-web/src/main/java/tools/refinery/language/web/xtext/SimpleSession.java +++ b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/SimpleSession.java | |||
@@ -1,4 +1,4 @@ | |||
1 | package tools.refinery.language.web.xtext; | 1 | package tools.refinery.language.web.xtext.servlet; |
2 | 2 | ||
3 | import java.util.HashMap; | 3 | import java.util.HashMap; |
4 | import java.util.Map; | 4 | import java.util.Map; |
diff --git a/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocket.java b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/TransactionExecutor.java index 0849ccb7..08687097 100644 --- a/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocket.java +++ b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/TransactionExecutor.java | |||
@@ -1,17 +1,8 @@ | |||
1 | package tools.refinery.language.web.xtext; | 1 | package tools.refinery.language.web.xtext.servlet; |
2 | 2 | ||
3 | import java.io.IOException; | 3 | import java.io.IOException; |
4 | import java.io.Reader; | ||
5 | 4 | ||
6 | import org.eclipse.emf.common.util.URI; | 5 | import org.eclipse.emf.common.util.URI; |
7 | import org.eclipse.jetty.websocket.api.Session; | ||
8 | import org.eclipse.jetty.websocket.api.StatusCode; | ||
9 | import org.eclipse.jetty.websocket.api.WriteCallback; | ||
10 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; | ||
11 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; | ||
12 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; | ||
13 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; | ||
14 | import org.eclipse.jetty.websocket.api.annotations.WebSocket; | ||
15 | import org.eclipse.xtext.resource.IResourceServiceProvider; | 6 | import org.eclipse.xtext.resource.IResourceServiceProvider; |
16 | import org.eclipse.xtext.web.server.IServiceResult; | 7 | import org.eclipse.xtext.web.server.IServiceResult; |
17 | import org.eclipse.xtext.web.server.ISession; | 8 | import org.eclipse.xtext.web.server.ISession; |
@@ -25,87 +16,35 @@ import org.eclipse.xtext.web.server.hover.HoverResult; | |||
25 | import org.eclipse.xtext.web.server.model.DocumentStateResult; | 16 | import org.eclipse.xtext.web.server.model.DocumentStateResult; |
26 | import org.eclipse.xtext.web.server.occurrences.OccurrencesResult; | 17 | import org.eclipse.xtext.web.server.occurrences.OccurrencesResult; |
27 | import org.eclipse.xtext.web.server.persistence.ResourceContentResult; | 18 | import org.eclipse.xtext.web.server.persistence.ResourceContentResult; |
28 | import org.slf4j.Logger; | ||
29 | import org.slf4j.LoggerFactory; | ||
30 | 19 | ||
31 | import com.google.common.base.Strings; | 20 | import com.google.common.base.Strings; |
32 | import com.google.gson.Gson; | ||
33 | import com.google.gson.JsonIOException; | ||
34 | import com.google.gson.JsonParseException; | ||
35 | import com.google.inject.Injector; | 21 | import com.google.inject.Injector; |
36 | 22 | ||
37 | @WebSocket | 23 | public class TransactionExecutor { |
38 | public class XtextWebSocket { | ||
39 | private static final Logger LOG = LoggerFactory.getLogger(XtextWebSocket.class); | ||
40 | |||
41 | private final Gson gson = new Gson(); | ||
42 | |||
43 | private final ISession session; | 24 | private final ISession session; |
44 | 25 | ||
45 | private final IResourceServiceProvider.Registry resourceServiceProviderRegistry; | 26 | private final IResourceServiceProvider.Registry resourceServiceProviderRegistry; |
46 | 27 | ||
47 | public XtextWebSocket(ISession session, IResourceServiceProvider.Registry resourceServiceProviderRegistry) { | 28 | public TransactionExecutor(ISession session, IResourceServiceProvider.Registry resourceServiceProviderRegistry) { |
48 | this.session = session; | 29 | this.session = session; |
49 | this.resourceServiceProviderRegistry = resourceServiceProviderRegistry; | 30 | this.resourceServiceProviderRegistry = resourceServiceProviderRegistry; |
50 | } | 31 | } |
51 | 32 | ||
52 | @OnWebSocketConnect | 33 | public void handleRequest(XtextWebSocketRequest request, ResponseHandler handler) throws IOException { |
53 | public void onConnect(Session webSocketSession) { | ||
54 | LOG.debug("New websocket connection from {}", webSocketSession.getRemoteAddress()); | ||
55 | } | ||
56 | |||
57 | @OnWebSocketClose | ||
58 | public void onClose(Session webSocketSession, int statusCode, String reason) { | ||
59 | if (statusCode == StatusCode.NORMAL) { | ||
60 | LOG.debug("{} closed connection normally: {}", webSocketSession.getRemoteAddress(), reason); | ||
61 | } else { | ||
62 | LOG.warn("{} closed connection with status code {}: {}", webSocketSession.getRemoteAddress(), statusCode, | ||
63 | reason); | ||
64 | } | ||
65 | } | ||
66 | |||
67 | @OnWebSocketError | ||
68 | public void onError(Session webSocketSession, Throwable error) { | ||
69 | LOG.error("Internal websocket error in connection from" + webSocketSession.getRemoteAddress(), error); | ||
70 | } | ||
71 | |||
72 | @OnWebSocketMessage | ||
73 | public void onMessage(Session webSocketSession, Reader reader) { | ||
74 | XtextWebSocketRequest request; | ||
75 | try { | ||
76 | request = gson.fromJson(reader, XtextWebSocketRequest.class); | ||
77 | } catch (JsonIOException e) { | ||
78 | LOG.error("Cannot read from websocket from" + webSocketSession.getRemoteAddress(), e); | ||
79 | if (webSocketSession.isOpen()) { | ||
80 | webSocketSession.close(StatusCode.SERVER_ERROR, "Cannot read payload"); | ||
81 | } | ||
82 | return; | ||
83 | } catch (JsonParseException e) { | ||
84 | LOG.warn("Malformed websocket request from" + webSocketSession.getRemoteAddress(), e); | ||
85 | webSocketSession.close(XtextStatusCode.INVALID_JSON, "Invalid JSON payload"); | ||
86 | return; | ||
87 | } | ||
88 | var requestData = request.getRequestData(); | 34 | var requestData = request.getRequestData(); |
89 | if (requestData == null || requestData.isEmpty()) { | 35 | if (requestData == null || requestData.isEmpty()) { |
90 | // Nothing to do. | 36 | // Nothing to do. |
91 | return; | 37 | return; |
92 | } | 38 | } |
93 | int nCalls = requestData.size(); | 39 | int nCalls = requestData.size(); |
94 | try { | 40 | int lastCall = handleTransaction(request, handler); |
95 | int lastCall = handleTransaction(webSocketSession, request); | 41 | for (int index = lastCall + 1; index < nCalls; index++) { |
96 | for (int index = lastCall + 1; index < nCalls; index++) { | 42 | handler.onResponse( |
97 | sendReply(webSocketSession, | 43 | new XtextWebSocketErrorResponse(request, index, XtextWebSocketErrorKind.TRANSACTION_CANCELLED)); |
98 | new XtextWebSocketErrorResponse(request, index, XtextWebSocketErrorKind.TRANSACTION_CANCELLED)); | ||
99 | } | ||
100 | } catch (IOException e) { | ||
101 | LOG.warn("Cannot initiaite async write to websocket " + webSocketSession.getRemoteAddress(), e); | ||
102 | if (webSocketSession.isOpen()) { | ||
103 | webSocketSession.close(StatusCode.SERVER_ERROR, "Cannot write payload"); | ||
104 | } | ||
105 | } | 44 | } |
106 | } | 45 | } |
107 | 46 | ||
108 | protected int handleTransaction(Session webSocketSession, XtextWebSocketRequest request) throws IOException { | 47 | protected int handleTransaction(XtextWebSocketRequest request, ResponseHandler handler) throws IOException { |
109 | var requestData = request.getRequestData(); | 48 | var requestData = request.getRequestData(); |
110 | var stateId = request.getRequiredStateId(); | 49 | var stateId = request.getRequiredStateId(); |
111 | int index = 0; | 50 | int index = 0; |
@@ -114,10 +53,10 @@ public class XtextWebSocket { | |||
114 | var serviceDispatcher = injector.getInstance(XtextServiceDispatcher.class); | 53 | var serviceDispatcher = injector.getInstance(XtextServiceDispatcher.class); |
115 | int nCalls = requestData.size(); | 54 | int nCalls = requestData.size(); |
116 | for (; index < nCalls; index++) { | 55 | for (; index < nCalls; index++) { |
117 | var serviceContext = new SimpleServiceContext(session, request, stateId, index); | 56 | var serviceContext = SimpleServiceContext.ofTransaction(session, request, stateId, index); |
118 | var service = serviceDispatcher.getService(serviceContext); | 57 | var service = serviceDispatcher.getService(serviceContext); |
119 | var serviceResult = service.getService().apply(); | 58 | var serviceResult = service.getService().apply(); |
120 | sendReply(webSocketSession, new XtextWebSocketOkResponse(request, index, serviceResult)); | 59 | handler.onResponse(new XtextWebSocketOkResponse(request, index, serviceResult)); |
121 | if (serviceResult instanceof ServiceConflictResult) { | 60 | if (serviceResult instanceof ServiceConflictResult) { |
122 | break; | 61 | break; |
123 | } | 62 | } |
@@ -127,25 +66,15 @@ public class XtextWebSocket { | |||
127 | } | 66 | } |
128 | } | 67 | } |
129 | } catch (InvalidRequestException e) { | 68 | } catch (InvalidRequestException e) { |
130 | sendReply(webSocketSession, | 69 | handler.onResponse( |
131 | new XtextWebSocketErrorResponse(request, index, XtextWebSocketErrorKind.REQUEST_ERROR, e)); | 70 | new XtextWebSocketErrorResponse(request, index, XtextWebSocketErrorKind.REQUEST_ERROR, e)); |
132 | } catch (RuntimeException e) { | 71 | } catch (RuntimeException e) { |
133 | sendReply(webSocketSession, | 72 | handler.onResponse( |
134 | new XtextWebSocketErrorResponse(request, index, XtextWebSocketErrorKind.SERVER_ERROR, e)); | 73 | new XtextWebSocketErrorResponse(request, index, XtextWebSocketErrorKind.SERVER_ERROR, e)); |
135 | } | 74 | } |
136 | return index; | 75 | return index; |
137 | } | 76 | } |
138 | 77 | ||
139 | protected void sendReply(Session webSocketSession, XtextWebSocketResponse response) throws IOException { | ||
140 | var responseString = gson.toJson(response); | ||
141 | webSocketSession.getRemote().sendPartialString(responseString, true, new WriteCallback() { | ||
142 | @Override | ||
143 | public void writeFailed(Throwable x) { | ||
144 | LOG.warn("Cannot complete async write to websocket " + webSocketSession.getRemoteAddress(), x); | ||
145 | } | ||
146 | }); | ||
147 | } | ||
148 | |||
149 | /** | 78 | /** |
150 | * Get the injector to satisfy the request in the {@code serviceContext}. | 79 | * Get the injector to satisfy the request in the {@code serviceContext}. |
151 | * | 80 | * |
diff --git a/language-web/src/main/java/tools/refinery/language/web/xtext/XtextStatusCode.java b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextStatusCode.java index 8ef60108..0cd229e8 100644 --- a/language-web/src/main/java/tools/refinery/language/web/xtext/XtextStatusCode.java +++ b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextStatusCode.java | |||
@@ -1,4 +1,4 @@ | |||
1 | package tools.refinery.language.web.xtext; | 1 | package tools.refinery.language.web.xtext.servlet; |
2 | 2 | ||
3 | public final class XtextStatusCode { | 3 | public final class XtextStatusCode { |
4 | public static final int INVALID_JSON = 4007; | 4 | public static final int INVALID_JSON = 4007; |
diff --git a/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java new file mode 100644 index 00000000..25f67545 --- /dev/null +++ b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java | |||
@@ -0,0 +1,94 @@ | |||
1 | package tools.refinery.language.web.xtext.servlet; | ||
2 | |||
3 | import java.io.IOException; | ||
4 | import java.io.Reader; | ||
5 | |||
6 | import org.eclipse.jetty.websocket.api.Session; | ||
7 | import org.eclipse.jetty.websocket.api.StatusCode; | ||
8 | import org.eclipse.jetty.websocket.api.WriteCallback; | ||
9 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; | ||
10 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; | ||
11 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; | ||
12 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; | ||
13 | import org.eclipse.jetty.websocket.api.annotations.WebSocket; | ||
14 | import org.eclipse.xtext.resource.IResourceServiceProvider; | ||
15 | import org.eclipse.xtext.web.server.ISession; | ||
16 | import org.slf4j.Logger; | ||
17 | import org.slf4j.LoggerFactory; | ||
18 | |||
19 | import com.google.gson.Gson; | ||
20 | import com.google.gson.JsonIOException; | ||
21 | import com.google.gson.JsonParseException; | ||
22 | |||
23 | @WebSocket | ||
24 | public class XtextWebSocket { | ||
25 | private static final Logger LOG = LoggerFactory.getLogger(XtextWebSocket.class); | ||
26 | |||
27 | private final Gson gson = new Gson(); | ||
28 | |||
29 | private final TransactionExecutor executor; | ||
30 | |||
31 | public XtextWebSocket(TransactionExecutor executor) { | ||
32 | this.executor = executor; | ||
33 | } | ||
34 | |||
35 | public XtextWebSocket(ISession session, IResourceServiceProvider.Registry resourceServiceProviderRegistry) { | ||
36 | this(new TransactionExecutor(session, resourceServiceProviderRegistry)); | ||
37 | } | ||
38 | |||
39 | @OnWebSocketConnect | ||
40 | public void onConnect(Session webSocketSession) { | ||
41 | LOG.debug("New websocket connection from {}", webSocketSession.getRemoteAddress()); | ||
42 | } | ||
43 | |||
44 | @OnWebSocketClose | ||
45 | public void onClose(Session webSocketSession, int statusCode, String reason) { | ||
46 | if (statusCode == StatusCode.NORMAL) { | ||
47 | LOG.debug("{} closed connection normally: {}", webSocketSession.getRemoteAddress(), reason); | ||
48 | } else { | ||
49 | LOG.warn("{} closed connection with status code {}: {}", webSocketSession.getRemoteAddress(), statusCode, | ||
50 | reason); | ||
51 | } | ||
52 | } | ||
53 | |||
54 | @OnWebSocketError | ||
55 | public void onError(Session webSocketSession, Throwable error) { | ||
56 | LOG.error("Internal websocket error in connection from" + webSocketSession.getRemoteAddress(), error); | ||
57 | } | ||
58 | |||
59 | @OnWebSocketMessage | ||
60 | public void onMessage(Session webSocketSession, Reader reader) { | ||
61 | XtextWebSocketRequest request; | ||
62 | try { | ||
63 | request = gson.fromJson(reader, XtextWebSocketRequest.class); | ||
64 | } catch (JsonIOException e) { | ||
65 | LOG.error("Cannot read from websocket from" + webSocketSession.getRemoteAddress(), e); | ||
66 | if (webSocketSession.isOpen()) { | ||
67 | webSocketSession.close(StatusCode.SERVER_ERROR, "Cannot read payload"); | ||
68 | } | ||
69 | return; | ||
70 | } catch (JsonParseException e) { | ||
71 | LOG.warn("Malformed websocket request from" + webSocketSession.getRemoteAddress(), e); | ||
72 | webSocketSession.close(XtextStatusCode.INVALID_JSON, "Invalid JSON payload"); | ||
73 | return; | ||
74 | } | ||
75 | try { | ||
76 | executor.handleRequest(request, response -> sendResponse(webSocketSession, response)); | ||
77 | } catch (IOException e) { | ||
78 | LOG.warn("Cannot initiaite async write to websocket " + webSocketSession.getRemoteAddress(), e); | ||
79 | if (webSocketSession.isOpen()) { | ||
80 | webSocketSession.close(StatusCode.SERVER_ERROR, "Cannot write payload"); | ||
81 | } | ||
82 | } | ||
83 | } | ||
84 | |||
85 | protected void sendResponse(Session webSocketSession, XtextWebSocketResponse response) throws IOException { | ||
86 | var responseString = gson.toJson(response); | ||
87 | webSocketSession.getRemote().sendPartialString(responseString, true, new WriteCallback() { | ||
88 | @Override | ||
89 | public void writeFailed(Throwable x) { | ||
90 | LOG.warn("Cannot complete async write to websocket " + webSocketSession.getRemoteAddress(), x); | ||
91 | } | ||
92 | }); | ||
93 | } | ||
94 | } | ||
diff --git a/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocketErrorKind.java b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketErrorKind.java index 5759f39e..66ea227f 100644 --- a/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocketErrorKind.java +++ b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketErrorKind.java | |||
@@ -1,4 +1,4 @@ | |||
1 | package tools.refinery.language.web.xtext; | 1 | package tools.refinery.language.web.xtext.servlet; |
2 | 2 | ||
3 | import com.google.gson.annotations.SerializedName; | 3 | import com.google.gson.annotations.SerializedName; |
4 | 4 | ||
diff --git a/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocketErrorResponse.java b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketErrorResponse.java index 1d2cf08a..3be7df39 100644 --- a/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocketErrorResponse.java +++ b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketErrorResponse.java | |||
@@ -1,4 +1,4 @@ | |||
1 | package tools.refinery.language.web.xtext; | 1 | package tools.refinery.language.web.xtext.servlet; |
2 | 2 | ||
3 | import java.util.Objects; | 3 | import java.util.Objects; |
4 | 4 | ||
diff --git a/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocketOkResponse.java b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketOkResponse.java index aa453544..0a841895 100644 --- a/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocketOkResponse.java +++ b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketOkResponse.java | |||
@@ -1,4 +1,4 @@ | |||
1 | package tools.refinery.language.web.xtext; | 1 | package tools.refinery.language.web.xtext.servlet; |
2 | 2 | ||
3 | import java.util.Objects; | 3 | import java.util.Objects; |
4 | 4 | ||
diff --git a/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocketRequest.java b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketRequest.java index 8aee70a1..8be67bc1 100644 --- a/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocketRequest.java +++ b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketRequest.java | |||
@@ -1,4 +1,4 @@ | |||
1 | package tools.refinery.language.web.xtext; | 1 | package tools.refinery.language.web.xtext.servlet; |
2 | 2 | ||
3 | import java.util.List; | 3 | import java.util.List; |
4 | import java.util.Map; | 4 | import java.util.Map; |
@@ -19,6 +19,16 @@ public class XtextWebSocketRequest { | |||
19 | @SerializedName("request") | 19 | @SerializedName("request") |
20 | private List<Map<String, String>> requestData; | 20 | private List<Map<String, String>> requestData; |
21 | 21 | ||
22 | public XtextWebSocketRequest(String id, String resourceName, String contentType, String requiredStateId, | ||
23 | List<Map<String, String>> requestData) { | ||
24 | super(); | ||
25 | this.id = id; | ||
26 | this.resourceName = resourceName; | ||
27 | this.contentType = contentType; | ||
28 | this.requiredStateId = requiredStateId; | ||
29 | this.requestData = requestData; | ||
30 | } | ||
31 | |||
22 | public String getId() { | 32 | public String getId() { |
23 | return id; | 33 | return id; |
24 | } | 34 | } |
diff --git a/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocketResponse.java b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketResponse.java index 9e15aa69..2e7cfbbb 100644 --- a/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocketResponse.java +++ b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketResponse.java | |||
@@ -1,4 +1,4 @@ | |||
1 | package tools.refinery.language.web.xtext; | 1 | package tools.refinery.language.web.xtext.servlet; |
2 | 2 | ||
3 | public sealed interface XtextWebSocketResponse permits XtextWebSocketOkResponse,XtextWebSocketErrorResponse { | 3 | public sealed interface XtextWebSocketResponse permits XtextWebSocketOkResponse,XtextWebSocketErrorResponse { |
4 | public String getId(); | 4 | public String getId(); |
diff --git a/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocketServlet.java b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketServlet.java index 5769e9e7..6d4d2cad 100644 --- a/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocketServlet.java +++ b/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketServlet.java | |||
@@ -1,4 +1,4 @@ | |||
1 | package tools.refinery.language.web.xtext; | 1 | package tools.refinery.language.web.xtext.servlet; |
2 | 2 | ||
3 | import java.io.IOException; | 3 | import java.io.IOException; |
4 | import java.time.Duration; | 4 | import java.time.Duration; |