From: Mathieu Baudier Date: Thu, 3 Nov 2022 08:05:34 +0000 (+0100) Subject: Simplify CMS RCP X-Git-Tag: v2.3.11~38 X-Git-Url: https://git.argeo.org/?p=lgpl%2Fargeo-commons.git;a=commitdiff_plain;h=ed37119631fa2b14b38d374914f94e8073946042 Simplify CMS RCP --- diff --git a/swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpDisplayFactory.xml b/swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpDisplayFactory.xml index a0c0f0f5c..8b1d14684 100644 --- a/swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpDisplayFactory.xml +++ b/swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpDisplayFactory.xml @@ -1,4 +1,4 @@ - + diff --git a/swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpHttpLauncher.xml b/swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpHttpLauncher.xml new file mode 100644 index 000000000..03abe196c --- /dev/null +++ b/swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpHttpLauncher.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpServletFactory.xml b/swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpServletFactory.xml deleted file mode 100644 index 5f6b94e04..000000000 --- a/swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpServletFactory.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/swt/rcp/org.argeo.cms.swt.rcp/bnd.bnd b/swt/rcp/org.argeo.cms.swt.rcp/bnd.bnd index 91f0a8a37..5cf5164e1 100644 --- a/swt/rcp/org.argeo.cms.swt.rcp/bnd.bnd +++ b/swt/rcp/org.argeo.cms.swt.rcp/bnd.bnd @@ -1,3 +1,4 @@ +Bundle-SymbolicName: org.argeo.cms.swt.rcp;singleton=true Import-Package:\ org.argeo.cms.auth,\ @@ -8,5 +9,5 @@ org.w3c.css.sac,\ Service-Component:\ OSGI-INF/cmsRcpDisplayFactory.xml,\ -OSGI-INF/cmsRcpServletFactory.xml +OSGI-INF/cmsRcpHttpLauncher.xml diff --git a/swt/rcp/org.argeo.cms.swt.rcp/build.properties b/swt/rcp/org.argeo.cms.swt.rcp/build.properties index 5eef7058e..4ed1d4748 100644 --- a/swt/rcp/org.argeo.cms.swt.rcp/build.properties +++ b/swt/rcp/org.argeo.cms.swt.rcp/build.properties @@ -2,5 +2,5 @@ output.. = bin/ bin.includes = META-INF/,\ .,\ OSGI-INF/,\ - OSGI-INF/cmsRcpServletFactory.xml + OSGI-INF/cmsRcpHttpLauncher.xml source.. = src/ diff --git a/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/CmsRcpDisplayFactory.java b/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/CmsRcpDisplayFactory.java index 950accece..e94825db8 100644 --- a/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/CmsRcpDisplayFactory.java +++ b/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/CmsRcpDisplayFactory.java @@ -1,5 +1,7 @@ package org.argeo.cms.ui.rcp; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; import java.nio.file.Path; import org.argeo.api.cms.CmsApp; @@ -9,6 +11,8 @@ import org.eclipse.swt.widgets.Display; /** Creates the SWT {@link Display} in a dedicated thread. */ public class CmsRcpDisplayFactory { + private final static Logger logger = System.getLogger(CmsRcpDisplayFactory.class.getName()); + /** File name in a run directory */ private final static String ARGEO_RCP_URL = "argeo.rcp.url"; @@ -50,20 +54,22 @@ public class CmsRcpDisplayFactory { @Override public void run() { - display = Display.getDefault(); - display.setRuntimeExceptionHandler((e) -> e.printStackTrace()); - display.setErrorHandler((e) -> e.printStackTrace()); - -// for (String contextName : cmsApps.keySet()) { -// openCmsApp(contextName); -// } - - while (!shutdown) { - if (!display.readAndDispatch()) - display.sleep(); + try { + display = Display.getDefault(); + display.setRuntimeExceptionHandler((e) -> e.printStackTrace()); + display.setErrorHandler((e) -> e.printStackTrace()); + + while (!shutdown) { + if (!display.readAndDispatch()) + display.sleep(); + } + display.dispose(); + display = null; + } catch (UnsatisfiedLinkError e) { + logger.log(Level.ERROR, + "Cannot load SWT, probably because the OSGi framework has been refresh. Restart the application.", + e); } - display.dispose(); - display = null; } } diff --git a/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/CmsRcpHttpLauncher.java b/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/CmsRcpHttpLauncher.java new file mode 100644 index 000000000..6246b0d0d --- /dev/null +++ b/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/CmsRcpHttpLauncher.java @@ -0,0 +1,128 @@ +package org.argeo.cms.ui.rcp; + +import static java.lang.System.Logger.Level.DEBUG; + +import java.io.IOException; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.net.DatagramSocket; +import java.net.ServerSocket; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.argeo.api.cms.CmsApp; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; + +/** Publishes one {@link CmsRcpServlet} per {@link CmsApp}. */ +public class CmsRcpHttpLauncher { + private final static Logger logger = System.getLogger(CmsRcpHttpLauncher.class.getName()); + private CompletableFuture httpServer = new CompletableFuture<>(); + + public void init() { + + } + + public void destroy() { + Path runFile = CmsRcpDisplayFactory.getUrlRunFile(); + try { + if (Files.exists(runFile)) { + Files.delete(runFile); + } + } catch (IOException e) { + logger.log(Level.ERROR, "Cannot delete " + runFile, e); + } + } + + public void addCmsApp(CmsApp cmsApp, Map properties) { + final String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY); + if (contextName != null) { + httpServer.thenAcceptAsync((httpServer) -> { + httpServer.createContext("/" + contextName, new HttpHandler() { + + @Override + public void handle(HttpExchange exchange) throws IOException { + String path = exchange.getRequestURI().getPath(); + String uiName = path != null ? path.substring(path.lastIndexOf('/') + 1) : ""; + CmsRcpDisplayFactory.openCmsApp(cmsApp, uiName, null); + exchange.sendResponseHeaders(200, -1); + logger.log(Level.DEBUG, "Opened RCP UI " + uiName + " of CMS App /" + contextName); + } + }); + }).exceptionally(e -> { + logger.log(Level.ERROR, "Cannot register RCO app " + contextName, e); + return null; + }); + logger.log(Level.DEBUG, "Registered RCP CMS APP /" + contextName); + } + } + + public void removeCmsApp(CmsApp cmsApp, Map properties) { + String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY); + if (contextName != null) { + httpServer.thenAcceptAsync((httpServer) -> { + httpServer.removeContext("/" + contextName); + }); + } + } + + public void setHttpServer(HttpServer httpServer) { + Integer httpPort = httpServer.getAddress().getPort(); + String baseUrl = "http://localhost:" + httpPort + "/"; + Path runFile = CmsRcpDisplayFactory.getUrlRunFile(); + try { + if (!Files.exists(runFile)) { + Files.createDirectories(runFile.getParent()); + // TODO give read permission only to the owner + Files.createFile(runFile); + } else { + URI uri = URI.create(Files.readString(runFile)); + if (!httpPort.equals(uri.getPort())) + if (!isPortAvailable(uri.getPort())) { + throw new IllegalStateException("Another CMS is running on " + runFile); + } else { + logger.log(Level.WARNING, + "Run file " + runFile + " found but port of " + uri + " is available. Overwriting..."); + } + } + Files.writeString(runFile, baseUrl, StandardCharsets.UTF_8); + } catch (IOException e) { + throw new RuntimeException("Cannot write run file to " + runFile, e); + } + logger.log(DEBUG, "RCP available under " + baseUrl + ", written to " + runFile); + this.httpServer.complete(httpServer); + } + + protected boolean isPortAvailable(int port) { + ServerSocket ss = null; + DatagramSocket ds = null; + try { + ss = new ServerSocket(port); + ss.setReuseAddress(true); + ds = new DatagramSocket(port); + ds.setReuseAddress(true); + return true; + } catch (IOException e) { + } finally { + if (ds != null) { + ds.close(); + } + + if (ss != null) { + try { + ss.close(); + } catch (IOException e) { + /* should not be thrown */ + } + } + } + + return false; + } +} diff --git a/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServlet.java b/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServlet.java deleted file mode 100644 index 8579527c1..000000000 --- a/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServlet.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.argeo.cms.ui.rcp.servlet; - -import java.io.IOException; -import java.lang.System.Logger; -import java.lang.System.Logger.Level; -import java.util.Objects; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.argeo.api.cms.CmsApp; -import org.argeo.cms.ui.rcp.CmsRcpDisplayFactory; - -/** Open the related app when called. */ -public class CmsRcpServlet extends HttpServlet { - private static final long serialVersionUID = -3944472431354848923L; - private final static Logger logger = System.getLogger(CmsRcpServlet.class.getName()); - - private CmsApp cmsApp; - - public CmsRcpServlet(CmsApp cmsApp) { - Objects.requireNonNull(cmsApp); - this.cmsApp = cmsApp; - } - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - String path = req.getPathInfo(); - String uiName = path != null ? path.substring(path.lastIndexOf('/') + 1) : ""; - CmsRcpDisplayFactory.openCmsApp(cmsApp, uiName, null); - logger.log(Level.DEBUG, "Opened RCP UI " + uiName + " of CMS App " + req.getServletPath()); - } - -} diff --git a/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServletFactory.java b/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServletFactory.java deleted file mode 100644 index 778cd1b21..000000000 --- a/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServletFactory.java +++ /dev/null @@ -1,129 +0,0 @@ -package org.argeo.cms.ui.rcp.servlet; - -import static java.lang.System.Logger.Level.DEBUG; - -import java.io.IOException; -import java.lang.System.Logger; -import java.lang.System.Logger.Level; -import java.net.DatagramSocket; -import java.net.ServerSocket; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Map; -import java.util.concurrent.CompletableFuture; - -import org.argeo.api.cms.CmsApp; -import org.argeo.cms.ui.rcp.CmsRcpDisplayFactory; - -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpServer; - -/** Publishes one {@link CmsRcpServlet} per {@link CmsApp}. */ -public class CmsRcpServletFactory { - private final static Logger logger = System.getLogger(CmsRcpServletFactory.class.getName()); - private CompletableFuture httpServer = new CompletableFuture<>(); - - public void init() { - - } - - public void destroy() { - Path runFile = CmsRcpDisplayFactory.getUrlRunFile(); - try { - if (Files.exists(runFile)) { - Files.delete(runFile); - } - } catch (IOException e) { - logger.log(Level.ERROR, "Cannot delete " + runFile, e); - } - } - - public void addCmsApp(CmsApp cmsApp, Map properties) { - final String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY); - if (contextName != null) { - httpServer.thenAcceptAsync((httpServer) -> { - httpServer.createContext("/" + contextName, new HttpHandler() { - - @Override - public void handle(HttpExchange exchange) throws IOException { - String path = exchange.getRequestURI().getPath(); - String uiName = path != null ? path.substring(path.lastIndexOf('/') + 1) : ""; - CmsRcpDisplayFactory.openCmsApp(cmsApp, uiName, null); - exchange.sendResponseHeaders(200, -1); - logger.log(Level.DEBUG, "Opened RCP UI " + uiName + " of CMS App /" + contextName); - } - }); - }).exceptionally(e -> { - logger.log(Level.ERROR, "Cannot register RCO app " + contextName, e); - return null; - }); - logger.log(Level.DEBUG, "Registered RCP CMS APP /" + contextName); - } - } - - public void removeCmsApp(CmsApp cmsApp, Map properties) { - String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY); - if (contextName != null) { - httpServer.thenAcceptAsync((httpServer) -> { - httpServer.removeContext("/" + contextName); - }); - } - } - - public void setHttpServer(HttpServer httpServer) { - Integer httpPort = httpServer.getAddress().getPort(); - String baseUrl = "http://localhost:" + httpPort + "/"; - Path runFile = CmsRcpDisplayFactory.getUrlRunFile(); - try { - if (!Files.exists(runFile)) { - Files.createDirectories(runFile.getParent()); - // TODO give read permission only to the owner - Files.createFile(runFile); - } else { - URI uri = URI.create(Files.readString(runFile)); - if (!httpPort.equals(uri.getPort())) - if (!isPortAvailable(uri.getPort())) { - throw new IllegalStateException("Another CMS is running on " + runFile); - } else { - logger.log(Level.WARNING, - "Run file " + runFile + " found but port of " + uri + " is available. Overwriting..."); - } - } - Files.writeString(runFile, baseUrl, StandardCharsets.UTF_8); - } catch (IOException e) { - throw new RuntimeException("Cannot write run file to " + runFile, e); - } - logger.log(DEBUG, "RCP available under " + baseUrl + ", written to " + runFile); - this.httpServer.complete(httpServer); - } - - protected boolean isPortAvailable(int port) { - ServerSocket ss = null; - DatagramSocket ds = null; - try { - ss = new ServerSocket(port); - ss.setReuseAddress(true); - ds = new DatagramSocket(port); - ds.setReuseAddress(true); - return true; - } catch (IOException e) { - } finally { - if (ds != null) { - ds.close(); - } - - if (ss != null) { - try { - ss.close(); - } catch (IOException e) { - /* should not be thrown */ - } - } - } - - return false; - } -}