Can specify JShell bundle
[lgpl/argeo-commons.git] / org.argeo.cms.jshell / src / org / argeo / internal / cms / jshell / osgi / OsgiExecutionControlProvider.java
index 673233cdee54dc92f3b5761318f13abc444596af..f785919b2455c6d0ff7c6d00173b24603a750856 100644 (file)
@@ -1,11 +1,20 @@
 package org.argeo.internal.cms.jshell.osgi;
 
+import java.io.File;
+import java.io.IOException;
+import java.io.Writer;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.NavigableMap;
 import java.util.Objects;
 import java.util.Set;
+import java.util.StringJoiner;
 import java.util.TreeMap;
 import java.util.TreeSet;
 
@@ -46,45 +55,119 @@ public class OsgiExecutionControlProvider implements ExecutionControlProvider {
        public ExecutionControl generate(ExecutionEnv env, Map<String, String> parameters) throws Throwable {
                // TODO find a better way to get a default bundle context
                // NOTE: the related default bundle has to be started
-               BundleContext bc = FrameworkUtil.getBundle(OsgiExecutionControlProvider.class).getBundleContext();
 
-               String symbolicName = parameters.get(BUNDLE_PARAMETER);
+//             String symbolicName = parameters.get(BUNDLE_PARAMETER);
+//             Bundle fromBundle = getBundleFromSn(symbolicName);
+
+               Long bundleId = Long.parseLong(parameters.get(BUNDLE_PARAMETER));
+               Bundle fromBundle = getBundleFromId(bundleId);
+
+               BundleWiring fromBundleWiring = fromBundle.adapt(BundleWiring.class);
+               ClassLoader fromBundleClassLoader = fromBundleWiring.getClassLoader();
+
+               // use the bundle classloade as context classloader
+               Thread.currentThread().setContextClassLoader(fromBundleClassLoader);
+               
+               ExecutionControl executionControl = new DirectExecutionControl(
+                               new WrappingLoaderDelegate(fromBundleClassLoader));
+               log.debug("JShell from " + fromBundle.getSymbolicName() + "_" + fromBundle.getVersion() + " ["
+                               + fromBundle.getBundleId() + "]");
+               return executionControl;
+       }
+
+       public static Bundle getBundleFromSn(String symbolicName) {
+               BundleContext bc = FrameworkUtil.getBundle(OsgiExecutionControlProvider.class).getBundleContext();
                Objects.requireNonNull(symbolicName);
                NavigableMap<Version, Bundle> bundles = new TreeMap<Version, Bundle>();
                for (Bundle b : bc.getBundles()) {
                        if (symbolicName.equals(b.getSymbolicName()))
                                bundles.put(b.getVersion(), b);
                }
+               if (bundles.isEmpty())
+                       return null;
                Bundle fromBundle = bundles.lastEntry().getValue();
+               return fromBundle;
+       }
+
+       public static Bundle getBundleFromId(Long bundleId) {
+               BundleContext bc = FrameworkUtil.getBundle(OsgiExecutionControlProvider.class).getBundleContext();
+               Bundle fromBundle = bc.getBundle(bundleId);
+               return fromBundle;
+       }
+
+       public static Path getBundleStartupScript(Long bundleId) {
+               BundleContext bc = FrameworkUtil.getBundle(OsgiExecutionControlProvider.class).getBundleContext();
+               Bundle fromBundle = bc.getBundle(bundleId);
+               Path bundleStartupScript = fromBundle.getDataFile("BUNDLE.jsh").toPath();
 
                BundleWiring fromBundleWiring = fromBundle.adapt(BundleWiring.class);
                ClassLoader fromBundleClassLoader = fromBundleWiring.getClassLoader();
 
                Set<String> packagesToImport = new TreeSet<>();
-               Set<Bundle> bundlesToAddToCompileClasspath = new TreeSet<>();
 
-               // from bundle
-               bundlesToAddToCompileClasspath.add(fromBundle);
                // from bundle packages
                for (Package pkg : fromBundleClassLoader.getDefinedPackages()) {
                        packagesToImport.add(pkg.getName());
                }
 
-//             System.out.println(Arrays.asList(fromBundleClassLoader.getDefinedPackages()));
                List<BundleWire> bundleWires = fromBundleWiring.getRequiredWires(BundleRevision.PACKAGE_NAMESPACE);
                for (BundleWire bw : bundleWires) {
-//                     System.out.println(bw.getCapability().getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE));
-                       bundlesToAddToCompileClasspath.add(bw.getProviderWiring().getBundle());
                        packagesToImport.add(bw.getCapability().getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE).toString());
                }
-               log.debug("JShell from " + fromBundle.getSymbolicName() + "_" + fromBundle.getVersion() + " ["
-                               + fromBundle.getBundleId() + "]");
-               log.debug("  required packages " + packagesToImport);
-               log.debug("  required bundles " + bundlesToAddToCompileClasspath);
 
-               ExecutionControl executionControl = new DirectExecutionControl(
-                               new WrappingLoaderDelegate(fromBundleClassLoader));
-               return executionControl;
+               try (Writer writer = Files.newBufferedWriter(bundleStartupScript, StandardCharsets.UTF_8)) {
+                       for (String p : packagesToImport) {
+                               writer.write("import " + p + ".*;\n");
+                       }
+               } catch (IOException e) {
+                       throw new RuntimeException("Cannot writer bundle startup script to " + bundleStartupScript, e);
+               }
+
+               return bundleStartupScript;
        }
 
+       public static String getBundleClasspath(Long bundleId) throws IOException {
+               String framework = System.getProperty("osgi.framework");
+               Path frameworkLocation = Paths.get(URI.create(framework)).toAbsolutePath();
+               BundleContext bc = FrameworkUtil.getBundle(OsgiExecutionControlProvider.class).getBundleContext();
+               Bundle fromBundle = bc.getBundle(bundleId);
+
+               BundleWiring fromBundleWiring = fromBundle.adapt(BundleWiring.class);
+
+               Set<Bundle> bundlesToAddToCompileClasspath = new TreeSet<>();
+
+               // from bundle
+               bundlesToAddToCompileClasspath.add(fromBundle);
+
+               List<BundleWire> bundleWires = fromBundleWiring.getRequiredWires(BundleRevision.PACKAGE_NAMESPACE);
+               for (BundleWire bw : bundleWires) {
+                       bundlesToAddToCompileClasspath.add(bw.getProviderWiring().getBundle());
+               }
+
+               StringJoiner classpath = new StringJoiner(File.pathSeparator);
+               bundles: for (Bundle b : bundlesToAddToCompileClasspath) {
+                       if (b.getBundleId() == 0) {// system bundle
+                               classpath.add(frameworkLocation.toString());
+                               continue bundles;
+                       }
+                       Path p = bundleToPath(frameworkLocation, b);
+                       classpath.add(p.toString());
+               }
+
+               return classpath.toString();
+       }
+
+       static Path bundleToPath(Path frameworkLocation, Bundle bundle) throws IOException {
+               String location = bundle.getLocation();
+               if (location.startsWith("initial@reference:file:")) {
+                       location = location.substring("initial@reference:file:".length());
+                       Path p = frameworkLocation.getParent().resolve(location).toRealPath();
+                       // TODO load dev.properties from OSGi configuration directory
+                       if (Files.isDirectory(p))
+                               p = p.resolve("bin");
+                       return p;
+               }
+               Path p = Paths.get(location);
+               return p;
+       }
 }