Clean up Argeo Init
[lgpl/argeo-commons.git] / org.argeo.init / src / org / argeo / init / osgi / OsgiBoot.java
index c26fad66817c3b654f1ea0e965d9025e556f0c2d..0d475ad21a090f2bdbe5bfce9410eeb0c8f92a4c 100644 (file)
@@ -1,9 +1,13 @@
 package org.argeo.init.osgi;
 
-import static org.argeo.init.osgi.OsgiBootUtils.debug;
-import static org.argeo.init.osgi.OsgiBootUtils.warn;
+import static java.lang.System.Logger.Level.DEBUG;
+import static java.lang.System.Logger.Level.ERROR;
+import static java.lang.System.Logger.Level.TRACE;
+import static java.lang.System.Logger.Level.WARNING;
 
 import java.io.File;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -14,22 +18,24 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Properties;
+import java.util.ServiceLoader;
 import java.util.Set;
 import java.util.SortedMap;
 import java.util.StringTokenizer;
 import java.util.TreeMap;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ForkJoinPool;
 
-import org.argeo.init.a2.A2Source;
-import org.argeo.init.a2.ProvisioningManager;
+import org.argeo.api.a2.A2Source;
+import org.argeo.api.a2.ProvisioningManager;
+import org.argeo.api.init.InitConstants;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleException;
 import org.osgi.framework.FrameworkEvent;
 import org.osgi.framework.Version;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.launch.FrameworkFactory;
 import org.osgi.framework.startlevel.BundleStartLevel;
 import org.osgi.framework.startlevel.FrameworkStartLevel;
 import org.osgi.framework.wiring.FrameworkWiring;
@@ -39,10 +45,8 @@ import org.osgi.framework.wiring.FrameworkWiring;
  * properties. The approach is to generate list of URLs based on various
  * methods, configured via properties.
  */
