/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.geronimo.deployment.plugin.remote;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.geronimo.deployment.plugin.local.AbstractDeployCommand;
import org.apache.geronimo.util.encoders.Base64;
import java.io.File;
import java.io.IOException;
import java.io.DataOutputStream;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.LinkedList;
import java.util.List;
import java.util.Iterator;
/**
* Knows how to upload files to a server
*
* @version $Rev: 567215 $ $Date: 2007-08-17 22:42:59 -0400 (Fri, 17 Aug 2007) $
*/
public class RemoteDeployUtil {
private static final Log log = LogFactory.getLog(RemoteDeployUtil.class);
/** Note: The below versions should be kept in sync with those in FileUploadServlet.java **/
// Starting RemoteDeploy datastream versions
public static final int REMOTE_DEPLOY_REQUEST_VER_0 = 0;
public static final int REMOTE_DEPLOY_RESPONSE_VER_0 = 0;
// Current RemoteDeploy datastream versions
public static final int REMOTE_DEPLOY_REQUEST_VER = 0;
public static final int REMOTE_DEPLOY_RESPONSE_VER = 0;
public static void uploadFilesToServer(File[] files, AbstractDeployCommand progress) {
if(files == null) {
return;
}
List valid = new LinkedList();
for(int i=0; i<files.length; i++) {
if(files[i] == null) {
continue;
}
File file = files[i];
if(!file.exists() || !file.canRead()) {
continue;
}
valid.add(new Integer(i));
}
if(valid.size() > 0) {
progress.updateStatus("Uploading "+valid.size()+" file(s) to server");
if (log.isDebugEnabled()) {
log.debug("Uploading "+valid.size()+" file(s) to server");
}
try {
/* --------------------
* RemoteDeploy Request
* --------------------
*
* Note: The below code has to match FileUploadServlet.java
*
* RemoteDeployer data stream format:
* 0) an int, the version of this datastream format - REMOTE_DEPLOY_REQUEST_VER
* 1) an int, the number of files being uploaded
* 2) for each file:
* 2.0) a UTF String, the filename of the file being uploaded
* 2.1) a long, the length of the file in bytes
* 2.2) byte[], byte count equal to the number above for the file
*/
URL url = progress.getRemoteDeployUploadURL();
URLConnection con = connectToServer(url, progress.getCommandContext().getUsername(), progress.getCommandContext().getPassword());
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(con.getOutputStream()));
// 0) an int, the version of this datastream format - REMOTE_DEPLOY_REQUEST_VER
out.writeInt(REMOTE_DEPLOY_REQUEST_VER);
// 1) an int, the number of files being uploaded
out.writeInt(valid.size());
byte[] buf = new byte[1024];
int size;
long total, length, threshold, next;
// 2) for each file:
for (Iterator it = valid.iterator(); it.hasNext();) {
Integer index = (Integer) it.next();
File file = files[index.intValue()];
// 2.0) a UTF String, the filename of the file being uploaded
out.writeUTF(file.getName().trim());
// 2.1) a long, the length of the file in bytes
out.writeLong(length = file.length());
threshold = Math.max(length / 100, (long)10240);
next = threshold;
BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
if (log.isDebugEnabled()) {
log.debug("Uploading "+file.getName());
}
total = 0;
// 2.2) raw bytes, equal to the number above for the file
while((size = in.read(buf)) > -1) {
out.write(buf, 0, size);
total += size;
if(total > next) {
progress.updateStatus("Uploading "+file.getName()+": "+(total/1024)+" KB");
while(total > next) next += threshold;
}
}
in.close();
}
out.flush();
out.close();
/* ---------------------
* RemoteDeploy Response
* ---------------------
*
* Note: The below code has to match FileUploadServlet.java
*
* RemoteDeployer response stream format:
* It returns a serialized stream containing:
* 0) an int, the version of this datastream format - REMOTE_DEPLOY_RESPONSE_VER
* 1) a UTF string, the status (should be "OK")
* 2) an int, the number of files received
* 3) for each file:
* 3.1) a UTF String, the path to the file as saved to the server's filesystem
* x) new data would be added here
*
* The file positions in the response will be the same as in the request.
* That is, a name for upload file #2 will be in response position #2.
*/
DataInputStream in = new DataInputStream(new BufferedInputStream(con.getInputStream()));
// 0) an int, the version of this datastream format - REMOTE_DEPLOY_RESPONSE_VER
int rspVer = in.readInt();
// whenever we update the stream version, the next line needs to
// be changed to just - (rspVer >= REMOTE_DEPLOY_RESPONSE_VER_0)
// but until then, be more restrictive so we can handle old servers
// that don't send a version as the first thing, but UTF instead...
if ((rspVer >= REMOTE_DEPLOY_RESPONSE_VER_0) && (rspVer <= REMOTE_DEPLOY_RESPONSE_VER)) {
// 1) a UTF string, the status (should be "OK")
String status = in.readUTF();
if(!status.equals("OK")) {
progress.fail("Unable to upload files to server. Server returned status="+status);
log.error("Unable to upload files to server. Server returned status="+status);
return;
}
progress.updateStatus("File upload complete (Server status="+status+")");
if (log.isDebugEnabled()) {
log.debug("File upload complete (Server status="+status+")");
}
// 2) an int, the number of files received
int count = in.readInt();
if(count != valid.size()) {
progress.fail("Server only received "+count+" of "+valid.size()+" files");
log.warn("Server only received "+count+" of "+valid.size()+" files");
}
// 3) for each file:
for (Iterator it = valid.iterator(); it.hasNext();) {
Integer index = (Integer) it.next();
// 3.1) a UTF String, the path to the file as saved to the server's filesystem
String serverFileName = in.readUTF();
if (serverFileName != null) {
files[index.intValue()] = new File(serverFileName);
} else {
log.error("Received an invalid filename from the server");
files[index.intValue()] = null;
}
if (log.isDebugEnabled()) {
log.debug("Server created file="+serverFileName);
}
}
// x) new data would be added here
if (rspVer > REMOTE_DEPLOY_RESPONSE_VER_0) {
// additions in later datastream versions would be handled here
if (rspVer > REMOTE_DEPLOY_RESPONSE_VER) {
// if the server is sending a newer version than we know about
// just ignore it and warn the user about the mismatch
log.warn("Received a newer server response ("+rspVer+") than expected ("+REMOTE_DEPLOY_RESPONSE_VER+"). Ignoring any additional server response data.");
}
}
} else {
// should never happen, but handle it anyway
progress.fail("Received unknown server response version="+rspVer);
log.warn("Received unknown server response version="+rspVer);
}
in.close();
progress.updateStatus("File(s) transferred to server. Resuming deployment operation.");
} catch (Exception e) {
progress.doFail(e);
}
}
}
private static URLConnection connectToServer(URL url, String username, String password) throws IOException {
URLConnection con = url.openConnection();
String auth = username + ":" + password;
byte[] data = auth.getBytes();
String s = new String(Base64.encode(data));
while(s.length() % 4 != 0) s += "=";
con.setRequestProperty("Authorization", "Basic "+s);
con.setDoInput(true);
con.setDoOutput(true);
con.connect();
return con;
}
}