edu.ycp.cs.marmoset.uploader.handlers.SubmitProjectHandler.java Source code

Java tutorial

Introduction

Here is the source code for edu.ycp.cs.marmoset.uploader.handlers.SubmitProjectHandler.java

Source

// Simple Marmoset Uploader
// Copyright (C) 2011-2014, David H. Hovemeyer <david.hovemeyer@gmail.com>
//
// Free software, distributed under the terms of the Apache License, version 2.0
// See: http://www.apache.org/licenses/LICENSE-2.0.html

package edu.ycp.cs.marmoset.uploader.handlers;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.http.HttpStatus;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.ISelectionService;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.handlers.HandlerUtil;

import edu.ycp.cs.marmoset.uploader.Activator;
import edu.ycp.cs.marmoset.uploader.ui.UsernamePasswordDialog;

/**
 * Command handler for Marmoset project submission.
 * 
 * @see org.eclipse.core.commands.IHandler
 * @see org.eclipse.core.commands.AbstractHandler
 * @author David Hovemeyer
 */
public class SubmitProjectHandler extends AbstractHandler {
    static final String PROP_SUBMIT_URL = "submitURL";
    private static final String PROP_SEMESTER = "semester";
    private static final String PROP_COURSE_NAME = "courseName";
    private static final String PROP_PROJECT_NUMBER = "projectNumber";

    static String[] REQUIRED_PROPERTIES = new String[] { PROP_PROJECT_NUMBER, PROP_COURSE_NAME, PROP_SEMESTER,
            PROP_SUBMIT_URL };

    /**
     * Regex pattern matching the submit url in a .submit file.
     * All we care about is the hostname and context path, since we will force submission via
     * the BlueJ upload servlet.
     */
    static final Pattern SUBMIT_URL_PATTERN = Pattern.compile("^(https?://([^/]+))(/.*)$");

    /**
     * The constructor.
     */
    public SubmitProjectHandler() {
    }

    /**
     * the command has been executed, so extract extract the needed information
     * from the application context.
     */
    public Object execute(ExecutionEvent event) throws ExecutionException {
        IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindowChecked(event);

        List<IProject> selectedProjects = null;

        ISelectionService selectionService = HandlerUtil.getActiveWorkbenchWindow(event).getSelectionService();
        ISelection selection = selectionService.getSelection();

        if (selection instanceof IStructuredSelection) {
            // Accumulate selected projects
            selectedProjects = getSelectedProjects((IStructuredSelection) selection);
        }

        if (selectedProjects == null || selectedProjects.isEmpty()) {
            MessageDialog.openError(window.getShell(), "No project selected",
                    "Please select a project in the package explorer.");
            return null;
        }

        if (selectedProjects.size() > 1) {
            MessageDialog.openError(window.getShell(), "Multiple projects selected",
                    "Please select a single project in the package explorer.");
            return null;
        }

        IProject project = selectedProjects.get(0);

        IFile dotSubmit = project.getFile(".submit");
        if (!dotSubmit.exists()) {
            MessageDialog.openError(window.getShell(), "No submit file",
                    "This project does not contain project submission information. Sorry.");
            return null;
        }

        // Read the .submit file.
        Properties submitProperties = new Properties();
        InputStream in = null;
        try {
            in = dotSubmit.getContents();
            submitProperties.load(in);
            checkSubmitProperties(submitProperties);
        } catch (Exception e) {
            MessageDialog.openError(window.getShell(), "Error reading submit file",
                    "An error occurred reading the project submission information. Sorry.\n" + e.getMessage());
            return null;
        } finally {
            IOUtil.closeQuietly(in);
        }

        // Get username and password.
        int rc;
        UsernamePasswordDialog dialog = new UsernamePasswordDialog(window.getShell(),
                submitProperties.getProperty(PROP_PROJECT_NUMBER), submitProperties.getProperty(PROP_COURSE_NAME),
                submitProperties.getProperty(PROP_SEMESTER));
        rc = dialog.open();
        if (rc != IDialogConstants.OK_ID) {
            return null; // canceled
        }

        // Set the project number (i.e., inbox): if there were multiple possibilities,
        // the UsernamePasswordDialog will have required the user to choose one
        submitProperties.setProperty(PROP_PROJECT_NUMBER, dialog.getSelectedInbox());

        File zipFile = null;

        try {
            zipFile = createZipFile(project);
        } catch (IOException e) {
            //         throw new ExecutionException("Could not create zip file of project", e);
            MessageDialog.openError(window.getShell(), "Error creating project zip file",
                    "Error creating a zip file of your project\n" + e.getMessage());
            return null;
        } catch (CoreException e) {
            //         throw new ExecutionException("Could not create zip file of project", e);
            MessageDialog.openError(window.getShell(), "Error creating project zip file",
                    "Error creating a zip file of your project (try refreshing the project)\n\n" + e.getMessage());
            return null;
        }

        //  Attempt the submission.
        try {
            uploadToServer(window, submitProperties, dialog, zipFile);
            return null;
        } finally {
            if (zipFile != null) {
                // delete eagerly (even though we've marked it delete-on-exit)
                zipFile.delete();
            }
        }

    }

