]> git.argeo.org Git - gpl/argeo-slc.git/blobdiff - runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/jsch/SshFilesDeployment.java
Update license header
[gpl/argeo-slc.git] / runtime / org.argeo.slc.support.simple / src / main / java / org / argeo / slc / jsch / SshFilesDeployment.java
index 97ba6c740d0db3479775393133aa0e6f581bc26b..7deacba1c31ad2e08f08f25fd9a6222cdaf69c1a 100644 (file)
+/*
+ * Copyright (C) 2010 Mathieu Baudier <mbaudier@argeo.org>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package org.argeo.slc.jsch;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.StringTokenizer;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.slc.SlcException;
+import org.argeo.slc.core.deploy.DigestCheck;
 import org.argeo.slc.core.deploy.ResourceSet;
 import org.springframework.core.io.Resource;
 
 import com.jcraft.jsch.Session;
 
 public class SshFilesDeployment extends AbstractJschTask implements Runnable {
+       private final static Log log = LogFactory.getLog(SshFilesDeployment.class);
        private String targetBase = "";
        private ResourceSet resourceSet;
+       /**
+        * Activate with algorithm as per
+        * http://java.sun.com/j2se/1.5.0/docs/guide/security/CryptoSpec.html#AppA
+        */
+       private String checksum = "MD5";
+       private int remoteChecksumsPerCall = 20;
+
+       public SshFilesDeployment() {
+       }
+
+       public SshFilesDeployment(SshTarget sshTarget, ResourceSet resourceSet) {
+               setSshTarget(sshTarget);
+               this.resourceSet = resourceSet;
+       }
 
        @Override
        void run(Session session) {
                JschMultiTasks multiTasks = new JschMultiTasks();
 
-               List<String> subDirs = new ArrayList<String>();
                Map<String, Resource> resources = resourceSet.listResources();
+
+               // Analyze set
+               List<String> subDirs = new ArrayList<String>();
+               Map<String, String> targetPaths = new HashMap<String, String>();
                for (String relPath : resources.keySet()) {
-                       // Create dir if necessary
-                       String dir;
+                       String parentDir;
                        int lastIndexSubDir = relPath.lastIndexOf('/');
                        if (lastIndexSubDir > 0)
-                               dir = targetBase + '/' + relPath.substring(0, lastIndexSubDir);
+                               parentDir = targetBase + '/'
+                                               + relPath.substring(0, lastIndexSubDir);
+                       else
+                               parentDir = targetBase;
+
+                       boolean skipDir = false;
+                       registerDirs: for (String registeredDir : new ArrayList<String>(
+                                       subDirs)) {
+                               if (parentDir.equals(registeredDir)) {
+                                       if (log.isTraceEnabled())
+                                               log.trace("Already registered, skip " + parentDir);
+                                       skipDir = true;
+                                       break registerDirs;
+                               }
+
+                               if (parentDir.startsWith(registeredDir))
+                                       if (subDirs.contains(registeredDir)) {
+                                               subDirs.remove(registeredDir);
+                                               if (log.isTraceEnabled())
+                                                       log.trace("Remove parent " + registeredDir + " of "
+                                                                       + parentDir);
+                                               continue registerDirs;
+                                       }
+
+                               if (registeredDir.startsWith(parentDir)) {
+                                       skipDir = true;
+                                       if (log.isTraceEnabled())
+                                               log.trace("Skip " + parentDir
+                                                               + " because child already registered.");
+                                       break registerDirs;
+                               }
+                       }
+
+                       if (!subDirs.contains(parentDir) && !skipDir) {
+                               subDirs.add(parentDir);
+                       }
+
+                       targetPaths.put(relPath, targetBase + "/" + relPath);
+               }
+
+               // checksum
+               List<String> targetPathsEqualsToLocal = new ArrayList<String>();
+               if (checksum != null) {
+                       Map<String, String> remoteChecksums = new HashMap<String, String>();
+                       List<String> csLines = new ArrayList<String>();
+                       String csExecutable;
+                       if ("MD5".equals(checksum))
+                               csExecutable = "/usr/bin/md5sum";
+                       else if ("SHA".equals(checksum))
+                               csExecutable = "/usr/bin/sha1sum";
+                       else if ("SHA-256".equals(checksum))
+                               csExecutable = "/usr/bin/sha256sum";
+                       else if ("SHA-512".equals(checksum))
+                               csExecutable = "/usr/bin/sha512sum";
                        else
-                               dir = targetBase;
+                               throw new SlcException(
+                                               "Don't know how to remotely execute checksum "
+                                                               + checksum);
+
+                       StringBuffer csCmd = new StringBuffer(csExecutable);
+                       int numberOfPaths = targetPaths.size();
+                       int count = 0;
+                       for (String targetPath : targetPaths.values()) {
+                               csCmd.append(" ").append(targetPath);
+                               count++;
+
+                               if ((count % remoteChecksumsPerCall == 0)
+                                               || count == numberOfPaths) {
+                                       RemoteExec remoteCs = new RemoteExec();
+                                       remoteCs.setSshTarget(getSshTarget());
+                                       remoteCs.setCommand(csCmd.toString());
+                                       remoteCs.setStdOutLines(csLines);
+                                       remoteCs.setFailOnBadExitStatus(false);
+                                       remoteCs.run(session);
+                                       csCmd = new StringBuffer(csExecutable);
+                               }
 
-                       if (!subDirs.contains(dir)) {
-                               subDirs.add(dir);
                        }
 
+                       remoteChecksums: for (String csLine : csLines) {
+                               StringTokenizer st = new StringTokenizer(csLine, ": ");
+                               String cs = st.nextToken();
+                               if (cs.equals(csExecutable)) {
+                                       // remote does not exist
+                                       continue remoteChecksums;
+                               } else {
+                                       String targetPath = st.nextToken();
+                                       if (log.isTraceEnabled())
+                                               log.trace("REMOTE: " + targetPath + "=" + cs);
+                                       remoteChecksums.put(targetPath, cs);
+                               }
+                       }
+
+                       // Local checksums
+                       for (String relPath : resources.keySet()) {
+                               Resource resource = resources.get(relPath);
+                               String targetPath = targetPaths.get(relPath);
+                               if (remoteChecksums.containsKey(targetPath)) {
+                                       String cs = DigestCheck.digest(checksum, resource);
+                                       if (log.isTraceEnabled())
+                                               log.trace("LOCAL : " + targetPath + "=" + cs);
+                                       if (remoteChecksums.get(targetPath).equals(cs))
+                                               targetPathsEqualsToLocal.add(targetPath);
+                               }
+                       }
+               }
+
+               // Prepare multitask
+
+               // Create dirs
+               StringBuffer mkdirCmd = new StringBuffer("mkdir -p");
+               RemoteExec remoteExec = new RemoteExec();
+               for (String dir : subDirs) {
+                       // remoteExec.getCommands().add("mkdir -p " + dir);
+                       mkdirCmd.append(' ');
+                       if (dir.indexOf(' ') >= 0)
+                               mkdirCmd.append('\"').append(dir).append('\"');
+                       else
+                               mkdirCmd.append(dir);
+               }
+               remoteExec.setCommand(mkdirCmd.toString());
+               multiTasks.getTasks().add(remoteExec);
+
+               // Perform copies
+               int copied = 0;
+               int skipped = 0;
+               copy: for (String relPath : resources.keySet()) {
+                       String targetPath = targetPaths.get(relPath);
+                       if (targetPathsEqualsToLocal.contains(targetPath)) {
+                               if (log.isTraceEnabled())
+                                       log.trace("Skip copy of " + relPath
+                                                       + " since it is equal to remote " + targetPath);
+                               skipped++;
+                               continue copy;
+                       }
                        // Copy resource
                        Resource resource = resources.get(relPath);
                        ScpTo scpTo = new ScpTo();
                        scpTo.setLocalResource(resource);
-                       scpTo.setRemotePath(targetBase + "/" + relPath);
+                       scpTo.setRemotePath(targetPath);
                        multiTasks.getTasks().add(scpTo);
-
+                       copied++;
                        // TODO: set permissions
                }
 
-               RemoteExec remoteExec = new RemoteExec();
-               for (String dir : subDirs) {
-                       remoteExec.getCommands().add("mkdir -p " + dir);
-               }
-               multiTasks.getTasks().add(0, remoteExec);
-
                multiTasks.setSshTarget(getSshTarget());
                multiTasks.run(session);
+
+               if (checksum != null && log.isDebugEnabled())
+                       log.debug("Copied " + copied + " files, skipped " + skipped
+                                       + " with same checksum.");
        }
 
        public void setTargetBase(String targetBase) {
@@ -59,4 +223,13 @@ public class SshFilesDeployment extends AbstractJschTask implements Runnable {
        public void setResourceSet(ResourceSet resourceSet) {
                this.resourceSet = resourceSet;
        }
+
+       public void setChecksum(String checksum) {
+               this.checksum = checksum;
+       }
+
+       /** Number of remote checksums per remote call */
+       public void setRemoteChecksumsPerCall(int remoteChecksumsPerCall) {
+               this.remoteChecksumsPerCall = remoteChecksumsPerCall;
+       }
 }