edu.wisc.ssec.mcidasv.supportform.Submitter.java Source code

Java tutorial

Introduction

Here is the source code for edu.wisc.ssec.mcidasv.supportform.Submitter.java

Source

/*
 * This file is part of McIDAS-V
 *
 * Copyright 2007-2019
 * Space Science and Engineering Center (SSEC)
 * University of Wisconsin - Madison
 * 1225 W. Dayton Street, Madison, WI 53706, USA
 * http://www.ssec.wisc.edu/mcidas
 * 
 * All Rights Reserved
 * 
 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
 * some McIDAS-V source code is based on IDV and VisAD source code.  
 * 
 * McIDAS-V is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 * 
 * McIDAS-V 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 Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 */
package edu.wisc.ssec.mcidasv.supportform;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.PartSource;
import org.apache.commons.httpclient.methods.multipart.StringPart;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ucar.unidata.util.IOUtil;
//import ucar.unidata.util.Misc;
import ucar.unidata.util.WrapperException;

import edu.wisc.ssec.mcidasv.util.BackgroundTask;

/**
 * Abstraction of a background thread that is used to submit support requests
 * to the McIDAS-V Help Desk Team.
 */
public class Submitter extends BackgroundTask<String> {

    /** Error message to display if the server had problems. */
    public static final String POST_ERROR = "Server encountered an error while attempting to forward message to mug@ssec.wisc.edu.\n\nPlease try sending email in your email client to mug@ssec.wisc.edu. We apologize for the inconvenience.";

    /** Logging object. */
    private static final Logger logger = LoggerFactory.getLogger(Submitter.class);

    /** We'll follow up to this many redirects for {@code requestUrl}. */
    private static final int POST_ATTEMPTS = 5;

    /** Used to gather user input and system information. */
    private final SupportForm form;

    /** URL that we'll attempt to {@code POST} our requests at.*/
    private final String requestUrl = "https://www.ssec.wisc.edu/mcidas/misc/mc-v/supportreq/support.php";

    /** Keeps track of the most recent redirect for {@code requestUrl}. */
    private String validFormUrl = requestUrl;

    /** Number of redirects we've tried since starting. */
    private int tryCount = 0;

    /** Handy reference to the status code (and more) of our {@code POST}. */
    private PostMethod method = null;

    /**
     * Prepare a support request to be sent (off of the event dispatch thread).
     * 
     * @param form Support request form to send. Cannot be {@code null}.
     */
    public Submitter(final SupportForm form) {
        this.form = form;
    }

    /** 
     * Creates a file attachment that's based upon a real file.
     * 
     * @param id The parameter ID. Usually something like 
     * {@literal "form_data[att_two]"}.
     * @param file Path to the file that's going to be attached.
     * 
     * @return {@code POST}-able file attachment using the name and contents of
     * {@code file}.
     */
    private static FilePart buildRealFilePart(final String id, final String file) {
        return new FilePart(id, new PartSource() {
            public InputStream createInputStream() {
                try {
                    return IOUtil.getInputStream(file);
                } catch (Exception e) {
                    throw new WrapperException("Reading file: " + file, e);
                }
            }

            public String getFileName() {
                return new File(file).getName();
            }

            public long getLength() {
                return new File(file).length();
            }
        });
    }

    /**
     * Creates a file attachment that isn't based upon an actual file. Useful 
     * for something like the {@literal "extra"} attachment where you collect
     * a bunch of data but don't want to deal with creating a temporary file.
     * 
     * @param id Parameter ID. Typically something like 
     * {@literal "form_data[att_extra]"}.
     * @param file Fake name of the file. Can be whatever you like.
     * @param data The actual data to place inside the attachment.
     * 
     * @return {@code POST}-able file attachment using a spoofed filename!
     */
    private static FilePart buildFakeFilePart(final String id, final String file, final byte[] data) {
        return new FilePart(id, new PartSource() {
            public InputStream createInputStream() {
                return new ByteArrayInputStream(data);
            }

            public String getFileName() {
                return file;
            }

            public long getLength() {
                return data.length;
            }
        });
    }

