From 912cf5554a61dc35fb61183da39673656b668467 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Mon, 26 Sep 2011 20:46:13 +0000 Subject: [PATCH] Working indexation (artifact, jar, osgi) in repo git-svn-id: https://svn.argeo.org/slc/trunk@4755 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- .../META-INF/MANIFEST.MF | 3 +- .../WEB-INF/maven-servlet.xml | 45 +-- .../WEB-INF/osgi.xml | 6 +- .../WEB-INF/security.xml | 17 +- .../META-INF/MANIFEST.MF | 7 +- .../META-INF/spring/jcr.xml | 4 +- .../META-INF/spring/maven.xml | 5 +- .../META-INF/spring/repo-osgi.xml | 7 + .../META-INF/spring/services.xml | 32 ++ .../org/argeo/slc/repo/ArtifactIndexer.java | 102 +++++ .../org/argeo/slc/repo/JarFileIndexer.java | 365 ++++++++++++++++++ .../java/org/argeo/slc/repo/NodeIndexer.java | 29 ++ .../java/org/argeo/slc/repo/RepoImpl.java | 88 +++++ .../repo/maven/ImportMavenDependencies.java | 26 +- .../repo/maven/proxy/MavenProxyService.java | 7 +- .../maven/proxy/MavenProxyServiceImpl.java | 115 ++---- .../repo/maven/proxy/MavenProxyServlet.java | 123 ------ .../resources/org/argeo/slc/repo/repo.cnd | 15 + .../org.argeo.slc.support.aether/.classpath | 1 + .../build.properties | 9 +- .../org/argeo/slc/aether/AetherUtils.java | 130 +++++++ .../org/argeo/slc/aether/AetherUtilsTest.java | 52 +++ .../META-INF/spring/maven-osgi.xml | 1 + 23 files changed, 889 insertions(+), 300 deletions(-) create mode 100644 modules/server/org.argeo.slc.server.repo/META-INF/spring/services.xml create mode 100644 runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/ArtifactIndexer.java create mode 100644 runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/JarFileIndexer.java create mode 100644 runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/NodeIndexer.java create mode 100644 runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/RepoImpl.java delete mode 100644 runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/maven/proxy/MavenProxyServlet.java create mode 100644 runtime/org.argeo.slc.support.aether/src/test/java/org/argeo/slc/aether/AetherUtilsTest.java diff --git a/modules/server/org.argeo.slc.server.repo.webapp/META-INF/MANIFEST.MF b/modules/server/org.argeo.slc.server.repo.webapp/META-INF/MANIFEST.MF index 04874c01c..9a8c42a02 100644 --- a/modules/server/org.argeo.slc.server.repo.webapp/META-INF/MANIFEST.MF +++ b/modules/server/org.argeo.slc.server.repo.webapp/META-INF/MANIFEST.MF @@ -9,6 +9,7 @@ Import-Package: javax.jcr;version="2.0.0", javax.servlet, javax.servlet.http, javax.servlet.resources, + org.argeo.jcr.mvc, org.argeo.security.core, org.argeo.security.jcr, org.argeo.slc.aether.spring, @@ -21,8 +22,6 @@ Import-Package: javax.jcr;version="2.0.0", org.springframework.security.ui;version="2.0.6.RELEASE", org.springframework.security.ui.basicauth;version="2.0.6.RELEASE", org.springframework.security.ui.logout;version="2.0.6.RELEASE", - org.springframework.security.ui.rememberme;version="2.0.6.RELEASE", - org.springframework.security.userdetails;version="2.0.6.RELEASE", org.springframework.security.util;version="2.0.6.RELEASE", org.springframework.security.vote;version="2.0.6.RELEASE", org.springframework.security.wrapper;version="2.0.6.RELEASE", diff --git a/modules/server/org.argeo.slc.server.repo.webapp/WEB-INF/maven-servlet.xml b/modules/server/org.argeo.slc.server.repo.webapp/WEB-INF/maven-servlet.xml index 0c0d817e3..04d5151f0 100644 --- a/modules/server/org.argeo.slc.server.repo.webapp/WEB-INF/maven-servlet.xml +++ b/modules/server/org.argeo.slc.server.repo.webapp/WEB-INF/maven-servlet.xml @@ -22,53 +22,12 @@ - - - - - - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/modules/server/org.argeo.slc.server.repo.webapp/WEB-INF/osgi.xml b/modules/server/org.argeo.slc.server.repo.webapp/WEB-INF/osgi.xml index 15231c8cb..2993316f5 100644 --- a/modules/server/org.argeo.slc.server.repo.webapp/WEB-INF/osgi.xml +++ b/modules/server/org.argeo.slc.server.repo.webapp/WEB-INF/osgi.xml @@ -12,10 +12,12 @@ - + + + + \ No newline at end of file diff --git a/modules/server/org.argeo.slc.server.repo.webapp/WEB-INF/security.xml b/modules/server/org.argeo.slc.server.repo.webapp/WEB-INF/security.xml index 340c8e76d..ddb4d2ec4 100644 --- a/modules/server/org.argeo.slc.server.repo.webapp/WEB-INF/security.xml +++ b/modules/server/org.argeo.slc.server.repo.webapp/WEB-INF/security.xml @@ -45,7 +45,6 @@ - @@ -56,19 +55,6 @@ - - - - - - - - - - - @@ -78,14 +64,13 @@ - - Argeo + Argeo Repository diff --git a/modules/server/org.argeo.slc.server.repo/META-INF/MANIFEST.MF b/modules/server/org.argeo.slc.server.repo/META-INF/MANIFEST.MF index cb1a3da59..e64aeef45 100644 --- a/modules/server/org.argeo.slc.server.repo/META-INF/MANIFEST.MF +++ b/modules/server/org.argeo.slc.server.repo/META-INF/MANIFEST.MF @@ -6,7 +6,12 @@ Bundle-Version: 0.13.1.SNAPSHOT Import-Package: javax.jcr;version="2.0.0", org.argeo.jackrabbit, org.argeo.jcr, + org.argeo.security.core, + org.argeo.security.jcr, org.argeo.slc.aether, org.argeo.slc.aether.spring, + org.argeo.slc.repo, org.argeo.slc.repo.maven, - org.sonatype.aether;version="1.9.0" + org.argeo.slc.repo.maven.proxy, + org.sonatype.aether;version="1.9.0", + org.springframework.security;version="2.0.6.RELEASE" diff --git a/modules/server/org.argeo.slc.server.repo/META-INF/spring/jcr.xml b/modules/server/org.argeo.slc.server.repo/META-INF/spring/jcr.xml index 299991b76..27566c82a 100644 --- a/modules/server/org.argeo.slc.server.repo/META-INF/spring/jcr.xml +++ b/modules/server/org.argeo.slc.server.repo/META-INF/spring/jcr.xml @@ -3,9 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> - + - - \ No newline at end of file diff --git a/modules/server/org.argeo.slc.server.repo/META-INF/spring/maven.xml b/modules/server/org.argeo.slc.server.repo/META-INF/spring/maven.xml index 2cdbbab71..7f6b1d4f1 100644 --- a/modules/server/org.argeo.slc.server.repo/META-INF/spring/maven.xml +++ b/modules/server/org.argeo.slc.server.repo/META-INF/spring/maven.xml @@ -7,7 +7,7 @@ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd "> - + + diff --git a/modules/server/org.argeo.slc.server.repo/META-INF/spring/repo-osgi.xml b/modules/server/org.argeo.slc.server.repo/META-INF/spring/repo-osgi.xml index bb39e61e2..1bf904e19 100644 --- a/modules/server/org.argeo.slc.server.repo/META-INF/spring/repo-osgi.xml +++ b/modules/server/org.argeo.slc.server.repo/META-INF/spring/repo-osgi.xml @@ -8,6 +8,10 @@ http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd"> + + + @@ -16,4 +20,7 @@ filter="(aether.repositorySystemType=maven)" /> + + + \ No newline at end of file diff --git a/modules/server/org.argeo.slc.server.repo/META-INF/spring/services.xml b/modules/server/org.argeo.slc.server.repo/META-INF/spring/services.xml new file mode 100644 index 000000000..173414f8e --- /dev/null +++ b/modules/server/org.argeo.slc.server.repo/META-INF/spring/services.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/ArtifactIndexer.java b/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/ArtifactIndexer.java new file mode 100644 index 000000000..4b7677961 --- /dev/null +++ b/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/ArtifactIndexer.java @@ -0,0 +1,102 @@ +package org.argeo.slc.repo; + +import javax.jcr.Node; +import javax.jcr.nodetype.NodeType; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.slc.SlcException; +import org.argeo.slc.aether.AetherUtils; +import org.argeo.slc.jcr.SlcNames; +import org.argeo.slc.jcr.SlcTypes; +import org.sonatype.aether.artifact.Artifact; + +/** + * Add {@link Artifact} properties to a {@link Node}. Does nothing if the node + * name doesn't start with the artifact id (in order to skip Maven metadata XML + * files and other non artifact files). + */ +public class ArtifactIndexer implements NodeIndexer { + private Log log = LogFactory.getLog(ArtifactIndexer.class); + + public Boolean support(String path) { + String relativePath = getRelativePath(path); + if (relativePath == null) + return false; + Artifact artifact = null; + try { + artifact = AetherUtils.convertPathToArtifact(relativePath, null); + } catch (Exception e) { + if (log.isTraceEnabled()) + log.trace("Malformed path " + path + ", skipping silently", e); + } + return artifact != null; + } + + public void index(Node fileNode) { + Artifact artifact = null; + try { + if (!fileNode.isNodeType(NodeType.NT_FILE)) + return; + + String relativePath = getRelativePath(fileNode.getPath()); + if (relativePath == null) + return; + artifact = AetherUtils.convertPathToArtifact(relativePath, null); + // support() guarantees that artifact won't be null, no NPE check + fileNode.addMixin(SlcTypes.SLC_ARTIFACT); + fileNode.setProperty(SlcNames.SLC_ARTIFACT_ID, + artifact.getArtifactId()); + fileNode.setProperty(SlcNames.SLC_GROUP_ID, artifact.getGroupId()); + fileNode.setProperty(SlcNames.SLC_ARTIFACT_VERSION, + artifact.getVersion()); + fileNode.setProperty(SlcNames.SLC_ARTIFACT_EXTENSION, + artifact.getExtension()); + // can be null but ok for JCR API + fileNode.setProperty(SlcNames.SLC_ARTIFACT_CLASSIFIER, + artifact.getClassifier()); + + // set higher levels + Node artifactVersionBase = fileNode.getParent(); + if (!artifactVersionBase + .isNodeType(SlcTypes.SLC_ARTIFACT_VERSION_BASE)) { + artifactVersionBase + .addMixin(SlcTypes.SLC_ARTIFACT_VERSION_BASE); + artifactVersionBase.setProperty(SlcNames.SLC_ARTIFACT_VERSION, + artifact.getBaseVersion()); + artifactVersionBase.setProperty(SlcNames.SLC_ARTIFACT_ID, + artifact.getArtifactId()); + artifactVersionBase.setProperty(SlcNames.SLC_GROUP_ID, + artifact.getGroupId()); + } + Node artifactBase = artifactVersionBase.getParent(); + if (!artifactBase.isNodeType(SlcTypes.SLC_ARTIFACT_BASE)) { + artifactBase.addMixin(SlcTypes.SLC_ARTIFACT_BASE); + artifactBase.setProperty(SlcNames.SLC_ARTIFACT_ID, + artifact.getArtifactId()); + artifactBase.setProperty(SlcNames.SLC_GROUP_ID, + artifact.getGroupId()); + } + Node groupBase = artifactBase.getParent(); + if (!groupBase.isNodeType(SlcTypes.SLC_GROUP_BASE)) { + groupBase.addMixin(SlcTypes.SLC_GROUP_BASE); + groupBase.setProperty(SlcNames.SLC_GROUP_ID, + artifact.getGroupId()); + } + + if (log.isTraceEnabled()) + log.trace("Indexed artifact " + artifact + " on " + fileNode); + } catch (Exception e) { + throw new SlcException("Cannot index artifact " + artifact + + " metadata on node " + fileNode, e); + } + } + + private String getRelativePath(String nodePath) { + String basePath = RepoConstants.ARTIFACTS_BASE_PATH; + if (!nodePath.startsWith(basePath)) + return null; + String relativePath = nodePath.substring(basePath.length()); + return relativePath; + } +} diff --git a/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/JarFileIndexer.java b/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/JarFileIndexer.java new file mode 100644 index 000000000..92460b021 --- /dev/null +++ b/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/JarFileIndexer.java @@ -0,0 +1,365 @@ +package org.argeo.slc.repo; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.Attributes.Name; +import java.util.jar.JarInputStream; +import java.util.jar.Manifest; + +import javax.jcr.Binary; +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.jcr.JcrUtils; +import org.argeo.slc.SlcException; +import org.argeo.slc.jcr.SlcNames; +import org.argeo.slc.jcr.SlcTypes; +import org.osgi.framework.Constants; +import org.osgi.framework.Version; + +/** + * Indexes jar file, currently supports standard J2SE and OSGi metadata (both + * from MANIFEST) + */ +public class JarFileIndexer implements NodeIndexer { + private final static Log log = LogFactory.getLog(JarFileIndexer.class); + + public Boolean support(String path) { + return FilenameUtils.getExtension(path).equals("jar"); + } + + public void index(Node fileNode) { + Binary fileBinary = null; + JarInputStream jarIn = null; + ByteArrayOutputStream bo = null; + ByteArrayInputStream bi = null; + Binary manifestBinary = null; + try { + if (!fileNode.isNodeType(NodeType.NT_FILE)) + return; + + Session jcrSession = fileNode.getSession(); + Node contentNode = fileNode.getNode(Node.JCR_CONTENT); + fileBinary = contentNode.getProperty(Property.JCR_DATA).getBinary(); + // jar file + // if (!FilenameUtils.isExtension(fileNode.getName(), "jar")) { + // return; + // } + + jarIn = new JarInputStream(fileBinary.getStream()); + Manifest manifest = jarIn.getManifest(); + if(manifest==null){ + log.error(fileNode+" has no MANIFEST"); + return; + } + bo = new ByteArrayOutputStream(); + manifest.write(bo); + bi = new ByteArrayInputStream(bo.toByteArray()); + manifestBinary = jcrSession.getValueFactory().createBinary(bi); + + // standard jar file + fileNode.addMixin(SlcTypes.SLC_JAR_FILE); + fileNode.setProperty(SlcNames.SLC_MANIFEST, manifestBinary); + Attributes attrs = manifest.getMainAttributes(); + if (log.isTraceEnabled()) + for (Object key : attrs.keySet()) + log.trace(key + ": " + attrs.getValue(key.toString())); + + // standard J2SE MANIFEST attributes + addAttr(Attributes.Name.MANIFEST_VERSION, fileNode, attrs); + addAttr(Attributes.Name.SIGNATURE_VERSION, fileNode, attrs); + addAttr(Attributes.Name.CLASS_PATH, fileNode, attrs); + addAttr(Attributes.Name.MAIN_CLASS, fileNode, attrs); + addAttr(Attributes.Name.EXTENSION_NAME, fileNode, attrs); + addAttr(Attributes.Name.IMPLEMENTATION_VERSION, fileNode, attrs); + addAttr(Attributes.Name.IMPLEMENTATION_VENDOR, fileNode, attrs); + addAttr(Attributes.Name.IMPLEMENTATION_VENDOR_ID, fileNode, attrs); + addAttr(Attributes.Name.SPECIFICATION_TITLE, fileNode, attrs); + addAttr(Attributes.Name.SPECIFICATION_VERSION, fileNode, attrs); + addAttr(Attributes.Name.SPECIFICATION_VENDOR, fileNode, attrs); + addAttr(Attributes.Name.SEALED, fileNode, attrs); + + // OSGi + if (attrs.containsKey(new Name(Constants.BUNDLE_SYMBOLICNAME))) { + addOsgiMetadata(fileNode, attrs); + if (log.isTraceEnabled()) + log.trace("Indexed OSGi bundle " + fileNode); + } else { + if (log.isTraceEnabled()) + log.trace("Indexed JAR file " + fileNode); + } + } catch (Exception e) { + throw new SlcException("Cannot index jar " + fileNode, e); + } finally { + IOUtils.closeQuietly(bi); + IOUtils.closeQuietly(bo); + IOUtils.closeQuietly(jarIn); + JcrUtils.closeQuietly(manifestBinary); + JcrUtils.closeQuietly(fileBinary); + } + + } + + protected void addOsgiMetadata(Node fileNode, Attributes attrs) + throws RepositoryException { + fileNode.addMixin(SlcTypes.SLC_BUNDLE_ARTIFACT); + + // symbolic name + String symbolicName = attrs.getValue(Constants.BUNDLE_SYMBOLICNAME); + // make sure there is no directive + symbolicName = symbolicName.split(";")[0]; + fileNode.setProperty(SlcNames.SLC_SYMBOLIC_NAME, symbolicName); + + // direct mapping + addAttr(Constants.BUNDLE_SYMBOLICNAME, fileNode, attrs); + addAttr(Constants.BUNDLE_NAME, fileNode, attrs); + addAttr(Constants.BUNDLE_DESCRIPTION, fileNode, attrs); + addAttr(Constants.BUNDLE_MANIFESTVERSION, fileNode, attrs); + addAttr(Constants.BUNDLE_CATEGORY, fileNode, attrs); + addAttr(Constants.BUNDLE_ACTIVATIONPOLICY, fileNode, attrs); + addAttr(Constants.BUNDLE_COPYRIGHT, fileNode, attrs); + addAttr(Constants.BUNDLE_VENDOR, fileNode, attrs); + addAttr("Bundle-License", fileNode, attrs); + addAttr(Constants.BUNDLE_DOCURL, fileNode, attrs); + addAttr(Constants.BUNDLE_CONTACTADDRESS, fileNode, attrs); + addAttr(Constants.BUNDLE_ACTIVATOR, fileNode, attrs); + addAttr(Constants.BUNDLE_UPDATELOCATION, fileNode, attrs); + addAttr(Constants.BUNDLE_LOCALIZATION, fileNode, attrs); + + // required execution environment + if (attrs.containsKey(new Name( + Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT))) + fileNode.setProperty(SlcNames.SLC_ + + Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT, attrs + .getValue(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT) + .split(",")); + + // bundle classpath + if (attrs.containsKey(new Name(Constants.BUNDLE_CLASSPATH))) + fileNode.setProperty(SlcNames.SLC_ + Constants.BUNDLE_CLASSPATH, + attrs.getValue(Constants.BUNDLE_CLASSPATH).split(",")); + + // version + Version version = new Version(attrs.getValue(Constants.BUNDLE_VERSION)); + fileNode.setProperty(SlcNames.SLC_BUNDLE_VERSION, version.toString()); + cleanSubNodes(fileNode, SlcNames.SLC_ + Constants.BUNDLE_VERSION); + Node bundleVersionNode = fileNode.addNode(SlcNames.SLC_ + + Constants.BUNDLE_VERSION, SlcTypes.SLC_OSGI_VERSION); + mapOsgiVersion(version, bundleVersionNode); + + // fragment + cleanSubNodes(fileNode, SlcNames.SLC_ + Constants.FRAGMENT_HOST); + if (attrs.containsKey(new Name(Constants.FRAGMENT_HOST))) { + String fragmentHost = attrs.getValue(Constants.FRAGMENT_HOST); + String[] tokens = fragmentHost.split(";"); + Node node = fileNode.addNode(SlcNames.SLC_ + + Constants.FRAGMENT_HOST, SlcTypes.SLC_FRAGMENT_HOST); + node.setProperty(SlcNames.SLC_SYMBOLIC_NAME, tokens[0]); + for (int i = 1; i < tokens.length; i++) { + if (tokens[i].startsWith(Constants.BUNDLE_VERSION_ATTRIBUTE)) { + node.setProperty(SlcNames.SLC_BUNDLE_VERSION, + attributeValue(tokens[i])); + } + } + } + + // imported packages + cleanSubNodes(fileNode, SlcNames.SLC_ + Constants.IMPORT_PACKAGE); + if (attrs.containsKey(new Name(Constants.IMPORT_PACKAGE))) { + String importPackages = attrs.getValue(Constants.IMPORT_PACKAGE); + List packages = parsePackages(importPackages); + for (String pkg : packages) { + String[] tokens = pkg.split(";"); + Node node = fileNode.addNode(SlcNames.SLC_ + + Constants.IMPORT_PACKAGE, + SlcTypes.SLC_IMPORTED_PACKAGE); + node.setProperty(SlcNames.SLC_NAME, tokens[0]); + for (int i = 1; i < tokens.length; i++) { + if (tokens[i].startsWith(Constants.VERSION_ATTRIBUTE)) { + node.setProperty(SlcNames.SLC_VERSION, + attributeValue(tokens[i])); + } else if (tokens[i] + .startsWith(Constants.RESOLUTION_DIRECTIVE)) { + node.setProperty( + SlcNames.SLC_OPTIONAL, + directiveValue(tokens[i]).equals( + Constants.RESOLUTION_OPTIONAL)); + } + } + } + } + + // dynamic import package + cleanSubNodes(fileNode, SlcNames.SLC_ + Constants.DYNAMICIMPORT_PACKAGE); + if (attrs.containsKey(new Name(Constants.DYNAMICIMPORT_PACKAGE))) { + String importPackages = attrs + .getValue(Constants.DYNAMICIMPORT_PACKAGE); + List packages = parsePackages(importPackages); + for (String pkg : packages) { + String[] tokens = pkg.split(";"); + Node node = fileNode.addNode(SlcNames.SLC_ + + Constants.DYNAMICIMPORT_PACKAGE, + SlcTypes.SLC_DYNAMIC_IMPORTED_PACKAGE); + node.setProperty(SlcNames.SLC_NAME, tokens[0]); + for (int i = 1; i < tokens.length; i++) { + if (tokens[i].startsWith(Constants.VERSION_ATTRIBUTE)) { + node.setProperty(SlcNames.SLC_VERSION, + attributeValue(tokens[i])); + } + } + } + } + + // exported packages + cleanSubNodes(fileNode, SlcNames.SLC_ + Constants.EXPORT_PACKAGE); + if (attrs.containsKey(new Name(Constants.EXPORT_PACKAGE))) { + String exportPackages = attrs.getValue(Constants.EXPORT_PACKAGE); + List packages = parsePackages(exportPackages); + for (String pkg : packages) { + String[] tokens = pkg.split(";"); + Node node = fileNode.addNode(SlcNames.SLC_ + + Constants.EXPORT_PACKAGE, + SlcTypes.SLC_EXPORTED_PACKAGE); + node.setProperty(SlcNames.SLC_NAME, tokens[0]); + // TODO: are these cleans really necessary? + cleanSubNodes(node, SlcNames.SLC_USES); + cleanSubNodes(node, SlcNames.SLC_VERSION); + for (int i = 1; i < tokens.length; i++) { + if (tokens[i].startsWith(Constants.VERSION_ATTRIBUTE)) { + String versionStr = attributeValue(tokens[i]); + Node versionNode = node.addNode(SlcNames.SLC_VERSION, + SlcTypes.SLC_OSGI_VERSION); + mapOsgiVersion(new Version(versionStr), versionNode); + } else if (tokens[i].startsWith(Constants.USES_DIRECTIVE)) { + String usedPackages = directiveValue(tokens[i]); + // log.debug("uses='" + usedPackages + "'"); + for (String usedPackage : usedPackages.split(",")) { + // log.debug("usedPackage='" + + // usedPackage + + // "'"); + Node usesNode = node.addNode(SlcNames.SLC_USES, + SlcTypes.SLC_JAVA_PACKAGE); + usesNode.setProperty(SlcNames.SLC_NAME, usedPackage); + } + } + } + } + } + + // required bundle + cleanSubNodes(fileNode, SlcNames.SLC_ + Constants.REQUIRE_BUNDLE); + if (attrs.containsKey(new Name(Constants.REQUIRE_BUNDLE))) { + String requireBundle = attrs.getValue(Constants.REQUIRE_BUNDLE); + String[] bundles = requireBundle.split(","); + for (String bundle : bundles) { + String[] tokens = bundle.split(";"); + Node node = fileNode.addNode(SlcNames.SLC_ + + Constants.REQUIRE_BUNDLE, + SlcTypes.SLC_REQUIRED_BUNDLE); + node.setProperty(SlcNames.SLC_SYMBOLIC_NAME, tokens[0]); + for (int i = 1; i < tokens.length; i++) { + if (tokens[i] + .startsWith(Constants.BUNDLE_VERSION_ATTRIBUTE)) { + node.setProperty(SlcNames.SLC_BUNDLE_VERSION, + attributeValue(tokens[i])); + } else if (tokens[i] + .startsWith(Constants.RESOLUTION_DIRECTIVE)) { + node.setProperty( + SlcNames.SLC_OPTIONAL, + directiveValue(tokens[i]).equals( + Constants.RESOLUTION_OPTIONAL)); + } + } + } + } + + } + + private void addAttr(String key, Node node, Attributes attrs) + throws RepositoryException { + addAttr(new Name(key), node, attrs); + } + + private void addAttr(Name key, Node node, Attributes attrs) + throws RepositoryException { + if (attrs.containsKey(key)) { + String value = attrs.getValue(key); + node.setProperty(SlcNames.SLC_ + key, value); + } + } + + private void cleanSubNodes(Node node, String name) + throws RepositoryException { + if (node.hasNode(name)) { + NodeIterator nit = node.getNodes(name); + while (nit.hasNext()) + nit.nextNode().remove(); + } + } + + private String attributeValue(String str) { + return extractValue(str, "="); + } + + private String directiveValue(String str) { + return extractValue(str, ":="); + } + + private String extractValue(String str, String eq) { + String[] tokens = str.split(eq); + // String key = tokens[0]; + String value = tokens[1].trim(); + // TODO: optimize? + if (value.startsWith("\"")) + value = value.substring(1); + if (value.endsWith("\"")) + value = value.substring(0, value.length() - 1); + return value; + } + + /** Parse package list with nested directive with ',' */ + private List parsePackages(String str) { + List res = new ArrayList(); + StringBuffer curr = new StringBuffer(""); + boolean in = false; + for (char c : str.toCharArray()) { + if (c == ',') { + if (!in) { + res.add(curr.toString()); + curr = new StringBuffer(""); + } + } else if (c == '\"') { + in = !in; + curr.append(c); + } else { + curr.append(c); + } + } + res.add(curr.toString()); + // log.debug(res); + return res; + } + + protected void mapOsgiVersion(Version version, Node versionNode) + throws RepositoryException { + versionNode.setProperty(SlcNames.SLC_AS_STRING, version.toString()); + versionNode.setProperty(SlcNames.SLC_MAJOR, version.getMajor()); + versionNode.setProperty(SlcNames.SLC_MINOR, version.getMinor()); + versionNode.setProperty(SlcNames.SLC_MICRO, version.getMicro()); + if (!version.getQualifier().equals("")) + versionNode.setProperty(SlcNames.SLC_QUALIFIER, + version.getQualifier()); + } + +} diff --git a/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/NodeIndexer.java b/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/NodeIndexer.java new file mode 100644 index 000000000..203dd2e48 --- /dev/null +++ b/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/NodeIndexer.java @@ -0,0 +1,29 @@ +package org.argeo.slc.repo; + +import javax.jcr.Node; +import javax.jcr.observation.EventListener; + +/** + * Adds metadata to an existing node, ideally via observation after it has been + * added. THere is a similar concept in ModeShape with which this abstraction + * may be merged in the future. + */ +public interface NodeIndexer { + /** + * Whether the node at this path will be supported. This is typically use in + * an {@link EventListener} before the node is loaded, and would apply on + * information contained in the path / file name: file extension, base path, + * etc. If the node needs to be loaded, the recommended approach is to + * return true here and wait for index to be called, possibly + * returning without processing if the node should node be indexed. While + * not stricly a requirements, this avoids to open sessions in the indexer, + * centrlaizing such tasks in the caller. + */ + public Boolean support(String path); + + /** + * Adds the metadata. This is the responsibility of the caller to save the + * udnerlying session. + */ + public void index(Node node); +} diff --git a/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/RepoImpl.java b/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/RepoImpl.java new file mode 100644 index 000000000..9951b33ba --- /dev/null +++ b/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/RepoImpl.java @@ -0,0 +1,88 @@ +package org.argeo.slc.repo; + +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.observation.Event; +import javax.jcr.observation.EventIterator; +import javax.jcr.observation.EventListener; + +import org.argeo.jcr.JcrUtils; +import org.argeo.slc.SlcException; + +/** Repository backend, maintain the JCR repository, mainly through listeners */ +public class RepoImpl { + + private Repository jcrRepository; + private Session adminSession; + + private ArtifactListener artifactListener; + + /** order may be important */ + private List nodeIndexers = new ArrayList(); + + public void init() { + try { + adminSession = jcrRepository.login(); + artifactListener = new ArtifactListener(); + adminSession + .getWorkspace() + .getObservationManager() + .addEventListener(artifactListener, Event.NODE_ADDED, + RepoConstants.ARTIFACTS_BASE_PATH, true, null, + null, true); + } catch (RepositoryException e) { + throw new SlcException("Cannot initialize repository backend", e); + } + } + + public void destroy() { + JcrUtils.logoutQuietly(adminSession); + } + + public void setJcrRepository(Repository jcrRepository) { + this.jcrRepository = jcrRepository; + } + + public void setNodeIndexers(List nodeIndexers) { + this.nodeIndexers = nodeIndexers; + } + + class ArtifactListener implements EventListener { + + public void onEvent(EventIterator events) { + while (events.hasNext()) { + Event event = events.nextEvent(); + try { + String newNodePath = event.getPath(); + Node newNode = null; + for (NodeIndexer nodeIndexer : nodeIndexers) { + try { + if (nodeIndexer.support(newNodePath)) { + if (newNode == null) + newNode = adminSession.getNode(newNodePath); + nodeIndexer.index(newNode); + } + } catch (RuntimeException e) { + e.printStackTrace(); + throw e; + } + } + if (newNode != null) + adminSession.save(); + } catch (RepositoryException e) { + throw new SlcException("Cannot process event " + event, e); + } finally { + JcrUtils.discardQuietly(adminSession); + } + } + + } + + } + +} diff --git a/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/maven/ImportMavenDependencies.java b/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/maven/ImportMavenDependencies.java index ed06e5023..962e429ea 100644 --- a/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/maven/ImportMavenDependencies.java +++ b/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/maven/ImportMavenDependencies.java @@ -36,6 +36,7 @@ import org.argeo.slc.SlcException; import org.argeo.slc.aether.AetherTemplate; import org.argeo.slc.jcr.SlcNames; import org.argeo.slc.jcr.SlcTypes; +import org.argeo.slc.repo.ArtifactIndexer; import org.argeo.slc.repo.RepoConstants; import org.osgi.framework.Constants; import org.osgi.framework.Version; @@ -59,6 +60,8 @@ public class ImportMavenDependencies implements Runnable { private String distributionsBasePath = "/slc/repo/distributions"; private String distributionName; + private ArtifactIndexer artifactIndexer = new ArtifactIndexer(); + public void run() { log.debug(jcrSession.getUserID()); Set artifacts = resolveDistribution(); @@ -154,7 +157,9 @@ public class ImportMavenDependencies implements Runnable { } else { fileNode = parentNode.getNode(file.getName()); } - processArtifact(fileNode, artifact); + + if (artifactIndexer.support(fileNode.getPath())) + artifactIndexer.index(fileNode); if (fileNode.isNodeType(SlcTypes.SLC_JAR_FILE)) { processOsgiBundle(fileNode); } @@ -208,25 +213,6 @@ public class ImportMavenDependencies implements Runnable { } } - protected void processArtifact(Node fileNode, Artifact artifact) { - try { - fileNode.addMixin(SlcTypes.SLC_ARTIFACT); - fileNode.setProperty(SlcNames.SLC_ARTIFACT_ID, - artifact.getArtifactId()); - fileNode.setProperty(SlcNames.SLC_GROUP_ID, artifact.getGroupId()); - fileNode.setProperty(SlcNames.SLC_ARTIFACT_VERSION, - artifact.getVersion()); - fileNode.setProperty(SlcNames.SLC_ARTIFACT_EXTENSION, - artifact.getExtension()); - fileNode.setProperty(SlcNames.SLC_ARTIFACT_CLASSIFIER, - artifact.getClassifier()); - } catch (RepositoryException e) { - throw new SlcException("Cannot process artifact " + artifact - + " on node " + fileNode, e); - } - - } - protected File artifactToFile(Artifact artifact) { return new File(System.getProperty("user.home") + File.separator diff --git a/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/maven/proxy/MavenProxyService.java b/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/maven/proxy/MavenProxyService.java index a1b7ffbe9..927e4e620 100644 --- a/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/maven/proxy/MavenProxyService.java +++ b/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/maven/proxy/MavenProxyService.java @@ -1,8 +1,7 @@ package org.argeo.slc.repo.maven.proxy; -/** Synchronizes JCR and Maven repositories */ -public interface MavenProxyService { - public String getNodePath(String path); +import org.argeo.jcr.proxy.ResourceProxy; - public String proxyFile(String path); +/** Marker interface (useful for OSGi servcies references), maybe extended later */ +public interface MavenProxyService extends ResourceProxy { } diff --git a/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/maven/proxy/MavenProxyServiceImpl.java b/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/maven/proxy/MavenProxyServiceImpl.java index 9e2cfd00a..19df339a2 100644 --- a/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/maven/proxy/MavenProxyServiceImpl.java +++ b/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/maven/proxy/MavenProxyServiceImpl.java @@ -1,97 +1,56 @@ package org.argeo.slc.repo.maven.proxy; -import java.io.InputStream; -import java.net.URL; import java.util.ArrayList; import java.util.List; -import javax.jcr.Binary; import javax.jcr.Node; import javax.jcr.NodeIterator; -import javax.jcr.Property; -import javax.jcr.Repository; import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.nodetype.NodeType; -import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.argeo.jcr.ArgeoNames; import org.argeo.jcr.JcrUtils; +import org.argeo.jcr.proxy.AbstractUrlProxy; import org.argeo.slc.SlcException; import org.argeo.slc.jcr.SlcNames; import org.argeo.slc.repo.RepoConstants; import org.sonatype.aether.repository.RemoteRepository; /** Synchronizes the node repository with remote Maven repositories */ -public class MavenProxyServiceImpl implements MavenProxyService, ArgeoNames, - SlcNames { +public class MavenProxyServiceImpl extends AbstractUrlProxy implements + MavenProxyService, ArgeoNames, SlcNames { private final static Log log = LogFactory .getLog(MavenProxyServiceImpl.class); - private Repository jcrRepository; - private Session jcrAdminSession; private List defaultRepositories = new ArrayList(); - public void init() { - try { - jcrAdminSession = jcrRepository.login(); - - JcrUtils.mkdirs(jcrAdminSession, RepoConstants.ARTIFACTS_BASE_PATH); - Node proxiedRepositories = JcrUtils.mkdirs(jcrAdminSession, - RepoConstants.PROXIED_REPOSITORIES); - for (RemoteRepository repository : defaultRepositories) { - if (!proxiedRepositories.hasNode(repository.getId())) { - Node proxiedRepository = proxiedRepositories - .addNode(repository.getId()); - proxiedRepository.setProperty(SLC_URL, repository.getUrl()); - proxiedRepository.setProperty(SLC_TYPE, - repository.getContentType()); - } + @Override + protected void beforeInitSessionSave() throws RepositoryException { + JcrUtils.mkdirs(getJcrAdminSession(), RepoConstants.ARTIFACTS_BASE_PATH); + Node proxiedRepositories = JcrUtils.mkdirs(getJcrAdminSession(), + RepoConstants.PROXIED_REPOSITORIES); + for (RemoteRepository repository : defaultRepositories) { + if (!proxiedRepositories.hasNode(repository.getId())) { + Node proxiedRepository = proxiedRepositories.addNode(repository + .getId()); + proxiedRepository.setProperty(SLC_URL, repository.getUrl()); + proxiedRepository.setProperty(SLC_TYPE, + repository.getContentType()); } - jcrAdminSession.save(); - } catch (RepositoryException e) { - JcrUtils.discardQuietly(jcrAdminSession); - throw new SlcException("Cannot initialize Maven proxy", e); } } - public void destroy() { - JcrUtils.logoutQuietly(jcrAdminSession); - } - /** * Retrieve and add this file to the repository */ - public synchronized String proxyFile(String path) { + protected String retrieve(String path) { try { Node node = null; - proxiedRepositories: for (NodeIterator nit = jcrAdminSession - .getNode(RepoConstants.PROXIED_REPOSITORIES).getNodes(); nit - .hasNext();) { - Node proxiedRepository = nit.nextNode(); - String repoUrl = proxiedRepository.getProperty(SLC_URL) - .getString(); - String remoteUrl = repoUrl + path; - if (log.isTraceEnabled()) - log.trace("remoteUrl=" + remoteUrl); - InputStream in = null; - try { - URL u = new URL(remoteUrl); - in = u.openStream(); - node = importFile(getNodePath(path), in); - if (log.isDebugEnabled()) - log.debug("Imported " + remoteUrl + " to " + node); - break proxiedRepositories; - } catch (Exception e) { - if (log.isDebugEnabled()) - log.debug("Cannot read " + remoteUrl + ", skipping... " - + e.getMessage()); - // e.printStackTrace(); - } finally { - IOUtils.closeQuietly(in); - } + baseUrls: for (String baseUrl : getBaseUrls()) { + node = proxyUrl(baseUrl, path); + if (node != null) + break baseUrls; } if (node != null) @@ -105,31 +64,21 @@ public class MavenProxyServiceImpl implements MavenProxyService, ArgeoNames, } } - /** The JCR path where this file could be found */ - public String getNodePath(String path) { - return RepoConstants.ARTIFACTS_BASE_PATH + path; - } - - protected Node importFile(String nodePath, InputStream in) { - Binary binary = null; - try { - Node node = JcrUtils.mkdirs(jcrAdminSession, nodePath, - NodeType.NT_FILE, NodeType.NT_FOLDER, false); - Node content = node.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE); - binary = jcrAdminSession.getValueFactory().createBinary(in); - content.setProperty(Property.JCR_DATA, binary); - jcrAdminSession.save(); - return node; - } catch (RepositoryException e) { - JcrUtils.discardQuietly(jcrAdminSession); - throw new SlcException("Cannot initialize Maven proxy", e); - } finally { - JcrUtils.closeQuietly(binary); + protected synchronized List getBaseUrls() + throws RepositoryException { + List baseUrls = new ArrayList(); + for (NodeIterator nit = getJcrAdminSession().getNode( + RepoConstants.PROXIED_REPOSITORIES).getNodes(); nit.hasNext();) { + Node proxiedRepository = nit.nextNode(); + String repoUrl = proxiedRepository.getProperty(SLC_URL).getString(); + baseUrls.add(repoUrl); } + return baseUrls; } - public void setJcrRepository(Repository jcrRepository) { - this.jcrRepository = jcrRepository; + /** The JCR path where this file could be found */ + public String getNodePath(String path) { + return RepoConstants.ARTIFACTS_BASE_PATH + path; } public void setDefaultRepositories( diff --git a/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/maven/proxy/MavenProxyServlet.java b/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/maven/proxy/MavenProxyServlet.java deleted file mode 100644 index acb0fb6c6..000000000 --- a/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/maven/proxy/MavenProxyServlet.java +++ /dev/null @@ -1,123 +0,0 @@ -package org.argeo.slc.repo.maven.proxy; - -import java.io.IOException; -import java.io.InputStream; - -import javax.jcr.Binary; -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.io.IOUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.jcr.ArgeoNames; -import org.argeo.jcr.JcrUtils; -import org.argeo.slc.SlcException; -import org.argeo.slc.jcr.SlcNames; - -/** - * Expose the SLC repository as a regular Maven repository, proxying third party - * repositories as well. - */ -public class MavenProxyServlet extends HttpServlet implements ArgeoNames, - SlcNames { - private static final long serialVersionUID = 5296857859305486588L; - - private final static Log log = LogFactory.getLog(MavenProxyServlet.class); - - private MavenProxyService proxyService; - - private Session jcrSession; - private String contentTypeCharset = "UTF-8"; - - @Override - protected void doGet(HttpServletRequest request, - HttpServletResponse response) throws ServletException, IOException { - String path = request.getPathInfo(); - - String nodePath = proxyService.getNodePath(path); - if (log.isTraceEnabled()) - log.trace("path=" + path + ", nodePath=" + nodePath); - - try { - Node node; - if (!jcrSession.itemExists(nodePath)) { - String nodeIdentifier = proxyService.proxyFile(path); - if (nodeIdentifier == null) { - //log.warn("Could not proxy " + path); - response.sendError(404); - return; - } else { - node = jcrSession.getNodeByIdentifier(nodeIdentifier); - } - } else { - node = jcrSession.getNode(nodePath); - } - processResponse(nodePath, node, response); - } catch (RepositoryException e) { - throw new SlcException("Cannot proxy " + request, e); - } - //super.doGet(request, response); - } - - /** Retrieve the content of the node. */ - protected void processResponse(String path, Node node, - HttpServletResponse response) { - Binary binary = null; - InputStream in = null; - try { - String fileName = node.getName(); - String ext = FilenameUtils.getExtension(fileName); - - // TODO use a more generic / standard approach - // see http://svn.apache.org/viewvc/tomcat/trunk/conf/web.xml - String contentType; - if ("xml".equals(ext)) - contentType = "text/xml;charset=" + contentTypeCharset; - else if ("jar".equals(ext)) - contentType = "application/java-archive"; - else if ("zip".equals(ext)) - contentType = "application/zip"; - else if ("gz".equals(ext)) - contentType = "application/x-gzip"; - else if ("tar".equals(ext)) - contentType = "application/x-tar"; - else - contentType = "application/octet-stream"; - contentType = contentType + ";name=\"" + fileName + "\""; - response.setHeader("Content-Disposition", "attachment; filename=\"" - + fileName + "\""); - response.setHeader("Expires", "0"); - response.setHeader("Cache-Control", "no-cache, must-revalidate"); - response.setHeader("Pragma", "no-cache"); - - response.setContentType(contentType); - - binary = node.getNode(Property.JCR_CONTENT) - .getProperty(Property.JCR_DATA).getBinary(); - in = binary.getStream(); - IOUtils.copy(in, response.getOutputStream()); - } catch (Exception e) { - throw new SlcException("Cannot download " + node, e); - } finally { - IOUtils.closeQuietly(in); - JcrUtils.closeQuietly(binary); - } - } - - public void setJcrSession(Session jcrSession) { - this.jcrSession = jcrSession; - } - - public void setProxyService(MavenProxyService proxyService) { - this.proxyService = proxyService; - } - -} diff --git a/runtime/org.argeo.slc.repo/src/main/resources/org/argeo/slc/repo/repo.cnd b/runtime/org.argeo.slc.repo/src/main/resources/org/argeo/slc/repo/repo.cnd index ca2cf1cbe..bc6c116ab 100644 --- a/runtime/org.argeo.slc.repo/src/main/resources/org/argeo/slc/repo/repo.cnd +++ b/runtime/org.argeo.slc.repo/src/main/resources/org/argeo/slc/repo/repo.cnd @@ -9,6 +9,21 @@ mixin - slc:artifactExtension (STRING) m - slc:artifactClassifier (STRING) ='' m a +[slc:artifactVersionBase] > nt:base +mixin +- slc:artifactId (STRING) primary m +- slc:groupId (STRING) m +- slc:artifactVersion (STRING) m + +[slc:artifactBase] > nt:base +mixin +- slc:artifactId (STRING) primary m +- slc:groupId (STRING) m + +[slc:groupBase] > nt:base +mixin +- slc:groupId (STRING) primary m + [slc:jarFile] > nt:base mixin - 'slc:manifest' (BINARY) m diff --git a/runtime/org.argeo.slc.support.aether/.classpath b/runtime/org.argeo.slc.support.aether/.classpath index 92f19d2ff..430cfacc0 100644 --- a/runtime/org.argeo.slc.support.aether/.classpath +++ b/runtime/org.argeo.slc.support.aether/.classpath @@ -3,5 +3,6 @@ + diff --git a/runtime/org.argeo.slc.support.aether/build.properties b/runtime/org.argeo.slc.support.aether/build.properties index 5fc538bc8..a77cc87b6 100644 --- a/runtime/org.argeo.slc.support.aether/build.properties +++ b/runtime/org.argeo.slc.support.aether/build.properties @@ -1,4 +1,11 @@ -source.. = src/main/java/ +source.. = src/main/java/,\ + src/test/java/ output.. = target/classes/ bin.includes = META-INF/,\ . +additional.bundles = com.springsource.junit,\ + com.springsource.slf4j.api,\ + com.springsource.slf4j.log4j,\ + com.springsource.org.apache.log4j,\ + com.springsource.slf4j.org.apache.commons.logging + \ No newline at end of file diff --git a/runtime/org.argeo.slc.support.aether/src/main/java/org/argeo/slc/aether/AetherUtils.java b/runtime/org.argeo.slc.support.aether/src/main/java/org/argeo/slc/aether/AetherUtils.java index c088afe1f..0749e8ab2 100644 --- a/runtime/org.argeo.slc.support.aether/src/main/java/org/argeo/slc/aether/AetherUtils.java +++ b/runtime/org.argeo.slc.support.aether/src/main/java/org/argeo/slc/aether/AetherUtils.java @@ -1,12 +1,22 @@ package org.argeo.slc.aether; +import java.util.regex.Pattern; + +import org.apache.commons.io.FilenameUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.slc.SlcException; import org.sonatype.aether.artifact.Artifact; import org.sonatype.aether.graph.DependencyNode; +import org.sonatype.aether.util.artifact.DefaultArtifact; /** Utilities related to Aether */ public class AetherUtils { + public final static String SNAPSHOT = "SNAPSHOT"; + // hacked from aether + public static final Pattern SNAPSHOT_TIMESTAMP = Pattern + .compile("^(.*-)?([0-9]{8}.[0-9]{6}-[0-9]+)$"); + private final static Log log = LogFactory.getLog(AetherUtils.class); /** Logs a dependency node and its transitive dependencies as a tree. */ @@ -29,4 +39,124 @@ public class AetherUtils { } } + /** + * Converts a path (relative to a repository root) to an {@link Artifact}. + * + * @param path + * the relative path + * @param type + * the layout type, currently ignored because only the 'default' + * Maven 2 layout is currently supported: + * /my/group/id/artifactId/ + * version/artifactId-version[-classifier].extension + * @return the related artifact or null if the file is not an artifact + * (Maven medata data XML files, check sums, etc.) + */ + public static Artifact convertPathToArtifact(String path, String type) { + // TODO rewrite it with regexp (unit tests first!) + + // normalize + if (path.startsWith("/")) + path = path.substring(1); + + // parse group id + String[] tokensSlash = path.split("/"); + if (tokensSlash.length < 4) + return null; + StringBuffer groupId = new StringBuffer(path.length()); + for (int i = 0; i < tokensSlash.length - 3; i++) { + if (i != 0) + groupId.append('.'); + groupId.append(tokensSlash[i]); + } + String artifactId = tokensSlash[tokensSlash.length - 3]; + String baseVersion = tokensSlash[tokensSlash.length - 2]; + String fileName = tokensSlash[tokensSlash.length - 1]; + + if (!fileName.startsWith(artifactId)) + return null; + // FIXME make it configurable? (via an argument?) + if (FilenameUtils.isExtension(fileName, new String[] { "sha1", "md5" })) + return null; + + String extension = FilenameUtils.getExtension(fileName); + String baseName = FilenameUtils.getBaseName(fileName); + + // check since we assume hereafter + if (!baseName.startsWith(artifactId)) + throw new SlcException("Base name '" + baseName + + " does not start with artifact id '" + artifactId + + "' in " + path); + + boolean isSnapshot = baseVersion.endsWith("-" + SNAPSHOT); + String baseBaseVersion = isSnapshot ? baseVersion.substring(0, + baseVersion.length() - SNAPSHOT.length() - 1) : baseVersion; + int artifactAndBaseBaseVersionLength = artifactId.length() + 1 + + baseBaseVersion.length() + 1; + String classifier = null; + if (baseName.length() > artifactAndBaseBaseVersionLength) { + String dashRest = baseName + .substring(artifactAndBaseBaseVersionLength); + String[] dashes = dashRest.split("-"); + + if (isSnapshot) { + if (dashes[0].equals(SNAPSHOT)) { + if (dashRest.length() > SNAPSHOT.length() + 1) + classifier = dashRest.substring(SNAPSHOT.length() + 1); + + } else { + if (dashes.length > 2)// assume no '-' in classifier + classifier = dashes[2]; + } + } else { + if (dashes.length > 0) + classifier = dashes[0]; + } + } + + // classifier + // String classifier = null; + // int firstDash = baseName.indexOf('-'); + // int classifierDash = baseName.lastIndexOf('-'); + // if (classifierDash > 0 && classifierDash != firstDash) { + // classifier = baseName.substring(classifierDash + 1); + // } + // if (isSnapshot && classifier != null) { + // if (classifier.equals(SNAPSHOT)) + // classifier = null; + // else + // try { + // Long.parseLong(classifier); // build number + // // if not failed this is a timestamped version + // classifier = null; + // } catch (NumberFormatException e) { + // // silent + // } + // } + + // version + String version = baseName.substring(artifactId.length() + 1); + if (classifier != null) + version = version.substring(0, + version.length() - classifier.length() - 1); + + // consistency checks + if (!isSnapshot && !version.equals(baseVersion)) + throw new SlcException("Base version '" + baseVersion + + "' and version '" + version + "' not in line in " + path); + if (!isSnapshot && isSnapshotVersion(version)) + throw new SlcException("SNAPSHOT base version '" + baseVersion + + "' and version '" + version + "' not in line in " + path); + + DefaultArtifact artifact = new DefaultArtifact(groupId.toString(), + artifactId, classifier, extension, version); + return artifact; + } + + /** Hacked from aether */ + public static boolean isSnapshotVersion(String version) { + return version.endsWith(SNAPSHOT) + || SNAPSHOT_TIMESTAMP.matcher(version).matches(); + } + } diff --git a/runtime/org.argeo.slc.support.aether/src/test/java/org/argeo/slc/aether/AetherUtilsTest.java b/runtime/org.argeo.slc.support.aether/src/test/java/org/argeo/slc/aether/AetherUtilsTest.java new file mode 100644 index 000000000..56b954cae --- /dev/null +++ b/runtime/org.argeo.slc.support.aether/src/test/java/org/argeo/slc/aether/AetherUtilsTest.java @@ -0,0 +1,52 @@ +package org.argeo.slc.aether; + +import org.sonatype.aether.artifact.Artifact; +import org.sonatype.aether.util.artifact.DefaultArtifact; + +import junit.framework.TestCase; + +public class AetherUtilsTest extends TestCase { + public void testConvertPathToArtifact() throws Exception { + checkPathConversion("my.group.id:my-artifactId:pom:1.2.3", + "/my/group/id/my-artifactId/1.2.3/my-artifactId-1.2.3.pom"); + checkPathConversion("my.group.id:my-artifactId:pom:1.2.3-SNAPSHOT", + "/my/group/id/my-artifactId/1.2.3-SNAPSHOT/my-artifactId-1.2.3-SNAPSHOT.pom"); + checkPathConversion("my.group.id:my-artifactId:pom:myClassifier:1.2.3", + "/my/group/id/my-artifactId/1.2.3/my-artifactId-1.2.3-myClassifier.pom"); + checkPathConversion( + "my.group.id:my-artifactId:pom:myClassifier:1.2.3-SNAPSHOT", + "/my/group/id/my-artifactId/1.2.3-SNAPSHOT/my-artifactId-1.2.3-SNAPSHOT-myClassifier.pom"); + checkPathConversion( + "my.group.id:my-artifactId:pom:myClassifier:20110828.223836-2", + "/my/group/id/my-artifactId/1.2.3-SNAPSHOT/my-artifactId-20110828.223836-2-myClassifier.pom"); + } + + public void testConvertPathToArtifactRealLife() throws Exception { + checkPathConversion( + "org.apache.maven.plugins:maven-antrun-plugin:pom:1.1", + "org/apache/maven/plugins/maven-antrun-plugin/1.1/maven-antrun-plugin-1.1.pom"); + checkPathConversion( + "org.apache.maven.plugins:maven-plugin-parent:pom:2.0.1", + "org/apache/maven/plugins/maven-plugin-parent/2.0.1/maven-plugin-parent-2.0.1.pom"); + checkPathConversion( + "org.apache.avalon.framework:avalon-framework-impl:pom:4.3.1", + "org/apache/avalon/framework/avalon-framework-impl/4.3.1/avalon-framework-impl-4.3.1.pom"); + checkPathConversion( + "org.apache.maven.shared:maven-dependency-tree:pom:1.2", + "org/apache/maven/shared/maven-dependency-tree/1.2/maven-dependency-tree-1.2.pom"); + checkPathConversion( + "org.argeo.maven.plugins:maven-argeo-osgi-plugin:pom:1.0.33", + "org/argeo/maven/plugins/maven-argeo-osgi-plugin/1.0.33/maven-argeo-osgi-plugin-1.0.33.pom"); + checkPathConversion( + "org.apache.maven.plugins:maven-clean-plugin:pom:2.4.1", + "org/apache/maven/plugins/maven-clean-plugin/2.4.1/maven-clean-plugin-2.4.1.pom"); + } + + protected void checkPathConversion(String expectedArtifact, String path) { + Artifact artifact = AetherUtils.convertPathToArtifact(path, null); + if (expectedArtifact == null) + assertNull(artifact); + else + assertEquals(new DefaultArtifact(expectedArtifact), artifact); + } +} diff --git a/runtime/org.argeo.slc.support.maven/META-INF/spring/maven-osgi.xml b/runtime/org.argeo.slc.support.maven/META-INF/spring/maven-osgi.xml index b5bf15764..ffc3117c9 100644 --- a/runtime/org.argeo.slc.support.maven/META-INF/spring/maven-osgi.xml +++ b/runtime/org.argeo.slc.support.maven/META-INF/spring/maven-osgi.xml @@ -6,6 +6,7 @@ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> + -- 2.39.2