]> git.argeo.org Git - gpl/argeo-slc.git/commitdiff
Working indexation (artifact, jar, osgi) in repo
authorMathieu Baudier <mbaudier@argeo.org>
Mon, 26 Sep 2011 20:46:13 +0000 (20:46 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Mon, 26 Sep 2011 20:46:13 +0000 (20:46 +0000)
git-svn-id: https://svn.argeo.org/slc/trunk@4755 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

23 files changed:
modules/server/org.argeo.slc.server.repo.webapp/META-INF/MANIFEST.MF
modules/server/org.argeo.slc.server.repo.webapp/WEB-INF/maven-servlet.xml
modules/server/org.argeo.slc.server.repo.webapp/WEB-INF/osgi.xml
modules/server/org.argeo.slc.server.repo.webapp/WEB-INF/security.xml
modules/server/org.argeo.slc.server.repo/META-INF/MANIFEST.MF
modules/server/org.argeo.slc.server.repo/META-INF/spring/jcr.xml
modules/server/org.argeo.slc.server.repo/META-INF/spring/maven.xml
modules/server/org.argeo.slc.server.repo/META-INF/spring/repo-osgi.xml
modules/server/org.argeo.slc.server.repo/META-INF/spring/services.xml [new file with mode: 0644]
runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/ArtifactIndexer.java [new file with mode: 0644]
runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/JarFileIndexer.java [new file with mode: 0644]
runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/NodeIndexer.java [new file with mode: 0644]
runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/RepoImpl.java [new file with mode: 0644]
runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/maven/ImportMavenDependencies.java
runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/maven/proxy/MavenProxyService.java
runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/maven/proxy/MavenProxyServiceImpl.java
runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/maven/proxy/MavenProxyServlet.java [deleted file]
runtime/org.argeo.slc.repo/src/main/resources/org/argeo/slc/repo/repo.cnd
runtime/org.argeo.slc.support.aether/.classpath
runtime/org.argeo.slc.support.aether/build.properties
runtime/org.argeo.slc.support.aether/src/main/java/org/argeo/slc/aether/AetherUtils.java
runtime/org.argeo.slc.support.aether/src/test/java/org/argeo/slc/aether/AetherUtilsTest.java [new file with mode: 0644]
runtime/org.argeo.slc.support.maven/META-INF/spring/maven-osgi.xml

index 04874c01ce8837dcaa61d97a27954c19ef0aa571..9a8c42a027d188927d726f0c4e65f30ac1cf0b65 100644 (file)
@@ -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",
index 0c0d817e32d1d8089518a03b9259126e29d26149..04d5151f04e11e2c14d4836bd3a1614951d99a7a 100644 (file)
                </property>
        </bean>
 
-       <bean id="mavenProxyService" class="org.argeo.slc.repo.maven.proxy.MavenProxyServiceImpl"
-               init-method="init" destroy-method="destroy">
-               <property name="jcrRepository" ref="jcrRepository" />
-               <property name="defaultRepositories" ref="defaultMavenRepositories" />
-       </bean>
-
-       <bean id="mavenProxyServlet" class="org.argeo.slc.repo.maven.proxy.MavenProxyServlet">
+       <bean id="mavenProxyServlet" class="org.argeo.jcr.mvc.ResourceProxyServlet">
                <property name="jcrSession" ref="jcrSession" />
-               <property name="proxyService" ref="mavenProxyService" />
+               <property name="proxy" ref="mavenProxy" />
        </bean>
 
        <bean id="jcrSession" class="org.argeo.security.jcr.SecureThreadBoundSession">
                <property name="repository" ref="jcrRepository" />
-               <property name="defaultUsername" value="root" />
-               <property name="defaultPassword" value="demo" />
        </bean>
-
-       <bean
-               class="org.argeo.security.core.AuthenticatedApplicationContextInitialization">
-               <property name="authenticationManager" ref="authenticationManager" />
-       </bean>
-
-       <!-- Remote repositories -->
-       <util:list id="defaultMavenRepositories">
-               <ref local="central" />
-               <ref local="com.springsource.repository.bundles.external" />
-               <ref local="com.springsource.repository.bundles.release" />
-               <ref local="argeo" />
-               <ref local="argeo-snapshots" />
-       </util:list>
-
-       <bean id="central" class="org.argeo.slc.aether.spring.RemoteRepositoryFactory">
-               <property name="url" value="http://repo1.maven.org/maven2" />
-       </bean>
-       <bean id="com.springsource.repository.bundles.release" class="org.argeo.slc.aether.spring.RemoteRepositoryFactory">
-               <property name="url"
-                       value="http://repository.springsource.com/maven/bundles/release" />
-       </bean>
-       <bean id="com.springsource.repository.bundles.external" class="org.argeo.slc.aether.spring.RemoteRepositoryFactory">
-               <property name="url"
-                       value="http://repository.springsource.com/maven/bundles/external" />
-       </bean>
-       <bean id="argeo" class="org.argeo.slc.aether.spring.RemoteRepositoryFactory">
-               <property name="url" value="http://maven.argeo.org/argeo/" />
-       </bean>
-       <bean id="argeo-snapshots" class="org.argeo.slc.aether.spring.RemoteRepositoryFactory">
-               <property name="url" value="http://dev.argeo.org/maven/argeo-snapshots/" />
-       </bean>
-
 </beans>
\ No newline at end of file
index 15231c8cb1697e7d2c24b999d82df7cc65c15ec4..2993316f54c83962e2cff0a0bc62c3ebbbc9f441 100644 (file)
        <!-- Security -->\r
        <reference id="authenticationManager"\r
                interface="org.springframework.security.AuthenticationManager" />\r
-       <reference id="userDetailsService"\r
-               interface="org.springframework.security.userdetails.UserDetailsService" />\r
 \r
        <!-- JCR -->\r
        <reference id="jcrRepository" interface="javax.jcr.Repository"\r
                filter="(argeo.jcr.repository.alias=slc)" />\r
+\r
+       <!-- Maven -->\r
+       <reference id="mavenProxy" interface="org.argeo.slc.repo.maven.proxy.MavenProxyService" />\r
+\r
 </beans:beans>
\ No newline at end of file
index 340c8e76d3b231f8bd510d4a4f372d8f10eed364..ddb4d2ec46f8abeacc7dcf18cf0ede31ac14d16d 100644 (file)
@@ -45,7 +45,6 @@
                <!-- URL redirected to after logout -->
                <constructor-arg>
                        <list>
-                               <ref bean="rememberMeServices" />
                                <bean
                                        class="org.springframework.security.ui.logout.SecurityContextLogoutHandler" />
                        </list>
        <bean id="securityContextHolderAwareRequestFilter"
                class="org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter" />
 
-       <!-- Use the remember me cookie to authenticate -->
-       <bean id="rememberMeProcessingFilter"
-               class="org.springframework.security.ui.rememberme.RememberMeProcessingFilter">
-               <property name="authenticationManager" ref="authenticationManager" />
-               <property name="rememberMeServices" ref="rememberMeServices" />
-       </bean>
-
-       <bean id="rememberMeServices"
-               class="org.springframework.security.ui.rememberme.TokenBasedRememberMeServices">
-               <property name="userDetailsService" ref="userDetailsService" />
-               <property name="key" value="${argeo.security.systemKey}" />
-       </bean>
-
        <!-- Basic authentication -->
        <bean id="basicProcessingFilter"
                class="org.springframework.security.ui.basicauth.BasicProcessingFilter">
                <property name="authenticationEntryPoint">
                        <ref local="basicProcessingFilterEntryPoint" />
                </property>
-               <property name="rememberMeServices" ref="rememberMeServices" />
        </bean>
 
        <!-- Activate basic auth when needed -->
        <bean id="basicProcessingFilterEntryPoint"
                class="org.springframework.security.ui.basicauth.BasicProcessingFilterEntryPoint">
                <property name="realmName">
-                       <value>Argeo</value>
+                       <value>Argeo Repository</value>
                </property>
        </bean>
 
index cb1a3da59cc22ca47599ce4581c465380c3c118b..e64aeef453c5cb70aa1e2f572ed6f3d302fdf172 100644 (file)
@@ -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"
index 299991b76653a673e9e4de8c629814efb66b6a0a..27566c82a97ef441c1112cc6416137da9b369927 100644 (file)
@@ -3,9 +3,7 @@
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">\r
 \r
-       <bean id="jcrSession" class="org.argeo.jcr.ThreadBoundJcrSessionFactory">\r
+       <bean id="jcrSession" class="org.argeo.security.jcr.SecureThreadBoundSession">\r
                <property name="repository" ref="jcrRepository" />\r
-               <property name="workspace" value="slc_repo" />\r
-               <property name="forceDefaultCredentials" value="true"/>\r
        </bean>\r
 </beans>
\ No newline at end of file
index 2cdbbab71338c3bf8571757edf3596eb4f2ab99d..7f6b1d4f1ce368daf2c700362934e98becab73a9 100644 (file)
@@ -7,7 +7,7 @@
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd
        ">
 
-
+<!-- 
        <bean class="org.argeo.slc.repo.maven.ImportMavenDependencies" init-method="run">
                <property name="rootCoordinates"
                        value="org.argeo.dep:versions-all:pom:1.1.3-SNAPSHOT" />
@@ -24,7 +24,8 @@
                        </set>
                </property>
        </bean>
-
+ -->
        <bean id="mavenAetherTemplate" class="org.argeo.slc.aether.AetherTemplate">
                <property name="repositorySystem" ref="mavenRepositorySystem" />
                <property name="repositorySystemSession" ref="mavenRepositorySystemSession" />
index bb39e61e21b2da834f9dd2fbf5a601981b01dbd4..1bf904e19b57ef936d88bda2d772e22d9f921df8 100644 (file)
@@ -8,6 +8,10 @@
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\r
        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">\r
 \r
+       <!-- REFERENCES -->\r
+       <!-- Security -->\r
+       <reference id="authenticationManager"\r
+               interface="org.springframework.security.AuthenticationManager" />\r
        <reference id="jcrRepository" interface="javax.jcr.Repository"\r
                filter="(argeo.jcr.repository.alias=slc)" />\r
 \r
@@ -16,4 +20,7 @@
                filter="(aether.repositorySystemType=maven)" />\r
        <reference id="mavenRepositorySystemSession" interface="org.sonatype.aether.RepositorySystemSession"\r
                filter="(aether.repositorySystemType=maven)" />\r
+\r
+       <!-- SERVICES -->\r
+       <service ref="mavenProxyService" interface="org.argeo.slc.repo.maven.proxy.MavenProxyService" />\r
 </beans:beans>
\ 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 (file)
index 0000000..173414f
--- /dev/null
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd
+       ">
+
+       <bean id="repo" class="org.argeo.slc.repo.RepoImpl" init-method="init" destroy-method="destroy">
+               <property name="jcrRepository" ref="jcrRepository" />
+               <property name="nodeIndexers">
+                       <list>
+                               <bean class="org.argeo.slc.repo.ArtifactIndexer" />
+                               <bean class="org.argeo.slc.repo.JarFileIndexer" />
+                       </list>
+               </property>
+       </bean>
+
+       <bean id="mavenProxyService" class="org.argeo.slc.repo.maven.proxy.MavenProxyServiceImpl"
+               init-method="init" destroy-method="destroy">
+               <property name="jcrRepository" ref="jcrRepository" />
+               <property name="defaultRepositories" ref="defaultMavenRepositories" />
+       </bean>
+
+       <bean
+               class="org.argeo.security.core.AuthenticatedApplicationContextInitialization">
+               <property name="authenticationManager" ref="authenticationManager" />
+       </bean>
+
+
+</beans>
\ 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 (file)
index 0000000..4b76779
--- /dev/null
@@ -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 (file)
index 0000000..92460b0
--- /dev/null
@@ -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<String> 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<String> 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<String> 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<String> parsePackages(String str) {
+               List<String> res = new ArrayList<String>();
+               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 (file)
index 0000000..203dd2e
--- /dev/null
@@ -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 <code>true</code> 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 (file)
index 0000000..9951b33
--- /dev/null
@@ -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<NodeIndexer> nodeIndexers = new ArrayList<NodeIndexer>();
+
+       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<NodeIndexer> 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);
+                               }
+                       }
+
+               }
+
+       }
+
+}
index ed06e502381efca41c2952edd96444edb6f95c55..962e429ea0dafc5d0a382ada39a880d666461810 100644 (file)
@@ -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<Artifact> 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
index a1b7ffbe932a6549362927d04d427e5b8b49d19d..927e4e62026bd9b0c1cc66aca125fd9aa82d1f26 100644 (file)
@@ -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 {
 }
