]> git.argeo.org Git - lgpl/argeo-commons.git/blob - osgi/OsgiExecutionControlProvider.java
Prepare next development cycle
[lgpl/argeo-commons.git] / osgi / OsgiExecutionControlProvider.java
1 package org.argeo.internal.cms.jshell.osgi;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.Writer;
6 import java.net.URI;
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;
13 import java.util.Map;
14 import java.util.NavigableMap;
15 import java.util.Objects;
16 import java.util.Set;
17 import java.util.StringJoiner;
18 import java.util.TreeMap;
19 import java.util.TreeSet;
20
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;
31
32 import jdk.jshell.spi.ExecutionControl;
33 import jdk.jshell.spi.ExecutionControlProvider;
34 import jdk.jshell.spi.ExecutionEnv;
35
36 public class OsgiExecutionControlProvider implements ExecutionControlProvider {
37 private final static CmsLog log = CmsLog.getLog(OsgiExecutionControlProvider.class);
38
39 public final static String PROVIDER_NAME = "osgi";
40 public final static String BUNDLE_PARAMETER = "bundle";
41
42 @Override
43 public String name() {
44 return PROVIDER_NAME;
45 }
46
47 @Override
48 public Map<String, String> defaultParameters() {
49 Map<String, String> defaultParameters = new HashMap<>();
50 defaultParameters.put(BUNDLE_PARAMETER, null);
51 return defaultParameters;
52 }
53
54 @Override
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);
58
59 BundleWiring fromBundleWiring = fromBundle.adapt(BundleWiring.class);
60 ClassLoader fromBundleClassLoader = fromBundleWiring.getClassLoader();
61
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;
67 }
68
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);
76 }
77 if (bundles.isEmpty())
78 return null;
79 Bundle fromBundle = bundles.lastEntry().getValue();
80 return fromBundle;
81 }
82
83 public static Bundle getBundleFromId(Long bundleId) {
84 BundleContext bc = FrameworkUtil.getBundle(OsgiExecutionControlProvider.class).getBundleContext();
85 Bundle fromBundle = bc.getBundle(bundleId);
86 return fromBundle;
87 }
88
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();
93
94 BundleWiring fromBundleWiring = fromBundle.adapt(BundleWiring.class);
95 ClassLoader fromBundleClassLoader = fromBundleWiring.getClassLoader();
96
97 Set<String> packagesToImport = new TreeSet<>();
98
99 // from bundle packages
100 for (Package pkg : fromBundleClassLoader.getDefinedPackages()) {
101 packagesToImport.add(pkg.getName());
102 }
103
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());
107 }
108
109 try (Writer writer = Files.newBufferedWriter(bundleStartupScript, StandardCharsets.UTF_8)) {
110 for (String p : packagesToImport) {
111 writer.write("import " + p + ".*;\n");
112 }
113
114 String std = """
115 import jdk.jshell.spi.ExecutionEnv;
116
117 /** Redirected standard IO. */
118 public class Std {
119 final static InputStream in = new Supplier<InputStream>() {
120
121 @Override
122 public InputStream get() {
123 return ((ExecutionEnv) getClass().getClassLoader()).userIn();
124 }
125
126 }.get();
127 final static PrintStream out = new Supplier<PrintStream>() {
128
129 @Override
130 public PrintStream get() {
131 return ((ExecutionEnv) getClass().getClassLoader()).userOut();
132 }
133
134 }.get();
135 final static PrintStream err = new Supplier<PrintStream>() {
136
137 @Override
138 public PrintStream get() {
139 return ((ExecutionEnv) getClass().getClassLoader()).userErr();
140 }
141
142 }.get();
143
144 }
145 """;
146 writer.write(std);
147 } catch (IOException e) {
148 throw new RuntimeException("Cannot writer bundle startup script to " + bundleStartupScript, e);
149 }
150
151 return bundleStartupScript;
152 }
153
154 public static String getBundleClasspath(Long bundleId) throws IOException {
155 String framework = System.getProperty("osgi.framework");
156 Path frameworkLocation = Paths.get(URI.create(framework)).toAbsolutePath();
157 BundleContext bc = FrameworkUtil.getBundle(OsgiExecutionControlProvider.class).getBundleContext();
158 Bundle fromBundle = bc.getBundle(bundleId);
159
160 BundleWiring fromBundleWiring = fromBundle.adapt(BundleWiring.class);
161
162 Set<Bundle> bundlesToAddToCompileClasspath = new TreeSet<>();
163
164 // from bundle
165 bundlesToAddToCompileClasspath.add(fromBundle);
166
167 List<BundleWire> bundleWires = fromBundleWiring.getRequiredWires(BundleRevision.PACKAGE_NAMESPACE);
168 for (BundleWire bw : bundleWires) {
169 bundlesToAddToCompileClasspath.add(bw.getProviderWiring().getBundle());
170 }
171
172 StringJoiner classpath = new StringJoiner(File.pathSeparator);
173 bundles: for (Bundle b : bundlesToAddToCompileClasspath) {
174 if (b.getBundleId() == 0) {// system bundle
175 classpath.add(frameworkLocation.toString());
176 continue bundles;
177 }
178 Path p = bundleToPath(frameworkLocation, b);
179 classpath.add(p.toString());
180 }
181
182 return classpath.toString();
183 }
184
185 static Path bundleToPath(Path frameworkLocation, Bundle bundle) throws IOException {
186 String location = bundle.getLocation();
187 if (location.startsWith("initial@reference:file:")) {
188 location = location.substring("initial@reference:file:".length());
189 Path p = frameworkLocation.getParent().resolve(location).toRealPath();
190 // TODO load dev.properties from OSGi configuration directory
191 if (Files.isDirectory(p))
192 p = p.resolve("bin");
193 return p;
194 }
195 Path p = Paths.get(location);
196 return p;
197 }
198 }