Java tutorial
/* * JetS3t : Java S3 Toolkit * Project hosted at http://bitbucket.org/jmurty/jets3t/ * * Copyright 2006-2011 James Murty * * 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.jets3t.apps.uploader; import java.awt.CardLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Rectangle; import java.awt.datatransfer.DataFlavor; import java.awt.dnd.DnDConstants; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTargetListener; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.GZIPInputStream; import javax.swing.BorderFactory; import javax.swing.JApplet; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.border.Border; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import org.apache.commons.httpclient.contrib.proxy.PluginProxyUtil; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.Header; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.auth.AuthScope; import org.apache.http.auth.Credentials; import org.apache.http.auth.NTCredentials; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.params.ConnRoutePNames; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; import org.apache.http.params.HttpProtocolParams; import org.apache.http.util.EntityUtils; import org.jets3t.gui.AuthenticationDialog; import org.jets3t.gui.ErrorDialog; import org.jets3t.gui.GuiUtils; import org.jets3t.gui.HyperlinkActivatedListener; import org.jets3t.gui.JHtmlLabel; import org.jets3t.gui.UserInputFields; import org.jets3t.gui.skins.SkinsFactory; import org.jets3t.service.Constants; import org.jets3t.service.Jets3tProperties; import org.jets3t.service.S3ServiceException; import org.jets3t.service.impl.rest.httpclient.RestS3Service; import org.jets3t.service.io.BytesProgressWatcher; import org.jets3t.service.io.ProgressMonitoredInputStream; import org.jets3t.service.model.S3Object; import org.jets3t.service.multithread.CancelEventTrigger; import org.jets3t.service.multithread.CopyObjectsEvent; import org.jets3t.service.multithread.CreateBucketsEvent; import org.jets3t.service.multithread.CreateObjectsEvent; import org.jets3t.service.multithread.DeleteObjectsEvent; import org.jets3t.service.multithread.DeleteVersionedObjectsEvent; import org.jets3t.service.multithread.DownloadObjectsEvent; import org.jets3t.service.multithread.GetObjectHeadsEvent; import org.jets3t.service.multithread.GetObjectsEvent; import org.jets3t.service.multithread.ListObjectsEvent; import org.jets3t.service.multithread.LookupACLEvent; import org.jets3t.service.multithread.S3ServiceEventListener; import org.jets3t.service.multithread.S3ServiceMulti; import org.jets3t.service.multithread.ServiceEvent; import org.jets3t.service.multithread.ThreadWatcher; import org.jets3t.service.multithread.UpdateACLEvent; import org.jets3t.service.security.AWSCredentials; import org.jets3t.service.utils.ByteFormatter; import org.jets3t.service.utils.Mimetypes; import org.jets3t.service.utils.RestUtils; import org.jets3t.service.utils.ServiceUtils; import org.jets3t.service.utils.TimeFormatter; import org.jets3t.service.utils.gatekeeper.GatekeeperMessage; import org.jets3t.service.utils.gatekeeper.SignatureRequest; import org.jets3t.service.utils.signedurl.SignedUrlAndObject; import contribs.com.centerkey.utils.BareBonesBrowserLaunch; /** * Dual application and applet for uploading files and XML metadata information to the Amazon S3 * service. * <p> * For more information and help please see the * <a href="http://www.jets3t.org/applications/uploader.html">Uploader Guide</a>. * </p> * <p> * The Uploader is highly configurable through properties specified in a file * <tt>uploader.properties</tt>. This file <b>must be available</b> at the root of the classpath. * * @author James Murty */ public class Uploader extends JApplet implements S3ServiceEventListener, ActionListener, ListSelectionListener, HyperlinkActivatedListener, CredentialsProvider { private static final long serialVersionUID = 2759324769352022783L; private static final Log log = LogFactory.getLog(Uploader.class); public static final String APPLICATION_DESCRIPTION = "Uploader/" + Constants.JETS3T_VERSION; public static final String UPLOADER_PROPERTIES_FILENAME = "uploader.properties"; private static final String UPLOADER_VERSION_ID = "JetS3t Uploader/" + Constants.JETS3T_VERSION; public static final int WIZARD_SCREEN_1 = 1; public static final int WIZARD_SCREEN_2 = 2; public static final int WIZARD_SCREEN_3 = 3; public static final int WIZARD_SCREEN_4 = 4; public static final int WIZARD_SCREEN_5 = 5; /* * Error codes displayed when errors occur. Each possbile error condition has its own code * to aid in resolving user's problems. */ public static final String ERROR_CODE__MISSING_REQUIRED_PARAM = "100"; public static final String ERROR_CODE__S3_UPLOAD_FAILED = "101"; public static final String ERROR_CODE__UPLOAD_REQUEST_DECLINED = "102"; public static final String ERROR_CODE__TRANSACTION_ID_REQUIRED_TO_CREATE_XML_SUMMARY = "103"; /* * HTTP connection settings for communication *with Gatekeeper only*, the * S3 connection parameters are set in the jets3t.properties file. */ public static final int HTTP_CONNECTION_TIMEOUT = 60000; public static final int SOCKET_CONNECTION_TIMEOUT = 60000; public static final int MAX_CONNECTION_RETRIES = 5; private Frame ownerFrame = null; private UserInputFields userInputFields = null; private Properties userInputProperties = null; private HttpClient httpClientGatekeeper = null; private S3ServiceMulti s3ServiceMulti = null; /** * The files to upload to S3. */ private File[] filesToUpload = null; /** * The list of file extensions accepted by the Uploader. */ private ArrayList validFileExtensions = new ArrayList(); /** * Uploader's properties. */ private Jets3tProperties uploaderProperties = null; /** * Properties set in stand-alone application from the command line arguments. */ private Properties standAloneArgumentProperties = null; private final ByteFormatter byteFormatter = new ByteFormatter(); private final TimeFormatter timeFormatter = new TimeFormatter(); /* * Upload file constraints. */ private int fileMaxCount = 0; private long fileMaxSizeMB = 0; private long fileMinSizeMB = 0; /* * Insets used throughout the application. */ private final Insets insetsDefault = new Insets(3, 5, 3, 5); private final Insets insetsNone = new Insets(0, 0, 0, 0); private final GuiUtils guiUtils = new GuiUtils(); private int currentState = 0; private final boolean isRunningAsApplet; private HashMap parametersMap = new HashMap(); private SkinsFactory skinsFactory = null; private final GridBagLayout GRID_BAG_LAYOUT = new GridBagLayout(); /* * GUI elements that need to be referenced outside initGui method. */ private JHtmlLabel userGuidanceLabel = null; private JPanel appContentPanel = null; private JPanel buttonsPanel = null; private JPanel primaryPanel = null; private CardLayout primaryPanelCardLayout = null; private CardLayout buttonsPanelCardLayout = null; private JButton backButton = null; private JButton nextButton = null; private JButton cancelUploadButton = null; private JHtmlLabel dragDropTargetLabel = null; private JHtmlLabel fileToUploadLabel = null; private JHtmlLabel fileInformationLabel = null; private JHtmlLabel progressTransferDetailsLabel = null; private JProgressBar progressBar = null; private JHtmlLabel progressStatusTextLabel = null; private JHtmlLabel finalMessageLabel = null; private CancelEventTrigger uploadCancelEventTrigger = null; /** * Set to true when the object/file being uploaded is the final in a set, eg when * the XML metadata is being uploaded after a movie file. */ private volatile boolean uploadingFinalObject = false; /** * If set to true via the "xmlSummary" configuration property, the Uploader will * generate an XML summary document describing the files uploaded by the user and * will upload this summary document to S3. */ private volatile boolean includeXmlSummaryDoc = false; /** * Set to true when a file upload has been cancelled, to prevent the Uploader from * uploading an XML summary file when the prior file upload was cancelled. */ private volatile boolean uploadCancelled = false; /** * Set to true if an upload failed due to a key name clash in S3, in which case an error * message is displayed in the final 'thankyou' screen. */ private boolean fatalErrorOccurred = false; /** * Variable to store application exceptions, so that client failure information can be * included in the information provided to the Gatekeeper when uploads are retried. */ private Exception priorFailureException = null; private final CredentialsProvider mCredentialProvider; private Uploader(boolean pIsRunningApplet) { isRunningAsApplet = pIsRunningApplet; mCredentialProvider = new BasicCredentialsProvider(); } /** * Constructor to run this application as an Applet. */ public Uploader() { this(true); } /** * Constructor to run this application in a stand-alone window. * * @param ownerFrame the frame the application will be displayed in * @throws S3ServiceException */ public Uploader(JFrame ownerFrame, Properties standAloneArgumentProperties) throws S3ServiceException { this(false); this.ownerFrame = ownerFrame; this.standAloneArgumentProperties = standAloneArgumentProperties; init(); ownerFrame.getContentPane().add(this); ownerFrame.setBounds(this.getBounds()); ownerFrame.setVisible(true); } /** * Prepares application to run as a GUI by finding/creating a root owner JFrame, and * (if necessary) creating a directory for storing remembered logins. */ @Override public void init() { super.init(); boolean isMissingRequiredInitProperty = false; // Find or create a Frame to own modal dialog boxes. if (this.ownerFrame == null) { Component c = this; while (!(c instanceof Frame) && c.getParent() != null) { c = c.getParent(); } if (!(c instanceof Frame)) { this.ownerFrame = new JFrame(); } else { this.ownerFrame = (Frame) c; } } // Read properties from uploader.properties in classpath. uploaderProperties = Jets3tProperties.getInstance(UPLOADER_PROPERTIES_FILENAME); if (isRunningAsApplet) { // Read parameters for Applet, based on names specified in the uploader properties. String appletParamNames = uploaderProperties.getStringProperty("applet.params", ""); StringTokenizer st = new StringTokenizer(appletParamNames, ","); while (st.hasMoreTokens()) { String paramName = st.nextToken(); String paramValue = this.getParameter(paramName); // Fatal error if a parameter is missing. if (null == paramValue) { log.error("Missing required applet parameter: " + paramName); isMissingRequiredInitProperty = true; } else { log.debug("Found applet parameter: " + paramName + "='" + paramValue + "'"); // Set params as properties in the central properties source for this application. // Note that parameter values will over-write properties with the same name. uploaderProperties.setProperty(paramName, paramValue); // Store params in a separate map, which is used to build XML document. parametersMap.put(paramName, paramValue); } } } else { // Add application parameters properties. if (standAloneArgumentProperties != null) { Enumeration e = standAloneArgumentProperties.keys(); while (e.hasMoreElements()) { String propName = (String) e.nextElement(); String propValue = standAloneArgumentProperties.getProperty(propName); // Fatal error if a parameter is missing. if (null == propValue) { log.error("Missing required command-line property: " + propName); isMissingRequiredInitProperty = true; } else { log.debug("Using command-line property: " + propName + "='" + propValue + "'"); // Set arguments as properties in the central properties source for this application. // Note that argument values will over-write properties with the same name. uploaderProperties.setProperty(propName, propValue); // Store arguments in a separate map, which is used to build XML document. parametersMap.put(propName, propValue); } } } } // Determine the file constraints. fileMaxCount = uploaderProperties.getIntProperty("file.maxCount", 1); fileMaxSizeMB = uploaderProperties.getLongProperty("file.maxSizeMB", 200); fileMinSizeMB = uploaderProperties.getLongProperty("file.minSizeMB", 0); // Initialise the GUI. initGui(); // Jump to error page if there was an exception raised during initialisation. if (isMissingRequiredInitProperty) { failWithFatalError(ERROR_CODE__MISSING_REQUIRED_PARAM); return; } // Determine valid file extensions. String validFileExtensionsStr = uploaderProperties.getStringProperty("file.extensions", ""); if (validFileExtensionsStr != null) { StringTokenizer st = new StringTokenizer(validFileExtensionsStr, ","); while (st.hasMoreTokens()) { validFileExtensions.add(st.nextToken().toLowerCase(Locale.getDefault())); } } } /** * Initialises the application's GUI elements. */ private void initGui() { // Initialise skins factory. skinsFactory = SkinsFactory.getInstance(uploaderProperties.getProperties()); // Set Skinned Look and Feel. LookAndFeel lookAndFeel = skinsFactory.createSkinnedMetalTheme("SkinnedLookAndFeel"); try { UIManager.setLookAndFeel(lookAndFeel); } catch (UnsupportedLookAndFeelException e) { log.error("Unable to set skinned LookAndFeel", e); } // Apply branding String applicationTitle = replaceMessageVariables( uploaderProperties.getStringProperty("gui.applicationTitle", null)); if (applicationTitle != null) { ownerFrame.setTitle(applicationTitle); } String applicationIconPath = uploaderProperties.getStringProperty("gui.applicationIcon", null); if (!isRunningAsApplet && applicationIconPath != null) { guiUtils.applyIcon(ownerFrame, applicationIconPath); } String footerHtml = uploaderProperties.getStringProperty("gui.footerHtml", null); String footerIconPath = uploaderProperties.getStringProperty("gui.footerIcon", null); // Footer for branding boolean includeFooter = false; JHtmlLabel footerLabel = skinsFactory.createSkinnedJHtmlLabel("FooterLabel"); footerLabel.setHyperlinkeActivatedListener(this); footerLabel.setHorizontalAlignment(JLabel.CENTER); if (footerHtml != null) { footerLabel.setText(replaceMessageVariables(footerHtml)); includeFooter = true; } if (footerIconPath != null) { guiUtils.applyIcon(footerLabel, footerIconPath); } userInputFields = new UserInputFields(insetsDefault, this, skinsFactory); // Screeen 1 : User input fields. JPanel screen1Panel = skinsFactory.createSkinnedJPanel("Screen1Panel"); screen1Panel.setLayout(GRID_BAG_LAYOUT); userInputFields.buildFieldsPanel(screen1Panel, uploaderProperties); // Screen 2 : Drag/drop panel. JPanel screen2Panel = skinsFactory.createSkinnedJPanel("Screen2Panel"); screen2Panel.setLayout(GRID_BAG_LAYOUT); dragDropTargetLabel = skinsFactory.createSkinnedJHtmlLabel("DragDropTargetLabel"); dragDropTargetLabel.setHyperlinkeActivatedListener(this); dragDropTargetLabel.setHorizontalAlignment(JLabel.CENTER); dragDropTargetLabel.setVerticalAlignment(JLabel.CENTER); JButton chooseFileButton = skinsFactory.createSkinnedJButton("ChooseFileButton"); chooseFileButton.setActionCommand("ChooseFile"); chooseFileButton.addActionListener(this); configureButton(chooseFileButton, "screen.2.browseButton"); screen2Panel.add(dragDropTargetLabel, new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, insetsDefault, 0, 0)); screen2Panel.add(chooseFileButton, new GridBagConstraints(0, 1, 1, 1, 1, 1, GridBagConstraints.NORTH, GridBagConstraints.NONE, insetsDefault, 0, 0)); // Screen 3 : Information about the file to be uploaded. JPanel screen3Panel = skinsFactory.createSkinnedJPanel("Screen3Panel"); screen3Panel.setLayout(GRID_BAG_LAYOUT); fileToUploadLabel = skinsFactory.createSkinnedJHtmlLabel("FileToUploadLabel"); fileToUploadLabel.setHyperlinkeActivatedListener(this); fileToUploadLabel.setHorizontalAlignment(JLabel.CENTER); fileToUploadLabel.setVerticalAlignment(JLabel.CENTER); screen3Panel.add(fileToUploadLabel, new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, insetsDefault, 0, 0)); // Screen 4 : Upload progress. JPanel screen4Panel = skinsFactory.createSkinnedJPanel("Screen4Panel"); screen4Panel.setLayout(GRID_BAG_LAYOUT); fileInformationLabel = skinsFactory.createSkinnedJHtmlLabel("FileInformationLabel"); fileInformationLabel.setHyperlinkeActivatedListener(this); fileInformationLabel.setHorizontalAlignment(JLabel.CENTER); progressBar = skinsFactory.createSkinnedJProgressBar("ProgressBar", 0, 100); progressBar.setStringPainted(true); progressStatusTextLabel = skinsFactory.createSkinnedJHtmlLabel("ProgressStatusTextLabel"); progressStatusTextLabel.setHyperlinkeActivatedListener(this); progressStatusTextLabel.setText(" "); progressStatusTextLabel.setHorizontalAlignment(JLabel.CENTER); progressTransferDetailsLabel = skinsFactory.createSkinnedJHtmlLabel("ProgressTransferDetailsLabel"); progressTransferDetailsLabel.setHyperlinkeActivatedListener(this); progressTransferDetailsLabel.setText(" "); progressTransferDetailsLabel.setHorizontalAlignment(JLabel.CENTER); cancelUploadButton = skinsFactory.createSkinnedJButton("CancelUploadButton"); cancelUploadButton.setActionCommand("CancelUpload"); cancelUploadButton.addActionListener(this); configureButton(cancelUploadButton, "screen.4.cancelButton"); screen4Panel.add(fileInformationLabel, new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, insetsDefault, 0, 0)); screen4Panel.add(progressBar, new GridBagConstraints(0, 1, 1, 1, 1, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, insetsDefault, 0, 0)); screen4Panel.add(progressStatusTextLabel, new GridBagConstraints(0, 2, 1, 1, 1, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, insetsDefault, 0, 0)); screen4Panel.add(progressTransferDetailsLabel, new GridBagConstraints(0, 3, 1, 1, 1, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, insetsDefault, 0, 0)); screen4Panel.add(cancelUploadButton, new GridBagConstraints(0, 4, 1, 1, 1, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE, insetsDefault, 0, 0)); // Screen 5 : Thankyou message. JPanel screen5Panel = skinsFactory.createSkinnedJPanel("Screen5Panel"); screen5Panel.setLayout(GRID_BAG_LAYOUT); finalMessageLabel = skinsFactory.createSkinnedJHtmlLabel("FinalMessageLabel"); finalMessageLabel.setHyperlinkeActivatedListener(this); finalMessageLabel.setHorizontalAlignment(JLabel.CENTER); screen5Panel.add(finalMessageLabel, new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, insetsDefault, 0, 0)); // Wizard Button panel. backButton = skinsFactory.createSkinnedJButton("backButton"); backButton.setActionCommand("Back"); backButton.addActionListener(this); nextButton = skinsFactory.createSkinnedJButton("nextButton"); nextButton.setActionCommand("Next"); nextButton.addActionListener(this); buttonsPanel = skinsFactory.createSkinnedJPanel("ButtonsPanel"); buttonsPanelCardLayout = new CardLayout(); buttonsPanel.setLayout(buttonsPanelCardLayout); JPanel buttonsInvisiblePanel = skinsFactory.createSkinnedJPanel("ButtonsInvisiblePanel"); JPanel buttonsVisiblePanel = skinsFactory.createSkinnedJPanel("ButtonsVisiblePanel"); buttonsVisiblePanel.setLayout(GRID_BAG_LAYOUT); buttonsVisiblePanel.add(backButton, new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, insetsDefault, 0, 0)); buttonsVisiblePanel.add(nextButton, new GridBagConstraints(1, 0, 1, 1, 1, 0, GridBagConstraints.EAST, GridBagConstraints.NONE, insetsDefault, 0, 0)); buttonsPanel.add(buttonsInvisiblePanel, "invisible"); buttonsPanel.add(buttonsVisiblePanel, "visible"); // Overall content panel. appContentPanel = skinsFactory.createSkinnedJPanel("ApplicationContentPanel"); appContentPanel.setLayout(GRID_BAG_LAYOUT); JPanel userGuidancePanel = skinsFactory.createSkinnedJPanel("UserGuidancePanel"); userGuidancePanel.setLayout(GRID_BAG_LAYOUT); primaryPanel = skinsFactory.createSkinnedJPanel("PrimaryPanel"); primaryPanelCardLayout = new CardLayout(); primaryPanel.setLayout(primaryPanelCardLayout); JPanel navigationPanel = skinsFactory.createSkinnedJPanel("NavigationPanel"); navigationPanel.setLayout(GRID_BAG_LAYOUT); appContentPanel.add(userGuidancePanel, new GridBagConstraints(0, 0, 1, 1, 1, 0.2, GridBagConstraints.CENTER, GridBagConstraints.BOTH, insetsDefault, 0, 0)); appContentPanel.add(primaryPanel, new GridBagConstraints(0, 1, 1, 1, 1, 0.6, GridBagConstraints.CENTER, GridBagConstraints.BOTH, insetsDefault, 0, 0)); appContentPanel.add(navigationPanel, new GridBagConstraints(0, 2, 1, 1, 1, 0.2, GridBagConstraints.CENTER, GridBagConstraints.BOTH, insetsDefault, 0, 0)); if (includeFooter) { log.debug("Adding footer for branding"); appContentPanel.add(footerLabel, new GridBagConstraints(0, 3, 1, 1, 1, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, insetsDefault, 0, 0)); } this.getContentPane().add(appContentPanel); userGuidanceLabel = skinsFactory.createSkinnedJHtmlLabel("UserGuidanceLabel"); userGuidanceLabel.setHyperlinkeActivatedListener(this); userGuidanceLabel.setHorizontalAlignment(JLabel.CENTER); userGuidancePanel.add(userGuidanceLabel, new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, insetsNone, 0, 0)); navigationPanel.add(buttonsPanel, new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, insetsNone, 0, 0)); primaryPanel.add(screen1Panel, "screen1"); primaryPanel.add(screen2Panel, "screen2"); primaryPanel.add(screen3Panel, "screen3"); primaryPanel.add(screen4Panel, "screen4"); primaryPanel.add(screen5Panel, "screen5"); // Set preferred sizes int preferredWidth = uploaderProperties.getIntProperty("gui.minSizeWidth", 400); int preferredHeight = uploaderProperties.getIntProperty("gui.minSizeHeight", 500); this.setBounds(new Rectangle(new Dimension(preferredWidth, preferredHeight))); // Initialize drop target. initDropTarget(new Component[] { this }); // Revert to default Look and Feel for all future GUI elements (eg Dialog boxes). try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { log.error("Unable to set default system LookAndFeel", e); } wizardStepForward(); } /** * Initialise the application's File drop targets for drag and drop copying of local files * to S3. * * @param dropTargetComponents * the components files can be dropped on to transfer them to S3 */ private void initDropTarget(Component[] dropTargetComponents) { DropTargetListener dropTargetListener = new DropTargetListener() { private Border originalBorder = appContentPanel.getBorder(); private Border dragOverBorder = BorderFactory.createBevelBorder(1); private boolean checkValidDrag(DropTargetDragEvent dtde) { if (dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor) && (DnDConstants.ACTION_COPY == dtde.getDropAction() || DnDConstants.ACTION_MOVE == dtde.getDropAction())) { dtde.acceptDrag(dtde.getDropAction()); return true; } else { dtde.rejectDrag(); return false; } } public void dragEnter(DropTargetDragEvent dtde) { if (checkValidDrag(dtde)) { SwingUtilities.invokeLater(new Runnable() { public void run() { appContentPanel.setBorder(dragOverBorder); }; }); } } public void dragOver(DropTargetDragEvent dtde) { checkValidDrag(dtde); } public void dropActionChanged(DropTargetDragEvent dtde) { checkValidDrag(dtde); } public void dragExit(DropTargetEvent dte) { SwingUtilities.invokeLater(new Runnable() { public void run() { appContentPanel.setBorder(originalBorder); }; }); } public void drop(DropTargetDropEvent dtde) { if (dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor) && (DnDConstants.ACTION_COPY == dtde.getDropAction() || DnDConstants.ACTION_MOVE == dtde.getDropAction())) { dtde.acceptDrop(dtde.getDropAction()); SwingUtilities.invokeLater(new Runnable() { public void run() { appContentPanel.setBorder(originalBorder); }; }); try { final List fileList = (List) dtde.getTransferable() .getTransferData(DataFlavor.javaFileListFlavor); if (fileList != null && fileList.size() > 0) { if (checkProposedUploadFiles(fileList)) { wizardStepForward(); } } } catch (Exception e) { String errorMessage = "Unable to accept dropped item"; log.error(errorMessage, e); ErrorDialog.showDialog(ownerFrame, null, uploaderProperties.getProperties(), errorMessage, e); } } else { dtde.rejectDrop(); } } }; // Attach drop target listener to each target component. for (int i = 0; i < dropTargetComponents.length; i++) { new DropTarget(dropTargetComponents[i], DnDConstants.ACTION_COPY, dropTargetListener, true); } } /** * Checks that all the files in a list are acceptable for uploading. * Files are checked against the following criteria: * <ul> * <li>There are not too many</li> * <li>The size is greater than the minimum size, and less that the maximum size.</li> * <li>The file has a file extension matching one of those explicitly allowed</li> * </ul> * Any deviations from the rules result in an error message, and the method returns false. * A side-effect of this method is that the wizard is moved forward one screen if the * files are all valid. * * @param fileList * A list of {@link File}s to check. * * @return * true if the files in the list are all acceptable, false otherwise. */ private boolean checkProposedUploadFiles(List fileList) { // Check number of files for upload is within constraints. if (fileMaxCount > 0 && fileList.size() > fileMaxCount) { String errorMessage = "You may only upload " + fileMaxCount + (fileMaxCount == 1 ? " file" : " files") + " at a time"; ErrorDialog.showDialog(ownerFrame, this, uploaderProperties.getProperties(), errorMessage, null); return false; } // Check file size within constraints. Iterator iter = fileList.iterator(); while (iter.hasNext()) { File file = (File) iter.next(); long fileSizeMB = file.length() / (1024 * 1024); if (fileMinSizeMB > 0 && fileSizeMB < fileMinSizeMB) { ErrorDialog.showDialog(ownerFrame, this, uploaderProperties.getProperties(), "File size must be greater than " + fileMinSizeMB + " MB", null); return false; } if (fileMaxSizeMB > 0 && fileSizeMB > fileMaxSizeMB) { ErrorDialog.showDialog(ownerFrame, this, uploaderProperties.getProperties(), "File size must be less than " + fileMaxSizeMB + " MB", null); return false; } } // Check file extension is acceptable. if (validFileExtensions.size() > 0) { iter = fileList.iterator(); while (iter.hasNext()) { File file = (File) iter.next(); String fileName = file.getName(); String fileExtension = fileName.substring(fileName.lastIndexOf(".") + 1); if (!validFileExtensions.contains(fileExtension.toLowerCase(Locale.getDefault()))) { String extList = validFileExtensions.toString(); extList = extList.substring(1, extList.length() - 1); extList = extList.replaceAll(",", " "); ErrorDialog.showDialog(ownerFrame, this, uploaderProperties.getProperties(), "<html>File name must end with one of the following extensions:<br>" + extList + "</html>", null); return false; } } } filesToUpload = (File[]) fileList.toArray(new File[fileList.size()]); return true; } /** * Builds a Gatekeeper response based on AWS credential information available in the Uploader * properties. The response signs URLs to be valid for 1 day. * <p> * The required properties are: * <ul> * <li>AwsAccessKey</li> * <li>AwsSecretKey</li> * <li>S3BucketName</li> * </ul> * * @param objects * @return */ private GatekeeperMessage buildGatekeeperResponse(S3Object[] objects) throws Exception { String awsAccessKey = userInputProperties.getProperty("AwsAccessKey"); String awsSecretKey = userInputProperties.getProperty("AwsSecretKey"); String s3BucketName = userInputProperties.getProperty("S3BucketName"); Calendar cal = Calendar.getInstance(); cal.add(Calendar.DATE, 1); Date expiryDate = cal.getTime(); AWSCredentials awsCredentials = new AWSCredentials(awsAccessKey, awsSecretKey); RestS3Service s3Service = new RestS3Service(awsCredentials); try { /* * Build Gatekeeper request. */ GatekeeperMessage gatekeeperMessage = new GatekeeperMessage(); gatekeeperMessage.addApplicationProperties(userInputProperties); // Add User inputs as application properties. gatekeeperMessage.addApplicationProperties(parametersMap); // Add any Applet/Application parameters as application properties. for (int i = 0; i < objects.length; i++) { String signedPutUrl = s3Service.createSignedPutUrl(s3BucketName, objects[i].getKey(), objects[i].getMetadataMap(), expiryDate, false); SignatureRequest signatureRequest = new SignatureRequest(SignatureRequest.SIGNATURE_TYPE_PUT, objects[i].getKey()); signatureRequest.setBucketName(s3BucketName); signatureRequest.setObjectMetadata(objects[i].getMetadataMap()); signatureRequest.signRequest(signedPutUrl); gatekeeperMessage.addSignatureRequest(signatureRequest); } return gatekeeperMessage; } catch (Exception e) { throw new Exception("Unable to generate locally-signed PUT URLs for testing", e); } } /** * Retrieves a signed PUT URL from the given URL address. * The URL must point at a server-side script or service that accepts POST messages. * The POST message will include parameters for all the items in uploaderProperties, * that is everything in the file uploader.properties plus all the applet's parameters. * Based on this input, the server-side script decides whether to allow access and return * a signed PUT URL. * * @param credsProviderParamName * the name of the parameter containing the server URL target for the PUT request. * @return * the AWS credentials provided by the server-side script if access was allowed, null otherwise. * * @throws HttpException * @throws Exception */ private GatekeeperMessage contactGatewayServer(S3Object[] objects) throws Exception { // Retrieve credentials from URL location value by the property 'credentialsServiceUrl'. String gatekeeperUrl = uploaderProperties.getStringProperty("gatekeeperUrl", "Missing required property gatekeeperUrl"); /* * Build Gatekeeper request. */ GatekeeperMessage gatekeeperMessage = new GatekeeperMessage(); gatekeeperMessage.addApplicationProperties(userInputProperties); // Add User inputs as application properties. gatekeeperMessage.addApplicationProperties(parametersMap); // Add any Applet/Application parameters as application properties. // Make the Uploader's identifier available to Gatekeeper for version compatibility checking (if necessary) gatekeeperMessage.addApplicationProperty(GatekeeperMessage.PROPERTY_CLIENT_VERSION_ID, UPLOADER_VERSION_ID); // If a prior failure has occurred, add information about this failure. if (priorFailureException != null) { gatekeeperMessage.addApplicationProperty(GatekeeperMessage.PROPERTY_PRIOR_FAILURE_MESSAGE, priorFailureException.getMessage()); // Now reset the prior failure variable. priorFailureException = null; } // Add all S3 objects as candiates for PUT signing. for (int i = 0; i < objects.length; i++) { SignatureRequest signatureRequest = new SignatureRequest(SignatureRequest.SIGNATURE_TYPE_PUT, objects[i].getKey()); signatureRequest.setObjectMetadata(objects[i].getMetadataMap()); gatekeeperMessage.addSignatureRequest(signatureRequest); } /* * Build HttpClient POST message. */ // Add all properties/parameters to credentials POST request. HttpPost postMethod = new HttpPost(gatekeeperUrl); Properties properties = gatekeeperMessage.encodeToProperties(); Iterator<Map.Entry<Object, Object>> propsIter = properties.entrySet().iterator(); List<NameValuePair> parameters = new ArrayList<NameValuePair>(properties.size()); while (propsIter.hasNext()) { Map.Entry<Object, Object> entry = propsIter.next(); String fieldName = (String) entry.getKey(); String fieldValue = (String) entry.getValue(); parameters.add(new BasicNameValuePair(fieldName, fieldValue)); } postMethod.setEntity(new UrlEncodedFormEntity(parameters)); // Create Http Client if necessary, and include User Agent information. if (httpClientGatekeeper == null) { httpClientGatekeeper = initHttpConnection(); } // Try to detect any necessary proxy configurations. try { HttpHost proxyHost = PluginProxyUtil.detectProxy(new URL(gatekeeperUrl)); if (proxyHost != null) { httpClientGatekeeper.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxyHost); } ((DefaultHttpClient) httpClientGatekeeper).setCredentialsProvider(this); } catch (Throwable t) { log.debug("No proxy detected"); } // Perform Gateway request. log.debug("Contacting Gatekeeper at: " + gatekeeperUrl); HttpResponse response = null; try { response = httpClientGatekeeper.execute(postMethod); int responseCode = response.getStatusLine().getStatusCode(); String contentType = response.getFirstHeader("Content-Type").getValue(); if (responseCode == 200) { InputStream responseInputStream = null; Header encodingHeader = response.getFirstHeader("Content-Encoding"); if (encodingHeader != null && "gzip".equalsIgnoreCase(encodingHeader.getValue())) { log.debug("Inflating gzip-encoded response"); responseInputStream = new GZIPInputStream(response.getEntity().getContent()); } else { responseInputStream = response.getEntity().getContent(); } if (responseInputStream == null) { throw new IOException("No response input stream available from Gatekeeper"); } Properties responseProperties = new Properties(); try { responseProperties.load(responseInputStream); } finally { responseInputStream.close(); } GatekeeperMessage gatekeeperResponseMessage = GatekeeperMessage .decodeFromProperties(responseProperties); // Check for Gatekeeper Error Code in response. String gatekeeperErrorCode = gatekeeperResponseMessage.getApplicationProperties() .getProperty(GatekeeperMessage.APP_PROPERTY_GATEKEEPER_ERROR_CODE); if (gatekeeperErrorCode != null) { log.warn("Received Gatekeeper error code: " + gatekeeperErrorCode); failWithFatalError(gatekeeperErrorCode); return null; } if (gatekeeperResponseMessage.getSignatureRequests().length != objects.length) { throw new Exception("The Gatekeeper service did not provide the necessary " + objects.length + " response items"); } return gatekeeperResponseMessage; } else { log.debug("The Gatekeeper did not permit a request. Response code: " + responseCode + ", Response content type: " + contentType); throw new Exception("The Gatekeeper did not permit your request"); } } catch (Exception e) { throw new Exception("Gatekeeper did not respond", e); } finally { EntityUtils.consume(response.getEntity()); } } private GatekeeperMessage retrieveGatekeeperResponse(S3Object[] objects) throws Exception { // Check whether Uploader has all necessary credentials from user inputs. boolean s3CredentialsProvided = userInputProperties.getProperty("AwsAccessKey") != null && userInputProperties.getProperty("AwsSecretKey") != null && userInputProperties.getProperty("S3BucketName") != null; GatekeeperMessage gatekeeperMessage = null; if (s3CredentialsProvided) { log.debug("S3 login credentials and bucket name are available, the Uploader " + "will generate its own Gatekeeper response"); gatekeeperMessage = buildGatekeeperResponse(objects); } else { gatekeeperMessage = contactGatewayServer(objects); } return gatekeeperMessage; } /** * Uploads to S3 the file referenced by the variable fileToUpload, providing * progress feedback to the user all the while. * */ private void uploadFilesToS3() { try { SwingUtilities.invokeLater(new Runnable() { public void run() { // Creating file hash message progressStatusTextLabel.setText(replaceMessageVariables(uploaderProperties.getStringProperty( "screen.4.hashingMessage", "Missing property 'screen.4.hashingMessage'"))); }; }); // Calculate total files size. final long filesSizeTotal[] = new long[1]; for (int i = 0; i < filesToUpload.length; i++) { filesSizeTotal[0] += filesToUpload[i].length(); } // Monitor generation of MD5 hash, and provide feedback via the progress bar. BytesProgressWatcher progressWatcher = new BytesProgressWatcher(filesSizeTotal[0]) { @Override public void updateBytesTransferred(long byteCount) { super.updateBytesTransferred(byteCount); final int percentage = (int) ((double) getBytesTransferred() * 100 / getBytesToTransfer()); SwingUtilities.invokeLater(new Runnable() { public void run() { progressBar.setValue(percentage); } }); } }; // Create objects for upload from file listing. S3Object[] objectsForUpload = new S3Object[filesToUpload.length]; for (int i = 0; i < filesToUpload.length; i++) { File file = filesToUpload[i]; log.debug("Computing MD5 hash for file: " + file); byte[] fileHash = ServiceUtils.computeMD5Hash(new ProgressMonitoredInputStream( // Report on MD5 hash progress. new FileInputStream(file), progressWatcher)); S3Object object = new S3Object(null, file); object.setMd5Hash(fileHash); objectsForUpload[i] = object; } // Obtain Gatekeeper response. SwingUtilities.invokeLater(new Runnable() { public void run() { progressStatusTextLabel.setText(replaceMessageVariables(uploaderProperties.getStringProperty( "screen.4.connectingMessage", "Missing property 'screen.4.connectingMessage'"))); progressBar.setValue(0); }; }); GatekeeperMessage gatekeeperMessage = null; try { gatekeeperMessage = retrieveGatekeeperResponse(objectsForUpload); } catch (Exception e) { log.info("Upload request was denied", e); failWithFatalError(ERROR_CODE__UPLOAD_REQUEST_DECLINED); return; } // If we get a null response, presume the error has already been handled. if (gatekeeperMessage == null) { return; } log.debug("Gatekeeper response properties: " + gatekeeperMessage.encodeToProperties()); XmlGenerator xmlGenerator = new XmlGenerator(); xmlGenerator.addApplicationProperties(gatekeeperMessage.getApplicationProperties()); xmlGenerator.addMessageProperties(gatekeeperMessage.getMessageProperties()); SignedUrlAndObject[] uploadItems = prepareSignedObjects(objectsForUpload, gatekeeperMessage.getSignatureRequests(), xmlGenerator); if (s3ServiceMulti == null) { s3ServiceMulti = new S3ServiceMulti(new RestS3Service(null, APPLICATION_DESCRIPTION, this), this); } /* * Prepare XML Summary document for upload, if the summary option is set. */ includeXmlSummaryDoc = uploaderProperties.getBoolProperty("xmlSummary", false); S3Object summaryXmlObject = null; if (includeXmlSummaryDoc) { String priorTransactionId = gatekeeperMessage.getMessageProperties() .getProperty(GatekeeperMessage.PROPERTY_TRANSACTION_ID); if (priorTransactionId == null) { failWithFatalError(ERROR_CODE__TRANSACTION_ID_REQUIRED_TO_CREATE_XML_SUMMARY); return; } summaryXmlObject = new S3Object(null, priorTransactionId + ".xml", xmlGenerator.generateXml()); summaryXmlObject.setContentType(Mimetypes.MIMETYPE_XML); summaryXmlObject.addMetadata(GatekeeperMessage.PROPERTY_TRANSACTION_ID, priorTransactionId); summaryXmlObject.addMetadata(GatekeeperMessage.SUMMARY_DOCUMENT_METADATA_FLAG, "true"); } // PUT the user's selected files in S3. uploadCancelled = false; uploadingFinalObject = (!includeXmlSummaryDoc); s3ServiceMulti.putObjects(uploadItems); // If an XML summary document is required, PUT this in S3 as well. if (includeXmlSummaryDoc && !uploadCancelled && !fatalErrorOccurred) { SwingUtilities.invokeLater(new Runnable() { public void run() { fileInformationLabel.setText(replaceMessageVariables( uploaderProperties.getStringProperty("screen.4.summaryFileInformation", "Missing property 'screen.4.summaryFileInformation'"))); progressStatusTextLabel.setText(replaceMessageVariables( uploaderProperties.getStringProperty("screen.4.connectingMessage", "Missing property 'screen.4.connectingMessage'"))); }; }); // Retrieve signed URL to PUT the XML summary document. gatekeeperMessage = retrieveGatekeeperResponse(new S3Object[] { summaryXmlObject }); SignedUrlAndObject[] xmlSummaryItem = prepareSignedObjects(new S3Object[] { summaryXmlObject }, gatekeeperMessage.getSignatureRequests(), null); // PUT the XML summary document. uploadingFinalObject = true; s3ServiceMulti.putObjects(xmlSummaryItem); } } catch (final Exception e) { priorFailureException = e; SwingUtilities.invokeLater(new Runnable() { public void run() { wizardStepBackward(); log.error("File upload failed", e); ErrorDialog.showDialog(ownerFrame, null, uploaderProperties.getProperties(), "File upload failed", e); }; }); } } private SignedUrlAndObject[] prepareSignedObjects(S3Object[] objects, SignatureRequest[] signatureRequests, XmlGenerator xmlGenerator) throws Exception { List signedObjects = new ArrayList(); String firstDeclineReason = null; for (int i = 0; i < signatureRequests.length; i++) { SignatureRequest request = signatureRequests[i]; S3Object object = objects[i]; // Store summary information in XML document generator. if (xmlGenerator != null) { Map clonedMetadata = new HashMap(); clonedMetadata.putAll(object.getMetadataMap()); xmlGenerator.addSignatureRequest(object.getKey(), object.getBucketName(), clonedMetadata, request); } if (request.isSigned()) { // Update object with any changes dictated by Gatekeeper. if (request.getObjectKey() != null) { object.setKey(request.getObjectKey()); } if (request.getBucketName() != null) { object.setBucketName(request.getBucketName()); } if (request.getObjectMetadata() != null && request.getObjectMetadata().size() > 0) { object.replaceAllMetadata(request.getObjectMetadata()); } SignedUrlAndObject urlAndObject = new SignedUrlAndObject(request.getSignedUrl(), object); signedObjects.add(urlAndObject); } else { // If ANY requests are declined, we will fail with a fatal error message. String declineReason = (request.getDeclineReason() == null ? "Unknown" : request.getDeclineReason()); log.warn("Upload of '" + objects[i].getKey() + "' was declined for reason: " + declineReason); if (firstDeclineReason == null) { firstDeclineReason = declineReason; } } } if (firstDeclineReason != null) { throw new Exception("Your upload" + (objects.length > 1 ? "s were" : " was") + " declined by the Gatekeeper. Reason: " + firstDeclineReason); } return (SignedUrlAndObject[]) signedObjects.toArray(new SignedUrlAndObject[signedObjects.size()]); } /** * Listener method that responds to events from the jets3t toolkit when objects are * created in S3 - ie when files are uploaded. */ public void s3ServiceEventPerformed(final CreateObjectsEvent event) { if (ServiceEvent.EVENT_STARTED == event.getEventCode()) { SwingUtilities.invokeLater(new Runnable() { public void run() { // Cancel button is enabled unless this upload is for the XML summary doc. boolean isXmlSummaryUpload = includeXmlSummaryDoc && uploadingFinalObject; cancelUploadButton.setEnabled(!isXmlSummaryUpload); } }); ThreadWatcher watcher = event.getThreadWatcher(); uploadCancelEventTrigger = watcher.getCancelEventListener(); // Show percentage of bytes transferred. String bytesTotalStr = byteFormatter.formatByteSize(watcher.getBytesTotal()); final String statusText = "Uploaded 0 of " + bytesTotalStr; SwingUtilities.invokeLater(new Runnable() { public void run() { progressStatusTextLabel.setText(replaceMessageVariables(statusText)); progressBar.setValue(0); } }); } else if (ServiceEvent.EVENT_IN_PROGRESS == event.getEventCode()) { ThreadWatcher watcher = event.getThreadWatcher(); if (watcher.getBytesTransferred() >= watcher.getBytesTotal()) { // Upload is completed, just waiting on resonse from S3. String statusText = "Upload completed, awaiting confirmation"; progressBar.setValue(100); progressStatusTextLabel.setText(replaceMessageVariables(statusText)); progressTransferDetailsLabel.setText(""); } else { String bytesCompletedStr = byteFormatter.formatByteSize(watcher.getBytesTransferred()); String bytesTotalStr = byteFormatter.formatByteSize(watcher.getBytesTotal()); String statusText = "Uploaded " + bytesCompletedStr + " of " + bytesTotalStr; int percentage = (int) (((double) watcher.getBytesTransferred() / watcher.getBytesTotal()) * 100); long bytesPerSecond = watcher.getBytesPerSecond(); String transferDetailsText = "Speed: " + byteFormatter.formatByteSize(bytesPerSecond) + "/s"; if (watcher.isTimeRemainingAvailable()) { long secondsRemaining = watcher.getTimeRemaining(); if (transferDetailsText.trim().length() > 0) { transferDetailsText += " - "; } transferDetailsText += "Time remaining: " + timeFormatter.formatTime(secondsRemaining); } progressBar.setValue(percentage); progressStatusTextLabel.setText(replaceMessageVariables(statusText)); progressTransferDetailsLabel.setText(replaceMessageVariables(transferDetailsText)); } } else if (ServiceEvent.EVENT_COMPLETED == event.getEventCode()) { if (uploadingFinalObject) { drawWizardScreen(WIZARD_SCREEN_5); } progressBar.setValue(0); progressStatusTextLabel.setText(""); progressTransferDetailsLabel.setText(""); SwingUtilities.invokeLater(new Runnable() { public void run() { cancelUploadButton.setEnabled(false); } }); } else if (ServiceEvent.EVENT_CANCELLED == event.getEventCode()) { progressBar.setValue(0); progressStatusTextLabel.setText(""); progressTransferDetailsLabel.setText(""); uploadCancelled = true; drawWizardScreen(WIZARD_SCREEN_3); SwingUtilities.invokeLater(new Runnable() { public void run() { cancelUploadButton.setEnabled(false); } }); } else if (ServiceEvent.EVENT_ERROR == event.getEventCode()) { progressBar.setValue(0); progressStatusTextLabel.setText(""); progressTransferDetailsLabel.setText(""); failWithFatalError(ERROR_CODE__S3_UPLOAD_FAILED); SwingUtilities.invokeLater(new Runnable() { public void run() { cancelUploadButton.setEnabled(false); } }); } } /** * Configures a button's text, tooltip and image using uploader properties prefixed * with the given properties prefix. * * @param button * @param propertiesPrefix */ private void configureButton(JButton button, String propertiesPrefix) { button.setHorizontalAlignment(JLabel.CENTER); String buttonImagePath = uploaderProperties.getStringProperty(propertiesPrefix + ".image", null); String buttonText = replaceMessageVariables( uploaderProperties.getStringProperty(propertiesPrefix + ".text", null)); String buttonTooltip = replaceMessageVariables( uploaderProperties.getStringProperty(propertiesPrefix + ".tooltip", null)); boolean hasImage = false; boolean hasText = false; if (buttonImagePath != null && buttonImagePath.length() > 0) { if (!guiUtils.applyIcon(button, buttonImagePath)) { log.error("Unable to load image URL for a button with property prefix '" + propertiesPrefix + "'. Image path: " + buttonImagePath); } else { hasImage = true; } } if (buttonText != null && buttonText.length() > 0) { String text = replaceMessageVariables(buttonText); button.setText(text); button.setMnemonic(text.charAt(0)); hasText = true; } if (buttonTooltip != null && buttonTooltip.length() > 0) { button.setToolTipText(buttonTooltip); } if (!hasImage && !hasText) { button.setVisible(false); } else { button.setVisible(true); } } /** * Draws the wizard screen appropriate to the stage in the wizard process the user has * reached. * * @param nextState * an integer detailing the screen the user is moving to. */ private void drawWizardScreen(int nextState) { // Configure screen based on properties. String title = uploaderProperties.getStringProperty("screen." + nextState + ".title", ""); userGuidanceLabel.setText(replaceMessageVariables(title)); configureButton(nextButton, "screen." + nextState + ".nextButton"); configureButton(backButton, "screen." + nextState + ".backButton"); this.getDropTarget().setActive(false); if (nextState == WIZARD_SCREEN_1) { primaryPanelCardLayout.show(primaryPanel, "screen1"); buttonsPanelCardLayout.show(buttonsPanel, "visible"); } else if (nextState == WIZARD_SCREEN_2) { userInputProperties = userInputFields.getUserInputsAsProperties(false); primaryPanelCardLayout.show(primaryPanel, "screen2"); dragDropTargetLabel.setText(replaceMessageVariables(uploaderProperties .getStringProperty("screen.2.dragDropPrompt", "Missing property 'screen.2.dragDropPrompt'"))); this.getDropTarget().setActive(true); } else if (nextState == WIZARD_SCREEN_3) { primaryPanelCardLayout.show(primaryPanel, "screen3"); String fileInformation = uploaderProperties.getStringProperty("screen.3.fileInformation", "Missing property 'screen.3.fileInformation'"); fileToUploadLabel.setText(replaceMessageVariables(fileInformation)); } else if (nextState == WIZARD_SCREEN_4) { primaryPanelCardLayout.show(primaryPanel, "screen4"); String fileInformation = uploaderProperties.getStringProperty("screen.4.fileInformation", "Missing property 'screen.4.fileInformation'"); fileInformationLabel.setText(replaceMessageVariables(fileInformation)); cancelUploadButton.setEnabled(false); new Thread() { @Override public void run() { uploadFilesToS3(); } }.start(); } else if (nextState == WIZARD_SCREEN_5) { primaryPanelCardLayout.show(primaryPanel, "screen5"); String finalMessage = null; if (fatalErrorOccurred) { finalMessage = uploaderProperties.getStringProperty("screen.5.errorMessage", "Missing property 'screen.5.errorMessage'"); } else { finalMessage = uploaderProperties.getStringProperty("screen.5.thankyouMessage", "Missing property 'screen.5.thankyouMessage'"); } finalMessageLabel.setText(replaceMessageVariables(finalMessage)); } else { log.error("Ignoring unexpected wizard screen number: " + nextState); return; } currentState = nextState; } /** * Move the wizard forward one step/screen. */ private void wizardStepForward() { drawWizardScreen(currentState + 1); } /** * Move the wizard backward one step/screen. */ private void wizardStepBackward() { drawWizardScreen(currentState - 1); } /** * When a fatal error occurs, go straight to last screen to display the error message * and make the error code available as a variable (<code>${errorCode}</code>) to be used * in the error message displayed to the user. * <p> * If there is an Uploader property <code>errorCodeMessage.<code></code> corresponding * to this error code, the value of this property is made available as a variable * (<code>${errorMessage}</code>). If there is no such property available the * <code>${errorMessage}</code> variable will be an empty string. * * * @param errorCode * the error code, which may correspond with an error message in uploader.properties. */ private void failWithFatalError(String errorCode) { uploaderProperties.setProperty("errorCode", errorCode); String errorCodeMessagePropertyName = "errorCodeMessage." + errorCode; String errorCodeMessage = uploaderProperties.getStringProperty(errorCodeMessagePropertyName, ""); uploaderProperties.setProperty("errorMessage", errorCodeMessage); fatalErrorOccurred = true; drawWizardScreen(WIZARD_SCREEN_5); } /** * Replaces variables of the form ${variableName} in the input string with the value of that * variable name in the local uploaderProperties properties object, or with one of the * following special variables: * <ul> * <li>fileName : Name of file being uploaded</li> * <li>fileSize : Size of the file being uploaded, eg 1.04 MB</li> * <li>filePath : Absolute path of the file being uploaded</li> * <li>maxFileSize : The maxiumum allowed file size in MB</li> * <li>maxFileCount : The maximum number of files that may be uploaded</li> * <li>validFileExtensions : A list of the file extensions allowed</li> * </ul> * If the variable named in the input string is not available, or has no value, the variable * reference is left in the result. * * @param message * string that may have variables to replace * @return * the input string with any variable referenced replaced with the variable's value. */ private String replaceMessageVariables(String message) { if (message == null) { return ""; } String result = message; // Replace upload file variables, if an upload file has been chosen. if (filesToUpload != null) { long filesSize = 0; StringBuffer fileNameList = new StringBuffer(); for (int i = 0; i < filesToUpload.length; i++) { filesSize += filesToUpload[i].length(); fileNameList.append(filesToUpload[i].getName()).append(" "); } result = result.replaceAll("\\$\\{fileNameList\\}", fileNameList.toString()); result = result.replaceAll("\\$\\{filesSize\\}", byteFormatter.formatByteSize(filesSize)); } result = result.replaceAll("\\$\\{maxFileSize\\}", String.valueOf(fileMaxSizeMB)); result = result.replaceAll("\\$\\{maxFileCount\\}", String.valueOf(fileMaxCount)); String extList = validFileExtensions.toString(); extList = extList.substring(1, extList.length() - 1); extList = extList.replaceAll(",", " "); result = result.replaceAll("\\$\\{validFileExtensions\\}", extList); Pattern pattern = Pattern.compile("\\$\\{.+?\\}"); Matcher matcher = pattern.matcher(result); int offset = 0; while (matcher.find(offset)) { String variable = matcher.group(); String variableName = variable.substring(2, variable.length() - 1); String replacement = null; if (userInputProperties != null && userInputProperties.containsKey(variableName)) { log.debug("Replacing variable '" + variableName + "' with value from a user input field"); replacement = userInputProperties.getProperty(variableName, null); } else if (parametersMap != null && parametersMap.containsKey(variableName)) { log.debug("Replacing variable '" + variableName + "' with value from Uploader's parameters"); replacement = (String) parametersMap.get(variableName); } else if (uploaderProperties != null && uploaderProperties.containsKey(variableName)) { log.debug("Replacing variable '" + variableName + "' with value from uploader.properties file"); replacement = uploaderProperties.getStringProperty(variableName, null); } if (replacement != null) { result = result.substring(0, matcher.start()) + replacement + result.substring(matcher.end()); offset = matcher.start() + 1; matcher.reset(result); } else { offset = matcher.start() + 1; } } if (!result.equals(message)) { log.debug("Replaced variables in text: " + message + " => " + result); } return result; } /** * Handles GUI actions. */ public void actionPerformed(ActionEvent actionEvent) { if ("Next".equals(actionEvent.getActionCommand())) { wizardStepForward(); } else if ("Back".equals(actionEvent.getActionCommand())) { wizardStepBackward(); } else if ("ChooseFile".equals(actionEvent.getActionCommand())) { JFileChooser fileChooser = new JFileChooser(); if (validFileExtensions.size() > 0) { UploaderFileExtensionFilter filter = new UploaderFileExtensionFilter("Allowed files", validFileExtensions); fileChooser.setFileFilter(filter); } fileChooser.setMultiSelectionEnabled(fileMaxCount > 1); fileChooser.setDialogTitle("Choose file" + (fileMaxCount > 1 ? "s" : "") + " to upload"); fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); fileChooser.setApproveButtonText("Choose file" + (fileMaxCount > 1 ? "s" : "")); int returnVal = fileChooser.showOpenDialog(ownerFrame); if (returnVal != JFileChooser.APPROVE_OPTION) { return; } List fileList = new ArrayList(); if (fileChooser.getSelectedFiles().length > 0) { fileList.addAll(Arrays.asList(fileChooser.getSelectedFiles())); } else { fileList.add(fileChooser.getSelectedFile()); } if (checkProposedUploadFiles(fileList)) { wizardStepForward(); } } else if ("CancelUpload".equals(actionEvent.getActionCommand())) { if (uploadCancelEventTrigger != null) { uploadCancelEventTrigger.cancelTask(this); progressBar.setValue(0); } else { log.warn("Ignoring attempt to cancel file upload when cancel trigger is not available"); } } else { log.warn("Unrecognised action command, ignoring: " + actionEvent.getActionCommand()); } } private HttpClient initHttpConnection() { // Set client parameters. HttpParams params = RestUtils.createDefaultHttpParams(); HttpProtocolParams.setUserAgent(params, ServiceUtils.getUserAgentDescription(APPLICATION_DESCRIPTION)); // Set connection parameters. HttpConnectionParams.setConnectionTimeout(params, HTTP_CONNECTION_TIMEOUT); HttpConnectionParams.setSoTimeout(params, SOCKET_CONNECTION_TIMEOUT); HttpConnectionParams.setStaleCheckingEnabled(params, false); DefaultHttpClient httpClient = new DefaultHttpClient(params); // Replace default error retry handler. httpClient.setHttpRequestRetryHandler(new RestUtils.JetS3tRetryHandler(MAX_CONNECTION_RETRIES, null)); return httpClient; } /** * Follows hyperlinks clicked on by a user. This is achieved differently depending on whether * Cockpit is running as an applet or as a stand-alone application: * <ul> * <li>Application: Detects the default browser application for the user's system (using * <tt>BareBonesBrowserLaunch</tt>) and opens the link as a new window in that browser</li> * <li>Applet: Opens the link in the current browser using the applet's context</li> * </ul> * * @param url * the url to open * @param target * the target pane to open the url in, eg "_blank". This may be null. */ public void followHyperlink(URL url, String target) { if (isRunningAsApplet) { if (target == null) { getAppletContext().showDocument(url); } else { getAppletContext().showDocument(url, target); } } else { BareBonesBrowserLaunch.openURL(url.toString()); } } public void setCredentials(AuthScope authscope, Credentials credentials) { mCredentialProvider.setCredentials(authscope, credentials); } /** * Clear credentials. */ public void clear() { mCredentialProvider.clear(); } /** * Implementation method for the CredentialsProvider interface. * <p> * Based on sample code: * <a href="http://svn.apache.org/viewvc/jakarta/commons/proper/httpclient/trunk/src/examples/InteractiveAuthenticationExample.java?view=markup">InteractiveAuthenticationExample</a> * */ public Credentials getCredentials(AuthScope scope) { if (scope == null || scope.getScheme() == null) { return null; } Credentials credentials = mCredentialProvider.getCredentials(scope); if (credentials != null) { return credentials; } try { if (scope.getScheme().equals("ntlm")) { //if (authscheme instanceof NTLMScheme) { AuthenticationDialog pwDialog = new AuthenticationDialog(ownerFrame, "Authentication Required", "<html>Host <b>" + scope.getHost() + ":" + scope.getPort() + "</b> requires Windows authentication</html>", true); pwDialog.setVisible(true); if (pwDialog.getUser().length() > 0) { credentials = new NTCredentials(pwDialog.getUser(), pwDialog.getPassword(), scope.getHost(), pwDialog.getDomain()); } pwDialog.dispose(); } else if (scope.getScheme().equals("basic") || scope.getScheme().equals("digest")) { //if (authscheme instanceof RFC2617Scheme) { AuthenticationDialog pwDialog = new AuthenticationDialog(ownerFrame, "Authentication Required", "<html><center>Host <b>" + scope.getHost() + ":" + scope.getPort() + "</b>" + " requires authentication for the realm:<br><b>" + scope.getRealm() + "</b></center></html>", false); pwDialog.setVisible(true); if (pwDialog.getUser().length() > 0) { credentials = new UsernamePasswordCredentials(pwDialog.getUser(), pwDialog.getPassword()); } pwDialog.dispose(); } else { throw new IllegalArgumentException("Unsupported authentication scheme: " + scope.getScheme()); } if (credentials != null) { mCredentialProvider.setCredentials(scope, credentials); } return credentials; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage(), e); } } // S3 Service events that are not used in this Uploader application. public void s3ServiceEventPerformed(ListObjectsEvent event) { } public void s3ServiceEventPerformed(CreateBucketsEvent event) { } public void s3ServiceEventPerformed(DeleteObjectsEvent event) { } public void s3ServiceEventPerformed(GetObjectsEvent event) { } public void s3ServiceEventPerformed(GetObjectHeadsEvent event) { } public void s3ServiceEventPerformed(LookupACLEvent event) { } public void s3ServiceEventPerformed(UpdateACLEvent event) { } public void s3ServiceEventPerformed(DownloadObjectsEvent event) { } public void s3ServiceEventPerformed(CopyObjectsEvent event) { } public void s3ServiceEventPerformed(DeleteVersionedObjectsEvent event) { } public void valueChanged(ListSelectionEvent arg0) { } /** * Run the Uploader as a stand-alone application. * * @param args * @throws Exception */ public static void main(String args[]) throws Exception { JFrame ownerFrame = new JFrame("JetS3t Uploader"); ownerFrame.addWindowListener(new WindowListener() { public void windowOpened(WindowEvent e) { } public void windowClosing(WindowEvent e) { e.getWindow().dispose(); } public void windowClosed(WindowEvent e) { } public void windowIconified(WindowEvent e) { } public void windowDeiconified(WindowEvent e) { } public void windowActivated(WindowEvent e) { } public void windowDeactivated(WindowEvent e) { } }); // Read arguments as properties of the form: <propertyName>'='<propertyValue> Properties argumentProperties = new Properties(); if (args.length > 0) { for (int i = 0; i < args.length; i++) { String arg = args[i]; int delimIndex = arg.indexOf("="); if (delimIndex >= 0) { String name = arg.substring(0, delimIndex); String value = arg.substring(delimIndex + 1); argumentProperties.put(name, value); } else { System.out.println("Ignoring property argument with incorrect format: " + arg); } } } new Uploader(ownerFrame, argumentProperties); } }