    /**
     * Attempts to {@code POST} to {@code url} using the information from 
     * {@code form}.
     * 
     * @param url URL that'll accept the {@code POST}. Typically 
     * {@link #requestUrl}.
     * @param form The {@link SupportForm} that contains the data to use in the
     * support request.
     * 
     * @return Big honkin' object that contains the support request.
     */
    private static PostMethod buildPostMethod(String url, SupportForm form) {
        PostMethod method = new PostMethod(url);

        List<Part> parts = new ArrayList<Part>();
        parts.add(new StringPart("form_data[fromName]", form.getUser()));
        parts.add(new StringPart("form_data[email]", form.getEmail()));
        parts.add(new StringPart("form_data[organization]", form.getOrganization()));
        parts.add(new StringPart("form_data[subject]", form.getSubject()));
        parts.add(new StringPart("form_data[description]", form.getDescription()));
        parts.add(new StringPart("form_data[submit]", ""));
        parts.add(new StringPart("form_data[p_version]", "p_version=ignored"));
        parts.add(new StringPart("form_data[opsys]", "opsys=ignored"));
        parts.add(new StringPart("form_data[hardware]", "hardware=ignored"));
        parts.add(new StringPart("form_data[cc_user]", Boolean.toString(form.getSendCopy())));

        // attach the files the user has explicitly attached.
        if (form.hasAttachmentOne()) {
            parts.add(buildRealFilePart("form_data[att_two]", form.getAttachmentOne()));
        }
        if (form.hasAttachmentTwo()) {
            parts.add(buildRealFilePart("form_data[att_three]", form.getAttachmentTwo()));
        }
        // if the user wants, attach an XML bundle of the state
        if (form.canBundleState() && form.getSendBundle()) {
            parts.add(
                    buildFakeFilePart("form_data[att_state]", form.getBundledStateName(), form.getBundledState()));
        }

        // attach system properties
        parts.add(buildFakeFilePart("form_data[att_extra]", form.getExtraStateName(), form.getExtraState()));

        // attach mcidasv.log (if it exists)
        if (form.canSendLog()) {
            parts.add(buildRealFilePart("form_data[att_log]", form.getLogPath()));
        }

        // attach RESOLV.SRV (if it exists)
        if (form.canSendResolvSrv()) {
            parts.add(buildRealFilePart("form_data[att_resolvsrv]", form.getResolvSrvPath()));
        }

        // tack on the contents of runMcV.prefs
        parts.add(buildRealFilePart("form_data[att_prefs]", form.getPrefsPath()));

        Part[] arr = parts.toArray(new Part[0]);
        MultipartRequestEntity mpr = new MultipartRequestEntity(arr, method.getParams());
        method.setRequestEntity(mpr);
        return method;
    }

    /**
     * Attempt to POST contents of support request form to {@link #requestUrl}.
     * 
     * @throws WrapperException if there was a problem on the server.
     */
    protected String compute() {
        // logic ripped from the IDV's HttpFormEntry#doPost(List, String)
        try {
            while ((tryCount++ < POST_ATTEMPTS) && !isCancelled()) {
                method = buildPostMethod(validFormUrl, form);
                HttpClient client = new HttpClient();
                client.executeMethod(method);
                if (method.getStatusCode() >= 300 && method.getStatusCode() <= 399) {
                    Header location = method.getResponseHeader("location");
                    if (location == null) {
                        return "Error: No 'location' given on the redirect";
                    }
                    validFormUrl = location.getValue();
                    if (method.getStatusCode() == 301) {
                        logger.warn("form post has been permanently moved to: {}", validFormUrl);
                    }
                    continue;
                }
                break;
            }
            return IOUtil.readContents(method.getResponseBodyAsStream());
        } catch (Exception e) {
            throw new WrapperException(POST_ERROR, e);
        }
    }

    //    protected String compute() {
    //        try {
    //            Misc.sleep(2000);
    //            return "dummy success!";
    //        } catch (Exception e) {
    //            throw new WrapperException(POST_ERROR, e);
    //        }
    //    }

    /**
     * Handles completion of a support request.
     * 
     * @param result Result of {@link #compute()}.
     * @param exception Exception thrown from {@link #compute()}, if any.
     * @param cancelled Whether or not the user opted to cancel.
     */
    @Override
    protected void onCompletion(String result, Throwable exception, boolean cancelled) {
        logger.trace("result={} exception={} cancelled={}", new Object[] { result, exception, cancelled });
        if (cancelled) {
            return;
        }

        if (exception == null) {
            form.showSuccess();
        } else {
            form.showFailure(exception.getMessage());
        }
    }

}