]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org/argeo/osgi/boot/DistributionBundle.java
Prepare next development cycle
[lgpl/argeo-commons.git] / org / argeo / osgi / boot / DistributionBundle.java
1 /*
2 * Copyright (C) 2007-2012 Argeo GmbH
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.argeo.osgi.boot;
17
18 import java.io.BufferedReader;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.InputStreamReader;
22 import java.net.URI;
23 import java.net.URL;
24 import java.nio.file.DirectoryStream;
25 import java.nio.file.Files;
26 import java.nio.file.Path;
27 import java.nio.file.PathMatcher;
28 import java.nio.file.Paths;
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.SortedMap;
32 import java.util.StringTokenizer;
33 import java.util.TreeMap;
34 import java.util.jar.JarEntry;
35 import java.util.jar.JarInputStream;
36 import java.util.jar.Manifest;
37
38 import org.osgi.framework.Constants;
39 import org.osgi.framework.Version;
40
41 /**
42 * A distribution bundle is a bundle within a maven-like distribution
43 * groupId:Bundle-SymbolicName:Bundle-Version which references others OSGi
44 * bundle. It is not required to be OSGi complete also it will generally be
45 * expected that it is. The root of the repository is computed based on the file
46 * name of the URL and of the content of the index.
47 */
48 public class DistributionBundle {
49 private final static String INDEX_FILE_NAME = "modularDistribution.csv";
50
51 private final String url;
52
53 private Manifest manifest;
54 private String symbolicName;
55 private String version;
56
57 /** can be null */
58 private String baseUrl;
59 /** can be null */
60 private String relativeUrl;
61
62 private List<OsgiArtifact> artifacts;
63
64 private String separator = ",";
65
66 public DistributionBundle(String url) {
67 this.url = url;
68 }
69
70 public DistributionBundle(String baseUrl, String relativeUrl) {
71 if (baseUrl == null || !baseUrl.endsWith("/"))
72 throw new OsgiBootException("Base url " + baseUrl + " badly formatted");
73 if (relativeUrl.startsWith("http") || relativeUrl.startsWith("file:"))
74 throw new OsgiBootException("Relative URL " + relativeUrl + " badly formatted");
75 this.url = constructUrl(baseUrl, relativeUrl);
76 this.baseUrl = baseUrl;
77 this.relativeUrl = relativeUrl;
78 }
79
80 protected String constructUrl(String baseUrl, String relativeUrl) {
81 try {
82 if (relativeUrl.indexOf('*') >= 0) {
83 if (!baseUrl.startsWith("file:"))
84 throw new IllegalArgumentException(
85 "Wildcard support only for file:, badly formatted " + baseUrl + " and " + relativeUrl);
86 Path basePath = Paths.get(new URI(baseUrl));
87 // Path basePath = Paths.get(new URI(baseUrl));
88 // int li = relativeUrl.lastIndexOf('/');
89 // String relativeDir = relativeUrl.substring(0, li);
90 // String relativeFile = relativeUrl.substring(li,
91 // relativeUrl.length());
92 String pattern = "glob:" + basePath + '/' + relativeUrl;
93 PathMatcher pm = basePath.getFileSystem().getPathMatcher(pattern);
94 SortedMap<Version, Path> res = new TreeMap<>();
95 checkDir(basePath, pm, res);
96 if (res.size() == 0)
97 throw new OsgiBootException("No file matching " + relativeUrl + " found in " + baseUrl);
98 return res.get(res.firstKey()).toUri().toString();
99 // try (DirectoryStream<Path> ds =
100 // Files.newDirectoryStream(basePath)) {
101 // Path res = null;
102 // for (Path path : ds) {
103 // if (pm.matches(path)) {
104 // if (res == null)
105 // res = path;
106 // else
107 // throw new OsgiBootException(
108 // "More than one file matching " + relativeUrl + " found in " +
109 // baseUrl);
110 // }
111 // }
112 // if (res == null)
113 // throw new OsgiBootException("No file matching " + relativeUrl
114 // + " found in " + baseUrl);
115 // return res.toUri().toURL().toString();
116 // // Iterator<Path> it = ds.iterator();
117 // // if (!it.hasNext())
118 // // throw new OsgiBootException("No file matching " +
119 // // relativeUrl + " found in " + baseUrl);
120 // // Path distributionBundlePath = it.next();
121 // // if (it.hasNext())// TODO implement version ordered
122 // // throw new OsgiBootException(
123 // // "More than one file matching " + relativeUrl + " found in
124 // // " + baseUrl);
125 // // return distributionBundlePath.toUri().toURL().toString();
126 // }
127 } else {
128 return baseUrl + relativeUrl;
129 }
130 } catch (Exception e) {
131 throw new OsgiBootException("Cannot build URL from " + baseUrl + " and " + relativeUrl, e);
132 }
133 }
134
135 private void checkDir(Path dir, PathMatcher pm, SortedMap<Version, Path> res) throws IOException {
136 try (DirectoryStream<Path> ds = Files.newDirectoryStream(dir)) {
137 for (Path path : ds) {
138 if (Files.isDirectory(path))
139 checkDir(path, pm, res);
140 else if (pm.matches(path)) {
141 String fileName = path.getFileName().toString();
142 fileName = fileName.substring(0, fileName.lastIndexOf('.'));
143 if (fileName.endsWith("-SNAPSHOT"))
144 fileName = fileName.substring(0, fileName.lastIndexOf('-')) + ".SNAPSHOT";
145 fileName = fileName.substring(fileName.lastIndexOf('-') + 1);
146 Version version = new Version(fileName);
147 res.put(version, path);
148 }
149 }
150 }
151 }
152
153 // public static void main(String[] args) {
154 // try {
155 // String baseUrl = "file:///home/mbaudier/.m2/repository/";
156 // String relativeUrl =
157 // "org/argeo/commons/org.argeo.dep.cms.node/2.1.*-SNAPSHOT/org.argeo.dep.cms.node-2.1.*-SNAPSHOT.jar";
158 // Path basePath = Paths.get(new URI(baseUrl));
159 // PathMatcher pm = basePath.getFileSystem().getPathMatcher("glob:" +
160 // relativeUrl);
161 //
162 // try (DirectoryStream<Path> ds = Files.newDirectoryStream(basePath, "**"))
163 // {
164 // for (Path path : ds) {
165 // System.out.println(path);
166 // }
167 // }
168 // } catch (Exception e) {
169 // e.printStackTrace();
170 // }
171 // }
172
173 public void processUrl() {
174 JarInputStream jarIn = null;
175 try {
176 URL u = new URL(url);
177 jarIn = new JarInputStream(u.openStream());
178
179 // meta data
180 manifest = jarIn.getManifest();
181 symbolicName = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
182 version = manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
183
184 JarEntry indexEntry;
185 while ((indexEntry = jarIn.getNextJarEntry()) != null) {
186 String entryName = indexEntry.getName();
187 if (entryName.equals(INDEX_FILE_NAME)) {
188 break;
189 }
190 jarIn.closeEntry();
191 }
192
193 // list artifacts
194 if (indexEntry == null)
195 throw new OsgiBootException("No index " + INDEX_FILE_NAME + " in " + url);
196 artifacts = listArtifacts(jarIn);
197 jarIn.closeEntry();
198
199 // find base URL
200 // won't work if distribution artifact is not listed
201 for (int i = 0; i < artifacts.size(); i++) {
202 OsgiArtifact osgiArtifact = (OsgiArtifact) artifacts.get(i);
203 if (osgiArtifact.getSymbolicName().equals(symbolicName) && osgiArtifact.getVersion().equals(version)) {
204 String relativeUrl = osgiArtifact.getRelativeUrl();
205 if (url.endsWith(relativeUrl)) {
206 baseUrl = url.substring(0, url.length() - osgiArtifact.getRelativeUrl().length());
207 break;
208 }
209 }
210 }
211 } catch (Exception e) {
212 throw new OsgiBootException("Cannot list URLs from " + url, e);
213 } finally {
214 if (jarIn != null)
215 try {
216 jarIn.close();
217 } catch (IOException e) {
218 // silent
219 }
220 }
221 }
222
223 protected List<OsgiArtifact> listArtifacts(InputStream in) {
224 List<OsgiArtifact> osgiArtifacts = new ArrayList<OsgiArtifact>();
225 BufferedReader reader = null;
226 try {
227 reader = new BufferedReader(new InputStreamReader(in));
228 String line = null;
229 while ((line = reader.readLine()) != null) {
230 StringTokenizer st = new StringTokenizer(line, separator);
231 String moduleName = st.nextToken();
232 String moduleVersion = st.nextToken();
233 String relativeUrl = st.nextToken();
234 osgiArtifacts.add(new OsgiArtifact(moduleName, moduleVersion, relativeUrl));
235 }
236 } catch (Exception e) {
237 throw new OsgiBootException("Cannot list artifacts", e);
238 }
239 return osgiArtifacts;
240 }
241
242 /** Convenience method */
243 public static DistributionBundle processUrl(String baseUrl, String realtiveUrl) {
244 DistributionBundle distributionBundle = new DistributionBundle(baseUrl, realtiveUrl);
245 distributionBundle.processUrl();
246 return distributionBundle;
247 }
248
249 /**
250 * List full URLs of the bundles, based on base URL, usable directly for
251 * download.
252 */
253 public List<String> listUrls() {
254 if (baseUrl == null)
255 throw new OsgiBootException("Base URL is not set");
256
257 if (artifacts == null)
258 throw new OsgiBootException("Artifact list not initialized");
259
260 List<String> urls = new ArrayList<String>();
261 for (int i = 0; i < artifacts.size(); i++) {
262 OsgiArtifact osgiArtifact = (OsgiArtifact) artifacts.get(i);
263 urls.add(baseUrl + osgiArtifact.getRelativeUrl());
264 }
265 return urls;
266 }
267
268 public void setBaseUrl(String baseUrl) {
269 this.baseUrl = baseUrl;
270 }
271
272 /** Separator used to parse the tabular file */
273 public void setSeparator(String modulesUrlSeparator) {
274 this.separator = modulesUrlSeparator;
275 }
276
277 public String getRelativeUrl() {
278 return relativeUrl;
279 }
280
281 /** One of the listed artifact */
282 protected static class OsgiArtifact {
283 private final String symbolicName;
284 private final String version;
285 private final String relativeUrl;
286
287 public OsgiArtifact(String symbolicName, String version, String relativeUrl) {
288 super();
289 this.symbolicName = symbolicName;
290 this.version = version;
291 this.relativeUrl = relativeUrl;
292 }
293
294 public String getSymbolicName() {
295 return symbolicName;
296 }
297
298 public String getVersion() {
299 return version;
300 }
301
302 public String getRelativeUrl() {
303 return relativeUrl;
304 }
305
306 }
307 }