]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.init/src/org/argeo/api/a2/AbstractProvisioningSource.java
Clarify ACR API
[lgpl/argeo-commons.git] / org.argeo.init / src / org / argeo / api / a2 / AbstractProvisioningSource.java
1 package org.argeo.api.a2;
2
3 import java.io.FileOutputStream;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.net.URL;
7 import java.nio.file.FileVisitResult;
8 import java.nio.file.Files;
9 import java.nio.file.Path;
10 import java.nio.file.SimpleFileVisitor;
11 import java.nio.file.attribute.BasicFileAttributes;
12 import java.util.Collections;
13 import java.util.Map;
14 import java.util.SortedMap;
15 import java.util.TreeMap;
16 import java.util.jar.JarInputStream;
17 import java.util.jar.JarOutputStream;
18 import java.util.jar.Manifest;
19 import java.util.zip.ZipEntry;
20
21 import org.osgi.framework.Bundle;
22 import org.osgi.framework.BundleContext;
23 import org.osgi.framework.BundleException;
24 import org.osgi.framework.Constants;
25 import org.osgi.framework.Version;
26
27 /** Where components are retrieved from. */
28 public abstract class AbstractProvisioningSource implements ProvisioningSource {
29 protected final Map<String, A2Contribution> contributions = Collections.synchronizedSortedMap(new TreeMap<>());
30
31 private final boolean usingReference;
32
33 public AbstractProvisioningSource(boolean usingReference) {
34 this.usingReference = usingReference;
35 }
36
37 public Iterable<A2Contribution> listContributions(Object filter) {
38 return contributions.values();
39 }
40
41 @Override
42 public Bundle install(BundleContext bc, A2Module module) {
43 try {
44 Object locator = module.getLocator();
45 if (usingReference && locator instanceof Path locatorPath) {
46 String referenceUrl = "reference:file:" + locatorPath.toString();
47 Bundle bundle = bc.installBundle(referenceUrl);
48 return bundle;
49 } else {
50 Path locatorPath = (Path) locator;
51 Path pathToUse;
52 boolean isTemp = false;
53 if (locator instanceof Path && Files.isDirectory(locatorPath)) {
54 pathToUse = toTempJar(locatorPath);
55 isTemp = true;
56 } else {
57 pathToUse = locatorPath;
58 }
59 Bundle bundle;
60 try (InputStream in = newInputStream(pathToUse)) {
61 bundle = bc.installBundle(locatorPath.toAbsolutePath().toString(), in);
62 }
63
64 if (isTemp && pathToUse != null)
65 Files.deleteIfExists(pathToUse);
66 return bundle;
67 }
68 } catch (BundleException | IOException e) {
69 throw new A2Exception("Cannot install module " + module, e);
70 }
71 }
72
73 @Override
74 public void update(Bundle bundle, A2Module module) {
75 try {
76 Object locator = module.getLocator();
77 if (usingReference && locator instanceof Path) {
78 try (InputStream in = newInputStream(locator)) {
79 bundle.update(in);
80 }
81 } else {
82 Path locatorPath = (Path) locator;
83 Path pathToUse;
84 boolean isTemp = false;
85 if (locator instanceof Path && Files.isDirectory(locatorPath)) {
86 pathToUse = toTempJar(locatorPath);
87 isTemp = true;
88 } else {
89 pathToUse = locatorPath;
90 }
91 try (InputStream in = newInputStream(pathToUse)) {
92 bundle.update(in);
93 }
94 if (isTemp && pathToUse != null)
95 Files.deleteIfExists(pathToUse);
96 }
97 } catch (BundleException | IOException e) {
98 throw new A2Exception("Cannot update module " + module, e);
99 }
100 }
101
102 @Override
103 public A2Branch findBranch(String componentId, Version version) {
104 A2Component component = findComponent(componentId);
105 if (component == null)
106 return null;
107 String branchId = version.getMajor() + "." + version.getMinor();
108 if (!component.branches.containsKey(branchId))
109 return null;
110 return component.branches.get(branchId);
111 }
112
113 protected A2Contribution getOrAddContribution(String contributionId) {
114 if (contributions.containsKey(contributionId))
115 return contributions.get(contributionId);
116 else {
117 A2Contribution contribution = new A2Contribution(this, contributionId);
118 contributions.put(contributionId, contribution);
119 return contribution;
120 }
121 }
122
123 protected void asTree(String prefix, StringBuffer buf) {
124 if (prefix == null)
125 prefix = "";
126 for (String contributionId : contributions.keySet()) {
127 buf.append(prefix);
128 buf.append(contributionId);
129 buf.append('\n');
130 A2Contribution contribution = contributions.get(contributionId);
131 contribution.asTree(prefix + " ", buf);
132 }
133 }
134
135 protected void asTree() {
136 StringBuffer buf = new StringBuffer();
137 asTree("", buf);
138 System.out.println(buf);
139 }
140
141 protected A2Component findComponent(String componentId) {
142 SortedMap<A2Contribution, A2Component> res = new TreeMap<>();
143 for (A2Contribution contribution : contributions.values()) {
144 components: for (String componentIdKey : contribution.components.keySet()) {
145 if (componentId.equals(componentIdKey)) {
146 res.put(contribution, contribution.components.get(componentIdKey));
147 break components;
148 }
149 }
150 }
151 if (res.size() == 0)
152 return null;
153 // TODO explicit contribution priorities
154 return res.get(res.lastKey());
155
156 }
157
158 protected String[] readNameVersionFromModule(Path modulePath) {
159 Manifest manifest;
160 if (Files.isDirectory(modulePath)) {
161 manifest = findManifest(modulePath);
162 } else {
163 try (JarInputStream in = new JarInputStream(newInputStream(modulePath))) {
164 manifest = in.getManifest();
165 } catch (IOException e) {
166 throw new A2Exception("Cannot read manifest from " + modulePath, e);
167 }
168 }
169 String versionStr = manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
170 String symbolicName = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
171 int semiColIndex = symbolicName.indexOf(';');
172 if (semiColIndex >= 0)
173 symbolicName = symbolicName.substring(0, semiColIndex);
174 return new String[] { symbolicName, versionStr };
175 }
176
177 protected String readVersionFromModule(Path modulePath) {
178 Manifest manifest;
179 if (Files.isDirectory(modulePath)) {
180 manifest = findManifest(modulePath);
181 } else {
182 try (JarInputStream in = new JarInputStream(newInputStream(modulePath))) {
183 manifest = in.getManifest();
184 } catch (IOException e) {
185 throw new A2Exception("Cannot read manifest from " + modulePath, e);
186 }
187 }
188 String versionStr = manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
189 return versionStr;
190 }
191
192 protected String readSymbolicNameFromModule(Path modulePath) {
193 Manifest manifest;
194 if (Files.isDirectory(modulePath)) {
195 manifest = findManifest(modulePath);
196 } else {
197 try (JarInputStream in = new JarInputStream(newInputStream(modulePath))) {
198 manifest = in.getManifest();
199 } catch (IOException e) {
200 throw new A2Exception("Cannot read manifest from " + modulePath, e);
201 }
202 }
203 String symbolicName = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
204 int semiColIndex = symbolicName.indexOf(';');
205 if (semiColIndex >= 0)
206 symbolicName = symbolicName.substring(0, semiColIndex);
207 return symbolicName;
208 }
209
210 protected boolean isUsingReference() {
211 return usingReference;
212 }
213
214 private InputStream newInputStream(Object locator) throws IOException {
215 if (locator instanceof Path) {
216 return Files.newInputStream((Path) locator);
217 } else if (locator instanceof URL) {
218 return ((URL) locator).openStream();
219 } else {
220 throw new IllegalArgumentException("Unsupported module locator type " + locator.getClass());
221 }
222 }
223
224 private static Manifest findManifest(Path currentPath) {
225 Path metaInfPath = currentPath.resolve("META-INF");
226 if (Files.exists(metaInfPath) && Files.isDirectory(metaInfPath)) {
227 Path manifestPath = metaInfPath.resolve("MANIFEST.MF");
228 try {
229 try (InputStream in = Files.newInputStream(manifestPath)) {
230 Manifest manifest = new Manifest(in);
231 return manifest;
232 }
233 } catch (IOException e) {
234 throw new A2Exception("Cannot read manifest from " + manifestPath, e);
235 }
236 } else {
237 Path parentPath = currentPath.getParent();
238 if (parentPath == null)
239 throw new A2Exception("MANIFEST.MF file not found.");
240 return findManifest(currentPath.getParent());
241 }
242 }
243
244 private static Path toTempJar(Path dir) {
245 try {
246 Manifest manifest = findManifest(dir);
247 Path jarPath = Files.createTempFile("a2Source", ".jar");
248 try (JarOutputStream zos = new JarOutputStream(new FileOutputStream(jarPath.toFile()), manifest)) {
249 Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
250 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
251 Path relPath = dir.relativize(file);
252 // skip MANIFEST from folder
253 if (relPath.toString().contentEquals("META-INF/MANIFEST.MF"))
254 return FileVisitResult.CONTINUE;
255 zos.putNextEntry(new ZipEntry(relPath.toString()));
256 Files.copy(file, zos);
257 zos.closeEntry();
258 return FileVisitResult.CONTINUE;
259 }
260 });
261 }
262 return jarPath;
263 } catch (IOException e) {
264 throw new A2Exception("Cannot install OSGi bundle from " + dir, e);
265 }
266
267 }
268
269 }