Use weak references to UIs and regular clean-up
authorMathieu Baudier <mbaudier@argeo.org>
Fri, 15 Dec 2023 06:50:27 +0000 (07:50 +0100)
committerMathieu Baudier <mbaudier@argeo.org>
Fri, 15 Dec 2023 06:50:27 +0000 (07:50 +0100)
swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/SwtArgeoApp.java

index a81c33ef83f17fce709099e4fe52dc4293a563bf..ef3bb8c330e1018de0b63c29cafa10124e9662d0 100644 (file)
@@ -2,6 +2,7 @@ package org.argeo.app.swt.ux;
 
 import static org.argeo.api.cms.ux.CmsView.CMS_VIEW_UID_PROPERTY;
 
+import java.lang.ref.WeakReference;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -10,6 +11,8 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
 import java.util.TreeMap;
 import java.util.TreeSet;
 
@@ -91,7 +94,8 @@ public class SwtArgeoApp extends AbstractArgeoApp implements CmsEventSubscriber
 //     private CmsUserManager cmsUserManager;
 
        // TODO make more optimal or via CmsSession/CmsView
-       private Map<String, SwtAppUi> managedUis = Collections.synchronizedMap(new HashMap<>());
+       private static Timer janitorTimer = new Timer(true);
+       private Map<String, WeakReference<SwtAppUi>> managedUis = new HashMap<>();
 
        // ACR
        private ContentRepository contentRepository;
@@ -104,9 +108,6 @@ public class SwtArgeoApp extends AbstractArgeoApp implements CmsEventSubscriber
                        getCmsContext().getCmsEventBus().addEventSubscriber(event.topic(), this);
                }
 
-               if (log.isDebugEnabled())
-                       log.info("Argeo Suite App started");
-
                if (properties.containsKey(DEFAULT_UI_NAME_PROPERTY))
                        defaultUiName = LangUtils.get(properties, DEFAULT_UI_NAME_PROPERTY);
                if (properties.containsKey(DEFAULT_THEME_ID_PROPERTY))
@@ -126,25 +127,39 @@ public class SwtArgeoApp extends AbstractArgeoApp implements CmsEventSubscriber
                        // TODO does it make sense to accept that?
                        appPid = "<unknown>";
                }
+               if (log.isDebugEnabled())
+                       log.info("Argeo Suite App " + appPid + " started");
 
                Objects.requireNonNull(contentRepository, "Content repository must be provided");
                Objects.requireNonNull(appUserState, "App user state must be provided");
-//             if (pidPrefix == null)
-//                     throw new IllegalArgumentException("PID prefix must be set.");
-
-//             headerPid = pidPrefix + "header";
-//             footerPid = pidPrefix + "footer";
-//             leadPanePid = pidPrefix + "leadPane";
-//             adminLeadPanePid = pidPrefix + "adminLeadPane";
-//             loginScreenPid = pidPrefix + "loginScreen";
+               
+               long janitorPeriod = 12 * 60 * 60 * 1000;// 12h
+               janitorTimer.schedule(new TimerTask() {
+
+                       @Override
+                       public void run() {
+                               refs: for (WeakReference<SwtAppUi> uiRef : managedUis.values()) {
+                                       SwtAppUi ui = uiRef.get();
+                                       if (ui == null)
+                                               continue refs;
+                                       ui.disposeIfTimedout();
+                               }
+                               if (log.isDebugEnabled())
+                                       log.debug(managedUis.size() + " UIs being managed by app " + appPid);
+                       }
+               }, janitorPeriod, janitorPeriod);
 
        }
 
        public void stop(Map<String, Object> properties) {
-               for (SwtAppUi ui : managedUis.values())
+               refs: for (WeakReference<SwtAppUi> uiRef : managedUis.values()) {
+                       SwtAppUi ui = uiRef.get();
+                       if (ui == null)
+                               continue refs;
                        if (!ui.isDisposed() && !ui.getDisplay().isDisposed()) {
                                ui.getDisplay().syncExec(() -> ui.dispose());
                        }
+               }
                managedUis.clear();
                if (log.isDebugEnabled())
                        log.info("Argeo Suite App stopped");
@@ -171,9 +186,11 @@ public class SwtArgeoApp extends AbstractArgeoApp implements CmsEventSubscriber
                if (theme != null)
                        CmsSwtUtils.registerCmsTheme(uiParent.getShell(), theme);
                SwtAppUi argeoSuiteUi = new SwtAppUi(uiParent, SWT.INHERIT_DEFAULT);
+               // TODO make timeout configurable
+               argeoSuiteUi.setUiTimeout(12 * 60 * 60 * 1000);// 12 hours
                String uid = cmsView.getUid();
-               managedUis.put(uid, argeoSuiteUi);
                argeoSuiteUi.addDisposeListener(new CleanUpUi(uid));
+               managedUis.put(uid, new WeakReference<>(argeoSuiteUi));
                return argeoSuiteUi;
        }
 
@@ -590,7 +607,10 @@ public class SwtArgeoApp extends AbstractArgeoApp implements CmsEventSubscriber
        }
 
        private SwtAppUi getRelatedUi(Map<String, Object> eventProperties) {
-               return managedUis.get(get(eventProperties, CMS_VIEW_UID_PROPERTY));
+               WeakReference<SwtAppUi> uiRef = managedUis.get(get(eventProperties, CMS_VIEW_UID_PROPERTY));
+               if (uiRef == null)
+                       return null;
+               return uiRef.get();
        }
 
        public static String get(Map<String, Object> eventProperties, String key) {
@@ -672,14 +692,6 @@ public class SwtArgeoApp extends AbstractArgeoApp implements CmsEventSubscriber
                }
        }
 
-//     public void setCmsUserManager(CmsUserManager cmsUserManager) {
-//             this.cmsUserManager = cmsUserManager;
-//     }
-
-//     protected ContentRepository getContentRepository() {
-//             return contentRepository;
-//     }
-
        public void setContentRepository(ContentRepository contentRepository) {
                this.contentRepository = contentRepository;
        }
@@ -697,7 +709,6 @@ public class SwtArgeoApp extends AbstractArgeoApp implements CmsEventSubscriber
                final String uid;
 
                public CleanUpUi(String uid) {
-                       super();
                        this.uid = uid;
                }