    public void uploadToServer(IWorkbenchWindow window, Properties submitProperties, UsernamePasswordDialog dialog,
            File zipFile) {
        try {
            Result result;
            result = Uploader.sendZipFileToServer(submitProperties, zipFile, dialog.getUsername(),
                    dialog.getPassword());

            if (result.httpCode == HttpStatus.SC_OK) {
                // Success!
                MessageDialog.openInformation(window.getShell(), "Upload result", result.responseBody);
            } else {
                if (result.responseBody.contains("Wrong password")) {
                    MessageDialog.openError(window.getShell(), "Project submission failed",
                            "Project submission failed\nYour password was not recognized (did you mistype it?)");
                } else if (result.responseBody.contains("Cannot find user")) {
                    MessageDialog.openError(window.getShell(), "Project submission failed",
                            "Project submission failed\nYour username was not recognized (did you mistype it?)");
                } else {
                    MessageDialog.openError(window.getShell(), "Project submission failed",
                            "An error occurred while uploading your project. Sorry.");
                    // Log it.
                    Activator.getDefault().getLog().log(new Status(Status.ERROR, Activator.PLUGIN_ID,
                            "Error submitting project: " + result.responseBody));
                }
            }
        } catch (IOException e) {
            MessageDialog.openError(window.getShell(), "Error uploading project", e.getMessage());
        }
    }

    private List<IProject> getSelectedProjects(IStructuredSelection selection) {
        List<IProject> selectedProjects = new ArrayList<IProject>();
        List<?> selectedItems = selection.toList();
        for (Object selectedItem : selectedItems) {
            if (selectedItem instanceof IAdaptable) {
                IAdaptable adaptable = (IAdaptable) selectedItem;
                IProject project = (IProject) adaptable.getAdapter(IProject.class);
                if (project != null) {
                    selectedProjects.add(project);
                }

            }
        }
        return selectedProjects;
    }

    private void checkSubmitProperties(Properties submitProperties) {
        for (String prop : REQUIRED_PROPERTIES) {
            if (submitProperties.getProperty(prop) == null) {
                throw new IllegalArgumentException("Missing required " + prop + " property");
            }
        }

        // Check submit URL
        String submitUrl = submitProperties.getProperty(PROP_SUBMIT_URL);
        Matcher m = SUBMIT_URL_PATTERN.matcher(submitUrl);
        if (!m.matches()) {
            throw new IllegalArgumentException("Invalid submit URL: " + submitUrl);
        }
    }

    private File createZipFile(IProject project) throws IOException, CoreException {
        File zipFile = File.createTempFile("marmosetSubmit", ".zip");
        zipFile.deleteOnExit();

        ZipOutputStream out = null;

        try {
            out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));

            recursivelyAddToZipFile(project, out);
        } finally {
            IOUtil.closeQuietly(out);
        }

        return zipFile;
    }

    private void recursivelyAddToZipFile(IResource resource, ZipOutputStream out)
            throws CoreException, IOException {
        if (resource instanceof IContainer) {
            IContainer container = (IContainer) resource;
            IResource[] children = container.members();

            for (IResource child : children) {
                recursivelyAddToZipFile(child, out);
            }
        } else if (resource instanceof IFile) {
            IFile file = (IFile) resource;

            IPath relativePath = resource.getProjectRelativePath();
            ZipEntry entry = new ZipEntry(relativePath.toPortableString());
            out.putNextEntry(entry);

            InputStream fileIn = null;

            try {
                fileIn = file.getContents();

                IOUtil.copy(fileIn, out);
            } finally {
                IOUtil.closeQuietly(fileIn);
            }
        }

    }
}