1 package org
.argeo
.internal
.cms
.jshell
.osgi
;
4 import java
.io
.IOException
;
7 import java
.nio
.charset
.StandardCharsets
;
8 import java
.nio
.file
.Files
;
9 import java
.nio
.file
.Path
;
10 import java
.nio
.file
.Paths
;
11 import java
.util
.HashMap
;
12 import java
.util
.List
;
14 import java
.util
.NavigableMap
;
15 import java
.util
.Objects
;
17 import java
.util
.StringJoiner
;
18 import java
.util
.TreeMap
;
19 import java
.util
.TreeSet
;
21 import org
.argeo
.api
.cms
.CmsLog
;
22 import org
.argeo
.cms
.jshell
.CmsExecutionControl
;
23 import org
.osgi
.framework
.Bundle
;
24 import org
.osgi
.framework
.BundleContext
;
25 import org
.osgi
.framework
.FrameworkUtil
;
26 import org
.osgi
.framework
.Version
;
27 import org
.osgi
.framework
.namespace
.PackageNamespace
;
28 import org
.osgi
.framework
.wiring
.BundleRevision
;
29 import org
.osgi
.framework
.wiring
.BundleWire
;
30 import org
.osgi
.framework
.wiring
.BundleWiring
;
32 import jdk
.jshell
.spi
.ExecutionControl
;
33 import jdk
.jshell
.spi
.ExecutionControlProvider
;
34 import jdk
.jshell
.spi
.ExecutionEnv
;
36 public class OsgiExecutionControlProvider
implements ExecutionControlProvider
{
37 private final static CmsLog log
= CmsLog
.getLog(OsgiExecutionControlProvider
.class);
39 public final static String PROVIDER_NAME
= "osgi";
40 public final static String BUNDLE_PARAMETER
= "bundle";
43 public String
name() {
48 public Map
<String
, String
> defaultParameters() {
49 Map
<String
, String
> defaultParameters
= new HashMap
<>();
50 defaultParameters
.put(BUNDLE_PARAMETER
, null);
51 return defaultParameters
;
55 public ExecutionControl
generate(ExecutionEnv env
, Map
<String
, String
> parameters
) throws Throwable
{
56 Long bundleId
= Long
.parseLong(parameters
.get(BUNDLE_PARAMETER
));
57 Bundle fromBundle
= getBundleFromId(bundleId
);
59 BundleWiring fromBundleWiring
= fromBundle
.adapt(BundleWiring
.class);
60 ClassLoader fromBundleClassLoader
= fromBundleWiring
.getClassLoader();
62 ExecutionControl executionControl
= new CmsExecutionControl(env
,
63 new WrappingLoaderDelegate(env
, fromBundleClassLoader
));
64 log
.trace(() -> "JShell from " + fromBundle
.getSymbolicName() + "_" + fromBundle
.getVersion() + " ["
65 + fromBundle
.getBundleId() + "]");
66 return executionControl
;
69 public static Bundle
getBundleFromSn(String symbolicName
) {
70 BundleContext bc
= FrameworkUtil
.getBundle(OsgiExecutionControlProvider
.class).getBundleContext();
71 Objects
.requireNonNull(symbolicName
);
72 NavigableMap
<Version
, Bundle
> bundles
= new TreeMap
<Version
, Bundle
>();
73 for (Bundle b
: bc
.getBundles()) {
74 if (symbolicName
.equals(b
.getSymbolicName()))
75 bundles
.put(b
.getVersion(), b
);
77 if (bundles
.isEmpty())
79 Bundle fromBundle
= bundles
.lastEntry().getValue();
83 public static Bundle
getBundleFromId(Long bundleId
) {
84 BundleContext bc
= FrameworkUtil
.getBundle(OsgiExecutionControlProvider
.class).getBundleContext();
85 Bundle fromBundle
= bc
.getBundle(bundleId
);
89 public static Path
getBundleStartupScript(Long bundleId
) {
90 BundleContext bc
= FrameworkUtil
.getBundle(OsgiExecutionControlProvider
.class).getBundleContext();
91 Bundle fromBundle
= bc
.getBundle(bundleId
);
92 Path bundleStartupScript
= fromBundle
.getDataFile("BUNDLE.jsh").toPath();
94 BundleWiring fromBundleWiring
= fromBundle
.adapt(BundleWiring
.class);
95 ClassLoader fromBundleClassLoader
= fromBundleWiring
.getClassLoader();
97 Set
<String
> packagesToImport
= new TreeSet
<>();
99 // from bundle packages
100 for (Package pkg
: fromBundleClassLoader
.getDefinedPackages()) {
101 packagesToImport
.add(pkg
.getName());
104 List
<BundleWire
> bundleWires
= fromBundleWiring
.getRequiredWires(BundleRevision
.PACKAGE_NAMESPACE
);
105 for (BundleWire bw
: bundleWires
) {
106 packagesToImport
.add(bw
.getCapability().getAttributes().get(PackageNamespace
.PACKAGE_NAMESPACE
).toString());
109 try (Writer writer
= Files
.newBufferedWriter(bundleStartupScript
, StandardCharsets
.UTF_8
)) {
110 for (String p
: packagesToImport
) {
111 writer
.write("import " + p
+ ".*;\n");
116 import jdk.jshell.spi.ExecutionEnv;
117 import java.util.function.*;
119 /** Redirected standard IO. */
121 final static InputStream in = new Supplier<InputStream>() {
124 public InputStream get() {
125 return ((ExecutionEnv) getClass().getClassLoader()).userIn();
129 final static PrintStream out = new Supplier<PrintStream>() {
132 public PrintStream get() {
133 return ((ExecutionEnv) getClass().getClassLoader()).userOut();
137 final static PrintStream err = new Supplier<PrintStream>() {
140 public PrintStream get() {
141 return ((ExecutionEnv) getClass().getClassLoader()).userErr();
149 } catch (IOException e
) {
150 throw new RuntimeException("Cannot writer bundle startup script to " + bundleStartupScript
, e
);
153 return bundleStartupScript
;
156 public static String
getBundleClasspath(Long bundleId
) throws IOException
{
157 String framework
= System
.getProperty("osgi.framework");
158 Path frameworkLocation
= Paths
.get(URI
.create(framework
)).toAbsolutePath();
159 BundleContext bc
= FrameworkUtil
.getBundle(OsgiExecutionControlProvider
.class).getBundleContext();
160 Bundle fromBundle
= bc
.getBundle(bundleId
);
162 BundleWiring fromBundleWiring
= fromBundle
.adapt(BundleWiring
.class);
164 Set
<Bundle
> bundlesToAddToCompileClasspath
= new TreeSet
<>();
167 bundlesToAddToCompileClasspath
.add(fromBundle
);
169 List
<BundleWire
> bundleWires
= fromBundleWiring
.getRequiredWires(BundleRevision
.PACKAGE_NAMESPACE
);
170 for (BundleWire bw
: bundleWires
) {
171 bundlesToAddToCompileClasspath
.add(bw
.getProviderWiring().getBundle());
174 StringJoiner classpath
= new StringJoiner(File
.pathSeparator
);
175 bundles
: for (Bundle b
: bundlesToAddToCompileClasspath
) {
176 if (b
.getBundleId() == 0) {// system bundle
177 classpath
.add(frameworkLocation
.toString());
180 Path p
= bundleToPath(frameworkLocation
, b
);
181 classpath
.add(p
.toString());
184 return classpath
.toString();
187 static Path
bundleToPath(Path frameworkLocation
, Bundle bundle
) throws IOException
{
188 String location
= bundle
.getLocation();
189 if (location
.startsWith("initial@reference:file:")) {
190 location
= location
.substring("initial@reference:file:".length());
191 Path p
= frameworkLocation
.getParent().resolve(location
).toRealPath();
192 // TODO load dev.properties from OSGi configuration directory
193 if (Files
.isDirectory(p
))
194 p
= p
.resolve("bin");
197 Path p
= Paths
.get(location
);