diff options
Diffstat (limited to 'subprojects/language-web/src')
7 files changed, 114 insertions, 43 deletions
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/CacheControlFilter.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/CacheControlFilter.java index fbce62c1..fd2af1b2 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/CacheControlFilter.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/CacheControlFilter.java | |||
@@ -13,8 +13,9 @@ import java.util.regex.Pattern; | |||
13 | public class CacheControlFilter implements Filter { | 13 | public class CacheControlFilter implements Filter { |
14 | private static final Pattern CACHE_URI_PATTERN = Pattern.compile(".*\\.(css|gif|js|map|png|svg|woff2?)"); | 14 | private static final Pattern CACHE_URI_PATTERN = Pattern.compile(".*\\.(css|gif|js|map|png|svg|woff2?)"); |
15 | 15 | ||
16 | private static final Set<String> CACHE_URI_DENYLIST = Set.of("apple-touch-icon.png", "favicon.png", "favicon.svg", | 16 | private static final Set<String> CACHE_URI_DENYLIST = Set.of("apple-touch-icon.png", "config.json", "favicon.png", |
17 | "favicon-96x96.png", "icon-any.svg", "icon-192x192.png", "icon-512x512.png", "mask-icon.svg", "sw.js"); | 17 | "favicon.svg", "favicon-96x96.png", "icon-any.svg", "icon-192x192.png", "icon-512x512.png", "mask-icon.svg", |
18 | "sw.js"); | ||
18 | 19 | ||
19 | private static final Duration EXPIRY = Duration.ofDays(365); | 20 | private static final Duration EXPIRY = Duration.ofDays(365); |
20 | 21 | ||
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java index ffa61321..58c8ea4e 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java | |||
@@ -14,6 +14,7 @@ import org.eclipse.jetty.util.resource.Resource; | |||
14 | import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; | 14 | import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; |
15 | import org.slf4j.Logger; | 15 | import org.slf4j.Logger; |
16 | import org.slf4j.LoggerFactory; | 16 | import org.slf4j.LoggerFactory; |
17 | import tools.refinery.language.web.config.BackendConfigServlet; | ||
17 | import tools.refinery.language.web.xtext.servlet.XtextWebSocketServlet; | 18 | import tools.refinery.language.web.xtext.servlet.XtextWebSocketServlet; |
18 | 19 | ||
19 | import java.io.File; | 20 | import java.io.File; |
@@ -41,11 +42,13 @@ public class ServerLauncher { | |||
41 | 42 | ||
42 | private final Server server; | 43 | private final Server server; |
43 | 44 | ||
44 | public ServerLauncher(InetSocketAddress bindAddress, Resource baseResource, String[] allowedOrigins) { | 45 | public ServerLauncher(InetSocketAddress bindAddress, Resource baseResource, String[] allowedOrigins, |
46 | String webSocketUrl) { | ||
45 | server = new Server(bindAddress); | 47 | server = new Server(bindAddress); |
46 | var handler = new ServletContextHandler(); | 48 | var handler = new ServletContextHandler(); |
47 | addSessionHandler(handler); | 49 | addSessionHandler(handler); |
48 | addProblemServlet(handler, allowedOrigins); | 50 | addProblemServlet(handler, allowedOrigins); |
51 | addBackendConfigServlet(handler, webSocketUrl); | ||
49 | if (baseResource != null) { | 52 | if (baseResource != null) { |
50 | handler.setBaseResource(baseResource); | 53 | handler.setBaseResource(baseResource); |
51 | handler.setWelcomeFiles(new String[]{"index.html"}); | 54 | handler.setWelcomeFiles(new String[]{"index.html"}); |
@@ -76,6 +79,12 @@ public class ServerLauncher { | |||
76 | JettyWebSocketServletContainerInitializer.configure(handler, null); | 79 | JettyWebSocketServletContainerInitializer.configure(handler, null); |
77 | } | 80 | } |
78 | 81 | ||
82 | private void addBackendConfigServlet(ServletContextHandler handler, String webSocketUrl) { | ||
83 | var backendConfigServletHolder = new ServletHolder(BackendConfigServlet.class); | ||
84 | backendConfigServletHolder.setInitParameter(BackendConfigServlet.WEBSOCKET_URL_INIT_PARAM, webSocketUrl); | ||
85 | handler.addServlet(backendConfigServletHolder, "/config.json"); | ||
86 | } | ||
87 | |||
79 | private void addDefaultServlet(ServletContextHandler handler) { | 88 | private void addDefaultServlet(ServletContextHandler handler) { |
80 | var defaultServletHolder = new ServletHolder(DefaultServlet.class); | 89 | var defaultServletHolder = new ServletHolder(DefaultServlet.class); |
81 | var isWindows = System.getProperty("os.name").toLowerCase().contains("win"); | 90 | var isWindows = System.getProperty("os.name").toLowerCase().contains("win"); |
@@ -97,7 +106,8 @@ public class ServerLauncher { | |||
97 | var bindAddress = getBindAddress(); | 106 | var bindAddress = getBindAddress(); |
98 | var baseResource = getBaseResource(); | 107 | var baseResource = getBaseResource(); |
99 | var allowedOrigins = getAllowedOrigins(); | 108 | var allowedOrigins = getAllowedOrigins(); |
100 | var serverLauncher = new ServerLauncher(bindAddress, baseResource, allowedOrigins); | 109 | var webSocketUrl = getWebSocketUrl(); |
110 | var serverLauncher = new ServerLauncher(bindAddress, baseResource, allowedOrigins, webSocketUrl); | ||
101 | serverLauncher.start(); | 111 | serverLauncher.start(); |
102 | } catch (Exception exception) { | 112 | } catch (Exception exception) { |
103 | LOG.error("Fatal server error", exception); | 113 | LOG.error("Fatal server error", exception); |
@@ -190,4 +200,19 @@ public class ServerLauncher { | |||
190 | } | 200 | } |
191 | return new String[]{urlWithPort}; | 201 | return new String[]{urlWithPort}; |
192 | } | 202 | } |
203 | |||
204 | private static String getWebSocketUrl() { | ||
205 | String host; | ||
206 | int port; | ||
207 | var publicHost = getPublicHost(); | ||
208 | if (publicHost == null) { | ||
209 | host = getListenAddress(); | ||
210 | port = getListenPort(); | ||
211 | } else { | ||
212 | host = publicHost; | ||
213 | port = getPublicPort(); | ||
214 | } | ||
215 | var scheme = port == HTTPS_DEFAULT_PORT ? "wss" : "ws"; | ||
216 | return String.format("%s://%s:%d/xtext-service", scheme, host, port); | ||
217 | } | ||
193 | } | 218 | } |
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/config/BackendConfig.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/config/BackendConfig.java new file mode 100644 index 00000000..2e864998 --- /dev/null +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/config/BackendConfig.java | |||
@@ -0,0 +1,20 @@ | |||
1 | package tools.refinery.language.web.config; | ||
2 | |||
3 | import com.google.gson.annotations.SerializedName; | ||
4 | |||
5 | public class BackendConfig { | ||
6 | @SerializedName("webSocketURL") | ||
7 | private String webSocketUrl; | ||
8 | |||
9 | public BackendConfig(String webSocketUrl) { | ||
10 | this.webSocketUrl = webSocketUrl; | ||
11 | } | ||
12 | |||
13 | public String getWebSocketUrl() { | ||
14 | return webSocketUrl; | ||
15 | } | ||
16 | |||
17 | public void setWebSocketUrl(String webSocketUrl) { | ||
18 | this.webSocketUrl = webSocketUrl; | ||
19 | } | ||
20 | } | ||
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/config/BackendConfigServlet.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/config/BackendConfigServlet.java new file mode 100644 index 00000000..f314a9fa --- /dev/null +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/config/BackendConfigServlet.java | |||
@@ -0,0 +1,39 @@ | |||
1 | package tools.refinery.language.web.config; | ||
2 | |||
3 | import com.google.gson.Gson; | ||
4 | import jakarta.servlet.ServletConfig; | ||
5 | import jakarta.servlet.ServletException; | ||
6 | import jakarta.servlet.http.HttpServlet; | ||
7 | import jakarta.servlet.http.HttpServletRequest; | ||
8 | import jakarta.servlet.http.HttpServletResponse; | ||
9 | import org.eclipse.jetty.http.HttpStatus; | ||
10 | |||
11 | import java.io.IOException; | ||
12 | |||
13 | public class BackendConfigServlet extends HttpServlet { | ||
14 | public static final String WEBSOCKET_URL_INIT_PARAM = "tools.refinery.language.web.config.BackendConfigServlet" + | ||
15 | ".webSocketUrl"; | ||
16 | |||
17 | private String serializedConfig; | ||
18 | |||
19 | @Override | ||
20 | public void init(ServletConfig config) throws ServletException { | ||
21 | super.init(config); | ||
22 | var webSocketUrl = config.getInitParameter(WEBSOCKET_URL_INIT_PARAM); | ||
23 | if (webSocketUrl == null) { | ||
24 | throw new IllegalArgumentException("Init parameter " + WEBSOCKET_URL_INIT_PARAM + " is mandatory"); | ||
25 | } | ||
26 | var backendConfig = new BackendConfig(webSocketUrl); | ||
27 | var gson = new Gson(); | ||
28 | serializedConfig = gson.toJson(backendConfig); | ||
29 | } | ||
30 | |||
31 | @Override | ||
32 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { | ||
33 | resp.setStatus(HttpStatus.OK_200); | ||
34 | resp.setContentType("application/json"); | ||
35 | var writer = resp.getWriter(); | ||
36 | writer.write(serializedConfig); | ||
37 | writer.flush(); | ||
38 | } | ||
39 | } | ||
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/ResponseHandlerException.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/ResponseHandlerException.java index 34fcb546..b686d33a 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/ResponseHandlerException.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/ResponseHandlerException.java | |||
@@ -1,7 +1,10 @@ | |||
1 | package tools.refinery.language.web.xtext.server; | 1 | package tools.refinery.language.web.xtext.server; |
2 | 2 | ||
3 | import java.io.Serial; | ||
4 | |||
3 | public class ResponseHandlerException extends Exception { | 5 | public class ResponseHandlerException extends Exception { |
4 | 6 | ||
7 | @Serial | ||
5 | private static final long serialVersionUID = 3589866922420268164L; | 8 | private static final long serialVersionUID = 3589866922420268164L; |
6 | 9 | ||
7 | public ResponseHandlerException(String message, Throwable cause) { | 10 | public ResponseHandlerException(String message, Throwable cause) { |
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java index 0b417b06..7bb11d2e 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java | |||
@@ -1,35 +1,25 @@ | |||
1 | package tools.refinery.language.web.xtext.server; | 1 | package tools.refinery.language.web.xtext.server; |
2 | 2 | ||
3 | import java.lang.ref.WeakReference; | 3 | import com.google.common.base.Strings; |
4 | import java.util.ArrayList; | 4 | import com.google.inject.Injector; |
5 | import java.util.HashMap; | ||
6 | import java.util.List; | ||
7 | import java.util.Map; | ||
8 | |||
9 | import org.eclipse.emf.common.util.URI; | 5 | import org.eclipse.emf.common.util.URI; |
10 | import org.eclipse.xtext.resource.IResourceServiceProvider; | 6 | import org.eclipse.xtext.resource.IResourceServiceProvider; |
11 | import org.eclipse.xtext.util.IDisposable; | 7 | import org.eclipse.xtext.util.IDisposable; |
12 | import org.eclipse.xtext.web.server.IServiceContext; | 8 | import org.eclipse.xtext.web.server.*; |
13 | import org.eclipse.xtext.web.server.IServiceResult; | ||
14 | import org.eclipse.xtext.web.server.ISession; | ||
15 | import org.eclipse.xtext.web.server.InvalidRequestException; | ||
16 | import org.eclipse.xtext.web.server.InvalidRequestException.UnknownLanguageException; | 9 | import org.eclipse.xtext.web.server.InvalidRequestException.UnknownLanguageException; |
17 | import org.eclipse.xtext.web.server.XtextServiceDispatcher; | ||
18 | import org.slf4j.Logger; | 10 | import org.slf4j.Logger; |
19 | import org.slf4j.LoggerFactory; | 11 | import org.slf4j.LoggerFactory; |
20 | 12 | import tools.refinery.language.web.xtext.server.message.*; | |
21 | import com.google.common.base.Strings; | ||
22 | import com.google.inject.Injector; | ||
23 | |||
24 | import tools.refinery.language.web.xtext.server.message.XtextWebErrorKind; | ||
25 | import tools.refinery.language.web.xtext.server.message.XtextWebErrorResponse; | ||
26 | import tools.refinery.language.web.xtext.server.message.XtextWebOkResponse; | ||
27 | import tools.refinery.language.web.xtext.server.message.XtextWebPushMessage; | ||
28 | import tools.refinery.language.web.xtext.server.message.XtextWebRequest; | ||
29 | import tools.refinery.language.web.xtext.server.push.PrecomputationListener; | 13 | import tools.refinery.language.web.xtext.server.push.PrecomputationListener; |
30 | import tools.refinery.language.web.xtext.server.push.PushWebDocument; | 14 | import tools.refinery.language.web.xtext.server.push.PushWebDocument; |
31 | import tools.refinery.language.web.xtext.servlet.SimpleServiceContext; | 15 | import tools.refinery.language.web.xtext.servlet.SimpleServiceContext; |
32 | 16 | ||
17 | import java.lang.ref.WeakReference; | ||
18 | import java.util.ArrayList; | ||
19 | import java.util.HashMap; | ||
20 | import java.util.List; | ||
21 | import java.util.Map; | ||
22 | |||
33 | public class TransactionExecutor implements IDisposable, PrecomputationListener { | 23 | public class TransactionExecutor implements IDisposable, PrecomputationListener { |
34 | private static final Logger LOG = LoggerFactory.getLogger(TransactionExecutor.class); | 24 | private static final Logger LOG = LoggerFactory.getLogger(TransactionExecutor.class); |
35 | 25 | ||
@@ -41,11 +31,11 @@ public class TransactionExecutor implements IDisposable, PrecomputationListener | |||
41 | 31 | ||
42 | private ResponseHandler responseHandler; | 32 | private ResponseHandler responseHandler; |
43 | 33 | ||
44 | private Object callPendingLock = new Object(); | 34 | private final Object callPendingLock = new Object(); |
45 | 35 | ||
46 | private boolean callPending; | 36 | private boolean callPending; |
47 | 37 | ||
48 | private List<XtextWebPushMessage> pendingPushMessages = new ArrayList<>(); | 38 | private final List<XtextWebPushMessage> pendingPushMessages = new ArrayList<>(); |
49 | 39 | ||
50 | public TransactionExecutor(ISession session, IResourceServiceProvider.Registry resourceServiceProviderRegistry) { | 40 | public TransactionExecutor(ISession session, IResourceServiceProvider.Registry resourceServiceProviderRegistry) { |
51 | this.session = session; | 41 | this.session = session; |
@@ -132,10 +122,9 @@ public class TransactionExecutor implements IDisposable, PrecomputationListener | |||
132 | 122 | ||
133 | /** | 123 | /** |
134 | * Get the injector to satisfy the request in the {@code serviceContext}. | 124 | * Get the injector to satisfy the request in the {@code serviceContext}. |
135 | * | ||
136 | * Based on {@link org.eclipse.xtext.web.servlet.XtextServlet#getInjector}. | 125 | * Based on {@link org.eclipse.xtext.web.servlet.XtextServlet#getInjector}. |
137 | * | 126 | * |
138 | * @param serviceContext the Xtext service context of the request | 127 | * @param context the Xtext service context of the request |
139 | * @return the injector for the Xtext language in the request | 128 | * @return the injector for the Xtext language in the request |
140 | * @throws UnknownLanguageException if the Xtext language cannot be determined | 129 | * @throws UnknownLanguageException if the Xtext language cannot be determined |
141 | */ | 130 | */ |
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java index fd41f213..82391d8b 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java | |||
@@ -1,31 +1,25 @@ | |||
1 | package tools.refinery.language.web.xtext.servlet; | 1 | package tools.refinery.language.web.xtext.servlet; |
2 | 2 | ||
3 | import java.io.IOException; | 3 | import com.google.gson.Gson; |
4 | import java.io.Reader; | 4 | import com.google.gson.JsonIOException; |
5 | 5 | import com.google.gson.JsonParseException; | |
6 | import org.eclipse.jetty.websocket.api.Session; | 6 | import org.eclipse.jetty.websocket.api.Session; |
7 | import org.eclipse.jetty.websocket.api.StatusCode; | 7 | import org.eclipse.jetty.websocket.api.StatusCode; |
8 | import org.eclipse.jetty.websocket.api.WriteCallback; | 8 | import org.eclipse.jetty.websocket.api.WriteCallback; |
9 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; | 9 | import org.eclipse.jetty.websocket.api.annotations.*; |
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; | 10 | import org.eclipse.xtext.resource.IResourceServiceProvider; |
15 | import org.eclipse.xtext.web.server.ISession; | 11 | import org.eclipse.xtext.web.server.ISession; |
16 | import org.slf4j.Logger; | 12 | import org.slf4j.Logger; |
17 | import org.slf4j.LoggerFactory; | 13 | 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 | import tools.refinery.language.web.xtext.server.ResponseHandler; | 14 | import tools.refinery.language.web.xtext.server.ResponseHandler; |
24 | import tools.refinery.language.web.xtext.server.ResponseHandlerException; | 15 | import tools.refinery.language.web.xtext.server.ResponseHandlerException; |
25 | import tools.refinery.language.web.xtext.server.TransactionExecutor; | 16 | import tools.refinery.language.web.xtext.server.TransactionExecutor; |
26 | import tools.refinery.language.web.xtext.server.message.XtextWebRequest; | 17 | import tools.refinery.language.web.xtext.server.message.XtextWebRequest; |
27 | import tools.refinery.language.web.xtext.server.message.XtextWebResponse; | 18 | import tools.refinery.language.web.xtext.server.message.XtextWebResponse; |
28 | 19 | ||
20 | import java.io.IOException; | ||
21 | import java.io.Reader; | ||
22 | |||
29 | @WebSocket | 23 | @WebSocket |
30 | public class XtextWebSocket implements WriteCallback, ResponseHandler { | 24 | public class XtextWebSocket implements WriteCallback, ResponseHandler { |
31 | private static final Logger LOG = LoggerFactory.getLogger(XtextWebSocket.class); | 25 | private static final Logger LOG = LoggerFactory.getLogger(XtextWebSocket.class); |
@@ -118,7 +112,7 @@ public class XtextWebSocket implements WriteCallback, ResponseHandler { | |||
118 | webSocketSession.getRemote().sendPartialString(responseString, true, this); | 112 | webSocketSession.getRemote().sendPartialString(responseString, true, this); |
119 | } catch (IOException e) { | 113 | } catch (IOException e) { |
120 | throw new ResponseHandlerException( | 114 | throw new ResponseHandlerException( |
121 | "Cannot initiaite async write to websocket " + webSocketSession.getRemoteAddress(), e); | 115 | "Cannot initiate async write to websocket " + webSocketSession.getRemoteAddress(), e); |
122 | } | 116 | } |
123 | } | 117 | } |
124 | 118 | ||