Skip to content

Commit 7b6fb23

Browse files
committed
Send diagnostics to tmc-bandicoot
1 parent e76a2d6 commit 7b6fb23

15 files changed

+418
-48
lines changed

google_checks.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
<property name="allowNonPrintableEscapes" value="true"/>
4646
</module>
4747
<module name="LineLength">
48-
<property name="max" value="100"/>
48+
<property name="max" value="160"/>
4949
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://|//.*"/>
5050
</module>
5151
<module name="AvoidStarImport"/>

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
<groupId>fi.helsinki.cs.tmc</groupId>
55
<artifactId>core</artifactId>
6-
<version>0.9.9-SNAPSHOT</version>
6+
<version>0.9.10-SNAPSHOT</version>
77
<packaging>jar</packaging>
88
<name>tmc-core</name>
99
<url>http://testmycode.net</url>

src/main/java/fi/helsinki/cs/tmc/core/TmcCore.java

+25-16
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import fi.helsinki.cs.tmc.core.commands.RequestCodeReview;
1313
import fi.helsinki.cs.tmc.core.commands.RunCheckStyle;
1414
import fi.helsinki.cs.tmc.core.commands.RunTests;
15+
import fi.helsinki.cs.tmc.core.commands.SendDiagnostics;
1516
import fi.helsinki.cs.tmc.core.commands.SendFeedback;
1617
import fi.helsinki.cs.tmc.core.commands.SendSpywareEvents;
1718
import fi.helsinki.cs.tmc.core.commands.Submit;
@@ -25,6 +26,7 @@
2526
import fi.helsinki.cs.tmc.core.domain.submission.SubmissionResult;
2627
import fi.helsinki.cs.tmc.core.holders.TmcLangsHolder;
2728
import fi.helsinki.cs.tmc.core.holders.TmcSettingsHolder;
29+
import fi.helsinki.cs.tmc.core.utilities.ExceptionTrackingCallable;
2830
import fi.helsinki.cs.tmc.langs.abstraction.ValidationResult;
2931
import fi.helsinki.cs.tmc.langs.domain.RunResult;
3032
import fi.helsinki.cs.tmc.langs.util.TaskExecutor;
@@ -72,81 +74,88 @@ public TmcCore(TmcSettings settings, TaskExecutor tmcLangs) {
7274
TmcLangsHolder.set(tmcLangs);
7375
}
7476

77+
public Callable<Void> sendDiagnostics(
78+
ProgressObserver observer) {
79+
logger.info("Creating new SendDiagnostics command");
80+
return new SendDiagnostics(observer);
81+
}
82+
7583
public Callable<List<Exercise>> downloadOrUpdateExercises(
7684
ProgressObserver observer, List<Exercise> exercises) {
7785
logger.info("Creating new DownloadOrUpdateExercises command");
78-
return new DownloadOrUpdateExercises(observer, exercises);
86+
return new ExceptionTrackingCallable<>(new DownloadOrUpdateExercises(observer, exercises));
7987
}
8088

8189
// TODO: returns new course.
8290
public Callable<Course> getCourseDetails(ProgressObserver observer, Course course) {
8391
logger.info("Creating new GetCourseDetails command");
84-
return new GetCourseDetails(observer, course);
92+
return new ExceptionTrackingCallable<>(new GetCourseDetails(observer, course));
8593
}
8694

8795
public Callable<List<Course>> listCourses(ProgressObserver observer) {
8896
logger.info("Creating new ListCourses command");
89-
return new ListCourses(observer);
97+
return new ExceptionTrackingCallable<>(new ListCourses(observer));
9098
}
9199

92100
public Callable<URI> pasteWithComment(
93101
ProgressObserver observer, Exercise exercise, String message) {
94102
logger.info("Creating new PasteWithComment command");
95-
return new PasteWithComment(observer, exercise, message);
103+
return new ExceptionTrackingCallable<>(new PasteWithComment(observer, exercise, message));
96104
}
97105

98106
public Callable<ValidationResult> runCheckStyle(ProgressObserver observer, Exercise exercise) {
99107
logger.info("Creating new RunCheckStyle command");
100-
return new RunCheckStyle(observer, exercise);
108+
return new ExceptionTrackingCallable<>(new RunCheckStyle(observer, exercise));
101109
}
102110

