]> git.argeo.org Git - gpl/argeo-slc.git/blob - org.argeo.slc.support/src/org/argeo/slc/jsch/SshFilesDeployment.java
Merge branch 'master' of https://github.com/argeo/argeo-slc.git
[gpl/argeo-slc.git] / org.argeo.slc.support / src / org / argeo / slc / jsch / SshFilesDeployment.java
1 /*
2 * Copyright (C) 2007-2012 Argeo GmbH
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.argeo.slc.jsch;
17
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.StringTokenizer;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.argeo.slc.SlcException;
27 import org.argeo.slc.core.deploy.DigestCheck;
28 import org.argeo.slc.core.deploy.ResourceSet;
29 import org.springframework.core.io.Resource;
30
31 import com.jcraft.jsch.Session;
32
33 public class SshFilesDeployment extends AbstractJschTask implements Runnable {
34 private final static Log log = LogFactory.getLog(SshFilesDeployment.class);
35 private String targetBase = "";
36 private ResourceSet resourceSet;
37 /**
38 * Activate with algorithm as per
39 * http://java.sun.com/j2se/1.5.0/docs/guide/security/CryptoSpec.html#AppA
40 */
41 private String checksum = "MD5";
42 private int remoteChecksumsPerCall = 20;
43
44 public SshFilesDeployment() {
45 }
46
47 public SshFilesDeployment(SshTarget sshTarget, ResourceSet resourceSet) {
48 setSshTarget(sshTarget);
49 this.resourceSet = resourceSet;
50 }
51
52 @Override
53 void run(Session session) {
54 JschMultiTasks multiTasks = new JschMultiTasks();
55
56 Map<String, Resource> resources = resourceSet.listResources();
57
58 // Analyze set
59 List<String> subDirs = new ArrayList<String>();
60 Map<String, String> targetPaths = new HashMap<String, String>();
61 for (String relPath : resources.keySet()) {
62 String parentDir;
63 int lastIndexSubDir = relPath.lastIndexOf('/');
64 if (lastIndexSubDir > 0)
65 parentDir = targetBase + '/'
66 + relPath.substring(0, lastIndexSubDir);
67 else
68 parentDir = targetBase;
69
70 boolean skipDir = false;
71 registerDirs: for (String registeredDir : new ArrayList<String>(
72 subDirs)) {
73 if (parentDir.equals(registeredDir)) {
74 if (log.isTraceEnabled())
75 log.trace("Already registered, skip " + parentDir);
76 skipDir = true;
77 break registerDirs;
78 }
79
80 if (parentDir.startsWith(registeredDir))
81 if (subDirs.contains(registeredDir)) {
82 subDirs.remove(registeredDir);
83 if (log.isTraceEnabled())
84 log.trace("Remove parent " + registeredDir + " of "
85 + parentDir);
86 continue registerDirs;
87 }
88
89 if (registeredDir.startsWith(parentDir)) {
90 skipDir = true;
91 if (log.isTraceEnabled())
92 log.trace("Skip " + parentDir
93 + " because child already registered.");
94 break registerDirs;
95 }
96 }
97
98 if (!subDirs.contains(parentDir) && !skipDir) {
99 subDirs.add(parentDir);
100 }
101
102 targetPaths.put(relPath, targetBase + "/" + relPath);
103 }
104
105 // checksum
106 List<String> targetPathsEqualsToLocal = new ArrayList<String>();
107 if (checksum != null) {
108 Map<String, String> remoteChecksums = new HashMap<String, String>();
109 List<String> csLines = new ArrayList<String>();
110 String csExecutable;
111 if ("MD5".equals(checksum))
112 csExecutable = "/usr/bin/md5sum";
113 else if ("SHA".equals(checksum))
114 csExecutable = "/usr/bin/sha1sum";
115 else if ("SHA-256".equals(checksum))
116 csExecutable = "/usr/bin/sha256sum";
117 else if ("SHA-512".equals(checksum))
118 csExecutable = "/usr/bin/sha512sum";
119 else
120 throw new SlcException(
121 "Don't know how to remotely execute checksum "
122 + checksum);
123
124 StringBuffer csCmd = new StringBuffer(csExecutable);
125 int numberOfPaths = targetPaths.size();
126 int count = 0;
127 for (String targetPath : targetPaths.values()) {
128 csCmd.append(" ").append(targetPath);
129 count++;
130
131 if ((count % remoteChecksumsPerCall == 0)
132 || count == numberOfPaths) {
133 RemoteExec remoteCs = new RemoteExec();
134 remoteCs.setSshTarget(getSshTarget());
135 remoteCs.setCommand(csCmd.toString());
136 remoteCs.setStdOutLines(csLines);
137 remoteCs.setFailOnBadExitStatus(false);
138 remoteCs.run(session);
139 csCmd = new StringBuffer(csExecutable);
140 }
141
142 }
143
144 remoteChecksums: for (String csLine : csLines) {
145 StringTokenizer st = new StringTokenizer(csLine, ": ");
146 String cs = st.nextToken();
147 if (cs.equals(csExecutable)) {
148 // remote does not exist
149 continue remoteChecksums;
150 } else {
151 String targetPath = st.nextToken();
152 if (log.isTraceEnabled())
153 log.trace("REMOTE: " + targetPath + "=" + cs);
154 remoteChecksums.put(targetPath, cs);
155 }
156 }
157
158 // Local checksums
159 for (String relPath : resources.keySet()) {
160 Resource resource = resources.get(relPath);
161 String targetPath = targetPaths.get(relPath);
162 if (remoteChecksums.containsKey(targetPath)) {
163 String cs = DigestCheck.digest(checksum, resource);
164 if (log.isTraceEnabled())
165 log.trace("LOCAL : " + targetPath + "=" + cs);
166 if (remoteChecksums.get(targetPath).equals(cs))
167 targetPathsEqualsToLocal.add(targetPath);
168 }
169 }
170 }
171
172 // Prepare multitask
173
174 // Create dirs
175 StringBuffer mkdirCmd = new StringBuffer("mkdir -p");
176 RemoteExec remoteExec = new RemoteExec();
177 for (String dir : subDirs) {
178 // remoteExec.getCommands().add("mkdir -p " + dir);
179 mkdirCmd.append(' ');
180 if (dir.indexOf(' ') >= 0)
181 mkdirCmd.append('\"').append(dir).append('\"');
182 else
183 mkdirCmd.append(dir);
184 }
185 remoteExec.setCommand(mkdirCmd.toString());
186 multiTasks.getTasks().add(remoteExec);
187
188 // Perform copies
189 int copied = 0;
190 int skipped = 0;
191 copy: for (String relPath : resources.keySet()) {
192 String targetPath = targetPaths.get(relPath);
193 if (targetPathsEqualsToLocal.contains(targetPath)) {
194 if (log.isTraceEnabled())
195 log.trace("Skip copy of " + relPath
196 + " since it is equal to remote " + targetPath);
197 skipped++;
198 continue copy;
199 }
200 // Copy resource
201 Resource resource = resources.get(relPath);
202 ScpTo scpTo = new ScpTo();
203 scpTo.setLocalResource(resource);
204 scpTo.setRemotePath(targetPath);
205 multiTasks.getTasks().add(scpTo);
206 copied++;
207 // TODO: set permissions
208 }
209
210 multiTasks.setSshTarget(getSshTarget());
211 multiTasks.run(session);
212
213 if (checksum != null && log.isDebugEnabled())
214 log.debug("Copied " + copied + " files, skipped " + skipped
215 + " with same checksum.");
216 }
217
218 public void setTargetBase(String targetBase) {
219 this.targetBase = targetBase;
220 }
221
222 public void setResourceSet(ResourceSet resourceSet) {
223 this.resourceSet = resourceSet;
224 }
225
226 public void setChecksum(String checksum) {
227 this.checksum = checksum;
228 }
229
230 /** Number of remote checksums per remote call */
231 public void setRemoteChecksumsPerCall(int remoteChecksumsPerCall) {
232 this.remoteChecksumsPerCall = remoteChecksumsPerCall;
233 }
234 }