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.maven;
17  
18  import java.io.IOException;
19  import java.util.ArrayList;
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.jar.Attributes.Name;
24  import java.util.jar.Manifest;
25  
26  import javax.jcr.Binary;
27  import javax.jcr.Node;
28  import javax.jcr.NodeIterator;
29  import javax.jcr.Property;
30  import javax.jcr.Repository;
31  import javax.jcr.RepositoryException;
32  import javax.jcr.Session;
33  import javax.jcr.nodetype.NodeType;
34  import javax.jcr.query.QueryManager;
35  import javax.jcr.query.QueryResult;
36  import javax.jcr.query.qom.Ordering;
37  import javax.jcr.query.qom.QueryObjectModel;
38  import javax.jcr.query.qom.QueryObjectModelFactory;
39  import javax.jcr.query.qom.Selector;
40  
41  import org.apache.commons.logging.Log;
42  import org.apache.commons.logging.LogFactory;
43  import org.argeo.jcr.JcrUtils;
44  import org.argeo.slc.NameVersion;
45  import org.argeo.slc.SlcException;
46  import org.argeo.slc.SlcNames;
47  import org.argeo.slc.SlcTypes;
48  import org.argeo.slc.repo.ArtifactIndexer;
49  import org.argeo.slc.repo.JarFileIndexer;
50  import org.argeo.slc.repo.RepoUtils;
51  import org.argeo.slc.repo.osgi.OsgiProfile;
52  import org.eclipse.aether.artifact.Artifact;
53  import org.eclipse.aether.artifact.DefaultArtifact;
54  import org.osgi.framework.Constants;
55  
56  /**
57   * Migrate the distribution from 1.2 to 1.4 by cleaning naming and dependencies.
58   * The dependency to the SpringSource Enterprise Bundle repository is removed as
59   * well as their naming conventions. All third party are move to org.argeo.tp
60   * group IDs. Maven dependency for Eclipse artifacts don't use version ranges
61   * anymore. Verison constraints on javax.* packages are removed (since they lead
62   * to "use package conflicts" when Eclipse and Spring Security are used
63   * together).
64   */
65  public class Migration_01_03 implements Runnable, SlcNames {
66  	final String SPRING_SOURCE_PREFIX = "com.springsource";
67  	private final static Log log = LogFactory.getLog(Migration_01_03.class);
68  
69  	private Repository repository;
70  	private String sourceWorkspace;
71  	private String targetWorkspace;
72  
73  	private List<String> excludedBundles = new ArrayList<String>();
74  	private Map<String, String> symbolicNamesMapping = new HashMap<String, String>();
75  
76  	private Session origSession;
77  	private Session targetSession;
78  
79  	private List<String> systemPackages = OsgiProfile.PROFILE_JAVA_SE_1_6
80  			.getSystemPackages();
81  
82  	private String artifactBasePath = "/";
83  
84  	private ArtifactIndexer artifactIndexer = new ArtifactIndexer();
85  	private JarFileIndexer jarFileIndexer = new JarFileIndexer();
86  
87  	public void init() throws RepositoryException {
88  		origSession = JcrUtils.loginOrCreateWorkspace(repository,
89  				sourceWorkspace);
90  		targetSession = JcrUtils.loginOrCreateWorkspace(repository,
91  				targetWorkspace);
92  
93  		// works only in OSGi!!
94  		// systemPackages = Arrays.asList(System.getProperty(
95  		// "org.osgi.framework.system.packages").split(","));
96  	}
97  
98  	public void destroy() {
99  		JcrUtils.logoutQuietly(origSession);
100 		JcrUtils.logoutQuietly(targetSession);
101 	}
102 
103 	public void run() {
104 
105 		try {
106 			// clear target
107 			NodeIterator nit = targetSession.getNode(artifactBasePath)
108 					.getNodes();
109 			while (nit.hasNext()) {
110 				Node node = nit.nextNode();
111 				if (node.isNodeType(NodeType.NT_FOLDER)
112 						|| node.isNodeType(NodeType.NT_UNSTRUCTURED)) {
113 					node.remove();
114 					node.getSession().save();
115 					if (log.isDebugEnabled())
116 						log.debug("Cleared " + node);
117 				}
118 			}
119 
120 			NodeIterator origArtifacts = listArtifactVersions(origSession);
121 			// process
122 			while (origArtifacts.hasNext()) {
123 				Node origArtifactNode = origArtifacts.nextNode();
124 				if (log.isTraceEnabled())
125 					log.trace(origArtifactNode);
126 
127 				processOrigArtifactVersion(origArtifactNode);
128 			}
129 		} catch (Exception e) {
130 			throw new SlcException("Cannot perform v1.3 migration from "
131 					+ sourceWorkspace + " to " + targetWorkspace, e);
132 		} finally {
133 			JcrUtils.discardQuietly(targetSession);
134 		}
135 	}
136 
137 	protected void processOrigArtifactVersion(Node origArtifactNode)
138 			throws RepositoryException, IOException {
139 		Artifact origArtifact = RepoUtils.asArtifact(origArtifactNode);
140 
141 		// skip eclipse artifacts
142 		if ((origArtifact.getGroupId().startsWith("org.eclipse") && !(origArtifact
143 				.getArtifactId().equals("org.eclipse.osgi")
144 				|| origArtifact.getArtifactId().equals(
145 						"org.eclipse.osgi.source") || origArtifact
146 				.getArtifactId().startsWith("org.eclipse.rwt.widgets.upload")))
147 				|| origArtifact.getArtifactId().startsWith("com.ibm.icu")) {
148 			if (log.isDebugEnabled())
149 				log.debug("Skip " + origArtifact);
150 			return;
151 		}
152 
153 		// skip SpringSource ActiveMQ
154 		if (origArtifact.getArtifactId().startsWith(
155 				"com.springsource.org.apache.activemq"))
156 			return;
157 
158 		String origJarNodeName = MavenConventionsUtils
159 				.artifactFileName(origArtifact);
160 		if (!origArtifactNode.hasNode(origJarNodeName))
161 			throw new SlcException("Cannot find jar node for "
162 					+ origArtifactNode);
163 		Node origJarNode = origArtifactNode.getNode(origJarNodeName);
164 
165 		// read MANIFEST
166 		Binary manifestBinary = origJarNode.getProperty(SLC_MANIFEST)
167 				.getBinary();
168 		Manifest origManifest = new Manifest(manifestBinary.getStream());
169 		JcrUtils.closeQuietly(manifestBinary);
170 
171 		Boolean manifestModified = false;
172 		Manifest targetManifest = new Manifest(origManifest);
173 
174 		// transform symbolic name
175 		String origSymbolicName = origManifest.getMainAttributes().getValue(
176 				Constants.BUNDLE_SYMBOLICNAME);
177 		final String targetSymbolicName;
178 		if (symbolicNamesMapping.containsKey(origSymbolicName)) {
179 			targetSymbolicName = symbolicNamesMapping.get(origSymbolicName);
180 		} else if (origSymbolicName.startsWith(SPRING_SOURCE_PREFIX)
181 				&& !origSymbolicName.equals(SPRING_SOURCE_PREFIX + ".json")) {
182 			targetSymbolicName = origSymbolicName
183 					.substring(SPRING_SOURCE_PREFIX.length() + 1);
184 		} else {
185 			targetSymbolicName = origSymbolicName;
186 		}
187 
188 		if (!targetSymbolicName.equals(origSymbolicName)) {
189 			targetManifest.getMainAttributes().putValue(
190 					Constants.BUNDLE_SYMBOLICNAME, targetSymbolicName);
191 			manifestModified = true;
192 			if (log.isDebugEnabled())
193 				log.debug(Constants.BUNDLE_SYMBOLICNAME + " to "
194 						+ targetSymbolicName + " \t\tfrom " + origSymbolicName);
195 		}
196 
197 		// skip excluded bundles
198 		if (excludedBundles.contains(targetSymbolicName))
199 			return;
200 
201 		// check fragment host
202 		if (origManifest.getMainAttributes().containsKey(
203 				new Name(Constants.FRAGMENT_HOST))) {
204 			String origFragmentHost = origManifest.getMainAttributes()
205 					.getValue(Constants.FRAGMENT_HOST);
206 			String targetFragmentHost;
207 			if (symbolicNamesMapping.containsKey(origFragmentHost)) {
208 				targetFragmentHost = symbolicNamesMapping.get(origFragmentHost);
209 			} else if (origFragmentHost.startsWith(SPRING_SOURCE_PREFIX)
210 					&& !origFragmentHost.equals(SPRING_SOURCE_PREFIX + ".json")) {
211 				targetFragmentHost = origFragmentHost
212 						.substring(SPRING_SOURCE_PREFIX.length() + 1);
213 			} else if (origFragmentHost
214 					.equals("org.argeo.dep.jacob;bundle-version=\"[1.14.3,1.14.4)\"")) {
215 				// this one for those who think I cannot be pragmatic - mbaudier
216 				targetFragmentHost = "com.jacob;bundle-version=\"[1.14.3,1.14.4)\"";
217 			} else {
218 				targetFragmentHost = origFragmentHost;
219 			}
220 
221 			if (!targetFragmentHost.equals(origFragmentHost)) {
222 				targetManifest.getMainAttributes().putValue(
223 						Constants.FRAGMENT_HOST, targetFragmentHost);
224 				manifestModified = true;
225 				if (log.isDebugEnabled())
226 					log.debug(Constants.FRAGMENT_HOST + " to "
227 							+ targetFragmentHost + " from " + origFragmentHost);
228 			}
229 		}
230 
231 		// we assume there is no Require-Bundle in com.springsource.* bundles
232 
233 		// javax with versions
234 		StringBuffer targetImportPackages = new StringBuffer("");
235 		NodeIterator origImportPackages = origJarNode.getNodes(SLC_
236 				+ Constants.IMPORT_PACKAGE);
237 		Boolean importPackagesModified = false;
238 		while (origImportPackages.hasNext()) {
239 			Node importPackage = origImportPackages.nextNode();
240 			String pkg = importPackage.getProperty(SLC_NAME).getString();
241 			targetImportPackages.append(pkg);
242 			if (importPackage.hasProperty(SLC_VERSION)) {
243 				String sourceVersion = importPackage.getProperty(SLC_VERSION)
244 						.getString();
245 				String targetVersion = sourceVersion;
246 				if (systemPackages.contains(pkg)) {
247 					if (!(sourceVersion.trim().equals("0") || sourceVersion
248 							.trim().equals("0.0.0"))) {
249 						targetVersion = null;
250 						importPackagesModified = true;
251 						if (log.isDebugEnabled())
252 							log.debug(origSymbolicName
253 									+ ": Nullify version of " + pkg + " from "
254 									+ sourceVersion);
255 					}
256 				}
257 				if (targetVersion != null)
258 					targetImportPackages.append(";version=\"")
259 							.append(targetVersion).append("\"");
260 			}
261 			if (importPackage.hasProperty(SLC_OPTIONAL)) {
262 				Boolean optional = importPackage.getProperty(SLC_OPTIONAL)
263 						.getBoolean();
264 				if (optional)
265 					targetImportPackages.append(";resolution:=\"optional\"");
266 
267 			}
268 			if (origImportPackages.hasNext())
269 				targetImportPackages.append(",");
270 		}
271 
272 		if (importPackagesModified) {
273 			targetManifest.getMainAttributes().putValue(
274 					Constants.IMPORT_PACKAGE, targetImportPackages.toString());
275 			manifestModified = true;
276 		}
277 
278 		if (!manifestModified && log.isTraceEnabled()) {
279 			log.trace("MANIFEST of " + origSymbolicName + " was not modified");
280 		}
281 
282 		// target coordinates
283 		final String targetGroupId;
284 		if (origArtifact.getArtifactId().startsWith(
285 				"org.eclipse.rwt.widgets.upload"))
286 			targetGroupId = "org.argeo.tp.rap";
287 		else if (origArtifact.getArtifactId().startsWith("org.polymap"))
288 			targetGroupId = "org.argeo.tp.rap";
289 		else if (origArtifact.getGroupId().startsWith("org.eclipse")
290 				&& !origArtifact.getArtifactId().equals("org.eclipse.osgi"))
291 			throw new SlcException(origArtifact + " should have been excluded");// targetGroupId
292 																				// =
293 																				// "org.argeo.tp.eclipse";
294 		else
295 			targetGroupId = "org.argeo.tp";
296 
297 		String targetArtifactId = targetSymbolicName.split(";")[0];
298 		Artifact targetArtifact = new DefaultArtifact(targetGroupId,
299 				targetArtifactId, "jar", origArtifact.getVersion());
300 		String targetParentPath = MavenConventionsUtils.artifactParentPath(
301 				artifactBasePath, targetArtifact);
302 		String targetFileName = MavenConventionsUtils
303 				.artifactFileName(targetArtifact);
304 		String targetJarPath = targetParentPath + '/' + targetFileName;
305 
306 		// copy
307 		Node targetParentNode = JcrUtils.mkfolders(targetSession,
308 				targetParentPath);
309 		targetSession.save();
310 		if (manifestModified) {
311 			Binary origBinary = origJarNode.getNode(Node.JCR_CONTENT)
312 					.getProperty(Property.JCR_DATA).getBinary();
313 			byte[] targetJarBytes = RepoUtils.modifyManifest(
314 					origBinary.getStream(), targetManifest);
315 			JcrUtils.copyBytesAsFile(targetParentNode, targetFileName,
316 					targetJarBytes);
317 			JcrUtils.closeQuietly(origBinary);
318 		} else {// just copy
319 			targetSession.getWorkspace().copy(sourceWorkspace,
320 					origJarNode.getPath(), targetJarPath);
321 		}
322 		targetSession.save();
323 
324 		// reindex
325 		Node targetJarNode = targetSession.getNode(targetJarPath);
326 		artifactIndexer.index(targetJarNode);
327 		jarFileIndexer.index(targetJarNode);
328 
329 		targetSession.save();
330 
331 		// sources
332 		Artifact origSourceArtifact = new DefaultArtifact(
333 				origArtifact.getGroupId(), origArtifact.getArtifactId()
334 						+ ".source", "jar", origArtifact.getVersion());
335 		String origSourcePath = MavenConventionsUtils.artifactPath(
336 				artifactBasePath, origSourceArtifact);
337 		if (origSession.itemExists(origSourcePath)) {
338 			Node origSourceJarNode = origSession.getNode(origSourcePath);
339 
340 			Artifact targetSourceArtifact = new DefaultArtifact(targetGroupId,
341 					targetArtifactId + ".source", "jar",
342 					origArtifact.getVersion());
343 			String targetSourceParentPath = MavenConventionsUtils
344 					.artifactParentPath(artifactBasePath, targetSourceArtifact);
345 			String targetSourceFileName = MavenConventionsUtils
346 					.artifactFileName(targetSourceArtifact);
347 			String targetSourceJarPath = targetSourceParentPath + '/'
348 					+ targetSourceFileName;
349 
350 			Node targetSourceParentNode = JcrUtils.mkfolders(targetSession,
351 					targetSourceParentPath);
352 			targetSession.save();
353 
354 			if (!targetSymbolicName.equals(origSymbolicName)) {
355 				Binary origBinary = origSourceJarNode.getNode(Node.JCR_CONTENT)
356 						.getProperty(Property.JCR_DATA).getBinary();
357 				NameVersion targetNameVersion = RepoUtils
358 						.readNameVersion(targetManifest);
359 				byte[] targetJarBytes = RepoUtils.packageAsPdeSource(
360 						origBinary.getStream(), targetNameVersion);
361 				JcrUtils.copyBytesAsFile(targetSourceParentNode,
362 						targetSourceFileName, targetJarBytes);
363 				JcrUtils.closeQuietly(origBinary);
364 			} else {// just copy
365 				targetSession.getWorkspace().copy(sourceWorkspace,
366 						origSourceJarNode.getPath(), targetSourceJarPath);
367 			}
368 			targetSession.save();
369 
370 			// reindex
371 			Node targetSourceJarNode = targetSession
372 					.getNode(targetSourceJarPath);
373 			artifactIndexer.index(targetSourceJarNode);
374 			jarFileIndexer.index(targetSourceJarNode);
375 
376 			targetSession.save();
377 		}
378 	}
379 
380 	/*
381 	 * UTILITIES
382 	 */
383 
384 	static NodeIterator listArtifactVersions(Session session)
385 			throws RepositoryException {
386 		QueryManager queryManager = session.getWorkspace().getQueryManager();
387 		QueryObjectModelFactory factory = queryManager.getQOMFactory();
388 
389 		final String artifactVersionsSelector = "artifactVersions";
390 		Selector source = factory.selector(SlcTypes.SLC_ARTIFACT_VERSION_BASE,
391 				artifactVersionsSelector);
392 
393 		Ordering orderByArtifactId = factory.ascending(factory.propertyValue(
394 				artifactVersionsSelector, SlcNames.SLC_ARTIFACT_ID));
395 		Ordering[] orderings = { orderByArtifactId };
396 
397 		QueryObjectModel query = factory.createQuery(source, null, orderings,
398 				null);
399 
400 		QueryResult result = query.execute();
401 		return result.getNodes();
402 	}
403 
404 	public void setRepository(Repository repository) {
405 		this.repository = repository;
406 	}
407 
408 	public void setSourceWorkspace(String sourceWorkspace) {
409 		this.sourceWorkspace = sourceWorkspace;
410 	}
411 
412 	public void setTargetWorkspace(String targetWorkspace) {
413 		this.targetWorkspace = targetWorkspace;
414 	}
415 
416 	public void setExcludedBundles(List<String> excludedBundles) {
417 		this.excludedBundles = excludedBundles;
418 	}
419 
420 	public void setSymbolicNamesMapping(Map<String, String> symbolicNamesMapping) {
421 		this.symbolicNamesMapping = symbolicNamesMapping;
422 	}
423 
424 }