aboutsummaryrefslogtreecommitdiffstats
path: root/language-web/src/main/java/tools
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2021-10-05 00:36:47 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2021-10-05 00:36:47 +0200
commitc3e27396c62f191b4343df151e5a86bfa63a32f3 (patch)
tree4f698c9ba0320a5c740c53877c3f75c00240dca4 /language-web/src/main/java/tools
parentfix(web): improve accessibility (diff)
downloadrefinery-c3e27396c62f191b4343df151e5a86bfa63a32f3.tar.gz
refinery-c3e27396c62f191b4343df151e5a86bfa63a32f3.tar.zst
refinery-c3e27396c62f191b4343df151e5a86bfa63a32f3.zip
chore: change package name
Diffstat (limited to 'language-web/src/main/java/tools')
-rw-r--r--language-web/src/main/java/tools/refinery/language/web/CacheControlFilter.java53
-rw-r--r--language-web/src/main/java/tools/refinery/language/web/ProblemServlet.java38
-rw-r--r--language-web/src/main/java/tools/refinery/language/web/ProblemWebModule.java11
-rw-r--r--language-web/src/main/java/tools/refinery/language/web/ProblemWebSetup.java25
-rw-r--r--language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java141
5 files changed, 268 insertions, 0 deletions
diff --git a/language-web/src/main/java/tools/refinery/language/web/CacheControlFilter.java b/language-web/src/main/java/tools/refinery/language/web/CacheControlFilter.java
new file mode 100644
index 00000000..e39ce54c
--- /dev/null
+++ b/language-web/src/main/java/tools/refinery/language/web/CacheControlFilter.java
@@ -0,0 +1,53 @@
1package tools.refinery.language.web;
2
3import java.io.IOException;
4import java.util.regex.Pattern;
5
6import javax.servlet.Filter;
7import javax.servlet.FilterChain;
8import javax.servlet.FilterConfig;
9import javax.servlet.ServletException;
10import javax.servlet.ServletRequest;
11import javax.servlet.ServletResponse;
12import javax.servlet.http.HttpServletRequest;
13import javax.servlet.http.HttpServletResponse;
14
15public class CacheControlFilter implements Filter {
16
17 private static final String CACHE_CONTROL_HEADER = "Cache-Control";
18
19 private static final String EXPIRES_HEADER = "Expires";
20
21 private static final Pattern CACHE_URI_PATTERN = Pattern.compile(".*\\.(css|gif|js|map|png|svg|woff2)");
22
23 private static final long EXPIRY = 31536000;
24
25 private static final String CACHE_CONTROL_CACHE_VALUE = "public, max-age: " + EXPIRY + ", immutable";
26
27 private static final String CACHE_CONTROL_NO_CACHE_VALUE = "no-cache, no-store, max-age: 0, must-revalidate";
28
29 @Override
30 public void init(FilterConfig filterConfig) throws ServletException {
31 // Nothing to initialize.
32 }
33
34 @Override
35 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
36 throws IOException, ServletException {
37 if (request instanceof HttpServletRequest httpRequest && response instanceof HttpServletResponse httpResponse) {
38 if (CACHE_URI_PATTERN.matcher(httpRequest.getRequestURI()).matches()) {
39 httpResponse.setHeader(CACHE_CONTROL_HEADER, CACHE_CONTROL_CACHE_VALUE);
40 httpResponse.setDateHeader(EXPIRES_HEADER, System.currentTimeMillis() + EXPIRY * 1000L);
41 } else {
42 httpResponse.setHeader(CACHE_CONTROL_HEADER, CACHE_CONTROL_NO_CACHE_VALUE);
43 httpResponse.setDateHeader(EXPIRES_HEADER, 0);
44 }
45 }
46 chain.doFilter(request, response);
47 }
48
49 @Override
50 public void destroy() {
51 // Nothing to dispose.
52 }
53}
diff --git a/language-web/src/main/java/tools/refinery/language/web/ProblemServlet.java b/language-web/src/main/java/tools/refinery/language/web/ProblemServlet.java
new file mode 100644
index 00000000..d249525f
--- /dev/null
+++ b/language-web/src/main/java/tools/refinery/language/web/ProblemServlet.java
@@ -0,0 +1,38 @@
1/*
2 * generated by Xtext 2.25.0
3 */
4package tools.refinery.language.web;
5
6import javax.servlet.ServletException;
7
8import org.eclipse.xtext.util.DisposableRegistry;
9import org.eclipse.xtext.web.servlet.XtextServlet;
10
11/**
12 * Deploy this class into a servlet container to enable DSL-specific services.
13 */
14public class ProblemServlet extends XtextServlet {
15
16 private static final long serialVersionUID = 1L;
17
18 // Xtext requires a mutable servlet instance field.
19 @SuppressWarnings("squid:S2226")
20 private DisposableRegistry disposableRegistry;
21
22 @Override
23 public void init() throws ServletException {
24 super.init();
25 var injector = new ProblemWebSetup().createInjectorAndDoEMFRegistration();
26 this.disposableRegistry = injector.getInstance(DisposableRegistry.class);
27 }
28
29 @Override
30 public void destroy() {
31 if (disposableRegistry != null) {
32 disposableRegistry.dispose();
33 disposableRegistry = null;
34 }
35 super.destroy();
36 }
37
38}
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
new file mode 100644
index 00000000..799a9c64
--- /dev/null
+++ b/language-web/src/main/java/tools/refinery/language/web/ProblemWebModule.java
@@ -0,0 +1,11 @@
1/*
2 * generated by Xtext 2.25.0
3 */
4package tools.refinery.language.web;
5
6
7/**
8 * Use this class to register additional components to be used within the web application.
9 */
10public class ProblemWebModule extends AbstractProblemWebModule {
11}
diff --git a/language-web/src/main/java/tools/refinery/language/web/ProblemWebSetup.java b/language-web/src/main/java/tools/refinery/language/web/ProblemWebSetup.java
new file mode 100644
index 00000000..4738bc80
--- /dev/null
+++ b/language-web/src/main/java/tools/refinery/language/web/ProblemWebSetup.java
@@ -0,0 +1,25 @@
1/*
2 * generated by Xtext 2.25.0
3 */
4package tools.refinery.language.web;
5
6import org.eclipse.xtext.util.Modules2;
7
8import com.google.inject.Guice;
9import com.google.inject.Injector;
10
11import tools.refinery.language.ProblemRuntimeModule;
12import tools.refinery.language.ProblemStandaloneSetup;
13import tools.refinery.language.ide.ProblemIdeModule;
14
15/**
16 * Initialization support for running Xtext languages in web applications.
17 */
18public class ProblemWebSetup extends ProblemStandaloneSetup {
19
20 @Override
21 public Injector createInjector() {
22 return Guice.createInjector(Modules2.mixin(new ProblemRuntimeModule(), new ProblemIdeModule(), new ProblemWebModule()));
23 }
24
25}
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
new file mode 100644
index 00000000..c6ee94dc
--- /dev/null
+++ b/language-web/src/main/java/tools/refinery/language/web/ServerLauncher.java
@@ -0,0 +1,141 @@
1/*
2 * generated by Xtext 2.25.0
3 */
4package tools.refinery.language.web;
5
6import java.io.File;
7import java.io.IOException;
8import java.net.InetSocketAddress;
9import java.net.URI;
10import java.net.URISyntaxException;
11import java.util.EnumSet;
12import java.util.Set;
13
14import javax.servlet.DispatcherType;
15import javax.servlet.SessionTrackingMode;
16
17import org.eclipse.jetty.server.Server;
18import org.eclipse.jetty.server.session.SessionHandler;
19import org.eclipse.jetty.servlet.DefaultServlet;
20import org.eclipse.jetty.servlet.ServletContextHandler;
21import org.eclipse.jetty.servlet.ServletHolder;
22import org.eclipse.jetty.util.log.Slf4jLog;
23import org.eclipse.jetty.util.resource.Resource;
24
25public class ServerLauncher {
26 public static final String DEFAULT_LISTEN_ADDRESS = "localhost";
27
28 public static final int DEFAULT_LISTEN_PORT = 1312;
29
30 // Use this cookie name for load balancing.
31 public static final String SESSION_COOKIE_NAME = "JSESSIONID";
32
33 private static final Slf4jLog LOG = new Slf4jLog(ServerLauncher.class.getName());
34
35 private final Server server;
36
37 public ServerLauncher(InetSocketAddress bindAddress, Resource baseResource) {
38 server = new Server(bindAddress);
39 var handler = new ServletContextHandler();
40 addSessionHandler(handler);
41 addProblemServlet(handler);
42 if (baseResource != null) {
43 handler.setBaseResource(baseResource);
44 handler.setWelcomeFiles(new String[] { "index.html" });
45 addDefaultServlet(handler);
46 }
47 handler.addFilter(CacheControlFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
48 server.setHandler(handler);
49 }
50
51 private void addSessionHandler(ServletContextHandler handler) {
52 var sessionHandler = new SessionHandler();
53 sessionHandler.setSessionTrackingModes(Set.of(SessionTrackingMode.COOKIE));
54 sessionHandler.setSessionCookie(SESSION_COOKIE_NAME);
55 handler.setSessionHandler(sessionHandler);
56 }
57
58 private void addProblemServlet(ServletContextHandler handler) {
59 handler.addServlet(ProblemServlet.class, "/xtext-service/*");
60 }
61
62 private void addDefaultServlet(ServletContextHandler handler) {
63 var defaultServletHolder = new ServletHolder(DefaultServlet.class);
64 var isWindows = System.getProperty("os.name").toLowerCase().contains("win");
65 // Avoid file locking on Windows: https://stackoverflow.com/a/4985717
66 // See also the related Jetty ticket:
67 // https://github.com/eclipse/jetty.project/issues/2925
68 defaultServletHolder.setInitParameter("useFileMappedBuffer", isWindows ? "false" : "true");
69 handler.addServlet(defaultServletHolder, "/");
70 }
71
72 public void start() throws Exception {
73 server.start();
74 LOG.info("Server started " + server.getURI() + "...");
75 LOG.info("Press enter to stop the server...");
76 int key = System.in.read();
77 if (key != -1) {
78 server.stop();
79 } else {
80 LOG.warn("Console input is not available. "
81 + "In order to stop the server, you need to cancel process manually.");
82 }
83 }
84
85 public static void main(String[] args) {
86 try {
87 var bindAddress = getBindAddress();
88 var baseResource = getBaseResource();
89 var serverLauncher = new ServerLauncher(bindAddress, baseResource);
90 serverLauncher.start();
91 } catch (Exception exception) {
92 LOG.warn(exception);
93 System.exit(1);
94 }
95 }
96
97 private static String getListenAddress() {
98 var listenAddress = System.getenv("LISTEN_ADDRESS");
99 if (listenAddress == null) {
100 return DEFAULT_LISTEN_ADDRESS;
101 }
102 return listenAddress;
103 }
104
105 private static int getListenPort() {
106 var portStr = System.getenv("LISTEN_PORT");
107 if (portStr != null) {
108 return Integer.parseInt(portStr);
109 }
110 return DEFAULT_LISTEN_PORT;
111 }
112
113 private static InetSocketAddress getBindAddress() {
114 var listenAddress = getListenAddress();
115 var listenPort = getListenPort();
116 return new InetSocketAddress(listenAddress, listenPort);
117 }
118
119 private static Resource getBaseResource() throws IOException, URISyntaxException {
120 var baseResourceOverride = System.getenv("BASE_RESOURCE");
121 if (baseResourceOverride != null) {
122 // If a user override is provided, use it.
123 return Resource.newResource(baseResourceOverride);
124 }
125 var indexUrlInJar = ServerLauncher.class.getResource("/webapp/index.html");
126 if (indexUrlInJar != null) {
127 // If the app is packaged in the jar, serve it.
128 var webRootUri = URI.create(indexUrlInJar.toURI().toASCIIString().replaceFirst("/index.html$", "/"));
129 return Resource.newResource(webRootUri);
130 }
131 // Look for unpacked production artifacts (convenience for running from IDE).
132 var unpackedResourcePathComponents = new String[] { System.getProperty("user.dir"), "build", "webpack",
133 "production" };
134 var unpackedResourceDir = new File(String.join(File.separator, unpackedResourcePathComponents));
135 if (unpackedResourceDir.isDirectory()) {
136 return Resource.newResource(unpackedResourceDir);
137 }
138 // Fall back to just serving a 404.
139 return null;
140 }
141}