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
.BundleException
;
26 import org
.osgi
.framework
.FrameworkUtil
;
27 import org
.osgi
.framework
.Version
;
28 import org
.osgi
.framework
.namespace
.PackageNamespace
;
29 import org
.osgi
.framework
.wiring
.BundleRevision
;
30 import org
.osgi
.framework
.wiring
.BundleWire
;
31 import org
.osgi
.framework
.wiring
.BundleWiring
;
33 import jdk
.jshell
.spi
.ExecutionControl
;
34 import jdk
.jshell
.spi
.ExecutionControlProvider
;
35 import jdk
.jshell
.spi
.ExecutionEnv
;
37 public class OsgiExecutionControlProvider
implements ExecutionControlProvider
{
38 private final static CmsLog log
= CmsLog
.getLog(OsgiExecutionControlProvider
.class);
40 public final static String PROVIDER_NAME
= "osgi";
41 public final static String BUNDLE_PARAMETER
= "bundle";
44 public String
name() {
49 public Map
<String
, String
> defaultParameters() {
50 Map
<String
, String
> defaultParameters
= new HashMap
<>();
51 defaultParameters
.put(BUNDLE_PARAMETER
, null);
52 return defaultParameters
;
56 public ExecutionControl
generate(ExecutionEnv env
, Map
<String
, String
> parameters
) throws Throwable
{
57 Long bundleId
= Long
.parseLong(parameters
.get(BUNDLE_PARAMETER
));
58 Bundle fromBundle
= getBundleFromId(bundleId
);
60 BundleWiring fromBundleWiring
= fromBundle
.adapt(BundleWiring
.class);
61 ClassLoader fromBundleClassLoader
= fromBundleWiring
.getClassLoader();
63 ExecutionControl executionControl
= new CmsExecutionControl(env
,
64 new WrappingLoaderDelegate(env
, fromBundleClassLoader
));
65 log
.trace(() -> "JShell from " + fromBundle
.getSymbolicName() + "_" + fromBundle
.getVersion() + " ["
66 + fromBundle
.getBundleId() + "]");
67 return executionControl
;
70 public static Bundle
getBundleFromSn(String symbolicName
) {
71 BundleContext bc
= FrameworkUtil
.getBundle(OsgiExecutionControlProvider
.class).getBundleContext();
72 Objects
.requireNonNull(symbolicName
);
73 NavigableMap
<Version
, Bundle
> bundles
= new TreeMap
<Version
, Bundle
>();
74 for (Bundle b
: bc
.getBundles()) {
75 if (symbolicName
.equals(b
.getSymbolicName()))
76 bundles
.put(b
.getVersion(), b
);
78 if (bundles
.isEmpty())
80 Bundle fromBundle
= bundles
.lastEntry().getValue();
84 public static Bundle
getBundleFromId(Long bundleId
) {
85 BundleContext bc
= FrameworkUtil
.getBundle(OsgiExecutionControlProvider
.class).getBundleContext();
86 Bundle fromBundle
= bc
.getBundle(bundleId
);
90 public static Path
getBundleStartupScript(Long bundleId
) {
91 BundleContext bc
= FrameworkUtil
.getBundle(OsgiExecutionControlProvider
.class).getBundleContext();
92 Bundle fromBundle
= bc
.getBundle(bundleId
);
94 int bundleState
= fromBundle
.getState();
95 if (Bundle
.INSTALLED
== bundleState
)
96 throw new IllegalStateException("Bundle " + fromBundle
.getSymbolicName() + " is not resolved");
97 if (Bundle
.RESOLVED
== bundleState
) {
100 } catch (BundleException e
) {
101 throw new IllegalStateException("Cannot start bundle " + fromBundle
.getSymbolicName(), e
);
103 while (Bundle
.ACTIVE
!= fromBundle
.getState())
106 } catch (InterruptedException e
) {
107 // we assume the session has been closed
108 throw new RuntimeException("Bundle " + fromBundle
.getSymbolicName() + " is not active", e
);
112 Path bundleStartupScript
= fromBundle
.getDataFile("BUNDLE.jsh").toPath();
114 BundleWiring fromBundleWiring
= fromBundle
.adapt(BundleWiring
.class);
115 ClassLoader fromBundleClassLoader
= fromBundleWiring
.getClassLoader();
117 Set
<String
> packagesToImport
= new TreeSet
<>();
119 // from bundle packages
120 for (Package pkg
: fromBundleClassLoader
.getDefinedPackages()) {
121 packagesToImport
.add(pkg
.getName());
124 // List<BundleWire> exportedWires = fromBundleWiring.getProvidedWires(BundleRevision.PACKAGE_NAMESPACE);
125 // for (BundleWire bw : exportedWires) {
126 // packagesToImport.add(bw.getCapability().getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE).toString());
129 List
<BundleWire
> importedWires
= fromBundleWiring
.getRequiredWires(BundleRevision
.PACKAGE_NAMESPACE
);
130 for (BundleWire bw
: importedWires
) {
131 packagesToImport
.add(bw
.getCapability().getAttributes().get(PackageNamespace
.PACKAGE_NAMESPACE
).toString());
134 try (Writer writer
= Files
.newBufferedWriter(bundleStartupScript
, StandardCharsets
.UTF_8
)) {
135 for (String p
: packagesToImport
) {
136 writer
.write("import " + p
+ ".*;\n");
141 import jdk.jshell.spi.ExecutionEnv;
142 import java.util.function.*;
144 /** Redirected standard IO. */
146 final static InputStream in = new Supplier<InputStream>() {
149 public InputStream get() {
150 return ((ExecutionEnv) getClass().getClassLoader()).userIn();
154 final static PrintStream out = new Supplier<PrintStream>() {
157 public PrintStream get() {
158 return ((ExecutionEnv) getClass().getClassLoader()).userOut();
162 final static PrintStream err = new Supplier<PrintStream>() {
165 public PrintStream get() {
166 return ((ExecutionEnv) getClass().getClassLoader()).userErr();
174 } catch (IOException e
) {
175 throw new RuntimeException("Cannot writer bundle startup script to " + bundleStartupScript
, e
);
178 return bundleStartupScript
;
181 public static String
getBundleClasspath(Long bundleId
) throws IOException
{
182 BundleContext bc
= FrameworkUtil
.getBundle(OsgiExecutionControlProvider
.class).getBundleContext();
183 String framework
= bc
.getProperty("osgi.framework");
184 Path frameworkLocation
= Paths
.get(URI
.create(framework
)).toAbsolutePath();
185 Bundle fromBundle
= bc
.getBundle(bundleId
);
187 BundleWiring fromBundleWiring
= fromBundle
.adapt(BundleWiring
.class);
189 Set
<Bundle
> bundlesToAddToCompileClasspath
= new TreeSet
<>();
192 bundlesToAddToCompileClasspath
.add(fromBundle
);
194 List
<BundleWire
> bundleWires
= fromBundleWiring
.getRequiredWires(BundleRevision
.PACKAGE_NAMESPACE
);
195 for (BundleWire bw
: bundleWires
) {
196 bundlesToAddToCompileClasspath
.add(bw
.getProviderWiring().getBundle());
199 StringJoiner classpath
= new StringJoiner(File
.pathSeparator
);
200 bundles
: for (Bundle b
: bundlesToAddToCompileClasspath
) {
201 if (b
.getBundleId() == 0) {// system bundle
202 classpath
.add(frameworkLocation
.toString());
205 Path p
= bundleToPath(frameworkLocation
, b
);
207 classpath
.add(p
.toString());
210 return classpath
.toString();
213 static Path
bundleToPath(Path frameworkLocation
, Bundle bundle
) throws IOException
{
214 String location
= bundle
.getLocation();
215 if (location
.startsWith("initial@reference:file:")) {
216 location
= location
.substring("initial@reference:file:".length());
217 Path p
= frameworkLocation
.getParent().resolve(location
).toAbsolutePath();
218 if (Files
.exists(p
)) {
220 // TODO load dev.properties from OSGi configuration directory
221 if (Files
.isDirectory(p
))
222 p
= p
.resolve("bin");
225 log
.warn("Ignore bundle " + p
+ " as it does not exist");
229 Path p
= Paths
.get(location
);