index 9e2cfd00a26c1db243c950f609b622e081d7c5fd..19df339a27b44b96c74c65853fcfb31be45e1e34 100644 (file)
@@ -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<RemoteRepository> defaultRepositories = new ArrayList<RemoteRepository>();
 
-       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<String> getBaseUrls()
+                       throws RepositoryException {
+               List<String> baseUrls = new ArrayList<String>();
+               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 (file)
index acb0fb6..0000000
+++ /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;
-       }
-
-}
index ca2cf1cbe7c7063295284be24c06990b82f6c554..bc6c116abee757a4e75714f1c0277a231dd11a83 100644 (file)
@@ -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
index 92f19d2ff95b83a87f5210157582c70bb070ed48..430cfacc031b2b52b501652d80f6f63a244340c8 100644 (file)
@@ -3,5 +3,6 @@
        <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
        <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
        <classpathentry kind="src" path="src/main/java"/>
+       <classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
        <classpathentry kind="output" path="target/classes"/>
 </classpath>
index 5fc538bc83f35ba123c9f84f2eb6124dd65c8789..a77cc87b6a1aff75c040afb46c99d3e4791d86a1 100644 (file)
@@ -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
index c088afe1f08a234b6c250f2f3906b08d107087d3..0749e8ab2306506567553f156e1853a74ccd6328 100644 (file)
@@ -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 (file)
index 0000000..56b954c
--- /dev/null
@@ -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);
+       }
+}
index b5bf157640a3d181093c673cd738148ab215fc76..ffc3117c977a17dad6987fd442a037806ab9fad0 100644 (file)
@@ -6,6 +6,7 @@
        http://www.springframework.org/schema/beans   \r
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">\r
 \r
+       <!-- SERVICES -->\r
        <service ref="mavenRepositorySystem" interface="org.sonatype.aether.RepositorySystem">\r
                <service-properties>\r
                        <beans:entry key="aether.repositorySystemType" value="maven" />\r