diff options
Diffstat (limited to 'subprojects/language-web/src/main/java/tools')
5 files changed, 79 insertions, 49 deletions
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/ProblemWebModule.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/ProblemWebModule.java index ec55036f..706413a9 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/ProblemWebModule.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/ProblemWebModule.java | |||
@@ -3,12 +3,13 @@ | |||
3 | */ | 3 | */ |
4 | package tools.refinery.language.web; | 4 | package tools.refinery.language.web; |
5 | 5 | ||
6 | import org.eclipse.xtext.ide.ExecutorServiceProvider; | ||
6 | import org.eclipse.xtext.web.server.XtextServiceDispatcher; | 7 | import org.eclipse.xtext.web.server.XtextServiceDispatcher; |
7 | import org.eclipse.xtext.web.server.model.IWebDocumentProvider; | 8 | import org.eclipse.xtext.web.server.model.IWebDocumentProvider; |
8 | import org.eclipse.xtext.web.server.model.XtextWebDocumentAccess; | 9 | import org.eclipse.xtext.web.server.model.XtextWebDocumentAccess; |
9 | import org.eclipse.xtext.web.server.occurrences.OccurrencesService; | 10 | import org.eclipse.xtext.web.server.occurrences.OccurrencesService; |
10 | |||
11 | import tools.refinery.language.web.occurrences.ProblemOccurrencesService; | 11 | import tools.refinery.language.web.occurrences.ProblemOccurrencesService; |
12 | import tools.refinery.language.web.xtext.VirtualThreadExecutorServiceProvider; | ||
12 | import tools.refinery.language.web.xtext.server.push.PushServiceDispatcher; | 13 | import tools.refinery.language.web.xtext.server.push.PushServiceDispatcher; |
13 | import tools.refinery.language.web.xtext.server.push.PushWebDocumentAccess; | 14 | import tools.refinery.language.web.xtext.server.push.PushWebDocumentAccess; |
14 | import tools.refinery.language.web.xtext.server.push.PushWebDocumentProvider; | 15 | import tools.refinery.language.web.xtext.server.push.PushWebDocumentProvider; |
@@ -20,16 +21,20 @@ public class ProblemWebModule extends AbstractProblemWebModule { | |||
20 | public Class<? extends IWebDocumentProvider> bindIWebDocumentProvider() { | 21 | public Class<? extends IWebDocumentProvider> bindIWebDocumentProvider() { |
21 | return PushWebDocumentProvider.class; | 22 | return PushWebDocumentProvider.class; |
22 | } | 23 | } |
23 | 24 | ||
24 | public Class<? extends XtextWebDocumentAccess> bindXtextWebDocumentAccess() { | 25 | public Class<? extends XtextWebDocumentAccess> bindXtextWebDocumentAccess() { |
25 | return PushWebDocumentAccess.class; | 26 | return PushWebDocumentAccess.class; |
26 | } | 27 | } |
27 | 28 | ||
28 | public Class<? extends XtextServiceDispatcher> bindXtextServiceDispatcher() { | 29 | public Class<? extends XtextServiceDispatcher> bindXtextServiceDispatcher() { |
29 | return PushServiceDispatcher.class; | 30 | return PushServiceDispatcher.class; |
30 | } | 31 | } |
31 | 32 | ||
32 | public Class<? extends OccurrencesService> bindOccurrencesService() { | 33 | public Class<? extends OccurrencesService> bindOccurrencesService() { |
33 | return ProblemOccurrencesService.class; | 34 | return ProblemOccurrencesService.class; |
34 | } | 35 | } |
36 | |||
37 | public Class<? extends ExecutorServiceProvider> bindExecutorServiceProvider() { | ||
38 | return VirtualThreadExecutorServiceProvider.class; | ||
39 | } | ||
35 | } | 40 | } |
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 58c8ea4e..5da16850 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 | |||
@@ -5,20 +5,21 @@ package tools.refinery.language.web; | |||
5 | 5 | ||
6 | import jakarta.servlet.DispatcherType; | 6 | import jakarta.servlet.DispatcherType; |
7 | import jakarta.servlet.SessionTrackingMode; | 7 | import jakarta.servlet.SessionTrackingMode; |
8 | import org.eclipse.jetty.ee10.servlet.DefaultServlet; | ||
9 | import org.eclipse.jetty.ee10.servlet.ServletContextHandler; | ||
10 | import org.eclipse.jetty.ee10.servlet.ServletHolder; | ||
11 | import org.eclipse.jetty.ee10.servlet.SessionHandler; | ||
12 | import org.eclipse.jetty.ee10.websocket.server.config.JettyWebSocketServletContainerInitializer; | ||
8 | import org.eclipse.jetty.server.Server; | 13 | import org.eclipse.jetty.server.Server; |
9 | import org.eclipse.jetty.server.session.SessionHandler; | 14 | import org.eclipse.jetty.util.VirtualThreads; |
10 | import org.eclipse.jetty.servlet.DefaultServlet; | ||
11 | import org.eclipse.jetty.servlet.ServletContextHandler; | ||
12 | import org.eclipse.jetty.servlet.ServletHolder; | ||
13 | import org.eclipse.jetty.util.resource.Resource; | 15 | import org.eclipse.jetty.util.resource.Resource; |
14 | import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; | 16 | import org.eclipse.jetty.util.resource.ResourceFactory; |
15 | import org.slf4j.Logger; | 17 | import org.slf4j.Logger; |
16 | import org.slf4j.LoggerFactory; | 18 | import org.slf4j.LoggerFactory; |
17 | import tools.refinery.language.web.config.BackendConfigServlet; | 19 | import tools.refinery.language.web.config.BackendConfigServlet; |
18 | import tools.refinery.language.web.xtext.servlet.XtextWebSocketServlet; | 20 | import tools.refinery.language.web.xtext.servlet.XtextWebSocketServlet; |
19 | 21 | ||
20 | import java.io.File; | 22 | import java.io.File; |
21 | import java.io.IOException; | ||
22 | import java.net.InetSocketAddress; | 23 | import java.net.InetSocketAddress; |
23 | import java.net.URI; | 24 | import java.net.URI; |
24 | import java.net.URISyntaxException; | 25 | import java.net.URISyntaxException; |
@@ -42,13 +43,18 @@ public class ServerLauncher { | |||
42 | 43 | ||
43 | private final Server server; | 44 | private final Server server; |
44 | 45 | ||
45 | public ServerLauncher(InetSocketAddress bindAddress, Resource baseResource, String[] allowedOrigins, | 46 | public ServerLauncher(InetSocketAddress bindAddress, String[] allowedOrigins, String webSocketUrl) { |
46 | String webSocketUrl) { | ||
47 | server = new Server(bindAddress); | 47 | server = new Server(bindAddress); |
48 | if (server.getThreadPool() instanceof VirtualThreads.Configurable virtualThreadsConfigurable) { | ||
49 | // Change this to setVirtualThreadsExecutor once | ||
50 | // https://github.com/eclipse/jetty.project/commit/83154b4ffe4767ef44981598d6c26e6a5d32e57c gets released. | ||
51 | virtualThreadsConfigurable.setUseVirtualThreads(VirtualThreads.areSupported()); | ||
52 | } | ||
48 | var handler = new ServletContextHandler(); | 53 | var handler = new ServletContextHandler(); |
49 | addSessionHandler(handler); | 54 | addSessionHandler(handler); |
50 | addProblemServlet(handler, allowedOrigins); | 55 | addProblemServlet(handler, allowedOrigins); |
51 | addBackendConfigServlet(handler, webSocketUrl); | 56 | addBackendConfigServlet(handler, webSocketUrl); |
57 | var baseResource = getBaseResource(); | ||
52 | if (baseResource != null) { | 58 | if (baseResource != null) { |
53 | handler.setBaseResource(baseResource); | 59 | handler.setBaseResource(baseResource); |
54 | handler.setWelcomeFiles(new String[]{"index.html"}); | 60 | handler.setWelcomeFiles(new String[]{"index.html"}); |
@@ -95,6 +101,35 @@ public class ServerLauncher { | |||
95 | handler.addServlet(defaultServletHolder, "/"); | 101 | handler.addServlet(defaultServletHolder, "/"); |
96 | } | 102 | } |
97 | 103 | ||
104 | private Resource getBaseResource() { | ||
105 | var factory = ResourceFactory.of(server); | ||
106 | var baseResourceOverride = System.getenv("BASE_RESOURCE"); | ||
107 | if (baseResourceOverride != null) { | ||
108 | // If a user override is provided, use it. | ||
109 | return factory.newResource(baseResourceOverride); | ||
110 | } | ||
111 | var indexUrlInJar = ServerLauncher.class.getResource("/webapp/index.html"); | ||
112 | if (indexUrlInJar != null) { | ||
113 | // If the app is packaged in the jar, serve it. | ||
114 | URI webRootUri = null; | ||
115 | try { | ||
116 | webRootUri = URI.create(indexUrlInJar.toURI().toASCIIString().replaceFirst("/index.html$", "/")); | ||
117 | } catch (URISyntaxException e) { | ||
118 | throw new IllegalStateException("Jar has invalid base resource URI", e); | ||
119 | } | ||
120 | return factory.newResource(webRootUri); | ||
121 | } | ||
122 | // Look for unpacked production artifacts (convenience for running from IDE). | ||
123 | var unpackedResourcePathComponents = new String[]{System.getProperty("user.dir"), "build", "webpack", | ||
124 | "production"}; | ||
125 | var unpackedResourceDir = new File(String.join(File.separator, unpackedResourcePathComponents)); | ||
126 | if (unpackedResourceDir.isDirectory()) { | ||
127 | return factory.newResource(unpackedResourceDir.toPath()); | ||
128 | } | ||
129 | // Fall back to just serving a 404. | ||
130 | return null; | ||
131 | } | ||
132 | |||
98 | public void start() throws Exception { | 133 | public void start() throws Exception { |
99 | server.start(); | 134 | server.start(); |
100 | LOG.info("Server started on {}", server.getURI()); | 135 | LOG.info("Server started on {}", server.getURI()); |
@@ -104,10 +139,9 @@ public class ServerLauncher { | |||
104 | public static void main(String[] args) { | 139 | public static void main(String[] args) { |
105 | try { | 140 | try { |
106 | var bindAddress = getBindAddress(); | 141 | var bindAddress = getBindAddress(); |
107 | var baseResource = getBaseResource(); | ||
108 | var allowedOrigins = getAllowedOrigins(); | 142 | var allowedOrigins = getAllowedOrigins(); |
109 | var webSocketUrl = getWebSocketUrl(); | 143 | var webSocketUrl = getWebSocketUrl(); |
110 | var serverLauncher = new ServerLauncher(bindAddress, baseResource, allowedOrigins, webSocketUrl); | 144 | var serverLauncher = new ServerLauncher(bindAddress, allowedOrigins, webSocketUrl); |
111 | serverLauncher.start(); | 145 | serverLauncher.start(); |
112 | } catch (Exception exception) { | 146 | } catch (Exception exception) { |
113 | LOG.error("Fatal server error", exception); | 147 | LOG.error("Fatal server error", exception); |
@@ -137,29 +171,6 @@ public class ServerLauncher { | |||
137 | return new InetSocketAddress(listenAddress, listenPort); | 171 | return new InetSocketAddress(listenAddress, listenPort); |
138 | } | 172 | } |
139 | 173 | ||
140 | private static Resource getBaseResource() throws IOException, URISyntaxException { | ||
141 | var baseResourceOverride = System.getenv("BASE_RESOURCE"); | ||
142 | if (baseResourceOverride != null) { | ||
143 | // If a user override is provided, use it. | ||
144 | return Resource.newResource(baseResourceOverride); | ||
145 | } | ||
146 | var indexUrlInJar = ServerLauncher.class.getResource("/webapp/index.html"); | ||
147 | if (indexUrlInJar != null) { | ||
148 | // If the app is packaged in the jar, serve it. | ||
149 | var webRootUri = URI.create(indexUrlInJar.toURI().toASCIIString().replaceFirst("/index.html$", "/")); | ||
150 | return Resource.newResource(webRootUri); | ||
151 | } | ||
152 | // Look for unpacked production artifacts (convenience for running from IDE). | ||
153 | var unpackedResourcePathComponents = new String[]{System.getProperty("user.dir"), "build", "webpack", | ||
154 | "production"}; | ||
155 | var unpackedResourceDir = new File(String.join(File.separator, unpackedResourcePathComponents)); | ||
156 | if (unpackedResourceDir.isDirectory()) { | ||
157 | return Resource.newResource(unpackedResourceDir); | ||
158 | } | ||
159 | // Fall back to just serving a 404. | ||
160 | return null; | ||
161 | } | ||
162 | |||
163 | private static String getPublicHost() { | 174 | private static String getPublicHost() { |
164 | var publicHost = System.getenv("PUBLIC_HOST"); | 175 | var publicHost = System.getenv("PUBLIC_HOST"); |
165 | if (publicHost != null) { | 176 | if (publicHost != null) { |
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/VirtualThreadExecutorServiceProvider.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/VirtualThreadExecutorServiceProvider.java new file mode 100644 index 00000000..ead98927 --- /dev/null +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/VirtualThreadExecutorServiceProvider.java | |||
@@ -0,0 +1,20 @@ | |||
1 | package tools.refinery.language.web.xtext; | ||
2 | |||
3 | import org.eclipse.xtext.ide.ExecutorServiceProvider; | ||
4 | |||
5 | import java.util.concurrent.ExecutorService; | ||
6 | import java.util.concurrent.Executors; | ||
7 | |||
8 | public class VirtualThreadExecutorServiceProvider extends ExecutorServiceProvider { | ||
9 | private static final String THREAD_POOL_NAME = "xtextWeb"; | ||
10 | |||
11 | @Override | ||
12 | protected ExecutorService createInstance(String key) { | ||
13 | var name = key == null ? THREAD_POOL_NAME : THREAD_POOL_NAME + "-" + key; | ||
14 | return Executors.newThreadPerTaskExecutor(Thread.ofVirtual() | ||
15 | .allowSetThreadLocals(true) | ||
16 | .inheritInheritableThreadLocals(false) | ||
17 | .name(name + "-", 0) | ||
18 | .factory()); | ||
19 | } | ||
20 | } | ||
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 82391d8b..1d9e0463 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 | |||
@@ -3,10 +3,10 @@ package tools.refinery.language.web.xtext.servlet; | |||
3 | import com.google.gson.Gson; | 3 | import com.google.gson.Gson; |
4 | import com.google.gson.JsonIOException; | 4 | import com.google.gson.JsonIOException; |
5 | import com.google.gson.JsonParseException; | 5 | import com.google.gson.JsonParseException; |
6 | import org.eclipse.jetty.websocket.api.Session; | 6 | import org.eclipse.jetty.ee10.websocket.api.Session; |
7 | import org.eclipse.jetty.websocket.api.StatusCode; | 7 | import org.eclipse.jetty.ee10.websocket.api.StatusCode; |
8 | import org.eclipse.jetty.websocket.api.WriteCallback; | 8 | import org.eclipse.jetty.ee10.websocket.api.WriteCallback; |
9 | import org.eclipse.jetty.websocket.api.annotations.*; | 9 | import org.eclipse.jetty.ee10.websocket.api.annotations.*; |
10 | import org.eclipse.xtext.resource.IResourceServiceProvider; | 10 | import org.eclipse.xtext.resource.IResourceServiceProvider; |
11 | import org.eclipse.xtext.web.server.ISession; | 11 | import org.eclipse.xtext.web.server.ISession; |
12 | import org.slf4j.Logger; | 12 | import org.slf4j.Logger; |
@@ -17,7 +17,6 @@ import tools.refinery.language.web.xtext.server.TransactionExecutor; | |||
17 | import tools.refinery.language.web.xtext.server.message.XtextWebRequest; | 17 | import tools.refinery.language.web.xtext.server.message.XtextWebRequest; |
18 | import tools.refinery.language.web.xtext.server.message.XtextWebResponse; | 18 | import tools.refinery.language.web.xtext.server.message.XtextWebResponse; |
19 | 19 | ||
20 | import java.io.IOException; | ||
21 | import java.io.Reader; | 20 | import java.io.Reader; |
22 | 21 | ||
23 | @WebSocket | 22 | @WebSocket |
@@ -108,12 +107,7 @@ public class XtextWebSocket implements WriteCallback, ResponseHandler { | |||
108 | throw new ResponseHandlerException("Trying to send message when websocket is disconnected"); | 107 | throw new ResponseHandlerException("Trying to send message when websocket is disconnected"); |
109 | } | 108 | } |
110 | var responseString = gson.toJson(response); | 109 | var responseString = gson.toJson(response); |
111 | try { | 110 | webSocketSession.getRemote().sendPartialString(responseString, true, this); |
112 | webSocketSession.getRemote().sendPartialString(responseString, true, this); | ||
113 | } catch (IOException e) { | ||
114 | throw new ResponseHandlerException( | ||
115 | "Cannot initiate async write to websocket " + webSocketSession.getRemoteAddress(), e); | ||
116 | } | ||
117 | } | 111 | } |
118 | 112 | ||
119 | @Override | 113 | @Override |
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketServlet.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketServlet.java index a2ad2943..9a32b937 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketServlet.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocketServlet.java | |||
@@ -2,7 +2,7 @@ package tools.refinery.language.web.xtext.servlet; | |||
2 | 2 | ||
3 | import jakarta.servlet.ServletConfig; | 3 | import jakarta.servlet.ServletConfig; |
4 | import jakarta.servlet.ServletException; | 4 | import jakarta.servlet.ServletException; |
5 | import org.eclipse.jetty.websocket.server.*; | 5 | import org.eclipse.jetty.ee10.websocket.server.*; |
6 | import org.eclipse.xtext.resource.IResourceServiceProvider; | 6 | import org.eclipse.xtext.resource.IResourceServiceProvider; |
7 | import org.slf4j.Logger; | 7 | import org.slf4j.Logger; |
8 | import org.slf4j.LoggerFactory; | 8 | import org.slf4j.LoggerFactory; |