Improve Repo
authorMathieu Baudier <mbaudier@argeo.org>
Fri, 26 Oct 2012 16:16:24 +0000 (16:16 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Fri, 26 Oct 2012 16:16:24 +0000 (16:16 +0000)
git-svn-id: https://svn.argeo.org/slc/trunk@5660 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

lib/org.argeo.slc.lib.repo/META-INF/spring/main.xml [new file with mode: 0644]
lib/org.argeo.slc.lib.repo/META-INF/spring/osgi.xml [new file with mode: 0644]
lib/org.argeo.slc.lib.repo/pom.xml
lib/pom.xml
plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/DistUiUtils.java [deleted file]
plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/commands/CopyWorkspace.java
plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/views/DistributionsView.java
pom.xml
runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/RepoSync.java [new file with mode: 0644]
runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/RepoUtils.java

diff --git a/lib/org.argeo.slc.lib.repo/META-INF/spring/main.xml b/lib/org.argeo.slc.lib.repo/META-INF/spring/main.xml
new file mode 100644 (file)
index 0000000..25920c9
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<beans xmlns:flow="http://www.argeo.org/schema/slc-flow" xmlns="http://www.springframework.org/schema/beans"\r
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"\r
+       xsi:schemaLocation="\r
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\r
+       http://www.argeo.org/schema/slc-flow http://www.argeo.org/schema/slc-flow-1.2.xsd">\r
+\r
+       <!-- Hello world -->\r
+       <flow:flow name="sync">\r
+               <flow:spec>\r
+                       <flow:primitive name="sourceRepo"\r
+                               value="https://repo.argeo.org/org.argeo.jcr.webapp/remoting/java" />\r
+                       <flow:primitive name="sourceWksp" />\r
+                       <flow:primitive name="sourceUsername" value="${user.name}" />\r
+                       <flow:primitive name="sourcePassword" type="password"/>\r
+                       <flow:primitive name="targetRepo" value="vm:///java/" />\r
+               </flow:spec>\r
+               <bean class="org.argeo.slc.repo.RepoSync">\r
+                       <flow:variable proxy-target-class="false" />\r
+                       <property name="sourceRepo" value="@{sourceRepo}" />\r
+                       <property name="sourceWksp" value="@{sourceWksp}" />\r
+                       <property name="sourceUsername" value="@{sourceUsername}" />\r
+                       <property name="sourcePassword" value="@{sourcePassword}" />\r
+                       <property name="targetRepo" value="@{targetRepo}" />\r
+                       <property name="repositoryFactory" ref="repositoryFactory" />\r
+               </bean>\r
+       </flow:flow>\r
+\r
+</beans>
\ No newline at end of file
diff --git a/lib/org.argeo.slc.lib.repo/META-INF/spring/osgi.xml b/lib/org.argeo.slc.lib.repo/META-INF/spring/osgi.xml
new file mode 100644 (file)
index 0000000..1b2b5bb
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<beans:beans xmlns="http://www.springframework.org/schema/osgi"\r
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"\r
+       xsi:schemaLocation="http://www.springframework.org/schema/osgi  \r
+       http://www.springframework.org/schema/osgi/spring-osgi-1.1.xsd\r
+       http://www.springframework.org/schema/beans   \r
+       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">\r
+\r
+       <!-- IMPORTS -->\r
+       <beans:import resource="classpath:org/argeo/slc/core/execution/spring.xml" />\r
+       <beans:import resource="classpath:org/argeo/slc/osgi/execution.xml" />\r
+\r
+       <!-- REFERENCES -->\r
+       <reference id="repositoryFactory" interface="javax.jcr.RepositoryFactory" />\r
+</beans:beans>
\ No newline at end of file
index 9cd454c615fd2172ade27931e1e3fc19b973b7f4..523794bd453edfad91dbd4d3472a972277d657f1 100644 (file)
@@ -9,4 +9,5 @@
        </parent>
        <artifactId>org.argeo.slc.lib.repo</artifactId>
        <name>SLC Lib - Repository Utilities</name>
+       <description>Utilities to transfer, convert, manage software repositories</description>
 </project>
index b53cc94477a2e9ece5c7ae4b47db3f2856cdbed8..ae4cf2e8880e8712cd6c59c351a80fde3c8f4477 100644 (file)
@@ -11,6 +11,9 @@
        <packaging>pom</packaging>
        <name>SLC Standard Libs</name>
        <description>SLC execution modules for generic tasks, to be used as parent pom</description>
+       <modules>
+               <module>org.argeo.slc.lib.repo</module>
+       </modules>
        <properties>
                <additionalImports.slc-lib>
                        com.jcraft.jsch;resolution:=optional,
diff --git a/plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/DistUiUtils.java b/plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/DistUiUtils.java
deleted file mode 100644 (file)
index f3b18b7..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-package org.argeo.slc.client.ui.dist;
-
-import javax.jcr.Credentials;
-import javax.jcr.GuestCredentials;
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.Property;
-import javax.jcr.PropertyIterator;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.RepositoryFactory;
-import javax.jcr.SimpleCredentials;
-import javax.jcr.nodetype.NodeType;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.ArgeoException;
-import org.argeo.jcr.ArgeoJcrUtils;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.jcr.ArgeoTypes;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.slc.SlcException;
-import org.argeo.slc.jcr.SlcNames;
-import org.argeo.slc.jcr.SlcTypes;
-import org.argeo.util.security.Keyring;
-
-/** Static utilities */
-public class DistUiUtils implements ArgeoNames, SlcNames {
-       private final static Log log = LogFactory.getLog(DistUiUtils.class);
-
-       /** Retrieve repository based on information in the repo node */
-       public static Repository getRepository(RepositoryFactory repositoryFactory,
-                       Keyring keyring, Node repoNode) {
-               try {
-                       Repository repository;
-                       if (repoNode.isNodeType(ArgeoTypes.ARGEO_REMOTE_REPOSITORY)) {
-                               String uri = repoNode.getProperty(ARGEO_URI).getString();
-                               if (uri.startsWith("http")) {// http, https
-                                       repository = ArgeoJcrUtils.getRepositoryByUri(
-                                                       repositoryFactory, uri);
-                               } else {// alias
-                                       String alias = uri;
-                                       repository = ArgeoJcrUtils.getRepositoryByAlias(
-                                                       repositoryFactory, alias);
-                               }
-                               return repository;
-                       } else {
-                               throw new SlcException("Unsupported node type " + repoNode);
-                       }
-               } catch (RepositoryException e) {
-                       throw new SlcException("Cannot connect to repository " + repoNode,
-                                       e);
-               }
-
-       }
-
-       /**
-        * Reads credentials from node, using keyring if there is a password. Cann
-        * return null if no credentials needed (local repo) at all, but returns
-        * {@link GuestCredentials} if user id is 'anonymous' .
-        */
-       public static Credentials getRepositoryCredentials(Keyring keyring,
-                       Node repoNode) {
-               try {
-                       if (repoNode.isNodeType(ArgeoTypes.ARGEO_REMOTE_REPOSITORY)) {
-                               if (!repoNode.hasProperty(ARGEO_USER_ID))
-                                       return null;
-
-                               String userId = repoNode.getProperty(ARGEO_USER_ID).getString();
-                               if (userId.equals("anonymous"))// FIXME hardcoded userId
-                                       return new GuestCredentials();
-                               char[] password = keyring.getAsChars(repoNode.getPath() + '/'
-                                               + ARGEO_PASSWORD);
-                               Credentials credentials = new SimpleCredentials(userId,
-                                               password);
-                               return credentials;
-                       } else {
-                               throw new SlcException("Unsupported node type " + repoNode);
-                       }
-               } catch (RepositoryException e) {
-                       throw new SlcException("Cannot connect to repository " + repoNode,
-                                       e);
-               }
-       }
-
-       /**
-        * Custom copy since the one in commons does not fit the needs when copying
-        * a workspace completely.
-        */
-       public static void copy(Node fromNode, Node toNode) {
-               try {
-                       if (log.isDebugEnabled())
-                               log.debug("copy node :" + fromNode.getPath());
-
-                       // FIXME : small hack to enable specific workspace copy
-                       if (fromNode.isNodeType("rep:ACL")
-                                       || fromNode.isNodeType("rep:system")) {
-                               if (log.isTraceEnabled())
-                                       log.trace("node " + fromNode + " skipped");
-                               return;
-                       }
-
-                       // add mixins
-                       for (NodeType mixinType : fromNode.getMixinNodeTypes()) {
-                               toNode.addMixin(mixinType.getName());
-                       }
-
-                       // Double check
-                       for (NodeType mixinType : toNode.getMixinNodeTypes()) {
-                               if (log.isDebugEnabled())
-                                       log.debug(mixinType.getName());
-                       }
-
-                       // process properties
-                       PropertyIterator pit = fromNode.getProperties();
-                       properties: while (pit.hasNext()) {
-                               Property fromProperty = pit.nextProperty();
-                               String propName = fromProperty.getName();
-                               try {
-                                       String propertyName = fromProperty.getName();
-                                       if (toNode.hasProperty(propertyName)
-                                                       && toNode.getProperty(propertyName).getDefinition()
-                                                                       .isProtected())
-                                               continue properties;
-
-                                       if (fromProperty.getDefinition().isProtected())
-                                               continue properties;
-
-                                       if (propertyName.equals("jcr:created")
-                                                       || propertyName.equals("jcr:createdBy")
-                                                       || propertyName.equals("jcr:lastModified")
-                                                       || propertyName.equals("jcr:lastModifiedBy"))
-                                               continue properties;
-
-                                       if (fromProperty.isMultiple()) {
-                                               toNode.setProperty(propertyName,
-                                                               fromProperty.getValues());
-                                       } else {
-                                               toNode.setProperty(propertyName,
-                                                               fromProperty.getValue());
-                                       }
-                               } catch (RepositoryException e) {
-                                       throw new ArgeoException("Cannot property " + propName, e);
-                               }
-                       }
-
-                       // recursively process children nodes
-                       NodeIterator nit = fromNode.getNodes();
-                       while (nit.hasNext()) {
-                               Node fromChild = nit.nextNode();
-                               Integer index = fromChild.getIndex();
-                               String nodeRelPath = fromChild.getName() + "[" + index + "]";
-                               Node toChild;
-                               if (toNode.hasNode(nodeRelPath))
-                                       toChild = toNode.getNode(nodeRelPath);
-                               else
-                                       toChild = toNode.addNode(fromChild.getName(), fromChild
-                                                       .getPrimaryNodeType().getName());
-                               copy(fromChild, toChild);
-                       }
-
-                       // update jcr:lastModified and jcr:lastModifiedBy in toNode in
-                       // case
-                       // they existed
-                       if (!toNode.getDefinition().isProtected()
-                                       && toNode.isNodeType(NodeType.MIX_LAST_MODIFIED))
-                               JcrUtils.updateLastModified(toNode);
-
-                       // Workaround to reduce session size: artifact is a saveable
-                       // unity
-                       if (toNode.isNodeType(SlcTypes.SLC_ARTIFACT))
-                               toNode.getSession().save();
-
-               } catch (RepositoryException e) {
-                       throw new ArgeoException("Cannot copy " + fromNode + " to "
-                                       + toNode, e);
-               }
-       }
-
-       private DistUiUtils() {
-
-       }
-}
index b80646cb878b73dc41c91faae343ef349d4c29bf..8f55f5f9f2f6bedaaa9c6b59d1dc3f299ac32e90 100644 (file)
@@ -9,8 +9,8 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.argeo.ArgeoException;
 import org.argeo.slc.client.ui.dist.DistPlugin;
-import org.argeo.slc.client.ui.dist.DistUiUtils;
 import org.argeo.slc.client.ui.dist.utils.CommandHelpers;
+import org.argeo.slc.repo.RepoUtils;
 import org.eclipse.core.commands.AbstractHandler;
 import org.eclipse.core.commands.ExecutionEvent;
 import org.eclipse.core.commands.ExecutionException;
@@ -62,7 +62,7 @@ public class CopyWorkspace extends AbstractHandler {
                        newSession = repository.login(newWorkspaceName);
                        // newSession.save();
                        Node newRootNode = newSession.getRootNode();
-                       DistUiUtils.copy(srcRootNode, newRootNode);
+                       RepoUtils.copy(srcRootNode, newRootNode);
                        newSession.save();
 
                        CommandHelpers.callCommand(RefreshDistributionsView.ID);
index 29a1ca9c57b988b20c448e24405d7cdeee1d1969..07d23946bbd2828e0a012e49ee672dcc8ebd148c 100644 (file)
@@ -44,7 +44,6 @@ import org.argeo.jcr.JcrUtils;
 import org.argeo.jcr.UserJcrUtils;
 import org.argeo.slc.SlcException;
 import org.argeo.slc.client.ui.dist.DistPlugin;
-import org.argeo.slc.client.ui.dist.DistUiUtils;
 import org.argeo.slc.client.ui.dist.commands.CopyWorkspace;
 import org.argeo.slc.client.ui.dist.commands.CreateWorkspace;
 import org.argeo.slc.client.ui.dist.commands.DeleteWorkspace;
@@ -55,6 +54,7 @@ import org.argeo.slc.client.ui.dist.editors.DistributionEditorInput;
 import org.argeo.slc.client.ui.dist.utils.CommandHelpers;
 import org.argeo.slc.jcr.SlcNames;
 import org.argeo.slc.repo.RepoConstants;
+import org.argeo.slc.repo.RepoUtils;
 import org.argeo.util.security.Keyring;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
@@ -109,7 +109,6 @@ public class DistributionsView extends ViewPart implements SlcNames, ArgeoNames
 
                TreeViewerColumn col = new TreeViewerColumn(viewer, SWT.NONE);
                col.getColumn().setWidth(200);
-               // col.getColumn().setText("Workspace");
                col.setLabelProvider(new ColumnLabelProvider() {
                        @Override
                        public String getText(Object element) {
@@ -160,9 +159,10 @@ public class DistributionsView extends ViewPart implements SlcNames, ArgeoNames
                                if (!repos.hasNode(alias)) {
                                        Node repoNode = repos.addNode(alias,
                                                        ArgeoTypes.ARGEO_REMOTE_REPOSITORY);
-                                       repoNode.setProperty(ARGEO_URI, alias);
+                                       repoNode.setProperty(ARGEO_URI, "vm:///" + alias);
                                        repoNode.addMixin(NodeType.MIX_TITLE);
-                                       repoNode.setProperty(Property.JCR_TITLE, "vm://" + alias);
+                                       repoNode.setProperty(Property.JCR_TITLE, "Internal "
+                                                       + alias + " repository");
                                        nodeSession.save();
                                }
                        }
@@ -345,10 +345,9 @@ public class DistributionsView extends ViewPart implements SlcNames, ArgeoNames
                protected void connect() {
                        if (repository != null)
                                return;
-                       repository = DistUiUtils.getRepository(repositoryFactory, keyring,
-                                       repoNode);
-                       credentials = DistUiUtils.getRepositoryCredentials(keyring,
+                       repository = RepoUtils.getRepository(repositoryFactory, keyring,
                                        repoNode);
+                       credentials = RepoUtils.getRepositoryCredentials(keyring, repoNode);
                }
 
                public String getLabel() {
@@ -509,17 +508,17 @@ public class DistributionsView extends ViewPart implements SlcNames, ArgeoNames
 
                        try {
                                String sourceWorkspace = sourceDist.getWorkspaceName();
-                               Repository sourceRepository = DistUiUtils.getRepository(
+                               Repository sourceRepository = RepoUtils.getRepository(
                                                repositoryFactory, keyring, sourceDist
                                                                .getWorkspaceNode().getParent());
-                               Credentials sourceCredentials = DistUiUtils
+                               Credentials sourceCredentials = RepoUtils
                                                .getRepositoryCredentials(keyring, sourceDist
                                                                .getWorkspaceNode().getParent());
 
                                String targetWorkspace = sourceWorkspace;
-                               Repository targetRepository = DistUiUtils.getRepository(
+                               Repository targetRepository = RepoUtils.getRepository(
                                                repositoryFactory, keyring, targetRepo.getRepoNode());
-                               Credentials targetCredentials = DistUiUtils
+                               Credentials targetCredentials = RepoUtils
                                                .getRepositoryCredentials(keyring,
                                                                targetRepo.getRepoNode());
 
diff --git a/pom.xml b/pom.xml
index 89ee2d48fd0e4eab4b98c07834ef338c8e24c68e..ea7b3b82df1228c0d227273daafd71bda2c318a9 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -20,9 +20,9 @@
        <modules>
                <module>runtime</module>
                <module>modules</module>
-               <module>lib</module>
                <module>plugins</module>
                <module>dep</module>
+               <module>lib</module>
                <module>archetypes</module>
                <module>dist</module>
                <module>demo</module>
diff --git a/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/RepoSync.java b/runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/RepoSync.java
new file mode 100644 (file)
index 0000000..cde8bc5
--- /dev/null
@@ -0,0 +1,87 @@
+package org.argeo.slc.repo;
+
+import javax.jcr.Credentials;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.RepositoryFactory;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.jcr.ArgeoJcrUtils;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.slc.SlcException;
+
+/** Sync to from software repositories */
+public class RepoSync implements Runnable {
+       private final static Log log = LogFactory.getLog(RepoSync.class);
+
+       private String sourceRepo;
+       private String targetRepo;
+
+       private String sourceWksp;
+
+       private String sourceUsername;
+       private char[] sourcePassword;
+
+       private RepositoryFactory repositoryFactory;
+
+       public void run() {
+               try {
+                       long begin = System.currentTimeMillis();
+
+                       Repository sourceRepository = ArgeoJcrUtils.getRepositoryByUri(
+                                       repositoryFactory, sourceRepo);
+                       Repository targetRepository = ArgeoJcrUtils.getRepositoryByUri(
+                                       repositoryFactory, targetRepo);
+                       Credentials sourceCredentials = null;
+                       if (sourceUsername != null)
+                               sourceCredentials = new SimpleCredentials(sourceUsername,
+                                               sourcePassword);
+                       Session sourceSession = sourceRepository.login(sourceCredentials,
+                                       sourceWksp);
+
+                       Credentials targetCredentials = null;
+                       Session targetSession = targetRepository.login(targetCredentials,
+                                       sourceWksp);
+
+                       Long count = JcrUtils.copyFiles(sourceSession.getRootNode(),
+                                       targetSession.getRootNode(), true, null);
+
+                       long duration = (System.currentTimeMillis() - begin) / 1000;// in
+                       // s
+                       if (log.isDebugEnabled())
+                               log.debug("Copied " + count + " files in " + (duration / 60)
+                                               + "min " + (duration % 60) + "s");
+               } catch (RepositoryException e) {
+                       throw new SlcException("Cannot sync " + sourceRepo + " to "
+                                       + targetRepo, e);
+               }
+       }
+
+       public void setSourceRepo(String sourceRepo) {
+               this.sourceRepo = sourceRepo;
+       }
+
+       public void setTargetRepo(String targetRepo) {
+               this.targetRepo = targetRepo;
+       }
+
+       public void setSourceWksp(String sourceWksp) {
+               this.sourceWksp = sourceWksp;
+       }
+
+       public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
+               this.repositoryFactory = repositoryFactory;
+       }
+
+       public void setSourceUsername(String sourceUsername) {
+               this.sourceUsername = sourceUsername;
+       }
+
+       public void setSourcePassword(char[] sourcePassword) {
+               this.sourcePassword = sourcePassword;
+       }
+
+}
index 6d22cbe687632ebcff13c7f4df87412ab4e653fd..c962daf2e29be57a23c0fb7a974c83518e9d7cde 100644 (file)
@@ -16,13 +16,25 @@ import java.util.jar.JarInputStream;
 import java.util.jar.JarOutputStream;
 import java.util.jar.Manifest;
 
+import javax.jcr.Credentials;
+import javax.jcr.GuestCredentials;
 import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
+import javax.jcr.RepositoryFactory;
+import javax.jcr.SimpleCredentials;
+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.ArgeoJcrUtils;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
 import org.argeo.jcr.JcrUtils;
 import org.argeo.slc.BasicNameVersion;
 import org.argeo.slc.NameVersion;
@@ -30,12 +42,13 @@ import org.argeo.slc.SlcException;
 import org.argeo.slc.jcr.SlcNames;
 import org.argeo.slc.jcr.SlcTypes;
 import org.argeo.slc.repo.maven.MavenConventionsUtils;
+import org.argeo.util.security.Keyring;
 import org.osgi.framework.Constants;
 import org.sonatype.aether.artifact.Artifact;
 import org.sonatype.aether.util.artifact.DefaultArtifact;
 
 /** Utilities around repo */
-public class RepoUtils implements SlcNames {
+public class RepoUtils implements ArgeoNames, SlcNames {
        private final static Log log = LogFactory.getLog(RepoUtils.class);
 
        /** Packages a regular sources jar as PDE source. */
@@ -295,4 +308,159 @@ public class RepoUtils implements SlcNames {
                else
                        return sourceBundleName;
        }
+
+       /*
+        * SOFTWARE REPOSITORIES
+        */
+
+       /** Retrieve repository based on information in the repo node */
+       public static Repository getRepository(RepositoryFactory repositoryFactory,
+                       Keyring keyring, Node repoNode) {
+               try {
+                       Repository repository;
+                       if (repoNode.isNodeType(ArgeoTypes.ARGEO_REMOTE_REPOSITORY)) {
+                               String uri = repoNode.getProperty(ARGEO_URI).getString();
+                               if (uri.startsWith("http")) {// http, https
+                                       repository = ArgeoJcrUtils.getRepositoryByUri(
+                                                       repositoryFactory, uri);
+                               } else if (uri.startsWith("vm:")) {// alias
+                                       repository = ArgeoJcrUtils.getRepositoryByUri(
+                                                       repositoryFactory, uri);
+                               } else {
+                                       throw new SlcException("Unsupported repository uri " + uri);
+                               }
+                               return repository;
+                       } else {
+                               throw new SlcException("Unsupported node type " + repoNode);
+                       }
+               } catch (RepositoryException e) {
+                       throw new SlcException("Cannot connect to repository " + repoNode,
+                                       e);
+               }
+
+       }
+
+       /**
+        * Reads credentials from node, using keyring if there is a password. Cann
+        * return null if no credentials needed (local repo) at all, but returns
+        * {@link GuestCredentials} if user id is 'anonymous' .
+        */
+       public static Credentials getRepositoryCredentials(Keyring keyring,
+                       Node repoNode) {
+               try {
+                       if (repoNode.isNodeType(ArgeoTypes.ARGEO_REMOTE_REPOSITORY)) {
+                               if (!repoNode.hasProperty(ARGEO_USER_ID))
+                                       return null;
+
+                               String userId = repoNode.getProperty(ARGEO_USER_ID).getString();
+                               if (userId.equals("anonymous"))// FIXME hardcoded userId
+                                       return new GuestCredentials();
+                               char[] password = keyring.getAsChars(repoNode.getPath() + '/'
+                                               + ARGEO_PASSWORD);
+                               Credentials credentials = new SimpleCredentials(userId,
+                                               password);
+                               return credentials;
+                       } else {
+                               throw new SlcException("Unsupported node type " + repoNode);
+                       }
+               } catch (RepositoryException e) {
+                       throw new SlcException("Cannot connect to repository " + repoNode,
+                                       e);
+               }
+       }
+
+       /**
+        * Custom copy since the one in commons does not fit the needs when copying
+        * a workspace completely.
+        */
+       public static void copy(Node fromNode, Node toNode) {
+               try {
+                       if (log.isDebugEnabled())
+                               log.debug("copy node :" + fromNode.getPath());
+
+                       // FIXME : small hack to enable specific workspace copy
+                       if (fromNode.isNodeType("rep:ACL")
+                                       || fromNode.isNodeType("rep:system")) {
+                               if (log.isTraceEnabled())
+                                       log.trace("node " + fromNode + " skipped");
+                               return;
+                       }
+
+                       // add mixins
+                       for (NodeType mixinType : fromNode.getMixinNodeTypes()) {
+                               toNode.addMixin(mixinType.getName());
+                       }
+
+                       // Double check
+                       for (NodeType mixinType : toNode.getMixinNodeTypes()) {
+                               if (log.isDebugEnabled())
+                                       log.debug(mixinType.getName());
+                       }
+
+                       // process properties
+                       PropertyIterator pit = fromNode.getProperties();
+                       properties: while (pit.hasNext()) {
+                               Property fromProperty = pit.nextProperty();
+                               String propName = fromProperty.getName();
+                               try {
+                                       String propertyName = fromProperty.getName();
+                                       if (toNode.hasProperty(propertyName)
+                                                       && toNode.getProperty(propertyName).getDefinition()
+                                                                       .isProtected())
+                                               continue properties;
+
+                                       if (fromProperty.getDefinition().isProtected())
+                                               continue properties;
+
+                                       if (propertyName.equals("jcr:created")
+                                                       || propertyName.equals("jcr:createdBy")
+                                                       || propertyName.equals("jcr:lastModified")
+                                                       || propertyName.equals("jcr:lastModifiedBy"))
+                                               continue properties;
+
+                                       if (fromProperty.isMultiple()) {
+                                               toNode.setProperty(propertyName,
+                                                               fromProperty.getValues());
+                                       } else {
+                                               toNode.setProperty(propertyName,
+                                                               fromProperty.getValue());
+                                       }
+                               } catch (RepositoryException e) {
+                                       throw new SlcException("Cannot property " + propName, e);
+                               }
+                       }
+
+                       // recursively process children nodes
+                       NodeIterator nit = fromNode.getNodes();
+                       while (nit.hasNext()) {
+                               Node fromChild = nit.nextNode();
+                               Integer index = fromChild.getIndex();
+                               String nodeRelPath = fromChild.getName() + "[" + index + "]";
+                               Node toChild;
+                               if (toNode.hasNode(nodeRelPath))
+                                       toChild = toNode.getNode(nodeRelPath);
+                               else
+                                       toChild = toNode.addNode(fromChild.getName(), fromChild
+                                                       .getPrimaryNodeType().getName());
+                               copy(fromChild, toChild);
+                       }
+
+                       // update jcr:lastModified and jcr:lastModifiedBy in toNode in
+                       // case
+                       // they existed
+                       if (!toNode.getDefinition().isProtected()
+                                       && toNode.isNodeType(NodeType.MIX_LAST_MODIFIED))
+                               JcrUtils.updateLastModified(toNode);
+
+                       // Workaround to reduce session size: artifact is a saveable
+                       // unity
+                       if (toNode.isNodeType(SlcTypes.SLC_ARTIFACT))
+                               toNode.getSession().save();
+
+               } catch (RepositoryException e) {
+                       throw new SlcException("Cannot copy " + fromNode + " to " + toNode,
+                                       e);
+               }
+       }
+
 }