Skip to content

Commit bbd5a9a

Browse files
author
Chris Hooks
committed
Refactored attachment response handling
Also whitespace fixes and other corrections
1 parent fb7c28e commit bbd5a9a

File tree

10 files changed

+167
-118
lines changed

10 files changed

+167
-118
lines changed

src/main/java/com/rusticisoftware/tincan/Attachment.java

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,57 +50,60 @@ public class Attachment extends JSONBase {
5050
private String sha2;
5151
private URL fileUrl;
5252
private byte[] content;
53-
53+
5454
public Attachment(JsonNode jsonNode) throws URISyntaxException, MalformedURLException, IOException, NoSuchAlgorithmException {
55+
this(jsonNode, null);
56+
}
57+
58+
public Attachment(JsonNode jsonNode, byte[] content) throws URISyntaxException, MalformedURLException, IOException, NoSuchAlgorithmException {
5559
JsonNode usageTypeNode = jsonNode.path("usageType");
5660
if (! usageTypeNode.isMissingNode()) {
5761
this.setUsageType(new URI(usageTypeNode.textValue()));
5862
}
59-
63+
6064
JsonNode displayNode = jsonNode.path("display");
6165
if (! displayNode.isMissingNode()) {
6266
this.setDisplay(new LanguageMap(displayNode));
6367
}
64-
68+
6569
JsonNode descriptionNode = jsonNode.path("description");
6670
if (! descriptionNode.isMissingNode()) {
6771
this.setDescription(new LanguageMap(descriptionNode));
6872
}
69-
73+
7074
JsonNode contentTypeNode = jsonNode.path("contentType");
7175
if (! contentTypeNode.isMissingNode()) {
7276
this.setContentType(contentTypeNode.textValue());
7377
}
74-
78+
7579
JsonNode lengthNode = jsonNode.path("length");
7680
if (! lengthNode.isMissingNode()) {
7781
this.setLength(lengthNode.intValue());
7882
}
79-
83+
8084
JsonNode sha2Node = jsonNode.path("sha2");
8185
if (! sha2Node.isMissingNode()) {
8286
this.setSha2(sha2Node.textValue());
8387
}
84-
88+
8589
JsonNode fileUrlNode = jsonNode.path("fileUrl");
8690
if (! fileUrlNode.isMissingNode()) {
8791
this.setFileUrl(new URL(fileUrlNode.textValue()));
8892
}
8993

90-
JsonNode contentNode = jsonNode.path("content");
91-
if (! contentNode.isMissingNode()) {
92-
this.setContent(contentNode.binaryValue());
94+
if (content != null) {
95+
this.setContent(content);
9396
}
9497
}
9598

9699

97100
public void setContent(byte[] content) throws NoSuchAlgorithmException {
98101
this.content = Arrays.copyOf(content, content.length);
99-
setLength(content.length);
102+
this.setLength(content.length);
100103
MessageDigest digest = MessageDigest.getInstance("SHA-256");
101104
digest.update(content);
102105
byte[] hash = digest.digest();
103-
setSha2(new String(Hex.encodeHex(hash)));
106+
this.setSha2(new String(Hex.encodeHex(hash)));
104107
}
105108

106109
@Override
@@ -130,7 +133,7 @@ public ObjectNode toJSONNode(TCAPIVersion version) {
130133
return node;
131134
}
132135

133-
public HTTPPart getPart(){
136+
public HTTPPart getPart() {
134137
HTTPPart part = new HTTPPart();
135138
part.setContent(content);
136139
part.setContentType(contentType);

src/main/java/com/rusticisoftware/tincan/LRS.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,13 @@ public interface LRS {
2929

3030
StatementLRSResponse saveStatement(Statement statement);
3131
StatementsResultLRSResponse saveStatements(List<Statement> statements);
32+
StatementLRSResponse retrieveStatement(String id);
33+
StatementLRSResponse retrieveVoidedStatement(String id);
3234
StatementLRSResponse retrieveStatement(String id, boolean attachments);
3335
StatementLRSResponse retrieveVoidedStatement(String id, boolean attachments);
3436
StatementsResultLRSResponse queryStatements(StatementsQueryInterface query);
3537
StatementsResultLRSResponse moreStatements(String moreURL);
3638

37-
3839
ProfileKeysLRSResponse retrieveStateIds(Activity activity, Agent agent, UUID registration);
3940
StateLRSResponse retrieveState(String id, Activity activity, Agent agent, UUID registration);
4041
LRSResponse saveState(StateDocument state);

src/main/java/com/rusticisoftware/tincan/RemoteLRS.java

Lines changed: 75 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@
2828
import com.rusticisoftware.tincan.documents.StateDocument;
2929
import com.rusticisoftware.tincan.exceptions.*;
3030
import com.rusticisoftware.tincan.lrsresponses.*;
31+
import com.rusticisoftware.tincan.http.HTTPPart;
3132
import com.rusticisoftware.tincan.http.HTTPRequest;
3233
import com.rusticisoftware.tincan.http.HTTPResponse;
34+
import com.rusticisoftware.tincan.internal.MultipartParser;
3335
import com.rusticisoftware.tincan.json.Mapper;
3436
import com.rusticisoftware.tincan.json.StringOfJSON;
3537
import com.rusticisoftware.tincan.v10x.StatementsQuery;
36-
import com.rusticisoftware.tincan.http.HTTPPart;
37-
import com.rusticisoftware.tincan.internal.MultipartParser;
3838
import org.eclipse.jetty.client.api.ContentResponse;
3939
import org.eclipse.jetty.client.api.Request;
4040
import org.eclipse.jetty.client.api.Response;
@@ -200,7 +200,6 @@ private HTTPResponse makeSyncRequest(HTTPRequest req) {
200200
}
201201
}
202202

203-
204203
final HTTPResponse response = new HTTPResponse();
205204

206205
try {
@@ -221,14 +220,14 @@ private HTTPResponse makeSyncRequest(HTTPRequest req) {
221220
}
222221

223222
OutputStreamContentProvider content = new OutputStreamContentProvider();
224-
InputStreamResponseListener listener = new InputStreamResponseListener();
223+
FutureResponseListener listener = new FutureResponseListener(webReq);
225224

226225
try (OutputStream output = content.getOutputStream()) {
227226
if (req.getPartList() == null || req.getPartList().size() <= 0) {
228227
if (req.getContentType() != null) {
229228
webReq.header("Content-Type", req.getContentType());
230229
}
231-
else if (req.getContentType() != "GET") {
230+
else if (req.getMethod() != "GET") {
232231
webReq.header("Content-Type", "application/octet-stream");
233232
}
234233

@@ -241,15 +240,15 @@ else if (req.getContentType() != "GET") {
241240
output.close();
242241
}
243242
else {
244-
245243
MultiPartOutputStream multiout = new MultiPartOutputStream(output);
246244

247245
webReq.header("Content-Type", "multipart/mixed; boundary=" + multiout.getBoundary());
248246
webReq.content(content).send(listener);
249247

250248
if (req.getContentType() != null) {
251249
multiout.startPart(req.getContentType());
252-
} else {
250+
}
251+
else {
253252
multiout.startPart("application/octet-stream");
254253
}
255254

@@ -260,73 +259,60 @@ else if (req.getContentType() != "GET") {
260259
for (HTTPPart part : req.getPartList()) {
261260
multiout.startPart(part.getContentType(), new String[]{
262261
"Content-Transfer-Encoding: binary",
263-
"X-Experience-API-Hash: " + part.getSha2()});
262+
"X-Experience-API-Hash: " + part.getSha2()
263+
});
264264
multiout.write(part.getContent());
265265
}
266266
multiout.close();
267267
}
268268
}
269269

270-
Response httpResponse = listener.get(5, TimeUnit.SECONDS);
270+
ContentResponse httpResponse = listener.get();
271271

272272
response.setStatus(httpResponse.getStatus());
273273
response.setStatusMsg(httpResponse.getReason());
274274
for (HttpField header : httpResponse.getHeaders()) {
275275
response.setHeader(header.getName(), header.getValue());
276276
}
277277

278+
if (response.getContentType() != null && response.getContentType().contains("multipart/mixed")) {
279+
String boundary = response.getContentType().split("boundary=")[1];
278280

281+
MultipartParser responseHandler = new MultipartParser(listener.getContentAsString(), boundary);
282+
String temp = listener.getContentAsString();
279283

280-
// Use try-with-resources to close input stream.
281-
try (InputStream responseContent = listener.getInputStream())
282-
{
283-
284-
if (response.getContentType() != null && response.getContentType().contains("multipart/mixed")) {
285-
286-
MultipartParser responseHandler = new MultipartParser(responseContent);
287-
288-
// We need to get the first part that contains the statements and parse them
289-
responseHandler.nextPart();
284+
// We need to get the first part that contains the statements and parse them
285+
responseHandler.nextPart();
290286

291-
ArrayList<Statement> statements = new ArrayList<Statement>();
287+
ArrayList<Statement> statements = new ArrayList<Statement>();
292288

293-
if (responseHandler.getHeaders().get("Content-Type").contains("application/json")) {
294-
JsonNode statementsNode = (new StringOfJSON(new String(responseHandler.getContent())).toJSONNode());
295-
if (! (statementsNode.findPath("statements").isMissingNode())) {
296-
statementsNode = statementsNode.findPath("statements");
297-
for (JsonNode obj : statementsNode) {
298-
statements.add(new Statement(obj));
299-
}
300-
}
301-
else {
302-
statements.add(new Statement(statementsNode));
303-
}
289+
if (responseHandler.getHeaders().get("Content-Type").contains("application/json")) {
290+
JsonNode statementsNode = (new StringOfJSON(new String(responseHandler.getContent())).toJSONNode());
291+
if (statementsNode.findPath("statements").isMissingNode()) {
292+
statements.add(new Statement(statementsNode));
304293
}
305-
306-
while (! responseHandler.noMoreParts) {
307-
responseHandler.nextPart();
308-
String hashToMatch = responseHandler.getHeaders().get("X-Experience-API-Hash");
309-
for (Statement stmt : statements) {
310-
if (stmt.getAttachments() != null) {
311-
for (Attachment a : stmt.getAttachments()) {
312-
if (a.getSha2().equals(hashToMatch)) {
313-
a.setContent(responseHandler.getContent());
314-
break;
315-
}
316-
}
317-
}
294+
else {
295+
statementsNode = statementsNode.findPath("statements");
296+
for (JsonNode obj : statementsNode) {
297+
statements.add(new Statement(obj));
318298
}
319299
}
320-
StatementsResult responseStatements = new StatementsResult();
321-
responseStatements.setStatements(statements);
322-
response.setContentBytes(responseStatements.toJSONNode(TCAPIVersion.V101).toString().getBytes());
323300
}
324301
else {
325-
String responseContentString = IO.toString(responseContent);
326-
response.setContentBytes(responseContentString.getBytes());
302+
throw new Exception("The first part of this response had a Content-Type other than \"application/json\"");
327303
}
328-
}
329304

305+
while (!responseHandler.noMoreParts) {
306+
responseHandler.nextPart();
307+
response.setAttachment(responseHandler.getHeaders().get("X-Experience-API-Hash"), responseHandler.getContent());
308+
}
309+
StatementsResult responseStatements = new StatementsResult();
310+
responseStatements.setStatements(statements);
311+
response.setContentBytes(responseStatements.toJSONNode(TCAPIVersion.V101).toString().getBytes());
312+
}
313+
else {
314+
response.setContentBytes(listener.getContent());
315+
}
330316
} catch (Exception ex) {
331317
response.setStatus(400);
332318
response.setStatusMsg("Exception in RemoteLRS.makeSyncRequest(): " + ex);
@@ -349,7 +335,21 @@ private StatementLRSResponse getStatement(HashMap<String, String> params) {
349335
if (status == 200) {
350336
lrsResponse.setSuccess(true);
351337
try {
352-
lrsResponse.setContent(new Statement(new StringOfJSON(response.getContent())));
338+
JsonNode contentNode = (new StringOfJSON(response.getContent())).toJSONNode();
339+
if (! (contentNode.findPath("statements").isMissingNode())) {
340+
contentNode = contentNode.findPath("statements").get(0);
341+
}
342+
343+
Statement stmt = new Statement (contentNode);
344+
for (Map.Entry<String, byte[]> entry : response.getAttachments().entrySet()) {
345+
for (Attachment a : stmt.getAttachments()) {
346+
if (entry.getKey().equals(a.getSha2())) {
347+
a.setContent(entry.getValue());
348+
}
349+
}
350+
}
351+
352+
lrsResponse.setContent(stmt);
353353
} catch (Exception ex) {
354354
lrsResponse.setErrMsg("Exception: " + ex.toString());
355355
lrsResponse.setSuccess(false);
@@ -598,18 +598,13 @@ public StatementsResultLRSResponse saveStatements(List<Statement> statements) {
598598
return lrsResponse;
599599
}
600600

601+
lrsResponse.getRequest().setPartList(new ArrayList<HTTPPart>());
601602
for (Statement statement: statements) {
602603
if (statement.hasAttachmentsWithContent()) {
603-
if(lrsResponse.getRequest().getPartList() == null){
604-
lrsResponse.getRequest().setPartList(statement.getPartList());
605-
}
606-
else{
607-
lrsResponse.getRequest().getPartList().addAll(statement.getPartList());
608-
}
604+
lrsResponse.getRequest().getPartList().addAll(statement.getPartList());
609605
}
610606
}
611607

612-
613608
HTTPResponse response = makeSyncRequest(lrsResponse.getRequest());
614609
int status = response.getStatus();
615610

@@ -636,6 +631,16 @@ public StatementsResultLRSResponse saveStatements(List<Statement> statements) {
636631
return lrsResponse;
637632
}
638633

634+
@Override
635+
public StatementLRSResponse retrieveStatement(String id) {
636+
return retrieveStatement(id, false);
637+
}
638+
639+
@Override
640+
public StatementLRSResponse retrieveVoidedStatement(String id) {
641+
return retrieveVoidedStatement(id, false);
642+
}
643+
639644
@Override
640645
public StatementLRSResponse retrieveStatement(String id, boolean attachments) {
641646
HashMap<String, String> params = new HashMap<String, String>();
@@ -690,6 +695,18 @@ public StatementsResultLRSResponse queryStatements(StatementsQueryInterface quer
690695
lrsResponse.setSuccess(true);
691696
try {
692697
lrsResponse.setContent(new StatementsResult(new StringOfJSON(response.getContent())));
698+
699+
for (Statement stmt : lrsResponse.getContent().getStatements()) {
700+
if (stmt.hasAttachments()) {
701+
for (Map.Entry<String, byte[]> entry : response.getAttachments().entrySet()) {
702+
for (Attachment a : stmt.getAttachments()) {
703+
if (entry.getKey().equals(a.getSha2())) {
704+
a.setContent(entry.getValue());
705+
}
706+
}
707+
}
708+
}
709+
}
693710
} catch (Exception ex) {
694711
lrsResponse.setErrMsg("Exception: " + ex.toString());
695712
lrsResponse.setSuccess(false);

src/main/java/com/rusticisoftware/tincan/Statement.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public class Statement extends StatementBase {
4747
private DateTime stored;
4848
private Agent authority;
4949
private TCAPIVersion version;
50-
50+
5151
@Deprecated
5252
private Boolean voided;
5353

@@ -135,6 +135,4 @@ public void stamp() {
135135
this.setTimestamp(new DateTime());
136136
}
137137
}
138-
139-
140138
}

src/main/java/com/rusticisoftware/tincan/http/HTTPPart.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,4 @@ public class HTTPPart {
2626
private String sha2;
2727
private String contentType;
2828
private byte[] content;
29-
}
30-
31-
29+
}

src/main/java/com/rusticisoftware/tincan/http/HTTPResponse.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@
3232
public class HTTPResponse {
3333
private int status;
3434
private String statusMsg;
35-
private final HashMap<String,String> headers = new HashMap<String, String>();
35+
private final HashMap<String, String> headers = new HashMap<String, String>();
3636
private byte[] contentBytes;
3737
private List<HTTPPart> partList;
38+
private HashMap<String, byte[]> attachments = new HashMap<String, byte[]>();
3839

3940
public String getHeader(String key) {
4041
return this.headers.get(key);
@@ -43,6 +44,9 @@ public void setHeader(String key, String val) {
4344
this.headers.put(key, val);
4445
}
4546

47+
public byte[] getAttachment(String key) { return this.attachments.get(key); }
48+
public void setAttachment(String key, byte[] val) { this.attachments.put(key, val); }
49+
4650
public String getContent() {
4751
return new String(this.getContentBytes());
4852
}

0 commit comments

Comments
 (0)