]> git.argeo.org Git - lgpl/argeo-commons.git/blob - servlet/CmsRcpServletFactory.java
Prepare next development cycle
[lgpl/argeo-commons.git] / servlet / CmsRcpServletFactory.java
1 package org.argeo.cms.ui.rcp.servlet;
2
3 import static java.lang.System.Logger.Level.DEBUG;
4
5 import java.io.IOException;
6 import java.lang.System.Logger;
7 import java.lang.System.Logger.Level;
8 import java.net.DatagramSocket;
9 import java.net.ServerSocket;
10 import java.net.URI;
11 import java.nio.charset.StandardCharsets;
12 import java.nio.file.Files;
13 import java.nio.file.Path;
14 import java.util.Map;
15 import java.util.concurrent.CompletableFuture;
16
17 import org.argeo.api.cms.CmsApp;
18 import org.argeo.cms.ui.rcp.CmsRcpDisplayFactory;
19
20 import com.sun.net.httpserver.HttpExchange;
21 import com.sun.net.httpserver.HttpHandler;
22 import com.sun.net.httpserver.HttpServer;
23
24 /** Publishes one {@link CmsRcpServlet} per {@link CmsApp}. */
25 public class CmsRcpServletFactory {
26 private final static Logger logger = System.getLogger(CmsRcpServletFactory.class.getName());
27 private CompletableFuture<HttpServer> httpServer =new CompletableFuture<>();
28
29 public void init() {
30
31 }
32
33 public void destroy() {
34 Path runFile = CmsRcpDisplayFactory.getUrlRunFile();
35 try {
36 if (Files.exists(runFile)) {
37 Files.delete(runFile);
38 }
39 } catch (IOException e) {
40 logger.log(Level.ERROR, "Cannot delete " + runFile, e);
41 }
42 }
43
44 public void addCmsApp(CmsApp cmsApp, Map<String, String> properties) {
45 final String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY);
46 if (contextName != null) {
47 httpServer.thenAcceptAsync((httpServer) -> {
48 httpServer.createContext("/" + contextName, new HttpHandler() {
49
50 @Override
51 public void handle(HttpExchange exchange) throws IOException {
52 String path = exchange.getRequestURI().getPath();
53 String uiName = path != null ? path.substring(path.lastIndexOf('/') + 1) : "";
54 CmsRcpDisplayFactory.openCmsApp(cmsApp, uiName, null);
55 exchange.sendResponseHeaders(200, -1);
56 logger.log(Level.DEBUG, "Opened RCP UI " + uiName + " of CMS App /" + contextName);
57 }
58 });
59 });
60 logger.log(Level.DEBUG, "Registered RCP CMS APP /" + contextName);
61 }
62 }
63
64 public void removeCmsApp(CmsApp cmsApp, Map<String, String> properties) {
65 String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY);
66 if (contextName != null) {
67 httpServer.thenAcceptAsync((httpServer) -> {
68 httpServer.removeContext("/" + contextName);
69 });
70 }
71 }
72
73 public void setHttpServer(HttpServer httpServer) {
74 Integer httpPort = httpServer.getAddress().getPort();
75 String baseUrl = "http://localhost:" + httpPort + "/";
76 Path runFile = CmsRcpDisplayFactory.getUrlRunFile();
77 try {
78 if (!Files.exists(runFile)) {
79 Files.createDirectories(runFile.getParent());
80 // TODO give read permission only to the owner
81 Files.createFile(runFile);
82 } else {
83 URI uri = URI.create(Files.readString(runFile));
84 if (!httpPort.equals(uri.getPort()))
85 if (!isPortAvailable(uri.getPort())) {
86 throw new IllegalStateException("Another CMS is running on " + runFile);
87 } else {
88 logger.log(Level.WARNING,
89 "Run file " + runFile + " found but port of " + uri + " is available. Overwriting...");
90 }
91 }
92 Files.writeString(runFile, baseUrl, StandardCharsets.UTF_8);
93 } catch (IOException e) {
94 throw new RuntimeException("Cannot write run file to " + runFile, e);
95 }
96 logger.log(DEBUG, "RCP available under " + baseUrl + ", written to " + runFile);
97 this.httpServer.complete(httpServer);
98 }
99
100 protected boolean isPortAvailable(int port) {
101 ServerSocket ss = null;
102 DatagramSocket ds = null;
103 try {
104 ss = new ServerSocket(port);
105 ss.setReuseAddress(true);
106 ds = new DatagramSocket(port);
107 ds.setReuseAddress(true);
108 return true;
109 } catch (IOException e) {
110 } finally {
111 if (ds != null) {
112 ds.close();
113 }
114
115 if (ss != null) {
116 try {
117 ss.close();
118 } catch (IOException e) {
119 /* should not be thrown */
120 }
121 }
122 }
123
124 return false;
125 }
126 }