/* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
* https://faban.dev.java.net/public/CDDLv1.0.html or
* install_dir/license.txt
* See the License for the specific language governing
* permission and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at faban/src/legal/CDDLv1.0.txt.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* $Id$
*
* Copyright 2005-2009 Sun Microsystems Inc. All Rights Reserved
*/
package com.sun.faban.harness.webclient;
import com.sun.faban.harness.common.Config;
import com.sun.faban.harness.common.RunId;
import org.apache.commons.fileupload.DiskFileUpload;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Logger;
import static com.sun.faban.harness.util.FileHelper.*;
/**
* The RunUploader represents the upload servlet as well as an upload client
* maintained in a single class.
*
* @author Akara Sucharitakul
*/
public class RunUploader extends HttpServlet {
static Logger logger = Logger.getLogger(Deployer.class.getName());
/**
* Post method to upload the run.
* @param request The servlet request
* @param response The servlet response
* @throws ServletException If the servlet fails
* @throws IOException If there is an I/O error
*/
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
String host = null;
String key = null;
boolean origin = false; // Whether the upload is to the original
// run requestor. If so, key is needed.
DiskFileUpload fu = new DiskFileUpload();
// No maximum size
fu.setSizeMax(-1);
// maximum size that will be stored in memory
fu.setSizeThreshold(4096);
// the location for saving data that is larger than getSizeThreshold()
fu.setRepositoryPath(Config.TMP_DIR);
List fileItems = null;
try {
fileItems = fu.parseRequest(request);
} catch (FileUploadException e) {
throw new ServletException(e);
}
// assume we know there are two files. The first file is a small
// text file, the second is unknown and is written to a file on
// the server
for (Iterator i = fileItems.iterator(); i.hasNext();) {
FileItem item = (FileItem) i.next();
String fieldName = item.getFieldName();
if (item.isFormField()) {
if ("host".equals(fieldName)) {
host = item.getString();
} else if ("key".equals(fieldName)) {
key = item.getString();
} else if ("origin".equals(fieldName)) {
String value = item.getString();
origin = Boolean.parseBoolean(value);
}
continue;
}
if (host == null) {
logger.warning("Host not received on upload request!");
response.sendError(HttpServletResponse.SC_FORBIDDEN);
break;
}
// The host, origin, key info must be here before we receive
// any file.
if (origin) {
if (Config.daemonMode != Config.DaemonModes.POLLEE) {
logger.warning("Origin upload requested. Not pollee!");
response.sendError(HttpServletResponse.SC_FORBIDDEN);
break;
}
if (key == null) {
logger.warning("Origin upload requested. No key!");
response.sendError(HttpServletResponse.SC_FORBIDDEN);
break;
}
if (!RunRetriever.authenticate(host, key)) {
logger.warning("Origin upload requested. " +
"Host/key mismatch: " +host + '/' + key + "!");
response.sendError(HttpServletResponse.SC_FORBIDDEN);
break;
}
}
if (!"jarfile".equals(fieldName)) // ignore
continue;
String fileName = item.getName();
if (fileName == null) // We don't process files without names
continue;
// Now, this name may have a path attached, dependent on the
// source browser. We need to cover all possible clients...
char[] pathSeparators = {'/', '\\'};
// Well, if there is another separator we did not account for,
// just add it above.
for (int j = 0; j < pathSeparators.length; j++) {
int idx = fileName.lastIndexOf(pathSeparators[j]);
if (idx != -1) {
fileName = fileName.substring(idx + 1);
break;
}
}
// Ignore all non-jarfiles.
if (!fileName.toLowerCase().endsWith(".jar"))
continue;
File uploadFile = new File(Config.TMP_DIR, host + '.' + fileName);
try {
item.write(uploadFile);
} catch (Exception e) {
throw new ServletException(e);
}
File runTmp = unjarTmp(uploadFile);
String runId = null;
if (origin) {
// Change origin file to know where this run came from.
File metaInf = new File(runTmp, "META-INF");
File originFile = new File(metaInf, "origin");
if (!originFile.exists()) {
logger.warning("Origin upload requested. Origin file" +
"does not exist!");
response.sendError(
HttpServletResponse.SC_NOT_ACCEPTABLE,
"Origin file does not exist!");
break;
}
RunId origRun;
try {
origRun = new RunId(readStringFromFile(originFile).trim());
} catch (IndexOutOfBoundsException e) {
response.sendError(HttpServletResponse.SC_NOT_ACCEPTABLE,
"Origin file error. " + e.getMessage());
break;
}
runId = origRun.getBenchName() + '.' + origRun.getRunSeq();
String localHost = origRun.getHostName();
if (!localHost.equals(Config.FABAN_HOST)) {
logger.warning("Origin upload requested. Origin host " +
localHost + " does not match this host " +
Config.FABAN_HOST + '!');
response.sendError(HttpServletResponse.SC_FORBIDDEN);
break;
}
writeStringToFile(runTmp.getName(), originFile);
} else {
runId = runTmp.getName();
}
if (recursiveCopy(runTmp, new File(Config.OUT_DIR, runId))) {
uploadFile.delete();
recursiveDelete(runTmp);
} else {
logger.warning("Origin upload requested. Copy error!");
response.sendError(HttpServletResponse.SC_NOT_ACCEPTABLE);
break;
}
response.setStatus(HttpServletResponse.SC_CREATED);
break;
}
}
/**
* Client call to upload the run back to the originating server.
* This method does nothing if the run is local.
* @param runId The id of the run
* @throws IOException If the upload fails
*/
public static void uploadIfOrigin(String runId) throws IOException {
// 1. Check origin
File originFile = new File(Config.OUT_DIR + File.separator + runId +
File.separator + "META-INF" + File.separator + "origin");
if (!originFile.isFile())
return; // Is local run, do nothing.
String originSpec = readStringFromFile(originFile);
int idx = originSpec.lastIndexOf('.');
if (idx == -1) { // This is wrong, we do not accept this.
logger.severe("Bad origin spec.");
return;
}
idx = originSpec.lastIndexOf('.', idx - 1);
if (idx == -1) {
logger.severe("Bad origin spec.");
return;
}
String host = originSpec.substring(0, idx);
String key = null;
URL target = null;
String proxyHost = null;
int proxyPort = -1;
// Search the poll hosts for this origin.
for (int i = 0; i < Config.pollHosts.length; i++) {
Config.HostInfo pollHost = Config.pollHosts[i];
if (host.equals(pollHost.name)) {
key = pollHost.key;
target = new URL(pollHost.url, "upload");
proxyHost = pollHost.proxyHost;
proxyPort = pollHost.proxyPort;
break;
}
}
if (key == null) {
logger.severe("Origin host/url/key not found!");
return;
}
// 2. Jar up the run
String[] files = new File(Config.OUT_DIR, runId).list();
File jarFile = new File(Config.TMP_DIR, runId + ".jar");
jar(Config.OUT_DIR + runId, files, jarFile.getAbsolutePath());
// 3. Upload the run
ArrayList<Part> params = new ArrayList<Part>();
//MultipartPostMethod post = new MultipartPostMethod(target.toString());
params.add(new StringPart("host", Config.FABAN_HOST));
params.add(new StringPart("key", key));
params.add(new StringPart("origin", "true"));
params.add(new FilePart("jarfile", jarFile));
Part[] parts = new Part[params.size()];
parts = params.toArray(parts);
PostMethod post = new PostMethod(target.toString());
post.setRequestEntity(
new MultipartRequestEntity(parts, post.getParams()));
HttpClient client = new HttpClient();
if (proxyHost != null)
client.getHostConfiguration().setProxy(proxyHost, proxyPort);
client.getHttpConnectionManager().getParams().
setConnectionTimeout(5000);
int status = client.executeMethod(post);
if (status == HttpStatus.SC_FORBIDDEN)
logger.severe("Server " + host + " denied permission to upload run "
+ runId + '!');
else if (status == HttpStatus.SC_NOT_ACCEPTABLE)
logger.severe("Run " + runId + " origin error!");
else if (status != HttpStatus.SC_CREATED)
logger.severe("Server responded with status code " +
status + ". Status code 201 (SC_CREATED) expected.");
jarFile.delete();
}
}