103111
public Callable<RunResult> runTests(ProgressObserver observer, Exercise exercise) {
104112
logger.info("Creating new RunTests command");
105-
return new RunTests(observer, exercise);
113+
return new ExceptionTrackingCallable<>(new ExceptionTrackingCallable<>(new RunTests(observer, exercise)));
106114
}
107115

108116
public Callable<Boolean> sendFeedback(
109117
ProgressObserver observer, List<FeedbackAnswer> answers, URI feedbackUri) {
110118
logger.info("Creating new SendFeedback command");
111-
return new SendFeedback(observer, answers, feedbackUri);
119+
return new ExceptionTrackingCallable<>(new SendFeedback(observer, answers, feedbackUri));
112120
}
113121

114122
public Callable<Void> sendSpywareEvents(
115-
ProgressObserver observer, Course currentCourse, List<LoggableEvent> events) {
123+
final ProgressObserver observer, final Course currentCourse, final List<LoggableEvent> events) {
116124
logger.info("Creating new SenSpywareEvents command");
117-
return new SendSpywareEvents(observer, currentCourse, events);
125+
return new ExceptionTrackingCallable<>(new SendSpywareEvents(observer, currentCourse, events));
126+
118127
}
119128

120129
public Callable<SubmissionResult> submit(ProgressObserver observer, Exercise exercise) {
121130
logger.info("Creating new Submit command");
122-
return new Submit(observer, exercise);
131+
return new ExceptionTrackingCallable<>(new Submit(observer, exercise));
123132
}
124133

125134
public Callable<GetUpdatableExercises.UpdateResult> getExerciseUpdates(
126135
ProgressObserver observer, Course course) {
127136
logger.info("Creating new GetUpdatableExercises command");
128-
return new GetUpdatableExercises(observer, course);
137+
return new ExceptionTrackingCallable<>(new GetUpdatableExercises(observer, course));
129138
}
130139

131140
public Callable<Void> markReviewAsRead(ProgressObserver observer, Review review) {
132141
logger.info("Creating new MarkReviewAsRead command");
133-
return new MarkReviewAsRead(observer, review);
142+
return new ExceptionTrackingCallable<>(new MarkReviewAsRead(observer, review));
134143
}
135144

136145
public Callable<List<Review>> getUnreadReviews(ProgressObserver observer, Course course) {
137146
logger.info("Creating new GetUnreadReviews command");
138-
return new GetUnreadReviews(observer, course);
147+
return new ExceptionTrackingCallable<>(new GetUnreadReviews(observer, course));
139148
}
140149

141150
public Callable<TmcServerCommunicationTaskFactory.SubmissionResponse> requestCodeReview(
142151
ProgressObserver observer, Exercise exercise, String messageForReviewer) {
143152
logger.info("Creating new RequestCodeReview command");
144-
return new RequestCodeReview(observer, exercise, messageForReviewer);
153+
return new ExceptionTrackingCallable<>(new RequestCodeReview(observer, exercise, messageForReviewer));
145154
}
146155

147156
public Callable<Exercise> downloadModelSolution(ProgressObserver observer, Exercise exercise) {
148157
logger.info("Creating new DownloadModelSolution command");
149-
return new DownloadModelSolution(observer, exercise);
158+
return new ExceptionTrackingCallable<>(new DownloadModelSolution(observer, exercise));
150159
}
151160

