Working multi RCP platform
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 7 Mar 2024 15:23:15 +0000 (16:23 +0100)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 7 Mar 2024 15:23:15 +0000 (16:23 +0100)
18 files changed:
org.argeo.cms.ee/bnd.bnd
org.argeo.cms.jshell/src/org/argeo/internal/cms/jshell/osgi/OsgiExecutionControlProvider.java
org.argeo.cms/src/org/argeo/cms/internal/osgi/CmsOsgiLogger.java
org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsDeploymentImpl.java
org.argeo.init/bnd.bnd
org.argeo.init/src/org/argeo/api/init/RuntimeManager.java
org.argeo.init/src/org/argeo/init/RuntimeManagerMain.java
org.argeo.init/src/org/argeo/init/logging/ThinLogging.java
org.argeo.init/src/org/argeo/init/osgi/OsgiBoot.java
org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeContext.java
sdk/argeo-build
swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java
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.swt.specific.rcp/bnd.bnd
swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpResourceManager.java
swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java
swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/internal/swt/specific/osgi/SwtSpecificRcpActivator.java [new file with mode: 0644]

index f09995c002a580a5684daf890e871e04148b319d..adff33d30d7c7b9482841a0e336d4c080ed9c043 100644 (file)
@@ -3,9 +3,11 @@ org.osgi.service.http;version=0.0.0,\
 org.osgi.service.http.whiteboard;version=0.0.0,\
 org.osgi.framework.namespace;version=0.0.0,\
 org.argeo.cms.osgi,\
-javax.servlet.*;version="[3,5)",\
 *
 
+# javax.servlet.*;version="[3,5)",\
+
+
 Service-Component:\
 OSGI-INF/pkgServletContext.xml,\
 OSGI-INF/pkgServlet.xml,\
