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