]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.init/src/org/argeo/init/osgi/SubFrameworkActivator.java
Simplify multi-runtime
[lgpl/argeo-commons.git] / org.argeo.init / src / org / argeo / init / osgi / SubFrameworkActivator.java
1 package org.argeo.init.osgi;
2
3 import static java.lang.System.Logger.Level.INFO;
4
5 import java.io.File;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.lang.System.Logger;
9 import java.net.URL;
10 import java.nio.file.Path;
11 import java.nio.file.Paths;
12 import java.util.Collections;
13 import java.util.Dictionary;
14 import java.util.HashMap;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Optional;
19 import java.util.UUID;
20 import java.util.function.Function;
21 import java.util.stream.Collectors;
22
23 import org.argeo.api.init.InitConstants;
24 import org.argeo.api.init.RuntimeManager;
25 import org.osgi.framework.Bundle;
26 import org.osgi.framework.BundleActivator;
27 import org.osgi.framework.BundleContext;
28 import org.osgi.framework.BundleException;
29 import org.osgi.framework.Constants;
30 import org.osgi.framework.FrameworkEvent;
31 import org.osgi.framework.connect.ConnectContent;
32 import org.osgi.framework.connect.ConnectFrameworkFactory;
33 import org.osgi.framework.connect.ConnectModule;
34 import org.osgi.framework.connect.ModuleConnector;
35 import org.osgi.framework.launch.Framework;
36 import org.osgi.framework.wiring.BundleWiring;
37
38 public class SubFrameworkActivator implements BundleActivator {
39 private final static Logger logger = System.getLogger(SubFrameworkActivator.class.getName());
40
41 // private final static String EQUINOX_FRAMEWORK_CLASS = "org.eclipse.osgi.launch.Equinox";
42 private final static String EQUINOX_FRAMEWORK_FACTORY_CLASS = "org.eclipse.osgi.launch.EquinoxFactory";
43
44 // private ClassLoader bundleClassLoader;
45 // private ClassLoader subFrameworkClassLoader;
46 private BundleContext foreignBundleContext;
47
48 private ConnectFrameworkFactory frameworkFactory;
49
50 private Map<UUID, Framework> subFrameworks = Collections.synchronizedMap(new HashMap<>());
51
52 private UUID foreignFrameworkUuid;
53
54 @Override
55 public void start(BundleContext context) throws Exception {
56 this.foreignBundleContext = context;
57 foreignFrameworkUuid = UUID.fromString(foreignBundleContext.getProperty(Constants.FRAMEWORK_UUID));
58
59 try {
60 // Bundle bundle = context.getBundle();
61 // ClassLoader bundleClassLoader = bundle.adapt(BundleWiring.class).getClassLoader();
62 // subFrameworkClassLoader = new URLClassLoader(new URL[0], bundleClassLoader);
63
64 @SuppressWarnings("unchecked")
65 Class<? extends ConnectFrameworkFactory> frameworkFactoryClass = (Class<? extends ConnectFrameworkFactory>) Framework.class
66 .getClassLoader().loadClass(EQUINOX_FRAMEWORK_FACTORY_CLASS);
67 frameworkFactory = frameworkFactoryClass.getConstructor().newInstance();
68
69 boolean test = false;
70 if (test)
71 new Thread() {
72
73 @Override
74 public void run() {
75 for (int i = 0; i < 5; i++) {
76 Map<String, String> config = new HashMap<>();
77 Path basePase = Paths.get(System.getProperty("user.home"), ".config/argeo/test/",
78 "test" + i);
79 config.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA,
80 basePase.resolve(RuntimeManager.STATE).toString());
81 config.put(InitConstants.PROP_OSGI_INSTANCE_AREA,
82 basePase.resolve(RuntimeManager.DATA).toString());
83 config.put("argeo.host", "host" + i);
84 config.put("osgi.console", "host" + i + ":2023");
85 createFramework(config);
86 }
87 }
88
89 }.start();
90 } catch (Exception e) {
91 e.printStackTrace();
92 throw e;
93 }
94 }
95
96 Framework createFramework(Map<String, String> config) {
97 try {
98 URL bundleConfigUrl = foreignBundleContext.getBundle().getEntry("config.ini");
99 try (InputStream in = bundleConfigUrl.openStream()) {
100 RuntimeManager.loadConfig(in, config);
101 }
102
103 // Equinox
104 // config.put("osgi.frameworkParentClassloader", "current");
105 // config.put("osgi.parentClassLoader", "app");
106 // config.put("osgi.contextClassLoaderParent", "app");
107
108 ModuleConnector moduleConnector = new ParentBundleModuleConnector(foreignBundleContext);
109
110 // URL frameworkUrl = URI.create(bundleContext.getProperty("osgi.framework")).toURL();
111 // URLClassLoader frameworkClassLoader = new URLClassLoader(new URL[] { frameworkUrl, });
112 // Class<? extends Framework> frameworkClass = (Class<? extends Framework>) frameworkClassLoader
113 // .loadClass(EQUINOX_FRAMEWORK_CLASS);
114 // Framework framework = frameworkClass.getConstructor(Map.class, ModuleConnector.class).newInstance(config,
115 // moduleConnector);
116
117 config.put(InitConstants.PROP_ARGEO_OSGI_PARENT_UUID, foreignFrameworkUuid.toString());
118 Framework framework = frameworkFactory.newFramework(config, moduleConnector);
119
120 framework.init((e) -> {
121 UUID frameworkUuid = UUID
122 .fromString(framework.getBundleContext().getProperty(Constants.FRAMEWORK_UUID));
123 if (e.getType() == FrameworkEvent.STOPPED) {
124 subFrameworks.remove(frameworkUuid);
125 logger.log(INFO, "Removed subframework " + frameworkUuid + " in parent " + foreignFrameworkUuid);
126 }
127 });
128
129 for (Bundle b : foreignBundleContext.getBundles()) {
130 if (b.getBundleId() == 0)
131 continue;
132 String location = b.getLocation();
133 if (location.contains("/org.argeo.tp/") //
134 || location.contains("/org.argeo.tp.sys/") //
135 || location.contains("/org.argeo.tp.httpd/") //
136 || location.contains("/org.argeo.tp.sshd/") //
137 ) {
138 framework.getBundleContext().installBundle(b.getLocation());
139 }
140 }
141
142 OsgiBoot osgiBoot = new OsgiBoot(framework.getBundleContext());
143 osgiBoot.install();
144 // OsgiBoot.uninstallBundles(osgiBoot.getBundleContext(), "org.argeo.api.cms");
145 // OsgiBoot.uninstallBundles(osgiBoot.getBundleContext(), "org.osgi.service.useradmin");
146 // osgiBoot.getBundleContext()
147 // .installBundle("initial@reference:file:../../../../../argeo-commons/org.argeo.api.cms/");
148 // osgiBoot.getBundleContext().installBundle(
149 // "reference:file:/usr/local/share/a2/osgi/equinox/org.argeo.tp.osgi/org.osgi.service.useradmin.1.1.jar");
150 osgiBoot.refresh();
151 framework.start();
152 osgiBoot.startBundles();
153
154 // for (Bundle b : framework.getBundleContext().getBundles()) {
155 // BundleContext bc = b.getBundleContext();
156 // if (bc == null)
157 // System.err.println(b.getSymbolicName() + " BC null");
158 // }
159
160 UUID frameworkUuid = UUID.fromString(framework.getBundleContext().getProperty(Constants.FRAMEWORK_UUID));
161 subFrameworks.put(frameworkUuid, framework);
162 logger.log(INFO, "Created subframework " + frameworkUuid + " in parent " + foreignFrameworkUuid);
163 return framework;
164 } catch (Exception e) {
165 throw new IllegalStateException("Cannot start framework", e);
166 }
167 }
168
169 @Override
170 public void stop(BundleContext context) throws Exception {
171 for (Iterator<Framework> it = subFrameworks.values().iterator(); it.hasNext();) {
172 Framework framework = it.next();
173 framework.stop();
174 it.remove();
175
176 }
177 // for (Framework framework : subFrameworks.values()) {
178 // framework.stop();
179 // }
180 subFrameworks.clear();
181 foreignBundleContext = null;
182 frameworkFactory = null;
183 }
184
185 static class ParentBundleModuleConnector implements ModuleConnector {
186 private final BundleContext foreignBundleContext;
187 private BundleContext localBundleContext;
188
189 public ParentBundleModuleConnector(BundleContext foreignBundleContext) {
190 this.foreignBundleContext = foreignBundleContext;
191 }
192
193 @Override
194 public Optional<BundleActivator> newBundleActivator() {
195 return Optional.of(new BundleActivator() {
196 @Override
197 public void start(BundleContext context) throws Exception {
198 ParentBundleModuleConnector.this.localBundleContext = context;
199 }
200
201 @Override
202 public void stop(BundleContext context) throws Exception {
203 ParentBundleModuleConnector.this.localBundleContext = null;
204 }
205
206 });
207 }
208
209 @Override
210 public void initialize(File storage, Map<String, String> configuration) {
211 }
212
213 @Override
214 public Optional<ConnectModule> connect(String location) throws BundleException {
215 Bundle bundle = foreignBundleContext.getBundle(location);
216 if (bundle != null && bundle.getBundleId() != 0) {
217 // System.out.println("Foreign Bundle: " + bundle.getSymbolicName() + " " +
218 // location);
219 ConnectModule module = new ConnectModule() {
220
221 @Override
222 public ConnectContent getContent() throws IOException {
223 return new ForeignBundleConnectContent(localBundleContext, bundle);
224 }
225 };
226 return Optional.of(module);
227 }
228 return Optional.empty();
229 }
230 }
231
232 static class ForeignBundleClassLoader extends ClassLoader {// implements BundleReference {
233 private BundleContext localBundleContext;
234 private Bundle foreignBundle;
235
236 public ForeignBundleClassLoader(BundleContext localBundleContext, Bundle foreignBundle) {
237 super("Foreign bundle " + foreignBundle.toString(), Optional
238 .ofNullable(foreignBundle.adapt(BundleWiring.class)).map((bw) -> bw.getClassLoader()).orElse(null));
239 this.localBundleContext = localBundleContext;
240 this.foreignBundle = foreignBundle;
241 }
242
243 // @Override
244 protected Bundle getBundle() {
245 return localBundleContext.getBundle(foreignBundle.getLocation());
246 }
247
248 // @Override
249 // public URL getResource(String resName) {
250 // URL res = super.getResource(resName);
251 // return res;
252 // }
253 //
254 // @Override
255 // protected URL findResource(String resName) {
256 // Bundle localBundle = getBundle();
257 // if (localBundle != null) {
258 // URL res = localBundle.getEntry(resName);
259 // if (res != null)
260 // return res;
261 // }
262 // return null;
263 // }
264
265 }
266
267 static class ForeignBundleConnectContent implements ConnectContent {
268 private final Bundle foreignBundle;
269 private final ClassLoader classLoader;
270
271 public ForeignBundleConnectContent(BundleContext localBundleContext, Bundle foreignBundle) {
272 this.foreignBundle = foreignBundle;
273 this.classLoader = new ForeignBundleClassLoader(localBundleContext, foreignBundle);
274 }
275
276 @Override
277 public Optional<Map<String, String>> getHeaders() {
278 Dictionary<String, String> dict = foreignBundle.getHeaders();
279 List<String> keys = Collections.list(dict.keys());
280 Map<String, String> dictCopy = keys.stream().collect(Collectors.toMap(Function.identity(), dict::get));
281 return Optional.of(dictCopy);
282 }
283
284 @Override
285 public Iterable<String> getEntries() throws IOException {
286 List<String> lst = Collections.list(foreignBundle.findEntries("", "*", true)).stream()
287 .map((u) -> u.getPath()).toList();
288 return lst;
289 }
290
291 @Override
292 public Optional<ConnectEntry> getEntry(String path) {
293 URL u = foreignBundle.getEntry(path);
294 if (u == null) {
295 u = foreignBundle.getEntry("bin/" + path);
296 // System.err.println(u2);
297 }
298 if (u == null) {
299 if ("plugin.xml".equals(path))
300 return Optional.empty();
301 if (path.startsWith("META-INF/versions/"))
302 return Optional.empty();
303 System.err.println(foreignBundle.getSymbolicName() + " " + path + " not found");
304 return Optional.empty();
305 }
306 URL url = u;
307 ConnectEntry urlConnectEntry = new ConnectEntry() {
308
309 @Override
310 public String getName() {
311 return path;
312 }
313
314 @Override
315 public long getLastModified() {
316 return foreignBundle.getLastModified();
317 }
318
319 @Override
320 public InputStream getInputStream() throws IOException {
321 return url.openStream();
322 }
323
324 @Override
325 public long getContentLength() {
326 return -1;
327 }
328 };
329 return Optional.of(urlConnectEntry);
330 }
331
332 @Override
333 public Optional<ClassLoader> getClassLoader() {
334 ClassLoader cl;
335 // cl = bundle.adapt(BundleWiring.class).getClassLoader();
336
337 // cl = subFrameworkClassLoader;
338 cl = classLoader;
339 return Optional.of(cl);
340 // return Optional.empty();
341 }
342
343 @Override
344 public void open() throws IOException {
345 }
346
347 @Override
348 public void close() throws IOException {
349
350 }
351
352 }
353 }