2 * Copyright (C) 2007-2012 Argeo GmbH
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org
.argeo
.osgi
.boot
;
18 import java
.io
.BufferedReader
;
19 import java
.io
.IOException
;
20 import java
.io
.InputStream
;
21 import java
.io
.InputStreamReader
;
23 import java
.net
.URISyntaxException
;
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
;
39 import org
.osgi
.framework
.Constants
;
40 import org
.osgi
.framework
.Version
;
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.
49 public class DistributionBundle
{
50 private final static String INDEX_FILE_NAME
= "modularDistribution.csv";
52 private final String url
;
54 private Manifest manifest
;
55 private String symbolicName
;
56 private String version
;
59 private String baseUrl
;
61 private String relativeUrl
;
62 private String localCache
;
64 private List
<OsgiArtifact
> artifacts
;
66 private String separator
= ",";
68 public DistributionBundle(String url
) {
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
;
83 protected String
constructUrl(String baseUrl
, String relativeUrl
) {
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
);
100 throw new OsgiBootException("No file matching " + relativeUrl
+ " found in " + baseUrl
);
101 return res
.get(res
.firstKey()).toUri().toString();
103 return baseUrl
+ relativeUrl
;
105 } catch (Exception e
) {
106 throw new OsgiBootException("Cannot build URL from " + baseUrl
+ " and " + relativeUrl
, e
);
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
);
128 public void processUrl() {
129 JarInputStream jarIn
= null;
131 URL u
= new URL(url
);
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());
141 manifest
= jarIn
.getManifest();
142 symbolicName
= manifest
.getMainAttributes().getValue(Constants
.BUNDLE_SYMBOLICNAME
);
143 version
= manifest
.getMainAttributes().getValue(Constants
.BUNDLE_VERSION
);
146 while ((indexEntry
= jarIn
.getNextJarEntry()) != null) {
147 String entryName
= indexEntry
.getName();
148 if (entryName
.equals(INDEX_FILE_NAME
)) {
155 if (indexEntry
== null)
156 throw new OsgiBootException("No index " + INDEX_FILE_NAME
+ " in " + url
);
157 artifacts
= listArtifacts(jarIn
);
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());
172 } catch (Exception e
) {
173 throw new OsgiBootException("Cannot list URLs from " + url
, e
);
178 } catch (IOException e
) {
184 protected List
<OsgiArtifact
> listArtifacts(InputStream in
) {
185 List
<OsgiArtifact
> osgiArtifacts
= new ArrayList
<OsgiArtifact
>();
186 BufferedReader reader
= null;
188 reader
= new BufferedReader(new InputStreamReader(in
));
190 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 osgiArtifacts
.add(new OsgiArtifact(moduleName
, moduleVersion
, relativeUrl
));
197 } catch (Exception e
) {
198 throw new OsgiBootException("Cannot list artifacts", e
);
200 return osgiArtifacts
;
203 /** Convenience method */
204 public static DistributionBundle
processUrl(String baseUrl
, String realtiveUrl
, String localCache
) {
205 DistributionBundle distributionBundle
= new DistributionBundle(baseUrl
, realtiveUrl
, localCache
);
206 distributionBundle
.processUrl();
207 return distributionBundle
;
211 * List full URLs of the bundles, based on base URL, usable directly for
214 public List
<String
> listUrls() {
216 throw new OsgiBootException("Base URL is not set");
218 if (artifacts
== null)
219 throw new OsgiBootException("Artifact list not initialized");
221 List
<String
> urls
= new ArrayList
<String
>();
222 for (int i
= 0; i
< artifacts
.size(); i
++) {
223 OsgiArtifact osgiArtifact
= (OsgiArtifact
) artifacts
.get(i
);
227 localUri
= new URI(localCache
+ relativeUrl
);
228 } catch (URISyntaxException e
) {
229 OsgiBootUtils
.warn(e
.getMessage());
232 Version version
= new Version(osgiArtifact
.getVersion());
233 if (localUri
!= null && Files
.exists(Paths
.get(localUri
))
234 && version
.getQualifier()!=null && version
.getQualifier().startsWith("SNAPSHOT")) {
235 urls
.add(localCache
+ osgiArtifact
.getRelativeUrl());
237 urls
.add(baseUrl
+ osgiArtifact
.getRelativeUrl());
243 public void setBaseUrl(String baseUrl
) {
244 this.baseUrl
= baseUrl
;
247 /** Separator used to parse the tabular file */
248 public void setSeparator(String modulesUrlSeparator
) {
249 this.separator
= modulesUrlSeparator
;
252 public String
getRelativeUrl() {
256 /** One of the listed artifact */
257 protected static class OsgiArtifact
{
258 private final String symbolicName
;
259 private final String version
;
260 private final String relativeUrl
;
262 public OsgiArtifact(String symbolicName
, String version
, String relativeUrl
) {
264 this.symbolicName
= symbolicName
;
265 this.version
= version
;
266 this.relativeUrl
= relativeUrl
;
269 public String
getSymbolicName() {
273 public String
getVersion() {
277 public String
getRelativeUrl() {