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
;
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
;
38 import org
.osgi
.framework
.Constants
;
39 import org
.osgi
.framework
.Version
;
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.
48 public class DistributionBundle
{
49 private final static String INDEX_FILE_NAME
= "modularDistribution.csv";
51 private final String url
;
53 private Manifest manifest
;
54 private String symbolicName
;
55 private String version
;
58 private String baseUrl
;
60 private String relativeUrl
;
62 private List
<OsgiArtifact
> artifacts
;
64 private String separator
= ",";
66 public DistributionBundle(String url
) {
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
;
80 protected String
constructUrl(String baseUrl
, String relativeUrl
) {
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
);
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)) {
102 // for (Path path : ds) {
103 // if (pm.matches(path)) {
107 // throw new OsgiBootException(
108 // "More than one file matching " + relativeUrl + " found in " +
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
125 // // return distributionBundlePath.toUri().toURL().toString();
128 return baseUrl
+ relativeUrl
;
130 } catch (Exception e
) {
131 throw new OsgiBootException("Cannot build URL from " + baseUrl
+ " and " + relativeUrl
, e
);
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
);
153 // public static void main(String[] args) {
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:" +
162 // try (DirectoryStream<Path> ds = Files.newDirectoryStream(basePath, "**"))
164 // for (Path path : ds) {
165 // System.out.println(path);
168 // } catch (Exception e) {
169 // e.printStackTrace();
173 public void processUrl() {
174 JarInputStream jarIn
= null;
176 URL u
= new URL(url
);
177 jarIn
= new JarInputStream(u
.openStream());
180 manifest
= jarIn
.getManifest();
181 symbolicName
= manifest
.getMainAttributes().getValue(Constants
.BUNDLE_SYMBOLICNAME
);
182 version
= manifest
.getMainAttributes().getValue(Constants
.BUNDLE_VERSION
);
185 while ((indexEntry
= jarIn
.getNextJarEntry()) != null) {
186 String entryName
= indexEntry
.getName();
187 if (entryName
.equals(INDEX_FILE_NAME
)) {
194 if (indexEntry
== null)
195 throw new OsgiBootException("No index " + INDEX_FILE_NAME
+ " in " + url
);
196 artifacts
= listArtifacts(jarIn
);
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());
211 } catch (Exception e
) {
212 throw new OsgiBootException("Cannot list URLs from " + url
, e
);
217 } catch (IOException e
) {
223 protected List
<OsgiArtifact
> listArtifacts(InputStream in
) {
224 List
<OsgiArtifact
> osgiArtifacts
= new ArrayList
<OsgiArtifact
>();
225 BufferedReader reader
= null;
227 reader
= new BufferedReader(new InputStreamReader(in
));
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
));
236 } catch (Exception e
) {
237 throw new OsgiBootException("Cannot list artifacts", e
);
239 return osgiArtifacts
;
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
;
250 * List full URLs of the bundles, based on base URL, usable directly for
253 public List
<String
> listUrls() {
255 throw new OsgiBootException("Base URL is not set");
257 if (artifacts
== null)
258 throw new OsgiBootException("Artifact list not initialized");
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());
268 public void setBaseUrl(String baseUrl
) {
269 this.baseUrl
= baseUrl
;
272 /** Separator used to parse the tabular file */
273 public void setSeparator(String modulesUrlSeparator
) {
274 this.separator
= modulesUrlSeparator
;
277 public String
getRelativeUrl() {
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
;
287 public OsgiArtifact(String symbolicName
, String version
, String relativeUrl
) {
289 this.symbolicName
= symbolicName
;
290 this.version
= version
;
291 this.relativeUrl
= relativeUrl
;
294 public String
getSymbolicName() {
298 public String
getVersion() {
302 public String
getRelativeUrl() {