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