/******************************************************************************* * Australian National University Data Commons * Copyright (C) 2013 The Australian National University * * This file is part of Australian National University Data Commons. * * Australian National University Data Commons is free software: you * can redistribute it and/or modify it under the terms of the GNU * General Public License as published by the Free Software Foundation, * either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package au.edu.anu.dcclient.cli; import static java.text.MessageFormat.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.net.Authenticator; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.cli.PosixParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import au.edu.anu.datacommons.config.Config; import au.edu.anu.datacommons.config.PropertiesFile; import au.edu.anu.dcclient.DcAuthenticator; import au.edu.anu.dcclient.TaskSummary; import au.edu.anu.dcclient.collection.CollectionInfo; import au.edu.anu.dcclient.stopwatch.StopWatch; import au.edu.anu.dcclient.tasks.CreateCollectionTask; import au.edu.anu.dcclient.tasks.EnumDirTask; import au.edu.anu.dcclient.tasks.FileTaskInfo; import au.edu.anu.dcclient.tasks.UploadFilesetTask; /** * Executes bag-related tasks based on the specified command line arguments. The following tasks are supported: * <ul> * <li>Creates/updates a record on DataCommons and uploads files specified in a parameter file against it replacing any * existing files on Data Commons.</li> * <li>Downloads a bag from Data Commons along with the files it contains.</li> * <li>Packages files into a bag making them ready for upload.</li> * <li>Uploads a bag to Data Commons.</li> * </ul> */ public class CmdMgr { private static final Logger LOGGER = LoggerFactory.getLogger(CmdMgr.class); private Options OPTIONS = new Options(); private int exitCode = 0; private TaskSummary summary = new TaskSummary(); /** * Constructor which takes the command line parameters as a String array. * * @param args * Command line arguments */ public CmdMgr(String[] args) { setupOptions(); CommandLineParser parser = new PosixParser(); try { CommandLine cmdLine = parser.parse(OPTIONS, args); if (cmdLine.hasOption('h')) { // If the command line contains help. dispHelp(OPTIONS); } else if (!cmdLine.hasOption("u")) { dispHelp(OPTIONS); } else if (cmdLine.hasOption('d')) { // Download. setCredentials(cmdLine); download(cmdLine); } else if (cmdLine.hasOption('l')) { // Upload Bag. setCredentials(cmdLine); upload(cmdLine); } else if (cmdLine.hasOption('c')) { // Parameter file. setCredentials(cmdLine); processParamFile(cmdLine); } } catch (ParseException e) { dispHelp(OPTIONS); } } private void setupOptions() { Option paramFile = new Option("c", "param-file", true, "Parameter file containing record information."); Option download = new Option("d", "download", true, "Pid of the item whose bag to download."); Option upload = new Option("l", "upload", true, "Pid of the item whose bag to upload."); Option username = new Option("u", "username", true, "Username to be used for logging into ANU Data Commons"); Option password = new Option("p", "password", true, "Password to be used for logging into ANU Data Commons."); Option help = new Option("h", "help", false, "Display help"); OPTIONS.addOption(paramFile); OPTIONS.addOption(download); OPTIONS.addOption(upload); OPTIONS.addOption(username); OPTIONS.addOption(password); OPTIONS.addOption(help); } /** * Extracts credentials provided in the command line and sets them for requests sent to Data Commons. * * @param cmdLine * Parsed command line as CommandLine */ private void setCredentials(CommandLine cmdLine) { String username = null; String password = null; File credsFile = new File(cmdLine.getOptionValue('u')); if (credsFile.exists()) { try { PropertiesFile credsProps = new PropertiesFile(credsFile); username = credsProps.getProperty("username"); password = credsProps.getProperty("password"); } catch (IOException e) { System.out.println(format("Unable to read credentials file at {0} . Check permissions and try again.", credsFile.getAbsolutePath())); } } else { username = cmdLine.getOptionValue('u'); password = cmdLine.getOptionValue('p'); if (password == null) { password = new String(System.console().readPassword("Password: ")); } } LOGGER.trace("Setting username {} and password **** for all requests to ANU Data Commons", username); Authenticator.setDefault(new DcAuthenticator(username, password)); } /** * Returns the exit code set representing the status of tasks performed. * * @return 0 if successful, 1 if error */ public int getExitCode() { return exitCode; } /** * Print the help for options with the specified command line syntax. * * @param options * Options object containing valid command line switches and help text. */ private void dispHelp(Options options) { HelpFormatter hf = new HelpFormatter(); PrintWriter writer = new PrintWriter(System.out); hf.printHelp("DcClient", options, true); writer.flush(); writer = null; } /** * Processes a parameter file and sends the required requests to Data Commons. * * @param cmdLine * Parsed command line */ private void processParamFile(CommandLine cmdLine) { // Create FedoraObject and then upload files. File paramFile = new File(cmdLine.getOptionValue('c')); try { StopWatch stopWatch = new StopWatch(); stopWatch.start(); // Read collection details. System.out.println("Reading values from collection file..."); CollectionInfo ci = new CollectionInfo(paramFile); String pid = ci.getPid(); if (pid == null) { // Pid doesn't exist in collection file. Create an object. CreateCollectionTask createCollTask = new CreateCollectionTask(ci); createCollTask.execute(); pid = createCollTask.get(); System.out.println("Created collection with Pid: " + pid); summary.put("Pid", pid + " (new)"); } else { System.out.println("Pid already exists for this collection: " + pid); summary.put("Pid", pid + " (existing)"); } summary.put("Parameter file", paramFile.getAbsolutePath()); // If local directory containing data files specified in parameter file, upload them. if (ci.getFilesDir() != null) { EnumDirTask enumDirTask = new EnumDirTask(ci.getFilesDir(), true) { private int fileCount = 0; @Override protected void process(List<File> chunks) { super.process(chunks); fileCount += chunks.size(); System.out.println(Config.NEWLINE); System.out.println(format("\rFiles to upload: {0}", fileCount)); } }; enumDirTask.execute(); UploadFilesetTask ulTask = new UploadFilesetTask(pid, enumDirTask.get()); ulTask.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals("progress")) { System.out.println(format("\r{0}%", evt.getNewValue())); } } }); System.out.println(Config.NEWLINE); ulTask.execute(); Map<File, FileTaskInfo> uploadResults = ulTask.get(); // Count success. for (Entry<File, FileTaskInfo> entry : uploadResults.entrySet()) { if (entry.getValue().getStatus() == FileTaskInfo.Status.FAILED) { summary.put(entry.getKey().getAbsolutePath(), "Failed to Upload"); } } } stopWatch.end(); summary.put("Total Time Taken", stopWatch.getFriendlyElapsed()); summary.put("Started", new Date(stopWatch.getStartTimeInMs()).toString()); summary.put("Ended", new Date(stopWatch.getEndTimeInMs()).toString()); summary.display(); exitCode = 0; } catch (IOException e) { LOGGER.error(e.getMessage(), e); System.out.println("Unable to complete operation."); exitCode = 1; } catch (Exception e) { LOGGER.error(e.getMessage(), e); System.out.println("Unable to complete operation."); exitCode = 1; } } /** * Uploads a bag to Data Commons. * * @param cmdLine * Parsed command line */ private void upload(CommandLine cmdLine) { // TODO Implement // String pid = cmdLine.getOptionValue('l'); // DcBag bag = new DcBag(Global.getLocalBagStoreAsFile(), pid, LoadOption.BY_FILES); // StopWatch stopWatch = new StopWatch(); // try { // stopWatch.start(); // if (cmdLine.hasOption('i')) { // setDataSource(bag); // bag.save(); // } // VerifyBagTask verifyTask = new VerifyBagTask(bag); // verifyTask.addProgressListener(new ConsoleProgressListener()); // SimpleResult result; // // Verify current bag. // result = verifyTask.call(); // if (result.isSuccess()) { // System.out.println("Verification complete. Bag is valid. Uploading bag..."); // // // Upload the bag. // UploadBagTask uploadTask = new UploadBagTask(bag, Global.getBagUploadUri()); // uploadTask.addProgressListener(new ConsoleProgressListener()); // ClientResponse resp = uploadTask.call(); // if (resp.getStatus() == HttpStatus.SC_OK) { // System.out.println("Bag uploaded successfully."); // } else { // System.out.println("Bag could not be uploaded. HTTP Status code: " + resp.getStatus()); // exitCode = 1; // } // } else { // System.out.println("Verification failed. Bag is invalid."); // exitCode = 1; // throw new Exception("Bag verification failed."); // } // // stopWatch.end(); // // } catch (Exception e) { // LOGGER.error("Unable to upload bag.", e); // System.out.println("Unable to upload bag."); // } } /** * Downloads a file from Data Commons. * * @param cmdLine * Parsed command line */ private void download(CommandLine cmdLine) { // TODO Implement // String pid = cmdLine.getOptionValue('d'); // DcBag bag = null; // // System.out.println("Getting bag information..."); // GetBagSummaryTask getInfoTask = new GetBagSummaryTask(Global.getBagUploadUri(), pid); // getInfoTask.addProgressListener(new ConsoleProgressListener()); // try { // ClientResponse resp = getInfoTask.call(); // System.out.println("Bag information received."); // if (resp.getStatus() == HttpStatus.SC_NOT_FOUND) { // // Bag for this pid not on server, create an empty local bag. // System.out.println("No bag for this collection found on server. Creating blank bag."); // bag = new DcBag(pid); // if (cmdLine.hasOption('i')) // setDataSource(bag); // File bagFile = bag.saveAs(Global.getLocalBagStoreAsFile(), pid, Format.FILESYSTEM); // File plDir = new File(bagFile, "data/"); // plDir.mkdirs(); // System.out.println("Completed"); // } else if (resp.getStatus() == HttpStatus.SC_UNAUTHORIZED) { // throw new Exception("Unauthorized to download this collection or incorrect username and/or password."); // } else if (resp.getStatus() == HttpStatus.SC_INTERNAL_SERVER_ERROR) { // throw new Exception("Server error"); // } else { // // Download bag. // DownloadBagTask dlTask = new DownloadBagTask(Global.getBagUploadUri(), pid, // Global.getLocalBagStoreAsFile()); // dlTask.addProgressListener(new ConsoleProgressListener()); // System.out.println("Downloading bag..."); // bag = new DcBag(dlTask.call(), LoadOption.BY_MANIFESTS); // if (cmdLine.hasOption('i')) { // setDataSource(bag); // bag.save(); // } // System.out.println("Bag downloaded."); // } // } catch (Exception e1) { // System.out.println("Unable to download bag from server."); // System.out.println(MessageFormat.format("Error: {0}", e1.getMessage())); // exitCode = 1; // } } }