diff options
author | 2021-10-13 19:25:15 +0200 | |
---|---|---|
committer | 2021-10-31 19:26:10 +0100 | |
commit | 86cb47fa2c86c27c2f82a70d77c3181e1ba43715 (patch) | |
tree | 258ca861c2c35fbfc2ab93215ff39cd38af8df0b /language-web/src/main/java/tools | |
parent | feat(web): better websocket logging (diff) | |
download | refinery-86cb47fa2c86c27c2f82a70d77c3181e1ba43715.tar.gz refinery-86cb47fa2c86c27c2f82a70d77c3181e1ba43715.tar.zst refinery-86cb47fa2c86c27c2f82a70d77c3181e1ba43715.zip |
feat(web): batch operations for websocket protocol
Diffstat (limited to 'language-web/src/main/java/tools')
8 files changed, 272 insertions, 52 deletions
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/SimpleServiceContext.java index 1ec5b235..8036b749 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/SimpleServiceContext.java | |||
@@ -5,14 +5,25 @@ import java.util.Set; | |||
5 | 5 | ||
6 | import org.eclipse.xtext.web.server.IServiceContext; | 6 | import org.eclipse.xtext.web.server.IServiceContext; |
7 | import org.eclipse.xtext.web.server.ISession; | 7 | import org.eclipse.xtext.web.server.ISession; |
8 | import org.eclipse.xtext.web.server.InvalidRequestException; | ||
8 | 9 | ||
9 | import com.google.common.collect.ImmutableSet; | 10 | import com.google.common.collect.ImmutableMap; |
10 | 11 | ||
11 | record SimpleServiceContext(ISession session, Map<String, String> parameters) implements IServiceContext { | 12 | record SimpleServiceContext(ISession session, Map<String, String> parameters) implements IServiceContext { |
12 | 13 | ||
14 | public static final String RESOURCE_NAME_PARAMETER = "resource"; | ||
15 | |||
16 | public static final String CONTENT_TYPE_PARAMETER = "contentType"; | ||
17 | |||
18 | public static final String STATE_ID_PARAMETER = "requiredStateId"; | ||
19 | |||
20 | public SimpleServiceContext(ISession session, XtextWebSocketRequest request, String stateId, int index) { | ||
21 | this(session, addPerTransactionData(request, stateId, request.getRequestData().get(index))); | ||
22 | } | ||
23 | |||
13 | @Override | 24 | @Override |
14 | public Set<String> getParameterKeys() { | 25 | public Set<String> getParameterKeys() { |
15 | return ImmutableSet.copyOf(parameters.keySet()); | 26 | return parameters.keySet(); |
16 | } | 27 | } |
17 | 28 | ||
18 | @Override | 29 | @Override |
@@ -24,4 +35,30 @@ record SimpleServiceContext(ISession session, Map<String, String> parameters) im | |||
24 | public ISession getSession() { | 35 | public ISession getSession() { |
25 | return session; | 36 | return session; |
26 | } | 37 | } |
38 | |||
39 | private static Map<String, String> addPerTransactionData(XtextWebSocketRequest request, String stateId, | ||
40 | Map<String, String> parameters) { | ||
41 | checkParameters(parameters, RESOURCE_NAME_PARAMETER); | ||
42 | checkParameters(parameters, CONTENT_TYPE_PARAMETER); | ||
43 | checkParameters(parameters, STATE_ID_PARAMETER); | ||
44 | var builder = ImmutableMap.<String, String>builder(); | ||
45 | builder.putAll(parameters); | ||
46 | if (request.getResourceName() != null) { | ||
47 | builder.put(RESOURCE_NAME_PARAMETER, request.getResourceName()); | ||
48 | } | ||
49 | if (request.getContentType() != null) { | ||
50 | builder.put(CONTENT_TYPE_PARAMETER, request.getContentType()); | ||
51 | } | ||
52 | if (stateId != null) { | ||
53 | builder.put(STATE_ID_PARAMETER, stateId); | ||
54 | } | ||
55 | return builder.build(); | ||
56 | } | ||
57 | |||
58 | private static void checkParameters(Map<String, String> parameters, String perTransactionParameter) { | ||
59 | if (parameters.containsKey(perTransactionParameter)) { | ||
60 | throw new InvalidRequestException.InvalidParametersException( | ||
61 | "Parameters map must not contain '" + perTransactionParameter + "' parameter."); | ||
62 | } | ||
63 | } | ||
27 | } | 64 | } |
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/XtextWebSocket.java index 4ad98b6e..0849ccb7 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/XtextWebSocket.java | |||
@@ -13,13 +13,18 @@ import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; | |||
13 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; | 13 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; |
14 | import org.eclipse.jetty.websocket.api.annotations.WebSocket; | 14 | import org.eclipse.jetty.websocket.api.annotations.WebSocket; |
15 | import org.eclipse.xtext.resource.IResourceServiceProvider; | 15 | import org.eclipse.xtext.resource.IResourceServiceProvider; |
16 | import org.eclipse.xtext.web.server.IServiceContext; | ||
17 | import org.eclipse.xtext.web.server.IServiceResult; | 16 | import org.eclipse.xtext.web.server.IServiceResult; |
18 | import org.eclipse.xtext.web.server.ISession; | 17 | import org.eclipse.xtext.web.server.ISession; |
19 | import org.eclipse.xtext.web.server.IUnwrappableServiceResult; | ||
20 | import org.eclipse.xtext.web.server.InvalidRequestException; | 18 | import org.eclipse.xtext.web.server.InvalidRequestException; |
21 | import org.eclipse.xtext.web.server.InvalidRequestException.UnknownLanguageException; | 19 | import org.eclipse.xtext.web.server.InvalidRequestException.UnknownLanguageException; |
20 | import org.eclipse.xtext.web.server.ServiceConflictResult; | ||
22 | import org.eclipse.xtext.web.server.XtextServiceDispatcher; | 21 | import org.eclipse.xtext.web.server.XtextServiceDispatcher; |
22 | import org.eclipse.xtext.web.server.contentassist.ContentAssistResult; | ||
23 | import org.eclipse.xtext.web.server.formatting.FormattingResult; | ||
24 | import org.eclipse.xtext.web.server.hover.HoverResult; | ||
25 | import org.eclipse.xtext.web.server.model.DocumentStateResult; | ||
26 | import org.eclipse.xtext.web.server.occurrences.OccurrencesResult; | ||
27 | import org.eclipse.xtext.web.server.persistence.ResourceContentResult; | ||
23 | import org.slf4j.Logger; | 28 | import org.slf4j.Logger; |
24 | import org.slf4j.LoggerFactory; | 29 | import org.slf4j.LoggerFactory; |
25 | 30 | ||
@@ -80,17 +85,18 @@ public class XtextWebSocket { | |||
80 | webSocketSession.close(XtextStatusCode.INVALID_JSON, "Invalid JSON payload"); | 85 | webSocketSession.close(XtextStatusCode.INVALID_JSON, "Invalid JSON payload"); |
81 | return; | 86 | return; |
82 | } | 87 | } |
83 | var serviceContext = new SimpleServiceContext(session, request.getRequestData()); | 88 | var requestData = request.getRequestData(); |
84 | var response = handleMessage(webSocketSession, serviceContext); | 89 | if (requestData == null || requestData.isEmpty()) { |
85 | response.setId(request.getId()); | 90 | // Nothing to do. |
86 | var responseString = gson.toJson(response); | 91 | return; |
92 | } | ||
93 | int nCalls = requestData.size(); | ||
87 | try { | 94 | try { |
88 | webSocketSession.getRemote().sendPartialString(responseString, true, new WriteCallback() { | 95 | int lastCall = handleTransaction(webSocketSession, request); |
89 | @Override | 96 | for (int index = lastCall + 1; index < nCalls; index++) { |
90 | public void writeFailed(Throwable x) { | 97 | sendReply(webSocketSession, |
91 | LOG.warn("Cannot complete async write to websocket " + webSocketSession.getRemoteAddress(), x); | 98 | new XtextWebSocketErrorResponse(request, index, XtextWebSocketErrorKind.TRANSACTION_CANCELLED)); |
92 | } | 99 | } |
93 | }); | ||
94 | } catch (IOException e) { | 100 | } catch (IOException e) { |
95 | LOG.warn("Cannot initiaite async write to websocket " + webSocketSession.getRemoteAddress(), e); | 101 | LOG.warn("Cannot initiaite async write to websocket " + webSocketSession.getRemoteAddress(), e); |
96 | if (webSocketSession.isOpen()) { | 102 | if (webSocketSession.isOpen()) { |
@@ -99,27 +105,45 @@ public class XtextWebSocket { | |||
99 | } | 105 | } |
100 | } | 106 | } |
101 | 107 | ||
102 | protected XtextWebSocketResponse handleMessage(Session webSocketSession, IServiceContext serviceContext) { | 108 | protected int handleTransaction(Session webSocketSession, XtextWebSocketRequest request) throws IOException { |
103 | IServiceResult serviceResult; | 109 | var requestData = request.getRequestData(); |
110 | var stateId = request.getRequiredStateId(); | ||
111 | int index = 0; | ||
104 | try { | 112 | try { |
105 | var injector = getInjector(serviceContext); | 113 | var injector = getInjector(request); |
106 | var serviceDispatcher = injector.getInstance(XtextServiceDispatcher.class); | 114 | var serviceDispatcher = injector.getInstance(XtextServiceDispatcher.class); |
107 | var service = serviceDispatcher.getService(serviceContext); | 115 | int nCalls = requestData.size(); |
108 | serviceResult = service.getService().apply(); | 116 | for (; index < nCalls; index++) { |
117 | var serviceContext = new SimpleServiceContext(session, request, stateId, index); | ||
118 | var service = serviceDispatcher.getService(serviceContext); | ||
119 | var serviceResult = service.getService().apply(); | ||
120 | sendReply(webSocketSession, new XtextWebSocketOkResponse(request, index, serviceResult)); | ||
121 | if (serviceResult instanceof ServiceConflictResult) { | ||
122 | break; | ||
123 | } | ||
124 | var nextStateId = getNextStateId(serviceResult); | ||
125 | if (nextStateId != null) { | ||
126 | stateId = nextStateId; | ||
127 | } | ||
128 | } | ||
109 | } catch (InvalidRequestException e) { | 129 | } catch (InvalidRequestException e) { |
110 | LOG.warn("Invalid request from websocket " + webSocketSession.getRemoteAddress(), e); | 130 | sendReply(webSocketSession, |
111 | var error = new XtextWebSocketErrorResponse(); | 131 | new XtextWebSocketErrorResponse(request, index, XtextWebSocketErrorKind.REQUEST_ERROR, e)); |
112 | error.setErrorMessage(e.getMessage()); | 132 | } catch (RuntimeException e) { |
113 | return error; | 133 | sendReply(webSocketSession, |
134 | new XtextWebSocketErrorResponse(request, index, XtextWebSocketErrorKind.SERVER_ERROR, e)); | ||
114 | } | 135 | } |
115 | var response = new XtextWebSocketOkResponse(); | 136 | return index; |
116 | if (serviceResult instanceof IUnwrappableServiceResult unwrappableServiceResult | 137 | } |
117 | && unwrappableServiceResult.getContent() != null) { | 138 | |
118 | response.setResponseData(unwrappableServiceResult.getContent()); | 139 | protected void sendReply(Session webSocketSession, XtextWebSocketResponse response) throws IOException { |
119 | } else { | 140 | var responseString = gson.toJson(response); |
120 | response.setResponseData(serviceResult); | 141 | webSocketSession.getRemote().sendPartialString(responseString, true, new WriteCallback() { |
121 | } | 142 | @Override |
122 | return response; | 143 | public void writeFailed(Throwable x) { |
144 | LOG.warn("Cannot complete async write to websocket " + webSocketSession.getRemoteAddress(), x); | ||
145 | } | ||
146 | }); | ||
123 | } | 147 | } |
124 | 148 | ||
125 | /** | 149 | /** |
@@ -131,14 +155,14 @@ public class XtextWebSocket { | |||
131 | * @return the injector for the Xtext language in the request | 155 | * @return the injector for the Xtext language in the request |
132 | * @throws UnknownLanguageException if the Xtext language cannot be determined | 156 | * @throws UnknownLanguageException if the Xtext language cannot be determined |
133 | */ | 157 | */ |
134 | protected Injector getInjector(IServiceContext serviceContext) { | 158 | protected Injector getInjector(XtextWebSocketRequest request) { |
135 | IResourceServiceProvider resourceServiceProvider = null; | 159 | IResourceServiceProvider resourceServiceProvider = null; |
136 | var resourceName = serviceContext.getParameter("resource"); | 160 | var resourceName = request.getResourceName(); |
137 | if (resourceName == null) { | 161 | if (resourceName == null) { |
138 | resourceName = ""; | 162 | resourceName = ""; |
139 | } | 163 | } |
140 | var emfURI = URI.createURI(resourceName); | 164 | var emfURI = URI.createURI(resourceName); |
141 | var contentType = serviceContext.getParameter("contentType"); | 165 | var contentType = request.getContentType(); |
142 | if (Strings.isNullOrEmpty(contentType)) { | 166 | if (Strings.isNullOrEmpty(contentType)) { |
143 | resourceServiceProvider = resourceServiceProviderRegistry.getResourceServiceProvider(emfURI); | 167 | resourceServiceProvider = resourceServiceProviderRegistry.getResourceServiceProvider(emfURI); |
144 | if (resourceServiceProvider == null) { | 168 | if (resourceServiceProvider == null) { |
@@ -159,4 +183,26 @@ public class XtextWebSocket { | |||
159 | } | 183 | } |
160 | return resourceServiceProvider.get(Injector.class); | 184 | return resourceServiceProvider.get(Injector.class); |
161 | } | 185 | } |
186 | |||
187 | protected String getNextStateId(IServiceResult serviceResult) { | ||
188 | if (serviceResult instanceof ContentAssistResult contentAssistResult) { | ||
189 | return contentAssistResult.getStateId(); | ||
190 | } | ||
191 | if (serviceResult instanceof DocumentStateResult documentStateResult) { | ||
192 | return documentStateResult.getStateId(); | ||
193 | } | ||
194 | if (serviceResult instanceof FormattingResult formattingResult) { | ||
195 | return formattingResult.getStateId(); | ||
196 | } | ||
197 | if (serviceResult instanceof HoverResult hoverResult) { | ||
198 | return hoverResult.getStateId(); | ||
199 | } | ||
200 | if (serviceResult instanceof OccurrencesResult occurrencesResult) { | ||
201 | return occurrencesResult.getStateId(); | ||
202 | } | ||
203 | if (serviceResult instanceof ResourceContentResult resourceContentResult) { | ||
204 | return resourceContentResult.getStateId(); | ||
205 | } | ||
206 | return null; | ||
207 | } | ||
162 | } | 208 | } |
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/XtextWebSocketErrorKind.java new file mode 100644 index 00000000..5759f39e --- /dev/null +++ b/language-web/src/main/java/tools/refinery/language/web/xtext/XtextWebSocketErrorKind.java | |||
@@ -0,0 +1,14 @@ | |||
1 | package tools.refinery.language.web.xtext; | ||
2 | |||
3 | import com.google.gson.annotations.SerializedName; | ||
4 | |||
5 | public enum XtextWebSocketErrorKind { | ||
6 | @SerializedName("request") | ||
7 | REQUEST_ERROR, | ||
8 | |||
9 | @SerializedName("server") | ||
10 | SERVER_ERROR, | ||
11 | |||
12 | @SerializedName("transaction") | ||
13 | TRANSACTION_CANCELLED, | ||
14 | } | ||
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/XtextWebSocketErrorResponse.java index 87b300b4..1d2cf08a 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/XtextWebSocketErrorResponse.java | |||
@@ -7,19 +7,60 @@ import com.google.gson.annotations.SerializedName; | |||
7 | public final class XtextWebSocketErrorResponse implements XtextWebSocketResponse { | 7 | public final class XtextWebSocketErrorResponse implements XtextWebSocketResponse { |
8 | private String id; | 8 | private String id; |
9 | 9 | ||
10 | private int index; | ||
11 | |||
10 | @SerializedName("error") | 12 | @SerializedName("error") |
13 | private XtextWebSocketErrorKind errorKind; | ||
14 | |||
15 | @SerializedName("message") | ||
11 | private String errorMessage; | 16 | private String errorMessage; |
12 | 17 | ||
13 | @Override | 18 | public XtextWebSocketErrorResponse(String id, int index, XtextWebSocketErrorKind errorKind, String errorMessage) { |
19 | super(); | ||
20 | this.id = id; | ||
21 | this.index = index; | ||
22 | this.errorKind = errorKind; | ||
23 | this.errorMessage = errorMessage; | ||
24 | } | ||
25 | |||
26 | public XtextWebSocketErrorResponse(XtextWebSocketRequest request, int index, XtextWebSocketErrorKind errorKind, | ||
27 | String errorMessage) { | ||
28 | this(request.getId(), index, errorKind, errorMessage); | ||
29 | } | ||
30 | |||
31 | public XtextWebSocketErrorResponse(XtextWebSocketRequest request, int index, XtextWebSocketErrorKind errorKind) { | ||
32 | this(request, index, errorKind, (String) null); | ||
33 | } | ||
34 | |||
35 | public XtextWebSocketErrorResponse(XtextWebSocketRequest request, int index, XtextWebSocketErrorKind errorKind, | ||
36 | Throwable t) { | ||
37 | this(request, index, errorKind, t.getMessage()); | ||
38 | } | ||
39 | |||
14 | public String getId() { | 40 | public String getId() { |
15 | return id; | 41 | return id; |
16 | } | 42 | } |
17 | 43 | ||
18 | @Override | ||
19 | public void setId(String id) { | 44 | public void setId(String id) { |
20 | this.id = id; | 45 | this.id = id; |
21 | } | 46 | } |
22 | 47 | ||
48 | public int getIndex() { | ||
49 | return index; | ||
50 | } | ||
51 | |||
52 | public void setIndex(int index) { | ||
53 | this.index = index; | ||
54 | } | ||
55 | |||
56 | public XtextWebSocketErrorKind getErrorKind() { | ||
57 | return errorKind; | ||
58 | } | ||
59 | |||
60 | public void setErrorKind(XtextWebSocketErrorKind errorKind) { | ||
61 | this.errorKind = errorKind; | ||
62 | } | ||
63 | |||
23 | public String getErrorMessage() { | 64 | public String getErrorMessage() { |
24 | return errorMessage; | 65 | return errorMessage; |
25 | } | 66 | } |
@@ -30,7 +71,7 @@ public final class XtextWebSocketErrorResponse implements XtextWebSocketResponse | |||
30 | 71 | ||
31 | @Override | 72 | @Override |
32 | public int hashCode() { | 73 | public int hashCode() { |
33 | return Objects.hash(errorMessage, id); | 74 | return Objects.hash(errorKind, errorMessage, id, index); |
34 | } | 75 | } |
35 | 76 | ||
36 | @Override | 77 | @Override |
@@ -42,11 +83,13 @@ public final class XtextWebSocketErrorResponse implements XtextWebSocketResponse | |||
42 | if (getClass() != obj.getClass()) | 83 | if (getClass() != obj.getClass()) |
43 | return false; | 84 | return false; |
44 | XtextWebSocketErrorResponse other = (XtextWebSocketErrorResponse) obj; | 85 | XtextWebSocketErrorResponse other = (XtextWebSocketErrorResponse) obj; |
45 | return Objects.equals(errorMessage, other.errorMessage) && Objects.equals(id, other.id); | 86 | return errorKind == other.errorKind && Objects.equals(errorMessage, other.errorMessage) |
87 | && Objects.equals(id, other.id) && index == other.index; | ||
46 | } | 88 | } |
47 | 89 | ||
48 | @Override | 90 | @Override |
49 | public String toString() { | 91 | public String toString() { |
50 | return "XtextWebSocketError [id=" + id + ", errorMessage=" + errorMessage + "]"; | 92 | return "XtextWebSocketErrorResponse [id=" + id + ", index=" + index + ", errorKind=" + errorKind |
93 | + ", errorMessage=" + errorMessage + "]"; | ||
51 | } | 94 | } |
52 | } | 95 | } |
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/XtextWebSocketOkResponse.java index 4ef1768b..aa453544 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/XtextWebSocketOkResponse.java | |||
@@ -2,24 +2,46 @@ package tools.refinery.language.web.xtext; | |||
2 | 2 | ||
3 | import java.util.Objects; | 3 | import java.util.Objects; |
4 | 4 | ||
5 | import org.eclipse.xtext.web.server.IServiceResult; | ||
6 | import org.eclipse.xtext.web.server.IUnwrappableServiceResult; | ||
7 | |||
5 | import com.google.gson.annotations.SerializedName; | 8 | import com.google.gson.annotations.SerializedName; |
6 | 9 | ||
7 | public final class XtextWebSocketOkResponse implements XtextWebSocketResponse { | 10 | public final class XtextWebSocketOkResponse implements XtextWebSocketResponse { |
8 | private String id; | 11 | private String id; |
9 | 12 | ||
13 | private int index; | ||
14 | |||
10 | @SerializedName("response") | 15 | @SerializedName("response") |
11 | private Object responseData; | 16 | private Object responseData; |
12 | 17 | ||
13 | @Override | 18 | public XtextWebSocketOkResponse(String id, int index, Object responseData) { |
19 | super(); | ||
20 | this.id = id; | ||
21 | this.index = index; | ||
22 | this.responseData = responseData; | ||
23 | } | ||
24 | |||
25 | public XtextWebSocketOkResponse(XtextWebSocketRequest request, int index, IServiceResult result) { | ||
26 | this(request.getId(), index, maybeUnwrap(result)); | ||
27 | } | ||
28 | |||
14 | public String getId() { | 29 | public String getId() { |
15 | return id; | 30 | return id; |
16 | } | 31 | } |
17 | 32 | ||
18 | @Override | ||
19 | public void setId(String id) { | 33 | public void setId(String id) { |
20 | this.id = id; | 34 | this.id = id; |
21 | } | 35 | } |
22 | 36 | ||
37 | public int getIndex() { | ||
38 | return index; | ||
39 | } | ||
40 | |||
41 | public void setIndex(int index) { | ||
42 | this.index = index; | ||
43 | } | ||
44 | |||
23 | public Object getResponseData() { | 45 | public Object getResponseData() { |
24 | return responseData; | 46 | return responseData; |
25 | } | 47 | } |
@@ -30,7 +52,7 @@ public final class XtextWebSocketOkResponse implements XtextWebSocketResponse { | |||
30 | 52 | ||
31 | @Override | 53 | @Override |
32 | public int hashCode() { | 54 | public int hashCode() { |
33 | return Objects.hash(id, responseData); | 55 | return Objects.hash(id, index, responseData); |
34 | } | 56 | } |
35 | 57 | ||
36 | @Override | 58 | @Override |
@@ -42,11 +64,20 @@ public final class XtextWebSocketOkResponse implements XtextWebSocketResponse { | |||
42 | if (getClass() != obj.getClass()) | 64 | if (getClass() != obj.getClass()) |
43 | return false; | 65 | return false; |
44 | XtextWebSocketOkResponse other = (XtextWebSocketOkResponse) obj; | 66 | XtextWebSocketOkResponse other = (XtextWebSocketOkResponse) obj; |
45 | return Objects.equals(id, other.id) && Objects.equals(responseData, other.responseData); | 67 | return Objects.equals(id, other.id) && index == other.index && Objects.equals(responseData, other.responseData); |
46 | } | 68 | } |
47 | 69 | ||
48 | @Override | 70 | @Override |
49 | public String toString() { | 71 | public String toString() { |
50 | return "XtextWebSocketResponse [id=" + id + ", responseData=" + responseData + "]"; | 72 | return "XtextWebSocketOkResponse [id=" + id + ", index=" + index + ", responseData=" + responseData + "]"; |
73 | } | ||
74 | |||
75 | private static Object maybeUnwrap(IServiceResult result) { | ||
76 | if (result instanceof IUnwrappableServiceResult unwrappableServiceResult | ||
77 | && unwrappableServiceResult.getContent() != null) { | ||
78 | return unwrappableServiceResult.getContent(); | ||
79 | } else { | ||
80 | return result; | ||
81 | } | ||
51 | } | 82 | } |
52 | } | 83 | } |
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/XtextWebSocketRequest.java index e34bf73a..8aee70a1 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/XtextWebSocketRequest.java | |||
@@ -1,5 +1,6 @@ | |||
1 | package tools.refinery.language.web.xtext; | 1 | package tools.refinery.language.web.xtext; |
2 | 2 | ||
3 | import java.util.List; | ||
3 | import java.util.Map; | 4 | import java.util.Map; |
4 | import java.util.Objects; | 5 | import java.util.Objects; |
5 | 6 | ||
@@ -8,8 +9,15 @@ import com.google.gson.annotations.SerializedName; | |||
8 | public class XtextWebSocketRequest { | 9 | public class XtextWebSocketRequest { |
9 | private String id; | 10 | private String id; |
10 | 11 | ||
12 | @SerializedName("resource") | ||
13 | private String resourceName; | ||
14 | |||
15 | private String contentType; | ||
16 | |||
17 | private String requiredStateId; | ||
18 | |||
11 | @SerializedName("request") | 19 | @SerializedName("request") |
12 | private Map<String, String> requestData; | 20 | private List<Map<String, String>> requestData; |
13 | 21 | ||
14 | public String getId() { | 22 | public String getId() { |
15 | return id; | 23 | return id; |
@@ -19,17 +27,41 @@ public class XtextWebSocketRequest { | |||
19 | this.id = id; | 27 | this.id = id; |
20 | } | 28 | } |
21 | 29 | ||
22 | public Map<String, String> getRequestData() { | 30 | public String getResourceName() { |
31 | return resourceName; | ||
32 | } | ||
33 | |||
34 | public void setResourceName(String resourceName) { | ||
35 | this.resourceName = resourceName; | ||
36 | } | ||
37 | |||
38 | public String getContentType() { | ||
39 | return contentType; | ||
40 | } | ||
41 | |||
42 | public void setContentType(String contentType) { | ||
43 | this.contentType = contentType; | ||
44 | } | ||
45 | |||
46 | public String getRequiredStateId() { | ||
47 | return requiredStateId; | ||
48 | } | ||
49 | |||
50 | public void setRequiredStateId(String requiredStateId) { | ||
51 | this.requiredStateId = requiredStateId; | ||
52 | } | ||
53 | |||
54 | public List<Map<String, String>> getRequestData() { | ||
23 | return requestData; | 55 | return requestData; |
24 | } | 56 | } |
25 | 57 | ||
26 | public void setRequestData(Map<String, String> request) { | 58 | public void setRequestData(List<Map<String, String>> requestData) { |
27 | this.requestData = request; | 59 | this.requestData = requestData; |
28 | } | 60 | } |
29 | 61 | ||
30 | @Override | 62 | @Override |
31 | public int hashCode() { | 63 | public int hashCode() { |
32 | return Objects.hash(id, requestData); | 64 | return Objects.hash(contentType, id, requestData, requiredStateId, resourceName); |
33 | } | 65 | } |
34 | 66 | ||
35 | @Override | 67 | @Override |
@@ -41,11 +73,15 @@ public class XtextWebSocketRequest { | |||
41 | if (getClass() != obj.getClass()) | 73 | if (getClass() != obj.getClass()) |
42 | return false; | 74 | return false; |
43 | XtextWebSocketRequest other = (XtextWebSocketRequest) obj; | 75 | XtextWebSocketRequest other = (XtextWebSocketRequest) obj; |
44 | return Objects.equals(id, other.id) && Objects.equals(requestData, other.requestData); | 76 | return Objects.equals(contentType, other.contentType) && Objects.equals(id, other.id) |
77 | && Objects.equals(requestData, other.requestData) | ||
78 | && Objects.equals(requiredStateId, other.requiredStateId) | ||
79 | && Objects.equals(resourceName, other.resourceName); | ||
45 | } | 80 | } |
46 | 81 | ||
47 | @Override | 82 | @Override |
48 | public String toString() { | 83 | public String toString() { |
49 | return "XtextWebSocketRequest [id=" + id + ", requestData=" + requestData + "]"; | 84 | return "XtextWebSocketRequest [id=" + id + ", resourceName=" + resourceName + ", contentType=" + contentType |
85 | + ", requiredStateId=" + requiredStateId + ", requestData=" + requestData + "]"; | ||
50 | } | 86 | } |
51 | } | 87 | } |
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/XtextWebSocketResponse.java index df0c228e..9e15aa69 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/XtextWebSocketResponse.java | |||
@@ -1,7 +1,11 @@ | |||
1 | package tools.refinery.language.web.xtext; | 1 | package tools.refinery.language.web.xtext; |
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(); |
5 | 5 | ||
6 | public void setId(String id); | 6 | public void setId(String id); |
7 | |||
8 | public int getIndex(); | ||
9 | |||
10 | public void setIndex(int index); | ||
7 | } | 11 | } |
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/XtextWebSocketServlet.java index 2db11325..5769e9e7 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/XtextWebSocketServlet.java | |||
@@ -24,6 +24,8 @@ public abstract class XtextWebSocketServlet extends JettyWebSocketServlet implem | |||
24 | 24 | ||
25 | public static final String ALLOWED_ORIGINS_INIT_PARAM = "tools.refinery.language.web.xtext.XtextWebSocketServlet.allowedOrigin"; | 25 | public static final String ALLOWED_ORIGINS_INIT_PARAM = "tools.refinery.language.web.xtext.XtextWebSocketServlet.allowedOrigin"; |
26 | 26 | ||
27 | public static final String XTEXT_SUBPROTOCOL_V1 = "tools.refinery.language.web.xtext.v1"; | ||
28 | |||
27 | /** | 29 | /** |
28 | * Maximum message size should be large enough to upload a full model file. | 30 | * Maximum message size should be large enough to upload a full model file. |
29 | */ | 31 | */ |
@@ -68,6 +70,13 @@ public abstract class XtextWebSocketServlet extends JettyWebSocketServlet implem | |||
68 | return null; | 70 | return null; |
69 | } | 71 | } |
70 | } | 72 | } |
73 | if (req.getSubProtocols().contains(XTEXT_SUBPROTOCOL_V1)) { | ||
74 | resp.setAcceptedSubProtocol(XTEXT_SUBPROTOCOL_V1); | ||
75 | } else { | ||
76 | log.error("None of the subprotocols {} offered by {} are supported", req.getSubProtocols(), | ||
77 | req.getRemoteSocketAddress()); | ||
78 | resp.setAcceptedSubProtocol(null); | ||
79 | } | ||
71 | var session = new SimpleSession(); | 80 | var session = new SimpleSession(); |
72 | return new XtextWebSocket(session, IResourceServiceProvider.Registry.INSTANCE); | 81 | return new XtextWebSocket(session, IResourceServiceProvider.Registry.INSTANCE); |
73 | } | 82 | } |