bluej.collect.DataSubmitter.java Source code

Java tutorial

Introduction

Here is the source code for bluej.collect.DataSubmitter.java

Source

/*
 This file is part of the BlueJ program. 
 Copyright (C) 2013  Michael Kolling and John Rosenberg 
     
 This program 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 2 
 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, write to the Free Software 
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. 
     
 This file is subject to the Classpath exception as provided in the  
 LICENSE.txt file that accompanied this code.
 */
package bluej.collect;

import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;

/**
 * This class that handles submitting compilation data to the remote server.
 * 
 * The class has nothing to do with collecting the data, and deliberately
 * does not depend on any other BlueJ classes.  Package-visible.
 * 
 * @author Davin McCall
 */
class DataSubmitter {
    private static final String submitUrl = "http://blackbox.bluej.org/master_events";
    //For testing: "http://localhost:3000/master_events"; 

    private static volatile boolean givenUp = false;

    /**
     * isRunning is only touched while synchonized on queue
     */
    private static boolean isRunning = false;

    private static List<Event> queue = new LinkedList<Event>();

    private static int sequenceNum;

    /**
     * The versions of the files as we have last sent them to the server.
     * 
     * Should only be accessed by the postData method, which is running on
     * the event-sending thread
     */
    private static Map<FileKey, List<String>> fileVersions = new HashMap<FileKey, List<String>>();

    /**
     * Submit data to be posted to the server. The data is added to a queue which is processed by
     * another thread.
     * 
     * Package-visible, only used by DataCollector
     */
    static void submitEvent(Event evt) {
        //This thread only reads the boolean, and is an optimisation:
        if (givenUp)
            return;

        synchronized (queue) {
            queue.add(evt);

            if (!isRunning) {
                new Thread() {
                    public void run() {
                        processQueue();
                    }
                }.start();
                isRunning = true;
            }
        }
    }

    /**
     * Process the queue of items to be posted to the server.
     */
    private static void processQueue() {
        while (true) {
            Event evt;
            synchronized (queue) {
                if (queue.isEmpty()) {
                    isRunning = false;
                    queue.notifyAll(); // in case anyone is waiting for us to finish
                    return;
                }
                evt = queue.remove(0);
            }

            if (!givenUp) {
                givenUp = !postData(evt);
            }
        }
    }

    /**
     * Actually post the data to the server.
     * 
     * Returns false if there was an error.
     */
    private static boolean postData(Event evt) {
        HttpParams params = new BasicHttpParams();
        HttpConnectionParams.setConnectionTimeout(params, 10000);
        HttpConnectionParams.setSoTimeout(params, 10000);
        HttpClient client = new DefaultHttpClient(params);

        try {
            HttpPost post = new HttpPost(submitUrl);

            /*
            if (errFilePath != null) {
            mpe.addPart("filepath", new StringBody(errFilePath, utf8));
            }
            if (errMsg != null) {
            mpe.addPart("errormsg", new StringBody(errMsg, utf8));
            }
            if (errline != 0) {
            mpe.addPart("errorline", new StringBody("" + errline, utf8));
            }
                
            int i = 0;
            for (SourceContent changedFile : changedFiles) {
            mpe.addPart("sourcefileName" + i, new StringBody(changedFile.getFilePath(), utf8));
            mpe.addPart("sourcefileContent" + i, new ByteArrayBody(changedFile.getFileContent(),
                    changedFile.getFilePath()));
            }
            */
            MultipartEntity mpe = evt.makeData(sequenceNum, fileVersions);
            if (mpe == null)
                return true; // nothing to send, no error
            //Only increment sequence number if we actually send data:
            sequenceNum += 1;
            post.setEntity(mpe);
            HttpResponse response = client.execute(post);

            for (Header h : response.getAllHeaders()) {
                if ("X-Status".equals(h.getName()) && !"Created".equals(h.getValue())) {
                    // Temporary printing:
                    System.err.println("Problem response: " + mpe.toString() + " " + response.toString());
                    return false;
                }
            }

            if (response.getStatusLine().getStatusCode() != 200) {
                System.err.println("Problem code: " + response.getStatusLine().getStatusCode());
                return false;
            }

            evt.success(fileVersions);

            EntityUtils.consume(response.getEntity());
        } catch (ClientProtocolException cpe) {
            return false;
        } catch (IOException ioe) {
            //For now:
            ioe.printStackTrace();

            return false;
        }

        return true;
    }

    /**
     * Waits until all pending events have been sent to the server, or the timeout expires.  If events are still being added in parallel
     * to this call, there will be undefined behaviour.
     */
    public static void waitForQueueFlush(int maxMillis) {
        final long endTime = System.currentTimeMillis() + maxMillis;

        try {
            synchronized (queue) {
                // Keep waiting if there is anything in the queue,
                // or the queue is empty but the submitter thread is still running.
                while (!queue.isEmpty() || isRunning) {
                    long waitTime = endTime - System.currentTimeMillis();
                    if (waitTime <= 0) {
                        break;
                    }
                    queue.wait(waitTime);
                }
            }
        } catch (InterruptedException e) {
            //Just finish anyway...
        }
    }

    public static void initSequence() {
        sequenceNum = 1; //Server relies on it starting at 1, do not change

    }
}