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