152161
/**
@@ -156,6 +165,6 @@ public Callable<Exercise> downloadModelSolution(ProgressObserver observer, Exerc
156165
*/
157166
public Callable<Void> downloadCompletedExercises(ProgressObserver observer) {
158167
logger.info("Creating new DownloadCompletedExercises command");
159-
return new DownloadCompletedExercises(observer);
168+
return new ExceptionTrackingCallable<>(new DownloadCompletedExercises(observer));
160169
}
161170
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package fi.helsinki.cs.tmc.core.commands;
2+
3+
import fi.helsinki.cs.tmc.core.communication.TmcBandicootCommunicationTaskFactory;
4+
import fi.helsinki.cs.tmc.core.domain.ProgressObserver;
5+
import fi.helsinki.cs.tmc.core.domain.bandicoot.Diagnostics;
6+
7+
import com.google.common.annotations.VisibleForTesting;
8+
9+
/**
10+
* Sends general diagnostics about the client.
11+
*/
12+
public class SendDiagnostics extends Command<Void> {
13+
14+
private TmcBandicootCommunicationTaskFactory tmcBandicootCommunicationTaskFactory;
15+
private final Diagnostics diagnostics;
16+
17+
public SendDiagnostics(ProgressObserver observer) {
18+
super(observer);
19+
this.diagnostics = new Diagnostics();
20+
this.tmcBandicootCommunicationTaskFactory = new TmcBandicootCommunicationTaskFactory();
21+
22+
}
23+
24+
@VisibleForTesting
25+
SendDiagnostics(ProgressObserver observer, TmcBandicootCommunicationTaskFactory factory) {
26+
this(observer);
27+
this.tmcBandicootCommunicationTaskFactory = factory;
28+
}
29+
30+
/**
31+
* Sends diagnostics to tmc-bandicoot.
32+
* @return null
33+
* @throws Exception ex
34+
*/
35+
@Override
36+
public Void call() throws Exception {
37+
tmcBandicootCommunicationTaskFactory.sendDiagnostics(diagnostics).call();
38+
return null;
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package fi.helsinki.cs.tmc.core.communication;
2+
3+
import fi.helsinki.cs.tmc.core.communication.http.HttpTasks;
4+
import fi.helsinki.cs.tmc.core.domain.bandicoot.Crash;
5+
6+
import fi.helsinki.cs.tmc.core.domain.bandicoot.Diagnostics;
7+
8+
import org.apache.commons.lang.math.NumberUtils;
9+
10+
import java.net.URI;
11+
import java.util.concurrent.Callable;
12+
13+
public class TmcBandicootCommunicationTaskFactory {
14+
15+
// Diagnostics url must end in '/', or the path resolves with client_infos and crashes won't work
16+
private final URI diagnosticsUrl = URI.create("https://tmc-bandicoot.testmycode.io/");
17+
18+
/**
19+
* Sends diagnostics to tmc-bandicoot.
20+
* @param diagnostics diagnostics
21+
*/
22+
public Callable<Void> sendDiagnostics(Diagnostics diagnostics) {
23+
URI uri = getUrl(diagnostics);
24+
final Callable<String> send = new HttpTasks().postJson(uri.resolve("client_infos"), diagnostics);
25+
26+
return new Callable<Void>() {
27+
@Override
28+
public Void call() throws Exception {
29+
send.call();
30+
return null;
31+
}
32+
};
33+
}
34+
35+
/**
36+
* Sends a crash report to tmc-bandicoot.
37+
* @param crash a crash
38+
*/
39+
public Callable<Void> sendCrash(Crash crash) {
40+
final Callable<String> send = new HttpTasks().postJson(diagnosticsUrl.resolve("crashes"), crash);
41+
42+
return new Callable<Void>() {
43+
@Override
44+
public Void call() throws Exception {
45+
send.call();
46+
return null;
47+
}
48+
};
49+
}
50+
51+
private URI getUrl(Diagnostics diagnostics) {
52+
URI uri = diagnosticsUrl;
53+
String[] versionParts = diagnostics.getJavaVersion().split("_");
54+
if (versionParts.length == 2) {
55+
String version = versionParts[0];
56+
if (version.startsWith("1.7") || version.startsWith("1.6") || (version.startsWith("1.8") && NumberUtils.toInt(versionParts[1], 100) < 66)) {
57+
uri = URI.create("https://tmc-bandicoot.now.sh/");
58+
}
59+
}
60+
return uri;
61+
}
62+
}

src/main/java/fi/helsinki/cs/tmc/core/communication/http/HttpRequestExecutor.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public HttpRequestExecutor setTimeout(int timeoutMs) {
7777

7878
@Override
7979
public BufferedHttpEntity call()
80-
throws IOException, InterruptedException, FailedHttpResponseException {
80+
throws IOException, InterruptedException, FailedHttpResponseException {
8181
CloseableHttpClient httpClient = makeHttpClient();
8282

8383
try {

src/main/java/fi/helsinki/cs/tmc/core/communication/http/HttpTasks.java

+23
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,22 @@
22

33
import fi.helsinki.cs.tmc.core.exceptions.FailedHttpResponseException;
44

5+
import com.google.gson.Gson;
6+
57
import org.apache.http.NameValuePair;
68
import org.apache.http.auth.UsernamePasswordCredentials;
79
import org.apache.http.client.entity.UrlEncodedFormEntity;
810
import org.apache.http.client.methods.HttpPost;
911
import org.apache.http.entity.ByteArrayEntity;
1012
import org.apache.http.entity.ContentType;
13+
import org.apache.http.entity.StringEntity;
1114
import org.apache.http.entity.mime.HttpMultipartMode;
1215
import org.apache.http.entity.mime.MultipartEntityBuilder;
1316
import org.apache.http.entity.mime.content.ByteArrayBody;
1417
import org.apache.http.message.BasicNameValuePair;
1518
import org.apache.http.util.EntityUtils;
1619

20+
import java.io.Serializable;
1721
import java.io.UnsupportedEncodingException;
1822
import java.net.URI;
1923
import java.util.ArrayList;
@@ -30,6 +34,7 @@
3034
public class HttpTasks {
3135
private static final ContentType UTF8_TEXT_CONTENT_TYPE =
3236
ContentType.create("text/plain", "utf-8");
37+
private static final Gson gson = new Gson();
3338

3439
private UsernamePasswordCredentials credentials = null;
3540

@@ -46,6 +51,24 @@ private HttpRequestExecutor createExecutor(HttpPost request) {
4651
return new HttpRequestExecutor(request).setCredentials(credentials);
4752
}
4853

54+
/**
55+
* Posts json to a url without authentication.
56+
*/
57+
public Callable<String> postJson(final URI uri, final Serializable json) {
58+
return new Callable<String>() {
59+
@Override
60+
public String call() throws Exception {
61+
String string = gson.toJson(json);
62+
StringEntity content = new StringEntity(string, "UTF-8");
63+
HttpPost httpPost = new HttpPost(uri);
64+
httpPost.setHeader("content-type", "application/json");
65+
httpPost.setEntity(content);
66+
HttpRequestExecutor executor = new HttpRequestExecutor(httpPost);
67+
return EntityUtils.toString(executor.call());
68+
}
69+
};
70+
}
71+
4972
public Callable<byte[]> getForBinary(URI url) {
5073
return downloadToBinary(createExecutor(url));
5174
}

src/main/java/fi/helsinki/cs/tmc/core/configuration/TmcSettings.java

+4
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,8 @@ public interface TmcSettings {
5252
void setConfigRoot(Path configRoot);
5353

5454
Path getConfigRoot();
55+
56+
String hostProgramName();
57+
58+
String hostProgramVersion();
5559
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package fi.helsinki.cs.tmc.core.domain.bandicoot;
2+
3+
import com.google.common.base.Function;
4+
import com.google.common.base.Optional;
5+
import com.google.gson.annotations.SerializedName;
6+
7+
import java.io.Serializable;
8+
import java.util.ArrayList;
9+
import java.util.List;
10+
11+
public class Crash implements Serializable {
12+
private final String name;
13+
private final String message;
14+
private final String cause;
15+
private final List<String> stacktrace;
16+
@SerializedName("client_info_attributes")
17+
private final Diagnostics diagnostics;
18+
19+
public Crash(Throwable throwable) {
20+
this.name = throwable.toString();
21+
this.message = throwable.getMessage();
22+
stacktrace = new ArrayList<>();
23+
this.diagnostics = new Diagnostics();
24+
Optional<String> cause = Optional.fromNullable(throwable.getCause())
25+
.transform(new Function<Throwable, String>() {
26+
@Override
27+
public String apply(Throwable throwable) {
28+
return throwable.getMessage();
29+
}
30+
});
31+
this.cause = cause.orNull();
32+
for (StackTraceElement s : throwable.getStackTrace()) {
33+
stacktrace.add(s.toString());
34+
}
35+
}
36+
}

0 commit comments

Comments
 (0)