]> git.argeo.org Git - gpl/argeo-slc.git/blob - runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/ModularDistributionIndexer.java
3070b9b757ef909038c8345c17de2c9795d2ac16
[gpl/argeo-slc.git] / runtime / org.argeo.slc.repo / src / main / java / org / argeo / slc / repo / ModularDistributionIndexer.java
1 package org.argeo.slc.repo;
2
3 import java.io.BufferedReader;
4 import java.io.InputStream;
5 import java.io.InputStreamReader;
6 import java.util.ArrayList;
7 import java.util.Collections;
8 import java.util.Iterator;
9 import java.util.List;
10 import java.util.Properties;
11 import java.util.StringTokenizer;
12 import java.util.jar.JarEntry;
13 import java.util.jar.JarInputStream;
14 import java.util.jar.Manifest;
15
16 import javax.jcr.Binary;
17 import javax.jcr.Node;
18 import javax.jcr.Property;
19 import javax.jcr.RepositoryException;
20 import javax.jcr.nodetype.NodeType;
21 import javax.xml.parsers.DocumentBuilder;
22 import javax.xml.parsers.DocumentBuilderFactory;
23
24 import org.apache.commons.io.FilenameUtils;
25 import org.apache.commons.io.IOUtils;
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.argeo.jcr.JcrUtils;
29 import org.argeo.slc.CategorizedNameVersion;
30 import org.argeo.slc.DefaultNameVersion;
31 import org.argeo.slc.NameVersion;
32 import org.argeo.slc.SlcException;
33 import org.argeo.slc.aether.AetherUtils;
34 import org.argeo.slc.build.Distribution;
35 import org.argeo.slc.jcr.SlcNames;
36 import org.argeo.slc.jcr.SlcTypes;
37 import org.osgi.framework.Constants;
38 import org.sonatype.aether.artifact.Artifact;
39 import org.sonatype.aether.util.artifact.DefaultArtifact;
40 import org.w3c.dom.Document;
41 import org.w3c.dom.Element;
42 import org.w3c.dom.NodeList;
43
44 /**
45 * Create or update JCR meta-data for an SLC Modular Distribution
46 *
47 * Currently, following types are managed: <list><li>* .jar: dependency
48 * artifacts with csv index</li> <li>.pom: artifact (binaries) that indexes a
49 * group, the .pom file contains a tag "dependencyManagement" that list all
50 * modules</li> </list>
51 */
52 public class ModularDistributionIndexer implements NodeIndexer, SlcNames {
53 private final static Log log = LogFactory
54 .getLog(ModularDistributionIndexer.class);
55
56 // Constants for csv indexing
57 private final static String INDEX_FILE_NAME = "modularDistribution.csv";
58 private String separator = ",";
59
60 // Artifact indexing
61 private final static List<String> BINARIES_ARTIFACTS_NAME;
62 static {
63 List<String> tmpList = new ArrayList<String>();
64 tmpList.add(RepoConstants.BINARIES_ARTIFACT_ID);
65 // tmpList.add(RepoConstants.SOURCES_ARTIFACT_ID);
66 // tmpList.add(RepoConstants.SDK_ARTIFACT_ID);
67 BINARIES_ARTIFACTS_NAME = Collections.unmodifiableList(tmpList);
68 }
69
70 private Manifest manifest;
71
72 // private Comparator<Artifact> artifactComparator = new
73 // ArtifactIdComparator();
74
75 public Boolean support(String path) {
76 if (FilenameUtils.getExtension(path).equals("jar"))
77 return true;
78 if (FilenameUtils.getExtension(path).equals("pom")
79 && BINARIES_ARTIFACTS_NAME.contains(FilenameUtils.getName(path)
80 .split("-")[0]))
81 return true;
82 return false;
83 }
84
85 public void index(Node fileNode) {
86 Binary fileBinary = null;
87 try {
88
89 String fileNodePath = fileNode.getPath();
90 if (!support(fileNodePath))
91 return;
92
93 if (!fileNode.isNodeType(NodeType.NT_FILE))
94 return;
95
96 Node contentNode = fileNode.getNode(Node.JCR_CONTENT);
97 fileBinary = contentNode.getProperty(Property.JCR_DATA).getBinary();
98
99 MyModularDistribution currDist = null;
100 if (FilenameUtils.getExtension(fileNode.getPath()).equals("jar"))
101 currDist = listModulesFromCsvIndex(fileNode, fileBinary);
102 else if (FilenameUtils.getExtension(fileNode.getPath()).equals(
103 "pom"))
104 currDist = listModulesFromPomIndex(fileNode, fileBinary);
105
106 if (fileNode.isNodeType(SlcTypes.SLC_MODULAR_DISTRIBUTION)
107 || currDist == null || !currDist.nameVersions().hasNext())
108 return; // already indexed or no modules found
109 else {
110 fileNode.addMixin(SlcTypes.SLC_MODULAR_DISTRIBUTION);
111 fileNode.addMixin(SlcTypes.SLC_CATEGORIZED_NAME_VERSION);
112 if (currDist.getCategory() != null)
113 fileNode.setProperty(SLC_CATEGORY, currDist.getCategory());
114 fileNode.setProperty(SLC_NAME, currDist.getName());
115 fileNode.setProperty(SLC_VERSION, currDist.getVersion());
116 indexDistribution(currDist, fileNode);
117 }
118
119 if (log.isTraceEnabled())
120 log.trace("Indexed " + fileNode + " as modular distribution");
121 } catch (Exception e) {
122 throw new SlcException("Cannot list dependencies from " + fileNode,
123 e);
124 }
125 }
126
127 private void indexDistribution(ArgeoOsgiDistribution osgiDist, Node distNode)
128 throws RepositoryException {
129 distNode.addMixin(SlcTypes.SLC_MODULAR_DISTRIBUTION);
130 distNode.addMixin(SlcTypes.SLC_CATEGORIZED_NAME_VERSION);
131 distNode.setProperty(SlcNames.SLC_CATEGORY, osgiDist.getCategory());
132 distNode.setProperty(SlcNames.SLC_NAME, osgiDist.getName());
133 distNode.setProperty(SlcNames.SLC_VERSION, osgiDist.getVersion());
134 Node modules = JcrUtils.mkdirs(distNode, SlcNames.SLC_MODULES,
135 NodeType.NT_UNSTRUCTURED);
136
137 for (Iterator<? extends NameVersion> it = osgiDist.nameVersions(); it
138 .hasNext();)
139 addModule(modules, it.next());
140 }
141
142 // Helpers
143 private Node addModule(Node modules, NameVersion nameVersion)
144 throws RepositoryException {
145 CategorizedNameVersion cnv = (CategorizedNameVersion) nameVersion;
146 Node moduleCoord = null;
147 moduleCoord = modules.addNode(cnv.getName(),
148 SlcTypes.SLC_MODULE_COORDINATES);
149 moduleCoord.setProperty(SlcNames.SLC_CATEGORY, cnv.getCategory());
150 moduleCoord.setProperty(SlcNames.SLC_NAME, cnv.getName());
151 moduleCoord.setProperty(SlcNames.SLC_VERSION, cnv.getVersion());
152 return moduleCoord;
153 }
154
155 private MyModularDistribution listModulesFromCsvIndex(Node fileNode,
156 Binary fileBinary) {
157 JarInputStream jarIn = null;
158 BufferedReader reader = null;
159 try {
160 jarIn = new JarInputStream(fileBinary.getStream());
161
162 List<CategorizedNameVersion> modules = new ArrayList<CategorizedNameVersion>();
163
164 // meta data
165 manifest = jarIn.getManifest();
166 if (manifest == null) {
167 log.error(fileNode + " has no MANIFEST");
168 return null;
169 }
170 String category = manifest.getMainAttributes().getValue(
171 RepoConstants.SLC_GROUP_ID);
172 String name = manifest.getMainAttributes().getValue(
173 Constants.BUNDLE_SYMBOLICNAME);
174 String version = manifest.getMainAttributes().getValue(
175 Constants.BUNDLE_VERSION);
176
177 Artifact distribution = new DefaultArtifact(category, name, "pom",
178 version);
179 // Retrieve the index file
180 JarEntry indexEntry;
181 while ((indexEntry = jarIn.getNextJarEntry()) != null) {
182 String entryName = indexEntry.getName();
183 if (entryName.equals(INDEX_FILE_NAME)) {
184 break;
185 }
186 try {
187 jarIn.closeEntry();
188 } catch (SecurityException se) {
189 log.error("Invalid signature file digest "
190 + "for Manifest main attributes: " + entryName
191 + " while looking for an index in bundle " + name);
192 }
193 }
194 if (indexEntry == null)
195 return null; // Not a modular definition artifact
196
197 // Process the index
198 reader = new BufferedReader(new InputStreamReader(jarIn));
199 String line = null;
200 while ((line = reader.readLine()) != null) {
201 StringTokenizer st = new StringTokenizer(line, separator);
202 st.nextToken(); // moduleName
203 st.nextToken(); // moduleVersion
204 String relativeUrl = st.nextToken();
205 Artifact currModule = AetherUtils.convertPathToArtifact(
206 relativeUrl, null);
207 modules.add(new MyCategorizedNameVersion(currModule
208 .getGroupId(), currModule.getArtifactId(), currModule
209 .getVersion()));
210 }
211 return new MyModularDistribution(distribution, modules);
212 } catch (Exception e) {
213 throw new SlcException("Cannot list artifacts", e);
214 } finally {
215 IOUtils.closeQuietly(jarIn);
216 IOUtils.closeQuietly(reader);
217 }
218 }
219
220 private MyModularDistribution listModulesFromPomIndex(Node fileNode,
221 Binary fileBinary) {
222 InputStream input = null;
223 List<CategorizedNameVersion> modules = new ArrayList<CategorizedNameVersion>();
224 try {
225 input = fileBinary.getStream();
226
227 DocumentBuilder documentBuilder = DocumentBuilderFactory
228 .newInstance().newDocumentBuilder();
229 Document doc = documentBuilder.parse(input);
230 // properties
231 Properties props = new Properties();
232 // props.setProperty("project.version",
233 // pomArtifact.getBaseVersion());
234 NodeList properties = doc.getElementsByTagName("properties");
235 if (properties.getLength() > 0) {
236 NodeList propertiesElems = properties.item(0).getChildNodes();
237 for (int i = 0; i < propertiesElems.getLength(); i++) {
238 if (propertiesElems.item(i) instanceof Element) {
239 Element property = (Element) propertiesElems.item(i);
240 props.put(property.getNodeName(),
241 property.getTextContent());
242 }
243 }
244 }
245
246 // full coordinates are under <dependencyManagement><dependencies>
247 NodeList dependencies = ((Element) doc.getElementsByTagName(
248 "dependencyManagement").item(0))
249 .getElementsByTagName("dependency");
250 for (int i = 0; i < dependencies.getLength(); i++) {
251 Element dependency = (Element) dependencies.item(i);
252 String groupId = dependency.getElementsByTagName("groupId")
253 .item(0).getTextContent().trim();
254 String artifactId = dependency
255 .getElementsByTagName("artifactId").item(0)
256 .getTextContent().trim();
257 String version = dependency.getElementsByTagName("version")
258 .item(0).getTextContent().trim();
259 modules.add(new MyCategorizedNameVersion(groupId, artifactId,
260 version));
261 }
262
263 String groupId = doc.getElementsByTagName("groupId").item(0)
264 .getTextContent().trim();
265 String artifactId = doc.getElementsByTagName("artifactId").item(0)
266 .getTextContent().trim();
267 String version = doc.getElementsByTagName("version").item(0)
268 .getTextContent().trim();
269
270 Artifact currDist = new DefaultArtifact(groupId, artifactId, "pom",
271 version);
272
273 return new MyModularDistribution(currDist, modules);
274 } catch (Exception e) {
275 throw new SlcException("Cannot process pom " + fileNode, e);
276 } finally {
277 IOUtils.closeQuietly(input);
278 }
279 }
280
281 /** The created modular distribution */
282 private static class MyCategorizedNameVersion extends DefaultNameVersion
283 implements CategorizedNameVersion {
284 private final String category;
285
286 public MyCategorizedNameVersion(String category, String name,
287 String version) {
288 super(name, version);
289 this.category = category;
290 }
291
292 public String getCategory() {
293 return category;
294 }
295 }
296
297 /**
298 * A consistent and versioned OSGi distribution, which can be built and
299 * tested.
300 */
301 private class MyModularDistribution extends ArtifactDistribution implements
302 ArgeoOsgiDistribution {
303
304 private List<CategorizedNameVersion> modules;
305
306 public MyModularDistribution(Artifact artifact,
307 List<CategorizedNameVersion> modules) {
308 super(artifact);
309 this.modules = modules;
310 }
311
312 public Iterator<CategorizedNameVersion> nameVersions() {
313 return modules.iterator();
314 }
315
316 // Modular distribution interface methods. Not yet used.
317 public Distribution getModuleDistribution(String moduleName,
318 String moduleVersion) {
319 return null;
320 }
321
322 public Object getModulesDescriptor(String descriptorType) {
323 return null;
324 }
325 }
326
327 /** Separator used to parse the tabular file, default is "," */
328 public void setSeparator(String modulesUrlSeparator) {
329 this.separator = modulesUrlSeparator;
330 }
331
332 }