package bluej.collect;
import javax.swing.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javafx.application.Platform;
import bluej.Boot;
import bluej.Config;
import bluej.extensions.event.ApplicationEvent;
import bluej.extmgr.ExtensionsManager;
import bluej.pkgmgr.Project;
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;
import threadchecker.OnThread;
import threadchecker.Tag;
| 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 = "https://blackbox.bluej.org/master_events";
private static AtomicBoolean givenUp = new AtomicBoolean(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
|
p.public static void submitEvent(Event evt)
{
if (givenUp.get())
return;
synchronized (queue) {
queue.add(evt);
if (! isRunning) {
new Thread() {
@OnThread(value = Tag.Worker, ignoreParent = true)
public void run()
{
processQueue();
}
}.start();
isRunning = true;
}
}
}
| Process the queue of items to be posted to the server.
|
@OnThread(Tag.Worker)
private static void processQueue()
{
while (true){
Event evt;
synchronized (queue) {
if (queue.isEmpty()) {
isRunning = false;
queue.notifyAll();
return;
}
evt = queue.remove(0);
}
if (!givenUp.get())
{
givenUp.set(!postData(evt));
if (givenUp.get())
{
SwingUtilities.invokeLater(() -> {
ExtensionsManager.getInstance().delegateEvent(new ApplicationEvent(ApplicationEvent.DATA_SUBMISSION_FAILED_EVENT));
Platform.runLater(() ->
{
if (Boot.isTrialRecording())
{
if (!Config.isGreenfoot())
new DataSubmissionFailedDialog().show();
Project.getProjects().forEach(project -> project.setAllEditorStatus(" - NOT RECORDING"));
}
});
});
}
}
}
}
| Actually post the data to the server.
|
| Returns false if there was an error.
|
@OnThread(Tag.Worker)
private static boolean postData(Event evt)
{
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(params, Boot.isTrialRecording() ? 30000 : 10000);
HttpConnectionParams.setSoTimeout(params, Boot.isTrialRecording() ? 30000 : 10000);
HttpClient client = new DefaultHttpClient(params);
try {
HttpPost post = new HttpPost(submitUrl);
MultipartEntity mpe = evt.makeData(sequenceNum, fileVersions);
if (mpe == null)
{
return true;
}
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()))
{
return false;
}
}
if (response.getStatusLine().getStatusCode() != 200)
{
return false;
}
evt.success(fileVersions);
EntityUtils.consume(response.getEntity());
}
catch (ClientProtocolException cpe) {
return false;
}
catch (IOException ioe) {
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)
{
while (!queue.isEmpty() || isRunning)
{
long waitTime = endTime - System.currentTimeMillis();
if (waitTime <= 0) {
break;
}
queue.wait(waitTime);
}
}
}
catch (InterruptedException e)
{
}
}
public static void initSequence()
{
sequenceNum = 1;
}
public static boolean hasGivenUp()
{
return givenUp.get();
}
}
. - DataSubmitter
. submitEvent
. run
. processQueue
. postData
. waitForQueueFlush
. initSequence
. hasGivenUp
256 neLoCode
+ 16 LoComm