From: Mathieu Baudier Date: Fri, 6 May 2022 10:42:17 +0000 (+0200) Subject: Simplify CMS App deployment X-Git-Tag: v2.3.10~238 X-Git-Url: http://git.argeo.org/?a=commitdiff_plain;h=e1123c4b16bc6125c9b9e6827ab754d0a4be643a;p=lgpl%2Fargeo-commons.git Simplify CMS App deployment --- diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleCmsSwtTheme.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleCmsSwtTheme.java index b9b2751a7..8626a53e3 100644 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleCmsSwtTheme.java +++ b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleCmsSwtTheme.java @@ -55,11 +55,18 @@ public class BundleCmsSwtTheme extends BundleCmsTheme implements CmsSwtTheme { paths: for (String p : getImagesPaths()) { int lastSlash = p.lastIndexOf('/'); String fileName = p; + String ext = ""; if (lastSlash >= 0) fileName = p.substring(lastSlash + 1); int lastDot = fileName.lastIndexOf('.'); - if (lastDot >= 0) + if (lastDot >= 0) { + ext = fileName.substring(lastDot + 1); fileName = fileName.substring(0, lastDot); + } + + if ("svg".equals(ext)) + continue paths; + if (fileName.equals(name)) {// matched Image img = getImage(p); int width = img.getBounds().width; diff --git a/org.argeo.api.cms/src/org/argeo/api/cms/CmsApp.java b/org.argeo.api.cms/src/org/argeo/api/cms/CmsApp.java index 761191e5d..af9665a63 100644 --- a/org.argeo.api.cms/src/org/argeo/api/cms/CmsApp.java +++ b/org.argeo.api.cms/src/org/argeo/api/cms/CmsApp.java @@ -13,6 +13,8 @@ public interface CmsApp { */ final static String UI_NAME_PROPERTY = CmsApp.class.getName() + ".ui.name"; + final static String CONTEXT_NAME_PROPERTY = "argeo.cms.app.contextName"; + Set getUiNames(); CmsUi initUi(Object uiParent); diff --git a/org.argeo.cms/src/org/argeo/cms/osgi/BundleCmsTheme.java b/org.argeo.cms/src/org/argeo/cms/osgi/BundleCmsTheme.java index 6c195d45f..3312d72f9 100644 --- a/org.argeo.cms/src/org/argeo/cms/osgi/BundleCmsTheme.java +++ b/org.argeo.cms/src/org/argeo/cms/osgi/BundleCmsTheme.java @@ -327,10 +327,15 @@ public class BundleCmsTheme implements CmsTheme { @Override public InputStream loadPath(String path) throws IOException { URL url = themeBundle.getResource(path); - if (url == null) - throw new IllegalArgumentException( - "Path " + path + " not found in bundle " + themeBundle.getSymbolicName()); - return url.openStream(); + if (url == null) { + if (parentTheme != null) + return parentTheme.loadPath(path); + else + throw new IllegalArgumentException( + "Path " + path + " not found in bundle " + themeBundle.getSymbolicName()); + } else { + return url.openStream(); + } } private static Bundle findThemeBundle(BundleContext bundleContext, String themeId) { diff --git a/org.argeo.util/src/org/argeo/util/OS.java b/org.argeo.util/src/org/argeo/util/OS.java index d8127b600..caf96dd8c 100644 --- a/org.argeo.util/src/org/argeo/util/OS.java +++ b/org.argeo.util/src/org/argeo/util/OS.java @@ -1,7 +1,8 @@ package org.argeo.util; import java.io.File; -import java.lang.management.ManagementFactory; +import java.nio.file.Path; +import java.nio.file.Paths; /** When OS specific informations are needed. */ public class OS { @@ -40,17 +41,25 @@ public class OS { return new String[] { "cmd.exe", "/C" }; } - public static Integer getJvmPid() { - /* - * This method works on most platforms (including Linux). Although when Java 9 - * comes along, there is a better way: long pid = - * ProcessHandle.current().getPid(); - * - * See: - * http://stackoverflow.com/questions/35842/how-can-a-java-program-get-its-own- - * process-id - */ - String pidAndHost = ManagementFactory.getRuntimeMXBean().getName(); - return Integer.parseInt(pidAndHost.substring(0, pidAndHost.indexOf('@'))); + public static long getJvmPid() { + return ProcessHandle.current().pid(); +// String pidAndHost = ManagementFactory.getRuntimeMXBean().getName(); +// return Integer.parseInt(pidAndHost.substring(0, pidAndHost.indexOf('@'))); + } + + /** + * Get the runtime directory. It will be the environment variable + * XDG_RUNTIME_DIR if it is set, or ~/.cache/argeo if not. + */ + public static Path getRunDir() { + Path runDir; + String xdgRunDir = System.getenv("XDG_RUNTIME_DIR"); + if (xdgRunDir != null) { + // TODO support multiple names + runDir = Paths.get(xdgRunDir); + } else { + runDir = Paths.get(System.getProperty("user.home"), ".cache/argeo"); + } + return runDir; } } diff --git a/rap/org.argeo.cms.ui.rap/.project b/rap/org.argeo.cms.ui.rap/.project index 1a37a6771..630160402 100644 --- a/rap/org.argeo.cms.ui.rap/.project +++ b/rap/org.argeo.cms.ui.rap/.project @@ -20,6 +20,11 @@ + + org.eclipse.pde.ds.core.builder + + + org.eclipse.pde.PluginNature diff --git a/rap/org.argeo.cms.ui.rap/OSGI-INF/cmsWebAppFactory.xml b/rap/org.argeo.cms.ui.rap/OSGI-INF/cmsWebAppFactory.xml new file mode 100644 index 000000000..aa7e0adca --- /dev/null +++ b/rap/org.argeo.cms.ui.rap/OSGI-INF/cmsWebAppFactory.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/rap/org.argeo.cms.ui.rap/bnd.bnd b/rap/org.argeo.cms.ui.rap/bnd.bnd index e3e71b309..dc528df8c 100644 --- a/rap/org.argeo.cms.ui.rap/bnd.bnd +++ b/rap/org.argeo.cms.ui.rap/bnd.bnd @@ -13,3 +13,5 @@ org.eclipse.jetty.*;version="[9.4,12)";resolution:=optional,\ javax.servlet.*;version="[3,5)",\ * +Service-Component: OSGI-INF/cmsWebAppFactory.xml + diff --git a/rap/org.argeo.cms.ui.rap/build.properties b/rap/org.argeo.cms.ui.rap/build.properties index 34d2e4d2d..7d543b56b 100644 --- a/rap/org.argeo.cms.ui.rap/build.properties +++ b/rap/org.argeo.cms.ui.rap/build.properties @@ -1,4 +1,5 @@ -source.. = src/ output.. = bin/ bin.includes = META-INF/,\ - . + .,\ + OSGI-INF/cmsWebAppFactory.xml +source.. = src/ diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/osgi/CmsWebAppFactory.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/osgi/CmsWebAppFactory.java new file mode 100644 index 000000000..8380a85dd --- /dev/null +++ b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/osgi/CmsWebAppFactory.java @@ -0,0 +1,54 @@ +package org.argeo.cms.web.osgi; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +import org.argeo.api.cms.CmsApp; +import org.argeo.cms.web.CmsWebApp; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.service.event.EventAdmin; + +/** Publish a CmsApp as a RAP application. */ +public class CmsWebAppFactory { + private BundleContext bundleContext = FrameworkUtil.getBundle(CmsWebAppFactory.class).getBundleContext(); + private final static String CONTEXT_NAME = "contextName"; + + private EventAdmin eventAdmin; + + private Map registrations = Collections.synchronizedMap(new HashMap<>()); + + public void addCmsApp(CmsApp cmsApp, Map properties) { + String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY); + if (contextName != null) { + CmsWebApp cmsWebApp = new CmsWebApp(); + cmsWebApp.setEventAdmin(eventAdmin); + cmsWebApp.setCmsApp(cmsApp, properties); + Hashtable serviceProperties = new Hashtable<>(); + if (!contextName.equals("")) + serviceProperties.put(CONTEXT_NAME, contextName); + cmsWebApp.init(bundleContext, serviceProperties); + registrations.put(contextName, cmsWebApp); + } + } + + public void removeCmsApp(CmsApp cmsApp, Map properties) { + String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY); + if (contextName != null) { + CmsWebApp cmsWebApp = registrations.get(contextName); + if (cmsWebApp != null) { + cmsWebApp.destroy(bundleContext, new HashMap<>()); + cmsWebApp.unsetCmsApp(cmsApp, properties); + } else { + // TODO log warning + } + } + } + + public void setEventAdmin(EventAdmin eventAdmin) { + this.eventAdmin = eventAdmin; + } + +} diff --git a/rcp/org.argeo.cms.ui.rcp/OSGI-INF/cmsRcpApp.xml b/rcp/org.argeo.cms.ui.rcp/OSGI-INF/cmsRcpApp.xml deleted file mode 100644 index 490606f0f..000000000 --- a/rcp/org.argeo.cms.ui.rcp/OSGI-INF/cmsRcpApp.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/rcp/org.argeo.cms.ui.rcp/OSGI-INF/cmsRcpDisplayFactory.xml b/rcp/org.argeo.cms.ui.rcp/OSGI-INF/cmsRcpDisplayFactory.xml new file mode 100644 index 000000000..a0c0f0f5c --- /dev/null +++ b/rcp/org.argeo.cms.ui.rcp/OSGI-INF/cmsRcpDisplayFactory.xml @@ -0,0 +1,4 @@ + + + + diff --git a/rcp/org.argeo.cms.ui.rcp/OSGI-INF/cmsRcpServletFactory.xml b/rcp/org.argeo.cms.ui.rcp/OSGI-INF/cmsRcpServletFactory.xml new file mode 100644 index 000000000..a1f0b3a2a --- /dev/null +++ b/rcp/org.argeo.cms.ui.rcp/OSGI-INF/cmsRcpServletFactory.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/rcp/org.argeo.cms.ui.rcp/bnd.bnd b/rcp/org.argeo.cms.ui.rcp/bnd.bnd index 72b00d2cd..91f0a8a37 100644 --- a/rcp/org.argeo.cms.ui.rcp/bnd.bnd +++ b/rcp/org.argeo.cms.ui.rcp/bnd.bnd @@ -1,9 +1,12 @@ -Service-Component: OSGI-INF/cmsRcpApp.xml - Import-Package:\ org.argeo.cms.auth,\ org.eclipse.swt,\ org.eclipse.swt.graphics,\ org.w3c.css.sac,\ * + +Service-Component:\ +OSGI-INF/cmsRcpDisplayFactory.xml,\ +OSGI-INF/cmsRcpServletFactory.xml + diff --git a/rcp/org.argeo.cms.ui.rcp/build.properties b/rcp/org.argeo.cms.ui.rcp/build.properties index 6210e849b..5eef7058e 100644 --- a/rcp/org.argeo.cms.ui.rcp/build.properties +++ b/rcp/org.argeo.cms.ui.rcp/build.properties @@ -1,5 +1,6 @@ output.. = bin/ bin.includes = META-INF/,\ .,\ - OSGI-INF/ + OSGI-INF/,\ + OSGI-INF/cmsRcpServletFactory.xml source.. = src/ diff --git a/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/CmsRcpApp.java b/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/CmsRcpApp.java index fb56773b8..664d49d2b 100644 --- a/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/CmsRcpApp.java +++ b/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/CmsRcpApp.java @@ -13,7 +13,6 @@ import javax.security.auth.login.LoginException; import org.argeo.api.cms.CmsApp; import org.argeo.api.cms.CmsAuth; -import org.argeo.api.cms.CmsContext; import org.argeo.api.cms.CmsImageManager; import org.argeo.api.cms.CmsLog; import org.argeo.api.cms.CmsSession; @@ -26,7 +25,6 @@ import org.argeo.cms.swt.CmsSwtUtils; import org.eclipse.e4.ui.css.core.engine.CSSEngine; import org.eclipse.e4.ui.css.core.engine.CSSErrorHandler; import org.eclipse.e4.ui.css.swt.engine.CSSSWTEngineImpl; -import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; @@ -42,13 +40,9 @@ public class CmsRcpApp implements CmsView { private BundleContext bundleContext = FrameworkUtil.getBundle(CmsRcpApp.class).getBundleContext(); - private Display display; private Shell shell; private CmsApp cmsApp; - private CmsUiThread uiThread; - private CmsContext cmsContext; - // CMS View private String uid; private LoginContext loginContext; @@ -61,123 +55,68 @@ public class CmsRcpApp implements CmsView { // TODO make it configurable private String uiName = "desktop"; - public CmsRcpApp() { + public CmsRcpApp(String uiName) { uid = UUID.randomUUID().toString(); + this.uiName = uiName; } - public void init(Map properties) { - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - // silent - } - uiThread = new CmsUiThread(); - uiThread.start(); - - } + public void initRcpApp() { + Display display = Display.getCurrent(); + shell = new Shell(display); + shell.setText("Argeo CMS"); + Composite parent = shell; + parent.setLayout(CmsSwtUtils.noSpaceGridLayout()); + CmsSwtUtils.registerCmsView(shell, CmsRcpApp.this); - public void destroy(Map properties) { - if (!shell.isDisposed()) - shell.dispose(); try { - uiThread.join(); - } catch (InterruptedException e) { - // silent - } finally { - uiThread = null; + loginContext = new LoginContext(CmsAuth.SINGLE_USER.getLoginContextName()); + loginContext.login(); + } catch (LoginException e) { + throw new IllegalStateException("Could not log in.", e); } - } - - class CmsUiThread extends Thread { - - public CmsUiThread() { - super("CMS UI"); - } - - @Override - public void run() { - display = Display.getDefault(); - shell = new Shell(display); - shell.setText("Argeo CMS"); - Composite parent = shell; - parent.setLayout(new GridLayout()); - CmsSwtUtils.registerCmsView(shell, CmsRcpApp.this); - -// Subject subject = new Subject(); -// CmsLoginShell loginShell = new CmsLoginShell(CmsRcpApp.this); -// loginShell.setSubject(subject); - try { - // try pre-auth -// loginContext = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, subject, loginShell); - loginContext = new LoginContext(CmsAuth.LOGIN_CONTEXT_SINGLE_USER); - loginContext.login(); - } catch (LoginException e) { - throw new IllegalStateException("Could not log in.", e); -// loginShell.createUi(); -// loginShell.open(); -// -// while (!loginShell.getShell().isDisposed()) { -// if (!display.readAndDispatch()) -// display.sleep(); -// } - } - if (log.isDebugEnabled()) - log.debug("Logged in to desktop: " + loginContext.getSubject()); - - Subject.doAs(loginContext.getSubject(), (PrivilegedAction) () -> { - - // TODO factorise with web app - parent.setData(CmsApp.UI_NAME_PROPERTY, uiName); - ui = cmsApp.initUi(parent); - if (ui instanceof Composite) - ((Composite) ui).setLayoutData(CmsSwtUtils.fillAll()); - // ui.setLayoutData(CmsUiUtils.fillAll()); - // we need ui to be set before refresh so that CmsView can store UI context data - // in it. - cmsApp.refreshUi(ui, null); - - // Styling - CmsTheme theme = CmsSwtUtils.getCmsTheme(parent); - if (theme != null) { - cssEngine = new CSSSWTEngineImpl(display); - for (String path : theme.getSwtCssPaths()) { - try (InputStream in = theme.loadPath(path)) { - cssEngine.parseStyleSheet(in); - } catch (IOException e) { - throw new IllegalStateException("Cannot load stylesheet " + path, e); - } + if (log.isDebugEnabled()) + log.debug("Logged in to desktop: " + loginContext.getSubject()); + + Subject.doAs(loginContext.getSubject(), (PrivilegedAction) () -> { + + // TODO factorise with web app + parent.setData(CmsApp.UI_NAME_PROPERTY, uiName); + ui = cmsApp.initUi(parent); + if (ui instanceof Composite) + ((Composite) ui).setLayoutData(CmsSwtUtils.fillAll()); + // we need ui to be set before refresh so that CmsView can store UI context data + // in it. + cmsApp.refreshUi(ui, null); + + // Styling + CmsTheme theme = CmsSwtUtils.getCmsTheme(parent); + if (theme != null) { + cssEngine = new CSSSWTEngineImpl(display); + for (String path : theme.getSwtCssPaths()) { + try (InputStream in = theme.loadPath(path)) { + cssEngine.parseStyleSheet(in); + } catch (IOException e) { + throw new IllegalStateException("Cannot load stylesheet " + path, e); } - cssEngine.setErrorHandler(new CSSErrorHandler() { - public void error(Exception e) { - log.error("SWT styling error: ", e); - } - }); - applyStyles(shell); - } - shell.layout(true, true); - - shell.open(); - while (!shell.isDisposed()) { - if (!display.readAndDispatch()) - display.sleep(); } - display.dispose(); - return null; - }); - } + cssEngine.setErrorHandler(new CSSErrorHandler() { + public void error(Exception e) { + log.error("SWT styling error: ", e); + } + }); + applyStyles(shell); + } + shell.layout(true, true); + shell.open(); + return null; + }); } - - /* * CMS VIEW */ - public void setCmsContext(CmsContext cmsContext) { - this.cmsContext = cmsContext; - } - @Override public String getUid() { return uid; @@ -274,10 +213,14 @@ public class CmsRcpApp implements CmsView { /* * DEPENDENCY INJECTION */ - public void setCmsApp(CmsApp cmsApp) { + public void setCmsApp(CmsApp cmsApp, Map properties) { this.cmsApp = cmsApp; } + public void unsetCmsApp(CmsApp cmsApp, Map properties) { + this.cmsApp = null; + } + public void setEventAdmin(EventAdmin eventAdmin) { this.eventAdmin = eventAdmin; } diff --git a/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/CmsRcpDisplayFactory.java b/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/CmsRcpDisplayFactory.java new file mode 100644 index 000000000..ceab08ab6 --- /dev/null +++ b/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/CmsRcpDisplayFactory.java @@ -0,0 +1,79 @@ +package org.argeo.cms.ui.rcp; + +import java.nio.file.Path; + +import org.argeo.api.cms.CmsApp; +import org.argeo.util.OS; +import org.eclipse.swt.widgets.Display; +import org.osgi.service.event.EventAdmin; + +/** Creates the SWT {@link Display} in a dedicated thread. */ +public class CmsRcpDisplayFactory { + /** File name in a run directory */ + private final static String ARGEO_RCP_URL = "argeo.rcp.url"; + + /** There is only one display in RCP mode */ + private static Display display; + + private CmsUiThread uiThread; + + private boolean shutdown = false; + + public void init() { + uiThread = new CmsUiThread(); + uiThread.start(); + + } + + public void destroy() { + shutdown = true; + display.wake(); + try { + uiThread.join(); + } catch (InterruptedException e) { + // silent + } finally { + uiThread = null; + } + } + + class CmsUiThread extends Thread { + + public CmsUiThread() { + super("CMS UI"); + } + + @Override + public void run() { + display = Display.getDefault(); + +// for (String contextName : cmsApps.keySet()) { +// openCmsApp(contextName); +// } + + while (!shutdown) { + if (!display.readAndDispatch()) + display.sleep(); + } + display.dispose(); + display = null; + } + } + + public static Display getDisplay() { + return display; + } + + public static void openCmsApp(EventAdmin eventAdmin, CmsApp cmsApp, String uiName) { + CmsRcpDisplayFactory.getDisplay().syncExec(() -> { + CmsRcpApp cmsRcpApp = new CmsRcpApp(uiName); + cmsRcpApp.setEventAdmin(eventAdmin); + cmsRcpApp.setCmsApp(cmsApp, null); + cmsRcpApp.initRcpApp(); + }); + } + + public static Path getUrlRunFile() { + return OS.getRunDir().resolve(CmsRcpDisplayFactory.ARGEO_RCP_URL); + } +} diff --git a/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServlet.java b/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServlet.java new file mode 100644 index 000000000..54e2165bb --- /dev/null +++ b/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServlet.java @@ -0,0 +1,40 @@ +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; +import org.osgi.service.event.EventAdmin; + +/** 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; + private EventAdmin eventAdmin; + + public CmsRcpServlet(EventAdmin eventAdmin, CmsApp cmsApp) { + Objects.requireNonNull(eventAdmin); + Objects.requireNonNull(cmsApp); + this.cmsApp = cmsApp; + this.eventAdmin = eventAdmin; + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String path = req.getPathInfo(); + String uiName = path.substring(path.lastIndexOf('/') + 1); + CmsRcpDisplayFactory.openCmsApp(eventAdmin, cmsApp, uiName); + logger.log(Level.DEBUG, "Opened RCP UI " + uiName + " of CMS App " + req.getServletPath()); + } + +} diff --git a/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServletFactory.java b/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServletFactory.java new file mode 100644 index 000000000..5a199d87c --- /dev/null +++ b/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServletFactory.java @@ -0,0 +1,132 @@ +package org.argeo.cms.ui.rcp.servlet; + +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.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import javax.servlet.Servlet; + +import org.argeo.api.cms.CmsApp; +import org.argeo.cms.ui.rcp.CmsRcpDisplayFactory; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.event.EventAdmin; +import org.osgi.service.http.HttpService; + +/** Publishes one {@link CmsRcpServlet} per {@link CmsApp}. */ +public class CmsRcpServletFactory { + private final static Logger logger = System.getLogger(CmsRcpServletFactory.class.getName()); + + private BundleContext bundleContext = FrameworkUtil.getBundle(CmsRcpServletFactory.class).getBundleContext(); + + private CompletableFuture eventAdmin = new CompletableFuture<>(); + + private Map> registrations = Collections.synchronizedMap(new HashMap<>()); + + 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) { + String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY); + if (contextName != null) { + eventAdmin.thenAccept((eventAdmin) -> { + CmsRcpServlet servlet = new CmsRcpServlet(eventAdmin, cmsApp); + Hashtable serviceProperties = new Hashtable<>(); + serviceProperties.put("osgi.http.whiteboard.servlet.pattern", "/" + contextName + "/*"); + ServiceRegistration sr = bundleContext.registerService(Servlet.class, servlet, + serviceProperties); + registrations.put(contextName, sr); + }); + } + } + + public void removeCmsApp(CmsApp cmsApp, Map properties) { + String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY); + if (contextName != null) { + ServiceRegistration sr = registrations.get(contextName); + sr.unregister(); + } + } + + public void setEventAdmin(EventAdmin eventAdmin) { + this.eventAdmin.complete(eventAdmin); + } + + public void setHttpService(HttpService httpService, Map properties) { + Integer httpPort = Integer.parseInt(properties.get("http.port").toString()); + 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(Level.DEBUG, "RCP available under " + baseUrl + ", written to " + runFile); + } + + 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/sdk/cms-e4-rcp.properties b/sdk/cms-e4-rcp.properties deleted file mode 100644 index df8363b76..000000000 --- a/sdk/cms-e4-rcp.properties +++ /dev/null @@ -1,39 +0,0 @@ -argeo.osgi.start.2.node=\ -org.eclipse.equinox.metatype,\ -org.eclipse.equinox.cm,\ -org.eclipse.equinox.ds,\ -org.argeo.init - -argeo.osgi.start.3.node=\ -org.argeo.cms,\ -org.argeo.cms.jcr,\ -org.argeo.cms.ui.rcp - - -# Local -argeo.node.repo.type=h2 -org.osgi.service.http.port=7070 -#org.osgi.service.http.port.secure=7073 - -argeo.node.useradmin.uris=os:/// - -#argeo.node.useradmin.uris=ldap://cn=Directory%20Manager:argeoargeo@localhost:10389/dc=example,dc=com - -argeo.node.init=../../init - -argeo.i18n.locales=en,fr -argeo.i18n.defaultLocale=en - -#tika.config=/home/mbaudier/dev/git/gpl/argeo-suite/sdk/exec/argeo-office-e4-rap/data/indexes/node/tika-config.xml - -# Logging -log.org.argeo=DEBUG - -# DON'T CHANGE BELOW -org.eclipse.equinox.http.jetty.autostart=false -org.osgi.framework.bootdelegation=com.sun.jndi.ldap,\ -com.sun.jndi.ldap.sasl,\ -com.sun.security.jgss,\ -com.sun.jndi.dns,\ -com.sun.nio.file,\ -com.sun.nio.sctp diff --git a/sdk/cms-rcp.properties b/sdk/cms-rcp.properties new file mode 100644 index 000000000..df8363b76 --- /dev/null +++ b/sdk/cms-rcp.properties @@ -0,0 +1,39 @@ +argeo.osgi.start.2.node=\ +org.eclipse.equinox.metatype,\ +org.eclipse.equinox.cm,\ +org.eclipse.equinox.ds,\ +org.argeo.init + +argeo.osgi.start.3.node=\ +org.argeo.cms,\ +org.argeo.cms.jcr,\ +org.argeo.cms.ui.rcp + + +# Local +argeo.node.repo.type=h2 +org.osgi.service.http.port=7070 +#org.osgi.service.http.port.secure=7073 + +argeo.node.useradmin.uris=os:/// + +#argeo.node.useradmin.uris=ldap://cn=Directory%20Manager:argeoargeo@localhost:10389/dc=example,dc=com + +argeo.node.init=../../init + +argeo.i18n.locales=en,fr +argeo.i18n.defaultLocale=en + +#tika.config=/home/mbaudier/dev/git/gpl/argeo-suite/sdk/exec/argeo-office-e4-rap/data/indexes/node/tika-config.xml + +# Logging +log.org.argeo=DEBUG + +# DON'T CHANGE BELOW +org.eclipse.equinox.http.jetty.autostart=false +org.osgi.framework.bootdelegation=com.sun.jndi.ldap,\ +com.sun.jndi.ldap.sasl,\ +com.sun.security.jgss,\ +com.sun.jndi.dns,\ +com.sun.nio.file,\ +com.sun.nio.sctp