X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms.jshell%2Fsrc%2Forg%2Fargeo%2Finternal%2Fcms%2Fjshell%2Fosgi%2FOsgiExecutionControlProvider.java;h=4a8f1685f769b0d622373cff420362902dbea124;hb=5d7ebadd9fe583fd9d3b2e4ca6e99079b99aac5a;hp=673233cdee54dc92f3b5761318f13abc444596af;hpb=2c5da70747629282585d5515720dcb1515a0011c;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.cms.jshell/src/org/argeo/internal/cms/jshell/osgi/OsgiExecutionControlProvider.java b/org.argeo.cms.jshell/src/org/argeo/internal/cms/jshell/osgi/OsgiExecutionControlProvider.java index 673233cde..4a8f1685f 100644 --- a/org.argeo.cms.jshell/src/org/argeo/internal/cms/jshell/osgi/OsgiExecutionControlProvider.java +++ b/org.argeo.cms.jshell/src/org/argeo/internal/cms/jshell/osgi/OsgiExecutionControlProvider.java @@ -1,17 +1,28 @@ 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; import org.argeo.api.cms.CmsLog; +import org.argeo.cms.jshell.CmsExecutionControl; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.Version; import org.osgi.framework.namespace.PackageNamespace; @@ -19,7 +30,6 @@ import org.osgi.framework.wiring.BundleRevision; import org.osgi.framework.wiring.BundleWire; import org.osgi.framework.wiring.BundleWiring; -import jdk.jshell.execution.DirectExecutionControl; import jdk.jshell.spi.ExecutionControl; import jdk.jshell.spi.ExecutionControlProvider; import jdk.jshell.spi.ExecutionEnv; @@ -44,47 +54,172 @@ public class OsgiExecutionControlProvider implements ExecutionControlProvider { @Override public ExecutionControl generate(ExecutionEnv env, Map 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(); + Long bundleId = Long.parseLong(parameters.get(BUNDLE_PARAMETER)); + Bundle fromBundle = getBundleFromId(bundleId); + + BundleWiring fromBundleWiring = fromBundle.adapt(BundleWiring.class); + ClassLoader fromBundleClassLoader = fromBundleWiring.getClassLoader(); + + ExecutionControl executionControl = new CmsExecutionControl(env, + new WrappingLoaderDelegate(env, fromBundleClassLoader)); + log.trace(() -> "JShell from " + fromBundle.getSymbolicName() + "_" + fromBundle.getVersion() + " [" + + fromBundle.getBundleId() + "]"); + return executionControl; + } - String symbolicName = parameters.get(BUNDLE_PARAMETER); + public static Bundle getBundleFromSn(String symbolicName) { + BundleContext bc = FrameworkUtil.getBundle(OsgiExecutionControlProvider.class).getBundleContext(); Objects.requireNonNull(symbolicName); NavigableMap bundles = new TreeMap(); 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); + + int bundleState = fromBundle.getState(); + if (Bundle.INSTALLED == bundleState) + throw new IllegalStateException("Bundle " + fromBundle.getSymbolicName() + " is not resolved"); + if (Bundle.RESOLVED == bundleState) { + try { + fromBundle.start(); + } catch (BundleException e) { + throw new IllegalStateException("Cannot start bundle " + fromBundle.getSymbolicName(), e); + } + while (Bundle.ACTIVE != fromBundle.getState()) + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // we assume the session has been closed + throw new RuntimeException("Bundle " + fromBundle.getSymbolicName() + " is not active", e); + } + } + + Path bundleStartupScript = fromBundle.getDataFile("BUNDLE.jsh").toPath(); BundleWiring fromBundleWiring = fromBundle.adapt(BundleWiring.class); ClassLoader fromBundleClassLoader = fromBundleWiring.getClassLoader(); Set packagesToImport = new TreeSet<>(); - Set 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 exportedWires = fromBundleWiring.getProvidedWires(BundleRevision.PACKAGE_NAMESPACE); +// for (BundleWire bw : exportedWires) { +// packagesToImport.add(bw.getCapability().getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE).toString()); +// } + + List importedWires = fromBundleWiring.getRequiredWires(BundleRevision.PACKAGE_NAMESPACE); + for (BundleWire bw : importedWires) { + packagesToImport.add(bw.getCapability().getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE).toString()); + } + + try (Writer writer = Files.newBufferedWriter(bundleStartupScript, StandardCharsets.UTF_8)) { + for (String p : packagesToImport) { + writer.write("import " + p + ".*;\n"); + } + + String std = """ + /open DEFAULT + import jdk.jshell.spi.ExecutionEnv; + import java.util.function.*; + + /** Redirected standard IO. */ + public class Std { + final static InputStream in = new Supplier() { + + @Override + public InputStream get() { + return ((ExecutionEnv) getClass().getClassLoader()).userIn(); + } + + }.get(); + final static PrintStream out = new Supplier() { + + @Override + public PrintStream get() { + return ((ExecutionEnv) getClass().getClassLoader()).userOut(); + } + + }.get(); + final static PrintStream err = new Supplier() { + + @Override + public PrintStream get() { + return ((ExecutionEnv) getClass().getClassLoader()).userErr(); + } + + }.get(); + + } + """; + writer.write(std); + } 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 bundlesToAddToCompileClasspath = new TreeSet<>(); + + // from bundle + bundlesToAddToCompileClasspath.add(fromBundle); + List 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; + 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; + } }