-public class OsgiBoot implements OsgiBootConstants {
-       public final static String PROP_ARGEO_OSGI_START = "argeo.osgi.start";
-       public final static String PROP_ARGEO_OSGI_MAX_START_LEVEL = "argeo.osgi.maxStartLevel";
-       public final static String PROP_ARGEO_OSGI_SOURCES = "argeo.osgi.sources";
+public class OsgiBoot {
+       private final static Logger logger = System.getLogger(OsgiBoot.class.getName());
 
        @Deprecated
        final static String PROP_ARGEO_OSGI_BUNDLES = "argeo.osgi.bundles";
@@ -62,18 +66,6 @@ public class OsgiBoot implements OsgiBootConstants {
        public final static String DEFAULT_BASE_URL = "reference:file:";
        final static String DEFAULT_MAX_START_LEVEL = "32";
 
-       // OSGi standard properties
-       final static String PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL = "osgi.bundles.defaultStartLevel";
-       final static String PROP_OSGI_STARTLEVEL = "osgi.startLevel";
-       public final static String PROP_OSGI_INSTANCE_AREA = "osgi.instance.area";
-       public final static String PROP_OSGI_CONFIGURATION_AREA = "osgi.configuration.area";
-       public final static String PROP_OSGI_SHARED_CONFIGURATION_AREA = "osgi.sharedConfiguration.area";
-       public final static String PROP_OSGI_USE_SYSTEM_PROPERTIES = "osgi.framework.useSystemProperties";
-
-       // Symbolic names
-       final static String SYMBOLIC_NAME_OSGI_BOOT = "org.argeo.init";
-       final static String SYMBOLIC_NAME_EQUINOX = "org.eclipse.osgi";
-
        private final BundleContext bundleContext;
        private final String localCache;
        private final ProvisioningManager provisioningManager;
@@ -89,7 +81,7 @@ public class OsgiBoot implements OsgiBootConstants {
                localCache = getProperty(PROP_ARGEO_OSGI_LOCAL_CACHE, homeUri + ".m2/repository/");
 
                provisioningManager = new ProvisioningManager(bundleContext);
-               String sources = getProperty(PROP_ARGEO_OSGI_SOURCES);
+               String sources = getProperty(InitConstants.PROP_ARGEO_OSGI_SOURCES);
                if (sources == null) {
                        provisioningManager.registerDefaultSource();
                } else {
@@ -101,19 +93,25 @@ public class OsgiBoot implements OsgiBootConstants {
                                        queryPart = source.substring(qmIndex);
                                        source = source.substring(0, qmIndex);
                                }
+                               // TODO centralise in A" package?
                                if (source.trim().equals(A2Source.DEFAULT_A2_URI)) {
                                        if (Files.exists(homePath))
                                                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/share/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/share/a2" + queryPart);
+                                       provisioningManager.registerSource(A2Source.SCHEME_A2_REFERENCE + ":///usr/lib/a2" + queryPart);
                                } else {
                                        provisioningManager.registerSource(source + queryPart);
                                }
@@ -138,10 +136,10 @@ public class OsgiBoot implements OsgiBootConstants {
                        long begin = System.currentTimeMillis();
 
                        // notify start
-                       String osgiInstancePath = getProperty(PROP_OSGI_INSTANCE_AREA);
-                       String osgiConfigurationPath = getProperty(PROP_OSGI_CONFIGURATION_AREA);
-                       String osgiSharedConfigurationPath = getProperty(PROP_OSGI_CONFIGURATION_AREA);
-                       OsgiBootUtils.info("OSGi bootstrap starting" //
+                       String osgiInstancePath = getProperty(InitConstants.PROP_OSGI_INSTANCE_AREA);
+                       String osgiConfigurationPath = getProperty(InitConstants.PROP_OSGI_CONFIGURATION_AREA);
+                       String osgiSharedConfigurationPath = getProperty(InitConstants.PROP_OSGI_CONFIGURATION_AREA);
+                       logger.log(DEBUG, () -> "OSGi bootstrap starting" //
                                        + (osgiInstancePath != null ? " data: " + osgiInstancePath + "" : "") //
                                        + (osgiConfigurationPath != null ? " state: " + osgiConfigurationPath + "" : "") //
                                        + (osgiSharedConfigurationPath != null ? " config: " + osgiSharedConfigurationPath + "" : "") //
@@ -165,28 +163,28 @@ public class OsgiBoot implements OsgiBootConstants {
 
                        // complete
                        long duration = System.currentTimeMillis() - begin;
-                       OsgiBootUtils.info("OSGi bootstrap completed in " + Math.round(((double) duration) / 1000) + "s ("
+                       logger.log(DEBUG, () -> "OSGi bootstrap completed in " + Math.round(((double) duration) / 1000) + "s ("
                                        + duration + "ms), " + bundleContext.getBundles().length + " bundles");
                } catch (RuntimeException e) {
-                       OsgiBootUtils.error("OSGi bootstrap FAILED", e);
+                       logger.log(ERROR, "OSGi bootstrap FAILED", e);
                        throw e;
                }
 
                // diagnostics
-               if (OsgiBootUtils.isDebug()) {
+               if (logger.isLoggable(TRACE)) {
                        OsgiBootDiagnostics diagnostics = new OsgiBootDiagnostics(bundleContext);
                        diagnostics.checkUnresolved();
                        Map<String, Set<String>> duplicatePackages = diagnostics.findPackagesExportedTwice();
                        if (duplicatePackages.size() > 0) {
-                               OsgiBootUtils.info("Packages exported twice:");
+                               logger.log(TRACE, "Packages exported twice:");
                                Iterator<String> it = duplicatePackages.keySet().iterator();
                                while (it.hasNext()) {
                                        String pkgName = it.next();
-                                       OsgiBootUtils.info(pkgName);
+                                       logger.log(TRACE, pkgName);
                                        Set<String> bdles = duplicatePackages.get(pkgName);
                                        Iterator<String> bdlesIt = bdles.iterator();
                                        while (bdlesIt.hasNext())
-                                               OsgiBootUtils.info("  " + bdlesIt.next());
+                                               logger.log(TRACE, "  " + bdlesIt.next());
                                }
                        }
                }
@@ -233,21 +231,20 @@ public class OsgiBoot implements OsgiBootConstants {
                try {
                        if (installedBundles.containsKey(url)) {
                                Bundle bundle = (Bundle) installedBundles.get(url);
-                               if (OsgiBootUtils.isDebug())
-                                       debug("Bundle " + bundle.getSymbolicName() + " already installed from " + url);
-                       } else if (url.contains("/" + SYMBOLIC_NAME_EQUINOX + "/")
-                                       || url.contains("/" + SYMBOLIC_NAME_OSGI_BOOT + "/")) {
-                               if (OsgiBootUtils.isDebug())
-                                       warn("Skip " + url);
+                               logger.log(TRACE, () -> "Bundle " + bundle.getSymbolicName() + " already installed from " + url);
+                       } else if (url.contains("/" + InitConstants.SYMBOLIC_NAME_EQUINOX + "/")
+                                       || url.contains("/" + InitConstants.SYMBOLIC_NAME_INIT + "/")) {
+                               if (logger.isLoggable(TRACE))
+                                       logger.log(WARNING, "Skip " + url);
                                return;
                        } else {
                                Bundle bundle = bundleContext.installBundle(url);
                                if (url.startsWith("http"))
-                                       OsgiBootUtils
-                                                       .info("Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url);
-                               else if (OsgiBootUtils.isDebug())
-                                       OsgiBootUtils.debug(
-                                                       "Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url);
+                                       logger.log(DEBUG,
+                                                       () -> "Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url);
+                               else
+                                       logger.log(TRACE,
+                                                       () -> "Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url);
                                assert bundle.getSymbolicName() != null;
                                // uninstall previous versions
                                bundles: for (Bundle b : bundleContext.getBundles()) {
@@ -262,17 +259,17 @@ public class OsgiBoot implements OsgiBootConstants {
                                                        if (bundleV.getMicro() > bV.getMicro()) {
                                                                // uninstall older bundles
                                                                b.uninstall();
-                                                               OsgiBootUtils.debug("Uninstalled " + b);
+                                                               logger.log(TRACE, () -> "Uninstalled " + b);
                                                        } else if (bundleV.getMicro() < bV.getMicro()) {
                                                                // uninstall just installed bundle if newer
                                                                bundle.uninstall();
-                                                               OsgiBootUtils.debug("Uninstalled " + bundle);
+                                                               logger.log(TRACE, () -> "Uninstalled " + bundle);
                                                                break bundles;
                                                        } else {
                                                                // uninstall any other with same major/minor
                                                                if (!bundleV.getQualifier().equals(bV.getQualifier())) {
                                                                        b.uninstall();
-                                                                       OsgiBootUtils.debug("Uninstalled " + b);
+                                                                       logger.log(TRACE, () -> "Uninstalled " + b);
                                                                }
                                                        }
                                                }
@@ -282,19 +279,19 @@ public class OsgiBoot implements OsgiBootConstants {
                } catch (BundleException e) {
                        final String ALREADY_INSTALLED = "is already installed";
                        String message = e.getMessage();
-                       if ((message.contains("Bundle \"" + SYMBOLIC_NAME_OSGI_BOOT + "\"")
-                                       || message.contains("Bundle \"" + SYMBOLIC_NAME_EQUINOX + "\""))
+                       if ((message.contains("Bundle \"" + InitConstants.SYMBOLIC_NAME_INIT + "\"")
+                                       || message.contains("Bundle \"" + InitConstants.SYMBOLIC_NAME_EQUINOX + "\""))
                                        && message.contains(ALREADY_INSTALLED)) {
                                // silent, in order to avoid warnings: we know that both
                                // have already been installed...
                        } else {
                                if (message.contains(ALREADY_INSTALLED)) {
-                                       if (OsgiBootUtils.isDebug())
-                                               OsgiBootUtils.warn("Duplicate install from " + url + ": " + message);
+                                       if (logger.isLoggable(TRACE))
+                                               logger.log(WARNING, "Duplicate install from " + url + ": " + message);
                                } else
-                                       OsgiBootUtils.warn("Could not install bundle from " + url + ": " + message);
+                                       logger.log(WARNING, "Could not install bundle from " + url + ": " + message);
                        }
-                       if (OsgiBootUtils.isDebug() && !message.contains(ALREADY_INSTALLED))
+                       if (logger.isLoggable(TRACE) && !message.contains(ALREADY_INSTALLED))
                                e.printStackTrace();
                }
        }
@@ -314,15 +311,16 @@ public class OsgiBoot implements OsgiBootConstants {
                if (properties != null) {
                        for (String key : properties.keySet()) {
                                String property = key;
-                               if (property.startsWith(PROP_ARGEO_OSGI_START)) {
+                               if (property.startsWith(InitConstants.PROP_ARGEO_OSGI_START)) {
                                        map.put(property, properties.get(property));
                                }
                        }
                }
                // then try all start level until a maximum
-               int maxStartLevel = Integer.parseInt(getProperty(PROP_ARGEO_OSGI_MAX_START_LEVEL, DEFAULT_MAX_START_LEVEL));
+               int maxStartLevel = Integer
+                               .parseInt(getProperty(InitConstants.PROP_ARGEO_OSGI_MAX_START_LEVEL, DEFAULT_MAX_START_LEVEL));
                for (int i = 1; i <= maxStartLevel; i++) {
-                       String key = PROP_ARGEO_OSGI_START + "." + i;
+                       String key = InitConstants.PROP_ARGEO_OSGI_START + "." + i;
                        String value = getProperty(key);
                        if (value != null)
                                map.put(key, value);
@@ -330,7 +328,7 @@ public class OsgiBoot implements OsgiBootConstants {
                }
                // finally, override with system properties
                for (Object key : System.getProperties().keySet()) {
-                       if (key.toString().startsWith(PROP_ARGEO_OSGI_START)) {
+                       if (key.toString().startsWith(InitConstants.PROP_ARGEO_OSGI_START)) {
                                map.put(key.toString(), System.getProperty(key.toString()));
                        }
                }
@@ -338,14 +336,13 @@ public class OsgiBoot implements OsgiBootConstants {
                doStartBundles(map);
        }
 
-       @Deprecated
-       public void startBundles(Properties properties) {
+       void startBundles(Properties properties) {
                Map<String, String> map = new TreeMap<>();
                // first use properties
                if (properties != null) {
                        for (Object key : properties.keySet()) {
                                String property = key.toString();
-                               if (property.startsWith(PROP_ARGEO_OSGI_START)) {
+                               if (property.startsWith(InitConstants.PROP_ARGEO_OSGI_START)) {
                                        map.put(property, properties.get(property).toString());
                                }
                        }
@@ -353,20 +350,25 @@ public class OsgiBoot implements OsgiBootConstants {
                startBundles(map);
        }
 
-       /** Start bundle based on keys starting with {@link #PROP_ARGEO_OSGI_START}. */
+       /**
+        * Start bundle based on keys starting with
+        * {@link InitConstants#PROP_ARGEO_OSGI_START}.
+        */
        protected void doStartBundles(Map<String, String> properties) {
                FrameworkStartLevel frameworkStartLevel = bundleContext.getBundle(0).adapt(FrameworkStartLevel.class);
 
                // default and active start levels from System properties
                int initialStartLevel = frameworkStartLevel.getInitialBundleStartLevel();
-               int defaultStartLevel = Integer.parseInt(getProperty(PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL, "4"));
-               int activeStartLevel = Integer.parseInt(getProperty(PROP_OSGI_STARTLEVEL, "6"));
-               if (OsgiBootUtils.isDebug()) {
-                       OsgiBootUtils.debug("OSGi default start level: "
-                                       + getProperty(PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL, "<not set>") + ", using " + defaultStartLevel);
-                       OsgiBootUtils.debug("OSGi active start level: " + getProperty(PROP_OSGI_STARTLEVEL, "<not set>")
+               int defaultStartLevel = Integer.parseInt(getProperty(InitConstants.PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL, "4"));
+               int activeStartLevel = Integer.parseInt(getProperty(InitConstants.PROP_OSGI_STARTLEVEL, "6"));
+               if (logger.isLoggable(TRACE)) {
+                       logger.log(TRACE,
+                                       "OSGi default start level: "
+                                                       + getProperty(InitConstants.PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL, "<not set>") + ", using "
+                                                       + defaultStartLevel);
+                       logger.log(TRACE, "OSGi active start level: " + getProperty(InitConstants.PROP_OSGI_STARTLEVEL, "<not set>")
                                        + ", using " + activeStartLevel);
-                       OsgiBootUtils.debug("Framework start level: " + frameworkStartLevel.getStartLevel() + " (initial: "
+                       logger.log(TRACE, "Framework start level: " + frameworkStartLevel.getStartLevel() + " (initial: "
                                        + initialStartLevel + ")");
                }
 
@@ -378,7 +380,23 @@ public class OsgiBoot implements OsgiBootConstants {
                        for (String bsn : startLevels.get(level))
                                bundleStartLevels.put(bsn, level);
                }
-               for (Bundle bundle : bundleContext.getBundles()) {
+
+               // keep only bundles with the highest version
+               Map<String, Bundle> startableBundles = new HashMap<>();
+               bundles: for (Bundle bundle : bundleContext.getBundles()) {
+                       if (bundle.getVersion() == null)
+                               continue bundles;
+                       String bsn = bundle.getSymbolicName();
+                       if (!startableBundles.containsKey(bsn)) {
+                               startableBundles.put(bsn, bundle);
+                       } else {
+                               if (bundle.getVersion().compareTo(startableBundles.get(bsn).getVersion()) > 0) {
+                                       startableBundles.put(bsn, bundle);
+                               }
+                       }
+               }
+
+               for (Bundle bundle : startableBundles.values()) {
                        String bsn = bundle.getSymbolicName();
                        if (bundleStartLevels.containsKey(bsn)) {
                                BundleStartLevel bundleStartLevel = bundle.adapt(BundleStartLevel.class);
@@ -388,49 +406,56 @@ public class OsgiBoot implements OsgiBootConstants {
                                        try {
                                                bundle.start();
                                        } catch (BundleException e) {
-                                               OsgiBootUtils.error("Cannot mark " + bsn + " as started", e);
+                                               logger.log(ERROR, "Cannot mark " + bsn + " as started", e);
                                        }
-                                       if (OsgiBootUtils.isDebug())
-                                               OsgiBootUtils.debug(bsn + " starts at level " + level);
+                                       logger.log(TRACE, () -> bsn + " v" + bundle.getVersion() + " starts at level " + level);
                                }
                        }
                }
 
-               if (OsgiBootUtils.isDebug())
-                       OsgiBootUtils.debug("About to set framework start level to " + activeStartLevel + " ...");
-
-               // Start the framework level after level
-               stages: for (int stage = initialStartLevel; stage <= activeStartLevel; stage++) {
-                       OsgiBootUtils.info("Starting stage " + stage + "...");
-                       final int nextStage = stage;
-                       final CompletableFuture<FrameworkEvent> stageCompleted = new CompletableFuture<>();
-                       ForkJoinPool.commonPool().execute(() -> {
-                               frameworkStartLevel.setStartLevel(nextStage, (FrameworkEvent event) -> {
-                                       stageCompleted.complete(event);
-                               });
-                       });
-                       FrameworkEvent event;
-                       try {
-                               event = stageCompleted.get();
-                       } catch (InterruptedException | ExecutionException e) {
-                               throw new IllegalStateException("Cannot continue start", e);
-                       }
-                       if (event.getThrowable() != null) {
-                               OsgiBootUtils.error("Stage " + nextStage + " failed, aborting start.", event.getThrowable());
-                               break stages;
+               logger.log(TRACE, () -> "About to set framework start level to " + activeStartLevel + " ...");
+
+               frameworkStartLevel.setStartLevel(activeStartLevel, (FrameworkEvent event) -> {
+                       if (event.getType() == FrameworkEvent.ERROR) {
+                               logger.log(ERROR, "Start sequence failed", event.getThrowable());
+                       } else {
+                               logger.log(TRACE, () -> "Framework started at level " + frameworkStartLevel.getStartLevel());
                        }
-               }
+               });
+
+//             // Start the framework level after level
+//             int currentStartLevel = frameworkStartLevel.getStartLevel();
+//             stages: for (int stage = currentStartLevel + 1; stage <= activeStartLevel; stage++) {
+//                     if (OsgiBootUtils.isDebug())
+//                             OsgiBootUtils.debug("Starting stage " + stage + "...");
+//                     final int nextStage = stage;
+//                     final CompletableFuture<FrameworkEvent> stageCompleted = new CompletableFuture<>();
+//                     frameworkStartLevel.setStartLevel(nextStage, (FrameworkEvent event) -> {
+//                             stageCompleted.complete(event);
+//                     });
+//                     FrameworkEvent event;
+//                     try {
+//                             event = stageCompleted.get();
+//                     } catch (InterruptedException | ExecutionException e) {
+//                             throw new IllegalStateException("Cannot continue start", e);
+//                     }
+//                     if (event.getThrowable() != null) {
+//                             OsgiBootUtils.error("Stage " + nextStage + " failed, aborting start.", event.getThrowable());
+//                             break stages;
+//                     }
+//             }
        }
 
        private static void computeStartLevels(SortedMap<Integer, List<String>> startLevels, Map<String, String> properties,
                        Integer defaultStartLevel) {
 
                // default (and previously, only behaviour)
-               appendToStartLevels(startLevels, defaultStartLevel, properties.getOrDefault(PROP_ARGEO_OSGI_START, ""));
+               appendToStartLevels(startLevels, defaultStartLevel,
+                               properties.getOrDefault(InitConstants.PROP_ARGEO_OSGI_START, ""));
 
                // list argeo.osgi.start.* system properties
                Iterator<String> keys = properties.keySet().iterator();
-               final String prefix = PROP_ARGEO_OSGI_START + ".";
+               final String prefix = InitConstants.PROP_ARGEO_OSGI_START + ".";
                while (keys.hasNext()) {
                        String key = keys.next();
                        if (key.startsWith(prefix)) {
@@ -501,8 +526,7 @@ public class OsgiBoot implements OsgiBootConstants {
                        return urls;
 
 //             bundlePatterns = SystemPropertyUtils.resolvePlaceholders(bundlePatterns);
-               if (OsgiBootUtils.isDebug())
-                       debug(PROP_ARGEO_OSGI_BUNDLES + "=" + bundlePatterns);
+               logger.log(TRACE, () -> PROP_ARGEO_OSGI_BUNDLES + "=" + bundlePatterns);
 
                StringTokenizer st = new StringTokenizer(bundlePatterns, ",");
                List<BundlesSet> bundlesSets = new ArrayList<BundlesSet>();
@@ -604,8 +628,8 @@ public class OsgiBoot implements OsgiBootConstants {
                        File[] files = baseDir.listFiles();
 
                        if (files == null) {
-                               if (OsgiBootUtils.isDebug())
-                                       OsgiBootUtils.warn("Base dir " + baseDir + " has no children, exists=" + baseDir.exists()
+                               if (logger.isLoggable(TRACE))
+                                       logger.log(Level.WARNING, "Base dir " + baseDir + " has no children, exists=" + baseDir.exists()
                                                        + ", isDirectory=" + baseDir.isDirectory());
                                return;
                        }
@@ -643,8 +667,8 @@ public class OsgiBoot implements OsgiBootConstants {
 //                                                     }
                                                } else {
                                                        boolean nonDirectoryOk = matcher.matches(Paths.get(newCurrentPath));
-                                                       if (OsgiBootUtils.isDebug())
-                                                               debug(currentPath + " " + (ok ? "" : " not ") + " matched with " + pattern);
+                                                       logger.log(TRACE,
+                                                                       () -> currentPath + " " + (ok ? "" : " not ") + " matched with " + pattern);
                                                        if (nonDirectoryOk)
                                                                matched.add(relativeToFullPath(base, newCurrentPath));
                                                }
@@ -717,6 +741,29 @@ public class OsgiBoot implements OsgiBootConstants {
                return getProperty(name, null);
        }
 
+       /*
+        * PLAIN OSGI LAUNCHER
+        */
+       /** Launch an OSGi framework. OSGi Boot initialisation is NOT performed. */
+       public static Framework defaultOsgiLaunch(Map<String, String> configuration) {
+               Optional<FrameworkFactory> frameworkFactory = ServiceLoader.load(FrameworkFactory.class).findFirst();
+               if (frameworkFactory.isEmpty())
+                       throw new IllegalStateException("No framework factory found");
+               return defaultOsgiLaunch(frameworkFactory.get(), configuration);
+       }
+
+       /** Launch an OSGi framework. OSGi Boot initialisation is NOT performed. */
+       public static Framework defaultOsgiLaunch(FrameworkFactory frameworkFactory, Map<String, String> configuration) {
+               // start OSGi
+               Framework framework = frameworkFactory.newFramework(configuration);
+               try {
+                       framework.start();
+               } catch (BundleException e) {
+                       throw new IllegalStateException("Cannot start OSGi framework", e);
+               }
+               return framework;
+       }
+
        /*
         * BEAN METHODS
         */