]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.init/src/org/argeo/init/a2/ProvisioningManager.java
Start release cycle
[lgpl/argeo-commons.git] / org.argeo.init / src / org / argeo / init / a2 / ProvisioningManager.java
1 package org.argeo.init.a2;
2
3 import java.io.File;
4 import java.io.UnsupportedEncodingException;
5 import java.net.URI;
6 import java.net.URLDecoder;
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.ArrayList;
12 import java.util.Arrays;
13 import java.util.Collection;
14 import java.util.Collections;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.LinkedHashMap;
18 import java.util.LinkedList;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22
23 import org.argeo.init.osgi.OsgiBootUtils;
24 import org.osgi.framework.Bundle;
25 import org.osgi.framework.BundleContext;
26 import org.osgi.framework.Constants;
27 import org.osgi.framework.Version;
28 import org.osgi.framework.launch.Framework;
29 import org.osgi.framework.wiring.FrameworkWiring;
30
31 /** Loads provisioning sources into an OSGi context. */
32 public class ProvisioningManager {
33 BundleContext bc;
34 OsgiContext osgiContext;
35 List<ProvisioningSource> sources = Collections.synchronizedList(new ArrayList<>());
36
37 public ProvisioningManager(BundleContext bc) {
38 this.bc = bc;
39 osgiContext = new OsgiContext(bc);
40 osgiContext.load();
41 }
42
43 protected void addSource(ProvisioningSource source) {
44 sources.add(source);
45 }
46
47 void installWholeSource(ProvisioningSource source) {
48 Set<Bundle> updatedBundles = new HashSet<>();
49 for (A2Contribution contribution : source.listContributions(null)) {
50 for (A2Component component : contribution.components.values()) {
51 A2Module module = component.last().last();
52 Bundle bundle = installOrUpdate(module);
53 if (bundle != null)
54 updatedBundles.add(bundle);
55 }
56 }
57 FrameworkWiring frameworkWiring = bc.getBundle(0).adapt(FrameworkWiring.class);
58 frameworkWiring.refreshBundles(updatedBundles);
59 }
60
61 public void registerSource(String uri) {
62 try {
63 URI u = new URI(uri);
64
65 // XOR
66 Map<String, List<String>> properties = queryToMap(u);
67 Map<String, String> xOr = new HashMap<>();
68 for (String key : properties.keySet()) {
69 List<String> lst = properties.get(key);
70 if (lst.size() != 1)
71 throw new IllegalArgumentException("Invalid XOR definitions in " + uri);
72 xOr.put(key, lst.get(0));
73 }
74
75 if (A2Source.SCHEME_A2.equals(u.getScheme())) {
76 if (u.getHost() == null || "".equals(u.getHost())) {
77 String baseStr = u.getPath();
78 if (File.separatorChar == '\\') {// MS Windows
79 baseStr = baseStr.substring(1).replace('/', File.separatorChar);
80 }
81 Path base = Paths.get(baseStr);
82 if (Files.exists(base)) {
83 FsA2Source source = new FsA2Source(base, xOr);
84 source.load();
85 addSource(source);
86 OsgiBootUtils.info("Registered " + uri + " as source");
87 }
88 }
89 }
90 } catch (Exception e) {
91 throw new A2Exception("Cannot add source " + uri, e);
92 }
93 }
94
95 public boolean registerDefaultSource() {
96 String frameworkLocation = bc.getProperty("osgi.framework");
97 try {
98 URI frameworkLocationUri = new URI(frameworkLocation);
99 if ("file".equals(frameworkLocationUri.getScheme())) {
100 Path frameworkPath = Paths.get(frameworkLocationUri);
101 if (frameworkPath.getParent().getFileName().toString().equals(A2Contribution.BOOT)) {
102 Path base = frameworkPath.getParent().getParent();
103 String baseStr = base.toString();
104 if (File.separatorChar == '\\')// MS Windows
105 baseStr = '/' + baseStr.replace(File.separatorChar, '/');
106 URI baseUri = new URI(A2Source.SCHEME_A2, null, null, 0, baseStr, null, null);
107 registerSource(baseUri.toString());
108 OsgiBootUtils.debug("Default source from framework location " + frameworkLocation);
109 return true;
110 }
111 }
112 } catch (Exception e) {
113 OsgiBootUtils.error("Cannot register default source based on framework location " + frameworkLocation, e);
114 }
115 return false;
116 }
117
118 public void install(String spec) {
119 if (spec == null) {
120 for (ProvisioningSource source : sources) {
121 installWholeSource(source);
122 }
123 }
124 }
125
126 /** @return the new/updated bundle, or null if nothing was done. */
127 protected Bundle installOrUpdate(A2Module module) {
128 try {
129 ProvisioningSource moduleSource = module.getBranch().getComponent().getContribution().getSource();
130 Version moduleVersion = module.getVersion();
131 A2Branch osgiBranch = osgiContext.findBranch(module.getBranch().getComponent().getId(), moduleVersion);
132 if (osgiBranch == null) {
133 // Bundle bundle = bc.installBundle(module.getBranch().getCoordinates(),
134 // moduleSource.newInputStream(module.getLocator()));
135 Bundle bundle = moduleSource.install(bc, module);
136 if (OsgiBootUtils.isDebug())
137 OsgiBootUtils.debug("Installed bundle " + bundle.getLocation() + " with version " + moduleVersion);
138 return bundle;
139 } else {
140 A2Module lastOsgiModule = osgiBranch.last();
141 int compare = moduleVersion.compareTo(lastOsgiModule.getVersion());
142 if (compare > 0) {// update
143 Bundle bundle = (Bundle) lastOsgiModule.getLocator();
144 // bundle.update(moduleSource.newInputStream(module.getLocator()));
145 moduleSource.update(bundle, module);
146 OsgiBootUtils.info("Updated bundle " + bundle.getLocation() + " to version " + moduleVersion);
147 return bundle;
148 }
149 }
150 } catch (Exception e) {
151 OsgiBootUtils.error("Could not install module " + module + ": " + e.getMessage(), e);
152 }
153 return null;
154 }
155
156 public Collection<Bundle> update() {
157 boolean fragmentsUpdated = false;
158 Set<Bundle> updatedBundles = new HashSet<>();
159 bundles: for (Bundle bundle : bc.getBundles()) {
160 for (ProvisioningSource source : sources) {
161 String componentId = bundle.getSymbolicName();
162 Version version = bundle.getVersion();
163 A2Branch branch = source.findBranch(componentId, version);
164 if (branch == null)
165 continue bundles;
166 A2Module module = branch.last();
167 Version moduleVersion = module.getVersion();
168 int compare = moduleVersion.compareTo(version);
169 if (compare > 0) {// update
170 try {
171 source.update(bundle, module);
172 // bundle.update(in);
173 String fragmentHost = bundle.getHeaders().get(Constants.FRAGMENT_HOST);
174 if (fragmentHost != null)
175 fragmentsUpdated = true;
176 OsgiBootUtils.info("Updated bundle " + bundle.getLocation() + " to version " + moduleVersion);
177 updatedBundles.add(bundle);
178 } catch (Exception e) {
179 OsgiBootUtils.error("Cannot update with module " + module, e);
180 }
181 }
182 }
183 }
184 FrameworkWiring frameworkWiring = bc.getBundle(0).adapt(FrameworkWiring.class);
185 if (fragmentsUpdated)// refresh all
186 frameworkWiring.refreshBundles(null);
187 else
188 frameworkWiring.refreshBundles(updatedBundles);
189 return updatedBundles;
190 }
191
192 private static Map<String, List<String>> queryToMap(URI uri) {
193 return queryToMap(uri.getQuery());
194 }
195
196 private static Map<String, List<String>> queryToMap(String queryPart) {
197 try {
198 final Map<String, List<String>> query_pairs = new LinkedHashMap<String, List<String>>();
199 if (queryPart == null)
200 return query_pairs;
201 final String[] pairs = queryPart.split("&");
202 for (String pair : pairs) {
203 final int idx = pair.indexOf("=");
204 final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), StandardCharsets.UTF_8.name())
205 : pair;
206 if (!query_pairs.containsKey(key)) {
207 query_pairs.put(key, new LinkedList<String>());
208 }
209 final String value = idx > 0 && pair.length() > idx + 1
210 ? URLDecoder.decode(pair.substring(idx + 1), StandardCharsets.UTF_8.name())
211 : null;
212 query_pairs.get(key).add(value);
213 }
214 return query_pairs;
215 } catch (UnsupportedEncodingException e) {
216 throw new IllegalArgumentException("Cannot convert " + queryPart + " to map", e);
217 }
218 }
219
220 public static void main(String[] args) {
221 if (args.length == 0)
222 throw new IllegalArgumentException("Usage: <path to A2 base>");
223 Map<String, String> configuration = new HashMap<>();
224 configuration.put("osgi.console", "2323");
225 configuration.put("org.osgi.framework.bootdelegation",
226 "com.sun.jndi.ldap,com.sun.jndi.ldap.sasl,com.sun.security.jgss,com.sun.jndi.dns,com.sun.nio.file,com.sun.nio.sctp,sun.nio.cs");
227 Framework framework = OsgiBootUtils.launch(configuration);
228 try {
229 ProvisioningManager pm = new ProvisioningManager(framework.getBundleContext());
230 Map<String, String> xOr = new HashMap<>();
231 xOr.put("osgi", "equinox");
232 xOr.put("swt", "rap");
233 FsA2Source context = new FsA2Source(Paths.get(args[0]), xOr);
234 context.load();
235 pm.addSource(context);
236 if (framework.getBundleContext().getBundles().length == 1) {// initial
237 pm.install(null);
238 } else {
239 pm.update();
240 }
241
242 Thread.sleep(2000);
243
244 Bundle[] bundles = framework.getBundleContext().getBundles();
245 Arrays.sort(bundles, (b1, b2) -> b1.getSymbolicName().compareTo(b2.getSymbolicName()));
246 for (Bundle b : bundles)
247 if (b.getState() == Bundle.RESOLVED || b.getState() == Bundle.STARTING || b.getState() == Bundle.ACTIVE)
248 System.out.println(b.getSymbolicName() + " " + b.getVersion());
249 else
250 System.err.println(b.getSymbolicName() + " " + b.getVersion() + " (" + b.getState() + ")");
251 } catch (Exception e) {
252 e.printStackTrace();
253 } finally {
254 try {
255 framework.stop();
256 } catch (Exception e) {
257 e.printStackTrace();
258 }
259 }
260 }
261
262 }