/*
* Copyright 1999-2008 University of Chicago
*
* 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.globus.workspace.cloud.client.util;
import edu.emory.mathcs.backport.java.util.concurrent.ExecutionException;
import edu.emory.mathcs.backport.java.util.concurrent.ExecutorService;
import edu.emory.mathcs.backport.java.util.concurrent.FutureTask;
import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
import edu.emory.mathcs.backport.java.util.concurrent.TimeoutException;
import org.globus.ftp.GridFTPClient;
import org.globus.ftp.MlsxEntry;
import org.globus.ftp.Session;
import org.globus.ftp.exception.ServerException;
import org.globus.gsi.gssapi.auth.Authorization;
import org.globus.gsi.gssapi.auth.HostAuthorization;
import org.globus.gsi.gssapi.auth.IdentityAuthorization;
import org.globus.io.urlcopy.UrlCopy;
import org.globus.util.GlobusURL;
import org.globus.workspace.client_core.ExecutionProblem;
import org.globus.workspace.client_core.ParameterProblem;
import org.globus.workspace.cloud.client.AllArgs;
import org.globus.workspace.cloud.client.Opts;
import org.globus.workspace.cloud.client.Props;
import org.globus.workspace.cloud.client.tasks.CopyTask;
import org.globus.workspace.cloud.client.tasks.CopyWatchTask;
import org.globus.workspace.common.SecurityUtil;
import org.globus.workspace.common.print.Print;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Vector;
public class GridFTPRepositoryUtil
implements RepositoryInterface {
private AllArgs args;
private final Print print;
private String remoteUserBaseURLString;
private String remoteUserBaseDir;
public GridFTPRepositoryUtil(
AllArgs args,
Print pr)
{
this.args = args;
this.print = pr;
}
public void paramterCheck(
AllArgs args,
String action)
throws ParameterProblem
{
CloudClientUtil.checkX509Credential(action, this.print);
this._checkGridFTPGeneric(action);
}
private void _checkGridFTPGeneric(String action) throws ParameterProblem {
if (this.args.getXferHostPort() == null) {
throw new ParameterProblem(action + " requires '" +
Opts.GRIDFTP_OPT_STRING + "'");
}
if (this.args.getTargetBaseDirectory() == null) {
throw new ParameterProblem(action + " requires '" +
Opts.TARGETDIR_OPT_STRING + "'");
}
if (this.args.getPropagationScheme() == null) {
throw new ParameterProblem(action + " with GridFTP requires '" +
Props.KEY_PROPAGATION_KEEPPORT + "' property");
}
final String url;
try {
url = this.getRemoteUserBaseURLString();
} catch (Exception e) {
throw new ParameterProblem("Issue deriving target image " +
"repository URL: " + e.getMessage());
}
if (!CloudClientUtil.validURL(url, this.print.getDebugProxy())) {
throw new ParameterProblem("Derived target image " +
"repository URL is not a valid URL: '" + url + "'");
}
}
public void uploadVM(
String localfile,
String vmName,
PrintStream info,
PrintStream debug,
ExecutorService executorService)
throws ExecutionProblem
{
String sourceUrlString = CloudClientUtil.localTargetURL(localfile);
String destUrlString;
long timeoutMinutes = this.args.getTimeoutMinutes();
String identityAuthorization = this.args.getGridftpID();
final File f = new File(vmName);
destUrlString = this.getRemoteUserBaseURLString() + f.getName();
this.sendFile(
sourceUrlString,
destUrlString,
timeoutMinutes,
identityAuthorization,
info,
debug,
executorService);
}
public void downloadVM(
String localfile,
String vmName,
PrintStream info,
PrintStream debug,
ExecutorService executorService)
throws ExecutionProblem
{
String sourceUrlString = this.getRemoteUserBaseURLString() + vmName;
String destUrlString = CloudClientUtil.localTargetURL(localfile);
long timeoutMinutes = this.args.getTimeoutMinutes();
String identityAuthorization = this.args.getGridftpID();
this.sendFile(
sourceUrlString,
destUrlString,
timeoutMinutes,
identityAuthorization,
info,
debug,
executorService);
}
private String getRemoteUserBaseURLString()
throws ExecutionProblem
{
if (this.remoteUserBaseURLString != null) {
return this.remoteUserBaseURLString;
}
String hash = null;
try
{
hash = SecurityUtil.hashGlobusCredential(
CloudClientUtil.getActiveX509Credential(this.print),
this.print.getDebugProxy());
}
catch(Exception ex)
{
hash = null;
}
if (hash == null) {
throw new ExecutionProblem("Could not obtain hash of current " +
"credential to generate directory name");
}
this.remoteUserBaseDir =
CloudClientUtil.destUserBaseDir(this.args.getTargetBaseDirectory(),
hash);
this.remoteUserBaseURLString =
CloudClientUtil.destUserBaseURL(this.args.getXferHostPort(),
this.args.getTargetBaseDirectory(),
hash);
this.print.debugln("\nDerived user base dir: " +
this.remoteUserBaseDir);
this.print.debugln("\nDerived user base URL: " +
this.remoteUserBaseURLString);
return this.remoteUserBaseURLString;
}
// -------------------------------------------------------------------------
// SEND FILE
// -------------------------------------------------------------------------
private void sendFile(String sourceUrlString,
String destUrlString,
long timeoutMinutes,
String identityAuthorization,
PrintStream info,
PrintStream debug,
ExecutorService executorService)
throws ExecutionProblem {
if (executorService == null) {
throw new IllegalArgumentException("executorService may not be null");
}
final UrlCopy urlcopy = new UrlCopy();
try {
final GlobusURL source = new GlobusURL(sourceUrlString);
urlcopy.setSourceUrl(source);
} catch (Exception e) {
throw new ExecutionProblem("Problem constructing source URL: " +
e.getMessage());
}
try {
final GlobusURL dest = new GlobusURL(destUrlString);
urlcopy.setDestinationUrl(dest);
} catch (Exception e) {
throw new ExecutionProblem("Problem constructing destination " +
"URL: " + e.getMessage());
}
if (identityAuthorization == null) {
urlcopy.setDestinationAuthorization(
HostAuthorization.getInstance());
if (debug != null) {
debug.println(
"Using host-based authorization of remote server");
}
} else {
final IdentityAuthorization idA =
new IdentityAuthorization(identityAuthorization);
urlcopy.setDestinationAuthorization(idA);
if (debug != null) {
debug.println("Using identity-based authorization of remote " +
"server: '" + identityAuthorization + "'");
}
}
PrintStream pr = null;
if (info != null) {
pr = info;
} else if (debug != null) {
pr = debug;
}
if (pr != null) {
pr.println("\nTransferring");
pr.println(" - Source: " + sourceUrlString);
pr.println(" - Destination: " + destUrlString);
pr.println();
}
final FutureTask[] tasks = new FutureTask[2];
tasks[0] = new FutureTask(new CopyTask(urlcopy));
if (info != null) {
tasks[1] = new FutureTask(new CopyWatchTask(info, debug));
} else {
tasks[1] = null;
}
for (int i = 0; i < tasks.length; i++) {
if (tasks[i] != null) {
executorService.submit(tasks[i]);
}
}
for (int i = 0; i < tasks.length; i++) {
if (tasks[i] != null) {
// all tasks currently return null
try {
if (timeoutMinutes < 1) {
tasks[i].get();
} else {
tasks[i].get(timeoutMinutes, TimeUnit.MINUTES);
}
} catch (InterruptedException e) {
throw new ExecutionProblem(e);
} catch (ExecutionException e) {
throw new ExecutionProblem("Problem transferring: " +
e.getMessage());
} catch (TimeoutException e) {
throw new ExecutionProblem("Timeout limit exceeded.");
}
}
}
if (info != null) {
info.println("Copy complete.\n");
} else if (debug != null) {
debug.println("Copy complete.\n");
}
}
// -------------------------------------------------------------------------
// DELETE FILE
// -------------------------------------------------------------------------
public void deleteVM(
String vmName,
PrintStream info,
PrintStream debug)
throws ExecutionProblem {
String identityAuthorization = this.args.getGridftpID();
String delUrlString = this.getRemoteUserBaseURLString() + vmName;
final GlobusURL delURL;
try {
delURL = new GlobusURL(delUrlString);
} catch (Exception e) {
throw new ExecutionProblem("Problem constructing delete URL: " +
e.getMessage());
}
final GridFTPClient ftp;
try {
ftp = new GridFTPClient(delURL.getHost(), delURL.getPort());
} catch (Exception e) {
throw new ExecutionProblem("Problem constructing GridFTPClient: " +
e.getMessage());
}
final Authorization auth;
if (identityAuthorization == null) {
auth = HostAuthorization.getInstance();
if (debug != null) {
debug.println(
"Using host-based authorization of remote server");
}
} else {
auth = new IdentityAuthorization(identityAuthorization);
if (debug != null) {
debug.println("Using identity-based authorization of remote " +
"server: '" + identityAuthorization + "'");
}
}
ftp.setAuthorization(auth);
PrintStream pr = null;
if (info != null) {
pr = info;
} else if (debug != null) {
pr = debug;
}
if (pr != null) {
pr.println("\nDeleting: " + delUrlString);
pr.println();
}
try {
ftp.authenticate(null);
ftp.deleteFile(delURL.getPath());
} catch (IOException e) {
throw new ExecutionProblem(e.getMessage(), e);
} catch (ServerException e) {
throw new ExecutionProblem(e.getMessage(), e);
}
if (info != null) {
info.println("Deleted.\n");
} else if (debug != null) {
debug.println("Deleted.\n");
}
}
// -------------------------------------------------------------------------
// LIST FILES
// -------------------------------------------------------------------------
public FileListing[] listFiles(
PrintStream info,
PrintStream err,
PrintStream debug)
throws ExecutionProblem
{
String url = null;
try {
url = this.getRemoteUserBaseURLString();
} catch (Exception e) {
throw new ExecutionProblem(e.getMessage(), e);
}
String identAuthz = this.args.getGridftpID();
try {
return listFilesImpl(url, identAuthz, info, err, debug);
} catch (Exception e) {
if (debug != null) {
debug.println("\n---------- Listing problem: ---------\n");
e.printStackTrace(debug);
debug.println("\n-------- (End Listing problem) ------\n");
}
throw new ExecutionProblem("Problem listing workspaces: " +
e.getMessage());
}
}
public FileListing[] listFilesImpl(
String url,
String identityAuthorization,
PrintStream info,
PrintStream err,
PrintStream debug)
throws Exception {
final GlobusURL listdir = new GlobusURL(url);
if (debug != null) {
debug.println("Listing:\n" + listdir.toString());
}
final GridFTPClient client =
new GridFTPClient(listdir.getHost(), listdir.getPort());
if (identityAuthorization == null) {
client.setAuthorization(HostAuthorization.getInstance());
if (debug != null) {
debug.println(
"Using host-based authorization of remote server");
}
} else {
final IdentityAuthorization idA =
new IdentityAuthorization(identityAuthorization);
client.setAuthorization(idA);
if (debug != null) {
debug.println("Using identity-based authorization of remote " +
"server: '" + identityAuthorization + "'");
}
}
if (debug != null) {
debug.println("Authenticating.");
}
client.authenticate(null);
client.setType(Session.TYPE_ASCII);
client.setPassive();
client.setLocalActive();
if (debug != null) {
debug.println("Changing directory (CWD).");
}
client.changeDir(listdir.getPath());
if (debug != null) {
debug.println("Listing.");
}
final Vector v = client.mlsd(null);
int len = v.size();
if (debug != null) {
debug.println("Size of list return vector: " + len);
}
final ArrayList files = new ArrayList(len);
while (! v.isEmpty()) {
final MlsxEntry f = (MlsxEntry)v.remove(0);
if (f == null) {
if (debug != null) {
debug.println("null MlsxEntry received (?)");
}
continue;
}
final String fileName = f.getFileName();
if (fileName == null) {
if (debug != null) {
debug.println("no MlsxEntry filename (?)");
}
continue;
}
if (fileName.equals(".")) {
len -= 1;
if (debug != null) {
debug.println("'.' is not an interesting file");
}
continue;
}
if (fileName.equals("..")) {
len -= 1;
if (debug != null) {
debug.println("'..' is not an interesting file");
}
continue;
}
final FileListing fl = new FileListing();
fl.setName(f.getFileName());
final String sizeStr = f.get("size");
if (sizeStr == null) {
fl.setSize(-1);
} else {
long x = -1;
try {
x = Long.parseLong(sizeStr);
} catch (NumberFormatException e) {
// pass.
}
fl.setSize(x);
}
final String modified = f.get("modify");
// 20080522161726
if (modified == null || modified.length() != 14) {
throw new Exception("cannot parse modified time");
}
fl.setDate(parseDate(modified));
fl.setTime(parseTime(modified));
final String type = f.get("type");
if (type != null && type.equals("dir")) {
fl.setDirectory(true);
}
// If user is root and perms are group no-write and all no-write,
// we can, because of several conventions, safely say that the user
// only has read-only access.
final String owner = f.get("unix.owner");
final String mode = f.get("unix.mode");
if (mode == null) {
fl.setReadWrite(false);
} else if (mode.substring(3,4).equals("6")) {
fl.setReadWrite(true);
} else if (mode.substring(1,2).equals("6")) {
if (owner != null && owner.equals("root")) {
fl.setReadWrite(false);
} else {
fl.setReadWrite(true);
}
} else if (mode.substring(2,3).equals("6")) {
fl.setReadWrite(true); // unknown to be actually true.
}
files.add(fl);
}
client.close();
return (FileListing[]) files.toArray(new FileListing[files.size()]);
}
private String parseDate(String modified) {
if (modified == null || modified.length() != 14) {
throw new IllegalArgumentException("invalid modified arg");
}
final String year = modified.substring(0,4);
final String monthNum = modified.substring(4,6);
final String month = getMonthStr(Integer.parseInt(monthNum));
final String day = modified.substring(6,8);
return month + " " + day + ", " + year;
}
private String getMonthStr(int month) {
switch (month) {
case 1: return "Jan";
case 2: return "Feb";
case 3: return "Mar";
case 4: return "Apr";
case 5: return "May";
case 6: return "Jun";
case 7: return "Jul";
case 8: return "Aug";
case 9: return "Sep";
case 10: return "Oct";
case 11: return "Nov";
case 12: return "Dec";
default: return "???";
}
}
private String parseTime(String modified) {
if (modified == null || modified.length() != 14) {
throw new IllegalArgumentException("invalid modified arg");
}
final String hours = modified.substring(8,10);
final String minutes = modified.substring(10,12);
return hours + ":" + minutes;
}
public String getDerivedImageURL(
String imageName)
throws ExecutionProblem
{
String imageURL = this.args.getPropagationScheme();
this.print.debugln("Given propagation scheme: '" + imageURL + "'");
imageURL += "://";
// a bit messy
if (this.args.isPropagationKeepPort()) {
imageURL += this.args.getXferHostPort();
} else {
final String[] parts = this.args.getXferHostPort().split(":");
if (parts.length != 2) {
throw new ExecutionProblem(
"gridftp host + port has no port?");
}
imageURL += parts[0];
this.print.debugln("Stripped port from repository host+port");
}
String url = imageURL + this.remoteUserBaseDir + "/" + imageName;
this.print.debugln("New image URL: " + url);
return url;
}
public void chmod(
String ownerId,
String permissions,
String vmName,
PrintStream info,
PrintStream debug)
throws ExecutionProblem
{
throw new ExecutionProblem(
"GridFTP does not provide a way to change permissions");
}
public String getRemoteUrl(
String fname)
throws ExecutionProblem
{
String sourcefile = this.args.getSourcefile();
String name = this.args.getName();
String rc = null;
if (sourcefile != null)
{
final File f = new File(sourcefile);
rc = this.getRemoteUserBaseURLString() + f.getName();
}
if (name != null)
{
rc = this.getRemoteUserBaseURLString() + name;
}
return rc;
}
}