X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=runtime%2Forg.argeo.slc.support.simple%2Fsrc%2Fmain%2Fjava%2Forg%2Fargeo%2Fslc%2Fjsch%2FSshFilesDeployment.java;h=7deacba1c31ad2e08f08f25fd9a6222cdaf69c1a;hb=5fcacdb600e4c9e765cb93b46132932662832c1b;hp=5809ca84463ace5c9400cd1a9a2281326c6c88be;hpb=283b0e37119d1a2af969f9043b950f15c7cc0208;p=gpl%2Fargeo-slc.git diff --git a/runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/jsch/SshFilesDeployment.java b/runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/jsch/SshFilesDeployment.java index 5809ca844..7deacba1c 100644 --- a/runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/jsch/SshFilesDeployment.java +++ b/runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/jsch/SshFilesDeployment.java @@ -1,51 +1,219 @@ +/* + * Copyright (C) 2010 Mathieu Baudier + * + * 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 subDirs = new ArrayList(); Map resources = resourceSet.listResources(); + + // Analyze set + List subDirs = new ArrayList(); + Map targetPaths = new HashMap(); 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( + 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 targetPathsEqualsToLocal = new ArrayList(); + if (checksum != null) { + Map remoteChecksums = new HashMap(); + List csLines = new ArrayList(); + 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; - if (!subDirs.contains(dir)) { - RemoteExec remoteExec = new RemoteExec(); - remoteExec.setCommand("mkdir -p " + dir); - subDirs.add(dir); - multiTasks.getTasks().add(remoteExec); + 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); + } + + } + + 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 } 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) { @@ -55,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; + } }