index 9ebf97ed07be957c25203e409a9272986a9badf5..67515e69cbf8c1f0de8c19014e6b5e87b5cf75e8 100644 (file)
@@ -212,7 +212,7 @@ public class OsgiExecutionControlProvider implements ExecutionControlProvider {
 
        static Path bundleToPath(Path frameworkLocation, Bundle bundle) throws IOException {
                String location = bundle.getLocation();
-               if (location.startsWith("initial@reference:file:")) {
+               if (location.startsWith("initial@reference:file:")) {// Eclipse IDE environment
                        location = location.substring("initial@reference:file:".length());
                        Path p = frameworkLocation.getParent().resolve(location).toAbsolutePath();
                        if (Files.exists(p)) {
@@ -225,6 +225,15 @@ public class OsgiExecutionControlProvider implements ExecutionControlProvider {
                                log.warn("Ignore bundle " + p + " as it does not exist");
                                return null;
                        }
+               } else if (location.startsWith("reference:file:")) {// a2+reference
+                       location = location.substring("reference:".length());
+                       Path p = Paths.get(URI.create(location));
+                       if (Files.exists(p)) {
+                               return p;
+                       } else {
+                               log.warn("Ignore bundle " + p + " as it does not exist");
+                               return null;
+                       }
                }
                Path p = Paths.get(location);
                return p;
index 405fa46f4a92e27ae93d2422b3f0581ef87c6fb0..5fc6fcb4ff6cdf421f2b182392834c43c18f8f95 100644 (file)
@@ -44,7 +44,10 @@ public class CmsOsgiLogger implements LogListener {
        //
        @Override
        public void logged(LogEntry status) {
-               CmsLog pluginLog = CmsLog.getLog(status.getBundle().getSymbolicName());
+               String loggerName = status.getBundle().getSymbolicName();
+               if (loggerName == null)
+                       loggerName = "org.argeo.ext.osgi";
+               CmsLog pluginLog = CmsLog.getLog(loggerName);
                LogLevel severity = status.getLogLevel();
                if (severity.equals(LogLevel.ERROR) && pluginLog.isErrorEnabled()) {
                        // FIXME Fix Argeo TP
index 0d53a09b090f2a33dedf5e500e5c164593675a54..d3f4b575aa7ff6486f5708a7fbfa93728af4bb8c 100644 (file)
@@ -123,7 +123,7 @@ public class CmsDeploymentImpl implements CmsDeployment {
                if (contextPath == null)
                        return; // ignore silently
                httpHandlers.remove(contextPath);
-               if (!httpServer.isDone())
+               if (!httpExpected || !httpServer.isDone())
                        return;
                // TODO use resultNow when switching to Java 21
                httpServer.join().removeContext(contextPath);
index 34023cc7d023e9793de433c6cb74bd1ce0b8cc7c..c207cb8f18a04547895088d78c534ca3a2cb7565 100644 (file)
@@ -7,4 +7,6 @@ Import-Package: \
 org.osgi.*;version=0.0.0,\
 java.util.logging;resolution:=optional
 
-Export-Package: org.argeo.api.*
\ No newline at end of file
+Export-Package: \
+org.argeo.api.*,\
+org.argeo.init.osgi,\
\ No newline at end of file
index 113aa46c45189f714f56438ba62038b50509fcbb..da15b1ebccab4dc6d7f30a69edbec23d0bfad837 100644 (file)
@@ -1,11 +1,45 @@
 package org.argeo.api.init;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.Map;
+import java.util.Properties;
 import java.util.function.Consumer;
 
 /** Dynamically manages multiple runtimes within a single JVM. */
 public interface RuntimeManager {
+       String JVM_ARGS = "jvm.args";
+       String STATE = "state";
+       String DATA = "data";
+
        public void startRuntime(String relPath, Consumer<Map<String, String>> configCallback);
 
        public void closeRuntime(String relPath, boolean async);
+
+       static void loadConfig(Path dir, Map<String, String> config) {
+                       try {
+       //                      System.out.println("Load from " + dir);
+                               Path jvmArgsPath = dir.resolve(RuntimeManager.JVM_ARGS);
+                               if (!Files.exists(jvmArgsPath)) {
+                                       // load from parent directory
+                                       loadConfig(dir.getParent(), config);
+                               }
+       
+                               if (Files.exists(dir))
+                                       for (Path p : Files.newDirectoryStream(dir, "*.ini")) {
+                                               Properties props = new Properties();
+                                               try (InputStream in = Files.newInputStream(p)) {
+                                                       props.load(in);
+                                               }
+                                               for (Object key : props.keySet()) {
+                                                       config.put(key.toString(), props.getProperty(key.toString()));
+                                               }
+                                       }
+                       } catch (IOException e) {
+                               throw new UncheckedIOException("Cannot load configuration from " + dir, e);
+                       }
+               }
 }
index 5a8331dfeeeb2cf2b4b0e32f2fd6139a7d860838..7a3f781391f9fd72e02424fb807ff63a5ae4a025 100644 (file)
@@ -2,19 +2,14 @@ package org.argeo.init;
 
 import static org.argeo.api.init.InitConstants.SYMBOLIC_NAME_INIT;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UncheckedIOException;
 import java.lang.System.Logger;
 import java.lang.System.Logger.Level;
-import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Map;
-import java.util.Properties;
 import java.util.TreeMap;
 import java.util.function.Consumer;
 
@@ -43,19 +38,20 @@ public class RuntimeManagerMain implements RuntimeManager {
 
        private final static long RUNTIME_SHUTDOWN_TIMEOUT = 60 * 1000;
 
-       private final static String JVM_ARGS = "jvm.args";
-
        private Path baseConfigArea;
-       private Path baseStateArea;
+       private Path baseWritableArea;
        private Map<String, String> configuration = new HashMap<>();
 
        private Map<String, OsgiRuntimeContext> runtimeContexts = new TreeMap<>();
 
        RuntimeManagerMain(Path configArea, Path stateArea) {
-               loadConfig(configArea, configuration);
-               configuration.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA, stateArea.toUri().toString());
+               RuntimeManager.loadConfig(configArea, configuration);
+               configuration.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA, stateArea.resolve(STATE).toUri().toString());
+               // use config area if instance area is not set
+               if (!configuration.containsKey(InitConstants.PROP_OSGI_INSTANCE_AREA))
+                       configuration.put(InitConstants.PROP_OSGI_INSTANCE_AREA, stateArea.resolve(DATA).toUri().toString());
                this.baseConfigArea = configArea.getParent();
-               this.baseStateArea = stateArea.getParent();
+               this.baseWritableArea = stateArea.getParent();
 
                logger.log(Level.TRACE, () -> "Runtime manager configuration: " + configuration);
 
@@ -129,44 +125,20 @@ public class RuntimeManagerMain implements RuntimeManager {
                }
        }
 
-       public static void loadConfig(Path dir, Map<String, String> config) {
-               try {
-//                     System.out.println("Load from " + dir);
-                       Path jvmArgsPath = dir.resolve(JVM_ARGS);
-                       if (!Files.exists(jvmArgsPath)) {
-                               // load from parent directory
-                               loadConfig(dir.getParent(), config);
-                       }
-
-                       if (Files.exists(dir))
-                               for (Path p : Files.newDirectoryStream(dir, "*.ini")) {
-                                       Properties props = new Properties();
-                                       try (InputStream in = Files.newInputStream(p)) {
-                                               props.load(in);
-                                       }
-                                       for (Object key : props.keySet()) {
-                                               config.put(key.toString(), props.getProperty(key.toString()));
-                                       }
-                               }
-               } catch (IOException e) {
-                       throw new UncheckedIOException("Cannot load configuration from " + dir, e);
-               }
-       }
-
        OsgiRuntimeContext loadRuntime(String relPath, Consumer<Map<String, String>> configCallback) {
                closeRuntime(relPath, false);
-               Path stateArea = baseStateArea.resolve(relPath);
+               Path writableArea = baseWritableArea.resolve(relPath);
                Path configArea = baseConfigArea.resolve(relPath);
                Map<String, String> config = new HashMap<>();
-               loadConfig(configArea, config);
-               config.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA, stateArea.toUri().toString());
+               RuntimeManager.loadConfig(configArea, config);
+               config.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA, writableArea.resolve(STATE).toUri().toString());
 
                if (configCallback != null)
                        configCallback.accept(config);
 
                // use config area if instance area is not set
                if (!config.containsKey(InitConstants.PROP_OSGI_INSTANCE_AREA))
-                       config.put(InitConstants.PROP_OSGI_INSTANCE_AREA, config.get(InitConstants.PROP_OSGI_CONFIGURATION_AREA));
+                       config.put(InitConstants.PROP_OSGI_INSTANCE_AREA, writableArea.resolve(DATA).toUri().toString());
 
                OsgiRuntimeContext runtimeContext = new OsgiRuntimeContext(config);
                runtimeContexts.put(relPath, runtimeContext);
@@ -177,6 +149,21 @@ public class RuntimeManagerMain implements RuntimeManager {
                OsgiRuntimeContext runtimeContext = loadRuntime(relPath, configCallback);
                runtimeContext.run();
                Framework framework = runtimeContext.getFramework();
+
+//             for (Bundle b : framework.getBundleContext().getBundles()) {
+//                     try {
+////                           if (b.getSymbolicName().startsWith("org.eclipse.swt.gtk")) {
+////                                   b.uninstall();
+////                           }
+////                           else if (b.getSymbolicName().startsWith("org.eclipse.jface")) {
+////                                   b.uninstall();
+////                           }
+//                     } catch (Exception e) {
+//                             // TODO Auto-generated catch block
+//                             e.printStackTrace();
+//                     }
+//             }
+
                if (framework != null) {// in case the framework has closed very quickly after run
                        framework.getBundleContext().addFrameworkListener((e) -> {
                                if (e.getType() >= FrameworkEvent.STOPPED) {
index d615678cd886e63c63b06f95080d3c3d53593f17..dd6fad2e3a5a54c261517b02f720cacf9f925278 100644 (file)
@@ -68,7 +68,7 @@ class ThinLogging implements Consumer<Map<String, Object>> {
        // we don't synchronize maps on purpose as it would be
        // too expensive during normal operation
        // updates to the config may be shortly inconsistent
-       private SortedMap<String, ThinLogger> loggers = new TreeMap<>();
+       private SortedMap<String, ThinLogger> loggers = Collections.synchronizedSortedMap(new TreeMap<>());
        private NavigableMap<String, Level> levels = new TreeMap<>();
        private volatile boolean updatingConfiguration = false;
 
@@ -191,6 +191,7 @@ class ThinLogging implements Consumer<Map<String, Object>> {
        }
 
        public Logger getLogger(String name, Module module) {
+               Objects.requireNonNull(name, "logger name");
                if (!loggers.containsKey(name)) {
                        ThinLogger logger = new ThinLogger(name, computeApplicableLevel(name));
                        loggers.put(name, logger);
index 0d475ad21a090f2bdbe5bfce9410eeb0c8f92a4c..9b9ed6a7a500f0f943f66c5333fa5e754ba45e85 100644 (file)
@@ -99,19 +99,19 @@ public class OsgiBoot {
                                                provisioningManager.registerSource(
                                                                A2Source.SCHEME_A2 + "://" + homePath.toString() + "/.local/share/a2" + queryPart);
                                        provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/local/share/a2" + queryPart);
-                                       provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/local/lib/a2" + queryPart);
+//                                     provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/local/lib/a2" + queryPart);
                                        provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/share/a2" + queryPart);
-                                       provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/lib/a2" + queryPart);
+//                                     provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/lib/a2" + queryPart);
                                } else if (source.trim().equals(A2Source.DEFAULT_A2_REFERENCE_URI)) {
                                        if (Files.exists(homePath))
                                                provisioningManager.registerSource(A2Source.SCHEME_A2_REFERENCE + "://" + homePath.toString()
                                                                + "/.local/share/a2" + queryPart);
                                        provisioningManager
                                                        .registerSource(A2Source.SCHEME_A2_REFERENCE + ":///usr/local/share/a2" + queryPart);
-                                       provisioningManager
-                                                       .registerSource(A2Source.SCHEME_A2_REFERENCE + ":///usr/local/lib/a2" + queryPart);
+//                                     provisioningManager
+//                                                     .registerSource(A2Source.SCHEME_A2_REFERENCE + ":///usr/local/lib/a2" + queryPart);
                                        provisioningManager.registerSource(A2Source.SCHEME_A2_REFERENCE + ":///usr/share/a2" + queryPart);
-                                       provisioningManager.registerSource(A2Source.SCHEME_A2_REFERENCE + ":///usr/lib/a2" + queryPart);
+//                                     provisioningManager.registerSource(A2Source.SCHEME_A2_REFERENCE + ":///usr/lib/a2" + queryPart);
                                } else {
                                        provisioningManager.registerSource(source + queryPart);
                                }
index ecfeca74ee839a66b8b1df9dd9f9539752f79d9a..268fb221b3e54796d46cea0775ba2e35bdf62258 100644 (file)
@@ -96,12 +96,21 @@ public class OsgiRuntimeContext implements RuntimeContext, AutoCloseable {
                }
                OsgiBoot osgiBoot = new OsgiBoot(bundleContext);
                String frameworkUuuid = bundleContext.getProperty(Constants.FRAMEWORK_UUID);
-               new Thread("OSGi boot framework " + frameworkUuuid) {
+
+               // separate thread in order to improve logging
+               Thread osgiBootThread = new Thread("OSGi boot framework " + frameworkUuuid) {
                        @Override
                        public void run() {
                                osgiBoot.bootstrap(config);
                        }
-               }.start();
+               };
+               osgiBootThread.start();
+               // TODO return a completable stage so that inits can run in parallel
+//             try {
+//                     osgiBootThread.join(60 * 1000);
+//             } catch (InterruptedException e) {
+//                     // silent
+//             }
        }
 
        public void update() {
index 6f8e29e850f9fcfa5149e296e650355fab930752..e021d17c09f8586a68177e9eb40d9caa088c37b1 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 6f8e29e850f9fcfa5149e296e650355fab930752
+Subproject commit e021d17c09f8586a68177e9eb40d9caa088c37b1
index 84ab27679b17451832d2af2fffcd2e7f98fef1b5..ae7bf49d79c92a8c38ca461aba6c7cea1e794f66 100644 (file)
@@ -1,9 +1,5 @@
 package org.argeo.eclipse.ui.specific;
 
-import org.eclipse.jface.viewers.AbstractTableViewer;
-import org.eclipse.jface.viewers.ColumnViewer;
-import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
-import org.eclipse.jface.viewers.Viewer;
 import org.eclipse.rap.rwt.RWT;
 import org.eclipse.swt.widgets.Widget;
 
@@ -27,14 +23,6 @@ public class EclipseUiSpecificUtils {
                widget.setData("org.eclipse.rap.rwt.markupValidationDisabled", Boolean.TRUE);
        }
 
-       /**
-        * TootlTip support is supported only for {@link AbstractTableViewer} in RAP
-        */
-       public static void enableToolTipSupport(Viewer viewer) {
-               if (viewer instanceof ColumnViewer)
-                       ColumnViewerToolTipSupport.enableFor((ColumnViewer) viewer);
-       }
-
        private EclipseUiSpecificUtils() {
        }
 }
index 77aeae061fc780a80786477864f8a0391760fe18..7af1456b7dad68905d30986101ef50781a8033bd 100644 (file)
@@ -81,20 +81,24 @@ public class CmsRcpApp extends AbstractSwtCmsView implements CmsView {
                        // Styling
                        CmsTheme theme = CmsSwtUtils.getCmsTheme(parent);
                        if (theme != null) {
-                               cssEngine = new CSSSWTEngineImpl(display);
-                               for (String path : theme.getSwtCssPaths()) {
-                                       try (InputStream in = theme.loadPath(path)) {
-                                               cssEngine.parseStyleSheet(in);
-                                       } catch (IOException e) {
-                                               throw new IllegalStateException("Cannot load stylesheet " + path, e);
+                               try {
+                                       cssEngine = new CSSSWTEngineImpl(display);
+                                       for (String path : theme.getSwtCssPaths()) {
+                                               try (InputStream in = theme.loadPath(path)) {
+                                                       cssEngine.parseStyleSheet(in);
+                                               } catch (IOException e) {
+                                                       throw new IllegalStateException("Cannot load stylesheet " + path, e);
+                                               }
                                        }
+                                       cssEngine.setErrorHandler(new CSSErrorHandler() {
+                                               public void error(Exception e) {
+                                                       log.error("SWT styling error: ", e);
+                                               }
+                                       });
+                                       applyStyles(shell);
+                               } catch (Throwable e) {// could be a class not found error
+                                       log.error("Cannot initialise RCP theming", e);
                                }
-                               cssEngine.setErrorHandler(new CSSErrorHandler() {
-                                       public void error(Exception e) {
-                                               log.error("SWT styling error: ", e);
-                                       }
-                               });
-                               applyStyles(shell);
                        }
                        shell.layout(true, true);
 
index cd554de9d1ec09f63616a11dd1510b99ecd5bc67..96de08e10d6c5caedf23da78f819aa67567fe42f 100644 (file)
@@ -61,15 +61,18 @@ public class CmsRcpDisplayFactory {
                public void run() {
                        try {
                                display = Display.getDefault();
-                               display.setRuntimeExceptionHandler((e) -> e.printStackTrace());
-                               display.setErrorHandler((e) -> e.printStackTrace());
-
-                               while (!shutdown) {
-                                       if (!display.readAndDispatch())
-                                               display.sleep();
+                               boolean displayOwner = display.getThread() == this;
+                               if (displayOwner) {
+                                       display.setRuntimeExceptionHandler((e) -> e.printStackTrace());
+                                       display.setErrorHandler((e) -> e.printStackTrace());
+
+                                       while (!shutdown) {
+                                               if (!display.readAndDispatch())
+                                                       display.sleep();
+                                       }
+                                       display.dispose();
+                                       display = null;
                                }
-                               display.dispose();
-                               display = null;
                        } catch (UnsatisfiedLinkError e) {
                                logger.log(Level.ERROR,
                                                "Cannot load SWT, either because the SWT DLLs are no in the java.library.path,"
@@ -79,6 +82,7 @@ public class CmsRcpDisplayFactory {
                }
        }
 
+       @Deprecated
        public Display getDisplay() {
                return display;
        }
index bb88efda7c5a964dfc46a03aa4c7ca220acc8fed..23ea12bc1346a3d7340d0fa6954af79daf5a4fbb 100644 (file)
@@ -1,20 +1,18 @@
 Import-Package: \
-!java.*,\
-org.apache.commons.io,\
-org.eclipse.core.commands,\
+org.eclipse.core.commands;resolution:=optional,\
 !org.eclipse.core.runtime,\
 !org.eclipse.ui.plugin,\
 org.eclipse.swt,\
-javax.servlet.http;version="[3,5)",\
-javax.servlet;version="[3,5)",\
+javax.servlet.http;resolution:=optional,\
+javax.servlet;resolution:=optional,\
 *
 
 Export-Package: org.argeo.*,\
 org.eclipse.rap.fileupload.*;version="3.10",\
 org.eclipse.rap.rwt.*;version="3.10"
 
-# Was !org.eclipse.core.commands,\ why ?
+# Was !org.eclipse.core.commands, why ?
 
-#Bundle-Activator: org.argeo.eclipse.ui.ArgeoUiPlugin
+Bundle-Activator: org.argeo.internal.swt.specific.osgi.SwtSpecificRcpActivator
 #Bundle-ActivationPolicy: lazy
 #Ignore-Package: org.eclipse.core.commands
\ No newline at end of file
index 47ff35dc0e13ac6a6ac70f8482332c80445b873f..b99fd5815a3b61badc11205ee4586625ebb498c0 100644 (file)
@@ -1,13 +1,11 @@
 package org.argeo.eclipse.ui.rcp.internal.rwt;
 
 import java.io.ByteArrayInputStream;
-import java.io.IOException;
 import java.io.InputStream;
 import java.util.Collections;
 import java.util.Map;
 import java.util.TreeMap;
 
-import org.argeo.cms.util.StreamUtils;
 import org.eclipse.rap.rwt.service.ResourceManager;
 
 public class RcpResourceManager implements ResourceManager {
@@ -15,11 +13,13 @@ public class RcpResourceManager implements ResourceManager {
 
        @Override
        public void register(String name, InputStream in) {
-               try {
-                       register.put(name, StreamUtils.toByteArray(in));
-               } catch (IOException e) {
-                       throw new RuntimeException("Cannot register " + name, e);
-               }
+               // FIXME implement it
+               throw new UnsupportedOperationException();
+//             try {
+//                     register.put(name, StreamUtils.toByteArray(in));
+//             } catch (IOException e) {
+//                     throw new RuntimeException("Cannot register " + name, e);
+//             }
        }
 
        @Override
index d1acbcfc026a39742ea8025bc972b8a625f5b0d5..11969b3eb4c7bc490ee374878d88bc5f3c617762 100644 (file)
@@ -1,8 +1,5 @@
 package org.argeo.eclipse.ui.specific;
 
-import org.eclipse.jface.viewers.ColumnViewer;
-import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
-import org.eclipse.jface.viewers.Viewer;
 import org.eclipse.swt.widgets.Widget;
 
 /** Static utilities to bridge differences between RCP and RAP */
@@ -25,16 +22,6 @@ public class EclipseUiSpecificUtils {
                // does nothing
        }
 
-       /**
-        * TootlTip support is supported for {@link ColumnViewer} in RCP
-        * 
-        * @see ColumnViewerToolTipSupport#enableFor(Viewer)
-        */
-       public static void enableToolTipSupport(Viewer viewer) {
-               if (viewer instanceof ColumnViewer)
-                       ColumnViewerToolTipSupport.enableFor((ColumnViewer) viewer);
-       }
-
        private EclipseUiSpecificUtils() {
        }
 }
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/internal/swt/specific/osgi/SwtSpecificRcpActivator.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/internal/swt/specific/osgi/SwtSpecificRcpActivator.java
new file mode 100644 (file)
index 0000000..e74e109
--- /dev/null
@@ -0,0 +1,75 @@
+package org.argeo.internal.swt.specific.osgi;
+
+import org.eclipse.swt.SWTError;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+public class SwtSpecificRcpActivator implements BundleActivator {
+       private static Display display;
+       private static Shell rootShell;
+       private static UiThread uiThread;
+
+       private static boolean debug = true;
+
+       @Override
+       public void start(BundleContext context) throws Exception {
+               if (display != null)
+                       throw new IllegalStateException("SWT display already exists");
+               uiThread = new UiThread();
+               uiThread.start();
+       }
+
+       @Override
+       public void stop(BundleContext context) throws Exception {
+               if (display == null)
+                       return; // TODO log issue
+               display.asyncExec(() -> rootShell.dispose());
+               display.wake();
+               uiThread.join(60 * 1000);
+               uiThread = null;
+               display = null;
+       }
+
+       static class UiThread extends Thread {
+
+               @Override
+               public void run() {
+                       boolean displayOwner = true;
+                       Display d = Display.getDefault();
+                       if (d.getThread() != UiThread.this) {
+                               displayOwner = false;
+                               // throw new IllegalStateException("There was already a default SWT display");
+                       }
+
+                       d.syncExec(() -> {
+                               try {
+                                       rootShell = new Shell(d);
+                               } catch (SWTError e) {
+                                       e.printStackTrace();
+                                       display.dispose();
+                                       return;
+                               }
+
+                               if (debug) {
+                                       rootShell.setLayout(new FillLayout());
+                                       new Label(rootShell, 0).setText("ROOT SHELL");
+                                       rootShell.pack();
+                                       rootShell.open();
+                               }
+                       });
+
+                       display = d;
+
+                       if (displayOwner) {
+                               while (!rootShell.isDisposed())
+                                       if (!display.readAndDispatch())
+                                               display.sleep();
+                               display.dispose();
+                       }
+               }
+       }
+}