mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-04-19 08:16:20 +00:00
Generalise meshms json list parsing into an abstract class
This commit is contained in:
parent
09d04d7c8f
commit
4b1f64c0e3
119
java/org/servalproject/servaldna/AbstractJsonList.java
Normal file
119
java/org/servalproject/servaldna/AbstractJsonList.java
Normal file
@ -0,0 +1,119 @@
|
||||
package org.servalproject.servaldna;
|
||||
|
||||
import org.servalproject.json.JSONInputException;
|
||||
import org.servalproject.json.JSONTableScanner;
|
||||
import org.servalproject.json.JSONTokeniser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by jeremy on 10/10/16.
|
||||
*/
|
||||
public abstract class AbstractJsonList<T, E extends Exception> {
|
||||
protected final ServalDHttpConnectionFactory httpConnector;
|
||||
protected final JSONTableScanner table;
|
||||
protected HttpURLConnection httpConnection;
|
||||
protected JSONTokeniser json;
|
||||
protected long rowCount = 0;
|
||||
|
||||
protected AbstractJsonList(ServalDHttpConnectionFactory httpConnector, JSONTableScanner table){
|
||||
this.httpConnector = httpConnector;
|
||||
this.table = table;
|
||||
}
|
||||
|
||||
protected abstract String getUrl();
|
||||
|
||||
public boolean isConnected(){
|
||||
return this.json != null;
|
||||
}
|
||||
|
||||
protected void consumeHeader() throws JSONInputException, IOException {
|
||||
throw new JSONTokeniser.UnexpectedTokenException(json.nextToken());
|
||||
}
|
||||
|
||||
protected void handleResponseError() throws E, IOException, ServalDInterfaceException {
|
||||
throw new ServalDFailureException("received unexpected HTTP Status "+
|
||||
httpConnection.getResponseCode()+" " + httpConnection.getResponseMessage()+" from " + httpConnection.getURL());
|
||||
}
|
||||
|
||||
public void connect() throws IOException, ServalDInterfaceException, E {
|
||||
String url = getUrl();
|
||||
httpConnection = httpConnector.newServalDHttpConnection(url);
|
||||
httpConnection.connect();
|
||||
|
||||
if (!httpConnection.getContentType().equals("application/json"))
|
||||
throw new ServalDInterfaceException("unexpected HTTP Content-Type: " + httpConnection.getContentType());
|
||||
|
||||
json = new JSONTokeniser(
|
||||
(httpConnection.getResponseCode() >= 300) ?
|
||||
httpConnection.getErrorStream() : httpConnection.getInputStream());
|
||||
|
||||
if (httpConnection.getResponseCode()!=200){
|
||||
handleResponseError();
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
json.consume(JSONTokeniser.Token.START_OBJECT);
|
||||
// allow for extra optional fields
|
||||
while(true) {
|
||||
Object tok = json.nextToken();
|
||||
if (tok.equals("header"))
|
||||
break;
|
||||
json.pushToken(tok);
|
||||
consumeHeader();
|
||||
}
|
||||
json.consume(JSONTokeniser.Token.COLON);
|
||||
table.consumeHeaderArray(json);
|
||||
json.consume(JSONTokeniser.Token.COMMA);
|
||||
json.consume("rows");
|
||||
json.consume(JSONTokeniser.Token.COLON);
|
||||
json.consume(JSONTokeniser.Token.START_ARRAY);
|
||||
}catch (JSONInputException e){
|
||||
throw new ServalDInterfaceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract T factory(Map<String,Object> row, long rowCount) throws ServalDInterfaceException;
|
||||
|
||||
public T next() throws ServalDInterfaceException, IOException{
|
||||
try {
|
||||
Object tok = json.nextToken();
|
||||
if (tok == JSONTokeniser.Token.END_ARRAY) {
|
||||
json.consume(JSONTokeniser.Token.END_OBJECT);
|
||||
json.consume(JSONTokeniser.Token.EOF);
|
||||
return null;
|
||||
}
|
||||
if (rowCount != 0)
|
||||
JSONTokeniser.match(tok, JSONTokeniser.Token.COMMA);
|
||||
else
|
||||
json.pushToken(tok);
|
||||
Map<String,Object> row = table.consumeRowArray(json);
|
||||
return factory(row, rowCount++);
|
||||
} catch (JSONInputException e) {
|
||||
throw new ServalDInterfaceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<T> toList() throws ServalDInterfaceException, IOException {
|
||||
List<T> ret = new ArrayList<T>();
|
||||
T item;
|
||||
while ((item = next()) != null) {
|
||||
ret.add(item);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void close() throws IOException
|
||||
{
|
||||
httpConnection = null;
|
||||
if (json != null) {
|
||||
json.close();
|
||||
json = null;
|
||||
}
|
||||
}
|
||||
}
|
@ -44,6 +44,12 @@ public class MeshMSCommon
|
||||
{
|
||||
if (!"application/json".equals(conn.getContentType()))
|
||||
throw new ServalDInterfaceException("unexpected HTTP Content-Type: " + conn.getContentType());
|
||||
for (int code: expected_response_codes) {
|
||||
if (conn.getResponseCode() == code) {
|
||||
JSONTokeniser json = new JSONTokeniser(conn.getInputStream());
|
||||
return json;
|
||||
}
|
||||
}
|
||||
switch (conn.getResponseCode()) {
|
||||
case HttpURLConnection.HTTP_NOT_FOUND:
|
||||
case 419: // Authentication Timeout, for missing secret
|
||||
@ -52,12 +58,6 @@ public class MeshMSCommon
|
||||
throwRestfulResponseExceptions(status, conn.getURL());
|
||||
throw new ServalDInterfaceException("unexpected MeshMS status = " + status.meshms_status_code + ", \"" + status.meshms_status_message + "\"");
|
||||
}
|
||||
for (int code: expected_response_codes) {
|
||||
if (conn.getResponseCode() == code) {
|
||||
JSONTokeniser json = new JSONTokeniser(conn.getInputStream());
|
||||
return json;
|
||||
}
|
||||
}
|
||||
throw new ServalDInterfaceException("unexpected HTTP response code: " + conn.getResponseCode());
|
||||
}
|
||||
|
||||
@ -118,6 +118,18 @@ public class MeshMSCommon
|
||||
}
|
||||
}
|
||||
|
||||
public static void processRestfulError(HttpURLConnection conn, JSONTokeniser json) throws IOException, ServalDInterfaceException, MeshMSException {
|
||||
switch (conn.getResponseCode()) {
|
||||
case HttpURLConnection.HTTP_NOT_FOUND:
|
||||
case 419: // Authentication Timeout, for missing secret
|
||||
Status status = decodeRestfulStatus(json);
|
||||
throwRestfulResponseExceptions(status, conn.getURL());
|
||||
throw new ServalDInterfaceException("unexpected MeshMS status = " + status.meshms_status_code + ", \"" + status.meshms_status_message + "\"");
|
||||
}
|
||||
throw new ServalDInterfaceException("unexpected HTTP response code: " + conn.getResponseCode());
|
||||
|
||||
}
|
||||
|
||||
public static MeshMSStatus sendMessage(ServalDHttpConnectionFactory connector, SubscriberId sid1, SubscriberId sid2, String text) throws IOException, ServalDInterfaceException, MeshMSException
|
||||
{
|
||||
HttpURLConnection conn = connector.newServalDHttpConnection("/restful/meshms/" + sid1.toHex() + "/" + sid2.toHex() + "/sendmessage");
|
||||
|
@ -23,29 +23,22 @@ package org.servalproject.servaldna.meshms;
|
||||
import org.servalproject.json.JSONInputException;
|
||||
import org.servalproject.json.JSONTableScanner;
|
||||
import org.servalproject.json.JSONTokeniser;
|
||||
import org.servalproject.servaldna.AbstractJsonList;
|
||||
import org.servalproject.servaldna.ServalDHttpConnectionFactory;
|
||||
import org.servalproject.servaldna.ServalDInterfaceException;
|
||||
import org.servalproject.servaldna.Subscriber;
|
||||
import org.servalproject.servaldna.SubscriberId;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class MeshMSMessageList {
|
||||
public class MeshMSMessageList extends AbstractJsonList<MeshMSMessage, MeshMSException> {
|
||||
|
||||
private ServalDHttpConnectionFactory httpConnector;
|
||||
private SubscriberId my_sid;
|
||||
private SubscriberId their_sid;
|
||||
private String sinceToken;
|
||||
private HttpURLConnection httpConnection;
|
||||
private JSONTokeniser json;
|
||||
private JSONTableScanner table;
|
||||
private final SubscriberId my_sid;
|
||||
private final SubscriberId their_sid;
|
||||
private final String sinceToken;
|
||||
private long readOffset;
|
||||
private long latestAckOffset;
|
||||
private int rowCount;
|
||||
|
||||
public MeshMSMessageList(ServalDHttpConnectionFactory connector, SubscriberId my_sid, SubscriberId their_sid)
|
||||
{
|
||||
@ -54,63 +47,80 @@ public class MeshMSMessageList {
|
||||
|
||||
public MeshMSMessageList(ServalDHttpConnectionFactory connector, SubscriberId my_sid, SubscriberId their_sid, String since_token)
|
||||
{
|
||||
this.httpConnector = connector;
|
||||
super(connector, new JSONTableScanner()
|
||||
.addColumn("type", String.class)
|
||||
.addColumn("my_sid", SubscriberId.class)
|
||||
.addColumn("their_sid", SubscriberId.class)
|
||||
.addColumn("offset", Long.class)
|
||||
.addColumn("token", String.class)
|
||||
.addColumn("text", String.class, JSONTokeniser.Narrow.ALLOW_NULL)
|
||||
.addColumn("delivered", Boolean.class)
|
||||
.addColumn("read", Boolean.class)
|
||||
.addColumn("timestamp", Long.class, JSONTokeniser.Narrow.ALLOW_NULL)
|
||||
.addColumn("ack_offset", Long.class, JSONTokeniser.Narrow.ALLOW_NULL));
|
||||
this.my_sid = my_sid;
|
||||
this.their_sid = their_sid;
|
||||
this.sinceToken = since_token;
|
||||
this.table = new JSONTableScanner()
|
||||
.addColumn("type", String.class)
|
||||
.addColumn("my_sid", SubscriberId.class)
|
||||
.addColumn("their_sid", SubscriberId.class)
|
||||
.addColumn("offset", Long.class)
|
||||
.addColumn("token", String.class)
|
||||
.addColumn("text", String.class, JSONTokeniser.Narrow.ALLOW_NULL)
|
||||
.addColumn("delivered", Boolean.class)
|
||||
.addColumn("read", Boolean.class)
|
||||
.addColumn("timestamp", Long.class, JSONTokeniser.Narrow.ALLOW_NULL)
|
||||
.addColumn("ack_offset", Long.class, JSONTokeniser.Narrow.ALLOW_NULL);
|
||||
}
|
||||
|
||||
public boolean isConnected()
|
||||
{
|
||||
return this.json != null;
|
||||
@Override
|
||||
protected String getUrl() {
|
||||
if (this.sinceToken == null)
|
||||
return "/restful/meshms/" + my_sid.toHex() + "/" + their_sid.toHex() + "/messagelist.json";
|
||||
else if(this.sinceToken.equals(""))
|
||||
return "/restful/meshms/" + my_sid.toHex() + "/" + their_sid.toHex() + "/newsince/messagelist.json";
|
||||
else
|
||||
return "/restful/meshms/" + my_sid.toHex() + "/" + their_sid.toHex() + "/newsince/" + sinceToken + "/messagelist.json";
|
||||
}
|
||||
|
||||
public void connect() throws MeshMSException, ServalDInterfaceException, IOException
|
||||
{
|
||||
assert json == null;
|
||||
try {
|
||||
rowCount = 0;
|
||||
if (this.sinceToken == null)
|
||||
httpConnection = httpConnector.newServalDHttpConnection("/restful/meshms/" + my_sid.toHex() + "/" + their_sid.toHex() + "/messagelist.json");
|
||||
else if(this.sinceToken.equals(""))
|
||||
httpConnection = httpConnector.newServalDHttpConnection("/restful/meshms/" + my_sid.toHex() + "/" + their_sid.toHex() + "/newsince/messagelist.json");
|
||||
else
|
||||
httpConnection = httpConnector.newServalDHttpConnection("/restful/meshms/" + my_sid.toHex() + "/" + their_sid.toHex() + "/newsince/" + sinceToken + "/messagelist.json");
|
||||
httpConnection.connect();
|
||||
json = MeshMSCommon.receiveRestfulResponse(httpConnection, HttpURLConnection.HTTP_OK);
|
||||
json.consume(JSONTokeniser.Token.START_OBJECT);
|
||||
if (this.sinceToken == null) {
|
||||
json.consume("read_offset");
|
||||
json.consume(JSONTokeniser.Token.COLON);
|
||||
readOffset = json.consume(Long.class);
|
||||
json.consume(JSONTokeniser.Token.COMMA);
|
||||
json.consume("latest_ack_offset");
|
||||
json.consume(JSONTokeniser.Token.COLON);
|
||||
latestAckOffset = json.consume(Long.class);
|
||||
json.consume(JSONTokeniser.Token.COMMA);
|
||||
}
|
||||
json.consume("header");
|
||||
@Override
|
||||
protected void consumeHeader() throws JSONInputException, IOException {
|
||||
Object tok = json.nextToken();
|
||||
if (tok.equals("read_offset")) {
|
||||
json.consume(JSONTokeniser.Token.COLON);
|
||||
table.consumeHeaderArray(json);
|
||||
readOffset = json.consume(Long.class);
|
||||
json.consume(JSONTokeniser.Token.COMMA);
|
||||
json.consume("rows");
|
||||
} else if (tok.equals("latest_ack_offset")) {
|
||||
json.consume(JSONTokeniser.Token.COLON);
|
||||
json.consume(JSONTokeniser.Token.START_ARRAY);
|
||||
}
|
||||
catch (JSONInputException e) {
|
||||
throw new ServalDInterfaceException(e);
|
||||
}
|
||||
latestAckOffset = json.consume(Long.class);
|
||||
json.consume(JSONTokeniser.Token.COMMA);
|
||||
} else
|
||||
super.consumeHeader();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleResponseError() throws MeshMSException, IOException, ServalDInterfaceException {
|
||||
if (json!=null)
|
||||
MeshMSCommon.processRestfulError(httpConnection, json);
|
||||
|
||||
super.handleResponseError();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MeshMSMessage factory(Map<String, Object> row, long rowCount) throws ServalDInterfaceException {
|
||||
String typesym = (String) row.get("type");
|
||||
MeshMSMessage.Type type;
|
||||
if (typesym.equals(">"))
|
||||
type = MeshMSMessage.Type.MESSAGE_SENT;
|
||||
else if (typesym.equals("<"))
|
||||
type = MeshMSMessage.Type.MESSAGE_RECEIVED;
|
||||
else if (typesym.equals("ACK"))
|
||||
type = MeshMSMessage.Type.ACK_RECEIVED;
|
||||
else
|
||||
throw new ServalDInterfaceException("invalid column value: type=" + typesym);
|
||||
return new MeshMSMessage(
|
||||
(int)rowCount,
|
||||
type,
|
||||
new Subscriber((SubscriberId)row.get("my_sid")),
|
||||
new Subscriber((SubscriberId)row.get("their_sid")),
|
||||
(Long)row.get("offset"),
|
||||
(String)row.get("token"),
|
||||
(String)row.get("text"),
|
||||
(Boolean)row.get("delivered"),
|
||||
(Boolean)row.get("read"),
|
||||
(Long)row.get("timestamp"),
|
||||
(Long)row.get("ack_offset")
|
||||
);
|
||||
}
|
||||
|
||||
public long getReadOffset()
|
||||
@ -125,65 +135,9 @@ public class MeshMSMessageList {
|
||||
return latestAckOffset;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public MeshMSMessage nextMessage() throws ServalDInterfaceException, IOException
|
||||
{
|
||||
assert json != null;
|
||||
try {
|
||||
Object tok = json.nextToken();
|
||||
if (tok == JSONTokeniser.Token.END_ARRAY) {
|
||||
json.consume(JSONTokeniser.Token.END_OBJECT);
|
||||
json.consume(JSONTokeniser.Token.EOF);
|
||||
return null;
|
||||
}
|
||||
if (rowCount != 0)
|
||||
JSONTokeniser.match(tok, JSONTokeniser.Token.COMMA);
|
||||
else
|
||||
json.pushToken(tok);
|
||||
Map<String,Object> row = table.consumeRowArray(json);
|
||||
String typesym = (String) row.get("type");
|
||||
MeshMSMessage.Type type;
|
||||
if (typesym.equals(">"))
|
||||
type = MeshMSMessage.Type.MESSAGE_SENT;
|
||||
else if (typesym.equals("<"))
|
||||
type = MeshMSMessage.Type.MESSAGE_RECEIVED;
|
||||
else if (typesym.equals("ACK"))
|
||||
type = MeshMSMessage.Type.ACK_RECEIVED;
|
||||
else
|
||||
throw new ServalDInterfaceException("invalid column value: type=" + typesym);
|
||||
return new MeshMSMessage(
|
||||
rowCount++,
|
||||
type,
|
||||
new Subscriber((SubscriberId)row.get("my_sid")),
|
||||
new Subscriber((SubscriberId)row.get("their_sid")),
|
||||
(Long)row.get("offset"),
|
||||
(String)row.get("token"),
|
||||
(String)row.get("text"),
|
||||
(Boolean)row.get("delivered"),
|
||||
(Boolean)row.get("read"),
|
||||
(Long)row.get("timestamp"),
|
||||
(Long)row.get("ack_offset")
|
||||
);
|
||||
}
|
||||
catch (JSONInputException e) {
|
||||
throw new ServalDInterfaceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws IOException
|
||||
{
|
||||
httpConnection = null;
|
||||
if (json != null) {
|
||||
json.close();
|
||||
json = null;
|
||||
}
|
||||
}
|
||||
|
||||
public List<MeshMSMessage> toList() throws ServalDInterfaceException, IOException {
|
||||
List<MeshMSMessage> ret = new ArrayList<MeshMSMessage>();
|
||||
MeshMSMessage item;
|
||||
while ((item = nextMessage()) != null) {
|
||||
ret.add(item);
|
||||
}
|
||||
return ret;
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user