Skip to content

Commit 36459b7

Browse files
committed
Fix bugs and add doc.
1 parent 5b47d2b commit 36459b7

File tree

3 files changed

+112
-63
lines changed

3 files changed

+112
-63
lines changed

tmc-plugin/src/fi/helsinki/cs/tmc/model/CourseDb.java

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,20 @@
1818
import java.util.Map;
1919
import java.util.logging.Level;
2020
import java.util.logging.Logger;
21+
import javax.swing.SwingUtilities;
2122

2223
/**
23-
* Stores the list of available courses, the current course and its exercise list.
24+
* Stores the list of available courses, the current course and its exercise
25+
* list.
2426
*/
2527
public class CourseDb {
2628

27-
public static class ChangedEvent implements TmcEvent {}
28-
29+
public static class ChangedEvent implements TmcEvent {
30+
}
31+
2932
public static final Logger logger = Logger.getLogger(CourseDb.class.getName());
3033
private static CourseDb defaultInstance;
31-
34+
3235
public static CourseDb getInstance() {
3336
if (defaultInstance == null) {
3437
defaultInstance = new CourseDb();
@@ -45,7 +48,7 @@ public static CourseDb getInstance() {
4548
private CourseDb() {
4649
this(TmcEventBus.getDefault(), new ConfigFile("CourseDb.json"));
4750
}
48-
51+
4952
public CourseDb(TmcEventBus eventBus, ConfigFile configFile) {
5053
this.eventBus = eventBus;
5154
this.configFile = configFile;
@@ -58,7 +61,7 @@ public CourseDb(TmcEventBus eventBus, ConfigFile configFile) {
5861
logger.log(Level.WARNING, "Failed to load course database", e);
5962
}
6063
}
61-
64+
6265
public List<Course> getAvailableCourses() {
6366
return Collections.unmodifiableList(availableCourses);
6467
}
@@ -102,7 +105,7 @@ public Exercise getExerciseByKey(ExerciseKey key) {
102105

103106
/**
104107
* Returns the exercises from currently selected course.
105-
*
108+
*
106109
* <p>
107110
* If no course is currently selected then returns the empty collection.
108111
*/
@@ -114,7 +117,7 @@ public List<Exercise> getCurrentCourseExercises() {
114117
return Collections.emptyList();
115118
}
116119
}
117-
120+
118121
public Course getCourseByName(String name) {
119122
for (Course course : availableCourses) {
120123
if (course.getName().equals(name)) {
@@ -123,7 +126,7 @@ public Course getCourseByName(String name) {
123126
}
124127
return null;
125128
}
126-
129+
127130
public boolean isUnlockable(Exercise ex) {
128131
Course course = getCourseByName(ex.getCourseName());
129132
if (course != null) {
@@ -132,9 +135,10 @@ public boolean isUnlockable(Exercise ex) {
132135
return false;
133136
}
134137
}
135-
138+
136139
/**
137-
* Returns all exercises from the current course that can be unlocked (and must be unlocked together).
140+
* Returns all exercises from the current course that can be unlocked (and
141+
* must be unlocked together).
138142
*/
139143
public List<Exercise> getCurrentCourseUnlockableExercises() {
140144
List<Exercise> result = new ArrayList<Exercise>();
@@ -158,34 +162,35 @@ public List<Exercise> getCurrentCourseUnlockableExercises() {
158162
public String getDownloadedExerciseChecksum(ExerciseKey ex) {
159163
return downloadedExerciseChecksums.get(ex);
160164
}
161-
165+
162166
/**
163167
* Informs the course database that the exercise is considered downloaded.
164-
*
168+
*
165169
* <p>
166-
* Sets the downloaded checksum of the exercise to be the one reported by the server.
170+
* Sets the downloaded checksum of the exercise to be the one reported by
171+
* the server.
167172
*/
168173
public void exerciseDownloaded(Exercise ex) {
169174
List<Exercise> exercises = new ArrayList<Exercise>();
170175
exercises.add(ex);
171176
this.multipleExerciseDownloaded(exercises);
172177
}
173-
178+
174179
/**
175180
* Informs the course database that the exercises are considered downloaded.
176-
*
181+
*
177182
* <p>
178-
* Sets the downloaded checksums of the exercises to be the ones reported by the server.
183+
* Sets the downloaded checksums of the exercises to be the ones reported by
184+
* the server.
179185
*/
180186
public void multipleExerciseDownloaded(List<Exercise> exercises) {
181187
for (Exercise ex : exercises) {
182188
downloadedExerciseChecksums.put(ex.getKey(), ex.getChecksum());
183189
}
184190
save();
185191
}
186-
192+
187193
//TODO: arrange for downloadedExerciseChecksums.put(..., null) when a project is deleted!
188-
189194
public void save() {
190195
try {
191196
saveToFile();
@@ -194,13 +199,14 @@ public void save() {
194199
}
195200
eventBus.post(new ChangedEvent());
196201
}
197-
202+
198203
private static class StoredStuff {
204+
199205
public List<Course> availableCourses;
200206
public String currentCourseName;
201207
public Map<ExerciseKey, String> downloadedExerciseChecksums;
202208
}
203-
209+
204210
private void saveToFile() throws IOException {
205211
StoredStuff stuff = new StoredStuff();
206212
stuff.availableCourses = this.availableCourses;
@@ -218,7 +224,7 @@ private void loadFromFile() throws IOException {
218224
if (!configFile.exists()) {
219225
return;
220226
}
221-
227+
222228
Reader reader = configFile.getReader();
223229
StoredStuff stuff;
224230
try {
@@ -231,16 +237,16 @@ private void loadFromFile() throws IOException {
231237
this.availableCourses.clear();
232238
this.availableCourses.addAll(stuff.availableCourses);
233239
}
234-
240+
235241
this.currentCourseName = stuff.currentCourseName;
236-
242+
237243
if (stuff.downloadedExerciseChecksums != null) {
238244
this.downloadedExerciseChecksums.clear();
239245
this.downloadedExerciseChecksums.putAll(stuff.downloadedExerciseChecksums);
240246
}
241247
}
242248
}
243-
249+
244250
private Gson getGson() {
245251
return new GsonBuilder()
246252
.serializeNulls()

tmc-plugin/src/fi/helsinki/cs/tmc/model/ServerAccess.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
package fi.helsinki.cs.tmc.model;
22

3-
import com.google.common.util.concurrent.FutureCallback;
4-
import com.google.common.util.concurrent.Futures;
5-
import com.google.common.util.concurrent.ListenableFuture;
63
import com.google.gson.Gson;
74
import com.google.gson.GsonBuilder;
85
import com.google.gson.JsonObject;
@@ -23,7 +20,6 @@
2320
import fi.helsinki.cs.tmc.utilities.http.FailedHttpResponseException;
2421
import fi.helsinki.cs.tmc.utilities.http.HttpTasks;
2522
import hy.tmc.core.TmcCore;
26-
import hy.tmc.core.exceptions.TmcCoreException;
2723
import java.io.ByteArrayOutputStream;
2824
import java.io.IOException;
2925
import java.io.OutputStreamWriter;
@@ -36,7 +32,6 @@
3632
import java.util.Map;
3733
import java.util.zip.GZIPOutputStream;
3834
import org.openide.modules.Modules;
39-
import org.openide.util.Exceptions;
4035

4136
/**
4237
* A frontend for the server.
@@ -351,7 +346,7 @@ public boolean cancel() {
351346
};
352347
}
353348

354-
private byte[] eventListToPostBody(List<LoggableEvent> events) throws IOException {
349+
public byte[] eventListToPostBody(List<LoggableEvent> events) throws IOException {
355350
ByteArrayOutputStream bufferBos = new ByteArrayOutputStream();
356351
GZIPOutputStream gzos = new GZIPOutputStream(bufferBos);
357352
OutputStreamWriter bufferWriter = new OutputStreamWriter(gzos, Charset.forName("UTF-8"));

tmc-plugin/src/fi/helsinki/cs/tmc/spyware/EventSendBuffer.java

Lines changed: 80 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,48 @@
11
package fi.helsinki.cs.tmc.spyware;
22

3+
import com.google.common.base.Optional;
34
import com.google.common.collect.Iterables;
5+
import com.google.common.util.concurrent.FutureCallback;
6+
import com.google.common.util.concurrent.Futures;
7+
import com.google.common.util.concurrent.ListenableFuture;
48
import hy.tmc.core.domain.Course;
59
import fi.helsinki.cs.tmc.model.CourseDb;
10+
import fi.helsinki.cs.tmc.model.NBTmcSettings;
611
import fi.helsinki.cs.tmc.model.ServerAccess;
7-
import fi.helsinki.cs.tmc.utilities.BgTask;
8-
import fi.helsinki.cs.tmc.utilities.CancellableCallable;
12+
import fi.helsinki.cs.tmc.model.TmcCoreSingleton;
913
import fi.helsinki.cs.tmc.utilities.Cooldown;
14+
import fi.helsinki.cs.tmc.utilities.ExceptionUtils;
1015
import fi.helsinki.cs.tmc.utilities.SingletonTask;
1116
import fi.helsinki.cs.tmc.utilities.TmcRequestProcessor;
17+
import hy.tmc.core.communication.HttpResult;
18+
import hy.tmc.core.exceptions.TmcCoreException;
1219
import java.io.IOException;
1320
import java.util.ArrayDeque;
1421
import java.util.ArrayList;
1522
import java.util.Arrays;
1623
import java.util.Iterator;
1724
import java.util.List;
1825
import java.util.Random;
19-
import java.util.concurrent.ExecutionException;
20-
import java.util.concurrent.Future;
2126
import java.util.concurrent.TimeoutException;
2227
import java.util.logging.Level;
2328
import java.util.logging.Logger;
29+
import org.netbeans.api.progress.ProgressHandle;
30+
import org.netbeans.api.progress.ProgressHandleFactory;
31+
import org.openide.util.Exceptions;
2432

2533
/**
26-
* Buffers {@link LoggableEvent}s and sends them to the server and/or syncs them to the disk periodically.
34+
* Buffers {@link LoggableEvent}s and sends them to the server and/or syncs them
35+
* to the disk periodically.
2736
*/
2837
public class EventSendBuffer implements EventReceiver {
38+
2939
private static final Logger log = Logger.getLogger(EventSendBuffer.class.getName());
3040

31-
public static final long DEFAULT_SEND_INTERVAL = 3*60*1000;
32-
public static final long DEFAULT_SAVE_INTERVAL = 1*60*1000;
41+
public static final long DEFAULT_SEND_INTERVAL = 3 * 60 * 1000;
42+
public static final long DEFAULT_SAVE_INTERVAL = 3 * 60 * 1000;
3343
public static final int DEFAULT_MAX_EVENTS = 64 * 1024;
3444
public static final int DEFAULT_AUTOSEND_THREHSOLD = DEFAULT_MAX_EVENTS / 2;
35-
public static final int DEFAULT_AUTOSEND_COOLDOWN = 30*1000;
45+
public static final int DEFAULT_AUTOSEND_COOLDOWN = 30 * 1000;
3646

3747
private Random random = new Random();
3848
private SpywareSettings settings;
@@ -47,7 +57,6 @@ public class EventSendBuffer implements EventReceiver {
4757
private int autosendThreshold = DEFAULT_AUTOSEND_THREHSOLD;
4858
private Cooldown autosendCooldown;
4959

50-
5160
public EventSendBuffer(SpywareSettings settings, ServerAccess serverAccess, CourseDb courseDb, EventStore eventStore) {
5261
this.settings = settings;
5362
this.serverAccess = serverAccess;
@@ -172,7 +181,6 @@ public void close() {
172181
}
173182
}
174183

175-
176184
private SingletonTask sendingTask = new SingletonTask(new Runnable() {
177185
// Sending too many at once may go over the server's POST size limit.
178186
private static final int MAX_EVENTS_PER_SEND = 500;
@@ -196,7 +204,7 @@ public void run() {
196204
return;
197205
}
198206

199-
log.log(Level.INFO, "Sending {0} events to {1}", new Object[] { eventsToSend.size(), url });
207+
log.log(Level.INFO, "Sending {0} events to {1}", new Object[]{eventsToSend.size(), url});
200208

201209
doSend(eventsToSend, url);
202210
} while (shouldSendMore);
@@ -235,32 +243,74 @@ private String pickDestinationUrl() {
235243
return url;
236244
}
237245

238-
private void doSend(final ArrayList<LoggableEvent> eventsToSend, final String url) {
239-
CancellableCallable<Object> task = serverAccess.getSendEventLogJob(url, eventsToSend);
240-
Future<Object> future = BgTask.start("Sending stats", task);
241-
242-
try {
243-
future.get();
244-
} catch (InterruptedException ex) {
245-
future.cancel(true);
246-
} catch (ExecutionException ex) {
247-
log.log(Level.INFO, "Sending failed", ex);
246+
/**
247+
* Converts events to data[] and sends it to defined url.
248+
*
249+
* @param eventsToSend
250+
* @param url
251+
*/
252+
private void doSend(final ArrayList<LoggableEvent> eventsToSend, final String url) {
253+
NBTmcSettings settings = NBTmcSettings.getDefault();
254+
Optional<Course> currentCourse = settings.getCurrentCourse();
255+
if (!currentCourse.isPresent()) {
248256
return;
249257
}
258+
addCorrectSpywareUrl(url, currentCourse);
259+
final ProgressHandle progress = ProgressHandleFactory.createSystemHandle("Sending stats");
260+
progress.start();
261+
try {
262+
byte[] data = convertEventsToByteArray(eventsToSend);
263+
ListenableFuture<List<HttpResult>> spywareSending = TmcCoreSingleton.getInstance().sendSpywareDiffs(
264+
data, settings
265+
);
266+
Futures.addCallback(spywareSending, new FutureCallback<List<HttpResult>>() {
267+
@Override
268+
public void onSuccess(List<HttpResult> success) {
269+
clearAfterSend(success);
270+
}
271+
@Override
272+
public void onFailure(Throwable thrwbl) {
273+
clearAfterSend(new ArrayList<HttpResult>());
274+
System.err.println(thrwbl.getMessage());
275+
}
276+
private void clearAfterSend(List<HttpResult> success) {
277+
278+
progress.finish();
279+
log.log(Level.INFO, "Sent {0} events successfully to {1}", new Object[]{eventsToSend.size(), url});
280+
removeSentEventsFromQueue();
281+
// If saving fails now (or is already running and fails later)
282+
// then we may end up sending duplicate events later.
283+
// This will hopefully be very rare.
284+
savingTask.start();
285+
}
286+
});
287+
288+
} catch (TmcCoreException ex) {
289+
progress.finish();
290+
Exceptions.printStackTrace(ex);
291+
}
292+
}
250293

251-
log.log(Level.INFO, "Sent {0} events successfully to {1}", new Object[] { eventsToSend.size(), url });
252-
253-
removeSentEventsFromQueue();
254-
255-
// If saving fails now (or is already running and fails later)
256-
// then we may end up sending duplicate events later.
257-
// This will hopefully be very rare.
258-
savingTask.start();
294+
private void addCorrectSpywareUrl(final String url, Optional<Course> currentCourse) {
295+
List<String> spywareUrls = new ArrayList<String>();
296+
String finalUrl = serverAccess.addApiCallQueryParameters(url);
297+
spywareUrls.add(finalUrl);
298+
currentCourse.get().setSpywareUrls(spywareUrls);
259299
}
260300

301+
private byte[] convertEventsToByteArray(final ArrayList<LoggableEvent> eventsToSend) throws RuntimeException {
302+
byte[] data;
303+
try {
304+
data = serverAccess.eventListToPostBody(eventsToSend);
305+
} catch (IOException ex) {
306+
throw ExceptionUtils.toRuntimeException(ex);
307+
}
308+
return data;
309+
}
310+
261311
private void removeSentEventsFromQueue() {
262312
synchronized (sendQueue) {
263-
assert(eventsToRemoveAfterSend <= sendQueue.size());
313+
assert (eventsToRemoveAfterSend <= sendQueue.size());
264314
while (eventsToRemoveAfterSend > 0) {
265315
sendQueue.pop();
266316
eventsToRemoveAfterSend--;
@@ -270,7 +320,6 @@ private void removeSentEventsFromQueue() {
270320

271321
}, TmcRequestProcessor.instance);
272322

273-
274323
private SingletonTask savingTask = new SingletonTask(new Runnable() {
275324
@Override
276325
public void run() {
@@ -285,5 +334,4 @@ public void run() {
285334
}
286335
}
287336
}, TmcRequestProcessor.instance);
288-
289337
}

0 commit comments

Comments
 (0)