2 * Copyright (C) 2007-2012 Argeo GmbH
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org
.argeo
.slc
.repo
;
18 import java
.io
.ByteArrayOutputStream
;
20 import java
.io
.FileInputStream
;
21 import java
.io
.IOException
;
22 import java
.io
.InputStream
;
23 import java
.io
.OutputStream
;
24 import java
.util
.Enumeration
;
25 import java
.util
.Iterator
;
26 import java
.util
.StringTokenizer
;
27 import java
.util
.jar
.Attributes
;
28 import java
.util
.jar
.JarEntry
;
29 import java
.util
.jar
.JarFile
;
30 import java
.util
.jar
.JarInputStream
;
31 import java
.util
.jar
.JarOutputStream
;
32 import java
.util
.jar
.Manifest
;
34 import javax
.jcr
.Credentials
;
35 import javax
.jcr
.GuestCredentials
;
36 import javax
.jcr
.Node
;
37 import javax
.jcr
.NodeIterator
;
38 import javax
.jcr
.Property
;
39 import javax
.jcr
.PropertyIterator
;
40 import javax
.jcr
.Repository
;
41 import javax
.jcr
.RepositoryException
;
42 import javax
.jcr
.RepositoryFactory
;
43 import javax
.jcr
.SimpleCredentials
;
44 import javax
.jcr
.nodetype
.NodeType
;
46 import org
.apache
.commons
.io
.FilenameUtils
;
47 import org
.apache
.commons
.io
.IOUtils
;
48 import org
.apache
.commons
.logging
.Log
;
49 import org
.apache
.commons
.logging
.LogFactory
;
50 import org
.argeo
.jcr
.ArgeoJcrUtils
;
51 import org
.argeo
.jcr
.ArgeoNames
;
52 import org
.argeo
.jcr
.ArgeoTypes
;
53 import org
.argeo
.jcr
.JcrUtils
;
54 import org
.argeo
.slc
.BasicNameVersion
;
55 import org
.argeo
.slc
.NameVersion
;
56 import org
.argeo
.slc
.SlcException
;
57 import org
.argeo
.slc
.jcr
.SlcNames
;
58 import org
.argeo
.slc
.jcr
.SlcTypes
;
59 import org
.argeo
.slc
.repo
.maven
.MavenConventionsUtils
;
60 import org
.argeo
.util
.security
.Keyring
;
61 import org
.osgi
.framework
.Constants
;
62 import org
.sonatype
.aether
.artifact
.Artifact
;
63 import org
.sonatype
.aether
.util
.artifact
.DefaultArtifact
;
65 /** Utilities around repo */
66 public class RepoUtils
implements ArgeoNames
, SlcNames
{
67 private final static Log log
= LogFactory
.getLog(RepoUtils
.class);
69 /** Packages a regular sources jar as PDE source. */
70 public static void packagesAsPdeSource(File sourceFile
,
71 NameVersion nameVersion
, OutputStream out
) throws IOException
{
72 if (isAlreadyPdeSource(sourceFile
)) {
73 FileInputStream in
= new FileInputStream(sourceFile
);
74 IOUtils
.copy(in
, out
);
75 IOUtils
.closeQuietly(in
);
77 String sourceSymbolicName
= nameVersion
.getName() + ".source";
79 Manifest sourceManifest
= null;
80 sourceManifest
= new Manifest();
81 sourceManifest
.getMainAttributes().put(
82 Attributes
.Name
.MANIFEST_VERSION
, "1.0");
83 sourceManifest
.getMainAttributes().putValue("Bundle-SymbolicName",
85 sourceManifest
.getMainAttributes().putValue("Bundle-Version",
86 nameVersion
.getVersion());
87 sourceManifest
.getMainAttributes().putValue(
88 "Eclipse-SourceBundle",
89 nameVersion
.getName() + ";version="
90 + nameVersion
.getVersion());
91 copyJar(sourceFile
, out
, sourceManifest
);
95 public static byte[] packageAsPdeSource(InputStream sourceJar
,
96 NameVersion nameVersion
) {
97 String sourceSymbolicName
= nameVersion
.getName() + ".source";
99 Manifest sourceManifest
= null;
100 sourceManifest
= new Manifest();
101 sourceManifest
.getMainAttributes().put(
102 Attributes
.Name
.MANIFEST_VERSION
, "1.0");
103 sourceManifest
.getMainAttributes().putValue("Bundle-SymbolicName",
105 sourceManifest
.getMainAttributes().putValue("Bundle-Version",
106 nameVersion
.getVersion());
107 sourceManifest
.getMainAttributes().putValue("Eclipse-SourceBundle",
108 nameVersion
.getName() + ";version=" + nameVersion
.getVersion());
110 return modifyManifest(sourceJar
, sourceManifest
);
114 * Check whether the file as already been packaged as PDE source, in order
115 * not to mess with Jar signing
117 private static boolean isAlreadyPdeSource(File sourceFile
) {
118 JarInputStream jarInputStream
= null;
121 jarInputStream
= new JarInputStream(new FileInputStream(sourceFile
));
123 Manifest manifest
= jarInputStream
.getManifest();
124 Iterator
<?
> it
= manifest
.getMainAttributes().keySet().iterator();
126 // containsKey() does not work, iterating...
128 if (it
.next().toString().equals("Eclipse-SourceBundle")) {
132 // boolean res = manifest.getMainAttributes().get(
133 // "Eclipse-SourceBundle") != null;
135 log
.info(sourceFile
+ " is already a PDE source");
137 } catch (Exception e
) {
138 // probably not a jar, skipping
139 if (log
.isDebugEnabled())
140 log
.debug("Skipping " + sourceFile
+ " because of "
144 IOUtils
.closeQuietly(jarInputStream
);
149 * Copy a jar, replacing its manifest with the provided one
154 private static void copyJar(File source
, OutputStream out
, Manifest manifest
)
156 JarFile sourceJar
= null;
157 JarOutputStream output
= null;
159 output
= manifest
!= null ?
new JarOutputStream(out
, manifest
)
160 : new JarOutputStream(out
);
161 sourceJar
= new JarFile(source
);
163 entries
: for (Enumeration
<?
> entries
= sourceJar
.entries(); entries
164 .hasMoreElements();) {
165 JarEntry entry
= (JarEntry
) entries
.nextElement();
167 && entry
.getName().equals("META-INF/MANIFEST.MF"))
170 InputStream entryStream
= sourceJar
.getInputStream(entry
);
171 JarEntry newEntry
= new JarEntry(entry
.getName());
172 // newEntry.setMethod(JarEntry.DEFLATED);
173 output
.putNextEntry(newEntry
);
174 IOUtils
.copy(entryStream
, output
);
177 IOUtils
.closeQuietly(output
);
179 if (sourceJar
!= null)
181 } catch (IOException e
) {
187 /** Copy a jar changing onlythe manifest */
188 public static void copyJar(InputStream in
, OutputStream out
,
190 JarInputStream jarIn
= null;
191 JarOutputStream jarOut
= null;
193 jarIn
= new JarInputStream(in
);
194 jarOut
= new JarOutputStream(out
, manifest
);
195 JarEntry jarEntry
= null;
196 while ((jarEntry
= jarIn
.getNextJarEntry()) != null) {
197 jarOut
.putNextEntry(jarEntry
);
198 IOUtils
.copy(jarIn
, jarOut
);
202 } catch (IOException e
) {
203 throw new SlcException("Could not copy jar with MANIFEST "
204 + manifest
.getMainAttributes(), e
);
206 IOUtils
.closeQuietly(jarIn
);
207 IOUtils
.closeQuietly(jarOut
);
211 /** Reads a jar file, modify its manifest */
212 public static byte[] modifyManifest(InputStream in
, Manifest manifest
) {
213 ByteArrayOutputStream out
= new ByteArrayOutputStream(200 * 1024);
215 copyJar(in
, out
, manifest
);
216 return out
.toByteArray();
218 IOUtils
.closeQuietly(out
);
222 /** Read the OSGi {@link NameVersion} */
223 public static NameVersion
readNameVersion(Artifact artifact
) {
224 File artifactFile
= artifact
.getFile();
225 if (artifact
.getExtension().equals("pom")) {
226 // hack to process jars which weirdly appear as POMs
227 File jarFile
= new File(artifactFile
.getParentFile(),
228 FilenameUtils
.getBaseName(artifactFile
.getPath()) + ".jar");
229 if (jarFile
.exists()) {
230 log
.warn("Use " + jarFile
+ " instead of " + artifactFile
231 + " for " + artifact
);
232 artifactFile
= jarFile
;
235 return readNameVersion(artifactFile
);
238 /** Read the OSGi {@link NameVersion} */
239 public static NameVersion
readNameVersion(File artifactFile
) {
241 return readNameVersion(new FileInputStream(artifactFile
));
242 } catch (Exception e
) {
243 // probably not a jar, skipping
244 if (log
.isDebugEnabled()) {
245 log
.debug("Skipping " + artifactFile
+ " because of " + e
);
246 // e.printStackTrace();
252 /** Read the OSGi {@link NameVersion} */
253 public static NameVersion
readNameVersion(InputStream in
) {
254 JarInputStream jarInputStream
= null;
256 jarInputStream
= new JarInputStream(in
);
257 return readNameVersion(jarInputStream
.getManifest());
258 } catch (Exception e
) {
259 // probably not a jar, skipping
260 if (log
.isDebugEnabled()) {
261 log
.debug("Skipping because of " + e
);
262 // e.printStackTrace();
265 IOUtils
.closeQuietly(jarInputStream
);
270 /** Read the OSGi {@link NameVersion} */
271 public static NameVersion
readNameVersion(Manifest manifest
) {
272 BasicNameVersion nameVersion
= new BasicNameVersion();
273 nameVersion
.setName(manifest
.getMainAttributes().getValue(
274 Constants
.BUNDLE_SYMBOLICNAME
));
276 // Skip additional specs such as
278 if (nameVersion
.getName().indexOf(';') > -1) {
280 .setName(new StringTokenizer(nameVersion
.getName(), " ;")
284 nameVersion
.setVersion(manifest
.getMainAttributes().getValue(
285 Constants
.BUNDLE_VERSION
));
293 /** The artifact described by this node */
294 public static Artifact
asArtifact(Node node
) throws RepositoryException
{
295 if (node
.isNodeType(SlcTypes
.SLC_ARTIFACT_VERSION_BASE
)) {
296 // FIXME update data model to store packaging at this level
297 String extension
= "jar";
298 return new DefaultArtifact(node
.getProperty(SLC_GROUP_ID
)
300 node
.getProperty(SLC_ARTIFACT_ID
).getString(), extension
,
301 node
.getProperty(SLC_ARTIFACT_VERSION
).getString());
302 } else if (node
.isNodeType(SlcTypes
.SLC_ARTIFACT
)) {
303 return new DefaultArtifact(node
.getProperty(SLC_GROUP_ID
)
305 node
.getProperty(SLC_ARTIFACT_ID
).getString(), node
306 .getProperty(SLC_ARTIFACT_CLASSIFIER
).getString(),
307 node
.getProperty(SLC_ARTIFACT_EXTENSION
).getString(), node
308 .getProperty(SLC_ARTIFACT_VERSION
).getString());
310 throw new SlcException("Unsupported node type for " + node
);
315 * Copy this bytes array as an artifact, relative to the root of the
316 * repository (typically the workspace root node)
318 public static Node
copyBytesAsArtifact(Node artifactsBase
,
319 Artifact artifact
, byte[] bytes
) throws RepositoryException
{
320 String parentPath
= MavenConventionsUtils
.artifactParentPath(
321 artifactsBase
.getPath(), artifact
);
322 Node folderNode
= JcrUtils
.mkfolders(artifactsBase
.getSession(),
324 return JcrUtils
.copyBytesAsFile(folderNode
,
325 MavenConventionsUtils
.artifactFileName(artifact
), bytes
);
328 private RepoUtils() {
331 /** If a source return the base bundle name, does not change otherwise */
332 public static String
extractBundleNameFromSourceName(String sourceBundleName
) {
333 if (sourceBundleName
.endsWith(".source"))
334 return sourceBundleName
.substring(0, sourceBundleName
.length()
335 - ".source".length());
337 return sourceBundleName
;
341 * SOFTWARE REPOSITORIES
344 /** Retrieve repository based on information in the repo node */
345 public static Repository
getRepository(RepositoryFactory repositoryFactory
,
346 Keyring keyring
, Node repoNode
) {
348 Repository repository
;
349 if (repoNode
.isNodeType(ArgeoTypes
.ARGEO_REMOTE_REPOSITORY
)) {
350 String uri
= repoNode
.getProperty(ARGEO_URI
).getString();
351 if (uri
.startsWith("http")) {// http, https
352 repository
= ArgeoJcrUtils
.getRepositoryByUri(
353 repositoryFactory
, uri
);
354 } else if (uri
.startsWith("vm:")) {// alias
355 repository
= ArgeoJcrUtils
.getRepositoryByUri(
356 repositoryFactory
, uri
);
358 throw new SlcException("Unsupported repository uri " + uri
);
362 throw new SlcException("Unsupported node type " + repoNode
);
364 } catch (RepositoryException e
) {
365 throw new SlcException("Cannot connect to repository " + repoNode
,
372 * Reads credentials from node, using keyring if there is a password. Cann
373 * return null if no credentials needed (local repo) at all, but returns
374 * {@link GuestCredentials} if user id is 'anonymous' .
376 public static Credentials
getRepositoryCredentials(Keyring keyring
,
379 if (repoNode
.isNodeType(ArgeoTypes
.ARGEO_REMOTE_REPOSITORY
)) {
380 if (!repoNode
.hasProperty(ARGEO_USER_ID
))
383 String userId
= repoNode
.getProperty(ARGEO_USER_ID
).getString();
384 if (userId
.equals("anonymous"))// FIXME hardcoded userId
385 return new GuestCredentials();
386 char[] password
= keyring
.getAsChars(repoNode
.getPath() + '/'
388 Credentials credentials
= new SimpleCredentials(userId
,
392 throw new SlcException("Unsupported node type " + repoNode
);
394 } catch (RepositoryException e
) {
395 throw new SlcException("Cannot connect to repository " + repoNode
,
401 * Custom copy since the one in commons does not fit the needs when copying
402 * a workspace completely.
404 public static void copy(Node fromNode
, Node toNode
) {
406 if (log
.isDebugEnabled())
407 log
.debug("copy node :" + fromNode
.getPath());
409 // FIXME : small hack to enable specific workspace copy
410 if (fromNode
.isNodeType("rep:ACL")
411 || fromNode
.isNodeType("rep:system")) {
412 if (log
.isTraceEnabled())
413 log
.trace("node " + fromNode
+ " skipped");
418 for (NodeType mixinType
: fromNode
.getMixinNodeTypes()) {
419 toNode
.addMixin(mixinType
.getName());
423 for (NodeType mixinType
: toNode
.getMixinNodeTypes()) {
424 if (log
.isDebugEnabled())
425 log
.debug(mixinType
.getName());
428 // process properties
429 PropertyIterator pit
= fromNode
.getProperties();
430 properties
: while (pit
.hasNext()) {
431 Property fromProperty
= pit
.nextProperty();
432 String propName
= fromProperty
.getName();
434 String propertyName
= fromProperty
.getName();
435 if (toNode
.hasProperty(propertyName
)
436 && toNode
.getProperty(propertyName
).getDefinition()
440 if (fromProperty
.getDefinition().isProtected())
443 if (propertyName
.equals("jcr:created")
444 || propertyName
.equals("jcr:createdBy")
445 || propertyName
.equals("jcr:lastModified")
446 || propertyName
.equals("jcr:lastModifiedBy"))
449 if (fromProperty
.isMultiple()) {
450 toNode
.setProperty(propertyName
,
451 fromProperty
.getValues());
453 toNode
.setProperty(propertyName
,
454 fromProperty
.getValue());
456 } catch (RepositoryException e
) {
457 throw new SlcException("Cannot property " + propName
, e
);
461 // recursively process children nodes
462 NodeIterator nit
= fromNode
.getNodes();
463 while (nit
.hasNext()) {
464 Node fromChild
= nit
.nextNode();
465 Integer index
= fromChild
.getIndex();
466 String nodeRelPath
= fromChild
.getName() + "[" + index
+ "]";
468 if (toNode
.hasNode(nodeRelPath
))
469 toChild
= toNode
.getNode(nodeRelPath
);
471 toChild
= toNode
.addNode(fromChild
.getName(), fromChild
472 .getPrimaryNodeType().getName());
473 copy(fromChild
, toChild
);
476 // update jcr:lastModified and jcr:lastModifiedBy in toNode in
479 if (!toNode
.getDefinition().isProtected()
480 && toNode
.isNodeType(NodeType
.MIX_LAST_MODIFIED
))
481 JcrUtils
.updateLastModified(toNode
);
483 // Workaround to reduce session size: artifact is a saveable
485 if (toNode
.isNodeType(SlcTypes
.SLC_ARTIFACT
))
486 toNode
.getSession().save();
488 } catch (RepositoryException e
) {
489 throw new SlcException("Cannot copy " + fromNode
+ " to " + toNode
,