View Javadoc
1   /*
2    * Copyright (C) 2007-2012 Argeo GmbH
3    *
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
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
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.
15   */
16  package org.argeo.slc.repo;
17  
18  import javax.jcr.Node;
19  import javax.jcr.NodeIterator;
20  import javax.jcr.RepositoryException;
21  import javax.jcr.nodetype.NodeType;
22  
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  import org.argeo.jcr.JcrUtils;
26  import org.argeo.slc.SlcException;
27  import org.argeo.slc.SlcNames;
28  import org.argeo.slc.SlcTypes;
29  import org.argeo.slc.repo.maven.AetherUtils;
30  import org.eclipse.aether.artifact.Artifact;
31  import org.osgi.framework.Constants;
32  
33  /**
34   * Add {@link Artifact} properties to a {@link Node}. Does nothing if the node
35   * name doesn't start with the artifact id (in order to skip Maven metadata XML
36   * files and other non artifact files).
37   */
38  public class ArtifactIndexer implements NodeIndexer, SlcNames {
39  	private Log log = LogFactory.getLog(ArtifactIndexer.class);
40  	private Boolean force = false;
41  
42  	public Boolean support(String path) {
43  		String relativePath = getRelativePath(path);
44  		if (relativePath == null)
45  			return false;
46  		Artifact artifact = null;
47  		try {
48  			artifact = AetherUtils.convertPathToArtifact(relativePath, null);
49  		} catch (Exception e) {
50  			if (log.isTraceEnabled())
51  				log.trace("Malformed path " + path + ", skipping silently", e);
52  		}
53  		return artifact != null;
54  	}
55  
56  	public void index(Node fileNode) {
57  		Artifact artifact = null;
58  		try {
59  			if (!support(fileNode.getPath()))
60  				return;
61  
62  			// Already indexed
63  			if (!force && fileNode.isNodeType(SlcTypes.SLC_ARTIFACT))
64  				return;
65  
66  			if (!fileNode.isNodeType(NodeType.NT_FILE))
67  				return;
68  
69  			String relativePath = getRelativePath(fileNode.getPath());
70  			if (relativePath == null)
71  				return;
72  			artifact = AetherUtils.convertPathToArtifact(relativePath, null);
73  			// support() guarantees that artifact won't be null, no NPE check
74  			fileNode.addMixin(SlcTypes.SLC_ARTIFACT);
75  			fileNode.setProperty(SlcNames.SLC_ARTIFACT_ID,
76  					artifact.getArtifactId());
77  			fileNode.setProperty(SlcNames.SLC_GROUP_ID, artifact.getGroupId());
78  			fileNode.setProperty(SlcNames.SLC_ARTIFACT_VERSION,
79  					artifact.getVersion());
80  			fileNode.setProperty(SlcNames.SLC_ARTIFACT_EXTENSION,
81  					artifact.getExtension());
82  			// can be null but ok for JCR API
83  			fileNode.setProperty(SlcNames.SLC_ARTIFACT_CLASSIFIER,
84  					artifact.getClassifier());
85  			JcrUtils.updateLastModified(fileNode);
86  
87  			// make sure there are checksums
88  			String shaNodeName = fileNode.getName() + ".sha1";
89  			if (!fileNode.getParent().hasNode(shaNodeName)) {
90  				String sha = JcrUtils.checksumFile(fileNode, "SHA-1");
91  				JcrUtils.copyBytesAsFile(fileNode.getParent(), shaNodeName,
92  						sha.getBytes());
93  			}
94  			String md5NodeName = fileNode.getName() + ".md5";
95  			if (!fileNode.getParent().hasNode(md5NodeName)) {
96  				String md5 = JcrUtils.checksumFile(fileNode, "MD5");
97  				JcrUtils.copyBytesAsFile(fileNode.getParent(), md5NodeName,
98  						md5.getBytes());
99  			}
100 
101 			// Create a default pom if none already exist
102 			String fileNodeName = fileNode.getName();
103 			String pomName = null;
104 			if (fileNodeName.endsWith(".jar"))
105 				pomName = fileNodeName.substring(0, fileNodeName.length()
106 						- ".jar".length())
107 						+ ".pom";
108 
109 			if (pomName != null && !fileNode.getParent().hasNode(pomName)) {
110 				String pom = generatePomForBundle(fileNode);
111 				Node pomNode = JcrUtils.copyBytesAsFile(fileNode.getParent(),
112 						pomName, pom.getBytes());
113 				// corresponding check sums
114 				String sha = JcrUtils.checksumFile(pomNode, "SHA-1");
115 				JcrUtils.copyBytesAsFile(fileNode.getParent(), pomName
116 						+ ".sha1", sha.getBytes());
117 				String md5 = JcrUtils.checksumFile(fileNode, "MD5");
118 				JcrUtils.copyBytesAsFile(fileNode.getParent(),
119 						pomName + ".md5", md5.getBytes());
120 			}
121 
122 			// set higher levels
123 			Node artifactVersionBase = fileNode.getParent();
124 			if (!artifactVersionBase
125 					.isNodeType(SlcTypes.SLC_ARTIFACT_VERSION_BASE)) {
126 				artifactVersionBase
127 						.addMixin(SlcTypes.SLC_ARTIFACT_VERSION_BASE);
128 				artifactVersionBase.setProperty(SlcNames.SLC_ARTIFACT_VERSION,
129 						artifact.getBaseVersion());
130 				artifactVersionBase.setProperty(SlcNames.SLC_ARTIFACT_ID,
131 						artifact.getArtifactId());
132 				artifactVersionBase.setProperty(SlcNames.SLC_GROUP_ID,
133 						artifact.getGroupId());
134 			}
135 			JcrUtils.updateLastModified(artifactVersionBase);
136 
137 			// pom
138 			if (artifact.getExtension().equals("pom")) {
139 				// TODO read to make it a distribution
140 			}
141 
142 			Node artifactBase = artifactVersionBase.getParent();
143 			if (!artifactBase.isNodeType(SlcTypes.SLC_ARTIFACT_BASE)) {
144 				artifactBase.addMixin(SlcTypes.SLC_ARTIFACT_BASE);
145 				artifactBase.setProperty(SlcNames.SLC_ARTIFACT_ID,
146 						artifact.getArtifactId());
147 				artifactBase.setProperty(SlcNames.SLC_GROUP_ID,
148 						artifact.getGroupId());
149 			}
150 			JcrUtils.updateLastModified(artifactBase);
151 
152 			Node groupBase = artifactBase.getParent();
153 			if (!groupBase.isNodeType(SlcTypes.SLC_GROUP_BASE)) {
154 				// if (groupBase.isNodeType(SlcTypes.SLC_ARTIFACT_BASE)) {
155 				// log.warn("Group base " + groupBase.getPath()
156 				// + " is also artifact base");
157 				// }
158 				groupBase.addMixin(SlcTypes.SLC_GROUP_BASE);
159 				groupBase.setProperty(SlcNames.SLC_GROUP_BASE_ID,
160 						artifact.getGroupId());
161 			}
162 			JcrUtils.updateLastModifiedAndParents(groupBase,
163 					RepoConstants.DEFAULT_ARTIFACTS_BASE_PATH);
164 
165 			if (log.isTraceEnabled())
166 				log.trace("Indexed artifact " + artifact + " on " + fileNode);
167 		} catch (Exception e) {
168 			throw new SlcException("Cannot index artifact " + artifact
169 					+ " metadata on node " + fileNode, e);
170 		}
171 	}
172 
173 	private String getRelativePath(String nodePath) {
174 		String basePath = RepoConstants.DEFAULT_ARTIFACTS_BASE_PATH;
175 		if (!nodePath.startsWith(basePath))
176 			return null;
177 		String relativePath = nodePath.substring(basePath.length());
178 		return relativePath;
179 	}
180 
181 	public void setForce(Boolean force) {
182 		this.force = force;
183 	}
184 
185 	private String generatePomForBundle(Node n) throws RepositoryException {
186 		StringBuffer p = new StringBuffer();
187 		p.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
188 		p.append("<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n");
189 		p.append("<modelVersion>4.0.0</modelVersion>");
190 
191 		// Categorized name version
192 		p.append("<groupId>").append(JcrUtils.get(n, SLC_GROUP_ID))
193 				.append("</groupId>\n");
194 		p.append("<artifactId>").append(JcrUtils.get(n, SLC_ARTIFACT_ID))
195 				.append("</artifactId>\n");
196 		p.append("<version>").append(JcrUtils.get(n, SLC_ARTIFACT_VERSION))
197 				.append("</version>\n");
198 		// TODO make it more generic
199 		p.append("<packaging>jar</packaging>\n");
200 		if (n.hasProperty(SLC_ + Constants.BUNDLE_NAME))
201 			p.append("<name>")
202 					.append(JcrUtils.get(n, SLC_ + Constants.BUNDLE_NAME))
203 					.append("</name>\n");
204 		if (n.hasProperty(SLC_ + Constants.BUNDLE_DESCRIPTION))
205 			p.append("<description>")
206 					.append(JcrUtils
207 							.get(n, SLC_ + Constants.BUNDLE_DESCRIPTION))
208 					.append("</description>\n");
209 
210 		// Dependencies in case of a distribution
211 		if (n.isNodeType(SlcTypes.SLC_MODULAR_DISTRIBUTION)) {
212 			p.append(getDependenciesSnippet(n.getNode(SlcNames.SLC_MODULES)
213 					.getNodes()));
214 			p.append(getDependencyManagementSnippet(n.getNode(
215 					SlcNames.SLC_MODULES).getNodes()));
216 		}
217 		p.append("</project>\n");
218 		return p.toString();
219 	}
220 
221 	private String getDependenciesSnippet(NodeIterator nit)
222 			throws RepositoryException {
223 		StringBuilder b = new StringBuilder();
224 		b.append("<dependencies>\n");
225 		while (nit.hasNext()) {
226 			Node currModule = nit.nextNode();
227 			if (currModule.isNodeType(SlcTypes.SLC_MODULE_COORDINATES)) {
228 				b.append(getDependencySnippet(
229 						currModule.getProperty(SlcNames.SLC_CATEGORY)
230 								.getString(),
231 						currModule.getProperty(SlcNames.SLC_NAME).getString(),
232 						null));
233 			}
234 		}
235 		b.append("</dependencies>\n");
236 		return b.toString();
237 	}
238 
239 	private String getDependencyManagementSnippet(NodeIterator nit)
240 			throws RepositoryException {
241 		StringBuilder b = new StringBuilder();
242 		b.append("<dependencyManagement>\n");
243 		b.append("<dependencies>\n");
244 		while (nit.hasNext()) {
245 			Node currModule = nit.nextNode();
246 			if (currModule.isNodeType(SlcTypes.SLC_MODULE_COORDINATES)) {
247 				b.append(getDependencySnippet(
248 						currModule.getProperty(SlcNames.SLC_CATEGORY)
249 								.getString(),
250 						currModule.getProperty(SlcNames.SLC_NAME).getString(),
251 						currModule.getProperty(SlcNames.SLC_VERSION)
252 								.getString()));
253 			}
254 		}
255 		b.append("</dependencies>\n");
256 		b.append("</dependencyManagement>\n");
257 		return b.toString();
258 	}
259 
260 	private String getDependencySnippet(String category, String name,
261 			String version) {
262 		StringBuilder b = new StringBuilder();
263 		b.append("<dependency>\n");
264 		b.append("\t<groupId>").append(category).append("</groupId>\n");
265 		b.append("\t<artifactId>").append(name).append("</artifactId>\n");
266 		if (version != null)
267 			b.append("\t<version>").append(version).append("</version>\n");
268 		b.append("</dependency>\n");
269 		return b.toString();
270 	}
271 }