org.argeo.cms.ux \
org.argeo.cms.ee \
org.argeo.cms.lib.jetty \
+org.argeo.cms.lib.dbus \
org.argeo.cms.lib.sshd \
org.argeo.cms.cli \
osgi/equinox/org.argeo.cms.lib.equinox \
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.argeo.cms.lib.dbus</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ds.core.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="start" deactivate="stop" name="cmsDBus">
+ <implementation class="org.argeo.cms.dbus.CmsDBusImpl"/>
+ <service>
+ <provide interface="org.argeo.cms.dbus.CmsDBus"/>
+ </service>
+ <reference bind="setCmsEventBus" cardinality="1..1" interface="org.argeo.api.cms.CmsEventBus" name="CmsEventBus" policy="static"/>
+</scr:component>
--- /dev/null
+Import-Package: \
+org.freedesktop.dbus.connections.transports,\
+*
+
+Service-Component: OSGI-INF/cmsDBus.xml
--- /dev/null
+bin.includes = META-INF/,\
+ .,\
+ OSGI-INF/cmsDBus.xml
+source.. = src/
+output.. = bin/
--- /dev/null
+package org.argeo.cms.dbus;
+
+import org.freedesktop.dbus.connections.impl.DBusConnection;
+
+public interface CmsDBus {
+ final static String DBUS_SESSION_BUS_ADDRESS = "DBUS_SESSION_BUS_ADDRESS";
+
+ DBusConnection openSessionConnection();
+}
--- /dev/null
+package org.argeo.cms.dbus;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.argeo.api.cms.CmsEventBus;
+import org.argeo.api.cms.CmsLog;
+import org.freedesktop.dbus.bin.EmbeddedDBusDaemon;
+import org.freedesktop.dbus.connections.BusAddress;
+import org.freedesktop.dbus.connections.impl.DBusConnection;
+import org.freedesktop.dbus.connections.impl.DBusConnectionBuilder;
+import org.freedesktop.dbus.exceptions.DBusException;
+
+public class CmsDBusImpl implements CmsDBus {
+ private final static CmsLog log = CmsLog.getLog(CmsDBusImpl.class);
+
+ private BusAddress sessionBusAddress;
+
+ private EmbeddedDBusDaemon dBusDaemon;
+
+ private CmsEventBus cmsEventBus;
+
+ public void start() {
+ try {
+ final String envSessionBusAddress = System.getenv(DBUS_SESSION_BUS_ADDRESS);
+ if (envSessionBusAddress != null) {
+ sessionBusAddress = BusAddress.of(envSessionBusAddress);
+
+ // !! We must first initialise a connection, otherwise there are classloader issues later on
+ try (DBusConnection dBusConnection = DBusConnectionBuilder.forAddress(sessionBusAddress)
+ .withShared(false).build()) {
+
+ }
+ log.debug(() -> "Found session DBus with address " + sessionBusAddress);
+ } else {
+ Path socketLocation = Paths.get(System.getProperty("user.home"), ".cache", "argeo", "bus");
+ Files.createDirectories(socketLocation.getParent());
+ // TODO escape : on Windows?
+ String embeddedSessionBusAddress = "unix:path=" + socketLocation.toUri().getPath();
+ dBusDaemon = new EmbeddedDBusDaemon(embeddedSessionBusAddress);
+ dBusDaemon.startInBackgroundAndWait(30 * 1000);
+
+ sessionBusAddress = BusAddress.of(embeddedSessionBusAddress);
+ try (DBusConnection dBusConnection = DBusConnectionBuilder.forAddress(sessionBusAddress)
+ .withShared(false).build()) {
+
+ }
+ log.debug(() -> "Started embedded session DBus with address " + sessionBusAddress);
+
+ // TODO set environment variable?
+ }
+ } catch (DBusException | IOException e) {
+ throw new IllegalStateException("Cannot find a session bus", e);
+ }
+ }
+
+ public void stop() {
+ if (dBusDaemon != null) {
+ try {
+ dBusDaemon.close();
+ } catch (IOException e) {
+ log.error("Cannot close embedded DBus daemon", e);
+ }
+ }
+ }
+
+ @Override
+ public DBusConnection openSessionConnection() {
+ try {
+ DBusConnection dBusConnection = DBusConnectionBuilder.forAddress(sessionBusAddress).withShared(false)
+ .build();
+ // TODO track all connections?
+ return dBusConnection;
+ } catch (DBusException e) {
+ e.printStackTrace();
+ throw new IllegalStateException("Cannot open connection to session DBus", e);
+ }
+ }
+
+ public void setCmsEventBus(CmsEventBus cmsEventBus) {
+ this.cmsEventBus = cmsEventBus;
+ }
+
+}
--- /dev/null
+package org.argeo.cms.freedesktop;
+
+import java.util.List;
+import java.util.Map;
+
+import org.freedesktop.dbus.annotations.DBusInterfaceName;
+import org.freedesktop.dbus.annotations.DBusMemberName;
+import org.freedesktop.dbus.interfaces.DBusInterface;
+import org.freedesktop.dbus.types.Variant;
+
+/**
+ * The org.freedesktop.Application interface.
+ */
+@DBusInterfaceName("org.freedesktop.Application")
+public interface FreeDesktopApplication extends DBusInterface {
+
+ @DBusMemberName(value = "Activate")
+ void activate(Map<String, Variant<?>> platformData);
+
+ @DBusMemberName(value = "Open")
+ void open(List<String> uris, Map<String, Variant<?>> platformData);
+
+ @DBusMemberName(value = "ActivateAction")
+ void activateAction(String actionName, List<Variant<?>> parameter, Map<String, Variant<?>> platformData);
+
+}
\ No newline at end of file
--- /dev/null
+<!-- DTD for D-Bus Introspection data -->
+<!-- (C) 2005-02-02 David A. Wheeler; released under the D-Bus licenses,
+ GNU GPL version 2 (or greater) and AFL 1.1 (or greater) -->
+
+<!-- see D-Bus specification for documentation -->
+
+<!ELEMENT node (node|interface)*>
+<!ATTLIST node name CDATA #IMPLIED>
+
+<!ELEMENT interface (method|signal|property|annotation)*>
+<!ATTLIST interface name CDATA #REQUIRED>
+
+<!ELEMENT method (arg|annotation)*>
+<!ATTLIST method name CDATA #REQUIRED>
+
+<!ELEMENT signal (arg|annotation)*>
+<!ATTLIST signal name CDATA #REQUIRED>
+
+<!ELEMENT arg EMPTY>
+<!ATTLIST arg name CDATA #IMPLIED>
+<!ATTLIST arg type CDATA #REQUIRED>
+<!-- Method arguments SHOULD include "direction",
+ while signal and error arguments SHOULD not (since there's no point).
+ The DTD format can't express that subtlety. -->
+<!ATTLIST arg direction (in|out) "in">
+
+<!-- AKA "attribute" -->
+<!ELEMENT property (annotation)*>
+<!ATTLIST property name CDATA #REQUIRED>
+<!ATTLIST property type CDATA #REQUIRED>
+<!ATTLIST property access (read|write|readwrite) #REQUIRED>
+
+<!ELEMENT annotation EMPTY> <!-- Generic metadata -->
+<!ATTLIST annotation name CDATA #REQUIRED>
+<!ATTLIST annotation value CDATA #REQUIRED>
+
+
--- /dev/null
+<!DOCTYPE node SYSTEM "introspect-1.0.dtd">
+<!-- See https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#dbus -->
+<node>
+ <interface name="org.freedesktop.Application">
+ <method name="Activate">
+ <arg type="a{sv}" name="platform-data" direction="in" />
+ </method>
+ <method name="Open">
+ <arg type="as" name="uris" direction="in" />
+ <arg type="a{sv}" name="platform-data" direction="in" />
+ </method>
+ <method name="ActivateAction">
+ <arg type="s" name="action-name" direction="in" />
+ <arg type="av" name="parameter" direction="in" />
+ <arg type="a{sv}" name="platform-data" direction="in" />
+ </method>
+ </interface>
+</node>
\ No newline at end of file
public static LoginContext loginAsSystemUser(Subject subject) {
try {
URL jaasConfigurationUrl = OsUserUtils.class.getClassLoader()
- .getResource("org/argeo/osgi/useradmin/jaas-os.cfg");
+ .getResource("org/argeo/cms/osgi/useradmin/jaas-os.cfg");
URIParameter uriParameter = new URIParameter(jaasConfigurationUrl.toURI());
Configuration jaasConfiguration = Configuration.getInstance("JavaLoginConfig", uriParameter);
LoginContext lc = new LoginContext(isWindows() ? LOGIN_CONTEXT_USER_NT : LOGIN_CONTEXT_USER_NIX, subject,
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="start" deactivate="stop" name="cmsRcpDBusLauncher">
+ <implementation class="org.argeo.cms.ui.rcp.dbus.CmsRcpDBusLauncher"/>
+ <reference bind="setCmsDBus" cardinality="1..1" interface="org.argeo.cms.dbus.CmsDBus" name="CmsDBus" policy="static"/>
+ <reference bind="addCmsApp" cardinality="0..n" interface="org.argeo.api.cms.CmsApp" name="CmsApp" policy="dynamic" unbind="removeCmsApp"/>
+</scr:component>
org.eclipse.swt,\
org.eclipse.swt.graphics,\
org.w3c.css.sac,\
+org.freedesktop.dbus.connections,\
*
Service-Component:\
OSGI-INF/cmsRcpDisplayFactory.xml,\
-OSGI-INF/cmsRcpHttpLauncher.xml
+OSGI-INF/cmsRcpDBusLauncher.xml
+#OSGI-INF/cmsRcpHttpLauncher.xml
-output.. = bin/
bin.includes = META-INF/,\
.,\
OSGI-INF/,\
- OSGI-INF/cmsRcpHttpLauncher.xml
+ OSGI-INF/cmsRcpHttpLauncher.xml,\
+ OSGI-INF/cmsRcpDBusLauncher.xml
+additional.bundles = org.slf4j.api
source.. = src/
+output.. = bin/
display = null;
} catch (UnsatisfiedLinkError e) {
logger.log(Level.ERROR,
- "Cannot load SWT, probably because the OSGi framework has been refresh. Restart the application.",
+ "Cannot load SWT, either because the SWT DLLs are no in the java.library.path,"
+ + " or because the OSGi framework has been refreshed." + " Restart the application.",
e);
}
}
--- /dev/null
+package org.argeo.cms.ui.rcp.dbus;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+import org.argeo.api.cms.CmsApp;
+import org.argeo.cms.dbus.CmsDBus;
+
+public class CmsRcpDBusLauncher {
+ private CompletableFuture<CmsDBus> cmsDBus = new CompletableFuture<>();
+
+ private Map<String, CmsRcpFreeDesktopApplication> apps = new HashMap<>();
+
+ public void start() {
+
+ }
+
+ public void stop() {
+
+ }
+
+ public void addCmsApp(CmsApp cmsApp, Map<String, String> properties) {
+ final String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY);
+ cmsDBus.thenAcceptAsync((cmsDBus) -> {
+ CmsRcpFreeDesktopApplication application = new CmsRcpFreeDesktopApplication(cmsDBus, contextName, cmsApp);
+ apps.put(contextName, application);
+ });
+ }
+
+ public void removeCmsApp(CmsApp cmsApp, Map<String, String> properties) {
+ final String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY);
+ CmsRcpFreeDesktopApplication application = apps.remove(contextName);
+ if (application != null) {
+ try {
+ application.close();
+ } catch (IOException e) {
+ throw new IllegalStateException("Cannot remove CMS RCP app " + contextName, e);
+ }
+ }
+ }
+
+ public void setCmsDBus(CmsDBus cmsDBus) {
+ this.cmsDBus.complete(cmsDBus);
+ }
+
+}
--- /dev/null
+package org.argeo.cms.ui.rcp.dbus;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import org.argeo.api.cms.CmsApp;
+import org.argeo.cms.dbus.CmsDBus;
+import org.argeo.cms.freedesktop.FreeDesktopApplication;
+import org.argeo.cms.ui.rcp.CmsRcpDisplayFactory;
+import org.freedesktop.dbus.connections.impl.DBusConnection;
+import org.freedesktop.dbus.exceptions.DBusException;
+import org.freedesktop.dbus.types.Variant;
+
+public class CmsRcpFreeDesktopApplication implements FreeDesktopApplication, Closeable {
+ private String path;
+
+ private CmsApp cmsApp;
+
+ private DBusConnection dBusConnection;
+
+ public CmsRcpFreeDesktopApplication(CmsDBus cmsDBus, String contextName, CmsApp cmsApp) {
+ // TODO find a better prefix and/or make it customisable
+ this.path = "/org/argeo/cms/" + contextName;
+ this.cmsApp = cmsApp;
+ try {
+ String appName = path.replace('/', '.').substring(1);
+ dBusConnection = cmsDBus.openSessionConnection();
+ dBusConnection.requestBusName(appName);
+ dBusConnection.exportObject(getObjectPath(), this);
+ } catch (DBusException e) {
+ throw new IllegalStateException("Cannot add CMS app " + path, e);
+ }
+ }
+
+ @Override
+ public String getObjectPath() {
+ return path;
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (dBusConnection != null)
+ dBusConnection.close();
+ }
+
+ @Override
+ public void activate(Map<String, Variant<?>> platformData) {
+ // String uiName = path != null ? path.substring(path.lastIndexOf('/') + 1) :
+ // "";
+ String uiName = "app";
+ CmsRcpDisplayFactory.openCmsApp(cmsApp, uiName, null);
+ }
+
+ @Override
+ public void open(List<String> uris, Map<String, Variant<?>> platformData) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void activateAction(String actionName, List<Variant<?>> parameter, Map<String, Variant<?>> platformData) {
+ // TODO Auto-generated method stub
+
+ }
+
+}
--- /dev/null
+package org.argeo.cms.ui.rcp.dbus;
+
+import java.util.List;
+import java.util.Map;
+
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.argeo.cms.freedesktop.FreeDesktopApplication;
+import org.freedesktop.dbus.connections.impl.DBusConnection;
+import org.freedesktop.dbus.connections.impl.DBusConnectionBuilder;
+import org.freedesktop.dbus.exceptions.DBusException;
+import org.freedesktop.dbus.types.Variant;
+
+public class TestApplication implements FreeDesktopApplication {
+ private DBusConnection dBusConnection;
+
+ private final Display display;
+
+ private Shell shell = null;
+
+ private Text text;
+
+ public TestApplication() throws DBusException {
+ display = Display.getCurrent();
+
+ /* Get a connection to the session bus so we can request a bus name */
+ dBusConnection = DBusConnectionBuilder.forSessionBus().build();
+// m_conn = DBusConnectionBuilder.forAddress("unix:path=/tmp/dbus-80908265778467677465").build();
+// m_conn = DBusConnectionBuilder.forAddress("tcp:host=localhost,port=55556").build();
+ /* Request a unique bus name */
+ dBusConnection.requestBusName("org.argeo.TestApplication");
+ /* Export this object onto the bus using the path '/' */
+ dBusConnection.exportObject(getObjectPath(), this);
+ }
+
+ @Override
+ public String getObjectPath() {
+ return "/org/argeo/TestApplication";
+ }
+
+ @Override
+ public void activate(Map<String, Variant<?>> platformData) {
+ display.syncExec(() -> {
+ shellVisible();
+ });
+
+ }
+
+ protected void shellVisible() {
+ if (shell == null || shell.isDisposed()) {
+ shell = new Shell(display);
+ shell.setLayout(new GridLayout());
+ text = new Text(shell, SWT.MULTI | SWT.WRAP);
+ text.setLayoutData(CmsSwtUtils.fillAll());
+ text.setText("New shell\n");
+ shell.open();
+ } else {
+ }
+ shell.forceActive();
+ }
+
+ @Override
+ public void open(List<String> uris, Map<String, Variant<?>> platformData) {
+ display.syncExec(() -> {
+ shellVisible();
+ for (String uri : uris) {
+ text.append(uri);
+ text.append("\n");
+ }
+ shell.forceActive();
+ });
+ }
+
+ @Override
+ public void activateAction(String actionName, List<Variant<?>> parameter, Map<String, Variant<?>> platformData) {
+ display.syncExec(() -> {
+ shellVisible();
+ text.append("Execute action '" + actionName + "' with arguments " + parameter);
+ text.append("\n");
+ });
+ }
+
+ public static void main(String[] args) throws DBusException {
+ Display display = new Display();
+ new TestApplication();
+ while (!display.isDisposed()) {
+ if (!display.readAndDispatch())
+ display.sleep();
+ }
+ display.dispose();
+ }
+}