]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.init/src/org/argeo/init/osgi/DistributionBundle.java
Simplify multi-runtime
[lgpl/argeo-commons.git] / org.argeo.init / src / org / argeo / init / osgi / DistributionBundle.java
1 package org.argeo.init.osgi;
2
3 import static java.lang.System.Logger.Level.WARNING;
4
5 import java.io.BufferedReader;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.io.InputStreamReader;
9 import java.lang.System.Logger;
10 import java.net.URI;
11 import java.net.URISyntaxException;
12 import java.net.URL;
13 import java.nio.file.DirectoryStream;
14 import java.nio.file.Files;
15 import java.nio.file.Path;
16 import java.nio.file.PathMatcher;
17 import java.nio.file.Paths;
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.SortedMap;
21 import java.util.StringTokenizer;
22 import java.util.TreeMap;
23 import java.util.jar.JarEntry;
24 import java.util.jar.JarInputStream;
25 import java.util.jar.Manifest;
26
27 import org.osgi.framework.Constants;
28 import org.osgi.framework.Version;
29
30 /**
31 * A distribution bundle is a bundle within a maven-like distribution
32 * groupId:Bundle-SymbolicName:Bundle-Version which references others OSGi
33 * bundle. It is not required to be OSGi complete also it will generally be
34 * expected that it is. The root of the repository is computed based on the file
35 * name of the URL and of the content of the index.
36 */
37 public class DistributionBundle {
38 private final static Logger logger = System.getLogger(DistributionBundle.class.getName());
39
40 private final static String INDEX_FILE_NAME = "modularDistribution.csv";
41
42 private final String url;
43
44 private Manifest manifest;
45 private String symbolicName;
46 private String version;
47
48 /** can be null */
49 private String baseUrl;
50 /** can be null */
51 private String relativeUrl;
52 private String localCache;
53
54 private List<OsgiArtifact> artifacts;
55
56 private String separator = ",";
57
58 public DistributionBundle(String url) {
59 this.url = url;
60 }
61
62 public DistributionBundle(String baseUrl, String relativeUrl, String localCache) {
63 if (baseUrl == null || !baseUrl.endsWith("/"))
64 throw new IllegalArgumentException("Base url " + baseUrl + " badly formatted");
65 if (relativeUrl.startsWith("http") || relativeUrl.startsWith("file:"))
66 throw new IllegalArgumentException("Relative URL " + relativeUrl + " badly formatted");
67 this.url = constructUrl(baseUrl, relativeUrl);
68 this.baseUrl = baseUrl;
69 this.relativeUrl = relativeUrl;
70 this.localCache = localCache;
71 }
72
73 protected String constructUrl(String baseUrl, String relativeUrl) {
74 try {
75 if (relativeUrl.indexOf('*') >= 0) {
76 if (!baseUrl.startsWith("file:"))
77 throw new IllegalArgumentException(
78 "Wildcard support only for file:, badly formatted " + baseUrl + " and " + relativeUrl);
79 Path basePath = Paths.get(new URI(baseUrl));
80 // Path basePath = Paths.get(new URI(baseUrl));
81 // int li = relativeUrl.lastIndexOf('/');
82 // String relativeDir = relativeUrl.substring(0, li);
83 // String relativeFile = relativeUrl.substring(li,
84 // relativeUrl.length());
85 String pattern = "glob:" + basePath + '/' + relativeUrl;
86 PathMatcher pm = basePath.getFileSystem().getPathMatcher(pattern);
87 SortedMap<Version, Path> res = new TreeMap<>();
88 checkDir(basePath, pm, res);
89 if (res.size() == 0)
90 throw new IllegalArgumentException("No file matching " + relativeUrl + " found in " + baseUrl);
91 return res.get(res.firstKey()).toUri().toString();
92 } else {
93 return baseUrl + relativeUrl;
94 }
95 } catch (Exception e) {
96 throw new RuntimeException("Cannot build URL from " + baseUrl + " and " + relativeUrl, e);
97 }
98 }
99
100 private void checkDir(Path dir, PathMatcher pm, SortedMap<Version, Path> res) throws IOException {
101 try (DirectoryStream<Path> ds = Files.newDirectoryStream(dir)) {
102 for (Path path : ds) {
103 if (Files.isDirectory(path))
104 checkDir(path, pm, res);
105 else if (pm.matches(path)) {
106 String fileName = path.getFileName().toString();
107 fileName = fileName.substring(0, fileName.lastIndexOf('.'));
108 if (fileName.endsWith("-SNAPSHOT"))
109 fileName = fileName.substring(0, fileName.lastIndexOf('-')) + ".SNAPSHOT";
110 fileName = fileName.substring(fileName.lastIndexOf('-') + 1);
111 Version version = new Version(fileName);
112 res.put(version, path);
113 }
114 }
115 }
116 }
117
118 public void processUrl() {
119 JarInputStream jarIn = null;
120 try {
121 URL u = new URI(url).toURL();
122
123 // local cache
124 URI localUri = new URI(localCache + relativeUrl);
125 Path localPath = Paths.get(localUri);
126 if (Files.exists(localPath))
127 u = localUri.toURL();
128 jarIn = new JarInputStream(u.openStream());
129
130 // meta data
131 manifest = jarIn.getManifest();
132 symbolicName = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
133 version = manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
134
135 JarEntry indexEntry;
136 while ((indexEntry = jarIn.getNextJarEntry()) != null) {
137 String entryName = indexEntry.getName();
138 if (entryName.equals(INDEX_FILE_NAME)) {
139 break;
140 }
141 jarIn.closeEntry();
142 }
143
144 // list artifacts
145 if (indexEntry == null)
146 throw new IllegalArgumentException("No index " + INDEX_FILE_NAME + " in " + url);
147 artifacts = listArtifacts(jarIn);
148 jarIn.closeEntry();
149
150 // find base URL
151 // won't work if distribution artifact is not listed
152 for (int i = 0; i < artifacts.size(); i++) {
153 OsgiArtifact osgiArtifact = (OsgiArtifact) artifacts.get(i);
154 if (osgiArtifact.getSymbolicName().equals(symbolicName) && osgiArtifact.getVersion().equals(version)) {
155 String relativeUrl = osgiArtifact.getRelativeUrl();
156 if (url.endsWith(relativeUrl)) {
157 baseUrl = url.substring(0, url.length() - osgiArtifact.getRelativeUrl().length());
158 break;
159 }
160 }
161 }
162 } catch (Exception e) {
163 throw new RuntimeException("Cannot list URLs from " + url, e);
164 } finally {
165 if (jarIn != null)
166 try {
167 jarIn.close();
168 } catch (IOException e) {
169 // silent
170 }
171 }
172 }
173
174 protected List<OsgiArtifact> listArtifacts(InputStream in) {
175 List<OsgiArtifact> osgiArtifacts = new ArrayList<OsgiArtifact>();
176 BufferedReader reader = null;
177 try {
178 reader = new BufferedReader(new InputStreamReader(in));
179 String line = null;
180 lines: while ((line = reader.readLine()) != null) {
181 StringTokenizer st = new StringTokenizer(line, separator);
182 String moduleName = st.nextToken();
183 String moduleVersion = st.nextToken();
184 String relativeUrl = st.nextToken();
185 if (relativeUrl.endsWith(".pom"))
186 continue lines;
187 osgiArtifacts.add(new OsgiArtifact(moduleName, moduleVersion, relativeUrl));
188 }
189 } catch (Exception e) {
190 throw new RuntimeException("Cannot list artifacts", e);
191 }
192 return osgiArtifacts;
193 }
194
195 /** Convenience method */
196 public static DistributionBundle processUrl(String baseUrl, String relativeUrl, String localCache) {
197 DistributionBundle distributionBundle = new DistributionBundle(baseUrl, relativeUrl, localCache);
198 distributionBundle.processUrl();
199 return distributionBundle;
200 }
201
202 /**
203 * List full URLs of the bundles, based on base URL, usable directly for
204 * download.
205 */
206 public List<String> listUrls() {
207 if (baseUrl == null)
208 throw new IllegalArgumentException("Base URL is not set");
209
210 if (artifacts == null)
211 throw new IllegalStateException("Artifact list not initialized");
212
213 List<String> urls = new ArrayList<String>();
214 for (int i = 0; i < artifacts.size(); i++) {
215 OsgiArtifact osgiArtifact = (OsgiArtifact) artifacts.get(i);
216 // local cache
217 URI localUri;
218 try {
219 localUri = new URI(localCache + relativeUrl);
220 } catch (URISyntaxException e) {
221 logger.log(WARNING, e.getMessage());
222 localUri = null;
223 }
224 Version version = new Version(osgiArtifact.getVersion());
225 if (localUri != null && Files.exists(Paths.get(localUri)) && version.getQualifier() != null
226 && version.getQualifier().startsWith("SNAPSHOT")) {
227 urls.add(localCache + osgiArtifact.getRelativeUrl());
228 } else {
229 urls.add(baseUrl + osgiArtifact.getRelativeUrl());
230 }
231 }
232 return urls;
233 }
234
235 public void setBaseUrl(String baseUrl) {
236 this.baseUrl = baseUrl;
237 }
238
239 /** Separator used to parse the tabular file */
240 public void setSeparator(String modulesUrlSeparator) {
241 this.separator = modulesUrlSeparator;
242 }
243
244 public String getRelativeUrl() {
245 return relativeUrl;
246 }
247
248 /** One of the listed artifact */
249 protected static class OsgiArtifact {
250 private final String symbolicName;
251 private final String version;
252 private final String relativeUrl;
253
254 public OsgiArtifact(String symbolicName, String version, String relativeUrl) {
255 super();
256 this.symbolicName = symbolicName;
257 this.version = version;
258 this.relativeUrl = relativeUrl;
259 }
260
261 public String getSymbolicName() {
262 return symbolicName;
263 }
264
265 public String getVersion() {
266 return version;
267 }
268
269 public String getRelativeUrl() {
270 return relativeUrl;
271 }
272
273 }
274 }