]> git.argeo.org Git - gpl/argeo-slc.git/blob - org.argeo.slc.repo/src/org/argeo/slc/repo/ModularDistributionIndexer.java
Adapt to changes in Argeo Commons
[gpl/argeo-slc.git] / org.argeo.slc.repo / src / org / argeo / slc / repo / ModularDistributionIndexer.java
1 package org.argeo.slc.repo;
2
3 import java.io.BufferedReader;
4 import java.io.InputStreamReader;
5 import java.util.ArrayList;
6 import java.util.Iterator;
7 import java.util.List;
8 import java.util.StringTokenizer;
9 import java.util.jar.JarEntry;
10 import java.util.jar.JarInputStream;
11 import java.util.jar.Manifest;
12
13 import javax.jcr.Binary;
14 import javax.jcr.Node;
15 import javax.jcr.Property;
16 import javax.jcr.RepositoryException;
17 import javax.jcr.nodetype.NodeType;
18
19 import org.apache.commons.io.FilenameUtils;
20 import org.apache.commons.io.IOUtils;
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23 import org.argeo.jcr.JcrUtils;
24 import org.argeo.slc.CategoryNameVersion;
25 import org.argeo.slc.DefaultCategoryNameVersion;
26 import org.argeo.slc.NameVersion;
27 import org.argeo.slc.SlcException;
28 import org.argeo.slc.SlcNames;
29 import org.argeo.slc.SlcTypes;
30 import org.argeo.slc.build.Distribution;
31 import org.argeo.slc.repo.maven.AetherUtils;
32 import org.eclipse.aether.artifact.Artifact;
33 import org.eclipse.aether.artifact.DefaultArtifact;
34 import org.osgi.framework.Constants;
35
36 /**
37 * Create or update JCR meta-data for an SLC Modular Distribution
38 *
39 * Currently, following types are managed: <list>
40 * <li>* .jar: dependency artifacts with csv index</li>
41 * <li>@Deprecated : .pom: artifact (binaries) that indexes a group, the .pom
42 * file contains a tag "dependencyManagement" that list all modules</li> </list>
43 */
44 public class ModularDistributionIndexer implements NodeIndexer, SlcNames {
45 private final static Log log = LogFactory.getLog(ModularDistributionIndexer.class);
46
47 // Constants for csv indexing
48 private final static String INDEX_FILE_NAME = "modularDistribution.csv";
49 private String separator = ",";
50
51 private Manifest manifest;
52
53 public Boolean support(String path) {
54 if (FilenameUtils.getExtension(path).equals("jar"))
55 return true;
56 return false;
57 }
58
59 public void index(Node fileNode) {
60 Binary fileBinary = null;
61 try {
62 String fileNodePath = fileNode.getPath();
63 if (!support(fileNodePath))
64 return;
65
66 if (!fileNode.isNodeType(NodeType.NT_FILE))
67 return;
68
69 Node contentNode = fileNode.getNode(Node.JCR_CONTENT);
70 fileBinary = contentNode.getProperty(Property.JCR_DATA).getBinary();
71
72 MyModularDistribution currDist = null;
73 if (FilenameUtils.getExtension(fileNode.getPath()).equals("jar"))
74 currDist = listModulesFromCsvIndex(fileNode, fileBinary);
75
76 if (fileNode.isNodeType(SlcTypes.SLC_MODULAR_DISTRIBUTION) || currDist == null
77 || !currDist.nameVersions().hasNext())
78 return; // already indexed or no modules found
79 else {
80 fileNode.addMixin(SlcTypes.SLC_MODULAR_DISTRIBUTION);
81 fileNode.addMixin(SlcTypes.SLC_CATEGORIZED_NAME_VERSION);
82 if (currDist.getCategory() != null)
83 fileNode.setProperty(SLC_CATEGORY, currDist.getCategory());
84 fileNode.setProperty(SLC_NAME, currDist.getName());
85 fileNode.setProperty(SLC_VERSION, currDist.getVersion());
86 indexDistribution(currDist, fileNode);
87 }
88
89 if (log.isTraceEnabled())
90 log.trace("Indexed " + fileNode + " as modular distribution");
91 } catch (Exception e) {
92 throw new SlcException("Cannot list dependencies from " + fileNode, e);
93 } finally {
94 JcrUtils.closeQuietly(fileBinary);
95 }
96 }
97
98 private void indexDistribution(ArgeoOsgiDistribution osgiDist, Node distNode) throws RepositoryException {
99 distNode.addMixin(SlcTypes.SLC_MODULAR_DISTRIBUTION);
100 distNode.addMixin(SlcTypes.SLC_CATEGORIZED_NAME_VERSION);
101 distNode.setProperty(SlcNames.SLC_CATEGORY, osgiDist.getCategory());
102 distNode.setProperty(SlcNames.SLC_NAME, osgiDist.getName());
103 distNode.setProperty(SlcNames.SLC_VERSION, osgiDist.getVersion());
104 if (distNode.hasNode(SLC_MODULES))
105 distNode.getNode(SLC_MODULES).remove();
106 Node modules = distNode.addNode(SLC_MODULES, NodeType.NT_UNSTRUCTURED);
107
108 for (Iterator<? extends NameVersion> it = osgiDist.nameVersions(); it.hasNext();)
109 addModule(modules, it.next());
110 }
111
112 // Helpers
113 private Node addModule(Node modules, NameVersion nameVersion) throws RepositoryException {
114 CategoryNameVersion cnv = (CategoryNameVersion) nameVersion;
115 Node moduleCoord = null;
116 moduleCoord = modules.addNode(cnv.getName(), SlcTypes.SLC_MODULE_COORDINATES);
117 moduleCoord.setProperty(SlcNames.SLC_CATEGORY, cnv.getCategory());
118 moduleCoord.setProperty(SlcNames.SLC_NAME, cnv.getName());
119 moduleCoord.setProperty(SlcNames.SLC_VERSION, cnv.getVersion());
120 return moduleCoord;
121 }
122
123 private MyModularDistribution listModulesFromCsvIndex(Node fileNode, Binary fileBinary) {
124 JarInputStream jarIn = null;
125 BufferedReader reader = null;
126 try {
127 jarIn = new JarInputStream(fileBinary.getStream());
128
129 List<CategoryNameVersion> modules = new ArrayList<CategoryNameVersion>();
130
131 // meta data
132 manifest = jarIn.getManifest();
133 if (manifest == null) {
134 log.error(fileNode + " has no MANIFEST");
135 return null;
136 }
137 String category = manifest.getMainAttributes().getValue(RepoConstants.SLC_CATEGORY_ID);
138 String name = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
139 String version = manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
140
141 Artifact distribution = new DefaultArtifact(category, name, "jar", version);
142 // Retrieve the index file
143 JarEntry indexEntry;
144 while ((indexEntry = jarIn.getNextJarEntry()) != null) {
145 String entryName = indexEntry.getName();
146 if (entryName.equals(INDEX_FILE_NAME)) {
147 break;
148 }
149 try {
150 jarIn.closeEntry();
151 } catch (SecurityException se) {
152 log.error("Invalid signature file digest " + "for Manifest main attributes: " + entryName
153 + " while looking for an index in bundle " + name);
154 }
155 }
156 if (indexEntry == null)
157 return null; // Not a modular definition
158
159 if (category == null) {
160 log.warn("Modular definition found but no " + RepoConstants.SLC_CATEGORY_ID + " in " + fileNode);
161 }
162
163 // Process the index
164 reader = new BufferedReader(new InputStreamReader(jarIn));
165 String line = null;
166 while ((line = reader.readLine()) != null) {
167 StringTokenizer st = new StringTokenizer(line, separator);
168 st.nextToken(); // moduleName
169 st.nextToken(); // moduleVersion
170 String relativeUrl = st.nextToken();
171 Artifact currModule = AetherUtils.convertPathToArtifact(relativeUrl, null);
172 modules.add(new DefaultCategoryNameVersion(currModule.getGroupId(), currModule.getArtifactId(),
173 currModule.getVersion()));
174 }
175 return new MyModularDistribution(distribution, modules);
176 } catch (Exception e) {
177 throw new SlcException("Cannot list artifacts", e);
178 } finally {
179 IOUtils.closeQuietly(jarIn);
180 IOUtils.closeQuietly(reader);
181 }
182 }
183
184 /**
185 * A consistent and versioned OSGi distribution, which can be built and tested.
186 */
187 private class MyModularDistribution extends ArtifactDistribution implements ArgeoOsgiDistribution {
188
189 private List<CategoryNameVersion> modules;
190
191 public MyModularDistribution(Artifact artifact, List<CategoryNameVersion> modules) {
192 super(artifact);
193 this.modules = modules;
194 }
195
196 public Iterator<CategoryNameVersion> nameVersions() {
197 return modules.iterator();
198 }
199
200 // Modular distribution interface methods. Not yet used.
201 public Distribution getModuleDistribution(String moduleName, String moduleVersion) {
202 return null;
203 }
204
205 public Object getModulesDescriptor(String descriptorType) {
206 return null;
207 }
208 }
209
210 /** Separator used to parse the tabular file, default is "," */
211 public void setSeparator(String modulesUrlSeparator) {
212 this.separator = modulesUrlSeparator;
213 }
214 }