MeshMS Java API: mark messages as read

This commit is contained in:
Andrew Bettison 2014-06-24 12:12:48 +09:30
parent 7736a4ceb1
commit c79a382a27
7 changed files with 210 additions and 24 deletions

View File

@ -147,13 +147,16 @@ public class JSONTokeniser {
if (tok instanceof Token)
throw new UnexpectedTokenException(tok, cls);
// Convert:
// Integer --> Float or Double
// Integer --> Long or Float or Double
// Long --> Float or Double
// Float --> Double
// Double --> Float
if (cls == Double.class && (tok instanceof Float || tok instanceof Integer))
if (cls == Double.class && (tok instanceof Float || tok instanceof Long || tok instanceof Integer))
tok = new Double(((Number)tok).doubleValue());
else if (cls == Float.class && (tok instanceof Double || tok instanceof Integer))
else if (cls == Float.class && (tok instanceof Double || tok instanceof Long || tok instanceof Integer))
tok = new Float(((Number)tok).floatValue());
else if (cls == Long.class && tok instanceof Integer)
tok = new Long(((Number)tok).longValue());
if (cls.isInstance(tok))
return (T)tok; // unchecked cast
throw new UnexpectedTokenException(tok, cls);
@ -239,7 +242,12 @@ public class JSONTokeniser {
public static boolean jsonIsToken(Object tok)
{
return tok instanceof Token || tok instanceof String || tok instanceof Double || tok instanceof Integer || tok instanceof Boolean;
return tok instanceof Token
|| tok instanceof String
|| tok instanceof Double
|| tok instanceof Long
|| tok instanceof Integer
|| tok instanceof Boolean;
}
public static String jsonTokenDescription(Object tok)
@ -247,7 +255,7 @@ public class JSONTokeniser {
if (tok == null)
return "null";
if (tok instanceof String)
return "\"" + tok + "\"";
return "\"" + ((String)tok).replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
if (tok instanceof Number)
return "" + tok;
if (tok instanceof Boolean)
@ -272,6 +280,7 @@ public class JSONTokeniser {
private int readHex(int digits) throws SyntaxException, IOException
{
assert digits <= 8;
char[] buf = new char[digits];
int len = 0;
while (len < buf.length) {
@ -285,8 +294,8 @@ public class JSONTokeniser {
return Integer.valueOf(hex, 16);
}
catch (NumberFormatException e) {
throw new SyntaxException("expecting " + digits + " hex digits, got \"" + hex + "\"");
}
throw new SyntaxException("expecting " + digits + " hex digits, got \"" + hex + "\"");
}
public void pushToken(Object tok)
@ -438,8 +447,14 @@ public class JSONTokeniser {
try {
if (isfloat)
return Double.parseDouble(number);
else
return Integer.parseInt(number);
else {
try {
return Integer.parseInt(number);
}
catch (NumberFormatException e) {
}
return Long.parseLong(number);
}
}
catch (NumberFormatException e) {
throw new SyntaxException("malformed JSON number: " + number);

View File

@ -77,6 +77,21 @@ public class ServalDClient implements ServalDHttpConnectionFactory
return MeshMSCommon.sendMessage(this, sid1, sid2, text);
}
public MeshMSStatus meshmsMarkAllConversationsRead(SubscriberId sid1) throws IOException, ServalDInterfaceException, MeshMSException
{
return MeshMSCommon.markAllConversationsRead(this, sid1);
}
public MeshMSStatus meshmsMarkAllMessagesRead(SubscriberId sid1, SubscriberId sid2) throws IOException, ServalDInterfaceException, MeshMSException
{
return MeshMSCommon.markAllMessagesRead(this, sid1, sid2);
}
public MeshMSStatus meshmsAdvanceReadOffset(SubscriberId sid1, SubscriberId sid2, long offset) throws IOException, ServalDInterfaceException, MeshMSException
{
return MeshMSCommon.advanceReadOffset(this, sid1, sid2, offset);
}
// interface ServalDHttpConnectionFactory
public HttpURLConnection newServalDHttpConnection(String path) throws ServalDInterfaceException, IOException
{

View File

@ -36,6 +36,12 @@ import org.servalproject.json.JSONInputException;
public class MeshMSCommon
{
protected static JSONTokeniser receiveRestfulResponse(HttpURLConnection conn, int expected_response_code) throws IOException, ServalDInterfaceException, MeshMSException
{
int[] expected_response_codes = { expected_response_code };
return receiveRestfulResponse(conn, expected_response_codes);
}
protected static JSONTokeniser receiveRestfulResponse(HttpURLConnection conn, int[] expected_response_codes) throws IOException, ServalDInterfaceException, MeshMSException
{
if (!conn.getContentType().equals("application/json"))
throw new ServalDInterfaceException("unexpected HTTP Content-Type: " + conn.getContentType());
@ -45,10 +51,13 @@ public class MeshMSCommon
throwRestfulResponseExceptions(status, conn.getURL());
throw new ServalDInterfaceException("unexpected MeshMS status = " + status.meshms_status + ", \"" + status.message + "\"");
}
if (conn.getResponseCode() != expected_response_code)
throw new ServalDInterfaceException("unexpected HTTP response code: " + conn.getResponseCode());
JSONTokeniser json = new JSONTokeniser(new InputStreamReader(conn.getInputStream(), "US-ASCII"));
return json;
for (int code: expected_response_codes) {
if (conn.getResponseCode() == code) {
JSONTokeniser json = new JSONTokeniser(new InputStreamReader(conn.getInputStream(), "US-ASCII"));
return json;
}
}
throw new ServalDInterfaceException("unexpected HTTP response code: " + conn.getResponseCode());
}
private static class Status {
@ -119,4 +128,40 @@ public class MeshMSCommon
return status.meshms_status;
}
public static MeshMSStatus markAllConversationsRead(ServalDHttpConnectionFactory connector, SubscriberId sid1) throws IOException, ServalDInterfaceException, MeshMSException
{
HttpURLConnection conn = connector.newServalDHttpConnection("/restful/meshms/" + sid1.toHex() + "/readall");
conn.setRequestMethod("POST");
conn.connect();
int[] expected_response_codes = { HttpURLConnection.HTTP_OK, HttpURLConnection.HTTP_CREATED };
JSONTokeniser json = MeshMSCommon.receiveRestfulResponse(conn, expected_response_codes);
Status status = decodeRestfulStatus(json);
throwRestfulResponseExceptions(status, conn.getURL());
return status.meshms_status;
}
public static MeshMSStatus markAllMessagesRead(ServalDHttpConnectionFactory connector, SubscriberId sid1, SubscriberId sid2) throws IOException, ServalDInterfaceException, MeshMSException
{
HttpURLConnection conn = connector.newServalDHttpConnection("/restful/meshms/" + sid1.toHex() + "/" + sid2.toHex() + "/readall");
conn.setRequestMethod("POST");
conn.connect();
int[] expected_response_codes = { HttpURLConnection.HTTP_OK, HttpURLConnection.HTTP_CREATED };
JSONTokeniser json = MeshMSCommon.receiveRestfulResponse(conn, expected_response_codes);
Status status = decodeRestfulStatus(json);
throwRestfulResponseExceptions(status, conn.getURL());
return status.meshms_status;
}
public static MeshMSStatus advanceReadOffset(ServalDHttpConnectionFactory connector, SubscriberId sid1, SubscriberId sid2, long offset) throws IOException, ServalDInterfaceException, MeshMSException
{
HttpURLConnection conn = connector.newServalDHttpConnection("/restful/meshms/" + sid1.toHex() + "/" + sid2.toHex() + "/recv/" + offset + "/read");
conn.setRequestMethod("POST");
conn.connect();
int[] expected_response_codes = { HttpURLConnection.HTTP_OK, HttpURLConnection.HTTP_CREATED };
JSONTokeniser json = MeshMSCommon.receiveRestfulResponse(conn, expected_response_codes);
Status status = decodeRestfulStatus(json);
throwRestfulResponseExceptions(status, conn.getURL());
return status.meshms_status;
}
}

View File

@ -35,23 +35,23 @@ public class MeshMSMessage {
public final Type type;
public final SubscriberId mySid;
public final SubscriberId theirSid;
public final int offset;
public final long offset;
public final String token;
public final String text;
public final boolean isDelivered;
public final boolean isRead;
public final Integer ackOffset;
public final Long ackOffset;
protected MeshMSMessage(int rowNumber,
Type type,
SubscriberId my_sid,
SubscriberId their_sid,
int offset,
long offset,
String token,
String text,
boolean delivered,
boolean read,
Integer ack_offset) throws ServalDInterfaceException
Long ack_offset) throws ServalDInterfaceException
{
if (my_sid == null)
throw new ServalDInterfaceException("my_sid is null");

View File

@ -38,8 +38,8 @@ public class MeshMSMessageList {
private SubscriberId their_sid;
private HttpURLConnection httpConnection;
private JSONTokeniser json;
private int readOffset;
private int latestAckOffset;
private long readOffset;
private long latestAckOffset;
private Vector<String> headers;
private int columnIndex_type;
private int columnIndex_my_sid;
@ -84,11 +84,11 @@ public class MeshMSMessageList {
json.consume(JSONTokeniser.Token.START_OBJECT);
json.consume("read_offset");
json.consume(JSONTokeniser.Token.COLON);
readOffset = json.consume(Integer.class);
readOffset = json.consume(Long.class);
json.consume(JSONTokeniser.Token.COMMA);
json.consume("latest_ack_offset");
json.consume(JSONTokeniser.Token.COLON);
latestAckOffset = json.consume(Integer.class);
latestAckOffset = json.consume(Long.class);
json.consume(JSONTokeniser.Token.COMMA);
json.consume("header");
json.consume(JSONTokeniser.Token.COLON);
@ -145,13 +145,13 @@ public class MeshMSMessageList {
}
}
public int getReadOffset()
public long getReadOffset()
{
assert json != null;
return readOffset;
}
public int getLatestAckOffset()
public long getLatestAckOffset()
{
assert json != null;
return latestAckOffset;
@ -188,12 +188,12 @@ public class MeshMSMessageList {
catch (SubscriberId.InvalidHexException e) {
throw new ServalDInterfaceException("invalid column value: their_sid", e);
}
int offset = JSONTokeniser.narrow(row[columnIndex_offset], Integer.class);
long offset = JSONTokeniser.narrow(row[columnIndex_offset], Long.class);
String token = JSONTokeniser.narrow(row[columnIndex_token], String.class);
String text = JSONTokeniser.narrow(row[columnIndex_text], String.class, JSONTokeniser.Narrow.ALLOW_NULL);
boolean is_delivered = JSONTokeniser.narrow(row[columnIndex_delivered], Boolean.class);
boolean is_read = JSONTokeniser.narrow(row[columnIndex_read], Boolean.class);
Integer ack_offset = JSONTokeniser.narrow(row[columnIndex_ack_offset], Integer.class, JSONTokeniser.Narrow.ALLOW_NULL);
Long ack_offset = JSONTokeniser.narrow(row[columnIndex_ack_offset], Long.class, JSONTokeniser.Narrow.ALLOW_NULL);
MeshMSMessage.Type type;
if (typesym.equals(">"))
type = MeshMSMessage.Type.MESSAGE_SENT;

View File

@ -107,6 +107,45 @@ public class Meshms {
System.exit(0);
}
static void meshms_mark_all_conversations_read(SubscriberId sid1) throws ServalDInterfaceException, IOException, InterruptedException
{
ServalDClient client = new ServerControl().getRestfulClient();
try {
MeshMSStatus status = client.meshmsMarkAllConversationsRead(sid1);
System.out.println("" + status);
}
catch (MeshMSException e) {
System.out.println(e.toString());
}
System.exit(0);
}
static void meshms_mark_all_messages_read(SubscriberId sid1, SubscriberId sid2) throws ServalDInterfaceException, IOException, InterruptedException
{
ServalDClient client = new ServerControl().getRestfulClient();
try {
MeshMSStatus status = client.meshmsMarkAllMessagesRead(sid1, sid2);
System.out.println("" + status);
}
catch (MeshMSException e) {
System.out.println(e.toString());
}
System.exit(0);
}
static void meshms_advance_read_offset(SubscriberId sid1, SubscriberId sid2, long offset) throws ServalDInterfaceException, IOException, InterruptedException
{
ServalDClient client = new ServerControl().getRestfulClient();
try {
MeshMSStatus status = client.meshmsAdvanceReadOffset(sid1, sid2, offset);
System.out.println("" + status);
}
catch (MeshMSException e) {
System.out.println(e.toString());
}
System.exit(0);
}
public static void main(String... args)
{
if (args.length < 1)
@ -119,6 +158,12 @@ public class Meshms {
meshms_list_messages(new SubscriberId(args[1]), new SubscriberId(args[2]));
else if (methodName.equals("meshms-send-message"))
meshms_send_message(new SubscriberId(args[1]), new SubscriberId(args[2]), args[3]);
else if (methodName.equals("meshms-mark-all-conversations-read"))
meshms_mark_all_conversations_read(new SubscriberId(args[1]));
else if (methodName.equals("meshms-mark-all-messages-read"))
meshms_mark_all_messages_read(new SubscriberId(args[1]), new SubscriberId(args[2]));
else if (methodName.equals("meshms-advance-read-offset"))
meshms_advance_read_offset(new SubscriberId(args[1]), new SubscriberId(args[2]), Long.parseLong(args[3]));
} catch (Exception e) {
e.printStackTrace();
System.exit(1);

View File

@ -163,4 +163,70 @@ test_MeshmsSendNoIdentity() {
tfw_cat --stdout --stderr
}
doc_MeshmsReadAllConversations="Java API MeshMS mark all conversations read"
setup_MeshmsReadAllConversations() {
setup
# create 3 threads, with all permutations of incoming and outgoing messages
executeOk_servald meshms send message $SIDA1 $SIDA2 "Message1"
executeOk_servald meshms send message $SIDA3 $SIDA1 "Message2"
executeOk_servald meshms send message $SIDA1 $SIDA4 "Message3"
executeOk_servald meshms send message $SIDA4 $SIDA1 "Message4"
executeOk_servald meshms list conversations $SIDA1
assertStdoutGrep --stderr --matches=1 ":$SIDA2::0:0\$"
assertStdoutGrep --stderr --matches=1 ":$SIDA3:unread:11:0\$"
assertStdoutGrep --stderr --matches=1 ":$SIDA4:unread:14:0\$"
}
test_MeshmsReadAllConversations() {
executeJavaOk org.servalproject.test.Meshms meshms-mark-all-conversations-read $SIDA1
assertStdoutIs -e 'UPDATED\n'
executeOk_servald meshms list conversations $SIDA1
assertStdoutGrep --stderr --matches=1 ":$SIDA2::0:0\$"
assertStdoutGrep --stderr --matches=1 ":$SIDA3::11:11\$"
assertStdoutGrep --stderr --matches=1 ":$SIDA4::14:14\$"
}
doc_MeshmsReadAllMessages="Java API MeshMS mark all conversations read"
setup_MeshmsReadAllMessages() {
setup
# create 3 threads, with all permutations of incoming and outgoing messages
executeOk_servald meshms send message $SIDA1 $SIDA2 "Message1"
executeOk_servald meshms send message $SIDA3 $SIDA1 "Message2"
executeOk_servald meshms send message $SIDA1 $SIDA2 "Message3"
executeOk_servald meshms send message $SIDA1 $SIDA4 "Message4"
executeOk_servald meshms send message $SIDA4 $SIDA1 "Message5"
executeOk_servald meshms send message $SIDA1 $SIDA2 "Message6"
executeOk_servald meshms list conversations $SIDA2
assertStdoutGrep --stderr --matches=1 ":$SIDA1:unread:33:0\$"
}
test_MeshmsReadAllMessages() {
executeJavaOk org.servalproject.test.Meshms meshms-mark-all-messages-read $SIDA2 $SIDA1
assertStdoutIs -e 'UPDATED\n'
executeOk_servald meshms list conversations $SIDA2
assertStdoutGrep --stderr --matches=1 ":$SIDA1::33:33\$"
}
doc_MeshmsReadMessage="Java API MeshMS mark a message as read"
setup_MeshmsReadMessage() {
setup
# create 3 threads, with all permutations of incoming and outgoing messages
executeOk_servald meshms send message $SIDA1 $SIDA2 "Message1"
executeOk_servald meshms send message $SIDA3 $SIDA1 "Message2"
executeOk_servald meshms send message $SIDA1 $SIDA2 "Message3"
executeOk_servald meshms send message $SIDA1 $SIDA4 "Message4"
executeOk_servald meshms send message $SIDA4 $SIDA1 "Message5"
executeOk_servald meshms send message $SIDA1 $SIDA2 "Message6"
executeOk_servald meshms list conversations $SIDA2
assertStdoutGrep --stderr --matches=1 ":$SIDA1:unread:33:0\$"
}
test_MeshmsReadMessage() {
executeJavaOk org.servalproject.test.Meshms meshms-advance-read-offset $SIDA2 $SIDA1 22
assertStdoutIs -e 'UPDATED\n'
executeOk_servald meshms list conversations $SIDA2
assertStdoutGrep --stderr --matches=1 ":$SIDA1:unread:33:22\$"
executeJavaOk org.servalproject.test.Meshms meshms-advance-read-offset $SIDA2 $SIDA1 11
assertStdoutIs -e 'OK\n'
executeOk_servald meshms list conversations $SIDA2
assertStdoutGrep --stderr --matches=1 ":$SIDA1:unread:33:22\$"
}
runTests "$@"