Introduce CMS event bus and use it in UI.
authorMathieu Baudier <mbaudier@argeo.org>
Sun, 24 Jul 2022 09:15:01 +0000 (11:15 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Sun, 24 Jul 2022 09:15:01 +0000 (11:15 +0200)
28 files changed:
org.argeo.api.cms/src/org/argeo/api/cms/CmsContext.java
org.argeo.api.cms/src/org/argeo/api/cms/CmsEventBus.java [new file with mode: 0644]
org.argeo.api.cms/src/org/argeo/api/cms/CmsState.java
org.argeo.cms.ee/src/org/argeo/cms/websocket/server/EventEndpoint.java
org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/CmsJettyServer.java
org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/JettyHttpServer.java
org.argeo.cms/OSGI-INF/cmsContext.xml
org.argeo.cms/OSGI-INF/cmsEventBus.xml [new file with mode: 0644]
org.argeo.cms/bnd.bnd
org.argeo.cms/build.properties
org.argeo.cms/src/org/argeo/cms/auth/RemoteAuthUtils.java
org.argeo.cms/src/org/argeo/cms/auth/SingleUserLoginModule.java
org.argeo.cms/src/org/argeo/cms/auth/SpnegoLoginModule.java
org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsContextImpl.java
org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsEventBusImpl.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsStateImpl.java
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/AbstractSwtCmsView.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLogin.java
swt/rap/org.argeo.cms.swt.rap/OSGI-INF/cmsWebAppFactory.xml
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsWebApp.java
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/osgi/CmsWebAppFactory.java
swt/rcp/org.argeo.cms.swt.rcp.cli/src/org/argeo/cms/swt/rcp/cli/CmsCli.java
swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpServletFactory.xml
swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/CmsRcpApp.java
swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/CmsRcpDisplayFactory.java
swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServlet.java
swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServletFactory.java

index 64bb4255c9922103f931aeee855a94fdd24a80cb..6ad0f512cab9180d4dbd1947e584022c672c0edc 100644 (file)
@@ -2,7 +2,7 @@ package org.argeo.api.cms;
 
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
+import java.util.UUID;
 
 import javax.security.auth.Subject;
 
@@ -29,11 +29,9 @@ public interface CmsContext {
        /** Get the CMS session of this subject. */
        CmsSession getCmsSession(Subject subject);
 
-       CmsState getCmsState();
+       CmsEventBus getCmsEventBus();
 
-       void sendEvent(String topic, Map<String, Object> event);
+       /** A new time based {@link UUID} (v1) using the current time */
+       UUID timeUUID();
 
-       void addEventSubscriber(String topic, CmsEventSubscriber eventSubscriber);
-
-       void removeEventSubscriber(String topic, CmsEventSubscriber eventSubscriber);
 }
diff --git a/org.argeo.api.cms/src/org/argeo/api/cms/CmsEventBus.java b/org.argeo.api.cms/src/org/argeo/api/cms/CmsEventBus.java
new file mode 100644 (file)
index 0000000..bb8a782
--- /dev/null
@@ -0,0 +1,12 @@
+package org.argeo.api.cms;
+
+import java.util.Map;
+
+public interface CmsEventBus {
+       void sendEvent(String topic, Map<String, Object> event);
+
+       void addEventSubscriber(String topic, CmsEventSubscriber eventSubscriber);
+
+       void removeEventSubscriber(String topic, CmsEventSubscriber eventSubscriber);
+
+}
index 9ffba68c12d1560a7447af92ee0b4d5e4319ba3c..181e4b9c661f716424f0d183bf97514431680b51 100644 (file)
@@ -6,6 +6,8 @@ import java.util.UUID;
 
 /** A running node process. */
 public interface CmsState {
+       String getHostname();
+
        Long getAvailableSince();
 
        UUID getUuid();
index a6b2a4df75c87a03c501f034f11f84de8243706e..c71c862d65c1cf195a8d0a7399a1ecfbd9a33f43 100644 (file)
@@ -10,7 +10,7 @@ import javax.websocket.Session;
 import javax.websocket.server.PathParam;
 import javax.websocket.server.ServerEndpoint;
 
-import org.argeo.api.cms.CmsContext;
+import org.argeo.api.cms.CmsEventBus;
 import org.argeo.api.cms.CmsEventSubscriber;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.FrameworkUtil;
@@ -20,15 +20,15 @@ public class EventEndpoint implements CmsEventSubscriber {
        private BundleContext bc = FrameworkUtil.getBundle(TestEndpoint.class).getBundleContext();
 
        private RemoteEndpoint.Basic remote;
-       private CmsContext cmsContext;
+       private CmsEventBus cmsEventBus;
 
 //     private String topic = "cms";
 
        @OnOpen
        public void onOpen(Session session, @PathParam("topic") String topic) {
                if (bc != null) {
-                       cmsContext = bc.getService(bc.getServiceReference(CmsContext.class));
-                       cmsContext.addEventSubscriber(topic, this);
+                       cmsEventBus = bc.getService(bc.getServiceReference(CmsEventBus.class));
+                       cmsEventBus.addEventSubscriber(topic, this);
                }
                remote = session.getBasicRemote();
 
@@ -36,7 +36,7 @@ public class EventEndpoint implements CmsEventSubscriber {
 
        @OnClose
        public void onClose(@PathParam("topic") String topic) {
-               cmsContext.removeEventSubscriber(topic, this);
+               cmsEventBus.removeEventSubscriber(topic, this);
        }
 
        @Override
index 4927e6f1006ab5da0d7b9eac079bbc31052c7a73..3d4a57b9e7ff113d38095e2da4541516349e1a66 100644 (file)
@@ -3,24 +3,11 @@ package org.argeo.cms.jetty;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.Map;
 
-import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
-import javax.websocket.DeploymentException;
-import javax.websocket.server.ServerContainer;
-import javax.websocket.server.ServerEndpointConfig;
-import com.sun.net.httpserver.Authenticator;
-import com.sun.net.httpserver.HttpContext;
-
-import org.argeo.api.cms.CmsState;
-import org.argeo.cms.CmsDeployProperty;
-import org.argeo.cms.websocket.server.CmsWebSocketConfigurator;
-import org.argeo.cms.websocket.server.TestEndpoint;
+
 import org.eclipse.jetty.server.session.SessionHandler;
 import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer;
-import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer.Configurator;
 
 public class CmsJettyServer extends JettyHttpServer {
        private static final String CONTEXT_TEMPDIR = "javax.servlet.context.tempdir";
@@ -37,9 +24,7 @@ public class CmsJettyServer extends JettyHttpServer {
 
        // WebSocket
 //     private ServerContainer wsServerContainer;
-       private ServerEndpointConfig.Configurator wsEndpointConfigurator;
-
-       private CmsState cmsState;
+//     private ServerEndpointConfig.Configurator wsEndpointConfigurator;
 
 //     private Authenticator defaultAuthenticator;
 
@@ -74,7 +59,7 @@ public class CmsJettyServer extends JettyHttpServer {
        @Override
        protected void configureRootContextHandler(ServletContextHandler servletContextHandler) throws ServletException {
                addServlets(servletContextHandler);
-               enableWebSocket(servletContextHandler);
+//             enableWebSocket(servletContextHandler);
 
        }
 
@@ -85,39 +70,31 @@ public class CmsJettyServer extends JettyHttpServer {
 //             return httpContext;
 //     }
 
-       protected void enableWebSocket(ServletContextHandler servletContextHandler) {
-               String webSocketEnabled = getDeployProperty(CmsDeployProperty.WEBSOCKET_ENABLED);
-               // web socket
-               if (webSocketEnabled != null && webSocketEnabled.equals(Boolean.toString(true))) {
-//                     JavaxWebSocketServletContainerInitializer.configure(servletContextHandler, new Configurator() {
-//
-//                             @Override
-//                             public void accept(ServletContext servletContext, ServerContainer serverContainer)
-//                                             throws DeploymentException {
-////                                   wsServerContainer = serverContainer;
-//
-//                                     CmsWebSocketConfigurator wsEndpointConfigurator = new CmsWebSocketConfigurator();
-//
-//                                     ServerEndpointConfig config = ServerEndpointConfig.Builder
-//                                                     .create(TestEndpoint.class, "/ws/test/events/{topic}").configurator(wsEndpointConfigurator)
-//                                                     .build();
-//                                     try {
-//                                             serverContainer.addEndpoint(config);
-//                                     } catch (DeploymentException e) {
-//                                             throw new IllegalStateException("Cannot initalise the WebSocket server runtime.", e);
-//                                     }
-//                             }
-//                     });
-               }
-       }
-
-       protected String getDeployProperty(CmsDeployProperty deployProperty) {
-               return cmsState.getDeployProperty(deployProperty.getProperty());
-       }
-
-       public void setCmsState(CmsState cmsState) {
-               this.cmsState = cmsState;
-       }
+//     protected void enableWebSocket(ServletContextHandler servletContextHandler) {
+//             String webSocketEnabled = getDeployProperty(CmsDeployProperty.WEBSOCKET_ENABLED);
+//             // web socket
+//             if (webSocketEnabled != null && webSocketEnabled.equals(Boolean.toString(true))) {
+////                   JavaxWebSocketServletContainerInitializer.configure(servletContextHandler, new Configurator() {
+////
+////                           @Override
+////                           public void accept(ServletContext servletContext, ServerContainer serverContainer)
+////                                           throws DeploymentException {
+//////                                 wsServerContainer = serverContainer;
+////
+////                                   CmsWebSocketConfigurator wsEndpointConfigurator = new CmsWebSocketConfigurator();
+////
+////                                   ServerEndpointConfig config = ServerEndpointConfig.Builder
+////                                                   .create(TestEndpoint.class, "/ws/test/events/{topic}").configurator(wsEndpointConfigurator)
+////                                                   .build();
+////                                   try {
+////                                           serverContainer.addEndpoint(config);
+////                                   } catch (DeploymentException e) {
+////                                           throw new IllegalStateException("Cannot initalise the WebSocket server runtime.", e);
+////                                   }
+////                           }
+////                   });
+//             }
+//     }
 
 //     public void setDefaultAuthenticator(Authenticator defaultAuthenticator) {
 //             this.defaultAuthenticator = defaultAuthenticator;
index 0eaaadb5f321924cbdee914cf6902119c0a7b50c..e414f5f984f3053dfc54ac3278069d0314cf83a0 100644 (file)
@@ -10,6 +10,7 @@ import java.util.concurrent.ThreadPoolExecutor;
 import javax.servlet.ServletException;
 
 import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.CmsState;
 import org.argeo.cms.CmsDeployProperty;
 import org.argeo.util.http.HttpServerUtils;
 import org.eclipse.jetty.http.UriCompliance;
@@ -41,7 +42,8 @@ public class JettyHttpServer extends HttpsServer {
        protected ServerConnector httpConnector;
        protected ServerConnector httpsConnector;
 
-       private InetSocketAddress address;
+       private InetSocketAddress httpAddress;
+       private InetSocketAddress httpsAddress;
 
        private ThreadPoolExecutor executor;
 
@@ -53,6 +55,8 @@ public class JettyHttpServer extends HttpsServer {
 
        private boolean started;
 
+       private CmsState cmsState;
+
        @Override
        public void bind(InetSocketAddress addr, int backlog) throws IOException {
                throw new UnsupportedOperationException();
@@ -182,7 +186,7 @@ public class JettyHttpServer extends HttpsServer {
 
        @Override
        public InetSocketAddress getAddress() {
-               return address;
+               return httpAddress;
        }
 
        @Override
@@ -195,8 +199,6 @@ public class JettyHttpServer extends HttpsServer {
                return httpsConfigurator;
        }
 
-       
-       
        protected void configureConnectors() {
                HttpConfiguration httpConfiguration = new HttpConfiguration();
 
@@ -210,6 +212,10 @@ public class JettyHttpServer extends HttpsServer {
 
                // try {
                if (httpPortStr != null || httpsPortStr != null) {
+                       // TODO deal with hostname resolving taking too much time
+//                     String fallBackHostname = InetAddress.getLocalHost().getHostName();
+                       String fallBackHostname = cmsState != null ? cmsState.getHostname() : "::1";
+
                        boolean httpEnabled = httpPortStr != null;
                        // props.put(JettyHttpConstants.HTTP_ENABLED, httpEnabled);
                        boolean httpsEnabled = httpsPortStr != null;
@@ -226,6 +232,8 @@ public class JettyHttpServer extends HttpsServer {
                                httpConnector.setPort(httpPort);
                                httpConnector.setHost(httpHost);
                                httpConnector.setIdleTimeout(DEFAULT_IDLE_TIMEOUT);
+
+                               httpAddress = new InetSocketAddress(httpHost != null ? httpHost : fallBackHostname, httpPort);
                        }
 
                        if (httpsEnabled) {
@@ -261,6 +269,8 @@ public class JettyHttpServer extends HttpsServer {
                                int httpsPort = Integer.parseInt(httpsPortStr);
                                httpsConnector.setPort(httpsPort);
                                httpsConnector.setHost(httpHost);
+
+                               httpsAddress = new InetSocketAddress(httpHost != null ? httpHost : fallBackHostname, httpsPort);
                        }
 
                }
@@ -268,7 +278,8 @@ public class JettyHttpServer extends HttpsServer {
        }
 
        protected String getDeployProperty(CmsDeployProperty deployProperty) {
-               return System.getProperty(deployProperty.getProperty());
+               return cmsState != null ? cmsState.getDeployProperty(deployProperty.getProperty())
+                               : System.getProperty(deployProperty.getProperty());
        }
 
        private String httpPortsMsg() {
@@ -297,7 +308,10 @@ public class JettyHttpServer extends HttpsServer {
 
        }
 
-       
+       public void setCmsState(CmsState cmsState) {
+               this.cmsState = cmsState;
+       }
+
        public boolean isStarted() {
                return started;
        }
index dfcf7987ebdb675874675a2dccc7c0441b3af95a..d2413bd1ffcd6f9aecbd55da02946c8605b1b1ee 100644 (file)
@@ -6,6 +6,7 @@
       <provide interface="org.argeo.api.cms.CmsContext"/>
    </service>
    <reference bind="setCmsState" cardinality="1..1" interface="org.argeo.api.cms.CmsState" name="CmsState" policy="static"/>
+   <reference bind="setCmsEventBus" cardinality="1..1" interface="org.argeo.api.cms.CmsEventBus" name="CmsEventBus" policy="static"/>
    <reference bind="setUserAdmin" cardinality="1..1" interface="org.osgi.service.useradmin.UserAdmin" name="UserAdmin" policy="static"/>
    <reference bind="setUuidFactory" cardinality="1..1" interface="org.argeo.api.uuid.UuidFactory" name="UuidFactory" policy="static"/>
  </scr:component>
diff --git a/org.argeo.cms/OSGI-INF/cmsEventBus.xml b/org.argeo.cms/OSGI-INF/cmsEventBus.xml
new file mode 100644 (file)
index 0000000..6bb67ce
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="CMS Event Bus">
+   <implementation class="org.argeo.cms.internal.runtime.CmsEventBusImpl"/>
+   <service>
+      <provide interface="org.argeo.api.cms.CmsEventBus"/>
+   </service>
+</scr:component>
index 927a7555ac06f9bd8471982e18a77a92efcec893..ddc328c21cff20178e10af5c3c5a27037995e61d 100644 (file)
@@ -9,8 +9,9 @@ org.apache.xerces.jaxp;resolution:=optional,\
 
 Service-Component:\
 OSGI-INF/cmsOsgiLogger.xml,\
-OSGI-INF/cmsState.xml,\
 OSGI-INF/uuidFactory.xml,\
+OSGI-INF/cmsEventBus.xml,\
+OSGI-INF/cmsState.xml,\
 OSGI-INF/transactionManager.xml,\
 OSGI-INF/cmsUserAdmin.xml,\
 OSGI-INF/cmsUserManager.xml,\
index 960230a18734015c3458b908a7261a64e8d14290..6ca041a2a19c39c0db563da5d0bc0ab660494744 100644 (file)
@@ -1,6 +1,7 @@
 bin.includes = META-INF/,\
                .,\
                bin/,\
-               OSGI-INF/
+               OSGI-INF/,\
+               OSGI-INF/cmsEventBus.xml
 source.. = src/
 output.. = bin/
index 1f2ea73a75cebb04a3eb65ef0cbed5e7a3dd997e..757bf73bfc43513fab8c004aaa3eb363a005f22a 100644 (file)
@@ -14,7 +14,6 @@ import javax.security.auth.login.LoginException;
 import org.argeo.api.cms.CmsAuth;
 import org.argeo.api.cms.CmsLog;
 import org.argeo.api.cms.CmsSession;
-import org.argeo.cms.internal.http.CmsAuthenticator;
 import org.argeo.cms.internal.runtime.CmsContextImpl;
 import org.argeo.util.http.HttpHeader;
 import org.ietf.jgss.GSSContext;
@@ -148,7 +147,7 @@ public class RemoteAuthUtils {
        public static int askForWwwAuth(RemoteAuthResponse remoteAuthResponse, String realm, boolean forceBasic) {
                // response.setHeader(HttpUtils.HEADER_WWW_AUTHENTICATE, "basic
                // realm=\"" + httpAuthRealm + "\"");
-               if (SpnegoLoginModule.hasAcceptorCredentials() && !forceBasic)// SPNEGO
+               if (hasAcceptorCredentials() && !forceBasic)// SPNEGO
                        remoteAuthResponse.setHeader(HttpHeader.WWW_AUTHENTICATE.getName(), HttpHeader.NEGOTIATE);
                else
                        remoteAuthResponse.setHeader(HttpHeader.WWW_AUTHENTICATE.getName(),
@@ -165,4 +164,8 @@ public class RemoteAuthUtils {
                return 401;
        }
 
+       private static boolean hasAcceptorCredentials() {
+               return CmsContextImpl.getCmsContext().getAcceptorCredentials() != null;
+       }
+
 }
index 972d6a2458a74b2edaa9438b6c3acbbd5e0e69ed..956987d52ddde586d7b9ff8cf370e1608dc8b72a 100644 (file)
@@ -14,6 +14,7 @@ import javax.security.auth.spi.LoginModule;
 import javax.security.auth.x500.X500Principal;
 
 import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.internal.runtime.CmsContextImpl;
 import org.argeo.osgi.useradmin.OsUserUtils;
 import org.argeo.util.directory.ldap.IpaUtils;
 import org.argeo.util.naming.LdapAttrs;
@@ -54,13 +55,7 @@ public class SingleUserLoginModule implements LoginModule {
                        Object username = sharedState.get(CmsAuthUtils.SHARED_STATE_NAME);
                        if (username == null)
                                throw new LoginException("No username available");
-                       String hostname;
-                       try {
-                               hostname = InetAddress.getLocalHost().getHostName();
-                       } catch (UnknownHostException e) {
-                               log.warn("Using localhost as hostname", e);
-                               hostname = "localhost";
-                       }
+                       String hostname = CmsContextImpl.getCmsContext().getCmsState().getHostname();
                        String baseDn = ("." + hostname).replaceAll("\\.", ",dc=");
                        X500Principal principal = new X500Principal(LdapAttrs.uid + "=" + username + baseDn);
                        authorizationName = principal.getName();
@@ -74,8 +69,8 @@ public class SingleUserLoginModule implements LoginModule {
                        locale = Locale.getDefault();
                Authorization authorization = new SingleUserAuthorization(authorizationName);
                CmsAuthUtils.addAuthorization(subject, authorization);
-               
-               // Add standard Java OS login 
+
+               // Add standard Java OS login
                OsUserUtils.loginAsSystemUser(subject);
 
                // additional principals (must be after Authorization registration)
index dad0dad4be64a0600fd3d3298e6a3e5c8761efa4..e24e5b45b6a1f456c36753daf1e2c377c210bdf3 100644 (file)
@@ -122,7 +122,7 @@ public class SpnegoLoginModule implements LoginModule {
        private GSSContext checkToken(byte[] authToken) {
                GSSManager manager = GSSManager.getInstance();
                try {
-                       GSSContext gContext = manager.createContext(CmsContextImpl.getAcceptorCredentials());
+                       GSSContext gContext = manager.createContext(CmsContextImpl.getCmsContext().getAcceptorCredentials());
 
                        if (gContext == null) {
                                log.debug("SpnegoUserRealm: failed to establish GSSContext");
@@ -143,8 +143,4 @@ public class SpnegoLoginModule implements LoginModule {
 
        }
 
-       @Deprecated
-       public static boolean hasAcceptorCredentials() {
-               return CmsContextImpl.getAcceptorCredentials() != null;
-       }
 }
index af45b74e31c7d640cb53c9136abc1b6fc7608da2..c2d2ccf42d4cdd2814e6c4806d366bf9385d935d 100644 (file)
@@ -6,19 +6,15 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.TreeMap;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Flow;
-import java.util.concurrent.Flow.Subscription;
-import java.util.concurrent.SubmissionPublisher;
 
 import javax.security.auth.Subject;
 
 import org.argeo.api.cms.CmsContext;
 import org.argeo.api.cms.CmsDeployment;
-import org.argeo.api.cms.CmsEventSubscriber;
+import org.argeo.api.cms.CmsEventBus;
 import org.argeo.api.cms.CmsLog;
 import org.argeo.api.cms.CmsSession;
 import org.argeo.api.cms.CmsSessionId;
@@ -40,6 +36,7 @@ public class CmsContextImpl implements CmsContext {
        private CmsDeployment cmsDeployment;
        private UserAdmin userAdmin;
        private UuidFactory uuidFactory;
+       private CmsEventBus cmsEventBus;
 //     private ProvidedRepository contentRepository;
 
        // i18n
@@ -52,10 +49,6 @@ public class CmsContextImpl implements CmsContext {
        private Map<UUID, CmsSessionImpl> cmsSessionsByUuid = new HashMap<>();
        private Map<String, CmsSessionImpl> cmsSessionsByLocalId = new HashMap<>();
 
-       // CMS events
-       private Map<String, SubmissionPublisher<Map<String, Object>>> topics = new TreeMap<>();
-//     private IdentityHashMap<CmsEventSubscriber, List<CmsEventFlowSubscriber>> subscriptions = new IdentityHashMap<>();
-
        public void start() {
                List<String> codes = CmsStateImpl.getDeployProperties(cmsState, CmsDeployProperty.LOCALE);
                locales = getLocaleList(codes);
@@ -198,6 +191,11 @@ public class CmsContextImpl implements CmsContext {
                return defaultLocale;
        }
 
+       @Override
+       public UUID timeUUID() {
+               return uuidFactory.timeUUID();
+       }
+
        @Override
        public List<Locale> getLocales() {
                return locales;
@@ -212,11 +210,19 @@ public class CmsContextImpl implements CmsContext {
                return availableSince != null;
        }
 
-       @Override
        public CmsState getCmsState() {
                return cmsState;
        }
 
+       @Override
+       public CmsEventBus getCmsEventBus() {
+               return cmsEventBus;
+       }
+
+       public void setCmsEventBus(CmsEventBus cmsEventBus) {
+               this.cmsEventBus = cmsEventBus;
+       }
+
        /*
         * STATIC
         */
@@ -226,9 +232,9 @@ public class CmsContextImpl implements CmsContext {
        }
 
        /** Required by SPNEGO login module. */
-       public static GSSCredential getAcceptorCredentials() {
+       public GSSCredential getAcceptorCredentials() {
                // TODO find a cleaner way
-               return ((CmsUserAdmin) getInstance().userAdmin).getAcceptorCredentials();
+               return ((CmsUserAdmin) userAdmin).getAcceptorCredentials();
        }
 
        private static void setInstance(CmsContextImpl cmsContextImpl) {
@@ -240,12 +246,14 @@ public class CmsContextImpl implements CmsContext {
 //                     instance = null;
 //             }
 //             CmsContextImpl.class.notifyAll();
-               
+
                if (cmsContextImpl != null) {
                        if (instance.isDone())
                                throw new IllegalStateException("CMS Context is already set");
                        instance.complete(cmsContextImpl);
                } else {
+                       if (!instance.isDone())
+                               instance.cancel(true);
                        instance = new CompletableFuture<CmsContextImpl>();
                }
        }
@@ -316,86 +324,4 @@ public class CmsContextImpl implements CmsContext {
                return cmsSessionsByLocalId.get(localId);
        }
 
-       /*
-        * CMS Events
-        */
-       public void sendEvent(String topic, Map<String, Object> event) {
-               SubmissionPublisher<Map<String, Object>> publisher = topics.get(topic);
-               if (publisher == null)
-                       return; // no one is interested
-               publisher.submit(event);
-       }
-
-       public void addEventSubscriber(String topic, CmsEventSubscriber subscriber) {
-               synchronized (topics) {
-                       if (!topics.containsKey(topic))
-                               topics.put(topic, new SubmissionPublisher<>());
-               }
-               SubmissionPublisher<Map<String, Object>> publisher = topics.get(topic);
-               CmsEventFlowSubscriber flowSubscriber = new CmsEventFlowSubscriber(topic, subscriber);
-               publisher.subscribe(flowSubscriber);
-       }
-
-       public void removeEventSubscriber(String topic, CmsEventSubscriber subscriber) {
-               SubmissionPublisher<Map<String, Object>> publisher = topics.get(topic);
-               if (publisher == null) {
-                       log.error("There should be an event topic " + topic);
-                       return;
-               }
-               for (Flow.Subscriber<? super Map<String, Object>> flowSubscriber : publisher.getSubscribers()) {
-                       if (flowSubscriber instanceof CmsEventFlowSubscriber)
-                               ((CmsEventFlowSubscriber) flowSubscriber).unsubscribe();
-               }
-               synchronized (topics) {
-                       if (!publisher.hasSubscribers()) {
-                               publisher.close();
-                               topics.remove(topic);
-                       }
-               }
-       }
-
-       static class CmsEventFlowSubscriber implements Flow.Subscriber<Map<String, Object>> {
-               private String topic;
-               private CmsEventSubscriber eventSubscriber;
-
-               private Subscription subscription;
-
-               public CmsEventFlowSubscriber(String topic, CmsEventSubscriber eventSubscriber) {
-                       this.topic = topic;
-                       this.eventSubscriber = eventSubscriber;
-               }
-
-               @Override
-               public void onSubscribe(Subscription subscription) {
-                       this.subscription = subscription;
-                       subscription.request(Long.MAX_VALUE);
-               }
-
-               @Override
-               public void onNext(Map<String, Object> item) {
-                       eventSubscriber.onEvent(topic, item);
-
-               }
-
-               @Override
-               public void onError(Throwable throwable) {
-                       // TODO Auto-generated method stub
-
-               }
-
-               @Override
-               public void onComplete() {
-                       // TODO Auto-generated method stub
-
-               }
-
-               void unsubscribe() {
-                       if (subscription != null)
-                               subscription.cancel();
-                       else
-                               throw new IllegalStateException("No subscription to cancel");
-               }
-
-       }
-
 }
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsEventBusImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsEventBusImpl.java
new file mode 100644 (file)
index 0000000..7fca23c
--- /dev/null
@@ -0,0 +1,105 @@
+package org.argeo.cms.internal.runtime;
+
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.Flow;
+import java.util.concurrent.Flow.Subscription;
+import java.util.concurrent.SubmissionPublisher;
+
+import org.argeo.api.cms.CmsEventBus;
+import org.argeo.api.cms.CmsEventSubscriber;
+import org.argeo.api.cms.CmsLog;
+
+public class CmsEventBusImpl implements CmsEventBus {
+       private final CmsLog log = CmsLog.getLog(CmsEventBus.class);
+
+       // CMS events
+       private Map<String, SubmissionPublisher<Map<String, Object>>> topics = new TreeMap<>();
+//     private IdentityHashMap<CmsEventSubscriber, List<CmsEventFlowSubscriber>> subscriptions = new IdentityHashMap<>();
+
+       /*
+        * CMS Events
+        */
+       @Override
+       public void sendEvent(String topic, Map<String, Object> event) {
+               SubmissionPublisher<Map<String, Object>> publisher = topics.get(topic);
+               if (publisher == null)
+                       return; // no one is interested
+               publisher.submit(event);
+       }
+
+       @Override
+       public void addEventSubscriber(String topic, CmsEventSubscriber subscriber) {
+               synchronized (topics) {
+                       if (!topics.containsKey(topic))
+                               topics.put(topic, new SubmissionPublisher<>());
+               }
+               SubmissionPublisher<Map<String, Object>> publisher = topics.get(topic);
+               CmsEventFlowSubscriber flowSubscriber = new CmsEventFlowSubscriber(topic, subscriber);
+               publisher.subscribe(flowSubscriber);
+       }
+
+       @Override
+       public void removeEventSubscriber(String topic, CmsEventSubscriber subscriber) {
+               SubmissionPublisher<Map<String, Object>> publisher = topics.get(topic);
+               if (publisher == null) {
+                       log.error("There should be an event topic " + topic);
+                       return;
+               }
+               for (Flow.Subscriber<? super Map<String, Object>> flowSubscriber : publisher.getSubscribers()) {
+                       if (flowSubscriber instanceof CmsEventFlowSubscriber)
+                               ((CmsEventFlowSubscriber) flowSubscriber).unsubscribe();
+               }
+               synchronized (topics) {
+                       if (!publisher.hasSubscribers()) {
+                               publisher.close();
+                               topics.remove(topic);
+                       }
+               }
+       }
+
+       static class CmsEventFlowSubscriber implements Flow.Subscriber<Map<String, Object>> {
+               private String topic;
+               private CmsEventSubscriber eventSubscriber;
+
+               private Subscription subscription;
+
+               public CmsEventFlowSubscriber(String topic, CmsEventSubscriber eventSubscriber) {
+                       this.topic = topic;
+                       this.eventSubscriber = eventSubscriber;
+               }
+
+               @Override
+               public void onSubscribe(Subscription subscription) {
+                       this.subscription = subscription;
+                       subscription.request(1);
+               }
+
+               @Override
+               public void onNext(Map<String, Object> item) {
+                       eventSubscriber.onEvent(topic, item);
+                       subscription.request(1);
+               }
+
+               @Override
+               public void onError(Throwable throwable) {
+                       // TODO Auto-generated method stub
+
+               }
+
+               @Override
+               public void onComplete() {
+                       // TODO Auto-generated method stub
+
+               }
+
+               void unsubscribe() {
+                       if (subscription != null)
+                               subscription.cancel();
+                       else
+                               throw new IllegalStateException("No subscription to cancel");
+               }
+
+       }
+
+}
index 902fe793b7a28ea98b377d65fb717c19d41f1274..c9109c8561fb2500485998c033cbb4ac4d58a419 100644 (file)
@@ -2,7 +2,9 @@ package org.argeo.cms.internal.runtime;
 
 import java.io.IOException;
 import java.io.Reader;
+import java.net.InetAddress;
 import java.net.URL;
+import java.net.UnknownHostException;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -21,6 +23,11 @@ import java.util.Objects;
 import java.util.Set;
 import java.util.StringJoiner;
 import java.util.UUID;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 import javax.security.auth.login.Configuration;
 
@@ -43,7 +50,7 @@ public class CmsStateImpl implements CmsState {
 
        private UUID uuid;
 //     private final boolean cleanState;
-//     private String hostname;
+       private String hostname;
 
        private UuidFactory uuidFactory;
 
@@ -96,11 +103,27 @@ public class CmsStateImpl implements CmsState {
 //                     this.uuid = UUID.fromString(stateUuidStr);
                        this.uuid = uuidFactory.timeUUID();
 //             this.cleanState = stateUuid.equals(frameworkUuid);
-//                     try {
-//                             this.hostname = InetAddress.getLocalHost().getHostName();
-//                     } catch (UnknownHostException e) {
-//                             log.error("Cannot set hostname: " + e);
-//                     }
+
+                       // hostname
+                       this.hostname = getDeployProperty(CmsDeployProperty.HOST);
+                       // TODO verify we have access to the IP address
+                       if (hostname == null) {
+                               final String LOCALHOST_IP = "::1";
+                               ForkJoinTask<String> hostnameFJT = ForkJoinPool.commonPool().submit(() -> {
+                                       try {
+                                               String hostname = InetAddress.getLocalHost().getHostName();
+                                               return hostname;
+                                       } catch (UnknownHostException e) {
+                                               throw new IllegalStateException("Cannot get local hostname", e);
+                                       }
+                               });
+                               try {
+                                       this.hostname = hostnameFJT.get(5, TimeUnit.SECONDS);
+                               } catch (InterruptedException | ExecutionException | TimeoutException e) {
+                                       this.hostname = LOCALHOST_IP;
+                                       log.warn("Could not get local hostname, using " + this.hostname);
+                               }
+                       }
 
                        availableSince = System.currentTimeMillis();
                        if (log.isDebugEnabled()) {
@@ -373,6 +396,10 @@ public class CmsStateImpl implements CmsState {
                this.uuidFactory = uuidFactory;
        }
 
+       public String getHostname() {
+               return hostname;
+       }
+
        /**
         * Called before node initialisation, in order populate OSGi instance are with
         * some files (typically LDIF, etc).
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/AbstractSwtCmsView.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/AbstractSwtCmsView.java
new file mode 100644 (file)
index 0000000..2059803
--- /dev/null
@@ -0,0 +1,110 @@
+package org.argeo.cms.swt;
+
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+
+import org.argeo.api.cms.CmsEventBus;
+import org.argeo.api.cms.ux.CmsImageManager;
+import org.argeo.api.cms.ux.CmsUi;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.api.cms.ux.UxContext;
+import org.argeo.cms.auth.CurrentUser;
+import org.eclipse.swt.widgets.Display;
+
+public abstract class AbstractSwtCmsView implements CmsView {
+       protected final String uiName;
+
+       protected LoginContext loginContext;
+       protected String state;
+       protected Throwable exception;
+       protected UxContext uxContext;
+       protected CmsImageManager imageManager;
+
+       protected Display display;
+       protected CmsUi ui;
+
+       protected String uid;
+
+       public AbstractSwtCmsView(String uiName) {
+               this.uiName = uiName;
+       }
+
+       public abstract CmsEventBus getCmsEventBus();
+
+       @Override
+       public void sendEvent(String topic, Map<String, Object> properties) {
+               if (properties == null)
+                       properties = new HashMap<>();
+               if (properties.containsKey(CMS_VIEW_UID_PROPERTY) && !properties.get(CMS_VIEW_UID_PROPERTY).equals(uid))
+                       throw new IllegalArgumentException("Property " + CMS_VIEW_UID_PROPERTY + " is set to another CMS view uid ("
+                                       + properties.get(CMS_VIEW_UID_PROPERTY) + ") then " + uid);
+               properties.put(CMS_VIEW_UID_PROPERTY, uid);
+               getCmsEventBus().sendEvent(topic, properties);
+       }
+
+       public <T> T doAs(PrivilegedAction<T> action) {
+               try {
+                       CompletableFuture<T> result = new CompletableFuture<>();
+                       Runnable toDo = () -> {
+                               T res = Subject.doAs(getSubject(), action);
+                               result.complete(res);
+                       };
+                       if (Thread.currentThread() == display.getThread())
+                               toDo.run();
+                       else
+                               display.syncExec(toDo);
+                       return result.get();
+               } catch (InterruptedException | ExecutionException e) {
+                       throw new IllegalStateException("Cannot execute action ins CMS view " + uid, e);
+               }
+       }
+
+       @Override
+       public UxContext getUxContext() {
+               return uxContext;
+       }
+
+       @Override
+       public String getUid() {
+               return uid;
+       }
+
+       @Override
+       public CmsImageManager<?, ?> getImageManager() {
+               return imageManager;
+       }
+
+       @Override
+       public boolean isAnonymous() {
+               return CurrentUser.isAnonymous(getSubject());
+       }
+
+       protected Subject getSubject() {
+               return loginContext.getSubject();
+       }
+
+       @Override
+       public Object getData(String key) {
+               if (ui != null) {
+                       return ui.getData(key);
+               } else {
+                       throw new IllegalStateException("UI is not initialized");
+               }
+       }
+
+       @Override
+       public void setData(String key, Object value) {
+               if (ui != null) {
+                       ui.setData(key, value);
+               } else {
+                       throw new IllegalStateException("UI is not initialized");
+               }
+       }
+
+}
index 4af0c6c1ac79f20e19596b12056cbe4e59aa4f01..b713c19feb1684e1d947d8009b7fa44a9920d75b 100644 (file)
@@ -279,7 +279,7 @@ public class CmsLogin implements CmsStyles, CallbackHandler {
                                loginContext = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, subject, this);
                        loginContext.login();
                        cmsView.authChange(loginContext);
-                       cmsContext.sendEvent("cms", Collections.singletonMap("msg", "New login"));
+                       cmsContext.getCmsEventBus().sendEvent("cms", Collections.singletonMap("msg", "New login"));
                        return true;
                } catch (LoginException e) {
                        if (log.isTraceEnabled())
index aa7e0adca1ea0d79a7384d047453e87a59281f3c..f5edebcdab453ed17a5135cc031fc54dda551e9e 100644 (file)
@@ -2,5 +2,5 @@
 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="CMS Web App Factory">
    <implementation class="org.argeo.cms.web.osgi.CmsWebAppFactory"/>
    <reference bind="addCmsApp" cardinality="0..n" interface="org.argeo.api.cms.CmsApp" name="CmsApp" policy="dynamic" unbind="removeCmsApp"/>
-   <reference bind="setEventAdmin" cardinality="1..1" interface="org.osgi.service.event.EventAdmin" name="EventAdmin" policy="static"/>
+   <reference bind="setCmsEventBus" cardinality="1..1" interface="org.argeo.api.cms.CmsEventBus" name="CmsEventBus" policy="static"/>
 </scr:component>
index fec9e2485d6bdc64c3a065574994ef325916e495..31555d168d10b1c2f8f1a44984014f20c458230a 100644 (file)
@@ -7,6 +7,7 @@ import java.util.Set;
 
 import org.argeo.api.cms.CmsApp;
 import org.argeo.api.cms.CmsAppListener;
+import org.argeo.api.cms.CmsEventBus;
 import org.argeo.api.cms.CmsLog;
 import org.argeo.api.cms.ux.CmsTheme;
 import org.argeo.api.cms.ux.CmsView;
@@ -21,7 +22,6 @@ import org.eclipse.rap.rwt.client.WebClient;
 import org.eclipse.swt.widgets.Display;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.event.EventAdmin;
 
 /** An RWT web app integrating with a {@link CmsApp}. */
 public class CmsWebApp implements ApplicationConfiguration, ExceptionHandler, CmsAppListener {
@@ -29,8 +29,8 @@ public class CmsWebApp implements ApplicationConfiguration, ExceptionHandler, Cm
 
        private BundleContext bundleContext;
        private CmsApp cmsApp;
-//     private String cmsAppId;
-       private EventAdmin eventAdmin;
+
+       private CmsEventBus cmsEventBus;
 
        private ServiceRegistration<ApplicationConfiguration> rwtAppReg;
 
@@ -103,7 +103,6 @@ public class CmsWebApp implements ApplicationConfiguration, ExceptionHandler, Cm
                        String entryPointName = !uiName.equals("") ? "/" + uiName : "/";
                        application.addEntryPoint(entryPointName, () -> {
                                CmsWebEntryPoint entryPoint = new CmsWebEntryPoint(this, uiName);
-                               entryPoint.setEventAdmin(eventAdmin);
                                return entryPoint;
                        }, properties);
                        if (log.isDebugEnabled())
@@ -156,8 +155,12 @@ public class CmsWebApp implements ApplicationConfiguration, ExceptionHandler, Cm
                }
        }
 
-       public void setEventAdmin(EventAdmin eventAdmin) {
-               this.eventAdmin = eventAdmin;
+       public void setCmsEventBus(CmsEventBus cmsEventBus) {
+               this.cmsEventBus = cmsEventBus;
+       }
+
+       public CmsEventBus getCmsEventBus() {
+               return cmsEventBus;
        }
 
        public void setContextName(String contextName) {
index 477a6569dc5ed4e4d193e24be7493185dc67beaa..3c894d158b84101a31e1d2a92aa805d251efd3e8 100644 (file)
@@ -3,9 +3,7 @@ package org.argeo.cms.web;
 import static org.eclipse.rap.rwt.internal.service.ContextProvider.getApplicationContext;
 
 import java.security.PrivilegedAction;
-import java.util.HashMap;
 import java.util.Locale;
-import java.util.Map;
 import java.util.UUID;
 
 import javax.security.auth.Subject;
@@ -14,17 +12,17 @@ import javax.security.auth.login.LoginException;
 
 import org.argeo.api.cms.CmsApp;
 import org.argeo.api.cms.CmsAuth;
+import org.argeo.api.cms.CmsEventBus;
 import org.argeo.api.cms.CmsLog;
 import org.argeo.api.cms.CmsSession;
 import org.argeo.api.cms.ux.CmsImageManager;
-import org.argeo.api.cms.ux.CmsUi;
 import org.argeo.api.cms.ux.CmsView;
-import org.argeo.api.cms.ux.UxContext;
 import org.argeo.cms.LocaleUtils;
 import org.argeo.cms.auth.CurrentUser;
 import org.argeo.cms.auth.RemoteAuthCallbackHandler;
 import org.argeo.cms.servlet.ServletHttpRequest;
 import org.argeo.cms.servlet.ServletHttpResponse;
+import org.argeo.cms.swt.AbstractSwtCmsView;
 import org.argeo.cms.swt.CmsSwtUtils;
 import org.argeo.cms.swt.SimpleSwtUxContext;
 import org.argeo.cms.swt.acr.AcrSwtImageManager;
@@ -41,30 +39,15 @@ import org.eclipse.swt.SWTError;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Shell;
-import org.osgi.service.event.Event;
-import org.osgi.service.event.EventAdmin;
+import org.eclipse.swt.widgets.Widget;
 
 /** The {@link CmsView} for a {@link CmsWebApp}. */
 @SuppressWarnings("restriction")
-public class CmsWebEntryPoint implements EntryPoint, CmsView, BrowserNavigationListener {
+public class CmsWebEntryPoint extends AbstractSwtCmsView implements EntryPoint, CmsView, BrowserNavigationListener {
        private static final long serialVersionUID = 7733510691684570402L;
        private final static CmsLog log = CmsLog.getLog(CmsWebEntryPoint.class);
 
-       private EventAdmin eventAdmin;
-
        private final CmsWebApp cmsWebApp;
-       private final String uiName;
-
-       private LoginContext loginContext;
-       private String state;
-       private Throwable exception;
-       private UxContext uxContext;
-       private CmsImageManager imageManager;
-
-       private Display display;
-       private CmsUi ui;
-
-       private String uid;
 
        // Client services
        // private final JavaScriptExecutor jsExecutor;
@@ -74,10 +57,10 @@ public class CmsWebEntryPoint implements EntryPoint, CmsView, BrowserNavigationL
        private boolean multipleShells = false;
 
        public CmsWebEntryPoint(CmsWebApp cmsWebApp, String uiName) {
+               super(uiName);
                assert cmsWebApp != null;
                assert uiName != null;
                this.cmsWebApp = cmsWebApp;
-               this.uiName = uiName;
                uid = UUID.randomUUID().toString();
 
                // Initial login
@@ -136,19 +119,6 @@ public class CmsWebEntryPoint implements EntryPoint, CmsView, BrowserNavigationL
                });
        }
 
-       protected Subject getSubject() {
-               return loginContext.getSubject();
-       }
-
-       public <T> T doAs(PrivilegedAction<T> action) {
-               return Subject.doAs(getSubject(), action);
-       }
-
-       @Override
-       public boolean isAnonymous() {
-               return CurrentUser.isAnonymous(getSubject());
-       }
-
        @Override
        public synchronized void logout() {
                if (loginContext == null)
@@ -220,16 +190,6 @@ public class CmsWebEntryPoint implements EntryPoint, CmsView, BrowserNavigationL
                return null;
        }
 
-       @Override
-       public UxContext getUxContext() {
-               return uxContext;
-       }
-
-       @Override
-       public String getUid() {
-               return uid;
-       }
-
        @Override
        public void navigateTo(String state) {
                exception = null;
@@ -251,14 +211,8 @@ public class CmsWebEntryPoint implements EntryPoint, CmsView, BrowserNavigationL
        }
 
        @Override
-       public void sendEvent(String topic, Map<String, Object> properties) {
-               if (properties == null)
-                       properties = new HashMap<>();
-               if (properties.containsKey(CMS_VIEW_UID_PROPERTY) && !properties.get(CMS_VIEW_UID_PROPERTY).equals(uid))
-                       throw new IllegalArgumentException("Property " + CMS_VIEW_UID_PROPERTY + " is set to another CMS view uid ("
-                                       + properties.get(CMS_VIEW_UID_PROPERTY) + ") then " + uid);
-               properties.put(CMS_VIEW_UID_PROPERTY, uid);
-               eventAdmin.sendEvent(new Event(topic, properties));
+       public CmsEventBus getCmsEventBus() {
+               return cmsWebApp.getCmsEventBus();
        }
 
        @Override
@@ -274,24 +228,6 @@ public class CmsWebEntryPoint implements EntryPoint, CmsView, BrowserNavigationL
                return cmsSession;
        }
 
-       @Override
-       public Object getData(String key) {
-               if (ui != null) {
-                       return ui.getData(key);
-               } else {
-                       throw new IllegalStateException("UI is not initialized");
-               }
-       }
-
-       @Override
-       public void setData(String key, Object value) {
-               if (ui != null) {
-                       ui.setData(key, value);
-               } else {
-                       throw new IllegalStateException("UI is not initialized");
-               }
-       }
-
        /*
         * EntryPoint IMPLEMENTATION
         */
@@ -360,9 +296,4 @@ public class CmsWebEntryPoint implements EntryPoint, CmsView, BrowserNavigationL
                }
                return shell;
        }
-
-       public void setEventAdmin(EventAdmin eventAdmin) {
-               this.eventAdmin = eventAdmin;
-       }
-
 }
index 19b9fe80dcb0b9a8c5972fbfee97180f958755cd..83a83e2adb8b3d8ce3efa1ebb738f65dc6b39fb5 100644 (file)
@@ -6,17 +6,17 @@ import java.util.Hashtable;
 import java.util.Map;
 
 import org.argeo.api.cms.CmsApp;
+import org.argeo.api.cms.CmsEventBus;
 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 CmsEventBus cmsEventBus;
 
        private Map<String, CmsWebApp> registrations = Collections.synchronizedMap(new HashMap<>());
 
@@ -24,7 +24,7 @@ public class CmsWebAppFactory {
                String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY);
                if (contextName != null) {
                        CmsWebApp cmsWebApp = new CmsWebApp();
-                       cmsWebApp.setEventAdmin(eventAdmin);
+                       cmsWebApp.setCmsEventBus(cmsEventBus);
                        cmsWebApp.setCmsApp(cmsApp);
                        Hashtable<String, String> serviceProperties = new Hashtable<>();
                        if (!contextName.equals(""))
@@ -47,8 +47,9 @@ public class CmsWebAppFactory {
                }
        }
 
-       public void setEventAdmin(EventAdmin eventAdmin) {
-               this.eventAdmin = eventAdmin;
+       public void setCmsEventBus(CmsEventBus cmsEventBus) {
+               this.cmsEventBus = cmsEventBus;
        }
 
+
 }
index 187f5b03ed4d377f56ed275917ced31c715077ed..3fe2935afb473134521cf6e506f010279a57acb5 100644 (file)
@@ -82,7 +82,7 @@ public class CmsCli extends CommandsCli {
                                protected void postActivation(ComponentRegister register) {
                                        if (ui) {
                                                Component<? extends CmsUserApp> cmsAppC = register.find(CmsUserApp.class, null).first();
-                                               CmsRcpDisplayFactory.openCmsApp(null, cmsAppC.get(), "data", (e) -> {
+                                               CmsRcpDisplayFactory.openCmsApp(cmsAppC.get(), "data", (e) -> {
                                                        // asynchronous in order to avoid deadlock in UI thread
                                                        ForkJoinPool.commonPool().execute(() -> stop());
                                                });
index a1f0b3a2a1bfde8b4c21b1bffd4aef12fc80ebe3..fe0bc64dbe7bab4f29efcb99f52c94dbf0780c25 100644 (file)
@@ -2,6 +2,5 @@
 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="CMS RCP Servlet Factory">
    <implementation class="org.argeo.cms.ui.rcp.servlet.CmsRcpServletFactory"/>
    <reference bind="addCmsApp" cardinality="0..n" interface="org.argeo.api.cms.CmsApp" name="CmsApp" policy="dynamic" unbind="removeCmsApp"/>
-   <reference bind="setEventAdmin" cardinality="1..1" interface="org.osgi.service.event.EventAdmin" name="EventAdmin" policy="static"/>
-   <reference bind="setHttpService" cardinality="1..1" interface="org.osgi.service.http.HttpService" name="HttpService" policy="static"/>
+   <reference bind="setHttpServer" cardinality="1..1" interface="com.sun.net.httpserver.HttpServer" name="HttpServer" policy="static"/>
 </scr:component>
index e25a9f71149b66d0aaf2168e4f0fe31e936e8e92..3cf243851cb2bd66483d381242b4ebc01ec7e924 100644 (file)
@@ -3,7 +3,6 @@ package org.argeo.cms.ui.rcp;
 import java.io.IOException;
 import java.io.InputStream;
 import java.security.PrivilegedAction;
-import java.util.HashMap;
 import java.util.Map;
 import java.util.UUID;
 
@@ -13,13 +12,12 @@ import javax.security.auth.login.LoginException;
 
 import org.argeo.api.cms.CmsApp;
 import org.argeo.api.cms.CmsAuth;
+import org.argeo.api.cms.CmsEventBus;
 import org.argeo.api.cms.CmsLog;
 import org.argeo.api.cms.CmsSession;
-import org.argeo.api.cms.ux.CmsImageManager;
 import org.argeo.api.cms.ux.CmsTheme;
-import org.argeo.api.cms.ux.CmsUi;
 import org.argeo.api.cms.ux.CmsView;
-import org.argeo.api.cms.ux.UxContext;
+import org.argeo.cms.swt.AbstractSwtCmsView;
 import org.argeo.cms.swt.CmsSwtUtils;
 import org.eclipse.e4.ui.css.core.engine.CSSEngine;
 import org.eclipse.e4.ui.css.core.engine.CSSErrorHandler;
@@ -27,39 +25,24 @@ import org.eclipse.e4.ui.css.swt.engine.CSSSWTEngineImpl;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Shell;
-import org.osgi.service.event.Event;
-import org.osgi.service.event.EventAdmin;
 
 /** Runs a {@link CmsApp} as an SWT desktop application. */
 @SuppressWarnings("restriction")
-public class CmsRcpApp implements CmsView {
+public class CmsRcpApp extends AbstractSwtCmsView implements CmsView {
        private final static CmsLog log = CmsLog.getLog(CmsRcpApp.class);
 
-       // private BundleContext bundleContext =
-       // FrameworkUtil.getBundle(CmsRcpApp.class).getBundleContext();
-
        private Shell shell;
        private CmsApp cmsApp;
 
-       // CMS View
-       private String uid;
-       private LoginContext loginContext;
-
-       private EventAdmin eventAdmin;
-
        private CSSEngine cssEngine;
 
-       private CmsUi ui;
-       // TODO make it configurable
-       private String uiName = "desktop";
-
        public CmsRcpApp(String uiName) {
+               super(uiName);
                uid = UUID.randomUUID().toString();
-               this.uiName = uiName;
        }
 
        public void initRcpApp() {
-               Display display = Display.getCurrent();
+               display = Display.getCurrent();
                shell = new Shell(display);
                shell.setText("Argeo CMS");
                Composite parent = shell;
@@ -115,16 +98,6 @@ public class CmsRcpApp implements CmsView {
         * CMS VIEW
         */
 
-       @Override
-       public String getUid() {
-               return uid;
-       }
-
-       @Override
-       public UxContext getUxContext() {
-               throw new UnsupportedOperationException();
-       }
-
        @Override
        public void navigateTo(String state) {
                throw new UnsupportedOperationException();
@@ -149,35 +122,12 @@ public class CmsRcpApp implements CmsView {
                log.error("Unexpected exception in CMS RCP", e);
        }
 
-       @Override
-       public CmsImageManager getImageManager() {
-               throw new UnsupportedOperationException();
-       }
-
        @Override
        public CmsSession getCmsSession() {
                CmsSession cmsSession = cmsApp.getCmsContext().getCmsSession(getSubject());
                return cmsSession;
        }
 
-       @Override
-       public Object getData(String key) {
-               if (ui != null) {
-                       return ui.getData(key);
-               } else {
-                       throw new IllegalStateException("UI is not initialized");
-               }
-       }
-
-       @Override
-       public void setData(String key, Object value) {
-               if (ui != null) {
-                       ui.setData(key, value);
-               } else {
-                       throw new IllegalStateException("UI is not initialized");
-               }
-       }
-
        @Override
        public boolean isAnonymous() {
                return false;
@@ -189,29 +139,15 @@ public class CmsRcpApp implements CmsView {
                        cssEngine.applyStyles(node, true);
        }
 
-       @Override
-       public void sendEvent(String topic, Map<String, Object> properties) {
-               if (properties == null)
-                       properties = new HashMap<>();
-               if (properties.containsKey(CMS_VIEW_UID_PROPERTY) && !properties.get(CMS_VIEW_UID_PROPERTY).equals(uid))
-                       throw new IllegalArgumentException("Property " + CMS_VIEW_UID_PROPERTY + " is set to another CMS view uid ("
-                                       + properties.get(CMS_VIEW_UID_PROPERTY) + ") then " + uid);
-               properties.put(CMS_VIEW_UID_PROPERTY, uid);
-               eventAdmin.sendEvent(new Event(topic, properties));
-       }
-
-       public <T> T doAs(PrivilegedAction<T> action) {
-               return Subject.doAs(getSubject(), action);
-       }
-
-       protected Subject getSubject() {
-               return loginContext.getSubject();
-       }
-
        public Shell getShell() {
                return shell;
        }
 
+       @Override
+       public CmsEventBus getCmsEventBus() {
+               return cmsApp.getCmsContext().getCmsEventBus();
+       }
+
        /*
         * DEPENDENCY INJECTION
         */
@@ -223,8 +159,4 @@ public class CmsRcpApp implements CmsView {
                this.cmsApp = null;
        }
 
-       public void setEventAdmin(EventAdmin eventAdmin) {
-               this.eventAdmin = eventAdmin;
-       }
-
 }
index ec471c0216785167044b6395189537a119175aaa..950acceceb1f0edff1c764143e62b1b63babe3e9 100644 (file)
@@ -4,9 +4,8 @@ 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;
 import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.widgets.Display;
 
 /** Creates the SWT {@link Display} in a dedicated thread. */
 public class CmsRcpDisplayFactory {
@@ -72,11 +71,9 @@ public class CmsRcpDisplayFactory {
                return display;
        }
 
-       public static void openCmsApp(EventAdmin eventAdmin, CmsApp cmsApp, String uiName,
-                       DisposeListener disposeListener) {
+       public static void openCmsApp(CmsApp cmsApp, String uiName, DisposeListener disposeListener) {
                CmsRcpDisplayFactory.getDisplay().syncExec(() -> {
                        CmsRcpApp cmsRcpApp = new CmsRcpApp(uiName);
-                       cmsRcpApp.setEventAdmin(eventAdmin);
                        cmsRcpApp.setCmsApp(cmsApp, null);
                        cmsRcpApp.initRcpApp();
                        if (disposeListener != null)
index f8aecd39b996a688daec78bfc1af8de00088439b..8579527c1f5057c5c1cdc7de18ecc6aace2464b5 100644 (file)
@@ -12,7 +12,6 @@ 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 {
@@ -20,20 +19,17 @@ public class CmsRcpServlet extends HttpServlet {
        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);
+       public CmsRcpServlet(CmsApp cmsApp) {
                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 != null ? path.substring(path.lastIndexOf('/') + 1) : "";
-               CmsRcpDisplayFactory.openCmsApp(eventAdmin, cmsApp, uiName, null);
+               CmsRcpDisplayFactory.openCmsApp(cmsApp, uiName, null);
                logger.log(Level.DEBUG, "Opened RCP UI  " + uiName + " of  CMS App " + req.getServletPath());
        }
 
index 7c24f87d9b02964098f7320e3ce2850c6b3f5465..09b1e41b4f82b9aa5f4f4f9e03c593f7e7e1f39b 100644 (file)
@@ -1,5 +1,7 @@
 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;
@@ -9,31 +11,22 @@ import java.net.URI;
 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;
+
+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 BundleContext bundleContext = FrameworkUtil.getBundle(CmsRcpServletFactory.class).getBundleContext();
-
-       private CompletableFuture<EventAdmin> eventAdmin = new CompletableFuture<>();
-
-       private Map<String, ServiceRegistration<Servlet>> registrations = Collections.synchronizedMap(new HashMap<>());
+       private HttpServer httpServer;
+//     private BundleContext bundleContext = FrameworkUtil.getBundle(CmsRcpServletFactory.class).getBundleContext();
+//
+//     private Map<String, ServiceRegistration<Servlet>> registrations = Collections.synchronizedMap(new HashMap<>());
 
        public void init() {
 
@@ -51,33 +44,40 @@ public class CmsRcpServletFactory {
        }
 
        public void addCmsApp(CmsApp cmsApp, Map<String, String> properties) {
-               String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY);
+               final String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY);
                if (contextName != null) {
-                       eventAdmin.thenAccept((eventAdmin) -> {
-                               CmsRcpServlet servlet = new CmsRcpServlet(eventAdmin, cmsApp);
-                               Hashtable<String, String> serviceProperties = new Hashtable<>();
-                               serviceProperties.put("osgi.http.whiteboard.servlet.pattern", "/" + contextName + "/*");
-                               ServiceRegistration<Servlet> sr = bundleContext.registerService(Servlet.class, servlet,
-                                               serviceProperties);
-                               registrations.put(contextName, sr);
+                       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);
+                               }
                        });
+                       logger.log(Level.DEBUG, "Registered RCP CMS APP /" + contextName);
+//                     CmsRcpServlet servlet = new CmsRcpServlet(cmsApp);
+//                     Hashtable<String, String> serviceProperties = new Hashtable<>();
+//                     serviceProperties.put("osgi.http.whiteboard.servlet.pattern", "/" + contextName + "/*");
+//                     ServiceRegistration<Servlet> sr = bundleContext.registerService(Servlet.class, servlet, serviceProperties);
+//                     registrations.put(contextName, sr);
                }
        }
 
        public void removeCmsApp(CmsApp cmsApp, Map<String, String> properties) {
                String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY);
                if (contextName != null) {
-                       ServiceRegistration<Servlet> sr = registrations.get(contextName);
-                       sr.unregister();
+                       httpServer.removeContext("/" + contextName);
+//                     ServiceRegistration<Servlet> sr = registrations.get(contextName);
+//                     sr.unregister();
                }
        }
 
-       public void setEventAdmin(EventAdmin eventAdmin) {
-               this.eventAdmin.complete(eventAdmin);
-       }
-
-       public void setHttpService(HttpService httpService, Map<String, Object> properties) {
-               Integer httpPort = Integer.parseInt(properties.get("http.port").toString());
+       public void setHttpServer(HttpServer httpServer) {
+               this.httpServer = httpServer;
+               Integer httpPort = httpServer.getAddress().getPort();
                String baseUrl = "http://localhost:" + httpPort + "/";
                Path runFile = CmsRcpDisplayFactory.getUrlRunFile();
                try {
@@ -99,7 +99,7 @@ public class CmsRcpServletFactory {
                } catch (IOException e) {
                        throw new RuntimeException("Cannot write run file to " + runFile, e);
                }
-               logger.log(Level.DEBUG, "RCP available under " + baseUrl + ", written to " + runFile);
+               logger.log(DEBUG, "RCP available under " + baseUrl + ", written to " + runFile);
        }
 
        protected boolean isPortAvailable(int port) {