]> git.argeo.org Git - gpl/argeo-slc.git/blob - runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/JarFileIndexer.java
Working indexation (artifact, jar, osgi) in repo
[gpl/argeo-slc.git] / runtime / org.argeo.slc.repo / src / main / java / org / argeo / slc / repo / JarFileIndexer.java
1 package org.argeo.slc.repo;
2
3 import java.io.ByteArrayInputStream;
4 import java.io.ByteArrayOutputStream;
5 import java.util.ArrayList;
6 import java.util.List;
7 import java.util.jar.Attributes;
8 import java.util.jar.Attributes.Name;
9 import java.util.jar.JarInputStream;
10 import java.util.jar.Manifest;
11
12 import javax.jcr.Binary;
13 import javax.jcr.Node;
14 import javax.jcr.NodeIterator;
15 import javax.jcr.Property;
16 import javax.jcr.RepositoryException;
17 import javax.jcr.Session;
18 import javax.jcr.nodetype.NodeType;
19
20 import org.apache.commons.io.FilenameUtils;
21 import org.apache.commons.io.IOUtils;
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24 import org.argeo.jcr.JcrUtils;
25 import org.argeo.slc.SlcException;
26 import org.argeo.slc.jcr.SlcNames;
27 import org.argeo.slc.jcr.SlcTypes;
28 import org.osgi.framework.Constants;
29 import org.osgi.framework.Version;
30
31 /**
32 * Indexes jar file, currently supports standard J2SE and OSGi metadata (both
33 * from MANIFEST)
34 */
35 public class JarFileIndexer implements NodeIndexer {
36 private final static Log log = LogFactory.getLog(JarFileIndexer.class);
37
38 public Boolean support(String path) {
39 return FilenameUtils.getExtension(path).equals("jar");
40 }
41
42 public void index(Node fileNode) {
43 Binary fileBinary = null;
44 JarInputStream jarIn = null;
45 ByteArrayOutputStream bo = null;
46 ByteArrayInputStream bi = null;
47 Binary manifestBinary = null;
48 try {
49 if (!fileNode.isNodeType(NodeType.NT_FILE))
50 return;
51
52 Session jcrSession = fileNode.getSession();
53 Node contentNode = fileNode.getNode(Node.JCR_CONTENT);
54 fileBinary = contentNode.getProperty(Property.JCR_DATA).getBinary();
55 // jar file
56 // if (!FilenameUtils.isExtension(fileNode.getName(), "jar")) {
57 // return;
58 // }
59
60 jarIn = new JarInputStream(fileBinary.getStream());
61 Manifest manifest = jarIn.getManifest();
62 if(manifest==null){
63 log.error(fileNode+" has no MANIFEST");
64 return;
65 }
66 bo = new ByteArrayOutputStream();
67 manifest.write(bo);
68 bi = new ByteArrayInputStream(bo.toByteArray());
69 manifestBinary = jcrSession.getValueFactory().createBinary(bi);
70
71 // standard jar file
72 fileNode.addMixin(SlcTypes.SLC_JAR_FILE);
73 fileNode.setProperty(SlcNames.SLC_MANIFEST, manifestBinary);
74 Attributes attrs = manifest.getMainAttributes();
75 if (log.isTraceEnabled())
76 for (Object key : attrs.keySet())
77 log.trace(key + ": " + attrs.getValue(key.toString()));
78
79 // standard J2SE MANIFEST attributes
80 addAttr(Attributes.Name.MANIFEST_VERSION, fileNode, attrs);
81 addAttr(Attributes.Name.SIGNATURE_VERSION, fileNode, attrs);
82 addAttr(Attributes.Name.CLASS_PATH, fileNode, attrs);
83 addAttr(Attributes.Name.MAIN_CLASS, fileNode, attrs);
84 addAttr(Attributes.Name.EXTENSION_NAME, fileNode, attrs);
85 addAttr(Attributes.Name.IMPLEMENTATION_VERSION, fileNode, attrs);
86 addAttr(Attributes.Name.IMPLEMENTATION_VENDOR, fileNode, attrs);
87 addAttr(Attributes.Name.IMPLEMENTATION_VENDOR_ID, fileNode, attrs);
88 addAttr(Attributes.Name.SPECIFICATION_TITLE, fileNode, attrs);
89 addAttr(Attributes.Name.SPECIFICATION_VERSION, fileNode, attrs);
90 addAttr(Attributes.Name.SPECIFICATION_VENDOR, fileNode, attrs);
91 addAttr(Attributes.Name.SEALED, fileNode, attrs);
92
93 // OSGi
94 if (attrs.containsKey(new Name(Constants.BUNDLE_SYMBOLICNAME))) {
95 addOsgiMetadata(fileNode, attrs);
96 if (log.isTraceEnabled())
97 log.trace("Indexed OSGi bundle " + fileNode);
98 } else {
99 if (log.isTraceEnabled())
100 log.trace("Indexed JAR file " + fileNode);
101 }
102 } catch (Exception e) {
103 throw new SlcException("Cannot index jar " + fileNode, e);
104 } finally {
105 IOUtils.closeQuietly(bi);
106 IOUtils.closeQuietly(bo);
107 IOUtils.closeQuietly(jarIn);
108 JcrUtils.closeQuietly(manifestBinary);
109 JcrUtils.closeQuietly(fileBinary);
110 }
111
112 }
113
114 protected void addOsgiMetadata(Node fileNode, Attributes attrs)
115 throws RepositoryException {
116 fileNode.addMixin(SlcTypes.SLC_BUNDLE_ARTIFACT);
117
118 // symbolic name
119 String symbolicName = attrs.getValue(Constants.BUNDLE_SYMBOLICNAME);
120 // make sure there is no directive
121 symbolicName = symbolicName.split(";")[0];
122 fileNode.setProperty(SlcNames.SLC_SYMBOLIC_NAME, symbolicName);
123
124 // direct mapping
125 addAttr(Constants.BUNDLE_SYMBOLICNAME, fileNode, attrs);
126 addAttr(Constants.BUNDLE_NAME, fileNode, attrs);
127 addAttr(Constants.BUNDLE_DESCRIPTION, fileNode, attrs);
128 addAttr(Constants.BUNDLE_MANIFESTVERSION, fileNode, attrs);
129 addAttr(Constants.BUNDLE_CATEGORY, fileNode, attrs);
130 addAttr(Constants.BUNDLE_ACTIVATIONPOLICY, fileNode, attrs);
131 addAttr(Constants.BUNDLE_COPYRIGHT, fileNode, attrs);
132 addAttr(Constants.BUNDLE_VENDOR, fileNode, attrs);
133 addAttr("Bundle-License", fileNode, attrs);
134 addAttr(Constants.BUNDLE_DOCURL, fileNode, attrs);
135 addAttr(Constants.BUNDLE_CONTACTADDRESS, fileNode, attrs);
136 addAttr(Constants.BUNDLE_ACTIVATOR, fileNode, attrs);
137 addAttr(Constants.BUNDLE_UPDATELOCATION, fileNode, attrs);
138 addAttr(Constants.BUNDLE_LOCALIZATION, fileNode, attrs);
139
140 // required execution environment
141 if (attrs.containsKey(new Name(
142 Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT)))
143 fileNode.setProperty(SlcNames.SLC_
144 + Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT, attrs
145 .getValue(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT)
146 .split(","));
147
148 // bundle classpath
149 if (attrs.containsKey(new Name(Constants.BUNDLE_CLASSPATH)))
150 fileNode.setProperty(SlcNames.SLC_ + Constants.BUNDLE_CLASSPATH,
151 attrs.getValue(Constants.BUNDLE_CLASSPATH).split(","));
152
153 // version
154 Version version = new Version(attrs.getValue(Constants.BUNDLE_VERSION));
155 fileNode.setProperty(SlcNames.SLC_BUNDLE_VERSION, version.toString());
156 cleanSubNodes(fileNode, SlcNames.SLC_ + Constants.BUNDLE_VERSION);
157 Node bundleVersionNode = fileNode.addNode(SlcNames.SLC_
158 + Constants.BUNDLE_VERSION, SlcTypes.SLC_OSGI_VERSION);
159 mapOsgiVersion(version, bundleVersionNode);
160
161 // fragment
162 cleanSubNodes(fileNode, SlcNames.SLC_ + Constants.FRAGMENT_HOST);
163 if (attrs.containsKey(new Name(Constants.FRAGMENT_HOST))) {
164 String fragmentHost = attrs.getValue(Constants.FRAGMENT_HOST);
165 String[] tokens = fragmentHost.split(";");
166 Node node = fileNode.addNode(SlcNames.SLC_
167 + Constants.FRAGMENT_HOST, SlcTypes.SLC_FRAGMENT_HOST);
168 node.setProperty(SlcNames.SLC_SYMBOLIC_NAME, tokens[0]);
169 for (int i = 1; i < tokens.length; i++) {
170 if (tokens[i].startsWith(Constants.BUNDLE_VERSION_ATTRIBUTE)) {
171 node.setProperty(SlcNames.SLC_BUNDLE_VERSION,
172 attributeValue(tokens[i]));
173 }
174 }
175 }
176
177 // imported packages
178 cleanSubNodes(fileNode, SlcNames.SLC_ + Constants.IMPORT_PACKAGE);
179 if (attrs.containsKey(new Name(Constants.IMPORT_PACKAGE))) {
180 String importPackages = attrs.getValue(Constants.IMPORT_PACKAGE);
181 List<String> packages = parsePackages(importPackages);
182 for (String pkg : packages) {
183 String[] tokens = pkg.split(";");
184 Node node = fileNode.addNode(SlcNames.SLC_
185 + Constants.IMPORT_PACKAGE,
186 SlcTypes.SLC_IMPORTED_PACKAGE);
187 node.setProperty(SlcNames.SLC_NAME, tokens[0]);
188 for (int i = 1; i < tokens.length; i++) {
189 if (tokens[i].startsWith(Constants.VERSION_ATTRIBUTE)) {
190 node.setProperty(SlcNames.SLC_VERSION,
191 attributeValue(tokens[i]));
192 } else if (tokens[i]
193 .startsWith(Constants.RESOLUTION_DIRECTIVE)) {
194 node.setProperty(
195 SlcNames.SLC_OPTIONAL,
196 directiveValue(tokens[i]).equals(
197 Constants.RESOLUTION_OPTIONAL));
198 }
199 }
200 }
201 }
202
203 // dynamic import package
204 cleanSubNodes(fileNode, SlcNames.SLC_ + Constants.DYNAMICIMPORT_PACKAGE);
205 if (attrs.containsKey(new Name(Constants.DYNAMICIMPORT_PACKAGE))) {
206 String importPackages = attrs
207 .getValue(Constants.DYNAMICIMPORT_PACKAGE);
208 List<String> packages = parsePackages(importPackages);
209 for (String pkg : packages) {
210 String[] tokens = pkg.split(";");
211 Node node = fileNode.addNode(SlcNames.SLC_
212 + Constants.DYNAMICIMPORT_PACKAGE,
213 SlcTypes.SLC_DYNAMIC_IMPORTED_PACKAGE);
214 node.setProperty(SlcNames.SLC_NAME, tokens[0]);
215 for (int i = 1; i < tokens.length; i++) {
216 if (tokens[i].startsWith(Constants.VERSION_ATTRIBUTE)) {
217 node.setProperty(SlcNames.SLC_VERSION,
218 attributeValue(tokens[i]));
219 }
220 }
221 }
222 }
223
224 // exported packages
225 cleanSubNodes(fileNode, SlcNames.SLC_ + Constants.EXPORT_PACKAGE);
226 if (attrs.containsKey(new Name(Constants.EXPORT_PACKAGE))) {
227 String exportPackages = attrs.getValue(Constants.EXPORT_PACKAGE);
228 List<String> packages = parsePackages(exportPackages);
229 for (String pkg : packages) {
230 String[] tokens = pkg.split(";");
231 Node node = fileNode.addNode(SlcNames.SLC_
232 + Constants.EXPORT_PACKAGE,
233 SlcTypes.SLC_EXPORTED_PACKAGE);
234 node.setProperty(SlcNames.SLC_NAME, tokens[0]);
235 // TODO: are these cleans really necessary?
236 cleanSubNodes(node, SlcNames.SLC_USES);
237 cleanSubNodes(node, SlcNames.SLC_VERSION);
238 for (int i = 1; i < tokens.length; i++) {
239 if (tokens[i].startsWith(Constants.VERSION_ATTRIBUTE)) {
240 String versionStr = attributeValue(tokens[i]);
241 Node versionNode = node.addNode(SlcNames.SLC_VERSION,
242 SlcTypes.SLC_OSGI_VERSION);
243 mapOsgiVersion(new Version(versionStr), versionNode);
244 } else if (tokens[i].startsWith(Constants.USES_DIRECTIVE)) {
245 String usedPackages = directiveValue(tokens[i]);
246 // log.debug("uses='" + usedPackages + "'");
247 for (String usedPackage : usedPackages.split(",")) {
248 // log.debug("usedPackage='" +
249 // usedPackage +
250 // "'");
251 Node usesNode = node.addNode(SlcNames.SLC_USES,
252 SlcTypes.SLC_JAVA_PACKAGE);
253 usesNode.setProperty(SlcNames.SLC_NAME, usedPackage);
254 }
255 }
256 }
257 }
258 }
259
260 // required bundle
261 cleanSubNodes(fileNode, SlcNames.SLC_ + Constants.REQUIRE_BUNDLE);
262 if (attrs.containsKey(new Name(Constants.REQUIRE_BUNDLE))) {
263 String requireBundle = attrs.getValue(Constants.REQUIRE_BUNDLE);
264 String[] bundles = requireBundle.split(",");
265 for (String bundle : bundles) {
266 String[] tokens = bundle.split(";");
267 Node node = fileNode.addNode(SlcNames.SLC_
268 + Constants.REQUIRE_BUNDLE,
269 SlcTypes.SLC_REQUIRED_BUNDLE);
270 node.setProperty(SlcNames.SLC_SYMBOLIC_NAME, tokens[0]);
271 for (int i = 1; i < tokens.length; i++) {
272 if (tokens[i]
273 .startsWith(Constants.BUNDLE_VERSION_ATTRIBUTE)) {
274 node.setProperty(SlcNames.SLC_BUNDLE_VERSION,
275 attributeValue(tokens[i]));
276 } else if (tokens[i]
277 .startsWith(Constants.RESOLUTION_DIRECTIVE)) {
278 node.setProperty(
279 SlcNames.SLC_OPTIONAL,
280 directiveValue(tokens[i]).equals(
281 Constants.RESOLUTION_OPTIONAL));
282 }
283 }
284 }
285 }
286
287 }
288
289 private void addAttr(String key, Node node, Attributes attrs)
290 throws RepositoryException {
291 addAttr(new Name(key), node, attrs);
292 }
293
294 private void addAttr(Name key, Node node, Attributes attrs)
295 throws RepositoryException {
296 if (attrs.containsKey(key)) {
297 String value = attrs.getValue(key);
298 node.setProperty(SlcNames.SLC_ + key, value);
299 }
300 }
301
302 private void cleanSubNodes(Node node, String name)
303 throws RepositoryException {
304 if (node.hasNode(name)) {
305 NodeIterator nit = node.getNodes(name);
306 while (nit.hasNext())
307 nit.nextNode().remove();
308 }
309 }
310
311 private String attributeValue(String str) {
312 return extractValue(str, "=");
313 }
314
315 private String directiveValue(String str) {
316 return extractValue(str, ":=");
317 }
318
319 private String extractValue(String str, String eq) {
320 String[] tokens = str.split(eq);
321 // String key = tokens[0];
322 String value = tokens[1].trim();
323 // TODO: optimize?
324 if (value.startsWith("\""))
325 value = value.substring(1);
326 if (value.endsWith("\""))
327 value = value.substring(0, value.length() - 1);
328 return value;
329 }
330
331 /** Parse package list with nested directive with ',' */
332 private List<String> parsePackages(String str) {
333 List<String> res = new ArrayList<String>();
334 StringBuffer curr = new StringBuffer("");
335 boolean in = false;
336 for (char c : str.toCharArray()) {
337 if (c == ',') {
338 if (!in) {
339 res.add(curr.toString());
340 curr = new StringBuffer("");
341 }
342 } else if (c == '\"') {
343 in = !in;
344 curr.append(c);
345 } else {
346 curr.append(c);
347 }
348 }
349 res.add(curr.toString());
350 // log.debug(res);
351 return res;
352 }
353
354 protected void mapOsgiVersion(Version version, Node versionNode)
355 throws RepositoryException {
356 versionNode.setProperty(SlcNames.SLC_AS_STRING, version.toString());
357 versionNode.setProperty(SlcNames.SLC_MAJOR, version.getMajor());
358 versionNode.setProperty(SlcNames.SLC_MINOR, version.getMinor());
359 versionNode.setProperty(SlcNames.SLC_MICRO, version.getMicro());
360 if (!version.getQualifier().equals(""))
361 versionNode.setProperty(SlcNames.SLC_QUALIFIER,
362 version.getQualifier());
363 }
364
365 }