Improve UI event support
authorMathieu Baudier <mbaudier@argeo.org>
Sun, 24 Jul 2022 11:21:49 +0000 (13:21 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Sun, 24 Jul 2022 11:21:49 +0000 (13:21 +0200)
org.argeo.api.cms/src/org/argeo/api/cms/CmsApp.java
org.argeo.api.cms/src/org/argeo/api/cms/ux/CmsView.java
org.argeo.cms/src/org/argeo/cms/LocaleUtils.java
org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsEventBusImpl.java
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/AbstractSwtCmsView.java
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/ChangePasswordDialog.java
swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java

index 745e5a4000b0d5d95e4c8673dc19601171c310d3..b180fff75b67593772f8482c1489c2fad69fc9f7 100644 (file)
@@ -1,12 +1,13 @@
 package org.argeo.api.cms;
 
+import java.util.Map;
 import java.util.Set;
 
 import org.argeo.api.cms.ux.CmsTheme;
 import org.argeo.api.cms.ux.CmsUi;
 
 /** An extensible user interface base on the CMS backend. */
-public interface CmsApp {
+public interface CmsApp extends CmsEventSubscriber {
        /**
         * If {@link CmsUi#setData(String, Object)} is set with this property, it
         * indicates a different UI (typically with another theming. The {@link CmsApp}
@@ -35,4 +36,10 @@ public interface CmsApp {
        void removeCmsAppListener(CmsAppListener listener);
 
        CmsContext getCmsContext();
+
+       @Override
+       default void onEvent(String topic, Map<String, Object> properties) {
+       }
+       
+       
 }
index 45629fba5bdd387947f0236b211a366783a3953c..15b6a5dc7aac338a7180f748fa404c31f4f28bb0 100644 (file)
@@ -1,8 +1,9 @@
 package org.argeo.api.cms.ux;
 
-import java.security.PrivilegedAction;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
 
 import javax.security.auth.login.LoginContext;
 
@@ -56,20 +57,14 @@ public interface CmsView {
 
        }
 
-       default <T> T doAs(PrivilegedAction<T> action) {
-               throw new UnsupportedOperationException();
-       }
-
-       default Void runAs(Runnable runnable) {
-               return doAs(new PrivilegedAction<Void>() {
+       /**
+        * Make sure that this action is executed with the proper subject and in a
+        * proper thread.
+        */
+       <T> T doAs(Callable<T> action);
 
-                       @Override
-                       public Void run() {
-                               if (runnable != null)
-                                       runnable.run();
-                               return null;
-                       }
-               });
+       default void runAs(Runnable runnable) {
+               doAs(Executors.callable(runnable));
        }
 
        default void stateChanged(String state, String title) {
index 415a0954dff41d4526e39ccf5b8413c3b7573b79..4bfda139d686345f261dd9675de1c500b9cb2eda 100644 (file)
@@ -59,9 +59,10 @@ public class LocaleUtils {
        /** Where the search for a message is actually performed. */
        public static String local(String key, Locale locale, String resource, ClassLoader classLoader) {
                ResourceBundle rb = ResourceBundle.getBundle(resource, locale, classLoader);
-               assert key.length() > 2;
-               if (isLocaleKey(key))
+               if (isLocaleKey(key)) {
+                       assert key.length() > 1;
                        key = key.substring(1);
+               }
                if (rb.containsKey(key))
                        return rb.getString(key);
                else // for simple cases, the key will actually be the English word
index 7fca23c991ee75fb3cce34bd7b85f3d216ed5be7..eaa63756dc05e61800ffdb8668bf4ba200907ff3 100644 (file)
@@ -72,13 +72,12 @@ public class CmsEventBusImpl implements CmsEventBus {
                @Override
                public void onSubscribe(Subscription subscription) {
                        this.subscription = subscription;
-                       subscription.request(1);
+                       this.subscription.request(Long.MAX_VALUE);
                }
 
                @Override
                public void onNext(Map<String, Object> item) {
                        eventSubscriber.onEvent(topic, item);
-                       subscription.request(1);
                }
 
                @Override
index 2059803036f2ab476205564534f0203ed1bdde62..c481a2cc07be061e38b0db28c2212e50d096775a 100644 (file)
@@ -1,23 +1,29 @@
 package org.argeo.cms.swt;
 
-import java.security.PrivilegedAction;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.Callable;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
 import java.util.concurrent.ExecutionException;
 
 import javax.security.auth.Subject;
 import javax.security.auth.login.LoginContext;
 
+import org.argeo.api.cms.CmsApp;
 import org.argeo.api.cms.CmsEventBus;
+import org.argeo.api.cms.CmsLog;
 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.argeo.util.CurrentSubject;
 import org.eclipse.swt.widgets.Display;
 
 public abstract class AbstractSwtCmsView implements CmsView {
+       private final static CmsLog log = CmsLog.getLog(AbstractSwtCmsView.class);
+
        protected final String uiName;
 
        protected LoginContext loginContext;
@@ -37,6 +43,8 @@ public abstract class AbstractSwtCmsView implements CmsView {
 
        public abstract CmsEventBus getCmsEventBus();
 
+       public abstract CmsApp getCmsApp();
+
        @Override
        public void sendEvent(String topic, Map<String, Object> properties) {
                if (properties == null)
@@ -45,20 +53,43 @@ public abstract class AbstractSwtCmsView implements CmsView {
                        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);
+
+               log.debug(() -> uid + ": send event to " + topic);
+
                getCmsEventBus().sendEvent(topic, properties);
+               //getCmsApp().onEvent(topic, properties);
        }
 
-       public <T> T doAs(PrivilegedAction<T> action) {
+//     public void runAs(Runnable runnable) {
+//             display.asyncExec(() -> doAs(Executors.callable(runnable)));
+//     }
+
+       public <T> T doAs(Callable<T> action) {
                try {
                        CompletableFuture<T> result = new CompletableFuture<>();
                        Runnable toDo = () -> {
-                               T res = Subject.doAs(getSubject(), action);
+                               log.debug(() -> uid + ": process doAs");
+                               Subject subject = CurrentSubject.current();
+                               T res;
+                               if (subject != null) {
+                                       assert subject == getSubject();
+                                       try {
+                                               res = action.call();
+                                       } catch (Exception e) {
+                                               throw new CompletionException("Failed to execute action for " + subject, e);
+                                       }
+                               } else {
+                                       res = CurrentSubject.callAs(getSubject(), action);
+                               }
                                result.complete(res);
                        };
                        if (Thread.currentThread() == display.getThread())
                                toDo.run();
-                       else
-                               display.syncExec(toDo);
+                       else {
+                               display.asyncExec(toDo);
+                               display.wake();
+                       }
+//                             throw new IllegalStateException("Must be called from UI thread");
                        return result.get();
                } catch (InterruptedException | ExecutionException e) {
                        throw new IllegalStateException("Cannot execute action ins CMS view " + uid, e);
index 06e4d0f9f996374b651ad3695e2ce23e88e62ba7..dedf61dea0f31acd0a4ac01e90a00218d5b2abc9 100644 (file)
@@ -1,7 +1,7 @@
 package org.argeo.cms.swt.dialogs;
 
-import java.security.PrivilegedAction;
 import java.util.Arrays;
+import java.util.concurrent.Callable;
 
 import org.argeo.api.cms.CmsLog;
 import org.argeo.api.cms.ux.CmsView;
@@ -22,7 +22,7 @@ public class ChangePasswordDialog extends CmsMessageDialog {
        private CmsUserManager cmsUserManager;
        private CmsView cmsView;
 
-       private PrivilegedAction<Integer> doIt;
+       private Callable<Integer> doIt;
 
        public ChangePasswordDialog(Shell parentShell, String message, int kind, CmsUserManager cmsUserManager) {
                super(parentShell, message, kind);
index cdd4899289e59d3ce83751eaff39402a8ed115c1..cda9a117f681f0f091590b87ece870517cb96de4 100644 (file)
@@ -2,6 +2,7 @@ package org.argeo.cms.e4.rap;
 
 import java.security.AccessController;
 import java.util.UUID;
+import java.util.concurrent.Callable;
 
 import javax.security.auth.Subject;
 import javax.security.auth.login.LoginContext;
@@ -180,4 +181,9 @@ public class CmsLoginLifecycle implements CmsView {
                return state;
        }
 
+       @Override
+       public <T> T doAs(Callable<T> action) {
+               throw new UnsupportedOperationException();
+       }
+
 }
index 3c894d158b84101a31e1d2a92aa805d251efd3e8..b3ca245ad76a819c98dc44e19ddcfd24af84850b 100644 (file)
@@ -34,12 +34,12 @@ import org.eclipse.rap.rwt.client.service.BrowserNavigation;
 import org.eclipse.rap.rwt.client.service.BrowserNavigationEvent;
 import org.eclipse.rap.rwt.client.service.BrowserNavigationListener;
 import org.eclipse.rap.rwt.internal.lifecycle.RWTLifeCycle;
+import org.eclipse.rap.rwt.service.ServerPushSession;
 import org.eclipse.swt.SWT;
 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.eclipse.swt.widgets.Widget;
 
 /** The {@link CmsView} for a {@link CmsWebApp}. */
 @SuppressWarnings("restriction")
@@ -56,6 +56,8 @@ public class CmsWebEntryPoint extends AbstractSwtCmsView implements EntryPoint,
        /** Experimental OS-like multi windows. */
        private boolean multipleShells = false;
 
+       private ServerPushSession serverPushSession;
+
        public CmsWebEntryPoint(CmsWebApp cmsWebApp, String uiName) {
                super(uiName);
                assert cmsWebApp != null;
@@ -86,6 +88,7 @@ public class CmsWebEntryPoint extends AbstractSwtCmsView implements EntryPoint,
                browserNavigation = RWT.getClient().getService(BrowserNavigation.class);
                if (browserNavigation != null)
                        browserNavigation.addBrowserNavigationListener(this);
+
        }
 
        protected void createContents(Composite parent) {
@@ -108,6 +111,11 @@ public class CmsWebEntryPoint extends AbstractSwtCmsView implements EntryPoint,
                                        ui = cmsWebApp.getCmsApp().initUi(parent);
                                        if (ui instanceof Composite)
                                                ((Composite) ui).setLayoutData(CmsSwtUtils.fillAll());
+                                       serverPushSession = new ServerPushSession();
+
+                                       // required in order to doAs to work
+                                       // TODO check whether it would be worth optimising
+                                       serverPushSession.start();
                                        // we need ui to be set before refresh so that CmsView can store UI context data
                                        // in it.
                                        cmsWebApp.getCmsApp().refreshUi(ui, null);
@@ -215,6 +223,11 @@ public class CmsWebEntryPoint extends AbstractSwtCmsView implements EntryPoint,
                return cmsWebApp.getCmsEventBus();
        }
 
+       @Override
+       public CmsApp getCmsApp() {
+               return cmsWebApp.getCmsApp();
+       }
+
        @Override
        public void stateChanged(String state, String title) {
                browserNavigation.pushState(state, title);